1 /* Icecast
2  *
3  * This program is distributed under the GNU General Public License, version 2.
4  * A copy of this license is included with this source.
5  *
6  * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7  *                      Michael Smith <msmith@xiph.org>,
8  *                      oddsock <oddsock@xiph.org>,
9  *                      Karl Heyes <karl@xiph.org>
10  *                      and others (see AUTHORS for details).
11  */
12 
13 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #ifdef WIN32
19 #include <winsock2.h>
20 #include <process.h>
21 #endif
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #ifdef HAVE_CURL
31 #include <curl/curl.h>
32 #endif
33 #include <git_hash.h>
34 
35 #include "thread/thread.h"
36 #include "avl/avl.h"
37 #include "net/sock.h"
38 #include "net/resolver.h"
39 #include "httpp/httpp.h"
40 
41 #ifdef CHUID
42 #include <sys/types.h>
43 #include <grp.h>
44 #include <pwd.h>
45 #endif
46 #ifdef HAVE_GETRLIMIT
47 #include <sys/resource.h>
48 #endif
49 
50 #include "cfgfile.h"
51 #include "sighandler.h"
52 
53 #include "global.h"
54 #include "compat.h"
55 #include "connection.h"
56 #include "refbuf.h"
57 #include "client.h"
58 #include "slave.h"
59 #include "stats.h"
60 #include "logging.h"
61 #include "xslt.h"
62 #include "fserve.h"
63 #include "auth.h"
64 
65 #include <libxml/xmlmemory.h>
66 
67 #undef CATMODULE
68 #define CATMODULE "main"
69 
70 static void _ch_root_uid_setup(void);
71 
72 static int background;
73 static char *pidfile = NULL;
74 
75 #define _fatal_error fatal_error
fatal_error(const char * perr)76 void fatal_error (const char *perr)
77 {
78 #if defined(WIN32)
79     MessageBox(NULL, perr, NULL, MB_SERVICE_NOTIFICATION);
80 #else
81     ERROR1("%s", perr);
82 #endif
83 }
84 
_print_usage(void)85 static void _print_usage(void)
86 {
87     printf("%s\n\n", ICECAST_VERSION_STRING);
88     printf("usage: icecast [-b -v] -c <file>\n");
89     printf("options:\n");
90     printf("\t-c <file>\tSpecify configuration file\n");
91     printf("\t-v\t\tDisplay version info\n");
92     printf("\t-b\t\tRun icecast in the background\n");
93     printf("\n");
94 }
95 
96 
initialize_subsystems(void)97 void initialize_subsystems(void)
98 {
99     global_initialize();
100     thread_initialize();
101     log_initialize_lib (thread_mtx_create_callback, thread_mtx_lock_callback);
102     errorlog = log_open_file (stderr);
103     sock_initialize();
104     resolver_initialize();
105     config_initialize();
106     connection_initialize();
107     refbuf_initialize();
108 
109     stats_initialize();
110     xslt_initialize();
111 #ifdef HAVE_CURL_GLOBAL_INIT
112     curl_global_init (CURL_GLOBAL_ALL);
113 #endif
114 }
115 
116 
shutdown_subsystems(void)117 void shutdown_subsystems(void)
118 {
119     connection_shutdown();
120     slave_shutdown();
121     xslt_shutdown();
122 
123     config_shutdown();
124     refbuf_shutdown();
125     resolver_shutdown();
126     sock_shutdown();
127 
128 #ifdef HAVE_CURL
129     curl_global_cleanup();
130 #endif
131 
132     /* Now that these are done, we can stop the loggers. */
133     log_shutdown();
134     global_shutdown();
135     thread_shutdown();
136 }
137 
_parse_config_opts(int argc,char ** argv,char * filename,int size)138 static int _parse_config_opts(int argc, char **argv, char *filename, int size)
139 {
140     int i = 1;
141     int config_ok = 0;
142 
143     background = 0;
144     if (argc < 2) return -1;
145 
146     while (i < argc) {
147         if (strcmp(argv[i], "-b") == 0) {
148 #ifndef WIN32
149             pid_t pid;
150             fprintf(stdout, "Starting icecast2\nDetaching from the console\n");
151 
152             pid = fork();
153 
154             if (pid > 0) {
155                 /* exit the parent */
156                 exit(0);
157             }
158             else if(pid < 0) {
159                 fprintf(stderr, "FATAL: Unable to fork child!");
160                 exit(1);
161             }
162             background = 1;
163 #endif
164         }
165         if (strcmp(argv[i], "-v") == 0) {
166             fprintf(stdout, "%s\n", ICECAST_VERSION_STRING "-" GIT_VERSION);
167             exit(0);
168         }
169 
170         if (strcmp(argv[i], "-c") == 0) {
171             if (i + 1 < argc) {
172                 strncpy(filename, argv[i + 1], size-1);
173                 filename[size-1] = 0;
174                 config_ok = 1;
175             } else {
176                 return -1;
177             }
178         }
179         i++;
180     }
181 
182     if(config_ok)
183         return 1;
184     else
185         return -1;
186 }
187 
188 
189 /* bind the socket and start listening */
server_proc_init(void)190 static int server_proc_init(void)
191 {
192     ice_config_t *config = config_get_config_unlocked();
193 
194     if (init_logging (config) < 0)
195         return 0;
196 
197     INFO2 ("%s server reading configuration from %s", ICECAST_VERSION_STRING, config->config_filename);
198 
199     if (connection_setup_sockets (config) == 0)
200         return 0;
201 
202     _ch_root_uid_setup(); /* Change user id and root if requested/possible */
203 
204     /* recreate the pid file */
205     if (config->pidfile)
206     {
207         FILE *f;
208         pidfile = strdup (config->pidfile);
209         if (pidfile && (f = fopen (config->pidfile, "w")) != NULL)
210         {
211             fprintf (f, "%d\n", (int)getpid());
212             fclose (f);
213         }
214     }
215 
216     return 1;
217 }
218 
219 
220 /* this is the heart of the beast */
server_process(void)221 void server_process (void)
222 {
223     INFO1 ("%s server started", ICECAST_VERSION_STRING);
224 
225     global.running = ICE_RUNNING;
226 
227     /* Do this after logging init */
228     auth_initialise ();
229 
230     if (background)
231     {
232         fclose (stdin);
233         fclose (stdout);
234         fclose (stderr);
235     }
236     slave_initialize();
237     INFO0("Shutting down");
238     auth_shutdown();
239 }
240 
241 
242 /* unix traditionally defaults to 1024 open FDs max, which is often a restriction for icecast
243  * so here (as root) we check the current limit against clients allowed and up it while we can
244  */
check_open_file_limit(ice_config_t * config)245 static void check_open_file_limit (ice_config_t *config)
246 {
247 #ifdef HAVE_GETRLIMIT
248     struct rlimit rlimit;
249     if (getrlimit (RLIMIT_NOFILE, &rlimit) == 0)
250     {
251         if (rlimit.rlim_max < config->client_limit)
252         {
253             rlim_t old = rlimit.rlim_max;
254             rlimit.rlim_cur = rlimit.rlim_max = config->client_limit;
255             if (setrlimit (RLIMIT_NOFILE, &rlimit) < 0)
256                 fprintf (stderr, "failed to increase max number of open files from %lu to %lu\n",
257                         (unsigned long)old, (unsigned long)config->client_limit);
258         }
259     }
260 #endif
261 }
262 
263 
264 /* chroot the process. Watch out - we need to do this before starting other
265  * threads. Change uid as well, after figuring out uid _first_ */
_ch_root_uid_setup(void)266 static void _ch_root_uid_setup(void)
267 {
268    ice_config_t *conf = config_get_config_unlocked();
269 #ifdef CHUID
270    struct passwd *user;
271    struct group *group;
272    uid_t uid=-1;
273    gid_t gid=-1;
274 
275    if(conf->chuid)
276    {
277        if(conf->user) {
278            user = getpwnam(conf->user);
279            if(user)
280                uid = user->pw_uid;
281            else
282                fprintf(stderr, "Couldn't find user \"%s\" in password file\n", conf->user);
283        }
284        if(conf->group) {
285            group = getgrnam(conf->group);
286 
287            if(group)
288                gid = group->gr_gid;
289            else
290                fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
291        }
292    }
293 #endif
294 
295    check_open_file_limit (conf);
296 
297 #ifdef HAVE_CHROOT
298    if (conf->chroot)
299    {
300        if(getuid()) /* root check */
301        {
302            fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
303        }
304        if (chroot(conf->base_dir) < 0 || chdir ("/") < 0)
305        {
306            fprintf(stderr,"WARNING: Couldn't change server root: %s\n", strerror(errno));
307            return;
308        }
309        else
310            fprintf(stdout, "Changed root successfully to \"%s\".\n", conf->base_dir);
311 
312    }
313 #endif
314 #ifdef CHUID
315 
316    if(conf->chuid)
317    {
318        if(getuid()) /* root check */
319        {
320            fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
321            return;
322        }
323 
324        if (gid != (gid_t)-1)
325        {
326            if (initgroups (conf->user, gid) < 0)
327                fprintf (stdout, "Error changing supplementary groups: %s.\n", strerror(errno));
328            else
329                fprintf (stdout, "Changed supplementary groups based on user: %s.\n", conf->user);
330 #ifdef HAVE_SETRESGID
331            if (setresgid (gid, gid, gid) < 0)
332 #else
333            if (setgid (gid) < 0)
334 #endif
335                fprintf (stdout, "Error changing groupid: %s.\n", strerror(errno));
336            else
337                fprintf (stdout, "Changed groupid to %i.\n", (int)gid);
338        }
339 
340        if (uid != (uid_t)-1)
341        {
342 #ifdef HAVE_SETRESUID
343            if (setresuid (uid, uid, uid) < 0)
344 #else
345            if (setuid (gid) < 0)
346 #endif
347                fprintf (stdout, "Error changing userid: %s.\n", strerror(errno));
348            else
349                fprintf (stdout, "Changed userid to %i.\n", (int)uid);
350        }
351    }
352 #endif
353 }
354 
355 
server_init(int argc,char * argv[])356 int server_init (int argc, char *argv[])
357 {
358     int  ret;
359     char filename[512];
360     char pbuf[1024];
361 
362     switch (_parse_config_opts (argc, argv, filename, 512))
363     {
364         case -1:
365             _print_usage();
366             return -1;
367         default:
368             /* parse the config file */
369             config_get_config();
370             ret = config_initial_parse_file(filename);
371             config_release_config();
372             if (ret < 0)
373             {
374                 snprintf (pbuf, sizeof(pbuf),
375                         "FATAL: error parsing config file (%s)", filename);
376                 _fatal_error (pbuf);
377                 switch (ret)
378                 {
379                     case CONFIG_EINSANE:
380                         _fatal_error("filename was null or blank");
381                         break;
382                     case CONFIG_ENOROOT:
383                         _fatal_error("no root element found");
384                         break;
385                     case CONFIG_EBADROOT:
386                         _fatal_error("root element is not <icecast>");
387                         break;
388                     default:
389                         _fatal_error("XML config parsing error");
390                         break;
391                 }
392                 return -1;
393             }
394     }
395 
396     /* override config file options with commandline options */
397     config_parse_cmdline(argc, argv);
398 
399     /* Bind socket, before we change userid */
400     if (server_proc_init() == 0)
401     {
402         _fatal_error("Server startup failed. Exiting");
403         return -1;
404     }
405     fserve_initialize();
406 
407 #ifdef CHUID
408     /* We'll only have getuid() if we also have setuid(), it's reasonable to
409      * assume */
410     if (getuid() == 0) /* Running as root! Don't allow this */
411     {
412         fprintf (stderr, "ERROR: You should not run icecast2 as root\n");
413         fprintf (stderr, "Use the changeowner directive in the config file\n");
414         return -1;
415     }
416 #endif
417     /* setup default signal handlers */
418     sighandler_initialize();
419 
420     if (start_logging (config_get_config_unlocked()) < 0)
421     {
422         _fatal_error("FATAL: Could not start logging");
423         return -1;
424     }
425     return 0;
426 }
427 
428 
429 #ifndef _WIN32
main(int argc,char * argv[])430 int main (int argc, char *argv[])
431 {
432     initialize_subsystems();
433 
434     if (server_init (argc, argv) == 0)
435         server_process();
436 
437     shutdown_subsystems();
438 
439     if (pidfile)
440     {
441         remove (pidfile);
442         free (pidfile);
443     }
444     return 0;
445 }
446 #endif
447 
448