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 | |
parent | 33d6ea6d65d97d2eb5a962b4b7ead21ea0176110 (diff) | |
download | luxio-ad5af52f76de82e6fa4607cc339ef4eb8e6370d6.tar.gz |
Bind sigaction(2)
-rw-r--r-- | config.ld | 1 | ||||
-rw-r--r-- | examples/sigaction.lua | 15 | ||||
-rw-r--r-- | luxio.c | 260 | ||||
-rw-r--r-- | luxio_constants.inc.in | 10 | ||||
-rw-r--r-- | tests/test-sigaction.lua | 68 |
5 files changed, 353 insertions, 1 deletions
@@ -1,4 +1,5 @@ file = { "luxio.c", "luxio/simple.lua" } +examples = { "examples" } format = "discount" project = "Luxio Light UNIX I/O" title = project diff --git a/examples/sigaction.lua b/examples/sigaction.lua new file mode 100644 index 0000000..ef89225 --- /dev/null +++ b/examples/sigaction.lua @@ -0,0 +1,15 @@ +local l = require "luxio" +local io = require "io" +local os = require "os" + +function callback(signo) + print("SIGINT received...") +end + +sa = {["sa_handler"] = callback} + +r, errno = l.sigaction(l.SIGINT, sa) +if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(errno))) + os.exit(os.EXIT_FAILURE) +end @@ -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; } diff --git a/luxio_constants.inc.in b/luxio_constants.inc.in index 9a5513b..ef3cadb 100644 --- a/luxio_constants.inc.in +++ b/luxio_constants.inc.in @@ -214,6 +214,8 @@ static const struct { ? E(WCONTINUED), /* signals */ + {"SIG_DFL", (int) SIG_DFL}, + {"SIG_IGN", (int) SIG_IGN}, E(SIGHUP), E(SIGINT), E(SIGQUIT), @@ -227,6 +229,14 @@ static const struct { E(SIGTERM), E(SIGUSR1), E(SIGUSR2), + E(SA_SIGINFO), + E(SA_NOCLDSTOP), + E(SA_ONSTACK), + E(SA_RESETHAND), + E(SA_RESTART), + E(SA_SIGINFO), + E(SA_NOCLDWAIT), + E(SA_NODEFER), ? E(SIGCHLD), ? E(SIGCONT), ? E(SIGSTOP), diff --git a/tests/test-sigaction.lua b/tests/test-sigaction.lua new file mode 100644 index 0000000..8f2e312 --- /dev/null +++ b/tests/test-sigaction.lua @@ -0,0 +1,68 @@ +local os = require "os" +local l = require "luxio" +local io = require "io" + +function MySigInfoCallback(signo, info) + print("MySigInfoCallback!", signo, info) + + for k, v in pairs(info) do + print(k, v) + end +end + +function MySIGTERMCallback(signo) + print("SIGTERM received", signo) + os.exit(os.EXIT_SUCCESS) +end + +function MySIGUSR1Callback(signo) + print("SIGUSR1 received, deregistering this callback now") + sa = {["sa_handler"] = l.SIG_DFL, sa_flags = 0} + + r, origsa = l.sigaction(l.SIGUSR1, sa) + if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(origsa))) + os.exit(os.EXIT_FAILURE) + end +end + +sa = {["sa_sigaction"] = MySigInfoCallback, ["sa_flags"] = l.SA_SIGINFO} + +r, origsa = l.sigaction(l.SIGINT, sa) +if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(origsa))) + os.exit(os.EXIT_FAILURE) +end + +sa = {["sa_handler"] = MySIGTERMCallback} + +r, origsa = l.sigaction(l.SIGTERM, sa) +if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(origsa))) + os.exit(os.EXIT_FAILURE) +end + +sa = {["sa_handler"] = MySIGUSR1Callback} + +r, origsa = l.sigaction(l.SIGUSR1, sa) +if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(origsa))) + os.exit(os.EXIT_FAILURE) +end + +sa = {["sa_handler"] = l.SIG_IGN} + +r, origsa = l.sigaction(l.SIGUSR2, sa) +if r == -1 then + io.stderr:write(("sigaction: %s\n"):format(l.strerror(origsa))) + os.exit(os.EXIT_FAILURE) +end + +for k, v in pairs(origsa) do + print(k, v) +end + +while (true) do + print("sleep!") + l.sleep(2) +end |