main.c 17.5 KB
Newer Older
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 8
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
9
 *                      Karl Heyes <karl@xiph.org>,
10
 *                      and others (see AUTHORS for details).
11
 * Copyright 2011-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12
 * Copyright 2014,      Thomas B. Ruecker <thomas@ruecker.fi>.
13 14
 */

15
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
16 17 18 19
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
20 21
#include <stdio.h>
#include <string.h>
22 23
#include <errno.h>

24
#ifdef HAVE_CURL
25
#include "curl.h"
26 27
#endif

28
#ifdef WIN32
29
#ifndef _WIN32_WINNT
30
#define _WIN32_WINNT 0x0400
31
#endif
32 33 34 35 36 37
/* For getpid() */
#include <process.h>
#include <windows.h>
#define snprintf _snprintf
#define getpid _getpid
#endif
Jack Moffitt's avatar
Jack Moffitt committed
38

39 40 41 42
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

43 44 45 46
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif

Marvin Scholz's avatar
Marvin Scholz committed
47 48 49 50
#include "common/thread/thread.h"
#include "common/net/sock.h"
#include "common/net/resolver.h"
#include "common/httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
51

52
#if HAVE_SYS_TYPES_H
53
#include <sys/types.h>
54 55
#endif
#if HAVE_GRP_H
56
#include <grp.h>
57 58
#endif
#if HAVE_PWD_H
59 60
#include <pwd.h>
#endif
Jack Moffitt's avatar
Jack Moffitt committed
61

62
#include "main.h"
63
#include "cfgfile.h"
64
#include "util.h"
Jack Moffitt's avatar
Jack Moffitt committed
65 66 67
#include "sighandler.h"

#include "global.h"
68
#include "compat.h"
Jack Moffitt's avatar
Jack Moffitt committed
69 70 71
#include "connection.h"
#include "refbuf.h"
#include "client.h"
72
#include "slave.h"
Jack Moffitt's avatar
Jack Moffitt committed
73 74
#include "stats.h"
#include "logging.h"
75
#include "xslt.h"
76
#include "fserve.h"
77
#include "yp.h"
78
#include "auth.h"
79
#include "event.h"
Jack Moffitt's avatar
Jack Moffitt committed
80

81 82
#include <libxml/xmlmemory.h>

Jack Moffitt's avatar
Jack Moffitt committed
83 84 85
#undef CATMODULE
#define CATMODULE "main"

86 87 88
static int background;
static char *pidfile = NULL;

89 90
static void pidfile_update(ice_config_t *config, int always_try);

91
static void _fatal_error(const char *perr)
92
{
93 94 95
#ifdef WIN32_SERVICE
    MessageBox(NULL, perr, "Error", MB_SERVICE_NOTIFICATION);
#elif defined(WIN32)
96 97 98 99 100 101
    MessageBox(NULL, perr, "Error", MB_OK);
#else
    fprintf(stdout, "%s\n", perr);
#endif
}

102
static void _print_usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
103
{
104
    printf("%s\n\n", ICECAST_VERSION_STRING);
105 106
    printf("usage: icecast [-b] -c <file>\n");
    printf("or   : icecast {-v|--version}\n");
107
    printf("options:\n");
108 109 110
    printf("\t-c <file>       Specify configuration file\n");
    printf("\t-v or --version Display version info\n");
    printf("\t-b              Run icecast in the background\n");
111
    printf("\n");
Jack Moffitt's avatar
Jack Moffitt committed
112 113
}

114 115
static void _stop_logging(void)
{
116 117
    log_close(errorlog);
    log_close(accesslog);
118
    log_close(playlistlog);
119 120
}

121
static void initialize_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
122
{
123 124 125 126 127
    log_initialize();
    thread_initialize();
    sock_initialize();
    resolver_initialize();
    config_initialize();
128
    tls_initialize();
129 130 131
    connection_initialize();
    global_initialize();
    refbuf_initialize();
132

133
    xslt_initialize();
134 135
#ifdef HAVE_CURL
    icecast_curl_initialize();
136
#endif
Jack Moffitt's avatar
Jack Moffitt committed
137 138
}

139
static void shutdown_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
140
{
141
    event_shutdown();
142
    fserve_shutdown();
143 144
    refbuf_shutdown();
    slave_shutdown();
145
    auth_shutdown();
146
    yp_shutdown();
147
    stats_shutdown();
148

149 150
    global_shutdown();
    connection_shutdown();
151
    tls_shutdown();
152 153 154 155
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
156

157
#ifdef HAVE_CURL
158
    icecast_curl_shutdown();
159 160
#endif

161 162
    /* Now that these are done, we can stop the loggers. */
    _stop_logging();
163
    log_shutdown();
164
    xslt_shutdown();
Jack Moffitt's avatar
Jack Moffitt committed
165 166
}

167 168 169 170 171 172
void main_config_reload(ice_config_t *config)
{
    ICECAST_LOG_DEBUG("Reloading configuration.");
    pidfile_update(config, 0);
}

Philipp Schafft's avatar
Philipp Schafft committed
173
static int _parse_config_opts(int argc, char **argv, char *filename, size_t size)
Jack Moffitt's avatar
Jack Moffitt committed
174
{
175
    int i = 1;
176 177
    int config_ok = 0;

178
    background = 0;
179 180
    if (argc < 2) {
        if (filename[0] != 0) {
181
            /* We have a default filename, so we can work with no options. */
182 183
            return 1;
        } else {
184
            /* We need at least a config filename. */
185 186 187
            return -1;
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    while (i < argc) {
        if (strcmp(argv[i], "-b") == 0) {
#ifndef WIN32
            pid_t pid;
            fprintf(stdout, "Starting icecast2\nDetaching from the console\n");

            pid = fork();

            if (pid > 0) {
                /* exit the parent */
                exit(0);
            }
            else if(pid < 0) {
                fprintf(stderr, "FATAL: Unable to fork child!");
                exit(1);
            }
            background = 1;
#endif
        }
208
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
209
            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
210 211 212
            exit(0);
        }

213 214 215
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 < argc) {
                strncpy(filename, argv[i + 1], size-1);
216
                filename[size-1] = 0;
217
                config_ok = 1;
218 219 220 221
            } else {
                return -1;
            }
        }
222
        i++;
223 224
    }

225 226 227 228
    if(config_ok)
        return 1;
    else
        return -1;
Jack Moffitt's avatar
Jack Moffitt committed
229 230
}

231 232 233 234 235
static int _start_logging_stdout(void) {
    errorlog = log_open_file(stderr);
    if ( errorlog < 0 )
        return 0;

236
    log_set_level(errorlog, ICECAST_LOGLEVEL_WARN);
237 238 239 240

    return 1;
}

241
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
242
{
243 244
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
245
    char fn_playlist[FILENAME_MAX];
246
    char buf[1024];
247
    int log_to_stderr;
248

249
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
250

251 252 253
    if(strcmp(config->error_log, "-") == 0) {
        /* this is already in place because of _start_logging_stdout() */
    } else {
254
        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
255
        errorlog = log_open(fn_error);
256
        log_to_stderr = 0;
257 258 259
        if (config->logsize)
            log_set_trigger (errorlog, config->logsize);
        log_set_archive_timestamp(errorlog, config->logarchive);
260
    }
261 262 263

    if (errorlog < 0) {
        buf[sizeof(buf)-1] = 0;
264
        snprintf(buf, sizeof(buf)-1,
265 266
                "FATAL: could not open error logging (%s): %s",
                log_to_stderr?"standard error":fn_error,
267 268 269 270 271
                strerror(errno));
        _fatal_error(buf);
    }
    log_set_level(errorlog, config->loglevel);

272 273 274 275
    if(strcmp(config->access_log, "-") == 0) {
        accesslog = log_open_file(stderr);
        log_to_stderr = 1;
    } else {
276
        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
277
        accesslog = log_open(fn_access);
278
        log_to_stderr = 0;
279 280 281
        if (config->logsize)
            log_set_trigger (accesslog, config->logsize);
        log_set_archive_timestamp(accesslog, config->logarchive);
282
    }
283 284 285

    if (accesslog < 0) {
        buf[sizeof(buf)-1] = 0;
286 287 288 289
        snprintf(buf, sizeof(buf) - 1,
            "FATAL: could not open access logging (%s): %s",
            log_to_stderr ? "standard error" : fn_access,
            strerror(errno));
290 291
        _fatal_error(buf);
    }
292 293 294 295 296 297

    if(config->playlist_log) {
        snprintf(fn_playlist, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
        playlistlog = log_open(fn_playlist);
        if (playlistlog < 0) {
            buf[sizeof(buf)-1] = 0;
298
            snprintf(buf, sizeof(buf)-1,
299 300 301 302 303 304
                "FATAL: could not open playlist logging (%s): %s",
                log_to_stderr?"standard error":fn_playlist,
                strerror(errno));
            _fatal_error(buf);
        }
        log_to_stderr = 0;
305 306 307
        if (config->logsize)
            log_set_trigger (playlistlog, config->logsize);
        log_set_archive_timestamp(playlistlog, config->logarchive);
308 309 310
    } else {
        playlistlog = -1;
    }
311

312 313
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);
314
    log_set_level(playlistlog, 4);
315 316

    if (errorlog >= 0 && accesslog >= 0) return 1;
317

318
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
319 320 321
}


322
static int _start_listening(void)
Jack Moffitt's avatar
Jack Moffitt committed
323
{
324 325
    int i;
    for(i=0; i < global.server_sockets; i++) {
326
        if (sock_listen(global.serversock[i], ICECAST_LISTEN_QUEUE) == SOCK_ERROR)
327
            return 0;
Jack Moffitt's avatar
Jack Moffitt committed
328

329
        sock_set_blocking(global.serversock[i], 0);
330
    }
Jack Moffitt's avatar
Jack Moffitt committed
331

332
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
333 334
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
static void pidfile_update(ice_config_t *config, int always_try)
{
    char *newpidfile = NULL;

    if (config->pidfile) {
        FILE *f;

        /* check if the file actually changed */
        if (pidfile && strcmp(pidfile, config->pidfile) == 0)
            return;

        ICECAST_LOG_DEBUG("New pidfile on %H", config->pidfile);

        if (!always_try) {
            if (config->chuid) {
                ICECAST_LOG_ERROR("Can not write new pidfile, changeowner in effect.");
                return;
            }

            if (config->chroot) {
                ICECAST_LOG_ERROR("Can not write new pidfile, chroot in effect.");
                return;
            }
        }

        newpidfile = strdup(config->pidfile);
        if (!newpidfile) {
            ICECAST_LOG_ERROR("Can not allocate memory for pidfile filename. BAD.");
            return;
        }

        f = fopen(newpidfile, "w");
        if (!f) {
            free(newpidfile);
            ICECAST_LOG_ERROR("Can not open new pidfile for writing.");
            return;
        }

        fprintf(f, "%lld\n", (long long int)getpid());
        fclose(f);

376
        ICECAST_LOG_INFO("pidfile %H updated.", pidfile);
377 378 379 380 381 382 383 384 385 386
    }

    if (newpidfile != pidfile) {
        if (pidfile)
            remove(pidfile);
        free(pidfile);
        pidfile = newpidfile;
    }
}

387
/* bind the socket and start listening */
388
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
389
{
390
    ice_config_t *config = config_get_config_unlocked();
391

392
    if (connection_setup_sockets (config) < 1)
393
        return 0;
Jack Moffitt's avatar
Jack Moffitt committed
394

395
    if (!_start_listening()) {
396
        _fatal_error("Failed trying to listen on server socket");
397 398
        return 0;
    }
399

400
    pidfile_update(config, 1);
401

402
    return 1;
403
}
Jack Moffitt's avatar
Jack Moffitt committed
404

405 406 407
/* this is the heart of the beast */
static void _server_proc(void)
{
408 409 410 411 412 413
    if (background)
    {
        fclose (stdin);
        fclose (stdout);
        fclose (stderr);
    }
414
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
415

416
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
417 418
}

419
/* chroot the process. Watch out - we need to do this before starting other
420
 * threads. Change uid as well, after figuring out uid _first_ */
421
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
422
static void _ch_root_uid_setup(void)
423
{
424
   ice_config_t *conf = config_get_config_unlocked();
425
#ifdef HAVE_SETUID
426 427 428 429 430 431 432
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

   if(conf->chuid)
   {
433 434 435 436 437 438 439 440 441
       if(conf->user) {
           user = getpwnam(conf->user);
           if(user)
               uid = user->pw_uid;
           else
               fprintf(stderr, "Couldn't find user \"%s\" in password file\n", conf->user);
       }
       if(conf->group) {
           group = getgrnam(conf->group);
442

443 444 445 446 447
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
448 449 450
   }
#endif

451
#if HAVE_CHROOT
452 453 454 455 456 457
   if (conf->chroot)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
       }
458
       if(chroot(conf->base_dir) == -1 || chdir("/") == -1)
459 460 461 462 463 464 465
       {
           fprintf(stderr,"WARNING: Couldn't change server root: %s\n", strerror(errno));
           return;
       }
       else
           fprintf(stdout, "Changed root successfully to \"%s\".\n", conf->base_dir);

466
   }
467
#endif
468

469
#if HAVE_SETUID
470 471 472 473 474 475 476 477
   if(conf->chuid)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
           return;
       }

478
       if(uid != (uid_t)-1 && gid != (gid_t)-1) {
479 480 481 482 483
#ifdef HAVE_SETRESGID
           if(!setresgid(gid, gid, gid)) {
#else
           if(!setgid(gid)) {
#endif
484
               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
485
           } else {
486
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
487
           }
488 489
           if(!initgroups(conf->user, gid))
               fprintf(stdout, "Changed supplementary groups based on user: %s.\n", conf->user);
490
           else
491
               fprintf(stdout, "Error changing supplementary groups: %s.\n", strerror(errno));
492 493 494 495 496
#ifdef HAVE_SETRESUID
           if(!setresuid(uid, uid, uid)) {
#else
           if(!setuid(uid)) {
#endif
497
               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
498
           } else {
499
               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
500
           }
501
       }
502 503
   }
#endif
504
}
505
#endif
Philipp Schafft's avatar
Philipp Schafft committed
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
static inline void __log_system_name(void) {
    char hostname[80] = "(unknown)";
    char system[128] = "(unknown)";
    int have_hostname = 0;
#ifdef HAVE_UNAME
    struct utsname utsname;
#endif
    ice_config_t *config;

#ifdef HAVE_GETHOSTNAME
    if (gethostname(hostname, sizeof(hostname)) != 0) {
        strncpy(hostname, "(unknown)", sizeof(hostname));
    } else {
        have_hostname = 1;
    }
#endif
#ifdef HAVE_UNAME
    if(uname(&utsname) == 0) {
        snprintf(system, sizeof(system), "%s %s, %s, %s, %s",
                 utsname.sysname, utsname.release, utsname.nodename, utsname.version, utsname.machine);
        if (!have_hostname) {
            strncpy(hostname, utsname.nodename, sizeof(hostname));
            have_hostname = 1;
        }
    }
#elif defined(WIN32)
    strncpy(system, "MS Windows", sizeof(system));
#endif

   ICECAST_LOG_INFO("Running on %s; OS: %s; Address Bits: %i", hostname, system, sizeof(void*)*8);

   if (have_hostname) {
       config = config_get_config();
       if (!config->sane_hostname && util_hostcheck(hostname) == HOSTCHECK_SANE) {
           ICECAST_LOG_WARN("Hostname is not set to anything useful in <hostname>, Consider setting it to the system's name \"%s\".", hostname);
       }
       config_release_config();
   }
}

547
#ifdef WIN32_SERVICE
Philipp Schafft's avatar
Philipp Schafft committed
548 549 550 551
int mainService(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
Jack Moffitt's avatar
Jack Moffitt committed
552
{
553
    int res, ret;
554 555 556 557 558
#ifdef ICECAST_DEFAULT_CONFIG
    char filename[512] = ICECAST_DEFAULT_CONFIG;
#else
    char filename[512] = "";
#endif
559
    char pbuf[1024];
560 561 562 563

    /* parse the '-c icecast.xml' option
    ** only, so that we can read a configfile
    */
564
    res = _parse_config_opts(argc, argv, filename, sizeof(filename));
565
    if (res == 1) {
566
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
567
        /* startup all the modules */
568
        initialize_subsystems();
569 570 571 572 573
        if (!_start_logging_stdout()) {
            _fatal_error("FATAL: Could not start logging on stderr.");
            shutdown_subsystems();
            return 1;
        }
574
#endif
575
        /* parse the config file */
576
        config_get_config();
577
        ret = config_initial_parse_file(filename);
578
        config_release_config();
579
        if (ret < 0) {
580
            memset(pbuf, '\000', sizeof(pbuf));
581
            snprintf(pbuf, sizeof(pbuf)-1,
582 583
                "FATAL: error parsing config file (%s)", filename);
            _fatal_error(pbuf);
584 585
            switch (ret) {
            case CONFIG_EINSANE:
586
                _fatal_error("filename was null or blank");
587 588
                break;
            case CONFIG_ENOROOT:
589
                _fatal_error("no root element found");
590 591
                break;
            case CONFIG_EBADROOT:
592
                _fatal_error("root element is not <icecast>");
593 594
                break;
            default:
595
                _fatal_error("XML config parsing error");
596 597
                break;
            }
598
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
599 600
            shutdown_subsystems();
#endif
601
            return 1;
602 603 604 605 606
        }
    } else if (res == -1) {
        _print_usage();
        return 1;
    }
607

608 609
    /* override config file options with commandline options */
    config_parse_cmdline(argc, argv);
Jack Moffitt's avatar
Jack Moffitt committed
610

611 612
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
613
        _fatal_error("Server startup failed. Exiting");
614
        shutdown_subsystems();
615 616
        return 1;
    }
617

618
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
619
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
620
#endif
621 622

    stats_initialize(); /* We have to do this later on because of threading */
623
    fserve_initialize(); /* This too */
624

625
#ifdef HAVE_SETUID
626 627
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
628
    if(!getuid() && getpid() != 1) /* Running as root! Don't allow this */
629
    {
630
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
631
        fprintf(stderr, "Use the changeowner directive in the config file\n");
632
        shutdown_subsystems();
633 634 635 636 637 638 639
        return 1;
    }
#endif

    /* setup default signal handlers */
    sighandler_initialize();

640
    if (!_start_logging()) {
641
        _fatal_error("FATAL: Could not start logging");
642
        shutdown_subsystems();
643 644
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
645

646
    ICECAST_LOG_INFO("%s server started", ICECAST_VERSION_STRING);
647
    ICECAST_LOG_INFO("Server's PID is %lli", (long long int)getpid());
648
    __log_system_name();
Jack Moffitt's avatar
Jack Moffitt committed
649

650
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
651

652
    /* let her rip */
653
    global.running = ICECAST_RUNNING;
654 655 656 657

    /* Startup yp thread */
    yp_initialize();

658 659
    /* Do this after logging init */
    slave_initialize();
660
    auth_initialise ();
661
    event_initialise();
662

663
    event_emit_global("icecast-start");
664
    _server_proc();
665
    event_emit_global("icecast-stop");
Jack Moffitt's avatar
Jack Moffitt committed
666

667
    ICECAST_LOG_INFO("Shutting down");
668
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
669 670
    shutdown_subsystems();
#endif
671 672 673 674 675 676
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

677
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
678 679 680
}