main.c 15.7 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

Marvin Scholz's avatar
Marvin Scholz committed
44 45 46 47 48
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/net/sock.h"
#include "common/net/resolver.h"
#include "common/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"
76
#include "event.h"
Jack Moffitt's avatar
Jack Moffitt committed
77

78 79
#include <libxml/xmlmemory.h>

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

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

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

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

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

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

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

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

143 144 145 146 147 148
    global_shutdown();
    connection_shutdown();
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
149

150 151 152 153
#ifdef HAVE_CURL
    curl_global_cleanup();
#endif

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

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

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

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

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

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

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

215
    log_set_level(errorlog, ICECAST_LOGLEVEL_WARN);
216 217 218 219

    return 1;
}

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

228
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
229

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

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

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

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

    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;
284 285 286
        if (config->logsize)
            log_set_trigger (playlistlog, config->logsize);
        log_set_archive_timestamp(playlistlog, config->logarchive);
287 288 289
    } else {
        playlistlog = -1;
    }
290

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

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


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

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

311
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
312 313
}

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

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

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

327 328 329 330 331 332 333 334 335 336 337 338
    /* 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);
        }
    }

339
    return 1;
340
}
Jack Moffitt's avatar
Jack Moffitt committed
341

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

353
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
354 355
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

582
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
583

584
    /* let her rip */
585
    global.running = ICECAST_RUNNING;
586 587 588 589

    /* Startup yp thread */
    yp_initialize();

590 591
    /* Do this after logging init */
    slave_initialize();
592
    auth_initialise ();
593
    event_initialise();
594

595
    event_emit_global("icecast-start");
596
    _server_proc();
597
    event_emit_global("icecast-stop");
Jack Moffitt's avatar
Jack Moffitt committed
598

599
    ICECAST_LOG_INFO("Shutting down");
600
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
601 602
    shutdown_subsystems();
#endif
603 604 605 606 607 608
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

609
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
610 611 612
}