webmcp

view framework/env/execute/command.lua @ 486:7d53e12f0804

New function execute.command{...}
author jbe
date Sun Jun 11 23:27:17 2017 +0200 (2017-06-11)
parents
children 91d0c8304d74
line source
1 --[[--
2 output, -- collected data from stdout if process exited successfully
3 errmsg = -- error message if execution failed or if process didn't exit successfully
4 execute.command{
5 command = { filename, arg1, arg2, ... }, -- command and arguments
6 stdin_data = stdin_data, -- optional data to be sent to process via stdin
7 stdout_result_handler = stdout_result_handler, -- callback receiving: stdout data, success boolean, optional error message
8 stderr_line_handler = stderr_line_handler, -- callback for processing stderr line-wise
9 exit_handler = exit_handler, -- callback when process exited
10 signal_handler = signal_handler, -- callback when process terminated due to signal
11 timeout_handler = timeout_handler, -- callback when process gets killed due to timeout
12 abort_handler = abort_handler, -- callback when process gets killed due to request by poll function
13 abortable = abortable, -- set to true if process shall be terminated if poll function requests termination
14 poll = poll, -- alternative poll command with moonbridge_io.poll(...) semantics
15 db = db, -- database handle for waiting for notifies
16 db_notify_handler = db_notify_handler -- handler for database notifications which may return true to kill process
17 }
19 --]]--
21 function execute.command(args)
23 local moonbridge_io = require("moonbridge_io")
24 local poll = args.poll or moonbridge_io.poll
26 local start = moonbridge_io.timeref()
27 local process, errmsg = moonbridge_io.exec(table.unpack(args.command))
28 if not process then return nil, errmsg end
30 local stdout_chunks, stderr_chunks = {}, {}
31 local read_fds = {[process.stdout] = true, [process.stderr] = true}
32 local write_fds = {[process.stdin] = true}
33 if args.db then
34 read_fds[args.db.fd] = true
35 end
37 local function return_error(errmsg)
38 if args.stdout_result_handler then
39 args.stdout_result_handler(table.concat(stdout_chunks), false, errmsg)
40 end
41 return nil, errmsg
42 end
44 local function write(...)
45 if write_fds[process.stdin] then
46 local buffered = process.stdin:flush_nb(...)
47 if not buffered or buffered == 0 then
48 process.stdin:close()
49 write_fds[process.stdin] = nil
50 end
51 end
52 end
53 write(args.stdin_data or "")
55 while
56 read_fds[process.stdout] or read_fds[process.stderr] or
57 write_fds[process.stdin]
58 do
59 local timeout = args.timeout and args.timeout-moonbridge_io.timeref(start)
60 local pollstatus, pollmsg, pollterm =
61 poll(read_fds, write_fds, timeout, args.abortable)
62 if not pollstatus then
63 process:kill():wait()
64 if pollterm then
65 if args.abort_handler then args.abort_handler() end
66 else
67 if args.timeout_handler then args.timeout_handler() end
68 end
69 return return_error(pollmsg)
70 end
71 if args.db then
72 local channel, payload, pid = db:wait(0)
73 if channel then
74 if args.db_notify_handler(channel, payload, pid) then
75 process:kill():wait()
76 return return_error("Database event received")
77 end
78 end
79 end
80 if read_fds[process.stdout] then
81 local chunk, status = process.stdout:read_nb()
82 if not chunk or status == "eof" then
83 process.stdout:close()
84 read_fds[process.stdout] = nil
85 end
86 if chunk and chunk ~= "" then
87 stdout_chunks[#stdout_chunks+1] = chunk
88 end
89 end
90 if read_fds[process.stderr] then
91 local chunk, status = process.stderr:read_nb()
92 if not chunk or status == "eof" then
93 process.stderr:close()
94 read_fds[process.stderr] = nil
95 end
96 if chunk and args.stderr_line_handler then
97 while true do
98 local chunk1, chunk2 = string.match(chunk, "(.-)\n(.*)")
99 if not chunk1 then break end
100 stderr_chunks[#stderr_chunks+1] = chunk1
101 args.stderr_line_handler(table.concat(stderr_chunks))
102 stderr_chunks = {}
103 chunk = chunk2
104 end
105 if chunk ~= "" then
106 stderr_chunks[#stderr_chunks+1] = chunk
107 end
108 if status == "eof" then
109 local line = table.concat(stderr_chunks)
110 if #line > 0 then args.stderr_line_handler(line) end
111 end
112 end
113 end
114 write()
115 end
117 local status = process:wait()
119 if status < 0 then
120 if args.signal_handler then
121 args.signal_handler(-status)
122 end
123 return return_error("Command terminated by signal " .. -status)
124 elseif status > 0 then
125 if args.exit_handler then
126 args.exit_handler(status)
127 end
128 return return_error("Command returned exit code " .. status)
129 elseif args.stdout_result_handler then
130 args.stdout_result_handler(table.concat(stdout_chunks), true)
131 return true
132 else
133 return table.concat(stdout_chunks)
134 end
136 end

Impressum / About Us