-- adapted from https://gist.github.com/iMega/f47f5a2ae1f02d7d5769a008538fd925 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[]); int kill(pid_t pid, int sig); pid_t setsid(void); ]]) 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 function spawn(args) 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 C.setsid() 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!! else return pid end end return {spawn=spawn, kill=C.kill}