xurfer/xurfer/ext/unixy/main.lua

117 lines
3.4 KiB
Lua
Raw Normal View History

2026-06-23 17:16:50 +02:00
-- The unixy extension allows proxying api-functions through cli apps.
-- Why?
-- Sometimes the host OS already has cli-apps which can make up for missing lua functionality.
-- The unixy extension is written in such way that it does not block the mainloop too much.
-- Instead it checks whether the PID still exists at a certain interval-seconds (dtmax)
--
-- example of proxying an api-function to a cli app (curl):
--
-- api.protocol.http.request = unixy.proxyCLI(
-- function(url,opts, cb) return string.format( "curl -v -s %s", url ) end,
-- function( stdout, stderr, retcode, url, opts, cb)
-- local status = 200
-- if retcode ~= 0 then status = retcode end
-- cb( retcode, stdout, {string = stderr}) -- todo: parse string into key/values
-- end
-- )
--
-- api.protocol.http.request( "https://2wa.isvery.ninja", {}, function(status,data,headers)
-- print_r(status)
-- print_r(headers)
-- print_r(data)
-- end)
--
local api = ...
local ecs = api.ecs
if table.unpack == nil then
table.unpack = unpack
end
local unixy
unixy = {
name = "unixy",
enabled = true,
jobs = {},
dtmax = 1,
dt = 0,
init = function()
end,
proxyCLI = function( cmd, cmdfunc, callback )
if api.util.cmdExist(cmd) then
print("[i] attaching ".. cmd .. " cli-cmd to api")
return function(...)
return unixy.addjob( cmdfunc, callback, ...)
end
else
print("[!] could not attach ".. cmd .. " cli-cmd to api: not found")
return function()end
2026-06-23 17:16:50 +02:00
end
end,
update = function(dt)
unixy.dt = unixy.dt + dt
if unixy.dt > unixy.dtmax then
unixy.dt = 0
if #unixy.jobs > 0 then
unixy.check_jobs()
end
end
end,
addjob = function( cmdgen, callback, ...)
local stdout_file = os.tmpname()
local stderr_file = os.tmpname()
local pid_file = os.tmpname()
-- Run curl in the background (&) + Save curl's PID ($!) to a file
local cmd = cmdgen( ... )
cmd = string.format( cmd .. " 1> %s 2> %s & echo $! > %s", stdout_file, stderr_file, pid_file)
os.execute(cmd)
-- Read the PID that was generated
local f = io.open(pid_file, "r")
local pid = f:read("*a"):gsub("%s+", "")
f:close()
os.remove(pid_file) -- clean up pid file immediately
table.insert(unixy.jobs, {
pid = pid,
args = {...},
stdout_file = stdout_file,
stderr_file = stderr_file,
callback = callback
})
trace("[unixy] start job with pid " .. pid .. " => 1> " .. stdout_file .. " 2> " .. stderr_file)
end,
check_jobs = function()
for i = #unixy.jobs, 1, -1 do
local job = unixy.jobs[i]
-- 'kill -0' doesn't kill the process, it just checks if it exists.
local stdout, retcode = os.execute("kill -0 " .. job.pid .. " > /dev/null")
2026-06-23 17:16:50 +02:00
if retcode ~= 0 then
-- Process finished! Read stdout & stderr
local f = io.open(job.stdout_file, "r")
local stdout = f:read("*a")
f:close()
f = io.open(job.stderr_file, "r")
local stderr = f:read("*a")
f:close()
-- Clean up
os.remove(job.stdout_file)
os.remove(job.stderr_file)
job.callback(stdout, stderr, retcode, table.unpack(job.args) )
2026-06-23 17:16:50 +02:00
table.remove(unixy.jobs, i)
else
io.write(".")
io.flush()
end
end
end,
}
return unixy