1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
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
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
|
/*
* Copyright (C) 1997-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** \file server.c Paraslash's main server. */
/**
* \mainpage Paraslash API Reference
*
* Starting points for getting an overview:
*
*
* - The main programs: \ref server.c, \ref audiod.c, \ref client.c,
* \ref audioc.c, \ref fsck.c, \ref afh.c
* - Server: \ref server_command, \ref sender,
* - Audio file selector: \ref audio_format_handler, \ref mood, \ref afs_table,
* - Client: \ref receiver, \ref receiver_node, \ref filter, \ref filter_node.
*
*
* The gory details, listed by topic:
*
* - Audio format handlers: \ref send_common.c \ref mp3_afh.c, \ref ogg_afh.c, \ref aac_afh.c,
* - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c, \ref aacdec_filter.c,
* - Volume normalizer: \ref compress_filter.c,
* - Output: \ref alsa_write.c, \ref osx_write.c,
* - http: \ref http_recv.c, \ref http_send.c,
* - udp: \ref udp_recv.c, \ref udp_send.c,
* - dccp: \ref dccp_recv.c, \ref dccp_send.c,
* - Audio file selector: \ref afs.c, \ref aft.c, \ref mood.c,
* - Afs structures: \ref afs_table, \ref audio_file_data,
* \ref afs_info \ref afh_info,
* - Afs tables: \ref aft.c, \ref mood.c, \ref playlist.c,
* \ref attribute.c, \ref score.c,
* - The virtual streaming system: \ref vss.c, \ref chunk_queue.c.
*
* Lower levels:
*
* - Scheduling: \ref sched.c, \ref sched.h,
* - Networking: \ref net.c,
* - File descriptors: \ref fd.c,
* - Signals: \ref signal.c,
* - Daemons: \ref daemon.c,
* - Strings: \ref string.c, \ref string.h,
* - Time: \ref time.c,
* - Spawning processes: \ref exec.c,
* - Inter process communication: \ref ipc.c,
* - The object storage layer: \ref osl.c,
* - Blob tables: \ref blob.c,
* - The error subssystem: \ref error.h.
* - Access control for paraslash senders: \ref acl.c, \ref acl.h.
*
* Low-level data structures:
*
* - Doubly linked lists: \ref list.h,
* - Red-black trees: \ref rbtree.h, \ref rbtree.c,
* - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h,
* - Hashing: \ref hash.h, \ref sha1.h, \ref sha1.c,
* - Crypto: \ref crypt.c.
* - Forward error correction: \ref fec.c
*/
#include <signal.h>
#include <dirent.h>
#include <sys/time.h>
#include "para.h"
#include "error.h"
#include "server.cmdline.h"
#include "afh.h"
#include "string.h"
#include "afs.h"
#include "server.h"
#include "vss.h"
#include "config.h"
#include "close_on_fork.h"
#include "list.h"
#include "send.h"
#include "net.h"
#include "daemon.h"
#include "ipc.h"
#include "fd.h"
#include "sched.h"
#include "signal.h"
#include "user_list.h"
#include "color.h"
/** Define the array of error lists needed by para_server. */
INIT_SERVER_ERRLISTS;
/** Shut down non-authorized connections after that many seconds. */
#define ALARM_TIMEOUT 10
/**
* Pointer to shared memory area for communication between para_server
* and its children. Exported to vss.c. command.c and to afs.
*/
struct misc_meta_data *mmd;
/**
* The configuration of para_server
*
* It also contains the options for the audio file selector, audio format
* handler and all supported senders.
*/
struct server_args_info conf;
/** A random value used in child context for authentication. */
uint32_t afs_socket_cookie;
/** The mutex protecting the shared memory area containing the mmd struct. */
int mmd_mutex;
/** The file containing user information (public key, permissions). */
static char *user_list_file = NULL;
/** The task responsible for server command handling. */
struct server_command_task {
/** TCP port on which para_server listens for connections. */
int listen_fd;
/** Copied from para_server's main function. */
int argc;
/** Argument vector passed to para_server's main function. */
char **argv;
/** The command task structure for scheduling. */
struct task task;
};
static int want_colors(void)
{
if (conf.color_arg == color_arg_no)
return 0;
if (conf.color_arg == color_arg_yes)
return 1;
if (conf.logfile_given)
return 0;
return isatty(STDERR_FILENO);
}
static void init_colors_or_die(void)
{
int ret, i;
if (!want_colors())
return;
daemon_set_flag(DF_COLOR_LOG);
daemon_set_default_log_colors();
for (i = 0; i < conf.log_color_given; i++) {
ret = daemon_set_log_color(conf.log_color_arg[i]);
if (ret < 0)
exit(EXIT_FAILURE);
}
}
/*
* setup shared memory area and get mutex for locking
*/
static void init_ipc_or_die(void)
{
void *shm;
int shmid, ret = shm_new(sizeof(struct misc_meta_data));
if (ret < 0)
goto err_out;
shmid = ret;
ret = shm_attach(shmid, ATTACH_RW, &shm);
shm_destroy(shmid);
if (ret < 0)
goto err_out;
mmd = shm;
ret = mutex_new();
if (ret < 0)
goto err_out;
mmd_mutex = ret;
mmd->num_played = 0;
mmd->num_commands = 0;
mmd->events = 0;
mmd->num_connects = 0;
mmd->active_connections = 0;
mmd->vss_status_flags = VSS_NEXT;
mmd->new_vss_status_flags = VSS_NEXT;
return;
err_out:
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
/**
* (Re-)read the server configuration files.
*
* \param override Passed to gengetopt to activate the override feature.
*
* This function also re-opens the logfile and sets the global \a
* user_list_file variable.
*/
void parse_config_or_die(int override)
{
char *home = para_homedir();
int ret;
char *cf;
daemon_close_log();
if (conf.config_file_given)
cf = para_strdup(conf.config_file_arg);
else
cf = make_message("%s/.paraslash/server.conf", home);
free(user_list_file);
if (!conf.user_list_given)
user_list_file = make_message("%s/.paraslash/server.users", home);
else
user_list_file = para_strdup(conf.user_list_arg);
ret = file_exists(cf);
if (conf.config_file_given && !ret) {
ret = -1;
PARA_EMERG_LOG("can not read config file %s\n", cf);
goto out;
}
if (ret) {
int tmp = conf.daemon_given;
struct server_cmdline_parser_params params = {
.override = override,
.initialize = 0,
.check_required = 1,
.check_ambiguity = 0,
.print_errors = !conf.daemon_given
};
server_cmdline_parser_config_file(cf, &conf, ¶ms);
conf.daemon_given = tmp;
}
if (conf.logfile_given) {
daemon_set_logfile(conf.logfile_arg);
daemon_open_log_or_die();
}
daemon_set_loglevel(conf.loglevel_arg);
init_colors_or_die();
daemon_set_flag(DF_LOG_PID);
daemon_set_flag(DF_LOG_LL);
daemon_set_flag(DF_LOG_TIME);
ret = 1;
out:
free(cf);
free(home);
if (ret > 0)
return;
free(user_list_file);
user_list_file = NULL;
exit(EXIT_FAILURE);
}
static void signal_pre_select(struct sched *s, struct task *t)
{
struct signal_task *st = container_of(t, struct signal_task, task);
para_fd_set(st->fd, &s->rfds, &s->max_fileno);
}
/*
* called when server gets SIGHUP or when client invokes hup command.
*/
static void handle_sighup(void)
{
PARA_NOTICE_LOG("SIGHUP\n");
parse_config_or_die(1); /* reopens log */
init_user_list(user_list_file); /* reload user list */
if (mmd->afs_pid)
kill(mmd->afs_pid, SIGHUP);
}
static void signal_post_select(struct sched *s, struct task *t)
{
struct signal_task *st = container_of(t, struct signal_task, task);
if (!FD_ISSET(st->fd, &s->rfds))
return;
st->signum = para_next_signal();
switch (st->signum) {
case SIGHUP:
handle_sighup();
break;
case SIGCHLD:
for (;;) {
pid_t pid;
int ret = para_reap_child(&pid);
if (ret <= 0)
break;
if (pid != mmd->afs_pid)
continue;
PARA_EMERG_LOG("fatal: afs died\n");
kill(0, SIGTERM);
goto cleanup;
}
break;
/* die on sigint/sigterm. Kill all children too. */
case SIGINT:
case SIGTERM:
PARA_EMERG_LOG("terminating on signal %d\n", st->signum);
kill(0, SIGTERM);
/*
* We must wait for afs because afs catches SIGINT/SIGTERM.
* Before reacting to the signal, afs might want to use the
* shared memory area and the mmd mutex. If we destroy this
* mutex too early and afs tries to lock the shared memory
* area, the call to mutex_lock() will fail and terminate the
* afs process. This leads to dirty osl tables.
*
* There's no such problem with the other children of the
* server process (the command handlers) as these reset their
* SIGINT/SIGTERM handlers to the default action, i.e. these
* processes get killed immediately by the above kill().
*/
PARA_INFO_LOG("waiting for afs (pid %d) to die\n",
(int)mmd->afs_pid);
waitpid(mmd->afs_pid, NULL, 0);
cleanup:
free(mmd->afd.afhi.chunk_table);
free(mmd->afd.afhi.info_string);
close_listed_fds();
mutex_destroy(mmd_mutex);
shm_detach(mmd);
exit(EXIT_FAILURE);
}
}
static void init_signal_task(void)
{
static struct signal_task signal_task_struct,
*st = &signal_task_struct;
st->task.pre_select = signal_pre_select;
st->task.post_select = signal_post_select;
sprintf(st->task.status, "signal task");
PARA_NOTICE_LOG("setting up signal handling\n");
st->fd = para_signal_init(); /* always successful */
para_install_sighandler(SIGINT);
para_install_sighandler(SIGTERM);
para_install_sighandler(SIGHUP);
para_install_sighandler(SIGCHLD);
para_sigaction(SIGPIPE, SIG_IGN);
add_close_on_fork_list(st->fd);
register_task(&st->task);
}
static void command_pre_select(struct sched *s, struct task *t)
{
struct server_command_task *sct = container_of(t, struct server_command_task, task);
para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
}
static void command_post_select(struct sched *s, struct task *t)
{
struct server_command_task *sct = container_of(t, struct server_command_task, task);
int new_fd, ret, i;
char *peer_name;
pid_t child_pid;
uint32_t *chunk_table;
char *info_string;
if (!FD_ISSET(sct->listen_fd, &s->rfds))
return;
ret = para_accept(sct->listen_fd, NULL, 0);
if (ret < 0)
goto out;
new_fd = ret;
peer_name = remote_name(new_fd);
PARA_INFO_LOG("got connection from %s, forking\n", peer_name);
mmd->num_connects++;
mmd->active_connections++;
random();
/* The chunk table and the info_string are pointers located in the
* mmd struct that point to dynamically allocated memory that must be
* freed by the parent and the child. However, as the mmd struct is in
* a shared memory area, there's no guarantee that after the fork these
* pointers are still valid in child context. As these two pointers are
* not used in the child anyway, we save them to local variables and
* free the memory via that copy in the child.
*/
info_string = mmd->afd.afhi.info_string;
chunk_table = mmd->afd.afhi.chunk_table;
child_pid = fork();
if (child_pid < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
goto out;
}
if (child_pid) {
close(new_fd);
/* parent keeps accepting connections */
return;
}
/* mmd might already have changed at this point */
free(info_string);
free(chunk_table);
alarm(ALARM_TIMEOUT);
close_listed_fds();
para_signal_shutdown();
/*
* put info on who we are serving into argv[0] to make
* client ip visible in top/ps
*/
for (i = sct->argc - 1; i >= 0; i--)
memset(sct->argv[i], 0, strlen(sct->argv[i]));
sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
return handle_connect(new_fd, peer_name);
out:
if (ret < 0)
PARA_CRIT_LOG("%s\n", para_strerror(-ret));
}
static void init_server_command_task(int argc, char **argv)
{
int ret;
static struct server_command_task server_command_task_struct,
*sct = &server_command_task_struct;
PARA_NOTICE_LOG("initializing tcp command socket\n");
sct->task.pre_select = command_pre_select;
sct->task.post_select = command_post_select;
sct->argc = argc;
sct->argv = argv;
ret = para_listen(AF_UNSPEC, IPPROTO_TCP, conf.port_arg);
if (ret < 0)
goto err;
sct->listen_fd = ret;
ret = mark_fd_nonblocking(sct->listen_fd);
if (ret < 0)
goto err;
add_close_on_fork_list(sct->listen_fd); /* child doesn't need the listener */
register_task(&sct->task);
return;
err:
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
static void init_random_seed(void)
{
unsigned int seed;
int fd, ret = para_open("/dev/urandom", O_RDONLY, 0);
if (ret < 0)
goto err;
fd = ret;
ret = read(fd, &seed, sizeof(seed));
if (ret < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
goto out;
}
if (ret != sizeof(seed)) {
ret = -ERRNO_TO_PARA_ERROR(EIO);
goto out;
}
srandom(seed);
ret = 1;
out:
close(fd);
if (ret >= 0)
return;
err:
PARA_EMERG_LOG("can not seed pseudo random number generator: %s\n",
para_strerror(-ret));
exit(EXIT_FAILURE);
}
static int init_afs(void)
{
int ret, afs_server_socket[2];
pid_t afs_pid;
ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
if (ret < 0)
exit(EXIT_FAILURE);
afs_socket_cookie = para_random((uint32_t)-1);
afs_pid = fork();
if (afs_pid < 0)
exit(EXIT_FAILURE);
if (afs_pid == 0) { /* child (afs) */
close(afs_server_socket[0]);
afs_init(afs_socket_cookie, afs_server_socket[1]);
}
mmd->afs_pid = afs_pid;
close(afs_server_socket[1]);
ret = mark_fd_nonblocking(afs_server_socket[0]);
if (ret < 0)
exit(EXIT_FAILURE);
add_close_on_fork_list(afs_server_socket[0]);
PARA_INFO_LOG("afs_socket: %d, afs_socket_cookie: %u\n",
afs_server_socket[0], (unsigned) afs_socket_cookie);
return afs_server_socket[0];
}
__noreturn static void tmp_sigchld_handler(__a_unused int s)
{
PARA_EMERG_LOG("caught early SIGCHLD\n");
exit(EXIT_FAILURE);
}
static void server_init(int argc, char **argv)
{
struct server_cmdline_parser_params params = {
.override = 0,
.initialize = 1,
.check_required = 0,
.check_ambiguity = 0,
.print_errors = 1
};
int afs_socket;
valid_fd_012();
init_random_seed();
/* parse command line options */
server_cmdline_parser_ext(argc, argv, &conf, ¶ms);
HANDLE_VERSION_FLAG("server", conf);
drop_privileges_or_die(conf.user_arg, conf.group_arg);
/* parse config file, open log and set defaults */
parse_config_or_die(0);
log_welcome("para_server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
/* make sure, the global now pointer is uptodate */
gettimeofday(now, NULL);
server_uptime(UPTIME_SET); /* reset server uptime */
init_user_list(user_list_file);
/* become daemon */
if (conf.daemon_given)
daemonize();
PARA_NOTICE_LOG("initializing audio format handlers\n");
afh_init();
/*
* Although afs uses its own signal handling we must ignore SIGUSR1
* _before_ the afs child process gets born by init_afs() below. It's
* racy to do this in the child because the parent might send SIGUSR1
* before the child gets a chance to ignore this signal -- only the
* good die young.
*/
para_sigaction(SIGUSR1, SIG_IGN);
/*
* We have to install a SIGCHLD handler before the afs process is being
* forked off. Otherwise, para_server does not notice if afs dies before
* the SIGCHLD handler has been installed by init_signal_task() below.
*/
para_sigaction(SIGCHLD, tmp_sigchld_handler);
PARA_NOTICE_LOG("initializing the audio file selector\n");
afs_socket = init_afs();
init_signal_task();
PARA_NOTICE_LOG("initializing virtual streaming system\n");
init_vss_task(afs_socket);
init_server_command_task(argc, argv);
PARA_NOTICE_LOG("server init complete\n");
}
static void status_refresh(void)
{
static int prev_uptime = -1, prev_events = -1;
int uptime = server_uptime(UPTIME_GET), ret = 1;
if (prev_events != mmd->events)
goto out;
if (mmd->new_vss_status_flags != mmd->vss_status_flags)
goto out_inc_events;
if (uptime / 60 != prev_uptime / 60)
goto out_inc_events;
return;
out_inc_events:
mmd->events++;
out:
prev_uptime = uptime;
prev_events = mmd->events;
mmd->vss_status_flags = mmd->new_vss_status_flags;
if (ret) {
PARA_DEBUG_LOG("%d events, forcing status update\n",
mmd->events);
killpg(0, SIGUSR1);
}
}
static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
struct timeval *timeout_tv)
{
int ret;
status_refresh();
mutex_unlock(mmd_mutex);
ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
mutex_lock(mmd_mutex);
return ret;
}
/**
* The main function of para_server.
*
* \param argc Usual argument count.
* \param argv Usual argument vector.
*
* \return EXIT_SUCCESS or EXIT_FAILURE.
*/
int main(int argc, char *argv[])
{
int ret;
static struct sched s = {
.default_timeout = {
.tv_sec = 1,
.tv_usec = 0
},
.select_function = server_select
};
server_init(argc, argv);
mutex_lock(mmd_mutex);
ret = schedule(&s);
if (ret < 0) {
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
|