main.c 15.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-2014, 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 24 25 26 27 28 29 30 31
#include <errno.h>

#ifdef WIN32
#define _WIN32_WINNT 0x0400
/* For getpid() */
#include <process.h>
#include <windows.h>
#define snprintf _snprintf
#define getpid _getpid
#endif
Jack Moffitt's avatar
Jack Moffitt committed
32

33 34 35
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
36 37 38
#ifdef HAVE_CURL
#include <curl/curl.h>
#endif
39

40 41 42 43
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif

Karl Heyes's avatar
Karl Heyes committed
44 45 46 47 48
#include "thread/thread.h"
#include "avl/avl.h"
#include "net/sock.h"
#include "net/resolver.h"
#include "httpp/httpp.h"
Jack Moffitt's avatar
Jack Moffitt committed
49

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

60
#include "cfgfile.h"
61
#include "util.h"
Jack Moffitt's avatar
Jack Moffitt committed
62 63 64
#include "sighandler.h"

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

77 78
#include <libxml/xmlmemory.h>

Jack Moffitt's avatar
Jack Moffitt committed
79 80 81
#undef CATMODULE
#define CATMODULE "main"

82 83 84
static int background;
static char *pidfile = NULL;

85
static void _fatal_error(const char *perr)
86
{
87 88 89
#ifdef WIN32_SERVICE
    MessageBox(NULL, perr, "Error", MB_SERVICE_NOTIFICATION);
#elif defined(WIN32)
90 91 92 93 94 95
    MessageBox(NULL, perr, "Error", MB_OK);
#else
    fprintf(stdout, "%s\n", perr);
#endif
}

96
static void _print_usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
97
{
98
    printf("%s\n\n", ICECAST_VERSION_STRING);
99 100
    printf("usage: icecast [-b] -c <file>\n");
    printf("or   : icecast {-v|--version}\n");
101
    printf("options:\n");
102 103 104
    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");
105
    printf("\n");
Jack Moffitt's avatar
Jack Moffitt committed
106 107
}

108 109
static void _stop_logging(void)
{
110 111
    log_close(errorlog);
    log_close(accesslog);
112
    log_close(playlistlog);
113 114
}

115
void initialize_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
116
{
117 118 119 120 121 122 123 124
    log_initialize();
    thread_initialize();
    sock_initialize();
    resolver_initialize();
    config_initialize();
    connection_initialize();
    global_initialize();
    refbuf_initialize();
125

126
    xslt_initialize();
127
#ifdef HAVE_CURL_GLOBAL_INIT
128 129
    curl_global_init (CURL_GLOBAL_ALL);
#endif
Jack Moffitt's avatar
Jack Moffitt committed
130 131
}

132
void shutdown_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
133
{
134
    fserve_shutdown();
135 136
    refbuf_shutdown();
    slave_shutdown();
137
    auth_shutdown();
138
    yp_shutdown();
139
    stats_shutdown();
140

141 142 143 144 145 146
    global_shutdown();
    connection_shutdown();
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
147

148 149 150 151
#ifdef HAVE_CURL
    curl_global_cleanup();
#endif

152 153
    /* Now that these are done, we can stop the loggers. */
    _stop_logging();
154
    log_shutdown();
155
    xslt_shutdown();
Jack Moffitt's avatar
Jack Moffitt committed
156 157
}

158
static int _parse_config_opts(int argc, char **argv, char *filename, int size)
Jack Moffitt's avatar
Jack Moffitt committed
159
{
160
    int i = 1;
161 162
    int config_ok = 0;

163
    background = 0;
164
    if (argc < 2) return -1;
Jack Moffitt's avatar
Jack Moffitt committed
165

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    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
        }
185
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
186
            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
187 188 189
            exit(0);
        }

190 191 192
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 < argc) {
                strncpy(filename, argv[i + 1], size-1);
193
                filename[size-1] = 0;
194
                config_ok = 1;
195 196 197 198
            } else {
                return -1;
            }
        }
199
        i++;
200 201
    }

202 203 204 205
    if(config_ok)
        return 1;
    else
        return -1;
Jack Moffitt's avatar
Jack Moffitt committed
206 207
}

208 209 210 211 212 213 214 215 216 217
static int _start_logging_stdout(void) {
    errorlog = log_open_file(stderr);
    if ( errorlog < 0 )
        return 0;

    log_set_level(errorlog, 2 /* WARN */);

    return 1;
}

218
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
219
{
220 221
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
222
    char fn_playlist[FILENAME_MAX];
223
    char buf[1024];
224
    int log_to_stderr;
225

226
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
227

228
    if(strcmp(config->error_log, "-")) {
229
        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
230
        errorlog = log_open(fn_error);
231
        log_to_stderr = 0;
232 233 234
        if (config->logsize)
            log_set_trigger (errorlog, config->logsize);
        log_set_archive_timestamp(errorlog, config->logarchive);
235
    } else {
236
        /* this is already in place because of _start_logging_stdout() */
237
    }
238 239 240

    if (errorlog < 0) {
        buf[sizeof(buf)-1] = 0;
241 242 243
        snprintf(buf, sizeof(buf)-1, 
                "FATAL: could not open error logging (%s): %s",
                log_to_stderr?"standard error":fn_error,
244 245 246 247 248
                strerror(errno));
        _fatal_error(buf);
    }
    log_set_level(errorlog, config->loglevel);

249
    if(strcmp(config->access_log, "-")) {
250
        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
251
        accesslog = log_open(fn_access);
252
        log_to_stderr = 0;
253 254 255
        if (config->logsize)
            log_set_trigger (accesslog, config->logsize);
        log_set_archive_timestamp(accesslog, config->logarchive);
256 257
    } else {
        accesslog = log_open_file(stderr);
258
        log_to_stderr = 1;
259
    }
260 261 262

    if (accesslog < 0) {
        buf[sizeof(buf)-1] = 0;
263 264 265
        snprintf(buf, sizeof(buf)-1, 
                "FATAL: could not open access logging (%s): %s",
                log_to_stderr?"standard error":fn_access,
266 267 268
                strerror(errno));
        _fatal_error(buf);
    }
269 270 271 272 273 274 275 276 277 278 279 280 281

    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;
            snprintf(buf, sizeof(buf)-1, 
                "FATAL: could not open playlist logging (%s): %s",
                log_to_stderr?"standard error":fn_playlist,
                strerror(errno));
            _fatal_error(buf);
        }
        log_to_stderr = 0;
282 283 284
        if (config->logsize)
            log_set_trigger (playlistlog, config->logsize);
        log_set_archive_timestamp(playlistlog, config->logarchive);
285 286 287
    } else {
        playlistlog = -1;
    }
288

289 290
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);
291
    log_set_level(playlistlog, 4);
292 293 294 295

    if (errorlog >= 0 && accesslog >= 0) return 1;
    
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
296 297 298
}


299
static int _start_listening(void)
Jack Moffitt's avatar
Jack Moffitt committed
300
{
301 302
    int i;
    for(i=0; i < global.server_sockets; i++) {
303
        if (sock_listen(global.serversock[i], ICECAST_LISTEN_QUEUE) == SOCK_ERROR)
304
            return 0;
Jack Moffitt's avatar
Jack Moffitt committed
305

306
        sock_set_blocking(global.serversock[i], 0);
307
    }
Jack Moffitt's avatar
Jack Moffitt committed
308

309
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
310 311
}

312
/* bind the socket and start listening */
313
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
314
{
315
    ice_config_t *config = config_get_config_unlocked();
316

317
    if (connection_setup_sockets (config) < 1)
318
        return 0;
Jack Moffitt's avatar
Jack Moffitt committed
319

320
    if (!_start_listening()) {
321
        _fatal_error("Failed trying to listen on server socket");
322 323
        return 0;
    }
324

325 326 327 328 329 330 331 332 333 334 335 336
    /* recreate the pid file */
    if (config->pidfile)
    {
        FILE *f;
        pidfile = strdup (config->pidfile);
        if (pidfile && (f = fopen (config->pidfile, "w")) != NULL)
        {
            fprintf (f, "%d\n", (int)getpid());
            fclose (f);
        }
    }

337
    return 1;
338
}
Jack Moffitt's avatar
Jack Moffitt committed
339

340 341 342
/* this is the heart of the beast */
static void _server_proc(void)
{
343 344 345 346 347 348
    if (background)
    {
        fclose (stdin);
        fclose (stdout);
        fclose (stderr);
    }
349
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
350

351
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
352 353
}

354
/* chroot the process. Watch out - we need to do this before starting other
355
 * threads. Change uid as well, after figuring out uid _first_ */
356
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
357
static void _ch_root_uid_setup(void)
358
{
Michael Smith's avatar
Michael Smith committed
359
   ice_config_t *conf = config_get_config_unlocked();
360
#ifdef HAVE_SETUID
361 362 363 364 365 366 367
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

   if(conf->chuid)
   {
368 369 370 371 372 373 374 375 376
       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);
377

378 379 380 381 382
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
383 384 385
   }
#endif

386
#if HAVE_CHROOT
387 388 389 390 391 392
   if (conf->chroot)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
       }
393
       if(chroot(conf->base_dir) == -1 || chdir("/") == -1)
394 395 396 397 398 399 400 401 402
       {
           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);

   }   
#endif
403

404
#if HAVE_SETUID
405 406 407 408 409 410 411 412
   if(conf->chuid)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
           return;
       }

413
       if(uid != (uid_t)-1 && gid != (gid_t)-1) {
414 415 416 417 418
#ifdef HAVE_SETRESGID
           if(!setresgid(gid, gid, gid)) {
#else
           if(!setgid(gid)) {
#endif
419
               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
420
           } else {
421
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
422
           }
423 424 425 426
           if(!initgroups(conf->user, gid))
               fprintf(stdout, "Changed supplementary groups based on user: %s.\n", conf->user);
	   else
               fprintf(stdout, "Error changing supplementary groups: %s.\n", strerror(errno));
427 428 429 430 431
#ifdef HAVE_SETRESUID
           if(!setresuid(uid, uid, uid)) {
#else
           if(!setuid(uid)) {
#endif
432
               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
433
           } else {
434
               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
435
           }
436
       }
437 438
   }
#endif
439
}
440
#endif
Philipp Schafft's avatar
Philipp Schafft committed
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
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();
   }
}

482
#ifdef WIN32_SERVICE
Philipp Schafft's avatar
Philipp Schafft committed
483 484 485 486
int mainService(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
Jack Moffitt's avatar
Jack Moffitt committed
487
{
488 489
    int res, ret;
    char filename[512];
490
    char pbuf[1024];
491 492 493 494

    /* parse the '-c icecast.xml' option
    ** only, so that we can read a configfile
    */
495
    res = _parse_config_opts(argc, argv, filename, 512);
496
    if (res == 1) {
497
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
498
        /* startup all the modules */
499
        initialize_subsystems();
500 501 502 503 504
        if (!_start_logging_stdout()) {
            _fatal_error("FATAL: Could not start logging on stderr.");
            shutdown_subsystems();
            return 1;
        }
505
#endif
506
        /* parse the config file */
Michael Smith's avatar
Michael Smith committed
507
        config_get_config();
508
        ret = config_initial_parse_file(filename);
Michael Smith's avatar
Michael Smith committed
509
        config_release_config();
510
        if (ret < 0) {
511 512 513 514
            memset(pbuf, '\000', sizeof(pbuf));
            snprintf(pbuf, sizeof(pbuf)-1, 
                "FATAL: error parsing config file (%s)", filename);
            _fatal_error(pbuf);
515 516
            switch (ret) {
            case CONFIG_EINSANE:
517
                _fatal_error("filename was null or blank");
518 519
                break;
            case CONFIG_ENOROOT:
520
                _fatal_error("no root element found");
521 522
                break;
            case CONFIG_EBADROOT:
523
                _fatal_error("root element is not <icecast>");
524 525
                break;
            default:
526
                _fatal_error("XML config parsing error");
527 528
                break;
            }
529
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
530 531
            shutdown_subsystems();
#endif
532
            return 1;
533 534 535 536 537 538 539 540
        }
    } else if (res == -1) {
        _print_usage();
        return 1;
    }
    
    /* override config file options with commandline options */
    config_parse_cmdline(argc, argv);
Jack Moffitt's avatar
Jack Moffitt committed
541

542 543
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
544
        _fatal_error("Server startup failed. Exiting");
545
        shutdown_subsystems();
546 547
        return 1;
    }
548

549
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
550
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
551
#endif
552 553

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

556
#ifdef HAVE_SETUID 
557 558 559 560
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
    if(!getuid()) /* Running as root! Don't allow this */
    {
561
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
562
        fprintf(stderr, "Use the changeowner directive in the config file\n");
563
        shutdown_subsystems();
564 565 566 567 568 569 570
        return 1;
    }
#endif

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

571
    if (!_start_logging()) {
572
        _fatal_error("FATAL: Could not start logging");
573
        shutdown_subsystems();
574 575
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
576

577
    ICECAST_LOG_INFO("%s server started", ICECAST_VERSION_STRING);
578
    __log_system_name();
Jack Moffitt's avatar
Jack Moffitt committed
579

580
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
581

582
    /* let her rip */
583
    global.running = ICECAST_RUNNING;
584 585 586 587

    /* Startup yp thread */
    yp_initialize();

588 589
    /* Do this after logging init */
    slave_initialize();
590
    auth_initialise ();
591

592
    _server_proc();
Jack Moffitt's avatar
Jack Moffitt committed
593

594
    ICECAST_LOG_INFO("Shutting down");
595
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
596 597
    shutdown_subsystems();
#endif
598 599 600 601 602 603
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

604
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
605 606 607
}