Last active
December 22, 2017 09:38
Revisions
-
neomantra revised this gist
Dec 11, 2013 . 1 changed file with 5 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,7 +28,9 @@ int execvp(const char *file, char *const argv[]); local bor = bit.bor local ffi_cast = ffi.cast local k_char_p_arr_t = ffi.typeof('const char * [?]') local char_p_k_p_t = ffi.typeof('char * const *') local octal = function(n) return tonumber(n, 8) end local O_WRONLY = octal('0001') @@ -107,12 +109,12 @@ local function spawn(cmd_line, stdout_redirect, stderr_redirect) redirect(stdout_redirect, FD_STDOUT) redirect(stderr_redirect, FD_STDERR) local argv = k_char_p_arr_t(#args + 1) -- automatically NULL terminated for i = 1, #args do argv[i-1] = args[i] -- args is 1-based Lua table, argv is 0-based C array end local res = C.execvp(args[1], ffi_cast(char_p_k_p_t, argv)) if res == -1 then error("execvp failed with " .. ffi.errno()) end -- HERE SHOULD BE UNREACHABLE!! end -
neomantra created this gist
Dec 11, 2013 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,122 @@ -- Spawn a command in the background, optionally redirecting stderr and stdout -- -- requiring this file returns a function(cmd_line, stdout_redirect, stderr_redirect) -- -- `cmd_line` is the command with possible arguments -- optional `stdout_redirect` is io.stdout, io.stderr, or a filename. default/nil is io.stdout -- optional `stderr_redirect` is io.stdout, io.stderr, or a filename. default/nil is io.stderr -- -- Example: -- luajit -e 'require("spawn")("cat /etc/network/interfaces", "foo1", io.stdout)' -- local ffi = require 'ffi' local C = ffi.C ffi.cdef([[ typedef int32_t pid_t; pid_t fork(void); int open(const char *pathname, int flags, int mode); int close(int fd); int dup2(int oldfd, int newfd); int execvp(const char *file, char *const argv[]); ]]) local bor = bit.bor local char_arr_t = ffi.typeof('const char * [?]') local octal = function(n) return tonumber(n, 8) end local O_WRONLY = octal('0001') local O_CREAT = octal('0100') local S_IRUSR = octal('00400') -- user has read permission local S_IWUSR = octal('00200') -- user has write permission local FD_STDOUT = 1 local FD_STDERR = 2 -- split a string by spaces, except that single-quoted items are kept as a single token local function tokenize_args( s ) local t = {} local i, prev = 1, 1 local in_q = nil local function capture_token() local w = s:sub(prev, i-1) if #w ~= 0 then t[#t+1] = w end prev = i + 1 end while i <= #s do local c = s:sub(i, i) if in_q then -- close quote? if c == in_q then capture_token() in_q = nil end elseif c == ' ' then capture_token() elseif c == '\'' then in_q = '\'' capture_token() end i = i + 1 end -- final cleanup capture_token() return t end -- dest should be either 0 or 1 (FD_STDOUT or FD_STDERR) local function redirect(io_or_filename, dest_fd) if io_or_filename == nil then return end -- first check for regular if (io_or_filename == io.stdout or io_or_filename == FD_STDOUT) and dest_fd ~= FD_STDOUT then C.dup2(FD_STDERR, FD_STDOUT) elseif (io_or_filename == io.stderr or io_or_filename == FD_STDERR) and dest_fd ~= FD_STDERR then C.dup2(FD_STDOUT, FD_STDERR) -- otherwise handle file-based redirection else local fd = C.open(io_or_filename, bor(O_WRONLY, O_CREAT), bor(S_IRUSR, S_IWUSR)) if fd < 0 then error("couldn't open file '" .. fname .. "': " .. ffi.errno()) end C.dup2(fd, dest_fd) C.close(fd) end end local function spawn(cmd_line, stdout_redirect, stderr_redirect) local args = tokenize_args(cmd_line) if not args or #args == 0 then error("couldn't tokenize cmd_line") end local pid = C.fork() if pid < 0 then error("fork failed " .. ffi.errno()) elseif pid == 0 then -- child process redirect(stdout_redirect, FD_STDOUT) redirect(stderr_redirect, FD_STDERR) local argv = char_arr_t(#args + 1) -- automatically NULL terminated for i = 1, #args do argv[i-1] = args[i] -- args is 1-based Lua table, argv is 0-based C array end local res = C.execvp(args[1], ffi.cast('char *const *', argv)) if res == -1 then error("execvp failed with " .. ffi.errno()) end -- HERE SHOULD BE UNREACHABLE!! end end return spawn