const struct sender dccp_sender = {
.name = "dccp",
.init = dccp_send_init,
+ .shutdown = dccp_shutdown_clients,
.pre_select = dccp_pre_select,
.post_select = dccp_post_select,
.shutdown_clients = dccp_shutdown_clients,
const struct sender http_sender = {
.name = "http",
.init = http_send_init,
+ .shutdown = http_shutdown_clients,
.pre_select = http_pre_select,
.post_select = http_post_select,
.send = http_send,
* the clients aware of the end-of-file condition.
*/
void (*shutdown_clients)(void);
+ /** Dellocate all resources. Only called on exit. */
+ void (*shutdown)(void);
/**
* Array of function pointers for the sender subcommands.
*
{
PARA_INFO_LOG("shutting down %s on fd %d\n", sc->name, sc->fd);
free(sc->name);
- close(sc->fd);
- del_close_on_fork_list(sc->fd);
+ if (!process_is_command_handler()) {
+ close(sc->fd);
+ del_close_on_fork_list(sc->fd);
+ }
cq_destroy(sc->cq);
list_del(&sc->node);
free(sc->private_data);
/** The process id of the audio file selector process. */
pid_t afs_pid = 0;
+/* The the main server process (parent of afs and the command handlers). */
+static pid_t server_pid;
+
+/**
+ * Tell whether the executing process is a command handler.
+ *
+ * Cleanup on exit must be performed differently for command handlers.
+ *
+ * \return True if the pid of the executing process is neither the server pid
+ * nor the afs pid.
+ */
+bool process_is_command_handler(void)
+{
+ pid_t pid = getpid();
+
+ return pid != afs_pid && pid != server_pid;
+}
+
/** The task responsible for server command handling. */
struct server_command_task {
/** TCP port on which para_server listens for connections. */
/* become daemon */
if (OPT_GIVEN(DAEMON))
daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
+ server_pid = getpid();
init_random_seed_or_die();
daemon_log_welcome("server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
ret = schedule(&sched);
sched_shutdown(&sched);
signal_shutdown(signal_task);
- if (sct->child_fd < 0) { /* parent (server) */
+ if (!process_is_command_handler()) { /* parent (server) */
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- } else { /* child (command handler) */
+ } else {
/*
- * We hold the lock: it was re-acquired in server_select()
+ * We hold the mmd lock: it was re-acquired in server_select()
* after the select call.
*/
mutex_unlock(mmd_mutex);
close_listed_fds();
ret = handle_connect(sct->child_fd);
}
+ vss_shutdown();
lls_free_parse_result(server_lpr, CMD_PTR);
if (server_lpr != cmdline_lpr)
lls_free_parse_result(cmdline_lpr, CMD_PTR);
int handle_connect(int fd);
void parse_config_or_die(bool reload);
char *server_get_tasks(void);
+bool process_is_command_handler(void);
PARA_NOTICE_LOG("deleting %s (%s) from list\n", sc->name, msg);
udp_close_target(sc);
- close(sc->fd);
- del_close_on_fork_list(sc->fd);
+ /* command handlers already called close_listed_fds() */
+ if (!process_is_command_handler()) {
+ close(sc->fd);
+ del_close_on_fork_list(sc->fd);
+ }
vss_del_fec_client(ut->fc);
list_del(&sc->node);
free(sc->name);
udp_close_target(sc);
}
+static void udp_shutdown(void)
+{
+ struct sender_client *sc, *tmp;
+ list_for_each_entry_safe(sc, tmp, &targets, node)
+ udp_delete_target(sc, "shutdown");
+}
+
static int udp_resolve_target(const char *url, struct sender_command_data *scd)
{
const char *result;
const struct sender udp_sender = {
.name = "udp",
.init = udp_send_init,
+ .shutdown = udp_shutdown,
.shutdown_clients = udp_shutdown_targets,
.resolve_target = udp_resolve_target,
.client_cmds = {
.context = vsst,
}, s);
}
+
+/**
+ * Turn off the virtual streaming system.
+ *
+ * This is only executed on exit. It calls the ->shutdowwn method of all senders.
+ */
+void vss_shutdown(void)
+{
+ int i;
+
+ FOR_EACH_SENDER(i) {
+ if (!senders[i]->shutdown)
+ continue;
+ PARA_NOTICE_LOG("shutting down %s sender\n", senders[i]->name);
+ senders[i]->shutdown();
+ }
+}
unsigned int vss_paused(void);
unsigned int vss_stopped(void);
struct timeval *vss_chunk_time(void);
+void vss_shutdown(void);
/** Stop playing after current audio file. */
#define VSS_NOMORE 1