@@ -78,6 +78,7 @@ static bool opt_unshare_cgroup_try = false;
7878static bool opt_needs_devpts = false;
7979static bool opt_new_session = false;
8080static bool opt_die_with_parent = false;
81+ static bool opt_forward_signals = false;
8182static uid_t opt_sandbox_uid = -1 ;
8283static gid_t opt_sandbox_gid = -1 ;
8384static int opt_sync_fd = -1 ;
@@ -373,6 +374,7 @@ usage (int ecode, FILE *out)
373374 " --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n"
374375 " --size BYTES Set size of next argument (only for --tmpfs)\n"
375376 " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n"
377+ " --forward-signals Forward various commonly used signals to the child process.\n"
376378 );
377379 exit (ecode );
378380}
@@ -387,6 +389,33 @@ handle_die_with_parent (void)
387389 die_with_error ("prctl" );
388390}
389391
392+ static int forwarded_signals [] =
393+ {
394+ SIGINT ,
395+ SIGTERM ,
396+ SIGCONT ,
397+ SIGHUP ,
398+ SIGQUIT ,
399+ SIGUSR1 ,
400+ SIGUSR2 ,
401+ SIGWINCH ,
402+ };
403+
404+ static void
405+ block_forwarded_signals (sigset_t * prevmask )
406+ {
407+ sigset_t mask ;
408+ size_t i ;
409+
410+ sigemptyset (& mask );
411+
412+ for (i = 0 ; i < N_ELEMENTS (forwarded_signals ); i ++ )
413+ sigaddset (& mask , forwarded_signals [i ]);
414+
415+ if (sigprocmask (SIG_BLOCK , & mask , prevmask ) == -1 )
416+ die_with_error ("sigprocmask" );
417+ }
418+
390419static void
391420block_sigchild (void )
392421{
@@ -508,6 +537,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd)
508537 int exitc ;
509538 pid_t died_pid ;
510539 int died_status ;
540+ size_t i ;
511541
512542 /* Close all extra fds in the monitoring process.
513543 Any passed in fds have been passed on to the child anyway. */
@@ -523,6 +553,9 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd)
523553 sigemptyset (& mask );
524554 sigaddset (& mask , SIGCHLD );
525555
556+ for (i = 0 ; i < N_ELEMENTS (forwarded_signals ); i ++ )
557+ sigaddset (& mask , forwarded_signals [i ]);
558+
526559 signal_fd = signalfd (-1 , & mask , SFD_CLOEXEC | SFD_NONBLOCK );
527560 if (signal_fd == -1 )
528561 die_with_error ("Can't create signalfd" );
@@ -561,12 +594,21 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd)
561594 }
562595
563596 /* We need to read the signal_fd, or it will keep polling as read,
564- * however we ignore the details as we get them from waitpid
597+ * however we ignore the details for SIGCHLD as we get them from waitpid
565598 * below anyway */
566599 s = read (signal_fd , & fdsi , sizeof (struct signalfd_siginfo ));
567600 if (s == -1 && errno != EINTR && errno != EAGAIN )
568601 die_with_error ("read signalfd" );
569602
603+ /* Propagate signal to child so that it will take the correct
604+ * action. This avoids the parent terminating, leaving an orphan. */
605+ if (fdsi .ssi_signo != SIGCHLD )
606+ {
607+ s = kill (child_pid , fdsi .ssi_signo );
608+ if (s != 0 && s != ESRCH )
609+ die_with_error ("kill child" );
610+ }
611+
570612 /* We may actually get several sigchld compressed into one
571613 SIGCHLD, so we have to handle all of them. */
572614 while ((died_pid = waitpid (-1 , & died_status , WNOHANG )) > 0 )
@@ -2742,6 +2784,10 @@ parse_args_recurse (int *argcp,
27422784 argc -= 1 ;
27432785 break ;
27442786 }
2787+ else if (strcmp (arg , "--forward-signals" ) == 0 )
2788+ {
2789+ opt_forward_signals = true;
2790+ }
27452791 else if (* arg == '-' )
27462792 {
27472793 die ("Unknown option %s" , arg );
@@ -2889,6 +2935,8 @@ main (int argc,
28892935 int intermediate_pids_sockets [2 ] = {-1 , -1 };
28902936 const char * exec_path = NULL ;
28912937 int i ;
2938+ sigset_t sigmask_before_forwarding ;
2939+ sigemptyset (& sigmask_before_forwarding );
28922940
28932941 /* Handle --version early on before we try to acquire/drop
28942942 * any capabilities so it works in a build environment;
@@ -3062,6 +3110,10 @@ main (int argc,
30623110 /* We block sigchild here so that we can use signalfd in the monitor. */
30633111 block_sigchild ();
30643112
3113+ /* We block other signals here to avoid leaving an orphan. */
3114+ if (opt_forward_signals )
3115+ block_forwarded_signals (& sigmask_before_forwarding );
3116+
30653117 clone_flags = SIGCHLD | CLONE_NEWNS ;
30663118 if (opt_unshare_user )
30673119 clone_flags |= CLONE_NEWUSER ;
@@ -3212,6 +3264,13 @@ main (int argc,
32123264 return monitor_child (event_fd , pid , setup_finished_pipe [0 ]);
32133265 }
32143266
3267+ /* Restore the state of sigmask from before the blocking. */
3268+ if (opt_forward_signals )
3269+ {
3270+ if (sigprocmask (SIG_SETMASK , & sigmask_before_forwarding , NULL ) != 0 )
3271+ die_with_error ("sigprocmask" );
3272+ }
3273+
32153274 if (opt_pidns_fd > 0 )
32163275 {
32173276 if (setns (opt_pidns_fd , CLONE_NEWPID ) != 0 )
0 commit comments