Edit

kc3-lang/libevent/signal.c

Branch :

  • Show log

    Commit

  • Author : Nick Mathewson
    Date : 2009-05-22 15:09:43
    Hash : 499d0039
    Message : Try to contain the failure when we are running without socketpair(). Some win32 systems (mostly those using Kaspersky, it would seem) prevent us from faking socketpair(). This makes our signal notification code just not work. Our response since 1.4 has been to assert. For users who would rather work without signals than not work at all, this has been a regression from 1.3e. This patch makes adding signal events fail in this case; there's no reason to kill the whole process. svn:r1304

  • signal.c
  • /*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
    
    /*
     * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #ifdef WIN32
    #define WIN32_LEAN_AND_MEAN
    #include <winsock2.h>
    #include <windows.h>
    #undef WIN32_LEAN_AND_MEAN
    #endif
    #include <sys/types.h>
    #ifdef HAVE_SYS_TIME_H
    #include <sys/time.h>
    #endif
    #include <sys/queue.h>
    #ifdef HAVE_SYS_SOCKET_H
    #include <sys/socket.h>
    #endif
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    #include <errno.h>
    #ifdef HAVE_FCNTL_H
    #include <fcntl.h>
    #endif
    #include <assert.h>
    
    #include "event.h"
    #include "event-internal.h"
    #include "evsignal.h"
    #include "evutil.h"
    #include "log.h"
    
    struct event_base *evsignal_base = NULL;
    
    static void evsignal_handler(int sig);
    
    /* Callback for when the signal handler write a byte to our signaling socket */
    static void
    evsignal_cb(int fd, short what, void *arg)
    {
    	static char signals[1];
    #ifdef WIN32
    	SSIZE_T n;
    #else
    	ssize_t n;
    #endif
    
    	n = recv(fd, signals, sizeof(signals), 0);
    	if (n == -1)
    		event_err(1, "%s: read", __func__);
    }
    
    #ifdef HAVE_SETFD
    #define FD_CLOSEONEXEC(x) do { \
            if (fcntl(x, F_SETFD, 1) == -1) \
                    event_warn("fcntl(%d, F_SETFD)", x); \
    } while (0)
    #else
    #define FD_CLOSEONEXEC(x)
    #endif
    
    int
    evsignal_init(struct event_base *base)
    {
    	int i;
    
    	/* 
    	 * Our signal handler is going to write to one end of the socket
    	 * pair to wake up our event loop.  The event loop then scans for
    	 * signals that got delivered.
    	 */
    	if (evutil_socketpair(
    		    AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
    #ifdef WIN32
    		/* Make this nonfatal on win32, where sometimes people
    		   have localhost firewalled. */
    		event_warn("%s: socketpair", __func__);
    #else
    		event_err(1, "%s: socketpair", __func__);
    #endif
    		return -1;
    	}
    
    	FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
    	FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
    	base->sig.sh_old = NULL;
    	base->sig.sh_old_max = 0;
    	base->sig.evsignal_caught = 0;
    	memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
    	/* initialize the queues for all events */
    	for (i = 0; i < NSIG; ++i)
    		TAILQ_INIT(&base->sig.evsigevents[i]);
    
            evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
    
    	event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
    		EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
    	base->sig.ev_signal.ev_base = base;
    	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
    
    	return 0;
    }
    
    /* Helper: set the signal handler for evsignal to handler in base, so that
     * we can restore the original handler when we clear the current one. */
    int
    _evsignal_set_handler(struct event_base *base,
    		      int evsignal, void (*handler)(int))
    {
    #ifdef HAVE_SIGACTION
    	struct sigaction sa;
    #else
    	ev_sighandler_t sh;
    #endif
    	struct evsignal_info *sig = &base->sig;
    	void *p;
    
    	/*
    	 * resize saved signal handler array up to the highest signal number.
    	 * a dynamic array is used to keep footprint on the low side.
    	 */
    	if (evsignal >= sig->sh_old_max) {
    		int new_max = evsignal + 1;
    		event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
    			    __func__, evsignal, sig->sh_old_max));
    		p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
    		if (p == NULL) {
    			event_warn("realloc");
    			return (-1);
    		}
    
    		memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
    		    0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));
    
    		sig->sh_old_max = new_max;
    		sig->sh_old = p;
    	}
    
    	/* allocate space for previous handler out of dynamic array */
    	sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
    	if (sig->sh_old[evsignal] == NULL) {
    		event_warn("malloc");
    		return (-1);
    	}
    
    	/* save previous handler and setup new handler */
    #ifdef HAVE_SIGACTION
    	memset(&sa, 0, sizeof(sa));
    	sa.sa_handler = handler;
    	sa.sa_flags |= SA_RESTART;
    	sigfillset(&sa.sa_mask);
    
    	if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
    		event_warn("sigaction");
    		free(sig->sh_old[evsignal]);
    		return (-1);
    	}
    #else
    	if ((sh = signal(evsignal, handler)) == SIG_ERR) {
    		event_warn("signal");
    		free(sig->sh_old[evsignal]);
    		return (-1);
    	}
    	*sig->sh_old[evsignal] = sh;
    #endif
    
    	return (0);
    }
    
    int
    evsignal_add(struct event *ev)
    {
    	int evsignal;
    	struct event_base *base = ev->ev_base;
    	struct evsignal_info *sig = &ev->ev_base->sig;
    
    	if (ev->ev_events & (EV_READ|EV_WRITE))
    		event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
    	evsignal = EVENT_SIGNAL(ev);
    	assert(evsignal >= 0 && evsignal < NSIG);
    	if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
    		event_debug(("%s: %p: changing signal handler", __func__, ev));
    		if (_evsignal_set_handler(
    			    base, evsignal, evsignal_handler) == -1)
    			return (-1);
    
    		/* catch signals if they happen quickly */
    		evsignal_base = base;
    
    		if (!sig->ev_signal_added) {
    			if (event_add(&sig->ev_signal, NULL))
    				return (-1);
    			sig->ev_signal_added = 1;
    		}
    	}
    
    	/* multiple events may listen to the same signal */
    	TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
    
    	return (0);
    }
    
    int
    _evsignal_restore_handler(struct event_base *base, int evsignal)
    {
    	int ret = 0;
    	struct evsignal_info *sig = &base->sig;
    #ifdef HAVE_SIGACTION
    	struct sigaction *sh;
    #else
    	ev_sighandler_t *sh;
    #endif
    
    	/* restore previous handler */
    	sh = sig->sh_old[evsignal];
    	sig->sh_old[evsignal] = NULL;
    #ifdef HAVE_SIGACTION
    	if (sigaction(evsignal, sh, NULL) == -1) {
    		event_warn("sigaction");
    		ret = -1;
    	}
    #else
    	if (signal(evsignal, *sh) == SIG_ERR) {
    		event_warn("signal");
    		ret = -1;
    	}
    #endif
    	free(sh);
    
    	return ret;
    }
    
    int
    evsignal_del(struct event *ev)
    {
    	struct event_base *base = ev->ev_base;
    	struct evsignal_info *sig = &base->sig;
    	int evsignal = EVENT_SIGNAL(ev);
    
    	assert(evsignal >= 0 && evsignal < NSIG);
    
    	/* multiple events may listen to the same signal */
    	TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next);
    
    	if (!TAILQ_EMPTY(&sig->evsigevents[evsignal]))
    		return (0);
    
    	event_debug(("%s: %p: restoring signal handler", __func__, ev));
    
    	return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev)));
    }
    
    static void
    evsignal_handler(int sig)
    {
    	int save_errno = errno;
    
    	if (evsignal_base == NULL) {
    		event_warn(
    			"%s: received signal %d, but have no base configured",
    			__func__, sig);
    		return;
    	}
    
    	evsignal_base->sig.evsigcaught[sig]++;
    	evsignal_base->sig.evsignal_caught = 1;
    
    #ifndef HAVE_SIGACTION
    	signal(sig, evsignal_handler);
    #endif
    
    	/* Wake up our notification mechanism */
    	send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
    	errno = save_errno;
    }
    
    void
    evsignal_process(struct event_base *base)
    {
    	struct evsignal_info *sig = &base->sig;
    	struct event *ev, *next_ev;
    	sig_atomic_t ncalls;
    	int i;
    	
    	base->sig.evsignal_caught = 0;
    	for (i = 1; i < NSIG; ++i) {
    		ncalls = sig->evsigcaught[i];
    		if (ncalls == 0)
    			continue;
    		sig->evsigcaught[i] -= ncalls;
    
    		for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
    		    ev != NULL; ev = next_ev) {
    			next_ev = TAILQ_NEXT(ev, ev_signal_next);
    			if (!(ev->ev_events & EV_PERSIST))
    				event_del(ev);
    			event_active(ev, EV_SIGNAL, ncalls);
    		}
    
    	}
    }
    
    void
    evsignal_dealloc(struct event_base *base)
    {
    	int i = 0;
    	if (base->sig.ev_signal_added) {
    		event_del(&base->sig.ev_signal);
    		base->sig.ev_signal_added = 0;
    	}
    	for (i = 0; i < NSIG; ++i) {
    		if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL)
    			_evsignal_restore_handler(base, i);
    	}
    
    	EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]);
    	base->sig.ev_signal_pair[0] = -1;
    	EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]);
    	base->sig.ev_signal_pair[1] = -1;
    	base->sig.sh_old_max = 0;
    
    	/* per index frees are handled in evsignal_del() */
    	free(base->sig.sh_old);
    }