bug#73589: system* does not honor SIGINT restoration in child

Olivier Dion <[email protected]>
Newsgroups gmane.lisp.guile.bugs
Message-ID <87jzer7djc.fsf@laura>
Hi,

Given the following C program:
--8<---------------cut here---------------start------------->8---
#include <stdio.h>
#include <signal.h>

int main(void)
{
	struct sigaction act;

	sigaction(SIGINT, NULL, &act);

	printf("%p %p %p\n", act.sa_handler, SIG_IGN, SIG_DFL);
}
--8<---------------cut here---------------end--------------->8---

and its ouput from various executions:

  1) $ ./a.out => (nil) 0x1 (nil)
  2) $ guile -c '(system "./a.out")' => (nil) 0x1 (nil)
  3) $ guile -c '(system* "./a.out")' => 0x1 0x1 (nil)

We can see that 3) does not honor restoration of `SIGINT' to `SIG_DFL'
like `system(3)' does.  This seems to be because of the following
sigaction before the creation of the process:

--8<---------------cut here---------------start------------->8---
1697  scm_dynwind_sigaction (SIGINT,
1698                         scm_from_uintptr_t ((uintptr_t) SIG_IGN),
1699                         SCM_UNDEFINED);
1700 #ifdef SIGQUIT
1701  scm_dynwind_sigaction (SIGQUIT,
1702                         scm_from_uintptr_t ((uintptr_t) SIG_IGN),
1703                         SCM_UNDEFINED);
1704 #endif
1705
1706  err = piped_process (&pid, prog, args,
1707                       SCM_UNDEFINED, SCM_UNDEFINED);
--8<---------------cut here---------------end--------------->8---

From execve(2):

   POSIX.1 specifies that the dispositions of any signals that are
   ignored or set to the default are left unchanged.  POSIX.1 specifies
   one exception: if SIGCHLD is being ignored, then an implementation
   may leave the disposition unchanged or reset it to the default; Linux
   does the former.

Therefore, setting `SIG_IGN' for `SIGINT' before the fork/execve results
in ignoring the signals in the child, which is unexpected as a user of
`system(3)'.  The solution would be to restore `SIG_DFL' before
`execve(2)' if before the call to `system*' the action was not
`SIG_IGN'.

In the mean time, I have a solution that works for single-threaded
application:
--8<---------------cut here---------------start------------->8---
(define (system* . args)
  (let ((handler+flags (sigaction SIGINT)))
    (dynamic-wind
      (lambda ()
        (sigaction SIGINT SIG_IGN))
      (lambda ()
        (let ((cpid (primitive-fork)))
          (if (zero? cpid)
              (catch #t
                (lambda ()
                  (sigaction SIGINT SIG_DFL)
                  (apply execlp (car args) args))
                (lambda _
                  (primitive-exit EXIT_FAILURE)))
              (waitpid cpid))))
      (lambda ()
        (sigaction SIGINT
          (car handler+flags)
          (cdr handler+flags))))))
--8<---------------cut here---------------end--------------->8---

Thanks,
Olivier

-- 
Olivier Dion
oldiob.ca
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.