|  | /* | 
|  | * 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 <signal.h> | 
|  | #include <stdio.h> | 
|  | #ifdef HAVE_SYS_POLL_H | 
|  | #include <sys/poll.h> | 
|  | #endif | 
|  | #include <unistd.h> | 
|  |  | 
|  | #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 ); | 
|  | action.sa_handler = SIG_IGN; | 
|  | sigaction( SIGXFSZ, &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 ); | 
|  | } |