main.c 12.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

13
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 15 16 17
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
18 19 20
#include <stdio.h>
#include <string.h>

21 22 23 24
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

Karl Heyes's avatar
Karl Heyes committed
25 26 27 28 29
#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
30

31 32 33 34 35 36
#ifdef CHUID
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#endif
Jack Moffitt's avatar
Jack Moffitt committed
37

38
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
39 40 41 42 43 44 45
#include "sighandler.h"

#include "global.h"
#include "os.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
46
#include "slave.h"
Jack Moffitt's avatar
Jack Moffitt committed
47 48
#include "stats.h"
#include "logging.h"
49
#include "xslt.h"
50
#include "fserve.h"
51
#include "yp.h"
52
#include "auth.h"
Jack Moffitt's avatar
Jack Moffitt committed
53

54 55
#include <libxml/xmlmemory.h>

56
#ifdef _WIN32
57 58
/* For getpid() */
#include <process.h>
59
#define snprintf _snprintf
60
#define getpid _getpid
61
#endif
62

Jack Moffitt's avatar
Jack Moffitt committed
63 64 65
#undef CATMODULE
#define CATMODULE "main"

66 67 68
static int background;
static char *pidfile = NULL;

69 70 71 72 73 74 75 76 77
static void _fatal_error(char *perr)
{
#ifdef WIN32
    MessageBox(NULL, perr, "Error", MB_OK);
#else
    fprintf(stdout, "%s\n", perr);
#endif
}

78
static void _print_usage()
Jack Moffitt's avatar
Jack Moffitt committed
79
{
80
    printf(ICECAST_VERSION_STRING "\n\n");
81
    printf("usage: icecast [-b -v] -c <file>\n");
82
    printf("options:\n");
83 84 85
    printf("\t-c <file>\tSpecify configuration file\n");
    printf("\t-v\t\tDisplay version info\n");
    printf("\t-b\t\tRun icecast in the background\n");
86
    printf("\n");
Jack Moffitt's avatar
Jack Moffitt committed
87 88
}

89 90
static void _stop_logging(void)
{
91 92
    log_close(errorlog);
    log_close(accesslog);
93
    log_close(playlistlog);
94 95
}

96
static void _initialize_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
97
{
98 99 100 101 102 103 104 105
    log_initialize();
    thread_initialize();
    sock_initialize();
    resolver_initialize();
    config_initialize();
    connection_initialize();
    global_initialize();
    refbuf_initialize();
106
    xslt_initialize();
Jack Moffitt's avatar
Jack Moffitt committed
107 108
}

109
static void _shutdown_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
110
{
111
    fserve_shutdown();
112
    xslt_shutdown();
113 114
    refbuf_shutdown();
    slave_shutdown();
115
    auth_shutdown();
116
    yp_shutdown();
117
    stats_shutdown();
118

119 120 121 122 123 124
    global_shutdown();
    connection_shutdown();
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
125 126 127

    /* Now that these are done, we can stop the loggers. */
    _stop_logging();
128
    log_shutdown();
129 130

    xmlCleanupParser();
Jack Moffitt's avatar
Jack Moffitt committed
131 132
}

133
static int _parse_config_opts(int argc, char **argv, char *filename, int size)
Jack Moffitt's avatar
Jack Moffitt committed
134
{
135
    int i = 1;
136 137
    int config_ok = 0;

138
    background = 0;
139
    if (argc < 2) return -1;
Jack Moffitt's avatar
Jack Moffitt committed
140

141 142
    while (i < argc) {
        if (strcmp(argv[i], "-b") == 0) {
143
#ifndef WIN32
Michael Smith's avatar
Michael Smith committed
144
            pid_t pid;
145
            fprintf(stdout, "Starting icecast2\nDetaching from the console\n");
Michael Smith's avatar
Michael Smith committed
146 147 148 149

            pid = fork();

            if (pid > 0) {
150
                /* exit the parent */
151 152
                exit(0);
            }
Michael Smith's avatar
Michael Smith committed
153
            else if(pid < 0) {
154 155
                fprintf(stderr, "FATAL: Unable to fork child!");
                exit(1);
156
            }
157
            background = 1;
158
#endif
159
        }
160 161
        if (strcmp(argv[i], "-v") == 0) {
            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
162 163 164
            exit(0);
        }

165 166 167
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 < argc) {
                strncpy(filename, argv[i + 1], size-1);
168
                filename[size-1] = 0;
169
                config_ok = 1;
170 171 172 173 174 175 176
            } else {
                return -1;
            }
        }
        i++;
    }

177 178 179 180
    if(config_ok)
        return 1;
    else
        return -1;
Jack Moffitt's avatar
Jack Moffitt committed
181 182
}

183
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
184
{
185 186
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
187
    char fn_playlist[FILENAME_MAX];
188
    char buf[1024];
189
    int log_to_stderr;
190

191
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
192

193
    if(strcmp(config->error_log, "-")) {
194
        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
195
        errorlog = log_open(fn_error);
196
        log_to_stderr = 0;
197 198
    } else {
        errorlog = log_open_file(stderr);
199
        log_to_stderr = 1;
200
    }
201 202 203

    if (errorlog < 0) {
        buf[sizeof(buf)-1] = 0;
204 205 206
        snprintf(buf, sizeof(buf)-1, 
                "FATAL: could not open error logging (%s): %s",
                log_to_stderr?"standard error":fn_error,
207 208 209 210 211
                strerror(errno));
        _fatal_error(buf);
    }
    log_set_level(errorlog, config->loglevel);

212
    if(strcmp(config->access_log, "-")) {
213
        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
214
        accesslog = log_open(fn_access);
215
        log_to_stderr = 0;
216 217
    } else {
        accesslog = log_open_file(stderr);
218
        log_to_stderr = 1;
219
    }
220 221 222

    if (accesslog < 0) {
        buf[sizeof(buf)-1] = 0;
223 224 225
        snprintf(buf, sizeof(buf)-1, 
                "FATAL: could not open access logging (%s): %s",
                log_to_stderr?"standard error":fn_access,
226 227 228
                strerror(errno));
        _fatal_error(buf);
    }
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

    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;
    } else {
        playlistlog = -1;
    }
245

246 247
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);
248
    log_set_level(playlistlog, 4);
249 250 251 252

    if (errorlog >= 0 && accesslog >= 0) return 1;
    
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
253 254
}

255
static int _setup_sockets(void)
Jack Moffitt's avatar
Jack Moffitt committed
256
{
257
    ice_config_t *config;
258 259 260
    int i = 0;
    int ret = 0;
    int successful = 0;
261
    char pbuf[1024];
Jack Moffitt's avatar
Jack Moffitt committed
262

263
    config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
264

265 266 267 268 269 270 271
    for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
        if(config->listeners[i].port <= 0)
            break;

        global.serversock[i] = sock_get_server_socket(
                config->listeners[i].port, config->listeners[i].bind_address);

272
        if (global.serversock[i] == SOCK_ERROR) {
273 274 275 276 277
            memset(pbuf, '\000', sizeof(pbuf));
            snprintf(pbuf, sizeof(pbuf)-1, 
                "Could not create listener socket on port %d", 
                config->listeners[i].port);
            _fatal_error(pbuf);
278 279 280 281 282 283 284
            return 0;
        }
        else {
            ret = 1;
            successful++;
        }
    }
285

286
    global.server_sockets = successful;
287 288
    
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
289 290
}

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

298
        sock_set_blocking(global.serversock[i], SOCK_NONBLOCK);
299
    }
Jack Moffitt's avatar
Jack Moffitt committed
300

301
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
302 303
}

304
/* bind the socket and start listening */
305
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
306
{
307 308
    ice_config_t *config;

309 310
    if (!_setup_sockets())
        return 0;
Jack Moffitt's avatar
Jack Moffitt committed
311

312
    if (!_start_listening()) {
313
        _fatal_error("Failed trying to listen on server socket");
314 315
        return 0;
    }
316

317 318 319 320 321 322 323 324 325 326 327 328 329
    config = config_get_config_unlocked();
    /* 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);
        }
    }

330
    return 1;
331
}
Jack Moffitt's avatar
Jack Moffitt committed
332

333 334 335
/* this is the heart of the beast */
static void _server_proc(void)
{
336 337
    int i;

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
    for(i=0; i < MAX_LISTEN_SOCKETS; i++)
347
        sock_close(global.serversock[i]);
Jack Moffitt's avatar
Jack Moffitt committed
348 349
}

350
/* chroot the process. Watch out - we need to do this before starting other
351
 * threads. Change uid as well, after figuring out uid _first_ */
352

Michael Smith's avatar
Michael Smith committed
353
static void _ch_root_uid_setup(void)
354
{
355
   ice_config_t *conf = config_get_config_unlocked();
356 357 358 359 360 361 362 363
#ifdef CHUID
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

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

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

#ifdef CHROOT
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
   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
#ifdef CHUID
401

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

410 411
       if(gid != -1) {
           if(!setgid(gid))
412
               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
413 414 415
           else
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
       }
416

417 418
       if(uid != -1) {
           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

Jack Moffitt's avatar
Jack Moffitt committed
427 428
int main(int argc, char **argv)
{
429 430
    int res, ret;
    char filename[512];
431
    char pbuf[1024];
432 433 434 435

    /* parse the '-c icecast.xml' option
    ** only, so that we can read a configfile
    */
436
    res = _parse_config_opts(argc, argv, filename, 512);
437 438 439 440 441
    if (res == 1) {
        /* startup all the modules */
        _initialize_subsystems();

        /* parse the config file */
442
        config_get_config();
443
        ret = config_initial_parse_file(filename);
444
        config_release_config();
445
        if (ret < 0) {
446 447 448 449
            memset(pbuf, '\000', sizeof(pbuf));
            snprintf(pbuf, sizeof(pbuf)-1, 
                "FATAL: error parsing config file (%s)", filename);
            _fatal_error(pbuf);
450 451
            switch (ret) {
            case CONFIG_EINSANE:
452
                _fatal_error("filename was null or blank");
453 454
                break;
            case CONFIG_ENOROOT:
455
                _fatal_error("no root element found");
456 457
                break;
            case CONFIG_EBADROOT:
458
                _fatal_error("root element is not <icecast>");
459 460
                break;
            default:
461
                _fatal_error("XML config parsing error");
462 463
                break;
            }
464 465
            _shutdown_subsystems();
            return 1;
466 467 468 469 470 471 472 473
        }
    } 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
474

475 476
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
477
        _fatal_error("Server startup failed. Exiting");
478 479 480
        _shutdown_subsystems();
        return 1;
    }
481

Michael Smith's avatar
Michael Smith committed
482
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
483 484

    stats_initialize(); /* We have to do this later on because of threading */
485
    fserve_initialize(); /* This too */
486 487 488 489 490 491

#ifdef CHUID 
    /* We'll only have getuid() if we also have setuid(), it's reasonable to
     * assume */
    if(!getuid()) /* Running as root! Don't allow this */
    {
492
        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
493 494 495 496 497 498 499 500 501
        fprintf(stderr, "Use the changeowner directive in the config file\n");
        _shutdown_subsystems();
        return 1;
    }
#endif

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

502
    if (!_start_logging()) {
503
        _fatal_error("FATAL: Could not start logging");
504 505 506
        _shutdown_subsystems();
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
507

508
    INFO0 (ICECAST_VERSION_STRING " server started");
Jack Moffitt's avatar
Jack Moffitt committed
509

510
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
511

512 513
    /* let her rip */
    global.running = ICE_RUNNING;
514 515 516 517

    /* Startup yp thread */
    yp_initialize();

518 519
    /* Do this after logging init */
    slave_initialize();
520
    auth_initialise ();
521

522
    _server_proc();
Jack Moffitt's avatar
Jack Moffitt committed
523

524
    INFO0("Shutting down");
Jack Moffitt's avatar
Jack Moffitt committed
525

526
    _shutdown_subsystems();
Jack Moffitt's avatar
Jack Moffitt committed
527

528 529 530 531 532 533
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

534
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
535 536 537
}