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:
parent
cf36215228
commit
6469b65354
2 changed files with 128 additions and 2 deletions
|
@ -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 = {}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in a new issue