diff options
-rw-r--r-- | jaildaemon.c | 160 |
1 files changed, 79 insertions, 81 deletions
diff --git a/jaildaemon.c b/jaildaemon.c index ef37ed8..b903fb2 100644 --- a/jaildaemon.c +++ b/jaildaemon.c | |||
@@ -10,9 +10,11 @@ | |||
10 | #include <sys/param.h> | 10 | #include <sys/param.h> |
11 | #include <sys/jail.h> | 11 | #include <sys/jail.h> |
12 | #include <sys/wait.h> | 12 | #include <sys/wait.h> |
13 | #include <err.h> | ||
13 | #include <errno.h> | 14 | #include <errno.h> |
14 | #include <stdio.h> | 15 | #include <stdio.h> |
15 | #include <stdlib.h> | 16 | #include <stdlib.h> |
17 | #include <stdarg.h> | ||
16 | #include <string.h> | 18 | #include <string.h> |
17 | #include <signal.h> | 19 | #include <signal.h> |
18 | #include <unistd.h> | 20 | #include <unistd.h> |
@@ -22,13 +24,13 @@ | |||
22 | #define IPC_PACKETSIZE 4096 | 24 | #define IPC_PACKETSIZE 4096 |
23 | #define MAGIC_EXIT_CODE 42 | 25 | #define MAGIC_EXIT_CODE 42 |
24 | enum { IAM_DAEMON, IAM_CLIENT, IAM_FORKSLAVE }; | 26 | enum { IAM_DAEMON, IAM_CLIENT, IAM_FORKSLAVE }; |
27 | enum { TASK_RESPAWN }; | ||
25 | static int g_uds; | 28 | static int g_uds; |
26 | static int g_whoami = IAM_CLIENT; | 29 | static int g_whoami = IAM_CLIENT; |
27 | static struct | ||
28 | pidfh * g_pidfilehandle; | ||
29 | static int g_fork_slave_fd; | 30 | static int g_fork_slave_fd; |
30 | static char g_ipc_packet[IPC_PACKETSIZE]; | 31 | static char g_ipc_packet[IPC_PACKETSIZE]; |
31 | static int * const g_ipc_packet_int = (int*)g_ipc_packet; | 32 | static int * const g_ipc_packet_int = (int*)g_ipc_packet; |
33 | static struct pidfh * g_pidfilehandle; | ||
32 | 34 | ||
33 | /* For house keeping a list of all processes we attach to jails (probes), with | 35 | /* For house keeping a list of all processes we attach to jails (probes), with |
34 | an initial vector size of 128. The vector never shrinks. */ | 36 | an initial vector size of 128. The vector never shrinks. */ |
@@ -53,8 +55,7 @@ static int add_task_to_kqueue( int kq, daemon_task * task_in ); | |||
53 | static pid_t fork_and_jail( int jid, char * proctitle ); | 55 | static pid_t fork_and_jail( int jid, char * proctitle ); |
54 | static void fork_and_execve( int kq, daemon_task * task ); | 56 | static void fork_and_execve( int kq, daemon_task * task ); |
55 | static int fork_fork_slave( ); | 57 | static int fork_fork_slave( ); |
56 | static void exerr( char * message ); | 58 | static void exerr( char * message, ... ); |
57 | static void warn( char * message ); | ||
58 | static void usage( char * command ); | 59 | static void usage( char * command ); |
59 | 60 | ||
60 | /* This handler ensures that we clean up our probes if asked to terminate | 61 | /* This handler ensures that we clean up our probes if asked to terminate |
@@ -66,25 +67,25 @@ static void term_handler( int signal ) { | |||
66 | 67 | ||
67 | /* Report error through the appropriate notification channel. | 68 | /* Report error through the appropriate notification channel. |
68 | Currently this just writes to stderr, which hopefully still is there. */ | 69 | Currently this just writes to stderr, which hopefully still is there. */ |
69 | static void exerr( char * message ) { | 70 | static void exerr( char * message, ... ) { |
71 | va_list args; | ||
72 | va_start(args, message); | ||
73 | |||
70 | switch( g_whoami ) { | 74 | switch( g_whoami ) { |
71 | case IAM_DAEMON: | 75 | case IAM_DAEMON: |
72 | syslog( LOG_ERR, "Error %s\n", message ); | 76 | vsyslog( LOG_ERR, message, args ); |
73 | break; | 77 | break; |
74 | case IAM_CLIENT: | 78 | case IAM_CLIENT: |
75 | fprintf( stderr, "Error %s\n", message ); | 79 | verrx( 1, message, args ); |
80 | /* Never returns */ | ||
76 | break; | 81 | break; |
77 | case IAM_FORKSLAVE: | 82 | case IAM_FORKSLAVE: |
78 | /* TODO */ | 83 | /* TODO */ |
79 | (void)message; | 84 | (void)message; |
85 | (void)args; | ||
80 | break; | 86 | break; |
81 | } | 87 | } |
82 | exit( 11 ); | 88 | exit( 1 ); |
83 | } | ||
84 | |||
85 | /* Report a non-fatal situation */ | ||
86 | static void warn( char * message ) { | ||
87 | syslog( LOG_WARNING, "%s\n", message ); | ||
88 | } | 89 | } |
89 | 90 | ||
90 | /* Report syntax of command line arguments to the user */ | 91 | /* Report syntax of command line arguments to the user */ |
@@ -138,18 +139,26 @@ static int fork_fork_slave( ) { | |||
138 | int sockets[2]; | 139 | int sockets[2]; |
139 | 140 | ||
140 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) | 141 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) |
141 | exerr("opening stream socket pair"); | 142 | exerr( "opening stream socket pair"); |
142 | 143 | ||
143 | switch( fork() ) { | 144 | switch( fork() ) { |
144 | case -1: | 145 | case -1: |
145 | exerr("forking fork slave"); | 146 | pidfile_remove( g_pidfilehandle ); |
147 | exerr( "forking fork slave"); | ||
146 | break; | 148 | break; |
147 | case 0: | 149 | case 0: |
148 | /* I am child, close master's socket fd */ | 150 | /* I am child, close master's socket fd */ |
149 | close( sockets[0] ); | 151 | close( sockets[0] ); |
150 | g_whoami = IAM_FORKSLAVE; | 152 | |
153 | /* Close IPC handle and wipe value */ | ||
154 | close( g_uds ); | ||
155 | g_uds = 0; | ||
156 | |||
157 | /* Close pid file and wipe value */ | ||
151 | pidfile_close( g_pidfilehandle ); | 158 | pidfile_close( g_pidfilehandle ); |
152 | g_pidfilehandle = NULL; | 159 | g_pidfilehandle = NULL; |
160 | |||
161 | g_whoami = IAM_FORKSLAVE; | ||
153 | fork_slave( sockets[1] ); /* Never returns */ | 162 | fork_slave( sockets[1] ); /* Never returns */ |
154 | exit(0); | 163 | exit(0); |
155 | default: | 164 | default: |
@@ -189,7 +198,7 @@ static pid_t fork_and_jail( int jid, char * proctitle ) { | |||
189 | 198 | ||
190 | /* Throw ourself into the jail */ | 199 | /* Throw ourself into the jail */ |
191 | if( jail_attach( jid ) ) | 200 | if( jail_attach( jid ) ) |
192 | exerr( "when attaching to jail" ); | 201 | exerr( "when attaching to jail %d", jid ); |
193 | 202 | ||
194 | /* wait for SIGHUP */ | 203 | /* wait for SIGHUP */ |
195 | sigemptyset(&sigset); | 204 | sigemptyset(&sigset); |
@@ -215,7 +224,7 @@ static int copy_daemontask( daemon_task ** out, daemon_task * const in ) { | |||
215 | 224 | ||
216 | /* If all strings could be copied, return array */ | 225 | /* If all strings could be copied, return array */ |
217 | if( ( !in->m_commandline || t->m_commandline ) && | 226 | if( ( !in->m_commandline || t->m_commandline ) && |
218 | ( !in->m_proctitle || t->m_proctitle ) ) | 227 | ( !in->m_proctitle || t->m_proctitle ) ) |
219 | return 0; | 228 | return 0; |
220 | 229 | ||
221 | free( t->m_commandline ); | 230 | free( t->m_commandline ); |
@@ -328,7 +337,7 @@ static int add_task_to_kqueue( int kq, daemon_task * t_in ) { | |||
328 | /* Expect reply from fork slave */ | 337 | /* Expect reply from fork slave */ |
329 | pid = *(pid_t*)g_ipc_packet; | 338 | pid = *(pid_t*)g_ipc_packet; |
330 | 339 | ||
331 | /* Associate pid with command line to execute and add to our kqueue */ | 340 | /* Associate pid with command line to execute and add to our kqueue */ |
332 | memset( &ke, 0, sizeof ke ); | 341 | memset( &ke, 0, sizeof ke ); |
333 | EV_SET( &ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, t ); | 342 | EV_SET( &ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, t ); |
334 | if( kevent( kq, &ke, 1, NULL, 0, NULL ) == 0 ) { | 343 | if( kevent( kq, &ke, 1, NULL, 0, NULL ) == 0 ) { |
@@ -374,6 +383,7 @@ static int add_task_to_kqueue( int kq, daemon_task * t_in ) { | |||
374 | /* jaildaemon -D <-ppidfile> <-fipcsockpath> -c command -j jid -t proctitle <-r> | 383 | /* jaildaemon -D <-ppidfile> <-fipcsockpath> -c command -j jid -t proctitle <-r> |
375 | */ | 384 | */ |
376 | int main( int argc, char **argv ) { | 385 | int main( int argc, char **argv ) { |
386 | pid_t second_pid; | ||
377 | int kq, i; | 387 | int kq, i; |
378 | int o_force_daemon = 0; | 388 | int o_force_daemon = 0; |
379 | int o_daemonize = 0, o_jid = -1, o_respawn = 0; | 389 | int o_daemonize = 0, o_jid = -1, o_respawn = 0; |
@@ -382,7 +392,7 @@ int main( int argc, char **argv ) { | |||
382 | struct kevent ke; | 392 | struct kevent ke; |
383 | struct sockaddr_un addr; | 393 | struct sockaddr_un addr; |
384 | struct sigaction sa; | 394 | struct sigaction sa; |
385 | size_t ipc_bytes = IPC_PACKETSIZE; | 395 | size_t ipc_bytes = 2 * IPC_PACKETSIZE; /* init value for setsockopt */ |
386 | 396 | ||
387 | /* If we are not started from root, there is not much we can do, | 397 | /* If we are not started from root, there is not much we can do, |
388 | neither access the unix domain socket.*/ | 398 | neither access the unix domain socket.*/ |
@@ -405,57 +415,25 @@ int main( int argc, char **argv ) { | |||
405 | } | 415 | } |
406 | } | 416 | } |
407 | 417 | ||
408 | /* Daemonize and start a fork slave while there is no file descriptors or | 418 | /* Need a command line, and jid if not a daemon */ |
409 | initialized memory yet. Communicate with this slave via socketpair */ | 419 | if( !o_daemonize && ( !o_command || o_jid <= 0 ) ) |
410 | if( o_daemonize ) { | 420 | usage( argv[0] ); |
411 | pid_t second_pid; | ||
412 | g_pidfilehandle = pidfile_open(o_pidfile, 0600, &second_pid ); | ||
413 | |||
414 | if (!g_pidfilehandle) { | ||
415 | if (errno == EEXIST) | ||
416 | exerr( "jaildaemon already running." ); | ||
417 | /* If we cannot create pidfile from other reasons, only warn. */ | ||
418 | warn( "Cannot open or create pidfile" ); | ||
419 | } | ||
420 | |||
421 | if( daemon(1,0) == -1 ) { | ||
422 | pidfile_remove(g_pidfilehandle); | ||
423 | exerr( "daemonzing" ); | ||
424 | } | ||
425 | pidfile_write(g_pidfilehandle); | ||
426 | |||
427 | g_fork_slave_fd = fork_fork_slave( ); | ||
428 | |||
429 | atexit( remove_pidfile ); | ||
430 | openlog( "jaildaemon", 0, LOG_DAEMON ); | ||
431 | setlogmask(LOG_UPTO(LOG_INFO)); | ||
432 | g_whoami = IAM_DAEMON; | ||
433 | |||
434 | } else { | ||
435 | /* Need a command line, and jid if not a daemon */ | ||
436 | if( !o_command || o_jid <= 0 ) | ||
437 | usage( argv[0] ); | ||
438 | } | ||
439 | 421 | ||
440 | /* Setup unix domain socket descriptors */ | 422 | /* Setup unix domain socket descriptors */ |
441 | g_uds = socket(AF_UNIX, SOCK_DGRAM, 0); | 423 | if( ( g_uds = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 ) |
442 | if( g_uds < 0 ) | ||
443 | exerr( "Can not create control channel." ); | 424 | exerr( "Can not create control channel." ); |
444 | 425 | ||
445 | if(1) { | 426 | /* Allow huge packets on our unix domain socket */ |
446 | size_t packet_size = 2 * IPC_PACKETSIZE; | 427 | setsockopt( g_uds, SOL_SOCKET, SO_SNDBUF, &ipc_bytes, sizeof(ipc_bytes) ); |
447 | socklen_t pss = sizeof(packet_size); | 428 | setsockopt( g_uds, SOL_SOCKET, SO_RCVBUF, &ipc_bytes, sizeof(ipc_bytes) ); |
448 | /* Allow huge packets on our unix domain socket */ | 429 | |
449 | setsockopt( g_uds, SOL_SOCKET, SO_SNDBUF, &packet_size, pss ); | ||
450 | setsockopt( g_uds, SOL_SOCKET, SO_RCVBUF, &packet_size, pss ); | ||
451 | } | ||
452 | memset(&addr, 0, sizeof(addr)); | 430 | memset(&addr, 0, sizeof(addr)); |
453 | addr.sun_family = AF_UNIX; | 431 | addr.sun_family = AF_UNIX; |
454 | strncpy(addr.sun_path, o_uds_path, sizeof(addr.sun_path)-1); | 432 | strncpy(addr.sun_path, o_uds_path, sizeof(addr.sun_path)-1); |
455 | 433 | ||
456 | if( !o_daemonize ) { | 434 | if( !o_daemonize ) { |
457 | /* If we're not supposed to daemonize, just try to pipe the | 435 | /* In utility mode try to pipe the request to the daemon already running |
458 | request to the daemon already running and exit | 436 | and exit |
459 | 437 | ||
460 | Packed packet format: | 438 | Packed packet format: |
461 | int m_flags ( 0x01 respawn, 0x02 executing, to be respawned ) | 439 | int m_flags ( 0x01 respawn, 0x02 executing, to be respawned ) |
@@ -491,12 +469,23 @@ int main( int argc, char **argv ) { | |||
491 | exit(0); | 469 | exit(0); |
492 | } | 470 | } |
493 | 471 | ||
472 | /* This utility mode code finished with the exit(0) above. We're daemon. */ | ||
473 | |||
474 | /* Daemonize and start a fork slave while there is no file descriptors or | ||
475 | initialized memory yet. Communicate with this slave via socketpair */ | ||
476 | if( !( g_pidfilehandle = pidfile_open(o_pidfile, 0600, &second_pid ) ) ) { | ||
477 | if (errno == EEXIST) | ||
478 | exerr( "jaildaemon already running." ); | ||
479 | |||
480 | /* If we cannot create pidfile from other reasons, only warn. */ | ||
481 | warn( "Cannot open or create pidfile" ); | ||
482 | } | ||
483 | |||
494 | /* Send test DGRAM through the unix domain socket. If this succeeds, there | 484 | /* Send test DGRAM through the unix domain socket. If this succeeds, there |
495 | likely is another daemon already listening. You have to force the daemon | 485 | likely is another daemon already listening. You have to force the daemon |
496 | to start in this case */ | 486 | to start in this case */ |
497 | if( sendto( g_uds, g_ipc_packet, 0, 0, | 487 | if( sendto( g_uds, g_ipc_packet, 0, 0, |
498 | (struct sockaddr*)&addr, sizeof(addr) ) == 0 ) { | 488 | (struct sockaddr*)&addr, sizeof(addr) ) == 0 ) { |
499 | |||
500 | if( !o_force_daemon ) | 489 | if( !o_force_daemon ) |
501 | exerr( "Found command channel. Refusing to overwrite a working one." | 490 | exerr( "Found command channel. Refusing to overwrite a working one." |
502 | " Another server may be running. Force with -F."); | 491 | " Another server may be running. Force with -F."); |
@@ -504,7 +493,29 @@ int main( int argc, char **argv ) { | |||
504 | warn( "Forcing start of daemon despite working command channel." ); | 493 | warn( "Forcing start of daemon despite working command channel." ); |
505 | } | 494 | } |
506 | 495 | ||
507 | /* Create the unix domain socket to receive commands on */ | 496 | if( daemon(1,0) == -1 ) { |
497 | pidfile_remove(g_pidfilehandle); | ||
498 | exerr( "daemonzing" ); | ||
499 | } | ||
500 | |||
501 | pidfile_write(g_pidfilehandle); | ||
502 | |||
503 | /* Spawn fork slave */ | ||
504 | g_fork_slave_fd = fork_fork_slave( ); | ||
505 | |||
506 | /* Register pid file remover after fork() so that fork slave wont remove our | ||
507 | pid file*/ | ||
508 | atexit( remove_pidfile ); | ||
509 | |||
510 | /* Initialize syslog facilities */ | ||
511 | openlog( "jaildaemon", 0, LOG_DAEMON ); | ||
512 | setlogmask(LOG_UPTO(LOG_INFO)); | ||
513 | |||
514 | /* From now we log through syslog */ | ||
515 | g_whoami = IAM_DAEMON; | ||
516 | |||
517 | /* Create the unix domain socket to receive commands on, N.B. error goes to | ||
518 | syslog, now */ | ||
508 | unlink(o_uds_path); | 519 | unlink(o_uds_path); |
509 | if (bind(g_uds, (struct sockaddr*)&addr, sizeof(addr)) == -1) | 520 | if (bind(g_uds, (struct sockaddr*)&addr, sizeof(addr)) == -1) |
510 | exerr( "binding to command channel. Maybe another daemon is running?" ); | 521 | exerr( "binding to command channel. Maybe another daemon is running?" ); |
@@ -549,19 +560,6 @@ int main( int argc, char **argv ) { | |||
549 | memset( g_probes, 0, sizeof(pid_t) * PROBES_VECTOR_SIZE ); | 560 | memset( g_probes, 0, sizeof(pid_t) * PROBES_VECTOR_SIZE ); |
550 | atexit( kill_all_probes ); | 561 | atexit( kill_all_probes ); |
551 | 562 | ||
552 | /* If daemon was started with some initial script, fire it now | ||
553 | -- this leaks some information in the command line to all jails and | ||
554 | thus is disabled | ||
555 | if( o_command ) { | ||
556 | daemon_task task; | ||
557 | task.m_jid = o_jid; | ||
558 | task.m_flags = o_respawn ? 0x01 : 0x00; | ||
559 | task.m_commandline = o_command; | ||
560 | task.m_proctitle = o_proctitle; | ||
561 | add_task_to_kqueue( kq, &task ); | ||
562 | } | ||
563 | */ | ||
564 | |||
565 | /* Main loop */ | 563 | /* Main loop */ |
566 | while( 1 ) { | 564 | while( 1 ) { |
567 | memset( &ke, 0, sizeof(ke) ); | 565 | memset( &ke, 0, sizeof(ke) ); |
@@ -585,14 +583,14 @@ int main( int argc, char **argv ) { | |||
585 | if( !task ) | 583 | if( !task ) |
586 | continue; | 584 | continue; |
587 | 585 | ||
588 | /* If this task was watched to respawn a daemon in the jail, | 586 | /* If this task was watched to respawn a daemon in the jail, |
589 | do it now */ | 587 | do it now */ |
590 | if( task->m_flags & 0x02 ) { | 588 | if( task->m_flags & 0x02 ) { |
591 | task->m_flags &= ~0x02; | 589 | task->m_flags &= ~0x02; |
592 | add_task_to_kqueue( kq, task ); | 590 | add_task_to_kqueue( kq, task ); |
593 | 591 | ||
594 | /* If the process exited with the correct magic code, | 592 | /* If the process exited with the correct magic code, |
595 | execute the associated command */ | 593 | execute the associated command */ |
596 | } else if( WEXITSTATUS(ke.data) == MAGIC_EXIT_CODE ) | 594 | } else if( WEXITSTATUS(ke.data) == MAGIC_EXIT_CODE ) |
597 | fork_and_execve( kq, task ); | 595 | fork_and_execve( kq, task ); |
598 | 596 | ||