/* * Server signal handling * * Copyright (C) 2003 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_SYS_POLL_H #include #endif #include #include "file.h" #include "object.h" #include "process.h" #include "thread.h" #if defined(linux) && defined(__SIGRTMIN) /* the signal used by linuxthreads as exit signal for clone() threads */ # define SIG_PTHREAD_CANCEL (__SIGRTMIN+1) #endif typedef void (*signal_callback)(void); struct handler { struct object obj; /* object header */ struct fd *fd; /* file descriptor for the pipe side */ int pipe_write; /* unix fd for the pipe write side */ volatile int pending; /* is signal pending? */ signal_callback callback; /* callback function */ }; static void handler_dump( struct object *obj, int verbose ); static void handler_destroy( struct object *obj ); static const struct object_ops handler_ops = { sizeof(struct handler), /* size */ handler_dump, /* dump */ no_add_queue, /* add_queue */ NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* satisfied */ no_get_fd, /* get_fd */ handler_destroy /* destroy */ }; static void handler_poll_event( struct fd *fd, int event ); static const struct fd_ops handler_fd_ops = { NULL, /* get_poll_events */ handler_poll_event, /* poll_event */ no_flush, /* flush */ no_get_file_info, /* get_file_info */ no_queue_async /* queue_async */ }; static struct handler *handler_sighup; static struct handler *handler_sigterm; static struct handler *handler_sigint; static struct handler *handler_sigchld; static struct handler *handler_sigio; static sigset_t blocked_sigset; /* create a signal handler */ static struct handler *create_handler( signal_callback callback ) { struct handler *handler; int fd[2]; if (pipe( fd ) == -1) return NULL; if (!(handler = alloc_object( &handler_ops ))) { close( fd[0] ); close( fd[1] ); return NULL; } handler->pipe_write = fd[1]; handler->pending = 0; handler->callback = callback; if (!(handler->fd = create_anonymous_fd( &handler_fd_ops, fd[0], &handler->obj ))) { release_object( handler ); return NULL; } set_fd_events( handler->fd, POLLIN ); return handler; } /* handle a signal received for a given handler */ static void do_signal( struct handler *handler ) { if (!handler->pending) { char dummy = 0; handler->pending = 1; write( handler->pipe_write, &dummy, 1 ); } } static void handler_dump( struct object *obj, int verbose ) { struct handler *handler = (struct handler *)obj; fprintf( stderr, "Signal handler fd=%p\n", handler->fd ); } static void handler_destroy( struct object *obj ) { struct handler *handler = (struct handler *)obj; if (handler->fd) release_object( handler->fd ); close( handler->pipe_write ); } static void handler_poll_event( struct fd *fd, int event ) { struct handler *handler = get_fd_user( fd ); if (event & (POLLERR | POLLHUP)) { /* this is not supposed to happen */ fprintf( stderr, "wineserver: Error on signal handler pipe\n" ); release_object( handler ); } else if (event & POLLIN) { char dummy; handler->pending = 0; read( get_unix_fd( handler->fd ), &dummy, 1 ); handler->callback(); } } /* SIGHUP callback */ static void sighup_callback(void) { #ifdef DEBUG_OBJECTS dump_objects(); #endif } /* SIGTERM callback */ static void sigterm_callback(void) { flush_registry(); exit(1); } /* SIGINT callback */ static void sigint_callback(void) { kill_all_processes( NULL, 1 ); flush_registry(); exit(1); } /* SIGHUP handler */ static void do_sighup() { do_signal( handler_sighup ); } /* SIGTERM handler */ static void do_sigterm() { do_signal( handler_sigterm ); } /* SIGINT handler */ static void do_sigint() { do_signal( handler_sigint ); } /* SIGCHLD handler */ static void do_sigchld() { do_signal( handler_sigchld ); } /* SIGIO handler */ #ifdef HAVE_SIGINFO_T_SI_FD static void do_sigio( int signum, siginfo_t *si, void *x ) { do_signal( handler_sigio ); do_change_notify( si->si_fd ); } #endif void init_signals(void) { struct sigaction action; if (!(handler_sighup = create_handler( sighup_callback ))) goto error; if (!(handler_sigterm = create_handler( sigterm_callback ))) goto error; if (!(handler_sigint = create_handler( sigint_callback ))) goto error; if (!(handler_sigchld = create_handler( sigchld_callback ))) goto error; if (!(handler_sigio = create_handler( sigio_callback ))) goto error; sigemptyset( &blocked_sigset ); sigaddset( &blocked_sigset, SIGCHLD ); sigaddset( &blocked_sigset, SIGHUP ); sigaddset( &blocked_sigset, SIGINT ); sigaddset( &blocked_sigset, SIGIO ); sigaddset( &blocked_sigset, SIGQUIT ); sigaddset( &blocked_sigset, SIGTERM ); #ifdef SIG_PTHREAD_CANCEL sigaddset( &blocked_sigset, SIG_PTHREAD_CANCEL ); #endif action.sa_mask = blocked_sigset; action.sa_flags = 0; action.sa_handler = do_sigchld; sigaction( SIGCHLD, &action, NULL ); #ifdef SIG_PTHREAD_CANCEL sigaction( SIG_PTHREAD_CANCEL, &action, NULL ); #endif action.sa_handler = do_sighup; sigaction( SIGHUP, &action, NULL ); action.sa_handler = do_sigint; sigaction( SIGINT, &action, NULL ); action.sa_handler = do_sigterm; sigaction( SIGQUIT, &action, NULL ); sigaction( SIGTERM, &action, NULL ); #ifdef HAVE_SIGINFO_T_SI_FD action.sa_sigaction = do_sigio; action.sa_flags = SA_SIGINFO; sigaction( SIGIO, &action, NULL ); #endif return; error: fprintf( stderr, "failed to initialize signal handlers\n" ); exit(1); } void close_signals(void) { sigprocmask( SIG_BLOCK, &blocked_sigset, NULL ); release_object( handler_sighup ); release_object( handler_sigterm ); release_object( handler_sigint ); release_object( handler_sigchld ); release_object( handler_sigio ); }