main.c 10.9 KB
Newer Older
1
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 3 4 5
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
6 7 8
#include <stdio.h>
#include <string.h>

9 10 11 12
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

Karl Heyes's avatar
Karl Heyes committed
13 14 15 16 17
#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
18

19 20 21 22 23 24
#ifdef CHUID
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#endif
Jack Moffitt's avatar
Jack Moffitt committed
25

26
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
27 28 29 30 31 32 33
#include "sighandler.h"

#include "global.h"
#include "os.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
34
#include "slave.h"
Jack Moffitt's avatar
Jack Moffitt committed
35 36
#include "stats.h"
#include "logging.h"
37
#include "xslt.h"
38
#include "fserve.h"
brendan's avatar
brendan committed
39
#ifdef USE_YP
40
#include "geturl.h"
41
#include "yp.h"
42
#endif
Jack Moffitt's avatar
Jack Moffitt committed
43

44 45
#include <libxml/xmlmemory.h>

46
#ifdef _WIN32
47 48
/* For getpid() */
#include <process.h>
49
#define snprintf _snprintf
50
#define getpid _getpid
51
#endif
52

Jack Moffitt's avatar
Jack Moffitt committed
53 54 55
#undef CATMODULE
#define CATMODULE "main"

56 57 58 59 60 61 62 63 64
static void _fatal_error(char *perr)
{
#ifdef WIN32
    MessageBox(NULL, perr, "Error", MB_OK);
#else
    fprintf(stdout, "%s\n", perr);
#endif
}

65
static void _print_usage()
Jack Moffitt's avatar
Jack Moffitt committed
66
{
67 68 69 70 71 72
    printf("usage: icecast [-h -b -v] -c <file>\n");
    printf("options:\n");
	printf("\t-c <file>\tSpecify configuration file\n");
	printf("\t-h\t\tDisplay usage\n");
	printf("\t-v\t\tDisplay version info\n");
	printf("\t-b\t\tRun icecast in the background\n");
73
    printf("\n");
Jack Moffitt's avatar
Jack Moffitt committed
74 75
}

76 77
static void _stop_logging(void)
{
78 79
    log_close(errorlog);
    log_close(accesslog);
80 81
}

82
static void _initialize_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
83
{
84 85 86 87 88 89 90 91
    log_initialize();
    thread_initialize();
    sock_initialize();
    resolver_initialize();
    config_initialize();
    connection_initialize();
    global_initialize();
    refbuf_initialize();
92
    xslt_initialize();
brendan's avatar
brendan committed
93
#ifdef USE_YP
94
    curl_initialize();
95
#endif
Jack Moffitt's avatar
Jack Moffitt committed
96 97
}

98
static void _shutdown_subsystems(void)
Jack Moffitt's avatar
Jack Moffitt committed
99
{
brendan's avatar
brendan committed
100
#ifdef USE_YP
101
    curl_shutdown();
102
#endif
103
    fserve_shutdown();
104
    xslt_shutdown();
105 106 107
    refbuf_shutdown();
    stats_shutdown();
    slave_shutdown();
108 109

    /* Now that these are done, we can stop the loggers. */
110
    _stop_logging();
111

112 113 114 115 116 117 118
    global_shutdown();
    connection_shutdown();
    config_shutdown();
    resolver_shutdown();
    sock_shutdown();
    thread_shutdown();
    log_shutdown();
119 120

    xmlCleanupParser();
Jack Moffitt's avatar
Jack Moffitt committed
121 122
}

123
static int _parse_config_file(int argc, char **argv, char *filename, int size)
Jack Moffitt's avatar
Jack Moffitt committed
124
{
125 126
    int i = 1;
    int    processID = 0;
Jack Moffitt's avatar
Jack Moffitt committed
127

128
    if (argc < 2) return -1;
Jack Moffitt's avatar
Jack Moffitt committed
129

130 131
    while (i < argc) {
        if (strcmp(argv[i], "-b") == 0) {
132
#ifndef WIN32
133 134 135 136 137
                fprintf(stdout, "Starting icecast2\nDetaching from the console\n");
                if ((processID = (int)fork()) > 0) {
                        /* exit the parent */
                        _exit(0);
                }
138
#endif
139
        }
140 141 142 143
        if (strcmp(argv[i], "-v") == 0) {
            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
			exit(0);
		}
144 145 146
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 < argc) {
                strncpy(filename, argv[i + 1], size-1);
147
                filename[size-1] = 0;
148 149 150 151 152 153 154 155 156
                return 1;
            } else {
                return -1;
            }
        }
        i++;
    }

    return -1;
Jack Moffitt's avatar
Jack Moffitt committed
157 158
}

159
static int _start_logging(void)
Jack Moffitt's avatar
Jack Moffitt committed
160
{
161 162
    char fn_error[FILENAME_MAX];
    char fn_access[FILENAME_MAX];
163 164
    char pbuf[1024];

165
    ice_config_t *config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
166

167
    if(strcmp(config->error_log, "-")) {
168
        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
169
        errorlog = log_open(fn_error);
170 171 172
    } else {
        errorlog = log_open_file(stderr);
    }
173
    if(strcmp(config->access_log, "-")) {
174
        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
175
        accesslog = log_open(fn_access);
176 177 178
    } else {
        accesslog = log_open_file(stderr);
    }
179 180 181 182
    
    log_set_level(errorlog, config->loglevel);
    log_set_level(accesslog, 4);

183 184 185 186 187 188 189 190
    if (errorlog < 0) {
        _fatal_error("FATAL: could not open error logging");
    }
    if (accesslog < 0) {
        memset(pbuf, '\000', sizeof(pbuf));
        snprintf(pbuf, sizeof(pbuf)-1, "FATAL: could not open access logging");
        _fatal_error(pbuf);
    }
191 192 193
    if (errorlog >= 0 && accesslog >= 0) return 1;
    
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
194 195
}

196
static int _setup_sockets(void)
Jack Moffitt's avatar
Jack Moffitt committed
197
{
198
    ice_config_t *config;
199 200 201
    int i = 0;
    int ret = 0;
    int successful = 0;
202
    char pbuf[1024];
Jack Moffitt's avatar
Jack Moffitt committed
203

204
    config = config_get_config_unlocked();
Jack Moffitt's avatar
Jack Moffitt committed
205

206 207 208 209 210 211 212
    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);

213
        if (global.serversock[i] == SOCK_ERROR) {
214 215 216 217 218
            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);
219 220 221 222 223 224 225
            return 0;
        }
        else {
            ret = 1;
            successful++;
        }
    }
226

227
    global.server_sockets = successful;
228 229
    
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
230 231
}

232
static int _start_listening(void)
Jack Moffitt's avatar
Jack Moffitt committed
233
{
234 235
    int i;
    for(i=0; i < global.server_sockets; i++) {
236 237
        if (sock_listen(global.serversock[i], ICE_LISTEN_QUEUE) == SOCK_ERROR)
            return 0;
Jack Moffitt's avatar
Jack Moffitt committed
238

239
        sock_set_blocking(global.serversock[i], SOCK_NONBLOCK);
240
    }
Jack Moffitt's avatar
Jack Moffitt committed
241

242
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
243 244
}

245
/* bind the socket and start listening */
246
static int _server_proc_init(void)
Jack Moffitt's avatar
Jack Moffitt committed
247
{
248 249
    if (!_setup_sockets())
        return 0;
Jack Moffitt's avatar
Jack Moffitt committed
250

251
    if (!_start_listening()) {
252
        _fatal_error("Failed trying to listen on server socket");
253 254
        return 0;
    }
255 256

    return 1;
257
}
Jack Moffitt's avatar
Jack Moffitt committed
258

259 260 261
/* this is the heart of the beast */
static void _server_proc(void)
{
262 263
    int i;

264
    connection_accept_loop();
Jack Moffitt's avatar
Jack Moffitt committed
265

266
    for(i=0; i < MAX_LISTEN_SOCKETS; i++)
267
        sock_close(global.serversock[i]);
Jack Moffitt's avatar
Jack Moffitt committed
268 269
}

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

Michael Smith's avatar
Michael Smith committed
273
static void _ch_root_uid_setup(void)
274
{
275
   ice_config_t *conf = config_get_config_unlocked();
276 277 278 279 280 281 282 283
#ifdef CHUID
   struct passwd *user;
   struct group *group;
   uid_t uid=-1;
   gid_t gid=-1;

   if(conf->chuid)
   {
284 285 286 287 288 289 290 291 292
       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);
293

294 295 296 297 298
           if(group)
               gid = group->gr_gid;
           else
               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
       }
299 300 301 302
   }
#endif

#ifdef CHROOT
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
   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
321

322 323 324 325 326 327 328 329
   if(conf->chuid)
   {
       if(getuid()) /* root check */
       {
           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
           return;
       }

330 331
       if(gid != -1) {
           if(!setgid(gid))
332
               fprintf(stdout, "Changed groupid to %i.\n", gid);
333 334 335
           else
               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
       }
336

337 338
       if(uid != -1) {
           if(!setuid(uid))
339
               fprintf(stdout, "Changed userid to %i.\n", uid);
340 341 342
           else
               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
       }
343 344
   }
#endif
345
}
346

Jack Moffitt's avatar
Jack Moffitt committed
347 348
int main(int argc, char **argv)
{
349
    int res, ret;
350 351
    ice_config_t *config;
    char *pidfile = NULL;
352
    char filename[512];
353
    char pbuf[1024];
354 355 356 357 358 359 360 361 362 363

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

        /* parse the config file */
364
        config_get_config();
365
        ret = config_initial_parse_file(filename);
366
        config_release_config();
367
        if (ret < 0) {
368 369 370 371
            memset(pbuf, '\000', sizeof(pbuf));
            snprintf(pbuf, sizeof(pbuf)-1, 
                "FATAL: error parsing config file (%s)", filename);
            _fatal_error(pbuf);
372 373
            switch (ret) {
            case CONFIG_EINSANE:
374
                _fatal_error("filename was null of blank");
375 376
                break;
            case CONFIG_ENOROOT:
377
                _fatal_error("no root element found");
378 379
                break;
            case CONFIG_EBADROOT:
380
                _fatal_error("root element is not <icecast>");
381 382
                break;
            default:
383
                _fatal_error("XML config parsing error");
384 385
                break;
            }
386 387
            _shutdown_subsystems();
            return 1;
388 389 390 391 392 393 394 395
        }
    } 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
396

397 398
    /* Bind socket, before we change userid */
    if(!_server_proc_init()) {
399
        _fatal_error("Server startup failed. Exiting");
400 401 402
        _shutdown_subsystems();
        return 1;
    }
403

Michael Smith's avatar
Michael Smith committed
404
    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
405 406

    stats_initialize(); /* We have to do this later on because of threading */
407
    fserve_initialize(); /* This too */
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423

#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 */
    {
        fprintf(stderr, "WARNING: You should not run icecast2 as root\n");
        fprintf(stderr, "Use the changeowner directive in the config file\n");
        _shutdown_subsystems();
        return 1;
    }
#endif

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

424
    if (!_start_logging()) {
425
        _fatal_error("FATAL: Could not start logging");
426 427 428
        _shutdown_subsystems();
        return 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
429

430 431 432 433 434 435 436 437 438 439 440 441
    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", getpid());
            fclose (f);
        }
    }
442 443 444
    /* Do this after logging init */
    slave_initialize();

445
    INFO0("icecast server started");
Jack Moffitt's avatar
Jack Moffitt committed
446

447
    /* REM 3D Graphics */
Jack Moffitt's avatar
Jack Moffitt committed
448

449 450
    /* let her rip */
    global.running = ICE_RUNNING;
451 452 453 454 455 456

#ifdef USE_YP
    /* Startup yp thread */
    yp_initialize();
#endif

457
    _server_proc();
Jack Moffitt's avatar
Jack Moffitt committed
458

459
    INFO0("Shutting down");
Jack Moffitt's avatar
Jack Moffitt committed
460

461
    _shutdown_subsystems();
Jack Moffitt's avatar
Jack Moffitt committed
462

463 464 465 466 467 468
    if (pidfile)
    {
        remove (pidfile);
        free (pidfile);
    }

469
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
}