end
end
---- Call several commands in a pipe.
--- @param cmds Table of shell commands.
--- @param infile Luafile that is readable, or nil.
--- @param outfile Luafile that is writeable, or nil.
--- @return True on success, false on error.
--- @return Error object on failure.
-function e2lib.callcmd_pipe(cmds, infile, outfile)
- local e = err.new("calling commands in a pipe failed")
- local rc, re
-
- local input = infile
-
- if not input then
- rc, re = eio.fopen("/dev/null", "r")
- if not rc then
- e:cat("input could not be opened")
- return false, e:cat(re)
- end
- input = rc
- end
-
- local rcs = {}
- local pids = {}
- local ers = {}
-
- if not input then
- return false, err.new("could not open /dev/null")
- end
-
- local c = #cmds
- for cmdidx = 1, c do
- local pipein, output
- local errin, errout
- local pid
-
- errin, errout = eio.pipe()
- if not errin then
- return false, e:cat(errout)
- end
-
- if cmdidx < c then
- pipein, output = eio.pipe()
- if not pipein then
- return false, e:cat(output)
- end
- else
- -- last command in pipe
- output = outfile or errout
- end
-
- e2lib.logf(3, "+ %s", cmds[cmdidx])
- pid, re = e2lib.fork()
- if not pid then
- return false, e:cat(re)
- elseif pid == 0 then
- if cmdidx < c then
- -- everyone but the last
- eio.fclose(pipein)
- end
-
- eio.fclose(errin)
-
- -- redirect stdin
- io.stdin:close()
- rc, re = eio.dup2(eio.fileno(input), eio.STDIN)
- if not rc then
- e2lib.abort(re)
- end
- -- redirect stdout
- io.stdout:close()
- rc, re = eio.dup2(eio.fileno(output), eio.STDOUT)
- if not rc then
- e2lib.abort(re)
- end
- -- redirect stderr
- io.stderr:close()
- rc, re = eio.dup2(eio.fileno(errout), eio.STDERR)
- if not rc then
- e2lib.abort(re)
- end
- -- run the command
- rc = os.execute(cmds[cmdidx])
- os.exit(rc/256) -- XXX: os.execute
- end
-
- pids[pid] = cmdidx
- e2lib.unblock(eio.fileno(errin))
- ers[cmdidx] = errin
- eio.fclose(errout)
-
- -- close all outputs except the last one (outfile)
- if cmdidx < c then
- eio.fclose(output)
- end
-
- -- do not close first input (infile)
- if cmdidx > 1 or not infile then
- eio.fclose(input)
- end
- input = pipein
- end
-
- while c > 0 do
- local fds = {}
- local ifd = {}
- for i, f in pairs(ers) do
- local n = eio.fileno(f)
- table.insert(fds, n)
- ifd[n] = i
- end
- local i, r = e2lib.poll(-1, fds)
-
- if i <= 0 then
- return false, err.new("poll error: %s", tostring(i))
- end
-
- i = ifd[fds[i]]
- if r then
- local line
-
- while true do
- line, re = eio.readline(ers[i])
- if not line then
- return false, re
- elseif line == "" then
- break
- end
-
- e2lib.log(3, line)
- end
-
- else
- eio.fclose(ers[i])
- ers[i] = nil
- c = c - 1
- end
- end
-
-
- c = #cmds
- rc = true
- while c > 0 do
- local status, pid = e2lib.wait(-1)
- if not status then
- re = pid
- return false, e:cat(re)
- end
-
- local cmdidx = pids[pid]
- if cmdidx then
- if status ~= 0 then
- rc = false
- end
-
- rcs[cmdidx] = status
- pids[pid] = nil
- c = c - 1
- end
- end
-
- if not rc then
- return false,
- err.new("failed to execute commands in a pipe, exit codes are: %s",
- table.concat(rcs, ", "))
- end
-
- return true
-end
-
--- File descriptor config table vector. This vector simply holds file
-- descriptor config tables.
-- @table fdctv