Skip to content

Instantly share code, notes, and snippets.

@mischief
Last active May 3, 2025 22:36
Show Gist options
  • Select an option

  • Save mischief/93efd00debeb9af83795424497b7a2d2 to your computer and use it in GitHub Desktop.

Select an option

Save mischief/93efd00debeb9af83795424497b7a2d2 to your computer and use it in GitHub Desktop.
luajit imsg
local ffi = require("ffi")
local unix = require("unix")
ffi.cdef [[
typedef uint32_t pid_t;
struct msgbuf;
struct imsgbuf {
struct msgbuf *w;
pid_t pid;
uint32_t maxsize;
int fd;
int flags;
};
struct imsg_hdr {
uint32_t type;
uint32_t len;
uint32_t peerid;
uint32_t pid;
};
struct imsg {
struct imsg_hdr hdr;
void *data;
struct ibuf *buf;
};
int imsgbuf_init(struct imsgbuf*, int);
int imsgbuf_read(struct imsgbuf*);
int imsgbuf_write(struct imsgbuf*);
int imsg_compose(struct imsgbuf*, uint32_t type, uint32_t id, pid_t pid, int fd, const void *data, size_t datalen);
void imsg_free(struct imsg*);
ssize_t imsg_get(struct imsgbuf*, struct imsg *img);
int imsg_get_data(struct imsg*, void*, size_t);
int imsg_get_type(struct imsg*);
size_t imsg_get_len(struct imsg*);
]]
local libutil = ffi.load("util")
local sizettype = ffi.typeof("size_t");
local charptype = ffi.typeof("char*")
local imsgbuftype = ffi.typeof("struct imsgbuf")
local imsgtype = ffi.typeof("struct imsg")
local t = {}
local imsgbufmeta = {}
imsgbufmeta.write = function(self)
local rv = libutil.imsgbuf_write(self.buf)
if rv ~= 0 then
error(unix.strerror(ffi.errno()))
end
end
imsgbufmeta.compose = function(self, type, id, pid, fd, data)
local rv = libutil.imsg_compose(self.buf, type, id, pid, fd, data, #data)
assert(rv == 1)
end
imsgbufmeta.compose_ct = function(self, typ, id, pid, fd, ct)
assert(type(ct) == "cdata")
local rv = libutil.imsg_compose(self.buf, typ, id, pid, fd, ct, ffi.sizeof(ct))
assert(rv == 1)
end
imsgbufmeta.read = function(self)
local rv = libutil.imsgbuf_read(self.buf)
return rv
end
local imsgmeta = {}
imsgmeta.type = function(self)
return libutil.imsg_get_type(self.imsg)
end
imsgmeta.len = function(self)
return libutil.imsg_get_len(self.imsg)
end
imsgmeta.as_ct = function(self, ct)
assert(type(ct) == "cdata")
local neu = ct()
local sz = ffi.sizeof(neu)
local rv = libutil.imsg_get_data(self.imsg, neu, sz)
if rv ~= 0 then
error("imsg_get_data: " .. unix.strerror(ffi.errno()))
end
return neu
end
local imsgtostring = function(self)
local sz = self:len()
local cp = ffi.new("char[?]", sz)
local rv = libutil.imsg_get_data(self.imsg, cp, sz)
if rv ~= 0 then
error("imsg_get_data: " .. unix.strerror(ffi.errno()))
end
return ffi.string(cp, sz)
end
imsgbufmeta.get = function(self)
local imsg = imsgtype()
local n = libutil.imsg_get(self.buf, imsg)
if n == -1 then
error("imsg_get error")
end
if n == 0 then
return 0, nil
end
ffi.gc(imsg, libutil.imsg_free)
return n, setmetatable({imsg=imsg}, {__index=imsgmeta,
__tostring=imsgtostring,
})
end
t.init = function(fd)
local buf = imsgbuftype()
if libutil.imsgbuf_init(buf, fd) == -1 then
error("imsgbuf_init failed")
end
return setmetatable({buf=buf}, {__index=imsgbufmeta})
end
return t
local os = assert(require("os"))
local ffi = assert(require("ffi"))
local imsg = assert(require("imsg"))
local unix = assert(require("unix"))
local TYP_STRING = 42
local TYP_MSG = 41
local msgtype = ffi.typeof("struct { int answer; char data[32]; }")
local child_main = function(imsg)
while true do
local rv = buf1:read()
if rv == 0 then
return 0
end
while true do
local n, msg = buf1:get()
if n == -1 then
error("read error?")
end
if n == 0 then
break
end
local typ = msg:type()
print("child", msg:type(), msg:len())
if typ == TYP_STRING then
print("child", msg)
elseif typ == TYP_MSG then
local m = msg:as_ct(msgtype)
print("child", m.answer, ffi.string(m.data))
end
end
end
end
local parent_main = function(imsg)
imsg:compose(TYP_STRING, 0, 0, -1, "hello world")
imsg:write()
local m = msgtype()
m.answer = 1234
ffi.copy(m.data, "fnord")
imsg:compose_ct(TYP_MSG, 0, 0, -1, m)
imsg:write()
return 0
end
p0, p1 = unix.socketpair()
local rv = unix.fork()
if rv == -1 then
error("fork")
end
if rv == 0 then
-- child
unix.close(p0)
buf1 = imsg.init(p1)
os.exit(child_main(buf1))
end
unix.close(p1)
buf0 = imsg.init(p0)
os.exit(parent_main(buf0))
local ffi = require("ffi")
local bit = require("bit")
ffi.cdef[[
typedef uint32_t pid_t;
int close(int);
int socketpair(int domain, int type, int protocol, int sv[2]);
pid_t fork(void);
char *strerror(int);
]]
local t = {}
t.close = ffi.C.close
t.fork = ffi.C.fork
t.strerror = function(eno)
return ffi.string(ffi.C.strerror(eno))
end
t.SOCK_STREAM = 1
t.SOCK_CLOEXEC = 0x8000
t.SOCK_NONBLOCK = 0x4000
t.AF_UNSPEC = 0
t.AF_UNIX = 1
t.PF_UNSPEC = t.AF_UNSPEC
t.socketpair = function()
local fds = ffi.new("int[?]", 2)
local r = ffi.C.socketpair(t.AF_UNIX, t.SOCK_STREAM, t.PF_UNSPEC, fds)
if r == -1 then
error(ffi.C.strerror(ffi.errno()))
end
return fds[0], fds[1]
end
return t
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment