main.c 17 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
    log_initialize();
    thread_initialize();
125
    global_initialize();
126 127 128
    sock_initialize();
    resolver_initialize();
    config_initialize();
129
    tls_initialize();
130 131
    connection_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
    connection_shutdown();
150
    tls_shutdown();
151 152 153
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
154
    global_shutdown();
155
    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 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
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);

362
        ICECAST_LOG_INFO("pidfile %H updated.", pidfile);
363 364 365 366 367 368 369 370 371 372
    }

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

373
/* bind the socket and start listening */
374
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
375
{
376
    ice_config_t *config = config_get_config_unlocked();
377

378
    connection_setup_sockets(config);
379

380
    pidfile_update(config, 1);
381

382
    return 1;
383
}
Jack Moffitt's avatar
Jack Moffitt committed
384

385 386 387
/* this is the heart of the beast */
static void _server_proc(void)
{
388 389 390 391 392 393
    if (background)
    {
        fclose (stdin);
        fclose (stdout);
        fclose (stderr);
    }
394
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
395

396
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
397 398
}

399
/* chroot the process. Watch out - we need to do this before starting other
400
 * threads. Change uid as well, after figuring out uid _first_ */
401
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
402
static void _ch_root_uid_setup(void)
403
{
404
   ice_config_t *conf = config_get_config_unlocked();
405
#ifdef HAVE_SETUID
406 407 408 409 410 411 412
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

   if(conf->chuid)
   {
413 414 415 416 417 418 419 420 421
       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);
422

423 424 425 426 427
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
428 429 430
   }
#endif

431
#if HAVE_CHROOT
432 433 434 435 436 437
   if (conf->chroot)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
       }
438
       if(chroot(conf->base_dir) == -1 || chdir("/") == -1)
439 440 441 442 443 444 445
       {
           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);

446
   }
447
#endif
448

449
#if HAVE_SETUID
450 451 452 453 454 455 456 457
   if(conf->chuid)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
           return;
       }

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

487 488 489 490 491 492 493 494 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
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();
   }
}

527
#ifdef WIN32_SERVICE
Philipp Schafft's avatar
Philipp Schafft committed
528 529 530 531
int mainService(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
Jack Moffitt's avatar
Jack Moffitt committed
532
{
533
    int res, ret;
534 535 536 537 538
#ifdef ICECAST_DEFAULT_CONFIG
    char filename[512] = ICECAST_DEFAULT_CONFIG;
#else
    char filename[512] = "";
#endif
539
    char pbuf[1024];
540 541 542 543

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

588 589
    /* override config file options with commandline options */
    config_parse_cmdline(argc, argv);
Jack Moffitt's avatar
Jack Moffitt committed
590

591 592
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
593
        _fatal_error("Server startup failed. Exiting");
594
        shutdown_subsystems();
595 596
        return 1;
    }
597

598
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
599
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
600
#endif
601 602

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

605
#ifdef HAVE_SETUID
606 607
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
608
    if(!getuid() && getpid() != 1) /* Running as root! Don't allow this */
609
    {
610
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
611
        fprintf(stderr, "Use the changeowner directive in the config file\n");
612
        shutdown_subsystems();
613 614 615 616 617 618 619
        return 1;
    }
#endif

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

620
    if (!_start_logging()) {
621
        _fatal_error("FATAL: Could not start logging");
622
        shutdown_subsystems();
623 624
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
625

626
    ICECAST_LOG_INFO("%s server started", ICECAST_VERSION_STRING);
627
    ICECAST_LOG_INFO("Server's PID is %lli", (long long int)getpid());
628
    __log_system_name();
Jack Moffitt's avatar
Jack Moffitt committed
629

630
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
631

632
    /* let her rip */
633
    global.running = ICECAST_RUNNING;
634 635 636 637

    /* Startup yp thread */
    yp_initialize();

638 639
    /* Do this after logging init */
    slave_initialize();
640
    auth_initialise ();
641
    event_initialise();
642

643
    event_emit_global("icecast-start");
644
    _server_proc();
645
    event_emit_global("icecast-stop");
Jack Moffitt's avatar
Jack Moffitt committed
646

647
    ICECAST_LOG_INFO("Shutting down");
648
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
649 650
    shutdown_subsystems();
#endif
651 652 653 654 655 656
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

657
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
658 659 660
}