From 87dc06e5e85a98eea2d5ebcca81c856ff2a2cbb4 Mon Sep 17 00:00:00 2001 From: erdgeist <> Date: Sat, 2 Mar 2013 02:26:17 +0000 Subject: Keep a list of our probe's jids and kill them when the daemon exits --- jaildaemon.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/jaildaemon.c b/jaildaemon.c index 506ca98..3ae8612 100644 --- a/jaildaemon.c +++ b/jaildaemon.c @@ -16,11 +16,17 @@ #define IPC_PACKETSIZE 4096 #define MAGIC_EXIT_CODE 42 enum { IAM_DAEMON, IAM_CLIENT, IAM_FORKSLAVE }; -static int g_uds; -static int g_whoami = IAM_CLIENT; -static int g_fork_slave_fd; -static char g_ipc_packet[IPC_PACKETSIZE]; -static int * const g_ipc_packet_int = (int*)g_ipc_packet; +static int g_uds; +static int g_whoami = IAM_CLIENT; +static int g_fork_slave_fd; +static char g_ipc_packet[IPC_PACKETSIZE]; +static int * const g_ipc_packet_int = (int*)g_ipc_packet; + +/* For house keeping a list of all processes we attach to jails (probes), with + an initial vector size of 128. The vector never shrinks. */ +#define PROBES_VECTOR_SIZE 128 +static pid_t * g_probes; +static size_t g_probes_size; typedef struct { int m_jid; @@ -31,6 +37,8 @@ typedef struct { /* Forward declarations */ static void signal_handler( int signal ); +static void term_handler( int signal ); +static void kill_all_probes( void ); static int check_for_jail( int jid ); static int copy_daemontask( daemon_task ** out, daemon_task * const in ); static int add_task_to_kqueue( int kq, daemon_task * task_in ); @@ -49,6 +57,11 @@ static void signal_handler( int signal ) { _exit( MAGIC_EXIT_CODE ); } +static void term_handler( int signal ) { + if( signal == SIGTERM ) + exit(0); +} + /* Report error through the appropriate notification channel. Currently this just writes to stderr, which hopefully still is there. */ static void exerr( char * message ) { @@ -266,10 +279,23 @@ static void fork_and_execve( int kq, daemon_task * t_in ) { } } +static void kill_all_probes( void ) { + size_t i; +syslog( LOG_ERR, "KILLING PROBES" ); + if( g_probes ) + for( i = 0; i < g_probes_size; ++i ) + if( g_probes[i] ) + kill( g_probes[i], SIGTERM ); + g_probes_size = 0; + free( g_probes ); + g_probes = 0; +} + static int add_task_to_kqueue( int kq, daemon_task * t_in ) { struct kevent ke; daemon_task * t; pid_t pid; + size_t i; if( check_for_jail( t_in->m_jid ) ) { syslog( LOG_ERR, "Invalid jail id: %d", t_in->m_jid ); @@ -297,6 +323,27 @@ static int add_task_to_kqueue( int kq, daemon_task * t_in ) { /* Expect reply from fork slave */ pid = *(pid_t*)g_ipc_packet; + /* Account for new pid */ + for( i = 0; i < g_probes_size; ++i ) + if( !g_probes[i] ) + g_probes[i] = pid; + + /* No space for pid entry => make room */ + if( i == g_probes_size ) { + size_t bytes = sizeof(pid_t) * g_probes_size; + pid_t *probes = realloc( g_probes, 4 * bytes ); + /* If we can not allocate memory, just ignore. Worst case is a defunct + probe process in the jail once the daemon dies. Probably the probe + will be killed anyway when the kevent below fails, too. */ + if( probes ) { + /* Erase new memory */ + memset( probes + g_probes_size, 0, 3 * bytes ); + probes[g_probes_size] = pid; + g_probes_size *= 4; + g_probes = probes; + } + } + /* Associate pid with command line to execute and add to our kqueue */ memset( &ke, 0, sizeof ke ); EV_SET( &ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, t ); @@ -320,7 +367,7 @@ int main( int argc, char **argv ) { int o_force_daemon = 0; int o_daemonize = 0, o_jid = -1, o_respawn = 0; char *o_command = NULL, *o_pidfile = NULL, *o_proctitle = NULL; - char *o_uds_path = "/var/run/jaildaemon"; + char *o_uds_path = "/var/run/jaildaemon.pipe"; struct kevent ke; struct sockaddr_un addr; struct sigaction sa; @@ -447,11 +494,16 @@ int main( int argc, char **argv ) { /* We do not care for the spawned process -- it is checked for in our kqueue filter. So just ignore SIGCHLD */ memset( &sa, 0, sizeof( sa ) ); - sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDWAIT; if( sigaction(SIGCHLD, &sa, NULL) == -1 ) exerr( "when trying to enable auto reap" ); + /* When dying gracefully, this signal handler sends TERM signals to all + probes */ + sa.sa_handler = term_handler; + if( sigaction(SIGTERM, &sa, NULL) == -1 ) + exerr( "when trying to install TERM handler" ); + /* Create our kqueue */ if( ( kq = kqueue( ) ) == -1 ) exerr( "when create kqueue" ); @@ -466,10 +518,18 @@ int main( int argc, char **argv ) { kevent( kq, &ke, 1, NULL, 0, NULL ); /* We want to be notified if the fork slave died. This is a good time to - die, too*/ + die, too */ EV_SET( &ke, g_fork_slave_fd, EVFILT_READ, EV_ADD, 0, 0, 0); kevent( kq, &ke, 1, NULL, 0, NULL ); + /* Prepare probe pids list, initally 128 processes long, vector grows by + factor 4, when exhausted */ + g_probes = malloc( sizeof(pid_t) * PROBES_VECTOR_SIZE ); + g_probes_size = PROBES_VECTOR_SIZE; + if( !g_probes ) + exerr( "allocating memory." ); + memset( g_probes, 0, sizeof(pid_t) * PROBES_VECTOR_SIZE ); + atexit( kill_all_probes ); /* If daemon was started with some initial script, fire it now -- this leaks some information in the command line to all jails and @@ -502,6 +562,7 @@ int main( int argc, char **argv ) { switch( ke.filter ) { case EVFILT_PROC: if( ke.fflags & NOTE_EXIT ) { + size_t i; daemon_task * task = (daemon_task *)ke.udata; if( !task ) continue; @@ -525,6 +586,11 @@ int main( int argc, char **argv ) { EV_SET( &ke, ke.ident, EVFILT_PROC, EV_DELETE, NOTE_EXIT, 0, NULL ); kevent( kq, &ke, 1, NULL, 0, NULL ); + + /* Remove pid from our probes list */ + for( i = 0; i < g_probes_size; ++i ) + if( g_probes[i] == (pid_t)ke.ident ) + g_probes[i] = 0; } break; case EVFILT_READ: -- cgit v1.2.3