*
*
* - The main programs: \ref server.c, \ref audiod.c, \ref client.c,
- * \ref audioc.c, \ref fsck.c, \ref afh.c
+ * \ref audioc.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.
* - 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 <openssl/rc4.h>
+#include <regex.h>
+#include <osl.h>
#include "para.h"
#include "error.h"
+#include "crypt.h"
#include "server.cmdline.h"
#include "afh.h"
#include "string.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;
/** The mutex protecting the shared memory area containing the mmd struct. */
int mmd_mutex;
-/* global variables for server-internal use */
-static FILE *logfile;
/** The file containing user information (public key, permissions). */
static char *user_list_file = NULL;
-static int mmd_shm_id;
/** The task responsible for server command handling. */
struct task task;
};
-/**
- * Para_server's log function.
- *
- * \param ll The log level.
- * \param fmt The format string describing the log message.
- */
-__printf_2_3 void para_log(int ll, const char* fmt,...)
+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)
{
- va_list argp;
- FILE *outfd;
- struct tm *tm;
- time_t t1;
- char str[MAXLINE] = "";
- pid_t mypid;
-
- if (ll < conf.loglevel_arg)
+ int ret, i;
+
+ if (!want_colors())
return;
- outfd = logfile? logfile : stderr;
- time(&t1);
- tm = localtime(&t1);
- strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
- fprintf(outfd, "%s ", str);
- if (conf.loglevel_arg <= INFO)
- fprintf(outfd, "%i: ", ll);
- mypid = getpid();
- if (conf.loglevel_arg <= INFO)
- fprintf(outfd, "(%d) ", (int)mypid);
- va_start(argp, fmt);
- vfprintf(outfd, fmt, argp);
- va_end(argp);
+ 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);
+ }
}
/*
static void init_ipc_or_die(void)
{
void *shm;
- int ret = shm_new(sizeof(struct misc_meta_data));
+ int shmid, ret = shm_new(sizeof(struct misc_meta_data));
if (ret < 0)
goto err_out;
-
- ret = shm_attach(ret, ATTACH_RW, &shm);
+ shmid = ret;
+ ret = shm_attach(shmid, ATTACH_RW, &shm);
+ shm_destroy(shmid);
if (ret < 0)
goto err_out;
mmd = shm;
- mmd_shm_id = ret;
ret = mutex_new();
if (ret < 0)
exit(EXIT_FAILURE);
}
-static void parse_config_or_die(int override)
+/**
+ * (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();
- struct stat statbuf;
int ret;
char *cf;
+ daemon_close_log();
if (conf.config_file_given)
cf = para_strdup(conf.config_file_arg);
else
user_list_file = make_message("%s/.paraslash/server.users", home);
else
user_list_file = para_strdup(conf.user_list_arg);
- ret = stat(cf, &statbuf);
- if (ret && conf.config_file_given) {
+ ret = file_exists(cf);
+ if (conf.config_file_given && !ret) {
ret = -1;
- PARA_EMERG_LOG("can not stat config file %s\n", cf);
+ PARA_EMERG_LOG("can not read config file %s\n", cf);
goto out;
}
- if (!ret) {
+ 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 = 1
+ .print_errors = !conf.daemon_given
};
server_cmdline_parser_config_file(cf, &conf, ¶ms);
conf.daemon_given = tmp;
}
- if (conf.logfile_given)
- logfile = open_log(conf.logfile_arg);
+ 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);
static void handle_sighup(void)
{
PARA_NOTICE_LOG("SIGHUP\n");
- close_log(logfile); /* gets reopened if necessary by parse_config */
- logfile = NULL;
parse_config_or_die(1); /* reopens log */
init_user_list(user_list_file); /* reload user list */
if (mmd->afs_pid)
if (pid != mmd->afs_pid)
continue;
PARA_EMERG_LOG("fatal: afs died\n");
- goto genocide;
+ 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);
-genocide:
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);
+ close_listed_fds();
mutex_destroy(mmd_mutex);
shm_detach(mmd);
- shm_destroy(mmd_shm_id);
-
exit(EXIT_FAILURE);
}
}
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_NOTICE_LOG("setting up signal handlers\n");
- if (para_install_sighandler(SIGINT) < 0)
- goto err;
- if (para_install_sighandler(SIGTERM) < 0)
- goto err;
- if (para_install_sighandler(SIGHUP) < 0)
- goto err;
- if (para_install_sighandler(SIGCHLD) < 0)
- goto err;
- if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
- goto err;
- if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
- goto err;
+ 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);
- return;
-err:
- PARA_EMERG_LOG("could not install signal handlers\n");
- exit(EXIT_FAILURE);
}
static void command_pre_select(struct sched *s, struct task *t)
int new_fd, ret, i;
char *peer_name;
pid_t child_pid;
+ uint32_t *chunk_table;
if (!FD_ISSET(sct->listen_fd, &s->rfds))
return;
PARA_INFO_LOG("got connection from %s, forking\n", peer_name);
mmd->num_connects++;
mmd->active_connections++;
- random();
+ /*
+ * The chunk table is a pointer located in the mmd struct that points
+ * to dynamically allocated memory, i.e. it 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 this pointer is still
+ * valid in child context. As it is not used in the child anyway, we
+ * save it to a local variable before the fork and free the memory via
+ * that copy in the child directly after the fork.
+ */
+ chunk_table = mmd->afd.afhi.chunk_table;
child_pid = fork();
if (child_pid < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
/* parent keeps accepting connections */
return;
}
+ /* mmd might already have changed at this point */
+ free(chunk_table);
alarm(ALARM_TIMEOUT);
close_listed_fds();
para_signal_shutdown();
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);
- mmd->afs_pid = fork();
- if (mmd->afs_pid < 0)
+ get_random_bytes_or_die((unsigned char *)&afs_socket_cookie,
+ sizeof(afs_socket_cookie));
+ afs_pid = fork();
+ if (afs_pid < 0)
exit(EXIT_FAILURE);
- if (!mmd->afs_pid) { /* child (afs) */
+ 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)
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 = {
int afs_socket;
valid_fd_012();
- init_random_seed();
+ init_random_seed_or_die();
/* 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", conf.loglevel_arg);
+ 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);
init_user_list(user_list_file);
/* become daemon */
if (conf.daemon_given)
- daemon_init();
+ 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();