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
266
        snprintf(buf, sizeof(buf) - 1,
            "FATAL: could not open access logging (%s): %s",
            log_to_stderr ? "standard error" : fn_access,
            strerror(errno));
267
268
        _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
}