Skip to content

jracevedob/lunatik

 
 

Repository files navigation

Lunatik

Lunatik is a framework for scripting the Linux kernel with Lua. It is composed by the Lua interpreter modified to run in the kernel; a device driver (written in Lua =)) and a command line tool to load and run scripts and manage runtime environments from the user space; a C API to load and run scripts and manage runtime environments from the kernel; and Lua APIs for binding kernel facilities to Lua scripts. Lunatik also offers a shell script as a helper for managing its kernel modules.

Here is an example of a character device driver written in Lua using Lunatik to generate random ASCII printable characters:

-- /lib/modules/lua/passwd.lua
--
-- implements /dev/passwd for generate passwords
-- usage: $ sudo sbin/lunatik --run passwd
--        $ head -c <width> /dev/passwd

local device = require("device")
local linux  = require("linux")

local function nop() end -- do nothing

local s = linux.stat
local driver = {name = "passwd", open = nop, release = nop, mode = s.IRUGO}

function driver:read() -- read(2) callback
	-- generate random ASCII printable characters
	return string.char(linux.random(32, 126))
end

-- creates a new character device
device.new(driver)

Usage

make
sudo sbin/lunatik.sh install # copy lunatik.lua to /lib/modules/lua
sudo sbin/lunatik.sh start   # load Lunatik kernel modules
sudo sbin/lunatik.sh run     # execute sbin/lunatik REPL
Lunatik 3.0  Copyright (C) 2023 ring-0 Ltda.
> return 42 -- execute this line in the kernel
42

sbin/lunatik

usage: sbin/lunatik [[--run] <script>] [--stop <script>]
  • --run: create a new runtime environment to run the script /lib/modules/lua/<script>.lua
  • --stop: stop the runtime environment created to run the script <script>
  • default: start a REPL (Read–Eval–Print Loop)

sbin/lunatik.sh

usage: sbin/lunatik.sh {start|stop|restart|status|install|run}
  • start: load Lunatik kernel modules
  • stop: remove Lunatik kernel modules
  • restart: reload Lunatik kernel modules
  • status: show what Lunatik kernel modules are currently loaded
  • install: copy lunatik.lua device driver to /lib/modules/lua
  • run: execute sbin/lunatik REPL

Lua Version

Lunatik 3.0 is based on Lua 5.4 adapted to run in the kernel.

Floating-point numbers

Lunatik does not support floating-point arithmetic, thus it does not support __div nor __pow metamethods and the type number has only the subtype integer.

Lua API

Lunatik does not support both io and os libraries, and the given identifiers from the following libraries:

Lunatik modifies the following identifiers:

  • _VERSION: is defined as "Lua 5.4-kernel".
  • collectgarbage("count"): returns the total memory in use by Lua in bytes, instead of Kbytes.
  • package.path: is defined as "/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua".
  • require: only supports built-in or already linked C modules, that is, Lunatik cannot load kernel modules dynamically.

C API

Lunatik does not support luaL_Stream, luaL_execresult, luaL_fileresult, luaopen_io and luaopen_os.

Lunatik modifies luaL_openlibs to remove luaopen_io and luaopen_os.

Lunatik C API

#include <lunatik.h>

lunatik_runtime

int lunatik_runtime(lunatik_runtime_t **pruntime, const char *script, bool sleep);

lunatik_runtime() creates a new runtime environment then loads and runs the script /lib/modules/lua/<script>.lua as the entry point for this environment. It must only be called from process context. The runtime environment is composed by a Lua state, a lock type and a reference counter. If sleep is true, it will use a mutex for locking the runtime environment and the GFP_KERNEL flag for allocating new memory later on on lunatik_run() calls. Otherwise, it will use a spinlock and GFP_ATOMIC. lunatik_runtime() opens the Lua standard libraries present on Lunatik and, if sleep is true, it also opens the lunatik library to make them available for the script. If successful, lunatik_runtime() sets the address pointed by pruntime and Lua's extra space with a pointer for the new created runtime environment, sets the reference counter to 1 and then returns 0. Otherwise, it returns -ENOMEM, if insufficient memory is available; or -EINVAL, if it fails to load or run the script.

Example
-- /lib/modules/lua/mydevice.lua
function myread(len, off)
	return "42"
end
static lunatik_runtime_t *runtime;

static int __init mydevice_init(void)
{
	return lunatik_runtime(&runtime, "mydevice", true);
}

lunatik_stop

int lunatik_stop(lunatik_runtime_t *runtime);

lunatik_stop() closes the Lua state created for this runtime environment and decrements the reference counter. Once the reference counter is decremented to zero, the lock type and the memory allocated for the runtime environment are released. If the runtime environment has been released, it returns 1; otherwise, it returns 0.

lunatik_run

void lunatik_run(lunatik_runtime_t *runtime, <inttype> (*handler)(...), <inttype> &ret, ...);

lunatik_run() locks the runtime environment and calls the handler passing the associated Lua state as the first argument followed by the variadic arguments. If the Lua state has been closed, ret is set with -ENXIO; otherwise, ret is set with the result of handler(L, ...) call. Then, it restores the Lua stack and unlocks the runtime environment. It is defined as a macro.

Example
static int l_read(lua_State *L, char *buf, size_t len, loff_t *off)
{
	size_t llen;
	const char *lbuf;

	lua_getglobal(L, "myread");
	lua_pushinteger(L, len);
	lua_pushinteger(L, *off);
	if (lua_pcall(L, 2, 2, 0) != LUA_OK) { /* calls myread(len, off) */
		pr_err("%s\n", lua_tostring(L, -1));
		return -ECANCELED;
	}

	lbuf = lua_tolstring(L, -2, &llen);
	llen = min(len, llen);
	if (copy_to_user(buf, lbuf, llen) != 0)
		return -EFAULT;

	*off = (loff_t)luaL_optinteger(L, -1, *off + llen);
	return (ssize_t)llen;
}

static ssize_t mydevice_read(struct file *f, char *buf, size_t len, loff_t *off)
{
	ssize_t ret;
	lunatik_runtime_t *runtime = (lunatik_runtime_t *)f->private_data;

	lunatik_run(runtime, l_read, ret, buf, len, off);
	return ret;
}

lunatik_get

void lunatik_get(lunatik_runtime_t *runtime);

lunatik_get() increments the reference counter of this runtime environment.

lunatik_put

int lunatik_put(lunatik_runtime_t *runtime);

lunatik_put() decrements the reference counter of this runtime environment. If the runtime environment has been released, it returns 1; otherwise, it returns 0.

lunatik_toruntime

lunatik_runtime_t *lunatik_toruntime(lua_State *L);

lunatik_toruntime() returns the runtime environment referenced by the L's extra space.

Lunatik Lua APIs

lunatik

The lunatik library provides support to load and run scripts and manage runtime environments from Lua.

lunatik.runtime(script [, sleep])

lunatik.runtime() creates a new runtime environment then loads and runs the script /lib/modules/lua/<script>.lua as the entry point for this environment. It returns a userdata representing the runtime environment. If sleep is true or omitted, it will use a mutex and GFP_KERNEL; otherwise, it will use a spinlock and GFP_ATOMIC. lunatik.runtime() opens the Lua standard libraries present on Lunatik, but differently from lunatik_runtime(), it does not open the lunatik library.

lunatik.stop(runtime), runtime:stop()

lunatik.stop() stops the runtime environment and clear its reference from the runtime userdata.

device

The device library provides support for writting character device drivers in Lua.

device.new(driver)

device.new() returns a new device userdata and installs its driver in the system. The driver must be defined as a table containing the following field:

  • name: string defining the device name; it is used for creating the device file (e.g., /dev/<name>).

The driver table might optionally contain the following fields:

  • read: callback function to handle the read operation on the device file. It receives the driver table as the first argument followed by two integers, the length to be read and the file offset. It should return a string and, optionally, the updated offset. If the length of the returned string is greater than the requested length, the string will be corrected to that length. If the updated offset is not returned, the offset will be updated with offset + length.
  • write: callback function to handle the write operation on the device file. It receives the driver table as the first argument followed by the string to be written and an integer as the file offset. It might return optionally the written length followed by the updated offset. If the returned length is greater than the requested length, the returned length will be corrected. If the updated offset is not returned, the offset will be updated with offset + length.
  • open: callback function to handle the open operation on the device file. It receives the driver table and it is expected to return nothing.
  • release: callback function to handle the release operation on the device file. It receives the driver table and it is expected to return nothing.
  • mode: an integer specifying the device file mode.

If an operation callback is not defined, the device returns -ENXIO to VFS on its access.

device.delete(dev), dev:delete()

device.delete() removes a device driver specified by the dev userdata from the system.

linux

The linux library provides support for some Linux kernel facilities.

linux.random([m [, n]])

linux.random() mimics the behavior of math.random, but binding <linux/random.h>'s get_random_u32() and get_random_u64() APIs.

When called without arguments, produces an integer with all bits (pseudo)random. When called with two integers m and n, linux.random() returns a pseudo-random integer with uniform distribution in the range [m, n]. The call math.random(n), for a positive n, is equivalent to math.random(1, n).

linux.stat

linux.stat is a table that exports <linux/stat.h> integer flags to Lua.

  • "IRWXUGO": permission to read, write and execute for user, group and other.
  • "IRUGO": permission only to read for user, group and other.
  • "IWUGO": permission only to write for user, group and other.
  • "IXUGO": permission only to execute for user, group and other.

References

About

Lunatik is a framework for scripting the Linux kernel with Lua.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • C 75.7%
  • Assembly 16.5%
  • Lua 3.9%
  • Shell 2.1%
  • Makefile 1.8%