The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Monday, November 28, 2011

Some Lua trickery

In my previous post, I presented this bit of Lua code:

process = require("org.conman.process")

-- --------------------------------------------------------------------
-- process limits added because an earlier version of the code actually
-- crashed the server it was running on, due to resource exhaustion.
-- --------------------------------------------------------------------

process.limits.hard.cpu  = "10m"	-- 10 minutes
process.limits.hard.core =  0		-- no core file
process.limits.hard.data = "20m"	-- 20 MB

It looks like a simple assignment to set process limits, yet under Unix, you need to call setrlimit(). What's happening under the hood (so to speak) is that it's easy to intercept assignments to tables (Lua “go-to” data structure) and that's exactly what's going on here. During the process of registering the module org.conman.process (more on the name later) we create some fake structures for the hard limits (and soft limits, but since it's similar, I'll skip that part) and attach a metatable, which contains code to intercept both reads and writes so we can do a bit of magic:

#define SYS_LIMIT_HARD	"rlimit_hard"
#define SYS_LIMIT_SOFT	"rlimit_soft"

static const struct luaL_reg mhlimit_reg[] =
{
  { "__index" 		, mhlimitlua___index	} ,
  { "__newindex"	, mhlimitlua___newindex	} ,
  { NULL		, NULL			}
};

static const struct luaL_reg mslimit_reg[] =
{
  { "__index"		, mslimitlua___index	} ,
  { "__newindex"	, mslimitlua___newindex	} ,
  { NULL		, NULL			}
};

int luaopen_org_conman_process(lua_State *const L)
{
  void *udata;
  
  assert(L != NULL);
  
  luaL_newmetatable(L,SYS_LIMIT_HARD);
  luaL_register(L,NULL,mhlimit_reg);
  
  luaL_newmetatable(L,SYS_LIMIT_SOFT);
  luaL_register(L,NULL,mslimit_reg);
  
  luaL_register(L,"org.conman.process",mprocess_reg);
  lua_createtable(L,0,2);
  
  udata = lua_newuserdata(L,sizeof(int));
  luaL_getmetatable(L,SYS_LIMIT_HARD);
  lua_setmetatable(L,-2);
  lua_setfield(L,-2,"hard");
  
  udata = lua_newuserdata(L,sizeof(int));
  luaL_getmetatable(L,SYS_LIMIT_SOFT);
  lua_setmetatable(L,-2);
  lua_setfield(L,-2,"soft");
  
  lua_setfield(L,-2,"limits");  
  return 1;
}

When Lua sees an assignment to the process.limits.hard table, it calls mhlimit_lua___newindex(), where the magic happens:

static int mhlimitlua___newindex(lua_State *const L)
{
  struct rlimit  limit;
  void          *ud;
  const char    *tkey;
  int            key;
  lua_Integer    ival;

  assert(L != NULL);
  
  ud   = luaL_checkudata(L,1,SYS_LIMIT_HARD);
  tkey = luaL_checkstring(L,2);
  
  if (!mlimit_trans(&key,tkey))
    return luaL_error(L,"Illegal limit resource: %s",tkey);

  if (lua_isnumber(L,3))
    ival = lua_tointeger(L,3);
  else if (lua_isstring(L,3))
  {
    const char *tval;
    const char *unit;
    
    tval = lua_tostring(L,3);
    ival = strtoul(tval,(char **)&unit,10);

    if (!mlimit_valid_suffix(&ival,key,unit))
      return luaL_error(L,"Illegal suffix: %c",*unit);
  } 
  else
    return luaL_error(L,"Non-supported type");

  limit.rlim_cur = ival;
  limit.rlim_max = ival;
  
  setrlimit(key,&limit);
  return 0;
}

We basically take the key we're given, say, “cpu”, and translate it to the appropriate value (which happens in mlimit_trans()—nothing terribly interesting, it just maps the string to the appropriate constant value, in this example, RLIMIT_CPU) and the same for the value; if it's a number, we'll use that and if it's a string, we'll convert it to a value and use any suffix to modify the value. For our example, “cpu”, it's a meaure of time, so the suffix “m” means “minutes.” mlimit_valid_suffix() handles this and again, it's pretty straightforward code.

I think it's a pretty cool trick, but I can see why some might not like the idea of masking what amounts to a system call with what looks like a simple assignment, since it does have side effects outside of the simple assignment, but I like the way it looks, and it's a more “natural” or even “Luaish” way of specifying the intent of the code.

Now, on to the name of the module, org.conman.process. When I first started playing around with Lua I wrote a few modules that did similar operations as existing modules, with the same names. One example is syslog. There's an existing Lua syslog module, but I don't like how it works, so I wrote my own.

The problem now becomes, what if I want to use a module that uses the existing Lua syslog module, but the rest of my code uses mine? If they both have the same name, some code is going to get a nasty surprise. To work around that, I decided to put all my modules under a “namespace” I control and is not likely to cause any conflicts with any existing (or even future) modules. Thus, the org.conman namespace.

Obligatory Picture

[The future's so bright, I gotta wear shades]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.