blog.poucet.org Rotating Header Image

January, 2009:

A SIGINT signal handler for lua

It has been a while since I last blogged, mostly due to the fact that I’ve been busy settling into my new place, travelling to IKEA, breaking IKEA mobility vans, buying furniture, etc.  I’ve started a little project on home networking, and I have a blog post in progress documenting the details.  But I thought I’d blog about something else in the meantime.

I’m currently on vacation in Barbados for Christmas and New Year, visiting my parents.  It would have been a great vacation, had it not been for the several mishaps.  The first day I stepped on a sea urchin, which led to me having several bouts of fevers over the next 2 weeks.  Additionally, I nearly landed on some corral cliffs after losing having the leash of my bodyboard snap and finding myself fighting pointlessly against a current that was too strong to even fight when I could stand.

Anyways, the reason I wanted to blog was because I’ve been contemplating about focussing more effort again on writing personal software projects.  I have a bunch becoming stale on my laptop, and would like to share them better.  In the past, on my previous blog, I would show snippets inside of blog posts, and I’ll probably do that again today to share something I wrote a while back.  However, this is not a very robust solution, and it makes it a pain to share bigger projects or to have others use it.  I have a VPS host, however the homepage I have on it is truly shameful.  Which brings up the question in this blog post: Does anyone have any suggestions for a nice layout for a homepage? It probably will be more organizational and less prose-based, as I can use my blog for prose.  Just a place to jot some details about myself, share various pieces of code (either in a darcs or a git repository), and put on some snippets with nice syntax highlighting.  Is the standard 3-column page still the hot meme, or is there something better?  And any decent suggestions on what color-scheme and typography to use?

So coming back to the source code snippet.  A while back, while working on some lua code, I wanted to have the ability to register lua code for the CTRL+C handler. The following piece of code does this by first registering a C function as a signal handler for SIGINT that sets a lua hook on the next line of lua call.  In the case that it takes too long to actually evaluate the debug hook, it temporarily sets the SIGINT back to SIG_DFL, such that two ctrl+c’s will kill the program as expected.

The use of it is fairly trivial, after loading the module with require 'signal', you simply call signal.set(function() ... some code ... end). If you want to reset the original signal handler that was set prior to your first signal.set you simply call it with no parameters.

/*------------------------------------------------------------------------------
 * signal.c - Allows for the registration of a lua signal handler for SIGINT.
 *
 * Authors: Christophe Poucet
 * License: BSD
 * Created: September 23, 2007
 *
 * Copyright 2007-2009 © Christophe Poucet. All Rights Reserved.
 *----------------------------------------------------------------------------*/

#include
#include "lua.h"

#include "lauxlib.h"
#include "lualib.h"

static lua_State *globalL = NULL;

static void (*old_handler)(int) = NULL;

static void laction(int i);

static void lstop (lua_State *L, lua_Debug *ar) {
  (void)ar;  /* unused arg. */
  lua_sethook(L, NULL, 0, 0);
  lua_getfield(L, LUA_REGISTRYINDEX, "handler");
  lua_pcall(L, 0, 0, 0);
  signal(SIGINT, laction);
}

static void laction (int i) {
  /* if another SIGINT occurs before lstop, terminate process(default action) */
  signal(i, SIG_DFL);
  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}

static int l_setsignal(lua_State *L) {
  globalL = L;
  luaL_checkany(L, 1);
  if (lua_isnil(L, 1)) {
    lua_pushnil(L);
    lua_setfield(L, LUA_REGISTRYINDEX, "handler");
    if (old_handler != NULL) {
      signal(SIGINT, old_handler);
      old_handler = NULL;
    }
  } else if (lua_isfunction(L, 1)) {
    lua_pushvalue(L, 1);
    lua_setfield(L, LUA_REGISTRYINDEX, "handler");
    if (old_handler != NULL) {
      signal(SIGINT, laction);
    } else {
      old_handler = signal(SIGINT, laction);
    }
  } else {
    luaL_error(L, "The argument should be either nil or a function");
  }
  return 0;
}

static const struct luaL_Reg s_signal [] = {
  {"set", l_setsignal},
  {NULL, NULL} /* sentinel */
};

int luaopen_signal(lua_State *L) {
  luaL_register(L, "signal", s_signal);
  return 1;
}