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