main.c 17.3 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"
80
#include "listensocket.h"
81
#include "fastevent.h"
Jack Moffitt's avatar
Jack Moffitt committed
82

83 84
#include <libxml/xmlmemory.h>

Jack Moffitt's avatar
Jack Moffitt committed
85 86 87
#undef CATMODULE
#define CATMODULE "main"

88 89 90
static int background;
static char *pidfile = NULL;

91 92
static void pidfile_update(ice_config_t *config, int always_try);

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

104
static void _print_usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
105
{
106
    printf("%s\n\n", ICECAST_VERSION_STRING);
107 108
    printf("usage: icecast [-b] -c <file>\n");
    printf("or   : icecast {-v|--version}\n");
109
    printf("options:\n");
110 111 112
    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");
113
    printf("\n");
Jack Moffitt's avatar
Jack Moffitt committed
114 115
}

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

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

136
    xslt_initialize();
137 138
#ifdef HAVE_CURL
    icecast_curl_initialize();
139
#endif
Jack Moffitt's avatar
Jack Moffitt committed
140 141
}

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

152
    connection_shutdown();
153
    tls_shutdown();
154 155 156
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
157
    fastevent_shutdown();
158
    global_shutdown();
159
    thread_shutdown();
160

161
#ifdef HAVE_CURL
162
    icecast_curl_shutdown();
163 164
#endif

165 166
    /* Now that these are done, we can stop the loggers. */
    _stop_logging();
167
    log_shutdown();
168
    xslt_shutdown();
Jack Moffitt's avatar
Jack Moffitt committed
169 170
}

171 172 173 174 175 176
void main_config_reload(ice_config_t *config)
{
    ICECAST_LOG_DEBUG("Reloading configuration.");
    pidfile_update(config, 0);
}

Philipp Schafft's avatar
Philipp Schafft committed
177
static int _parse_config_opts(int argc, char **argv, char *filename, size_t size)
Jack Moffitt's avatar
Jack Moffitt committed
178
{
179
    int i = 1;
180 181
    int config_ok = 0;

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

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    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
        }
212
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
213
            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
214 215 216
            exit(0);
        }

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

229 230 231 232
    if(config_ok)
        return 1;
    else
        return -1;
Jack Moffitt's avatar
Jack Moffitt committed
233 234
}

235 236 237 238 239
static int _start_logging_stdout(void) {
    errorlog = log_open_file(stderr);
    if ( errorlog < 0 )
        return 0;

240
    log_set_level(errorlog, ICECAST_LOGLEVEL_WARN);
241 242 243 244

    return 1;
}

245
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
246
{
247 248
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
249
    char fn_playlist[FILENAME_MAX];
250
    char buf[1024];
251
    int log_to_stderr;
252

253
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
254

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

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

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

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

    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;
302
            snprintf(buf, sizeof(buf)-1,
303 304 305 306 307 308
                "FATAL: could not open playlist logging (%s): %s",
                log_to_stderr?"standard error":fn_playlist,
                strerror(errno));
            _fatal_error(buf);
        }
        log_to_stderr = 0;
309 310 311
        if (config->logsize)
            log_set_trigger (playlistlog, config->logsize);
        log_set_archive_timestamp(playlistlog, config->logarchive);
312 313 314
    } else {
        playlistlog = -1;
    }
315

316 317
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);
318
    log_set_level(playlistlog, 4);
319 320

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

322
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
323 324
}

325 326 327 328 329 330 331 332 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
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);

366
        ICECAST_LOG_INFO("pidfile %H updated.", pidfile);
367 368 369 370 371 372 373 374 375 376
    }

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

377
/* bind the socket and start listening */
378
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
379
{
380
    ice_config_t *config = config_get_config_unlocked();
381

382
    connection_setup_sockets(config);
383

384 385 386 387 388
    if (listensocket_container_sockcount(global.listensockets) < 1) {
        ICECAST_LOG_ERROR("Can not listen on any sockets.");
        return 0;
    }

389
    pidfile_update(config, 1);
390

391
    return 1;
392
}
Jack Moffitt's avatar
Jack Moffitt committed
393

394 395 396
/* this is the heart of the beast */
static void _server_proc(void)
{
397 398 399 400 401 402
    if (background)
    {
        fclose (stdin);
        fclose (stdout);
        fclose (stderr);
    }
403
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
404

405
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
406 407
}

408
/* chroot the process. Watch out - we need to do this before starting other
409
 * threads. Change uid as well, after figuring out uid _first_ */
410
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
411
static void _ch_root_uid_setup(void)
412
{
413
   ice_config_t *conf = config_get_config_unlocked();
414
#ifdef HAVE_SETUID
415 416 417 418 419 420 421
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

   if(conf->chuid)
   {
422 423 424 425 426 427 428 429 430
       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);
431

432 433 434 435 436
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
437 438 439
   }
#endif

440
#if HAVE_CHROOT
441 442 443 444 445 446
   if (conf->chroot)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
       }
447
       if(chroot(conf->base_dir) == -1 || chdir("/") == -1)
448 449 450 451 452 453 454
       {
           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);

455
   }
456
#endif
457

458
#if HAVE_SETUID
459 460 461 462 463 464 465 466
   if(conf->chuid)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
           return;
       }

467
       if(uid != (uid_t)-1 && gid != (gid_t)-1) {
468 469 470 471 472
#ifdef HAVE_SETRESGID
           if(!setresgid(gid, gid, gid)) {
#else
           if(!setgid(gid)) {
#endif
473
               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
474
           } else {
475
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
476
           }
477 478
           if(!initgroups(conf->user, gid))
               fprintf(stdout, "Changed supplementary groups based on user: %s.\n", conf->user);
479
           else
480
               fprintf(stdout, "Error changing supplementary groups: %s.\n", strerror(errno));
481 482 483 484 485
#ifdef HAVE_SETRESUID
           if(!setresuid(uid, uid, uid)) {
#else
           if(!setuid(uid)) {
#endif
486
               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
487
           } else {
488
               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
489
           }
490
       }
491 492
   }
#endif
493
}
494
#endif
Philipp Schafft's avatar
Philipp Schafft committed
495

496 497 498 499 500 501 502 503 504 505 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
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();
   }
}

536
#ifdef WIN32_SERVICE
Philipp Schafft's avatar
Philipp Schafft committed
537 538 539 540
int mainService(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
Jack Moffitt's avatar
Jack Moffitt committed
541
{
542
    int res, ret;
543 544 545 546 547
#ifdef ICECAST_DEFAULT_CONFIG
    char filename[512] = ICECAST_DEFAULT_CONFIG;
#else
    char filename[512] = "";
#endif
548
    char pbuf[1024];
549 550 551 552

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

597 598
    /* override config file options with commandline options */
    config_parse_cmdline(argc, argv);
Jack Moffitt's avatar
Jack Moffitt committed
599

600 601
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
602
        _fatal_error("Server startup failed. Exiting");
603
        shutdown_subsystems();
604 605
        return 1;
    }
606

607
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
608
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
609
#endif
610 611

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

614
#ifdef HAVE_SETUID
615 616
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
617
    if(!getuid() && getpid() != 1) /* Running as root! Don't allow this */
618
    {
619
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
620
        fprintf(stderr, "Use the changeowner directive in the config file\n");
621
        shutdown_subsystems();
622 623 624 625 626 627 628
        return 1;
    }
#endif

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

629
    if (!_start_logging()) {
630
        _fatal_error("FATAL: Could not start logging");
631
        shutdown_subsystems();
632 633
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
634

635
    ICECAST_LOG_INFO("%s server started", ICECAST_VERSION_STRING);
636
    ICECAST_LOG_INFO("Server's PID is %lli", (long long int)getpid());
637
    __log_system_name();
Jack Moffitt's avatar
Jack Moffitt committed
638

639
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
640

641
    /* let her rip */
642
    global.running = ICECAST_RUNNING;
643 644 645 646

    /* Startup yp thread */
    yp_initialize();

647 648
    /* Do this after logging init */
    slave_initialize();
649
    auth_initialise ();
650
    event_initialise();
651

652
    event_emit_global("icecast-start");
653
    _server_proc();
654
    event_emit_global("icecast-stop");
Jack Moffitt's avatar
Jack Moffitt committed
655

656
    ICECAST_LOG_INFO("Shutting down");
657
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
658 659
    shutdown_subsystems();
#endif
660 661 662 663 664 665
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

666
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
667 668 669
}