---
config.ld | 1 +
examples/sigaction.lua | 15 +++
luxio.c | 235 ++++++++++++++++++++++++++++++++++++++-
luxio_constants.inc.in | 8 ++
tests/test-sigaction.lua | 60 ++++++++++
5 files changed, 318 insertions(+), 1 deletion(-)
create mode 100644 examples/sigaction.lua
create mode 100644 tests/test-sigaction.lua
diff --git a/config.ld b/config.ld
index ac2d734..ac19bc2 100644
--- a/config.ld
+++ b/config.ld
@@ -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
diff --git a/luxio.c b/luxio.c
index 1dca902..50f02b6 100644
--- a/luxio.c
+++ b/luxio.c
@@ -481,7 +481,237 @@ 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
+
+static struct {
+ lua_State *state;
+ lua_Hook orighook;
+ int orighookmask;
+ int orighookcount;
+ sigset_t origset;
+ int signo;
+ bool isinfo;
+ siginfo_t info;
+ int 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]);
+ 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.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 adding an sa_handler or sa_sigaction
+entry to sa_params, this entry should be set to nil or SIG_DFL.
+
+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;
+ lua_pushvalue(L, -2);
+ key = luaL_checkstring(L, -1);
+ lua_pop(L, 1);
+
+ if (strcmp(key, "sa_handler") == 0) {
+ if (lua_isfunction(L, -1)) {
+ luxio__signal_ctx.handlers[signo] = luaL_ref(L, LUA_REGISTRYINDEX);
+ luxio__signal_ctx.state = L;
+ } else if (lua_isnil(L, -1)) {
+ sa.sa_handler = SIG_DFL;
+ } else {
+ goto einval;
+ }
+
+ sa.sa_handler = luxio__sigaction_sa_handler;
+ } else if (strcmp(key, "sa_sigaction") == 0) {
+ if (lua_isfunction(L, -1)) {
+ luxio__signal_ctx.handlers[signo] = luaL_ref(L, LUA_REGISTRYINDEX);
+ luxio__signal_ctx.state = L;
+ } else if (lua_isnil(L, -1)) {
+ sa.sa_handler = SIG_DFL;
+ } else {
+ goto einval;
+ }
+
+ sa.sa_sigaction = luxio__sigaction_sigaction_handler;
+ } else if (strcmp(key, "sa_flags") == 0) {
+ sa.sa_flags = luaL_checkint(L, -1);
+ lua_pop(L, 1);
+ } else {
+ goto einval;
+ }
+ }
+
+ lua_pop(L, 1);
+
+ 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_mask);
+ lua_setfield(L, -2, "sa_mask");
+
+ lua_pushinteger(L, old_sa.sa_flags);
+ lua_setfield(L, -2, "sa_flags");
+ }
+
+ return 2;
+
+einval:
+ lua_pushinteger(L, -1);
+ lua_pushinteger(L, EINVAL);
+ return 2;
+}
+
/* TODO: pthread_sigmask(), sigprocmask() 3.3.5 */
/* TODO: sigpending() 3.3.6 */
/* TODO: sigsuspend() 3.3.7 */
@@ -4563,6 +4793,8 @@ luxio_functions[] = {
{ "chdir", luxio_chdir },
{ "getcwd", luxio_getcwd },
+ { "sigaction", luxio_sigaction },
+
{ "alarm", luxio_alarm },
{ "pause", luxio_pause },
{ "sleep", luxio_sleep },
@@ -4705,6 +4937,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..5ccdd08 100644
--- a/luxio_constants.inc.in
+++ b/luxio_constants.inc.in
@@ -227,6 +227,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..c32bb52
--- /dev/null
+++ b/tests/test-sigaction.lua
@@ -0,0 +1,60 @@
+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"] = nil}
+
+ 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
+
+for k, v in pairs(origsa) do
+ print(k, v)
+end
+
+while (true) do
+ print("sleep!")
+ l.sleep(2)
+end
--
2.21.0