diff options
author | Richard Ipsum <richardipsum@fastmail.co.uk> | 2019-06-08 21:25:40 +0100 |
---|---|---|
committer | Daniel Silverstone <dsilvers@digital-scurf.org> | 2019-06-10 09:39:14 +0100 |
commit | ad5af52f76de82e6fa4607cc339ef4eb8e6370d6 (patch) | |
tree | 68e7e9a5b925a564d65adb41eed500fec372e411 /luxio.c | |
parent | 33d6ea6d65d97d2eb5a962b4b7ead21ea0176110 (diff) | |
download | luxio-ad5af52f76de82e6fa4607cc339ef4eb8e6370d6.tar.bz2 |
Bind sigaction(2)
Diffstat (limited to 'luxio.c')
-rw-r--r-- | luxio.c | 260 |
1 files changed, 259 insertions, 1 deletions
@@ -481,7 +481,262 @@ luxio_kill(lua_State *L) /* 3.3.2 */ /* Signals are going to be almost impossible to do nicely and safely. */ /* TODO: Manipulate Signal Sets 3.3.3 */ -/* TODO: sigaction() 3.3.4 */ + +/* NSIG is not in POSIX, it's 64 on Linux and 33 on OpenBSD + * we define a value much larger than either to be on the safe side */ +#define LUXIO_NSIG 512 + +struct luxio_signal_handler { + lua_State *state; + int handler_fn; +}; + +static struct { + lua_Hook orighook; + int orighookmask; + int orighookcount; + sigset_t origset; + int signo; + bool isinfo; + siginfo_t info; + struct luxio_signal_handler handlers[LUXIO_NSIG]; + bool sigaction; +} luxio__signal_ctx; + +static void luxio__sigaction_hook(lua_State *L, lua_Debug *ar) +{ + int nargs = 1; + + /* Push the callback onto the stack using the Lua reference we */ + /* stored in the registry */ + lua_rawgeti(L, LUA_REGISTRYINDEX, + luxio__signal_ctx.handlers[luxio__signal_ctx.signo].handler_fn); + lua_pushinteger(L, luxio__signal_ctx.signo); + + if (luxio__signal_ctx.sigaction) { + nargs++; + + if (luxio__signal_ctx.isinfo) { + siginfo_t *info = &luxio__signal_ctx.info; + lua_newtable(L); + + lua_pushinteger(L, info->si_signo); + lua_setfield(L, -2, "si_signo"); + + lua_pushinteger(L, info->si_code); + lua_setfield(L, -2, "si_code"); + + lua_pushinteger(L, info->si_errno); + lua_setfield(L, -2, "si_errno"); + + lua_pushinteger(L, info->si_pid); + lua_setfield(L, -2, "si_pid"); + + lua_pushinteger(L, info->si_uid); + lua_setfield(L, -2, "si_uid"); + + lua_pushinteger(L, info->si_utime); + lua_setfield(L, -2, "si_utime"); + + lua_pushinteger(L, info->si_stime); + lua_setfield(L, -2, "si_stime"); + + lua_pushinteger(L, (lua_Integer) info->si_addr); + lua_setfield(L, -2, "si_addr"); + + lua_pushinteger(L, info->si_status); + lua_setfield(L, -2, "si_status"); + +#ifdef __linux__ + lua_pushinteger(L, info->si_band); + lua_setfield(L, -2, "si_band"); +#endif + } else { + lua_pushnil(L); + } + } + + lua_pcall(L, nargs, 0, 0); + + /* Restore original hook */ + lua_sethook(L, luxio__signal_ctx.orighook, + luxio__signal_ctx.orighookmask, + luxio__signal_ctx.orighookcount); + + /* The signal we're currently handling was blocked during signal delivery, + * but we also blocked everything manually in the handler, + * so we must now unblock this signal ourselves as well. + */ + sigdelset(&luxio__signal_ctx.origset, luxio__signal_ctx.signo); + + /* Restore the original signal mask */ + sigprocmask(SIG_SETMASK, &luxio__signal_ctx.origset, NULL); +} + +static void luxio__sigaction_common_handler(int signo, siginfo_t *info) +{ + sigset_t set; + lua_State *L = luxio__signal_ctx.handlers[signo].state; + + /* Block everything till we're done handling the signal on the lua side */ + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &luxio__signal_ctx.origset); + + luxio__signal_ctx.orighook = lua_gethook(L); + luxio__signal_ctx.orighookmask = lua_gethookmask(L); + luxio__signal_ctx.orighookcount = lua_gethookcount(L); + luxio__signal_ctx.signo = signo; + if (info != NULL) { + luxio__signal_ctx.isinfo = true; + luxio__signal_ctx.info = *info; + } else { + luxio__signal_ctx.isinfo = false; + } + + lua_sethook(L, luxio__sigaction_hook, + LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + +static void luxio__sigaction_sa_handler(int signo) +{ + luxio__signal_ctx.sigaction = false; + luxio__sigaction_common_handler(signo, NULL); +} + +static void luxio__sigaction_sigaction_handler(int signo, siginfo_t *info, void *context) +{ + luxio__signal_ctx.sigaction = true; + luxio__sigaction_common_handler(signo, info); +} + +/**% sigaction + * retval = sigaction(sig, sa_params) + * retval, errno = sigaction(sig, sa_params) + */ +/*** Examine and change a signal action. + +sigaction can be used to install a signal handler function. When this handler +is installed the default action for the signal, which is usually program +termination, is overriden. When a signal, such as SIGINT, is received, +the associated handler is called. + +A signal handler can be deregistered by setting sa_handler to nil or SIG_DFL. + +A signal can be ignored by setting sa_handler to SIG_IGN. + +There may only be one handler registered per signal at any given time, +if one Lua VM attempts to register a signal that is already registered by another +VM then sigaction shall return -1 and errno shall be set to ENOSPC. + +Return 0 on success, on error -1 is returned and errno will be set. +On success a table containing the currently installed signal mask and flags +is returned. + +Example usage: @{sigaction.lua} + +@tparam number sig signal to examine/change +@tparam sigaction-table sa_params sigaction parameters +@treturn number return value +@treturn sigaction-table|errno result table, or errno +@function sigaction +*/ + +/*** sigaction() table +@table sigaction-table +@field sa_handler function that will be the handler, may also be SIG_DFL or nil. +@field sa_sigaction function that will be a sa_sigaction handler, may also be SIG_DFL or nil. +@field sa_flags flags +*/ +static int +luxio_sigaction(lua_State *L) +{ + int signo; + struct sigaction sa = {.sa_handler = SIG_DFL}, old_sa; + + signo = luaL_checkinteger(L, 1); + + if (signo >= LUXIO_NSIG) { + goto einval; + } + + luaL_checktype(L, 2, LUA_TTABLE); + lua_pushnil(L); + + while (lua_next(L, 2)) { + const char *key; + /* push a copy of the key onto the stack + * so we can convert it into a string and + * leave the original untouched for lua_next + */ + lua_pushvalue(L, -2); + + key = luaL_checkstring(L, -1); + lua_pop(L, 1); + + if (strcmp(key, "sa_handler") == 0 || strcmp(key, "sa_sigaction") == 0) { + if (lua_isfunction(L, -1)) { + lua_State *sigstate = luxio__signal_ctx.handlers[signo].state; + + if (sigstate != NULL && sigstate != L) { + goto enospc; + } + + luxio__signal_ctx.handlers[signo].handler_fn = luaL_ref(L, LUA_REGISTRYINDEX); + luxio__signal_ctx.handlers[signo].state = L; + + if (strcmp(key, "sa_handler") == 0) { + sa.sa_handler = luxio__sigaction_sa_handler; + } else { + sa.sa_sigaction = luxio__sigaction_sigaction_handler; + } + } else if (lua_type(L, -1) == LUA_TNUMBER) { + int disposition = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + if (disposition == (int) SIG_DFL) { + sa.sa_handler = SIG_DFL; + } else if (disposition == (int) SIG_IGN) { + sa.sa_handler = SIG_IGN; + } else { + goto einval; + } + } else { + goto einval; + } + } else if (strcmp(key, "sa_flags") == 0) { + sa.sa_flags = luaL_checkint(L, -1); + lua_pop(L, 1); + } else { + goto einval; + } + } + + lua_pushinteger(L, sigaction(signo, &sa, &old_sa)); + if (errno != 0) { + lua_pushinteger(L, errno); + } else { + lua_newtable(L); + + lua_pushinteger(L, old_sa.sa_flags); + lua_setfield(L, -2, "sa_flags"); + } + + if (sa.sa_handler == SIG_DFL) { + luxio__signal_ctx.handlers[signo].state = NULL; + } + + return 2; + +einval: + lua_pushinteger(L, -1); + lua_pushinteger(L, EINVAL); + return 2; +enospc: + lua_pushinteger(L, -1); + lua_pushinteger(L, ENOSPC); + return 2; +} + /* TODO: pthread_sigmask(), sigprocmask() 3.3.5 */ /* TODO: sigpending() 3.3.6 */ /* TODO: sigsuspend() 3.3.7 */ @@ -4563,6 +4818,8 @@ luxio_functions[] = { { "chdir", luxio_chdir }, { "getcwd", luxio_getcwd }, + { "sigaction", luxio_sigaction }, + { "alarm", luxio_alarm }, { "pause", luxio_pause }, { "sleep", luxio_sleep }, @@ -4705,6 +4962,7 @@ luaopen_luxio(lua_State *L) NUMERIC_CONSTANT(DT_LNK); NUMERIC_CONSTANT(DT_SOCK); #endif + return 1; } |