From 9037f4bbfe3a6e57a06eda5ec2e668d0c8a9b1fc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 26 Mar 2003 01:32:18 +0000 Subject: [PATCH] Added generic signal handling mechanism based on pipes to synchronize signals with the main poll loop. --- server/Makefile.in | 1 + server/fd.c | 56 ---------- server/main.c | 12 +-- server/object.h | 5 + server/ptrace.c | 2 +- server/request.c | 1 + server/signal.c | 257 +++++++++++++++++++++++++++++++++++++++++++++ server/thread.h | 2 +- 8 files changed, 271 insertions(+), 65 deletions(-) create mode 100644 server/signal.c diff --git a/server/Makefile.in b/server/Makefile.in index ca0bb2adb7b..149e9ec78f5 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -33,6 +33,7 @@ C_SRCS = \ request.c \ semaphore.c \ serial.c \ + signal.c \ smb.c \ snapshot.c \ sock.c \ diff --git a/server/fd.c b/server/fd.c index 05347def63a..7417dee8aea 100644 --- a/server/fd.c +++ b/server/fd.c @@ -309,57 +309,10 @@ static void remove_poll_user( struct fd *fd, int user ) } -/* SIGHUP handler */ -static void sighup_handler() -{ -#ifdef DEBUG_OBJECTS - dump_objects(); -#endif -} - -/* SIGTERM handler */ -static void sigterm_handler() -{ - flush_registry(); - exit(1); -} - -/* SIGINT handler */ -static void sigint_handler() -{ - kill_all_processes( NULL, 1 ); - flush_registry(); - exit(1); -} - /* server main poll() loop */ void main_loop(void) { int ret; - sigset_t sigset; - struct sigaction action; - - /* block the signals we use */ - sigemptyset( &sigset ); - sigaddset( &sigset, SIGCHLD ); - sigaddset( &sigset, SIGHUP ); - sigaddset( &sigset, SIGINT ); - sigaddset( &sigset, SIGQUIT ); - sigaddset( &sigset, SIGTERM ); - sigprocmask( SIG_BLOCK, &sigset, NULL ); - - /* set the handlers */ - action.sa_mask = sigset; - action.sa_flags = 0; - action.sa_handler = sigchld_handler; - sigaction( SIGCHLD, &action, NULL ); - action.sa_handler = sighup_handler; - sigaction( SIGHUP, &action, NULL ); - action.sa_handler = sigint_handler; - sigaction( SIGINT, &action, NULL ); - action.sa_handler = sigterm_handler; - sigaction( SIGQUIT, &action, NULL ); - sigaction( SIGTERM, &action, NULL ); while (active_users) { @@ -380,16 +333,7 @@ void main_loop(void) } if (!active_users) break; /* last user removed by a timeout */ } - - sigprocmask( SIG_UNBLOCK, &sigset, NULL ); - - /* Note: we assume that the signal handlers do not manipulate the pollfd array - * or the timeout list, otherwise there is a race here. - */ ret = poll( pollfd, nb_users, diff ); - - sigprocmask( SIG_BLOCK, &sigset, NULL ); - if (ret > 0) { int i; diff --git a/server/main.c b/server/main.c index b68268eca17..8515525e61e 100644 --- a/server/main.c +++ b/server/main.c @@ -110,27 +110,25 @@ static void sigterm_handler() exit(1); /* make sure atexit functions get called */ } -/* initialize signal handling */ -static void signal_init(void) +int main( int argc, char *argv[] ) { + parse_args( argc, argv ); + + /* setup temporary handlers before the real signal initialization is done */ signal( SIGPIPE, SIG_IGN ); signal( SIGHUP, sigterm_handler ); signal( SIGINT, sigterm_handler ); signal( SIGQUIT, sigterm_handler ); signal( SIGTERM, sigterm_handler ); signal( SIGABRT, sigterm_handler ); -} -int main( int argc, char *argv[] ) -{ - parse_args( argc, argv ); - signal_init(); sock_init(); open_master_socket(); sync_namespace = create_namespace( 37, TRUE ); setvbuf( stderr, NULL, _IOLBF, 0 ); if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + init_signals(); init_registry(); main_loop(); diff --git a/server/object.h b/server/object.h index 256c99eade1..1815e2495c1 100644 --- a/server/object.h +++ b/server/object.h @@ -145,6 +145,11 @@ extern void flush_registry(void); extern void close_registry(void); extern void registry_close_handle( struct object *obj, obj_handle_t hkey ); +/* signal functions */ + +extern void init_signals(void); +extern void close_signals(void); + /* atom functions */ extern void close_atom_table(void); diff --git a/server/ptrace.c b/server/ptrace.c index 002bc8f63dc..5323007d103 100644 --- a/server/ptrace.c +++ b/server/ptrace.c @@ -109,7 +109,7 @@ static int handle_child_status( struct thread *thread, int pid, int status, int } /* handle a SIGCHLD signal */ -void sigchld_handler() +void sigchld_callback(void) { int pid, status; diff --git a/server/request.c b/server/request.c index 6b3b4b69883..d598486f442 100644 --- a/server/request.c +++ b/server/request.c @@ -784,6 +784,7 @@ static void close_socket_timeout( void *arg ) #ifdef DEBUG_OBJECTS /* shut down everything properly */ release_object( master_socket ); + close_signals(); close_global_hooks(); close_global_handles(); close_registry(); diff --git a/server/signal.c b/server/signal.c new file mode 100644 index 00000000000..3388a664340 --- /dev/null +++ b/server/signal.c @@ -0,0 +1,257 @@ +/* + * 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" + +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; + 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); +} + +/* SIGIO callback */ +static void sigio_callback(void) +{ + /* nothing here yet */ +} + +/* 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 */ +static void do_sigio( int signum, siginfo_t *si, void *x ) +{ + /* do_change_notify( si->si_fd ); */ + do_signal( handler_sigio ); +} + +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 ); + + action.sa_mask = blocked_sigset; + action.sa_flags = 0; + action.sa_handler = do_sigchld; + sigaction( SIGCHLD, &action, NULL ); + 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_sigaction = do_sigio; + action.sa_flags = SA_SIGINFO; + sigaction( SIGIO, &action, NULL ); + 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 ); +} diff --git a/server/thread.h b/server/thread.h index 21fcdcb9091..f30ef4c86cb 100644 --- a/server/thread.h +++ b/server/thread.h @@ -126,7 +126,7 @@ extern struct thread_snapshot *thread_snap( int *count ); /* ptrace functions */ -extern void sigchld_handler(); +extern void sigchld_callback(void); extern int get_ptrace_pid( struct thread *thread ); extern void detach_thread( struct thread *thread, int sig ); extern int attach_process( struct process *process );