lua-users home
lua-l archive

Subprocess execution with protection against parameter modification

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


G'day,
[This message should be threaded, but I read the list in
Digest mode, so apologies for any thread breakage.]
A query came up in the last couple of days regarding
executing commands but protecting parameters against
unwanted modification by special character substitution
by the shell.
I'm on GNU/Linux; not sure if the following suggestion
works on Windows(R).
I use the Lua Rock "luaposix", which contains a slew of
functions to work with the Posix interface of an OS.
Subprocesses can be set up by:
 Facilitating stdin/stdout/stderr connections:
	 posix.pipe()
	 posix.dup2()
	 posix.fileno()
 Having a single-word command (Cmd), plus all
 arguments in a single table (CmdlineArgs); each of
 these are interpreted as ASCII/UTF-8 C strings -- the
 first NUL ends the string.
 Using execp() in the child process to search the
 environment path for the command (e.g. "cp"), but
 otherwise making no changes to CmdlineArgs; and
 Parent: Close the child end of the interconnecting
 pipes, and return the child's process ID (pid), along
 with the parent-end stdin/stdout/stderr file
 descriptors; and
 Child: Close the parent end of the interconnecting
 pipes, then use posix.dup2 to redirect the child's
 stdin/stdout/stderr to each go to its respective
 parent pipe; and
 Child: Use posix.execp to execute the command,
 together with its command-line arguments.
The code for this is part of a module, and is reproduced
below:
--- Obtain pipes for stdin/stdout/stderr, fork the child process from
-- the parent, and:
--
-- (a) **Child**: Set up its end of the pipes, and execute the specified
-- command with the given arguments; or
--
-- (b) **Parent**: Close down the child end of the pipes, and hand details
-- of the child's operation, e.g. pid, fd0, fd1 and fd2, to the caller.
function M.raw_exec(Command, CmdlineArgs)
 assert(Command, "missing command name")
 CmdlineArgs = CmdlineArgs or {}
 local Pipes = GetNonstdPipes(3)
 local pid = posix.fork()
 if pid < 0 then
 return nil, "raw_exec: Unable to fork(): "
 .. posix.errno(pid)
 end
 if pid ~= 0 then
 -- Parent: Close child end of pipes
 assert(posix.close(Pipes[1].Rd))
 assert(posix.close(Pipes[2].Wr))
 assert(posix.close(Pipes[3].Wr))
 -- Report PID and piped FDs to caller
 return pid, Pipes[1].Wr, Pipes[2].Rd, Pipes[3].Rd
 end
 -- Child: Close parent end of pipes
 assert(posix.close(Pipes[1].Wr))
 assert(posix.close(Pipes[2].Rd))
 assert(posix.close(Pipes[3].Rd))
 -- Change child's stdout/stdin/stderr to use its end of the
 -- pipes.
 assert(posix.dup2(Pipes[1].Rd, posix.fileno(io.stdin)))
 assert(posix.dup2(Pipes[2].Wr, posix.fileno(io.stdout)))
 assert(posix.dup2(Pipes[3].Wr, posix.fileno(io.stderr)))
 -- Finally, execute the requested command in the child
 -- environment.
 local _, Errstr
 _, Errstr = posix.execp(Command, CmdlineArgs)
 -- Execp should overwrite the Lua interpreter and replace it
 -- with the command, so, if Lua is still functioning here,
 -- something went terribly wrong. Try to retrieve an error
 -- string (presumably from the command shell), and write it to
 -- the parent's stderr pipe.
 PosixUnistd.write(Pipes[3].Wr, Errstr)
 io.flush()
 os.exit(1)
end
After that, the parent uses posix.poll to handle events
from the child, including gathering output, writing input
and handling the child-process-exit (HUP signal) case. This
is called with a timeout, so it does not consume large
portions of CPU time polling file descriptors.
At the end, the results, especially including any child
output text, stderr text, and the exit status of the
child's program, are reported to the caller.
------------------------------------
The above is lifted almost literally from a demonstration
program in the luaposix package. I've gathered the bits
into a module I've called "PosixExec", which makes life
easier for the user:
 local posix = require("posix") -- optional
 local PE = require("PosixExec" -- PosixExec.lua a "pseudo-rock"
 local Result
 local CmdlineArgs
 -- Dest comes from elsewhere
 CmdlineArgs {"-p", "--", Source"}
 if Dest == "" then
 Dest = "unknown-destination"
 end
 CmdlineArgs[#CmdlineArgs + 1] = Dest
 Result = PE.exec("cp", CmdlineArgs)
 PE.ValidateRun(Result)
 print("Child stdout: ", Result.stdout)
 print("Child stderr: ", Result.stderr)
There is an ancient version of PosixExec.lua floating around
the Internet already; if this list is strongly interested,
I'll post the current (MIT Licensed) version to this list;
which 375 lines, 15077 bytes.
(The latest uses std.normalize and std.strict, to provide
some standardization in the face of different Lua versions.)
----
cheers,
sur-behoffski (Brenton Hoff)
programmer, rouse Software

AltStyle によって変換されたページ (->オリジナル) /