File: //opt/alt/luajit/share/lua/syscall/linux/types.lua
-- Linux kernel types
local require, error, assert, tonumber, tostring,
setmetatable, pairs, ipairs, unpack, rawget, rawset,
pcall, type, table, string =
require, error, assert, tonumber, tostring,
setmetatable, pairs, ipairs, unpack, rawget, rawset,
pcall, type, table, string
-- TODO add __len to metatables of more
local function init(types)
local abi = require "syscall.abi"
local t, pt, s, ctypes = types.t, types.pt, types.s, types.ctypes
local ffi = require "ffi"
local bit = require "syscall.bit"
local h = require "syscall.helpers"
local addtype, addtype_var, addtype_fn, addraw2 = h.addtype, h.addtype_var, h.addtype_fn, h.addraw2
local ptt, reviter, mktype, istype, lenfn, lenmt, getfd, newfn
= h.ptt, h.reviter, h.mktype, h.istype, h.lenfn, h.lenmt, h.getfd, h.newfn
local ntohl, ntohl, ntohs, htons = h.ntohl, h.ntohl, h.ntohs, h.htons
local split, trim = h.split, h.trim
local c = require "syscall.linux.constants"
local mt = {} -- metatables
-- TODO cleanup this (what should provide this?)
local signal_reasons_gen = {}
local signal_reasons = {}
for k, v in pairs(c.SI) do
signal_reasons_gen[v] = k
end
signal_reasons[c.SIG.ILL] = {}
for k, v in pairs(c.SIGILL) do
signal_reasons[c.SIG.ILL][v] = k
end
signal_reasons[c.SIG.FPE] = {}
for k, v in pairs(c.SIGFPE ) do
signal_reasons[c.SIG.FPE][v] = k
end
signal_reasons[c.SIG.SEGV] = {}
for k, v in pairs(c.SIGSEGV) do
signal_reasons[c.SIG.SEGV][v] = k
end
signal_reasons[c.SIG.BUS] = {}
for k, v in pairs(c.SIGBUS) do
signal_reasons[c.SIG.BUS][v] = k
end
signal_reasons[c.SIG.TRAP] = {}
for k, v in pairs(c.SIGTRAP) do
signal_reasons[c.SIG.TRAP][v] = k
end
signal_reasons[c.SIG.CHLD] = {}
for k, v in pairs(c.SIGCLD) do
signal_reasons[c.SIG.CHLD][v] = k
end
signal_reasons[c.SIG.POLL] = {}
for k, v in pairs(c.SIGPOLL or {}) do
signal_reasons[c.SIG.POLL][v] = k
end
local addtypes = {
fdset = "fd_set",
clockid = "clockid_t",
sighandler = "sighandler_t",
aio_context = "aio_context_t",
clockid = "clockid_t",
}
local addstructs = {
ucred = "struct ucred",
sysinfo = "struct sysinfo",
nlmsghdr = "struct nlmsghdr",
rtgenmsg = "struct rtgenmsg",
ifinfomsg = "struct ifinfomsg",
ifaddrmsg = "struct ifaddrmsg",
rtattr = "struct rtattr",
rta_cacheinfo = "struct rta_cacheinfo",
nlmsgerr = "struct nlmsgerr",
nda_cacheinfo = "struct nda_cacheinfo",
ndt_stats = "struct ndt_stats",
ndtmsg = "struct ndtmsg",
ndt_config = "struct ndt_config",
utsname = "struct utsname",
fdb_entry = "struct fdb_entry",
seccomp_data = "struct seccomp_data",
rtnl_link_stats = "struct rtnl_link_stats",
statfs = "struct statfs64",
ifa_cacheinfo = "struct ifa_cacheinfo",
input_event = "struct input_event",
input_id = "struct input_id",
input_absinfo = "struct input_absinfo",
input_keymap_entry = "struct input_keymap_entry",
ff_replay = "struct ff_replay",
ff_trigger = "struct ff_trigger",
ff_envelope = "struct ff_envelope",
ff_constant_effect = "struct ff_constant_effect",
ff_ramp_effect = "struct ff_ramp_effect",
ff_condition_effect = "struct ff_condition_effect",
ff_periodic_effect = "struct ff_periodic_effect",
ff_rumble_effect = "struct ff_rumble_effect",
ff_effect = "struct ff_effect",
sock_fprog = "struct sock_fprog",
bpf_attr = "union bpf_attr",
user_cap_header = "struct user_cap_header",
user_cap_data = "struct user_cap_data",
xt_get_revision = "struct xt_get_revision",
vfs_cap_data = "struct vfs_cap_data",
ucontext = "ucontext_t",
mcontext = "mcontext_t",
tun_pi = "struct tun_pi",
tun_filter = "struct tun_filter",
vhost_vring_state = "struct vhost_vring_state",
vhost_vring_file = "struct vhost_vring_file",
vhost_vring_addr = "struct vhost_vring_addr",
vhost_memory_region = "struct vhost_memory_region",
vhost_memory = "struct vhost_memory",
}
for k, v in pairs(addtypes) do addtype(types, k, v) end
for k, v in pairs(addstructs) do addtype(types, k, v, lenmt) end
-- these ones not in table as not helpful with vararg or arrays TODO add more addtype variants
t.inotify_event = ffi.typeof("struct inotify_event")
pt.inotify_event = ptt("struct inotify_event") -- still need pointer to this
pt.perf_event_header = ptt("struct perf_event_header")
t.aio_context1 = ffi.typeof("aio_context_t[1]")
t.sock_fprog1 = ffi.typeof("struct sock_fprog[1]")
t.bpf_attr1 = ffi.typeof("union bpf_attr[1]")
t.perf_event_attr1 = ffi.typeof("struct perf_event_attr[1]")
t.user_cap_data2 = ffi.typeof("struct user_cap_data[2]")
-- luaffi gets confused if call ffi.typeof("...[?]") it calls __new so redefine as functions
local iocbs = ffi.typeof("struct iocb[?]")
t.iocbs = function(n, ...) return ffi.new(iocbs, n, ...) end
local sock_filters = ffi.typeof("struct sock_filter[?]")
t.sock_filters = function(n, ...) return ffi.new(sock_filters, n, ...) end
local bpf_insns = ffi.typeof("struct bpf_insn[?]")
t.bpf_insns = function(n, ...) return ffi.new(bpf_insns, n, ...) end
local iocb_ptrs = ffi.typeof("struct iocb *[?]")
t.iocb_ptrs = function(n, ...) return ffi.new(iocb_ptrs, n, ...) end
-- types with metatypes
-- Note 32 bit dev_t; glibc has 64 bit dev_t but we use syscall API which does not
local function makedev(major, minor)
if type(major) == "table" then major, minor = major[1], major[2] end
local dev = major or 0
if minor then dev = bit.bor(bit.lshift(bit.band(minor, 0xffffff00), 12), bit.band(minor, 0xff), bit.lshift(major, 8)) end
return dev
end
mt.device = {
index = {
major = function(dev)
local d = dev.dev
return bit.band(bit.rshift(d, 8), 0x00000fff)
end,
minor = function(dev)
local d = dev.dev
return bit.bor(bit.band(d, 0x000000ff), bit.band(bit.rshift(d, 12), 0x000000ff))
end,
device = function(dev) return tonumber(dev.dev) end,
},
newindex = {
device = function(dev, major, minor) dev.dev = makedev(major, minor) end,
},
__new = function(tp, major, minor)
return ffi.new(tp, makedev(major, minor))
end,
}
addtype(types, "device", "struct {dev_t dev;}", mt.device)
mt.sockaddr = {
index = {
family = function(sa) return sa.sa_family end,
},
}
addtype(types, "sockaddr", "struct sockaddr", mt.sockaddr)
-- cast socket address to actual type based on family, defined later
local samap_pt = {}
mt.sockaddr_storage = {
index = {
family = function(sa) return sa.ss_family end,
},
newindex = {
family = function(sa, v) sa.ss_family = c.AF[v] end,
},
__index = function(sa, k)
if mt.sockaddr_storage.index[k] then return mt.sockaddr_storage.index[k](sa) end
local st = samap_pt[sa.ss_family]
if st then
local cs = st(sa)
return cs[k]
end
error("invalid index " .. k)
end,
__newindex = function(sa, k, v)
if mt.sockaddr_storage.newindex[k] then
mt.sockaddr_storage.newindex[k](sa, v)
return
end
local st = samap_pt[sa.ss_family]
if st then
local cs = st(sa)
cs[k] = v
return
end
error("invalid index " .. k)
end,
__new = function(tp, init)
local ss = ffi.new(tp)
local family
if init and init.family then family = c.AF[init.family] end
local st
if family then
st = samap_pt[family]
ss.ss_family = family
init.family = nil
end
if st then
local cs = st(ss)
for k, v in pairs(init) do
cs[k] = v
end
end
return ss
end,
-- netbsd likes to see the correct size when it gets a sockaddr; Linux was ok with a longer one
__len = function(sa)
if samap_pt[sa.family] then
local cs = samap_pt[sa.family](sa)
return #cs
else
return s.sockaddr_storage
end
end,
}
-- experiment, see if we can use this as generic type, to avoid allocations.
addtype(types, "sockaddr_storage", "struct sockaddr_storage", mt.sockaddr_storage)
mt.sockaddr_in = {
index = {
family = function(sa) return sa.sin_family end,
port = function(sa) return ntohs(sa.sin_port) end,
addr = function(sa) return sa.sin_addr end,
},
newindex = {
family = function(sa, v) sa.sin_family = v end,
port = function(sa, v) sa.sin_port = htons(v) end,
addr = function(sa, v) sa.sin_addr = mktype(t.in_addr, v) end,
},
__new = function(tp, port, addr)
if type(port) == "table" then return newfn(tp, port) end
return newfn(tp, {family = c.AF.INET, port = port, addr = addr})
end,
__len = function(tp) return s.sockaddr_in end,
}
addtype(types, "sockaddr_in", "struct sockaddr_in", mt.sockaddr_in)
mt.sockaddr_in6 = {
index = {
family = function(sa) return sa.sin6_family end,
port = function(sa) return ntohs(sa.sin6_port) end,
addr = function(sa) return sa.sin6_addr end,
},
newindex = {
family = function(sa, v) sa.sin6_family = v end,
port = function(sa, v) sa.sin6_port = htons(v) end,
addr = function(sa, v) sa.sin6_addr = mktype(t.in6_addr, v) end,
flowinfo = function(sa, v) sa.sin6_flowinfo = v end,
scope_id = function(sa, v) sa.sin6_scope_id = v end,
},
__new = function(tp, port, addr, flowinfo, scope_id) -- reordered initialisers.
if type(port) == "table" then return newfn(tp, port) end
return newfn(tp, {family = c.AF.INET6, port = port, addr = addr, flowinfo = flowinfo, scope_id = scope_id})
end,
__len = function(tp) return s.sockaddr_in6 end,
}
addtype(types, "sockaddr_in6", "struct sockaddr_in6", mt.sockaddr_in6)
-- we do provide this directly for compatibility, only use for standard names
mt.sockaddr_un = {
index = {
family = function(sa) return sa.sun_family end,
path = function(sa) return ffi.string(sa.sun_path) end, -- only valid for proper names
},
newindex = {
family = function(sa, v) sa.sun_family = v end,
path = function(sa, v) ffi.copy(sa.sun_path, v) end,
},
__new = function(tp, path) return newfn(tp, {family = c.AF.UNIX, path = path}) end, -- TODO accept table initialiser
__len = function(tp) return s.sockaddr_un end, -- TODO lenfn (default) instead
}
addtype(types, "sockaddr_un", "struct sockaddr_un", mt.sockaddr_un)
-- this is a bit odd, but we actually use Lua metatables for sockaddr_un, and use t.sa to multiplex
-- basically the lINUX unix socket structure is not possible to interpret without size, but does not have size in struct
-- nasty, but have not thought of a better way yet; could make an ffi type
local lua_sockaddr_un_mt = {
__index = function(un, k)
local sa = un.addr
if k == 'family' then return sa.family end
local namelen = un.addrlen - s.sun_family
if namelen > 0 then
if sa.sun_path[0] == 0 then
if k == 'abstract' then return true end
if k == 'name' then return ffi.string(sa.sun_path, namelen) end -- should we also remove leading \0?
else
if k == 'name' then return ffi.string(sa.sun_path) end
end
else
if k == 'unnamed' then return true end
end
end,
__len = function(un) return un.addrlen end,
}
function t.sa(addr, addrlen)
local family = addr.family
if family == c.AF.UNIX then -- we return Lua metatable not metatype, as need length to decode
local sa = t.sockaddr_un()
ffi.copy(sa, addr, addrlen)
return setmetatable({addr = sa, addrlen = addrlen}, lua_sockaddr_un_mt)
end
return addr
end
local nlgroupmap = { -- map from netlink socket type to group names. Note there are two forms of name though, bits and shifts.
[c.NETLINK.ROUTE] = c.RTMGRP, -- or RTNLGRP_ and shift not mask TODO make shiftflags function
-- add rest of these
-- [c.NETLINK.SELINUX] = c.SELNLGRP,
}
mt.sockaddr_nl = {
index = {
family = function(sa) return sa.nl_family end,
pid = function(sa) return sa.nl_pid end,
groups = function(sa) return sa.nl_groups end,
},
newindex = {
pid = function(sa, v) sa.nl_pid = v end,
groups = function(sa, v) sa.nl_groups = v end,
},
__new = function(tp, pid, groups, nltype)
if type(pid) == "table" then
local tb = pid
pid, groups, nltype = tb.nl_pid or tb.pid, tb.nl_groups or tb.groups, tb.type
end
if nltype and nlgroupmap[nltype] then groups = nlgroupmap[nltype][groups] end -- see note about shiftflags
return ffi.new(tp, {nl_family = c.AF.NETLINK, nl_pid = pid, nl_groups = groups})
end,
__len = function(tp) return s.sockaddr_nl end,
}
addtype(types, "sockaddr_nl", "struct sockaddr_nl", mt.sockaddr_nl)
mt.sockaddr_ll = {
index = {
family = function(sa) return sa.sll_family end,
protocol = function(sa) return ntohs(sa.sll_protocol) end,
ifindex = function(sa) return sa.sll_ifindex end,
hatype = function(sa) return sa.sll_hatype end,
pkttype = function(sa) return sa.sll_pkttype end,
halen = function(sa) return sa.sll_halen end,
addr = function(sa)
if sa.sll_halen == 6 then return pt.macaddr(sa.sll_addr) else return ffi.string(sa.sll_addr, sa.sll_halen) end
end,
},
newindex = {
protocol = function(sa, v) sa.sll_protocol = htons(c.ETH_P[v]) end,
ifindex = function(sa, v) sa.sll_ifindex = v end,
hatype = function(sa, v) sa.sll_hatype = v end,
pkttype = function(sa, v) sa.sll_pkttype = v end,
halen = function(sa, v) sa.sll_halen = v end,
addr = function(sa, v)
if ffi.istype(t.macaddr, v) then
sa.sll_halen = 6
ffi.copy(sa.sll_addr, v, 6)
else sa.sll_addr = v end
end,
},
__new = function(tp, tb)
local sa = ffi.new(tp, {sll_family = c.AF.PACKET})
for k, v in pairs(tb or {}) do sa[k] = v end
return sa
end,
__len = function(tp) return s.sockaddr_ll end,
}
addtype(types, "sockaddr_ll", "struct sockaddr_ll", mt.sockaddr_ll)
mt.stat = {
index = {
dev = function(st) return t.device(st.st_dev) end,
ino = function(st) return tonumber(st.st_ino) end,
mode = function(st) return st.st_mode end,
nlink = function(st) return tonumber(st.st_nlink) end,
uid = function(st) return st.st_uid end,
gid = function(st) return st.st_gid end,
size = function(st) return tonumber(st.st_size) end,
blksize = function(st) return tonumber(st.st_blksize) end,
blocks = function(st) return tonumber(st.st_blocks) end,
atime = function(st) return tonumber(st.st_atime) end,
ctime = function(st) return tonumber(st.st_ctime) end,
mtime = function(st) return tonumber(st.st_mtime) end,
rdev = function(st) return t.device(st.st_rdev) end,
type = function(st) return bit.band(st.st_mode, c.S_I.FMT) end,
todt = function(st) return bit.rshift(st.type, 12) end,
isreg = function(st) return st.type == c.S_I.FREG end,
isdir = function(st) return st.type == c.S_I.FDIR end,
ischr = function(st) return st.type == c.S_I.FCHR end,
isblk = function(st) return st.type == c.S_I.FBLK end,
isfifo = function(st) return st.type == c.S_I.FIFO end,
islnk = function(st) return st.type == c.S_I.FLNK end,
issock = function(st) return st.type == c.S_I.FSOCK end,
},
}
-- add some friendlier names to stat, also for luafilesystem compatibility
mt.stat.index.access = mt.stat.index.atime
mt.stat.index.modification = mt.stat.index.mtime
mt.stat.index.change = mt.stat.index.ctime
local namemap = {
file = mt.stat.index.isreg,
directory = mt.stat.index.isdir,
link = mt.stat.index.islnk,
socket = mt.stat.index.issock,
["char device"] = mt.stat.index.ischr,
["block device"] = mt.stat.index.isblk,
["named pipe"] = mt.stat.index.isfifo,
}
mt.stat.index.typename = function(st)
for k, v in pairs(namemap) do if v(st) then return k end end
return "other"
end
addtype(types, "stat", "struct stat", mt.stat)
local signames = {}
local duplicates = {IOT = true, CLD = true, POLL = true}
for k, v in pairs(c.SIG) do
if not duplicates[k] then signames[v] = k end
end
-- TODO this is broken, need to use fields from the correct union technically
-- ie check which of the unions we should be using and get all fields from that
-- (note as per Musl list the standard kernel,glibc definitions are wrong too...)
mt.siginfo = {
index = {
signo = function(s) return s.si_signo end,
errno = function(s) return s.si_errno end,
code = function(s) return s.si_code end,
pid = function(s) return s._sifields.kill.si_pid end,
uid = function(s) return s._sifields.kill.si_uid end,
timerid = function(s) return s._sifields.timer.si_tid end,
overrun = function(s) return s._sifields.timer.si_overrun end,
status = function(s) return s._sifields.sigchld.si_status end,
utime = function(s) return s._sifields.sigchld.si_utime end,
stime = function(s) return s._sifields.sigchld.si_stime end,
value = function(s) return s._sifields.rt.si_sigval end,
int = function(s) return s._sifields.rt.si_sigval.sival_int end,
ptr = function(s) return s._sifields.rt.si_sigval.sival_ptr end,
addr = function(s) return s._sifields.sigfault.si_addr end,
band = function(s) return s._sifields.sigpoll.si_band end,
fd = function(s) return s._sifields.sigpoll.si_fd end,
signame = function(s) return signames[s.signo] end,
},
newindex = {
signo = function(s, v) s.si_signo = v end,
errno = function(s, v) s.si_errno = v end,
code = function(s, v) s.si_code = v end,
pid = function(s, v) s._sifields.kill.si_pid = v end,
uid = function(s, v) s._sifields.kill.si_uid = v end,
timerid = function(s, v) s._sifields.timer.si_tid = v end,
overrun = function(s, v) s._sifields.timer.si_overrun = v end,
status = function(s, v) s._sifields.sigchld.si_status = v end,
utime = function(s, v) s._sifields.sigchld.si_utime = v end,
stime = function(s, v) s._sifields.sigchld.si_stime = v end,
value = function(s, v) s._sifields.rt.si_sigval = v end,
int = function(s, v) s._sifields.rt.si_sigval.sival_int = v end,
ptr = function(s, v) s._sifields.rt.si_sigval.sival_ptr = v end,
addr = function(s, v) s._sifields.sigfault.si_addr = v end,
band = function(s, v) s._sifields.sigpoll.si_band = v end,
fd = function(s, v) s._sifields.sigpoll.si_fd = v end,
},
}
addtype(types, "siginfo", "struct siginfo", mt.siginfo)
-- Linux internally uses non standard sigaction type k_sigaction
local sa_handler_type = ffi.typeof("void (*)(int)")
local to_handler = function(v) return ffi.cast(sa_handler_type, t.uintptr(v)) end -- luaffi needs uintptr, and full cast
mt.sigaction = {
index = {
handler = function(sa) return sa.sa_handler end,
sigaction = function(sa) return sa.sa_handler end,
mask = function(sa) return sa.sa_mask end, -- TODO would rather return type of sigset_t
flags = function(sa) return tonumber(sa.sa_flags) end,
},
newindex = {
handler = function(sa, v)
if type(v) == "string" then v = to_handler(c.SIGACT[v]) end
if type(v) == "number" then v = to_handler(v) end
sa.sa_handler = v
end,
sigaction = function(sa, v)
if type(v) == "string" then v = to_handler(c.SIGACT[v]) end
if type(v) == "number" then v = to_handler(v) end
sa.sa_handler.sa_sigaction = v
end,
mask = function(sa, v)
if not ffi.istype(t.sigset, v) then v = t.sigset(v) end
ffi.copy(sa.sa_mask, v, ffi.sizeof(sa.sa_mask))
end,
flags = function(sa, v) sa.sa_flags = c.SA[v] end,
},
__new = function(tp, tab)
local sa = ffi.new(tp)
if tab then for k, v in pairs(tab) do sa[k] = v end end
if tab and tab.sigaction then sa.sa_flags = bit.bor(sa.flags, c.SA.SIGINFO) end -- this flag must be set if sigaction set
return sa
end,
}
addtype(types, "sigaction", "struct k_sigaction", mt.sigaction)
mt.rlimit = {
index = {
cur = function(r) if r.rlim_cur == c.RLIM.INFINITY then return -1 else return tonumber(r.rlim_cur) end end,
max = function(r) if r.rlim_max == c.RLIM.INFINITY then return -1 else return tonumber(r.rlim_max) end end,
},
newindex = {
cur = function(r, v)
if v == -1 then v = c.RLIM.INFINITY end
r.rlim_cur = c.RLIM[v] -- allows use of "infinity"
end,
max = function(r, v)
if v == -1 then v = c.RLIM.INFINITY end
r.rlim_max = c.RLIM[v] -- allows use of "infinity"
end,
},
__new = newfn,
}
-- TODO some fields still missing
mt.sigevent = {
index = {
notify = function(self) return self.sigev_notify end,
signo = function(self) return self.sigev_signo end,
value = function(self) return self.sigev_value end,
},
newindex = {
notify = function(self, v) self.sigev_notify = c.SIGEV[v] end,
signo = function(self, v) self.sigev_signo = c.SIG[v] end,
value = function(self, v) self.sigev_value = t.sigval(v) end, -- auto assigns based on type
},
__new = newfn,
}
addtype(types, "sigevent", "struct sigevent", mt.sigevent)
addtype(types, "rlimit", "struct rlimit64", mt.rlimit)
mt.signalfd = {
index = {
signo = function(ss) return tonumber(ss.ssi_signo) end,
code = function(ss) return tonumber(ss.ssi_code) end,
pid = function(ss) return tonumber(ss.ssi_pid) end,
uid = function(ss) return tonumber(ss.ssi_uid) end,
fd = function(ss) return tonumber(ss.ssi_fd) end,
tid = function(ss) return tonumber(ss.ssi_tid) end,
band = function(ss) return tonumber(ss.ssi_band) end,
overrun = function(ss) return tonumber(ss.ssi_overrun) end,
trapno = function(ss) return tonumber(ss.ssi_trapno) end,
status = function(ss) return tonumber(ss.ssi_status) end,
int = function(ss) return tonumber(ss.ssi_int) end,
ptr = function(ss) return ss.ssi_ptr end,
utime = function(ss) return tonumber(ss.ssi_utime) end,
stime = function(ss) return tonumber(ss.ssi_stime) end,
addr = function(ss) return ss.ssi_addr end,
},
__index = function(ss, k) -- TODO simplify this
local sig = c.SIG[k]
if sig then return tonumber(ss.ssi_signo) == sig end
local rname = signal_reasons_gen[ss.ssi_code]
if not rname and signal_reasons[ss.ssi_signo] then rname = signal_reasons[ss.ssi_signo][ss.ssi_code] end
if rname == k then return true end
if rname == k:upper() then return true end -- TODO use some metatable to hide this?
if mt.signalfd.index[k] then return mt.signalfd.index[k](ss) end
error("invalid index " .. k)
end,
}
addtype(types, "signalfd_siginfo", "struct signalfd_siginfo", mt.signalfd)
mt.siginfos = {
__index = function(ss, k)
return ss.sfd[k - 1]
end,
__len = function(p) return p.count end,
__new = function(tp, ss)
return ffi.new(tp, ss, ss, ss * s.signalfd_siginfo)
end,
}
addtype_var(types, "siginfos", "struct {int count, bytes; struct signalfd_siginfo sfd[?];}", mt.siginfos)
-- TODO convert to use constants? note missing some macros eg WCOREDUMP(). Allow lower case. Also do not create table dynamically.
mt.wait = {
__index = function(w, k)
local WTERMSIG = bit.band(w.status, 0x7f)
local EXITSTATUS = bit.rshift(bit.band(w.status, 0xff00), 8)
local WIFEXITED = (WTERMSIG == 0)
local tab = {
WIFEXITED = WIFEXITED,
WIFSTOPPED = bit.band(w.status, 0xff) == 0x7f,
WIFSIGNALED = not WIFEXITED and bit.band(w.status, 0x7f) ~= 0x7f -- I think this is right????? TODO recheck, cleanup
}
if tab.WIFEXITED then tab.EXITSTATUS = EXITSTATUS end
if tab.WIFSTOPPED then tab.WSTOPSIG = EXITSTATUS end
if tab.WIFSIGNALED then tab.WTERMSIG = WTERMSIG end
if tab[k] then return tab[k] end
local uc = 'W' .. k:upper()
if tab[uc] then return tab[uc] end
end
}
-- cannot use metatype as just an integer
function t.waitstatus(status)
return setmetatable({status = status}, mt.wait)
end
-- termios
local bits_to_speed = {}
for k, v in pairs(c.B) do
bits_to_speed[v] = tonumber(k)
end
mt.termios = {
makeraw = function(termios)
termios.c_iflag = bit.band(termios.c_iflag, bit.bnot(c.IFLAG["IGNBRK,BRKINT,PARMRK,ISTRIP,INLCR,IGNCR,ICRNL,IXON"]))
termios.c_oflag = bit.band(termios.c_oflag, bit.bnot(c.OFLAG["OPOST"]))
termios.c_lflag = bit.band(termios.c_lflag, bit.bnot(c.LFLAG["ECHO,ECHONL,ICANON,ISIG,IEXTEN"]))
termios.c_cflag = bit.bor(bit.band(termios.c_cflag, bit.bnot(c.CFLAG["CSIZE,PARENB"])), c.CFLAG.CS8)
termios.c_cc[c.CC.VMIN] = 1
termios.c_cc[c.CC.VTIME] = 0
return true
end,
index = {
iflag = function(termios) return termios.c_iflag end,
oflag = function(termios) return termios.c_oflag end,
cflag = function(termios) return termios.c_cflag end,
lflag = function(termios) return termios.c_lflag end,
makeraw = function(termios) return mt.termios.makeraw end,
speed = function(termios)
local bits = bit.band(termios.c_cflag, c.CBAUD)
return bits_to_speed[bits]
end,
},
newindex = {
iflag = function(termios, v) termios.c_iflag = c.IFLAG(v) end,
oflag = function(termios, v) termios.c_oflag = c.OFLAG(v) end,
cflag = function(termios, v) termios.c_cflag = c.CFLAG(v) end,
lflag = function(termios, v) termios.c_lflag = c.LFLAG(v) end,
speed = function(termios, speed)
local speed = c.B[speed]
termios.c_cflag = bit.bor(bit.band(termios.c_cflag, bit.bnot(c.CBAUD)), speed)
end,
},
}
mt.termios.index.ospeed = mt.termios.index.speed
mt.termios.index.ispeed = mt.termios.index.speed
mt.termios.newindex.ospeed = mt.termios.newindex.speed
mt.termios.newindex.ispeed = mt.termios.newindex.speed
for k, i in pairs(c.CC) do
mt.termios.index[k] = function(termios) return termios.c_cc[i] end
mt.termios.newindex[k] = function(termios, v) termios.c_cc[i] = v end
end
addtype(types, "termios", "struct termios", mt.termios)
addtype(types, "termios2", "struct termios2", mt.termios)
mt.iocb = {
index = {
opcode = function(iocb) return iocb.aio_lio_opcode end,
data = function(iocb) return tonumber(iocb.aio_data) end,
reqprio = function(iocb) return iocb.aio_reqprio end,
fildes = function(iocb) return iocb.aio_fildes end, -- do not convert to fd as will already be open, don't want to gc
buf = function(iocb) return iocb.aio_buf end,
nbytes = function(iocb) return tonumber(iocb.aio_nbytes) end,
offset = function(iocb) return tonumber(iocb.aio_offset) end,
resfd = function(iocb) return iocb.aio_resfd end,
flags = function(iocb) return iocb.aio_flags end,
},
newindex = {
opcode = function(iocb, v) iocb.aio_lio_opcode = c.IOCB_CMD[v] end,
data = function(iocb, v) iocb.aio_data = v end,
reqprio = function(iocb, v) iocb.aio_reqprio = v end,
fildes = function(iocb, v) iocb.aio_fildes = getfd(v) end,
buf = function(iocb, v) iocb.aio_buf = ffi.cast(t.int64, pt.void(v)) end,
nbytes = function(iocb, v) iocb.aio_nbytes = v end,
offset = function(iocb, v) iocb.aio_offset = v end,
flags = function(iocb, v) iocb.aio_flags = c.IOCB_FLAG[v] end,
resfd = function(iocb, v)
iocb.aio_flags = bit.bor(iocb.aio_flags, c.IOCB_FLAG.RESFD)
iocb.aio_resfd = getfd(v)
end,
},
__new = newfn,
}
addtype(types, "iocb", "struct iocb", mt.iocb)
-- aio operations want an array of pointers to struct iocb. To make sure no gc, we provide a table with array and pointers
-- easiest to do as Lua table not ffi type.
-- expects Lua table of either tables or iocb as input. can provide ptr table too
-- TODO check maybe the implementation actually copies these? only the posix aio says you need to keep.
t.iocb_array = function(tab, ptrs)
local nr = #tab
local a = {nr = nr, iocbs = {}, ptrs = ptrs or t.iocb_ptrs(nr)}
for i = 1, nr do
local iocb = tab[i]
a.iocbs[i] = istype(t.iocb, iocb) or t.iocb(iocb)
a.ptrs[i - 1] = a.iocbs[i]
end
return a
end
mt.sock_filter = {
__new = function(tp, code, k, jt, jf)
return ffi.new(tp, c.BPF[code], jt or 0, jf or 0, k or 0)
end
}
addtype(types, "sock_filter", "struct sock_filter", mt.sock_filter)
mt.bpf_insn = {
__new = function(tp, code, dst_reg, src_reg, off, imm)
return ffi.new(tp, c.BPF[code], dst_reg or 0, src_reg or 0, off or 0, imm or 0)
end
}
addtype(types, "bpf_insn", "struct bpf_insn", mt.bpf_insn)
-- capabilities data is an array so cannot put metatable on it. Also depends on version, so combine into one structure.
-- TODO maybe add caching
local function capflags(val, str)
if not str then return val end
if #str == 0 then return val end
local a = h.split(",", str)
for i, v in ipairs(a) do
local s = h.trim(v):upper()
if not c.CAP[s] then error("invalid capability " .. s) end
val[s] = true
end
return val
end
mt.cap = {
__index = function(cap, k)
local ci = c.CAP[k]
if not ci then error("invalid capability " .. k) end
local i, shift = h.divmod(ci, 32)
local mask = bit.lshift(1, shift)
return bit.band(cap.cap[i], mask) ~= 0
end,
__newindex = function(cap, k, v)
if v == true then v = 1 elseif v == false then v = 0 end
local ci = c.CAP[k]
if not ci then error("invalid capability " .. k) end
local i, shift = h.divmod(ci, 32)
local mask = bit.bnot(bit.lshift(1, shift))
local set = bit.lshift(v, shift)
cap.cap[i] = bit.bor(bit.band(cap.cap[i], mask), set)
end,
__tostring = function(cap)
local tab = {}
for k, _ in pairs(c.CAP) do
if cap[k] then tab[#tab + 1] = k end
end
return table.concat(tab, ",")
end,
__new = function(tp, str)
local cap = ffi.new(tp)
if str then capflags(cap, str) end
return cap
end,
}
addtype(types, "cap", "struct cap", mt.cap)
mt.capabilities = {
hdrdata = function(cap)
local hdr, data = t.user_cap_header(cap.version, cap.pid), t.user_cap_data2()
data[0].effective, data[1].effective = cap.effective.cap[0], cap.effective.cap[1]
data[0].permitted, data[1].permitted = cap.permitted.cap[0], cap.permitted.cap[1]
data[0].inheritable, data[1].inheritable = cap.inheritable.cap[0], cap.inheritable.cap[1]
return hdr, data
end,
index = {
hdrdata = function(cap) return mt.capabilities.hdrdata end,
},
__new = function(tp, hdr, data)
local cap = ffi.new(tp, c.LINUX_CAPABILITY_VERSION[3], 0)
if type(hdr) == "table" then
if hdr.permitted then cap.permitted = t.cap(hdr.permitted) end
if hdr.effective then cap.effective = t.cap(hdr.effective) end
if hdr.inheritable then cap.inheritable = t.cap(hdr.inheritable) end
cap.pid = hdr.pid or 0
if hdr.version then cap.version = c.LINUX_CAPABILITY_VERSION[hdr.version] end
return cap
end
-- not passed a table
if hdr then cap.version, cap.pid = hdr.version, hdr.pid end
if data then
cap.effective.cap[0], cap.effective.cap[1] = data[0].effective, data[1].effective
cap.permitted.cap[0], cap.permitted.cap[1] = data[0].permitted, data[1].permitted
cap.inheritable.cap[0], cap.inheritable.cap[1] = data[0].inheritable, data[1].inheritable
end
return cap
end,
__tostring = function(cap)
local str = ""
for nm, capt in pairs{permitted = cap.permitted, inheritable = cap.inheritable, effective = cap.effective} do
str = str .. nm .. ": "
str = str .. tostring(capt) .. "\n"
end
return str
end,
}
addtype(types, "capabilities", "struct capabilities", mt.capabilities)
-- difficult to sanely use an ffi metatype for inotify events, so use Lua table
mt.inotify_events = {
__index = function(tab, k)
if c.IN[k] then return bit.band(tab.mask, c.IN[k]) ~= 0 end
error("invalid index " .. k)
end
}
t.inotify_events = function(buffer, len)
local off, ee = 0, {}
while off < len do
local ev = pt.inotify_event(buffer + off)
local le = setmetatable({wd = ev.wd, mask = ev.mask, cookie = ev.cookie}, mt.inotify_events)
if ev.len > 0 then le.name = ffi.string(ev.name) end
ee[#ee + 1] = le
off = off + ffi.sizeof(t.inotify_event(ev.len))
end
return ee
end
-- TODO for input should be able to set modes automatically from which fields are set.
mt.timex = {
__new = function(tp, a)
if type(a) == 'table' then
if a.modes then a.modes = c.ADJ[a.modes] end
if a.status then a.status = c.STA[a.status] end
return ffi.new(tp, a)
end
return ffi.new(tp)
end,
}
addtype(types, "timex", "struct timex", mt.timex)
-- not sane to convert to ffi metatype, only used as adjtimex needs to return ret and a struct
mt.adjtimex = {
__index = function(timex, k)
if c.TIME[k] then return timex.state == c.TIME[k] end
return nil
end
}
t.adjtimex = function(ret, timex)
return setmetatable({state = ret, timex = timex}, mt.adjtimex)
end
mt.epoll_event = {
index = {
fd = function(e) return tonumber(e.data.fd) end,
u64 = function(e) return e.data.u64 end,
u32 = function(e) return e.data.u32 end,
ptr = function(e) return e.data.ptr end,
},
newindex = {
fd = function(e, v) e.data.fd = v end,
u64 = function(e, v) e.data.u64 = v end,
u32 = function(e, v) e.data.u32 = v end,
ptr = function(e, v) e.data.ptr = v end,
},
__new = function(tp, a)
local e = ffi.new(tp)
if a then
if type(a) == "string" then a.events = c.EPOLL[a]
else
if a.events then a.events = c.EPOLL[a.events] end
for k, v in pairs(a) do e[k] = v end
end
end
return e
end,
}
for k, v in pairs(c.EPOLL) do
mt.epoll_event.index[k] = function(e) return bit.band(e.events, v) ~= 0 end
end
addtype(types, "epoll_event", "struct epoll_event", mt.epoll_event)
mt.epoll_events = {
__len = function(ep) return ep.count end,
__new = function(tp, n) return ffi.new(tp, n, n) end,
__ipairs = function(ep) return reviter, ep.ep, ep.count end
}
addtype_var(types, "epoll_events", "struct {int count; struct epoll_event ep[?];}", mt.epoll_events)
mt.io_event = {
index = {
error = function(ev) if (ev.res < 0) then return t.error(-ev.res) end end,
}
}
addtype(types, "io_event", "struct io_event", mt.io_event)
mt.io_events = {
__len = function(evs) return evs.count end,
__new = function(tp, n) return ffi.new(tp, n, n) end,
__ipairs = function(evs) return reviter, evs.ev, evs.count end
}
addtype_var(types, "io_events", "struct {int count; struct io_event ev[?];}", mt.io_events)
mt.cpu_set = {
index = {
zero = function(set) ffi.fill(set, s.cpu_set) end,
set = function(set, cpu)
if type(cpu) == "table" then -- table is an array of CPU numbers eg {1, 2, 4}
for i = 1, #cpu do set:set(cpu[i]) end
return set
end
local d = bit.rshift(cpu, 5) -- 5 is 32 bits
set.val[d] = bit.bor(set.val[d], bit.lshift(1, cpu % 32))
return set
end,
clear = function(set, cpu)
if type(cpu) == "table" then -- table is an array of CPU numbers eg {1, 2, 4}
for i = 1, #cpu do set:clear(cpu[i]) end
return set
end
local d = bit.rshift(cpu, 5) -- 5 is 32 bits
set.val[d] = bit.band(set.val[d], bit.bnot(bit.lshift(1, cpu % 32)))
return set
end,
get = function(set, cpu)
local d = bit.rshift(cpu, 5) -- 5 is 32 bits
return bit.band(set.val[d], bit.lshift(1, cpu % 32)) ~= 0
end,
-- TODO add rest of interface from man(3) CPU_SET
},
__index = function(set, k)
if mt.cpu_set.index[k] then return mt.cpu_set.index[k] end
if type(k) == "number" then return set:get(k) end
error("invalid index " .. k)
end,
__newindex = function(set, k, v)
if type(k) ~= "number" then error("invalid index " .. k) end
if v then set:set(k) else set:clear(k) end
end,
__new = function(tp, tab)
local set = ffi.new(tp)
if tab then set:set(tab) end
return set
end,
__tostring = function(set)
local tab = {}
for i = 0, s.cpu_set * 8 - 1 do if set:get(i) then tab[#tab + 1] = i end end
return "{" .. table.concat(tab, ",") .. "}"
end,
}
addtype(types, "cpu_set", "struct cpu_set_t", mt.cpu_set)
mt.mq_attr = {
index = {
flags = function(mqa) return tonumber(mqa.mq_flags) end,
maxmsg = function(mqa) return tonumber(mqa.mq_maxmsg) end,
msgsize = function(mqa) return tonumber(mqa.mq_msgsize) end,
curmsgs = function(mqa) return tonumber(mqa.mq_curmsgs) end,
},
newindex = {
flags = function(mqa, v) mqa.mq_flags = c.OMQATTR[v] end, -- only allows O.NONBLOCK
maxmsg = function(mqa, v) mqa.mq_maxmsg = v end,
msgsize = function(mqa, v) mqa.mq_msgsize = v end,
-- no sense in writing curmsgs
},
__new = newfn,
}
addtype(types, "mq_attr", "struct mq_attr", mt.mq_attr)
mt.ifreq = {
index = {
name = function(ifr) return ffi.string(ifr.ifr_ifrn.ifrn_name) end,
addr = function(ifr) return ifr.ifr_ifru.ifru_addr end,
dstaddr = function(ifr) return ifr.ifr_ifru.ifru_dstaddr end,
broadaddr = function(ifr) return ifr.ifr_ifru.ifru_broadaddr end,
netmask = function(ifr) return ifr.ifr_ifru.ifru_netmask end,
hwaddr = function(ifr) return ifr.ifr_ifru.ifru_hwaddr end,
flags = function(ifr) return ifr.ifr_ifru.ifru_flags end,
ivalue = function(ifr) return ifr.ifr_ifru.ifru_ivalue end,
-- TODO rest of fields
},
newindex = {
name = function(ifr, v)
assert(#v <= c.IFNAMSIZ, "name too long")
ifr.ifr_ifrn.ifrn_name = v
end,
flags = function(ifr, v) ifr.ifr_ifru.ifru_flags = c.IFREQ[v] end,
ivalue = function(ifr, v) ifr.ifr_ifru.ifru_ivalue = v end,
-- TODO rest of fields
},
__new = newfn,
}
addtype(types, "ifreq", "struct ifreq", mt.ifreq)
-- note t.dirents iterator is defined in common types
local d_name_offset = ffi.offsetof("struct linux_dirent64", "d_name") -- d_name is at end of struct
mt.dirent = {
index = {
ino = function(self) return tonumber(self.d_ino) end,
off = function(self) return self.d_off end,
reclen = function(self) return self.d_reclen end,
name = function(self) return ffi.string(pt.char(self) + d_name_offset) end,
type = function(self) return self.d_type end,
toif = function(self) return bit.lshift(self.d_type, 12) end, -- convert to stat types
},
__len = function(self) return self.d_reclen end,
}
-- TODO previously this allowed lower case values, but this static version does not
-- could add mt.dirent.index[tolower(k)] = mt.dirent.index[k] but need to do consistently elsewhere
for k, v in pairs(c.DT) do
mt.dirent.index[k] = function(self) return self.type == v end
end
addtype(types, "dirent", "struct linux_dirent64", mt.dirent)
mt.rtmsg = {
index = {
family = function(self) return tonumber(self.rtm_family) end,
},
newindex = {
family = function(self, v) self.rtm_family = c.AF[v] end,
protocol = function(self, v) self.rtm_protocol = c.RTPROT[v] end,
type = function(self, v) self.rtm_type = c.RTN[v] end,
scope = function(self, v) self.rtm_scope = c.RT_SCOPE[v] end,
flags = function(self, v) self.rtm_flags = c.RTM_F[v] end,
table = function(self, v) self.rtm_table = c.RT_TABLE[v] end,
dst_len = function(self, v) self.rtm_dst_len = v end,
src_len = function(self, v) self.rtm_src_len = v end,
tos = function(self, v) self.rtm_tos = v end,
},
__new = newfn,
}
addtype(types, "rtmsg", "struct rtmsg", mt.rtmsg)
mt.ndmsg = {
index = {
family = function(self) return tonumber(self.ndm_family) end,
},
newindex = {
family = function(self, v) self.ndm_family = c.AF[v] end,
state = function(self, v) self.ndm_state = c.NUD[v] end,
flags = function(self, v) self.ndm_flags = c.NTF[v] end,
type = function(self, v) self.ndm_type = v end, -- which lookup?
ifindex = function(self, v) self.ndm_ifindex = v end,
},
__new = newfn,
}
addtype(types, "ndmsg", "struct ndmsg", mt.ndmsg)
mt.sched_param = {
__new = function(tp, v) -- allow positional parameters as only first is ever used
local obj = ffi.new(tp)
obj.sched_priority = v or 0
return obj
end,
}
addtype(types, "sched_param", "struct sched_param", mt.sched_param)
mt.flock = {
index = {
type = function(self) return self.l_type end,
whence = function(self) return self.l_whence end,
start = function(self) return self.l_start end,
len = function(self) return self.l_len end,
pid = function(self) return self.l_pid end,
},
newindex = {
type = function(self, v) self.l_type = c.FCNTL_LOCK[v] end,
whence = function(self, v) self.l_whence = c.SEEK[v] end,
start = function(self, v) self.l_start = v end,
len = function(self, v) self.l_len = v end,
pid = function(self, v) self.l_pid = v end,
},
__new = newfn,
}
addtype(types, "flock", "struct flock64", mt.flock)
mt.mmsghdr = {
index = {
hdr = function(self) return self.msg_hdr end,
len = function(self) return self.msg_len end,
},
newindex = {
hdr = function(self, v) self.hdr = v end,
},
__new = newfn,
}
addtype(types, "mmsghdr", "struct mmsghdr", mt.mmsghdr)
mt.mmsghdrs = {
__len = function(p) return p.count end,
__new = function(tp, ps)
if type(ps) == 'number' then return ffi.new(tp, ps, ps) end
local count = #ps
local mms = ffi.new(tp, count, count)
for n = 1, count do
mms.msg[n - 1].msg_hdr = mktype(t.msghdr, ps[n])
end
return mms
end,
__ipairs = function(p) return reviter, p.msg, p.count end -- TODO want forward iterator really...
}
addtype_var(types, "mmsghdrs", "struct {int count; struct mmsghdr msg[?];}", mt.mmsghdrs)
addtype(types, "bpf_attr", "union bpf_attr")
-- Metatype for Linux perf events
mt.perf_event_attr = {
index = {
type = function(self) return self.pe_type end,
config = function(self) return self.pe_config end,
sample_type = function(self) return self.pe_sample_type end,
},
newindex = {
type = function(self, v) self.pe_type = c.PERF_TYPE[v] end,
config = function(self, v) self.pe_config = c.PERF_COUNT[v] end,
sample_type = function(self, v) self.pe_sample_type = c.PERF_SAMPLE[v] end,
},
}
addtype(types, "perf_event_attr", "struct perf_event_attr", mt.perf_event_attr)
-- this is declared above
samap_pt = {
[c.AF.UNIX] = pt.sockaddr_un,
[c.AF.INET] = pt.sockaddr_in,
[c.AF.INET6] = pt.sockaddr_in6,
[c.AF.NETLINK] = pt.sockaddr_nl,
[c.AF.PACKET] = pt.sockaddr_ll,
}
return types
end
return {init = init}