summaryrefslogtreecommitdiff
path: root/luxio.c
diff options
context:
space:
mode:
authorRichard Ipsum <richardipsum@fastmail.co.uk>2019-06-08 21:25:40 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2019-06-10 09:39:14 +0100
commitad5af52f76de82e6fa4607cc339ef4eb8e6370d6 (patch)
tree68e7e9a5b925a564d65adb41eed500fec372e411 /luxio.c
parent33d6ea6d65d97d2eb5a962b4b7ead21ea0176110 (diff)
downloadluxio-ad5af52f76de82e6fa4607cc339ef4eb8e6370d6.tar.bz2
Bind sigaction(2)
Diffstat (limited to 'luxio.c')
-rw-r--r--luxio.c260
1 files changed, 259 insertions, 1 deletions
diff --git a/luxio.c b/luxio.c
index 1dca902..f3e298d 100644
--- a/luxio.c
+++ b/luxio.c
@@ -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;
}