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()

Reply via email to