From 18cbb4d7d645016d1a9c0ee772c724d8c41db3bd Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 28 Feb 2008 10:15:21 +0000 Subject: signal handling to properly cleanup on SIGINT and SIGTERM --- tests/server/sockfilt.c | 196 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 172 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 2d1a47ba2..921ce6c2b 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -46,6 +46,40 @@ * * (Source originally based on sws.c) */ + +/* + * Signal handling notes for sockfilt + * ---------------------------------- + * + * This program is a single-threaded process. + * + * This program is intended to be highly portable and as such it must be kept as + * simple as possible, due to this the only signal handling mechanisms used will + * be those of ANSI C, and used only in the most basic form which is good enough + * for the purpose of this program. + * + * For the above reason and the specific needs of this program signals SIGHUP, + * SIGPIPE and SIGALRM will be simply ignored on systems where this can be done. + * If possible, signals SIGINT and SIGTERM will be handled by this program as an + * indication to cleanup and finish execution as soon as possible. This will be + * achieved with a single signal handler 'exit_signal_handler' for both signals. + * + * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal + * will just set to one the global var 'got_exit_signal' storing in global var + * 'exit_signal' the signal that triggered this change. + * + * Nothing fancy that could introduce problems is used, the program at certain + * points in its normal flow checks if var 'got_exit_signal' is set and in case + * this is true it just makes its way out of loops and functions in structured + * and well behaved manner to achieve proper program cleanup and termination. + * + * Even with the above mechanism implemented it is worthwile to note that other + * signals might still be received, or that there might be systems on which it + * is not possible to trap and ignore some of the above signals. This implies + * that for increased portability and reliability the program must be coded as + * if no signal was being ignored or handled at all. Enjoy it! + */ + #include "setup.h" /* portability help from the lib directory */ #ifdef HAVE_SIGNAL_H @@ -85,10 +119,6 @@ #define DEFAULT_LOGFILE "log/sockfilt.log" #endif -#ifdef SIGPIPE -static volatile int sigpipe; /* Why? It's not used */ -#endif - const char *serverlogfile = (char *)DEFAULT_LOGFILE; bool verbose = FALSE; @@ -103,6 +133,103 @@ enum sockmode { ACTIVE_DISCONNECT /* as a client, disconnected from server */ }; +/* do-nothing macro replacement for systems which lack siginterrupt() */ + +#ifndef HAVE_SIGINTERRUPT +#define siginterrupt(x,y) do {} while(0) +#endif + +/* vars used to keep around previous signal handlers */ + +typedef RETSIGTYPE (*SIGHANDLER_T)(int); + +static SIGHANDLER_T old_sighup_handler = SIG_ERR; +static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; +static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; +static SIGHANDLER_T old_sigint_handler = SIG_ERR; +static SIGHANDLER_T old_sigterm_handler = SIG_ERR; + +/* var which if set indicates that the program should finish execution */ + +SIG_ATOMIC_T got_exit_signal = 0; + +/* if next is set indicates the first signal handled in exit_signal_handler */ + +static volatile int exit_signal = 0; + +/* signal handler that will be triggered to indicate that the program + should finish its execution in a controlled manner as soon as possible. + The first time this is called it will set got_exit_signal to one and + store in exit_signal the signal that triggered its execution. */ + +static RETSIGTYPE exit_signal_handler(int signum) +{ + int old_errno = ERRNO; + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } + (void)signal(signum, exit_signal_handler); + SET_ERRNO(old_errno); +} + +static void install_signal_handlers(void) +{ +#ifdef SIGHUP + /* ignore SIGHUP signal */ + if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGHUP handler: 5s", strerror(ERRNO)); +#endif +#ifdef SIGPIPE + /* ignore SIGPIPE signal */ + if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGPIPE handler: 5s", strerror(ERRNO)); +#endif +#ifdef SIGALRM + /* ignore SIGALRM signal */ + if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGALRM handler: 5s", strerror(ERRNO)); +#endif +#ifdef SIGINT + /* handle SIGINT signal with our exit_signal_handler */ + if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGINT handler: 5s", strerror(ERRNO)); + else + siginterrupt(SIGINT, 1); +#endif +#ifdef SIGTERM + /* handle SIGTERM signal with our exit_signal_handler */ + if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGTERM handler: 5s", strerror(ERRNO)); + else + siginterrupt(SIGTERM, 1); +#endif +} + +static void restore_signal_handlers(void) +{ +#ifdef SIGHUP + if(SIG_ERR != old_sighup_handler) + (void)signal(SIGHUP, old_sighup_handler); +#endif +#ifdef SIGPIPE + if(SIG_ERR != old_sigpipe_handler) + (void)signal(SIGPIPE, old_sigpipe_handler); +#endif +#ifdef SIGALRM + if(SIG_ERR != old_sigalrm_handler) + (void)signal(SIGALRM, old_sigalrm_handler); +#endif +#ifdef SIGINT + if(SIG_ERR != old_sigint_handler) + (void)signal(SIGINT, old_sigint_handler); +#endif +#ifdef SIGTERM + if(SIG_ERR != old_sigterm_handler) + (void)signal(SIGTERM, old_sigterm_handler); +#endif +} + /* * fullread is a wrapper around the read() function. This will repeat the call * to read() until it actually has read the complete number of bytes indicated @@ -119,6 +246,11 @@ static ssize_t fullread(int filedes, void *buffer, size_t nbytes) do { rc = read(filedes, (unsigned char *)buffer + nread, nbytes - nread); + if(got_exit_signal) { + logmsg("signalled to die"); + return -1; + } + if(rc < 0) { error = ERRNO; if((error == EINTR) || (error == EAGAIN)) @@ -158,6 +290,11 @@ static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) do { wc = write(filedes, (unsigned char *)buffer + nwrite, nbytes - nwrite); + if(got_exit_signal) { + logmsg("signalled to die"); + return -1; + } + if(wc < 0) { error = ERRNO; if((error == EINTR) || (error == EAGAIN)) @@ -252,14 +389,6 @@ static void lograw(unsigned char *buffer, ssize_t len) logmsg("'%s'", data); } -#ifdef SIGPIPE -static void sigpipe_handler(int sig) -{ - (void)sig; /* prevent warning */ - sigpipe = 1; -} -#endif - /* sockfdp is a pointer to an established stream or CURL_SOCKET_BAD @@ -287,6 +416,11 @@ static bool juggle(curl_socket_t *sockfdp, unsigned char buffer[17010]; char data[16]; + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + return FALSE; + } + #ifdef HAVE_GETPPID /* As a last resort, quit if sockfilt process becomes orphan. Just in case parent ftpserver process has died without killing its sockfilt children */ @@ -359,6 +493,11 @@ static bool juggle(curl_socket_t *sockfdp, rc = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + return FALSE; + } + } while((rc == -1) && ((error = SOCKERRNO) == EINTR)); if(rc < 0) { @@ -549,6 +688,11 @@ static curl_socket_t sockdaemon(curl_socket_t sock, sclose(sock); return CURL_SOCKET_BAD; } + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + sclose(sock); + return CURL_SOCKET_BAD; + } totdelay += delay; delay *= 2; /* double the sleep for next attempt */ } @@ -712,18 +856,10 @@ int main(int argc, char *argv[]) #ifdef WIN32 win32_init(); atexit(win32_cleanup); -#else - -#ifdef SIGPIPE -#ifdef HAVE_SIGNAL - signal(SIGPIPE, sigpipe_handler); -#endif -#ifdef HAVE_SIGINTERRUPT - siginterrupt(SIGPIPE, 1); -#endif -#endif #endif + install_signal_handlers(); + #ifdef ENABLE_IPV6 if(!use_ipv6) #endif @@ -805,10 +941,22 @@ sockfilt_cleanup: sclose(msgsock); if(sock != CURL_SOCKET_BAD) - sclose(sock); + sclose(sock); if(wrotepidfile) - unlink(pidname); + unlink(pidname); + + restore_signal_handlers(); + + if(got_exit_signal) { + logmsg("============> sockfilt exits with signal (%d)", exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } logmsg("============> sockfilt quits"); return 0; -- cgit v1.2.3