4 Apr 2012 23:17
Spawning processes, handling their output non-blocking and separated by stdout/stderr
Uli Schlachter <psychon <at> znc.in>
2012-04-04 21:17:17 GMT
2012-04-04 21:17:17 GMT
Hi list, people occasionally complain about awesome awesome.spawn() (and the wrappers awful.util.spawn() and spawn_with_shell()). They want more control over the argument array that is passed to the new process. They want to handle stdout and stderr individually. They want all of this to be non-blocking, so that their WM doesn't freeze. They want the child's exist code. And the coder heard their complaints. But he didn't want to write any code. So he asked the almighty Google. Anyway, attached is a sample program which does all of the above. This needs lua-ev[0] and luaposix[1]. lua-ev handles the non-blocking part (and accidentally integrates with awesome's main loop) while luaposix let's us set up pipes, fork and execute a process. Actually, I lied. To make lua-ev work with awesome, it needs a simple patch[2]. However, I hope that gets resolved eventually. So now that I mentioned this, let's see what cool stuff you come up with. Actually, I write this mail mostly as a reminder to myself, but whatever. Cheers, Uli P.S.: Yes, POSIX is not trivial, nor is libev. So what? :-P [0] https://github.com/brimworks/lua-ev [1] https://github.com/rrthomas/luaposix [2] https://github.com/psychon/lua-ev/commit/7580fb759b8664f6598199eed4ac0c9d70c4352c -- "For saving the Earth.. and eating cheesecake!"
local posix = require("posix")
local ev = require("ev")
-- Fork of a child process that executes something and read its stdout/stderr
local read_out, write_out = posix.pipe()
local read_err, write_err = posix.pipe()
local pid = posix.fork()
if pid == 0 then
local stdin = posix.open("/dev/null", { "RDONLY" })
posix.dup2(stdin, 0)
posix.dup2(write_out, 1)
posix.dup2(write_err, 2)
for _, fd in pairs({ stdin, write_out, write_err, read_out, read_err }) do
posix.close(fd)
end
-- Let's get out of our parent's session
posix.setpid('s')
local err = posix.execp("/bin/echo", "hello", "world")
posix.write(2, err)
os.exit(-1)
end
posix.close(write_out)
posix.close(write_err)
print("Parent", pid)
-- Now handle input from the child
local function handle_io(fd, callback)
return ev.IO.new(function(loop, io, revents)
local str = posix.read(fd, 1024)
if str ~= "" then
callback(str)
else
io:stop(loop)
posix.close(fd)
end
end, fd, ev.READ)
end
handle_io(read_out, function(str)
print("stdout: " .. str)
end):start(ev.Loop.default)
handle_io(read_err, function(str)
print("stderr: " .. str)
end):start(ev.Loop.default)
-- Wait for child to die
ev.Child.new(function(loop, child, revents)
-- Note: we don't necessarily have all of the child's output read yet
local status = child:getstatus()
print("Child " .. child:getrpid() .. " died with status "
.. status.exit_status .. "!")
child:stop(loop)
end, pid, false):start(ev.Loop.default)
ev.Loop.default:loop()
RSS Feed