main.c 17.2 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"
Jack Moffitt's avatar
Jack Moffitt committed
81

82 83
#include <libxml/xmlmemory.h>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return 1;
}

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

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

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

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

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

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

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

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

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

319
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
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 362
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);

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

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

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

379
    connection_setup_sockets(config);
380

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

386
    pidfile_update(config, 1);
387

388
    return 1;
389
}
Jack Moffitt's avatar
Jack Moffitt committed
390

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

402
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
403 404
}

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

   if(conf->chuid)
   {
419 420 421 422 423 424 425 426 427
       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);
428

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

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

452
   }
453
#endif
454

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

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

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

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

594 595
    /* override config file options with commandline options */
    config_parse_cmdline(argc, argv);
Jack Moffitt's avatar
Jack Moffitt committed
596

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

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

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

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

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

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

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

636
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
637

638
    /* let her rip */
639
    global.running = ICECAST_RUNNING;
640 641 642 643

    /* Startup yp thread */
    yp_initialize();

644 645
    /* Do this after logging init */
    slave_initialize();
646
    auth_initialise ();
647
    event_initialise();
648

649
    event_emit_global("icecast-start");
650
    _server_proc();
651
    event_emit_global("icecast-stop");
Jack Moffitt's avatar
Jack Moffitt committed
652

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

663
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
664 665 666
}