# HG changeset patch # User jbe # Date 1497132163 -7200 # Node ID 28aab22e68b689511ce374ff88dc50d4548ea78c # Parent 245406b9e43cc2dbc0a7127e168ee373ed9d3b11 New implementation of SIGTERM handling diff -r 245406b9e43c -r 28aab22e68b6 moonbridge.c --- a/moonbridge.c Fri Jun 09 18:33:57 2017 +0200 +++ b/moonbridge.c Sun Jun 11 00:02:43 2017 +0200 @@ -755,6 +755,7 @@ char controlmsg; int fd; struct itimerval notimer = { { 0, }, { 0, } }; + moonbr_io_sigterm_setup(L); // NOTE: should not fail lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); if (lua_isnil(L, -1)) lua_pop(L, 1); else if (lua_pcall(L, 0, 0, 1)) { diff -r 245406b9e43c -r 28aab22e68b6 moonbridge_io.c --- a/moonbridge_io.c Fri Jun 09 18:33:57 2017 +0200 +++ b/moonbridge_io.c Sun Jun 11 00:02:43 2017 +0200 @@ -55,18 +55,12 @@ moonbr_io_return_prepared_errmsg(); \ } while (0) -#define MOONBR_IO_MAXSIGNUM 127 -static int moonbr_io_signalfds[2*(MOONBR_IO_MAXSIGNUM+1)]; -#define moonbr_io_signalfd_read(x) moonbr_io_signalfds[2*(x)+0] -#define moonbr_io_signalfd_write(x) moonbr_io_signalfds[2*(x)+1] - #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle" #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public" #define MOONBR_IO_LISTENER_MT_REGKEY "moonbridge_io_listener" #define MOONBR_IO_CHILD_MT_REGKEY "moonbridge_io_child" #define MOONBR_IO_CHILD_PT_REGKEY "moonbridge_io_child_pt" #define MOONBR_IO_SIGNALS_REGKEY "moonbridge_io_signals" -#define MOONBR_IO_SIGNALSOCKETS_REGKEY "moonbridge_io_signalsockets" #ifdef MOONBR_IO_USE_TLS @@ -123,6 +117,8 @@ pid_t pid; } moonbr_io_child_t; +static volatile sig_atomic_t moonbr_io_sigterm_flag = 0; + static int moonbr_io_yield(lua_State *L) { return lua_yield(L, lua_gettop(L)); } @@ -1627,48 +1623,17 @@ moonbr_io_yield_wrapper(moonbr_io_wait_yield, moonbr_io_wait_call); -static void moonbr_io_signalhandler(int sig) { - int errno2 = errno; - char buf[1] = {'.'}; - write(moonbr_io_signalfd_write(sig), buf, 1); - errno = errno2; +static void moonbr_io_sigterm_handler(int sig) { + moonbr_io_sigterm_flag = 1; } -static int moonbr_io_signalsocket(lua_State *L) { - int sig, fd; - if (lua_type(L, 1) == LUA_TSTRING) { - lua_getfield(L, LUA_REGISTRYINDEX, MOONBR_IO_SIGNALS_REGKEY); - lua_pushvalue(L, 1); - lua_gettable(L, -2); - sig = lua_tointeger(L, -1); - if (!sig) { - lua_pushvalue(L, 1); - luaL_error(L, "Unknown signal \"%s\"", lua_tostring(L, 1)); - } - } else { - sig = luaL_checkinteger(L, 1); - } - if (sig < 1 || sig > MOONBR_IO_MAXSIGNUM) { - luaL_error(L, "Signal number %i out of range", sig); - } - if (moonbr_io_signalfd_read(sig) < 0) { - if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, moonbr_io_signalfds+2*sig)) { - luaL_error(L, "Could not create socket pair for signal queueing"); - } - } - errno = 0; - signal(sig, moonbr_io_signalhandler); - if (errno) luaL_error(L, "Could not install signal handler (invalid signal number %i?)", sig); - lua_getfield(L, LUA_REGISTRYINDEX, MOONBR_IO_SIGNALSOCKETS_REGKEY); - lua_pushinteger(L, sig); - lua_gettable(L, -2); - if (!lua_isnil(L, -1)) return 1; - fd = dup(moonbr_io_signalfd_read(sig)); /* avoid close on Lua error */ - if (fd < 0) luaL_error(L, "Could not create duplicated file descriptor for signal socket"); - moonbr_io_pushhandle(L, fd); - lua_pushinteger(L, sig); - lua_pushvalue(L, -2); - lua_settable(L, -5); /* store reference to signal socket in cache table */ +int moonbr_io_sigterm_setup(lua_State *L) { + signal(SIGTERM, moonbr_io_sigterm_handler); + return 0; +} + +static int moonbr_io_sigterm_received(lua_State *L) { + lua_pushboolean(L, moonbr_io_sigterm_flag); return 1; } @@ -1712,7 +1677,10 @@ int fd, isnum; int nfds = 0; fd_set readfds, writefds, exceptfds; - struct timeval timeout = {0, }; + struct timespec timeout = {0, }; + int use_timeout = 0; + int check_sigterm = 0; + sigset_t mask, orig_mask; int status; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1784,13 +1752,31 @@ return 2; } else if (isnum && n>=0 && n<100000000) { timeout.tv_sec = n; - timeout.tv_usec = 1e6 * (n - timeout.tv_sec); + timeout.tv_nsec = 1e9 * (n - timeout.tv_sec); } else { luaL_argcheck(L, 0, 3, "not a valid timeout"); } - status = select(nfds, &readfds, &writefds, &exceptfds, &timeout); - } else { - status = select(nfds, &readfds, &writefds, &exceptfds, NULL); + use_timeout = 1; + } + if (!lua_isnoneornil(L, 4)) luaL_checktype(L, 4, LUA_TBOOLEAN); + check_sigterm = lua_toboolean(L, 4); + if (check_sigterm) { + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigprocmask(SIG_BLOCK, &mask, &orig_mask); + if (moonbr_io_sigterm_flag) { + if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); + lua_pushboolean(L, 0); + lua_pushliteral(L, "SIGTERM received"); + return 2; + } + } + status = pselect(nfds, &readfds, &writefds, &exceptfds, use_timeout ? &timeout : NULL, check_sigterm ? &orig_mask : NULL); + if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); + if (moonbr_io_sigterm_flag) { + lua_pushboolean(L, 0); + lua_pushliteral(L, "SIGTERM received"); + return 2; } if (status == -1) { if (errno == EINTR) { @@ -2026,7 +2012,8 @@ {"locallisten", moonbr_io_locallisten}, {"tcplisten", moonbr_io_tcplisten}, {"exec", moonbr_io_exec}, - {"signalsocket", moonbr_io_signalsocket}, + {"sigterm_setup", moonbr_io_sigterm_setup}, + {"sigterm_received", moonbr_io_sigterm_received}, {"getpid", moonbr_io_getpid}, {"poll", moonbr_io_poll}, {"timeref", moonbr_io_timeref}, @@ -2048,13 +2035,6 @@ int luaopen_moonbridge_io(lua_State *L) { signal(SIGPIPE, SIG_IGN); /* generate I/O errors instead of signal 13 */ - { - int i; - for (i=0; i<=MOONBR_IO_MAXSIGNUM; i++) { - moonbr_io_signalfd_read(i) = -1; - moonbr_io_signalfd_write(i) = -1; - } - } lua_newtable(L); // module @@ -2132,9 +2112,6 @@ lua_setfield(L, -3, "signals"); lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_SIGNALS_REGKEY); - lua_newtable(L); // signal sockets - lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_SIGNALSOCKETS_REGKEY); - luaL_setfuncs(L, moonbr_io_module_funcs, 0); return 1; diff -r 245406b9e43c -r 28aab22e68b6 moonbridge_io.h --- a/moonbridge_io.h Fri Jun 09 18:33:57 2017 +0200 +++ b/moonbridge_io.h Sun Jun 11 00:02:43 2017 +0200 @@ -1,5 +1,6 @@ void moonbr_io_pushhandle(lua_State *L, int fd); void moonbr_io_closehandle(lua_State *L, int idx, int reset); +int moonbr_io_sigterm_setup(lua_State *L); int luaopen_moonbridge_io(lua_State *L); diff -r 245406b9e43c -r 28aab22e68b6 reference.txt --- a/reference.txt Fri Jun 09 18:33:57 2017 +0200 +++ b/reference.txt Sun Jun 11 00:02:43 2017 +0200 @@ -377,7 +377,7 @@ nil (as first return value) plus an error message (as second return value). -### moonbridge_io.poll(input_set, output_set, timeout) +### moonbridge_io.poll(input_set, output_set, timeout, wakeup_on_sigterm) This function waits for at least one of the given file descriptors and/or I/O handles to be ready for input or output. The two sets of file descriptors @@ -385,9 +385,15 @@ which does evaluate to true, e.g. input_set = {[socketA] = true}. If a set is nil, it is treated as being empty. +Returns false (plus a notice as second return value) in case of timeout. If the +4th parameter is set to true, also returns false (plus a notice) when a SIGTERM +has been received since a corresponding signal handler has been installed with +moonbridge_io.sigterm_setup(). + Returns true when at least one file descriptor or handle is ready for reading -or writing respectively, or if a signal has been received during waiting. -Returns false (plus a notice as second return value) in case of timeout. +or writing respectively. The function may also return true if signals have been +received during waiting (unless the 4th parameter is set to true and a SIGTERM +was received). ### moonbridge_io.signals @@ -396,17 +402,19 @@ number (e.g. 9 or 15, respectively). -### moonbridge_io.signalsocket(signal) +### moonbridge_io.sigterm_received() -This function installs a signal handler. As argument, either the signal number -is passed (e.g. 15) or a name (e.g. "TERM"). The function returns a socket -object that receives a character (".") each time a signal is received. +Returns true if a SIGTERM was received after moonbridge_io.sigterm_setup() has +installed a corresponding signal handler. The function will then always return +true until the process terminates. + -The function can be called multiple times, in which case the same socket object -is returned. The returned socket should never be closed by the caller. +### moonbridge_io.sigterm_setup() -The process should not be forked after calling this function (except for -replacing the process with another program). +This function installs a signal handler for SIGTERM. Use the function +moonbridge_io.sigterm_received() to check whether the signal has been received. +In addition, moonbridge_io.poll(...) will wakeup prematurely if the +4th parameter is set to true. ### moonbridge_io.tcpconnect(hostname, port)