luci-base: add luci.sys.process.exec()

The new process.exec() function simplifies spawning external processes
and capturing their stdio.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2018-11-12 10:48:08 +01:00
parent cf36215228
commit 6469b65354
2 changed files with 128 additions and 2 deletions

View file

@ -13,8 +13,8 @@ local luci = {}
luci.util = require "luci.util"
luci.ip = require "luci.ip"
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack
module "luci.sys"
@ -436,6 +436,96 @@ end
process.signal = nixio.kill
local function xclose(fd)
if fd and fd:fileno() > 2 then
fd:close()
end
end
function process.exec(command, stdout, stderr, nowait)
local out_r, out_w, err_r, err_w
if stdout then out_r, out_w = nixio.pipe() end
if stderr then err_r, err_w = nixio.pipe() end
local pid = nixio.fork()
if pid == 0 then
nixio.chdir("/")
local null = nixio.open("/dev/null", "w+")
if null then
nixio.dup(out_w or null, nixio.stdout)
nixio.dup(err_w or null, nixio.stderr)
nixio.dup(null, nixio.stdin)
xclose(out_w)
xclose(out_r)
xclose(err_w)
xclose(err_r)
xclose(null)
end
nixio.exec(unpack(command))
os.exit(-1)
end
local _, pfds, rv = nil, {}, { code = -1, pid = pid }
xclose(out_w)
xclose(err_w)
if out_r then
pfds[#pfds+1] = {
fd = out_r,
cb = type(stdout) == "function" and stdout,
name = "stdout",
events = nixio.poll_flags("in", "err", "hup")
}
end
if err_r then
pfds[#pfds+1] = {
fd = err_r,
cb = type(stderr) == "function" and stderr,
name = "stderr",
events = nixio.poll_flags("in", "err", "hup")
}
end
while #pfds > 0 do
local nfds, err = nixio.poll(pfds, -1)
if not nfds and err ~= nixio.const.EINTR then
break
end
local i
for i = #pfds, 1, -1 do
local rfd = pfds[i]
if rfd.revents > 0 then
local chunk, err = rfd.fd:read(4096)
if chunk and #chunk > 0 then
if rfd.cb then
rfd.cb(chunk)
else
rfd.buf = rfd.buf or {}
rfd.buf[#rfd.buf + 1] = chunk
end
else
table.remove(pfds, i)
if rfd.buf then
rv[rfd.name] = table.concat(rfd.buf, "")
end
rfd.fd:close()
end
end
end
end
if not nowait then
_, _, rv.code = nixio.waitpid(pid)
end
return rv
end
user = {}

View file

@ -271,6 +271,42 @@ Send a signal to a process identified by given pid.
@return Number containing the error code if failed
]]
---[[
Execute a process, optionally capturing stdio.
Executes the process specified by the given argv vector, e.g.
`{ "/bin/sh", "-c", "echo 1" }` and waits for it to terminate unless a true
value has been passed for the "nowait" parameter.
When a function value is passed for the stdout or stderr arguments, the passed
function is repeatedly called for each chunk read from the corresponding stdio
stream. The read data is passed as string containing at most 4096 bytes at a
time.
When a true, non-function value is passed for the stdout or stderr arguments,
the data of the corresponding stdio stream is read into an internal string
buffer and returned as "stdout" or "stderr" field respectively in the result
table.
When a true value is passed to the nowait parameter, the function does not
await process termination but returns as soon as all captured stdio streams
have been closed or - if no streams are captured - immediately after launching
the process.
@class function
@name process.exec
@param commend Table containing the argv vector to execute
@param stdout Callback function or boolean to indicate capturing (optional)
@param stderr Callback function or boolean to indicate capturing (optional)
@param nowait Don't wait for process termination when true (optional)
@return Table containing at least the fields "code" which holds the exit
status of the invoked process or "-1" on error and "pid", which
contains the process id assigned to the spawned process. When
stdout and/or stderr capturing has been requested, it additionally
contains "stdout" and "stderr" fields respectively, holding the
captured stdio data as string.
]]
---[[
LuCI system utilities / user related functions.