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

Karl Heyes's avatar
Karl Heyes committed
40 41 42 43 44
#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
45

46
#if HAVE_SYS_TYPES_H
47
#include <sys/types.h>
48 49
#endif
#if HAVE_GRP_H
50
#include <grp.h>
51 52
#endif
#if HAVE_PWD_H
53 54
#include <pwd.h>
#endif
Jack Moffitt's avatar
Jack Moffitt committed
55

56
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
57 58 59
#include "sighandler.h"

#include "global.h"
60
#include "compat.h"
Jack Moffitt's avatar
Jack Moffitt committed
61 62 63
#include "connection.h"
#include "refbuf.h"
#include "client.h"
64
#include "slave.h"
Jack Moffitt's avatar
Jack Moffitt committed
65 66
#include "stats.h"
#include "logging.h"
67
#include "xslt.h"
68
#include "fserve.h"
69
#include "yp.h"
70
#include "auth.h"
Jack Moffitt's avatar
Jack Moffitt committed
71

72 73
#include <libxml/xmlmemory.h>

Jack Moffitt's avatar
Jack Moffitt committed
74 75 76
#undef CATMODULE
#define CATMODULE "main"

77 78 79
static int background;
static char *pidfile = NULL;

80
static void _fatal_error(const char *perr)
81
{
82 83 84
#ifdef WIN32_SERVICE
    MessageBox(NULL, perr, "Error", MB_SERVICE_NOTIFICATION);
#elif defined(WIN32)
85 86 87 88 89 90
    MessageBox(NULL, perr, "Error", MB_OK);
#else
    fprintf(stdout, "%s\n", perr);
#endif
}

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

103 104
static void _stop_logging(void)
{
105 106
    log_close(errorlog);
    log_close(accesslog);
107
    log_close(playlistlog);
108 109
}

110
void initialize_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
111
{
112 113 114 115 116 117 118 119
    log_initialize();
    thread_initialize();
    sock_initialize();
    resolver_initialize();
    config_initialize();
    connection_initialize();
    global_initialize();
    refbuf_initialize();
120

121
    xslt_initialize();
122
#ifdef HAVE_CURL_GLOBAL_INIT
123 124
    curl_global_init (CURL_GLOBAL_ALL);
#endif
Jack Moffitt's avatar
Jack Moffitt committed
125 126
}

127
void shutdown_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
128
{
129
    fserve_shutdown();
130 131
    refbuf_shutdown();
    slave_shutdown();
132
    auth_shutdown();
133
    yp_shutdown();
134
    stats_shutdown();
135

136 137 138 139 140 141
    global_shutdown();
    connection_shutdown();
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
142

143 144 145 146
#ifdef HAVE_CURL
    curl_global_cleanup();
#endif

147 148
    /* Now that these are done, we can stop the loggers. */
    _stop_logging();
149
    log_shutdown();
150
    xslt_shutdown();
Jack Moffitt's avatar
Jack Moffitt committed
151 152
}

153
static int _parse_config_opts(int argc, char **argv, char *filename, int size)
Jack Moffitt's avatar
Jack Moffitt committed
154
{
155
    int i = 1;
156 157
    int config_ok = 0;

158
    background = 0;
159
    if (argc < 2) return -1;
Jack Moffitt's avatar
Jack Moffitt committed
160

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

185 186 187
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 < argc) {
                strncpy(filename, argv[i + 1], size-1);
188
                filename[size-1] = 0;
189
                config_ok = 1;
190 191 192 193
            } else {
                return -1;
            }
        }
194
        i++;
195 196
    }

197 198 199 200
    if(config_ok)
        return 1;
    else
        return -1;
Jack Moffitt's avatar
Jack Moffitt committed
201 202
}

203 204 205 206 207 208 209 210 211 212
static int _start_logging_stdout(void) {
    errorlog = log_open_file(stderr);
    if ( errorlog < 0 )
        return 0;

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

    return 1;
}

213
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
214
{
215 216
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
217
    char fn_playlist[FILENAME_MAX];
218
    char buf[1024];
219
    int log_to_stderr;
220

221
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
222

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

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

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

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

    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;
277 278 279
        if (config->logsize)
            log_set_trigger (playlistlog, config->logsize);
        log_set_archive_timestamp(playlistlog, config->logarchive);
280 281 282
    } else {
        playlistlog = -1;
    }
283

284 285
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);
286
    log_set_level(playlistlog, 4);
287 288 289 290

    if (errorlog >= 0 && accesslog >= 0) return 1;
    
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
291 292 293
}


294
static int _start_listening(void)
Jack Moffitt's avatar
Jack Moffitt committed
295
{
296 297
    int i;
    for(i=0; i < global.server_sockets; i++) {
298
        if (sock_listen(global.serversock[i], ICECAST_LISTEN_QUEUE) == SOCK_ERROR)
299
            return 0;
Jack Moffitt's avatar
Jack Moffitt committed
300

301
        sock_set_blocking(global.serversock[i], 0);
302
    }
Jack Moffitt's avatar
Jack Moffitt committed
303

304
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
305 306
}

307
/* bind the socket and start listening */
308
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
309
{
310
    ice_config_t *config = config_get_config_unlocked();
311

312
    if (connection_setup_sockets (config) < 1)
313
        return 0;
Jack Moffitt's avatar
Jack Moffitt committed
314

315
    if (!_start_listening()) {
316
        _fatal_error("Failed trying to listen on server socket");
317 318
        return 0;
    }
319

320 321 322 323 324 325 326 327 328 329 330 331
    /* 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);
        }
    }

332
    return 1;
333
}
Jack Moffitt's avatar
Jack Moffitt committed
334

335 336 337
/* this is the heart of the beast */
static void _server_proc(void)
{
338 339 340 341 342 343
    if (background)
    {
        fclose (stdin);
        fclose (stdout);
        fclose (stderr);
    }
344
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
345

346
    connection_setup_sockets (NULL);
Jack Moffitt's avatar
Jack Moffitt committed
347 348
}

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

   if(conf->chuid)
   {
363 364 365 366 367 368 369 370 371
       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);
372

373 374 375 376 377
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
378 379 380
   }
#endif

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

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

409
       if(uid != (uid_t)-1 && gid != (gid_t)-1) {
410
           if(!setgid(gid))
411
               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
412 413
           else
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
414 415 416 417
           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));
418
           if(!setuid(uid))
419
               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
420 421 422
           else
               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
       }
423 424
   }
#endif
425
}
426
#endif
Philipp Schafft's avatar
Philipp Schafft committed
427

428
#ifdef WIN32_SERVICE
Philipp Schafft's avatar
Philipp Schafft committed
429 430 431 432
int mainService(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
Jack Moffitt's avatar
Jack Moffitt committed
433
{
434 435
    int res, ret;
    char filename[512];
436
    char pbuf[1024];
437 438 439 440

    /* parse the '-c icecast.xml' option
    ** only, so that we can read a configfile
    */
441
    res = _parse_config_opts(argc, argv, filename, 512);
442
    if (res == 1) {
443
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
444
        /* startup all the modules */
445
        initialize_subsystems();
446 447 448 449 450
        if (!_start_logging_stdout()) {
            _fatal_error("FATAL: Could not start logging on stderr.");
            shutdown_subsystems();
            return 1;
        }
451
#endif
452
        /* parse the config file */
Michael Smith's avatar
Michael Smith committed
453
        config_get_config();
454
        ret = config_initial_parse_file(filename);
Michael Smith's avatar
Michael Smith committed
455
        config_release_config();
456
        if (ret < 0) {
457 458 459 460
            memset(pbuf, '\000', sizeof(pbuf));
            snprintf(pbuf, sizeof(pbuf)-1, 
                "FATAL: error parsing config file (%s)", filename);
            _fatal_error(pbuf);
461 462
            switch (ret) {
            case CONFIG_EINSANE:
463
                _fatal_error("filename was null or blank");
464 465
                break;
            case CONFIG_ENOROOT:
466
                _fatal_error("no root element found");
467 468
                break;
            case CONFIG_EBADROOT:
469
                _fatal_error("root element is not <icecast>");
470 471
                break;
            default:
472
                _fatal_error("XML config parsing error");
473 474
                break;
            }
475
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
476 477
            shutdown_subsystems();
#endif
478
            return 1;
479 480 481 482 483 484 485 486
        }
    } 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
487

488 489
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
490
        _fatal_error("Server startup failed. Exiting");
491
        shutdown_subsystems();
492 493
        return 1;
    }
494

495
#if defined(HAVE_SETUID) || defined(HAVE_CHROOT) || defined(HAVE_SETUID)
Michael Smith's avatar
Michael Smith committed
496
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
497
#endif
498 499

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

502
#ifdef HAVE_SETUID 
503 504 505 506
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
    if(!getuid()) /* Running as root! Don't allow this */
    {
507
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
508
        fprintf(stderr, "Use the changeowner directive in the config file\n");
509
        shutdown_subsystems();
510 511 512 513 514 515 516
        return 1;
    }
#endif

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

517
    if (!_start_logging()) {
518
        _fatal_error("FATAL: Could not start logging");
519
        shutdown_subsystems();
520 521
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
522

523
    ICECAST_LOG_INFO("%s server started", ICECAST_VERSION_STRING);
Jack Moffitt's avatar
Jack Moffitt committed
524

525
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
526

527
    /* let her rip */
528
    global.running = ICECAST_RUNNING;
529 530 531 532

    /* Startup yp thread */
    yp_initialize();

533 534
    /* Do this after logging init */
    slave_initialize();
535
    auth_initialise ();
536

537
    _server_proc();
Jack Moffitt's avatar
Jack Moffitt committed
538

539
    ICECAST_LOG_INFO("Shutting down");
540
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
541 542
    shutdown_subsystems();
#endif
543 544 545 546 547 548
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

549
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
550 551 552
}