From 04aa0e394ea53e1e87c547e2476c52dbf560fda1 Mon Sep 17 00:00:00 2001 From: jbj Date: Sun, 16 Mar 2003 02:59:17 +0000 Subject: [PATCH] Start unifying signal handlers. CVS patchset: 6693 CVS date: 2003/03/16 02:59:17 --- lib/Makefile.am | 2 +- lib/tsystem.c | 504 +++++++++++++++++++++++++++----------------------------- 2 files changed, 241 insertions(+), 265 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index bea82b1..c3473bd 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -101,7 +101,7 @@ tthread: tthread.o librpm.la $(LINK) $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< $(mylibs) @WITH_LIBELF_LIB@ tsystem: tsystem.o - $(LINK) $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< -L/lib/tls -lpthread + $(LINK) $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< -lpthread tplatform: tplatform.o librpm.la $(LINK) @LDFLAGS_STATIC@ $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< $(mylibs) diff --git a/lib/tsystem.c b/lib/tsystem.c index dffa220..083aaa4 100644 --- a/lib/tsystem.c +++ b/lib/tsystem.c @@ -17,307 +17,283 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include -#include +#include "system.h" +#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include "psm.h" + +#include "debug.h" + +#define _PSM_DEBUG 0 +/*@unchecked@*/ +int _psm_debug = _PSM_DEBUG; #define SHELL_PATH "/bin/sh" /* Path of the shell. */ #define SHELL_NAME "sh" /* Name to give it. */ -static struct sigaction intr; -static struct sigaction quit; - -static int syssa_refcnt; - -static pthread_mutex_t syssa_lock = PTHREAD_MUTEX_INITIALIZER; - -/* =============================================================== */ - -/* We need some help from the assembler to generate optimal code. We - define some macros here which later will be used. */ -asm (".L__X'%ebx = 1\n\t" - ".L__X'%ecx = 2\n\t" - ".L__X'%edx = 2\n\t" - ".L__X'%eax = 3\n\t" - ".L__X'%esi = 3\n\t" - ".L__X'%edi = 3\n\t" - ".L__X'%ebp = 3\n\t" - ".L__X'%esp = 3\n\t" - ".macro bpushl name reg\n\t" - ".if 1 - \\name\n\t" - ".if 2 - \\name\n\t" - "pushl %ebx\n\t" - ".else\n\t" - "xchgl \\reg, %ebx\n\t" - ".endif\n\t" - ".endif\n\t" - ".endm\n\t" - ".macro bpopl name reg\n\t" - ".if 1 - \\name\n\t" - ".if 2 - \\name\n\t" - "popl %ebx\n\t" - ".else\n\t" - "xchgl \\reg, %ebx\n\t" - ".endif\n\t" - ".endif\n\t" - ".endm\n\t" - ".macro bmovl name reg\n\t" - ".if 1 - \\name\n\t" - ".if 2 - \\name\n\t" - "movl \\reg, %ebx\n\t" - ".endif\n\t" - ".endif\n\t" - ".endm\n\t"); - -/* Define a macro which expands inline into the wrapper code for a system - call. */ -#undef INLINE_SYSCALL -#define INLINE_SYSCALL(name, nr, args...) \ - ({ \ - unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \ - if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0)) \ - { \ - errno = (INTERNAL_SYSCALL_ERRNO (resultvar, )); \ - resultvar = 0xffffffff; \ - } \ - (int) resultvar; }) - -/* Define a macro which expands inline into the wrapper code for a system - call. This use is for internal calls that do not need to handle errors - normally. It will never touch errno. This returns just what the kernel - gave back. */ -# define INTERNAL_SYSCALL(name, err, nr, args...) \ - ({ \ - unsigned int resultvar; \ - asm volatile ( \ - LOADARGS_##nr \ - "movl %1, %%eax\n\t" \ - "int $0x80\n\t" \ - RESTOREARGS_##nr \ - : "=a" (resultvar) \ - : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc"); \ - (int) resultvar; }) - -#undef INTERNAL_SYSCALL_DECL -#define INTERNAL_SYSCALL_DECL(err) do { } while (0) - -#undef INTERNAL_SYSCALL_ERROR_P -#define INTERNAL_SYSCALL_ERROR_P(val, err) \ - ((unsigned int) (val) >= 0xfffff001u) - -#undef INTERNAL_SYSCALL_ERRNO -#define INTERNAL_SYSCALL_ERRNO(val, err) (-(val)) - -#define LOADARGS_0 -# define LOADARGS_1 \ - "bpushl .L__X'%k3, %k3\n\t" \ - "bmovl .L__X'%k3, %k3\n\t" -#define LOADARGS_2 LOADARGS_1 -#define LOADARGS_3 LOADARGS_1 -#define LOADARGS_4 LOADARGS_1 -#define LOADARGS_5 LOADARGS_1 - -#define RESTOREARGS_0 -# define RESTOREARGS_1 \ - "bpopl .L__X'%k3, %k3\n\t" -#define RESTOREARGS_2 RESTOREARGS_1 -#define RESTOREARGS_3 RESTOREARGS_1 -#define RESTOREARGS_4 RESTOREARGS_1 -#define RESTOREARGS_5 RESTOREARGS_1 - -#define ASMFMT_0() -#define ASMFMT_1(arg1) \ - , "acdSD" (arg1) -#define ASMFMT_2(arg1, arg2) \ - , "adSD" (arg1), "c" (arg2) -#define ASMFMT_3(arg1, arg2, arg3) \ - , "aSD" (arg1), "c" (arg2), "d" (arg3) -#define ASMFMT_4(arg1, arg2, arg3, arg4) \ - , "aD" (arg1), "c" (arg2), "d" (arg3), "S" (arg4) -#define ASMFMT_5(arg1, arg2, arg3, arg4, arg5) \ - , "a" (arg1), "c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5) - -/* We have to and actually can handle cancelable system(). The big - problem: we have to kill the child process if necessary. To do - this a cleanup handler has to be registered and is has to be able - to find the PID of the child. The main problem is to reliable have - the PID when needed. It is not necessary for the parent thread to - return. It might still be in the kernel when the cancellation - request comes. Therefore we have to use the clone() calls ability - to have the kernel write the PID into the user-level variable. */ -# define FORK() \ - INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid) - -/* =============================================================== */ + +/** + */ +/*@unchecked@*/ +static sigset_t caught; + +/** + */ +/*@unchecked@*/ +static struct psmtbl_s { + int nalloced; + int npsms; +/*@null@*/ + rpmpsm * psms; +} psmtbl = { 0, 0, NULL }; + +/* forward ref */ +static void handler(int signum) + /*@globals caught, psmtbl, fileSystem @*/ + /*@modifies caught, psmtbl, fileSystem @*/; + +/** + */ +/*@unchecked@*/ +static pthread_mutex_t satbl_lock = PTHREAD_MUTEX_INITIALIZER; + +/** + */ +/*@unchecked@*/ +/*@-fullinitblock@*/ +static struct sigtbl_s { + int signum; + int active; + void (*handler) (int signum); + struct sigaction oact; +} satbl[] = { + { SIGINT, 0, handler }, +#define satbl_sigint (&satbl[0]) + { SIGQUIT, 0, handler }, +#define satbl_sigquit (&satbl[1]) + { SIGCHLD, 0, handler }, +#define satbl_sigchld (&satbl[2]) +#define sigchld_active satbl_sigchld->active +#define DO_LOCK() pthread_mutex_lock(&satbl_lock); +#define DO_UNLOCK() pthread_mutex_unlock(&satbl_lock); +#define INIT_LOCK() \ + { pthread_mutex_init (&satbl_lock, NULL); sigchld_active = 0; } +#define ADD_REF() sigchld_active++ +#define SUB_REF() --sigchld_active + +#define CLEANUP_HANDLER(__handler, __arg, __oldtypeptr) \ + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, (__oldtypeptr)); \ + pthread_cleanup_push((__handler), (__arg)); +#define CLEANUP_RESET(__execute, __oldtype) \ + pthread_cleanup_pop(__execute); \ + pthread_setcanceltype ((__oldtype), &(__oldtype)); + + { SIGHUP, 0, handler }, +#define satbl_sighup (&satbl[3]) + { SIGTERM, 0, handler }, +#define satbl_sigterm (&satbl[4]) + { SIGPIPE, 0, handler }, +#define satbl_sigpipe (&satbl[5]) + { -1, 0, NULL }, +}; +typedef struct sigtbl_s * sigtbl; +/*@=fullinitblock@*/ + +/** + */ +/*@-incondefs@*/ +static void handler(int signum) +{ + sigtbl tbl; + + for (tbl = satbl; tbl->signum >= 0; tbl++) { + if (tbl->signum != signum) + continue; + if (!tbl->active) + continue; + (void) sigaddset(&caught, signum); + switch (signum) { + case SIGCHLD: + while (1) { + int status = 0; + pid_t reaped = waitpid(0, &status, WNOHANG); + int i; + + if (reaped <= 0) + /*@innerbreak@*/ break; + + if (psmtbl.psms) + for (i = 0; i < psmtbl.npsms; i++) { + rpmpsm psm = psmtbl.psms[i]; + if (psm->child != reaped) + /*@innercontinue@*/ continue; + +#if _PSM_DEBUG +/*@-modfilesys@*/ +if (_psm_debug) +fprintf(stderr, " Reap: %p[%d:%d:%d] = %p child %d\n", psmtbl.psms, i, psmtbl.npsms, psmtbl.nalloced, psm, psm->child); +/*@=modfilesys@*/ +#endif + + psm->reaped = reaped; + psm->status = status; + /*@innerbreak@*/ break; + } + } + /*@switchbreak@*/ break; + default: + /*@switchbreak@*/ break; + } + break; + } +} +/*@=incondefs@*/ /* The cancellation handler. */ static void -cancel_handler (void *arg) +sigchld_cancel (void *arg) { - pid_t child = *(pid_t *) arg; - pid_t result; - int err; - - err = kill(child, SIGKILL); - - do { - result = waitpid(child, NULL, 0); - } while (result == (pid_t)-1 && errno == EINTR); - - pthread_mutex_lock(&syssa_lock); - if (--syssa_refcnt == 0) { - (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); - (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); - } - pthread_mutex_unlock(&syssa_lock); + pid_t child = *(pid_t *) arg; + pid_t result; + int err; + + err = kill(child, SIGKILL); + + do { + result = waitpid(child, NULL, 0); + } while (result == (pid_t)-1 && errno == EINTR); + + DO_LOCK (); + if (SUB_REF () == 0) { + (void) sigaction (SIGQUIT, &satbl_sigquit->oact, (struct sigaction *) NULL); + (void) sigaction (SIGINT, &satbl_sigint->oact, (struct sigaction *) NULL); + } + DO_UNLOCK (); } /* Execute LINE as a shell command, returning its status. */ static int do_system (const char *line) { - int oldtype; - int status; - int save; - pid_t pid; - pid_t result; - struct sigaction sa; - sigset_t omask; - -fprintf(stderr, "*** %s(%p) %s\n", __FUNCTION__, line, line); - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigemptyset (&sa.sa_mask); - - pthread_mutex_lock(&syssa_lock); - if (syssa_refcnt++ == 0) { - if (sigaction (SIGINT, &sa, &intr) < 0) { - --syssa_refcnt; - goto out; - } - if (sigaction (SIGQUIT, &sa, &quit) < 0) { - save = errno; - --syssa_refcnt; - goto out_restore_sigint; - } - } - pthread_mutex_unlock(&syssa_lock); - - /* We reuse the bitmap in the 'sa' structure. */ - sigaddset (&sa.sa_mask, SIGCHLD); - save = errno; - if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) { - pthread_mutex_lock(&syssa_lock); - if (--syssa_refcnt == 0) - { - save = errno; - (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); - out_restore_sigint: - (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); - errno = save; - } - out: - pthread_mutex_unlock(&syssa_lock); - return -1; + int oldtype; + int status = -1; + pid_t pid; + pid_t result; + struct sigaction sa; + sigset_t omask; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + DO_LOCK (); + if (ADD_REF () == 0) { + if (sigaction (SIGINT, &sa, &satbl_sigint->oact) < 0) { + SUB_REF (); + goto out; + } + if (sigaction (SIGQUIT, &sa, &satbl_sigquit->oact) < 0) { + SUB_REF (); + goto out_restore_sigint; + } + } + DO_UNLOCK (); + + /* We reuse the bitmap in the 'sa' structure. */ + sigaddset (&sa.sa_mask, SIGCHLD); + if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) { + DO_LOCK (); + if (SUB_REF () == 0) + goto out_restore_sigquit_and_sigint; + goto out; } - pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); - pthread_cleanup_push(cancel_handler, &pid); - -/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg, - pid_t *tid, struct user_desc *tls); */ -/* INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid) */ - -/* pid = clone(sub, NULL, (CLONE_PARENT_SETTID | SIGCHLD), line, - &pid???, &tls???); */ - - pid = FORK (); - if (pid == (pid_t) 0) { - /* Child side. */ - const char *new_argv[4]; - new_argv[0] = SHELL_NAME; - new_argv[1] = "-c"; - new_argv[2] = line; - new_argv[3] = NULL; - - /* Restore the signals. */ - (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); - (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); - (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); - - { pthread_mutex_init (&syssa_lock, NULL); syssa_refcnt = 0; } - - /* Exec the shell. */ - (void) execve (SHELL_PATH, (char *const *) new_argv, __environ); - _exit (127); - } else if (pid < (pid_t) 0) { - /* The fork failed. */ - status = -1; - } else { - /* Parent side. */ - do { - result = waitpid(pid, &status, 0); - } while (result == (pid_t)-1 && errno == EINTR); - if (result != pid) - status = -1; - } - - pthread_cleanup_pop(0); - pthread_setcanceltype (oldtype, &oldtype); - - save = errno; - pthread_mutex_lock(&syssa_lock); - if ((--syssa_refcnt == 0 - && (sigaction (SIGINT, &intr, (struct sigaction *) NULL) - | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0) - || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) - { - status = -1; - } - pthread_mutex_unlock(&syssa_lock); + CLEANUP_HANDLER(sigchld_cancel, &pid, &oldtype); + + pid = fork (); + if (pid == (pid_t) 0) { + /* Child side. */ + const char *new_argv[4]; + new_argv[0] = SHELL_NAME; + new_argv[1] = "-c"; + new_argv[2] = line; + new_argv[3] = NULL; + + /* Restore the signals. */ + (void) sigaction (SIGINT, &satbl_sigint->oact, NULL); + (void) sigaction (SIGQUIT, &satbl_sigquit->oact, NULL); + (void) sigprocmask (SIG_SETMASK, &omask, NULL); + + INIT_LOCK (); + + /* Exec the shell. */ + (void) execve (SHELL_PATH, (char *const *) new_argv, __environ); + _exit (127); + } else if (pid < (pid_t) 0) { + /* The fork failed. */ + goto out; + } else { + /* Parent side. */ + do { + result = waitpid(pid, &status, 0); + } while (result == (pid_t)-1 && errno == EINTR); + if (result != pid) + status = -1; + } + + CLEANUP_RESET(0, oldtype); - return status; + DO_LOCK (); + if ((SUB_REF () == 0 + && (sigaction (SIGINT, &satbl_sigint->oact, NULL) + | sigaction (SIGQUIT, &satbl_sigquit->oact, NULL)) != 0) + || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) + { + status = -1; + } + goto out; + +out_restore_sigquit_and_sigint: + (void) sigaction (SIGQUIT, &satbl_sigquit->oact, NULL); +out_restore_sigint: + (void) sigaction (SIGINT, &satbl_sigint->oact, NULL); +out: + DO_UNLOCK (); + return status; } static int -system (const char * line) +xsystem (const char * line) { -fprintf(stderr, "*** %s(%p) %s\n", __FUNCTION__, line, line); - if (line == NULL) - /* Check that we have a command processor available. It might - not be available after a chroot(), for example. */ - return do_system ("exit 0") == 0; + if (line == NULL) + /* Check that we have a command processor available. It might + not be available after a chroot(), for example. */ + return do_system ("exit 0") == 0; - return do_system (line); + return do_system (line); } static void * other_thread(void *dat) { const char * s = (const char *)dat; -fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, dat); - return ((void *) system(s)); + return ((void *) xsystem(s)); } int main(int argc, char *argv[]) { - pthread_t pth; + pthread_t pth; - pthread_create(&pth, NULL, other_thread, "/bin/pwd" ); + pthread_create(&pth, NULL, other_thread, "/bin/sleep 30" ); - pthread_join(pth, NULL); + sleep(2); + pthread_cancel(pth); - return 0; + pthread_join(pth, NULL); + return 0; } - -- 2.7.4