From b5e9e2a7bc2bc9cc5da8d1f7d301bdee3663fba6 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 23 Jun 2021 18:21:36 +0800 Subject: [PATCH 01/19] refactor process.c - include api.h instead of individual #includes - moved metatable name to API_TYPE_PROCESS - moved read buffer size to READ_BUF_SIZE --- src/api/api.h | 1 + src/api/process.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/api/api.h b/src/api/api.h index 4b0e14f0..51ebb9a8 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -7,6 +7,7 @@ #define API_TYPE_FONT "Font" #define API_TYPE_REPLACE "Replace" +#define API_TYPE_PROCESS "Process" void api_load_libs(lua_State *L); diff --git a/src/api/process.c b/src/api/process.c index 57c74d5c..3726bef3 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -5,12 +5,12 @@ */ #include -#include -#include -#include -#include #include #include +#include +#include "api.h" + +#define READ_BUF_SIZE 4096 typedef struct { reproc_t * process; @@ -29,7 +29,7 @@ static int process_new(lua_State* L) self->process = NULL; self->L = L; - luaL_getmetatable(L, "PROCESS"); + luaL_getmetatable(L, API_TYPE_PROCESS); lua_setmetatable(L, -2); return 1; @@ -53,7 +53,7 @@ static int process_strerror(lua_State* L) static int process_gc(lua_State* L) { - process_t* self = (process_t*) luaL_checkudata(L, 1, "PROCESS"); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); if(self->process){ reproc_kill(self->process); @@ -145,7 +145,7 @@ static int process_read(lua_State* L) process_t* self = (process_t*) lua_touserdata(L, 1); if(self->process){ - int read_size = 4096; + int read_size = READ_BUF_SIZE; if (lua_type(L, 2) == LUA_TNUMBER){ read_size = (int) lua_tonumber(L, 2); } @@ -401,7 +401,7 @@ static const struct luaL_Reg process[] = { int luaopen_process(lua_State *L) { - luaL_newmetatable(L, "PROCESS"); + luaL_newmetatable(L, API_TYPE_PROCESS); luaL_setfuncs(L, process_methods, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); From ab0bdd501755d2bfd3c0b5340dfa138d8f96c2a0 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 23 Jun 2021 18:23:19 +0800 Subject: [PATCH 02/19] add generic read function process_read and process_read_errors no longer contain redundant code --- src/api/process.c | 58 ++++++----------------------------------------- 1 file changed, 7 insertions(+), 51 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 3726bef3..b593dbe1 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -140,7 +140,7 @@ static int process_pid(lua_State* L) return 1; } -static int process_read(lua_State* L) +static int g_read(lua_State* L, int stream) { process_t* self = (process_t*) lua_touserdata(L, 1); @@ -194,58 +194,14 @@ static int process_read(lua_State* L) return 1; } +static int process_read(lua_State *L) +{ + return g_read(L, REPROC_STREAM_OUT); +} + static int process_read_errors(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); - - if(self->process){ - int read_size = 4096; - if (lua_type(L, 2) == LUA_TNUMBER){ - read_size = (int) lua_tonumber(L, 2); - } - - int tries = 1; - if (lua_type(L, 3) == LUA_TNUMBER){ - tries = (int) lua_tonumber(L, 3); - } - - int out = 0; - uint8_t buffer[read_size]; - - int runs; - for (runs=0; runsprocess, - REPROC_STREAM_ERR, - buffer, - read_size - ); - - if (out >= 0) - break; - } - - // if request for tries was set and nothing - // read kill the process - if(tries > 1 && out < 0) - out = REPROC_EPIPE; - - if(out == REPROC_EPIPE){ - reproc_kill(self->process); - reproc_destroy(self->process); - self->process = NULL; - - lua_pushnil(L); - } else if(out > 0) { - lua_pushlstring(L, (const char*) buffer, out); - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } - - return 1; + return g_read(L, REPROC_STREAM_ERR); } static int process_write(lua_State* L) From 257cb47cd1a24245dd88fea429cdce86b4f7c785 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 23 Jun 2021 18:29:38 +0800 Subject: [PATCH 03/19] fix wrongly spaced variable name --- src/api/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/process.c b/src/api/process.c index b593dbe1..ff197879 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -194,7 +194,7 @@ static int g_read(lua_State* L, int stream) return 1; } -static int process_read(lua_State *L) +static int process_read(lua_State* L) { return g_read(L, REPROC_STREAM_OUT); } From 022f92bcd4ccff76666d9e4af705f1879ac9b50a Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 24 Jun 2021 16:01:45 +0800 Subject: [PATCH 04/19] do not terminate process when read fails --- src/api/process.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index ff197879..e71785cb 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -171,11 +171,6 @@ static int g_read(lua_State* L, int stream) break; } - // if request for tries was set and nothing - // read kill the process - if(tries > 1 && out < 0) - out = REPROC_EPIPE; - if(out == REPROC_EPIPE){ reproc_kill(self->process); reproc_destroy(self->process); From ccbf676f5085a5ae33e836e6bb4f36618140c13c Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Tue, 6 Jul 2021 17:00:41 +0800 Subject: [PATCH 05/19] major refactor to the process API - process.new() is replaced by the Process class - Process() accepts redirection options and environment variables - Process:read() does not accept `tries` - Process:read_stdout() and process:read_stderr() is introduced - Process:read() now accepts stream as it's first parameter - Process:running() actually detects if process is running - Process.returncode() returns the exit code (or exit SIGNAL on POSIX when available) - constants are moved to process.constants - process_t.L is removed --- src/api/process.c | 475 +++++++++++++++++++++++----------------------- 1 file changed, 234 insertions(+), 241 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index e71785cb..f2a1b1c2 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -12,26 +12,133 @@ #define READ_BUF_SIZE 4096 +#define L_GETTABLE(L, idx, key, conv, def) ( \ + lua_getfield(L, idx, key), \ + conv(L, -1, def) \ +) + +#define L_GETNUM(L, idx, key, def) L_GETTABLE(L, idx, key, luaL_optnumber, def) +#define L_GETSTR(L, idx, key, def) L_GETTABLE(L, idx, key, luaL_optstring, def) + +#define L_SETNUM(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) + +#define L_RETURN_REPROC_ERROR(L, code) { \ + lua_pushnil(L); \ + lua_pushstring(L, reproc_strerror(code)); \ + lua_pushnumber(L, code); \ + return 3; \ +} + +#define ASSERT_MALLOC(ptr) \ + if (ptr == NULL) \ + L_RETURN_REPROC_ERROR(L, REPROC_ENOMEM) + +#define ASSERT_REPROC_ERRNO(L, code) { \ + if (code < 0) \ + L_RETURN_REPROC_ERROR(L, code) \ +} + typedef struct { reproc_t * process; - lua_State* L; - + bool running; + int returncode; } process_t; +// this function should be called instead of reproc_wait +static int poll_process(process_t* proc, int timeout) +{ + int ret = reproc_wait(proc->process, timeout); + if (ret != REPROC_ETIMEDOUT) { + proc->running = false; + proc->returncode = ret; + } + return ret; +} + static int process_new(lua_State* L) { - process_t* self = (process_t*) lua_newuserdata( - L, sizeof(process_t) + int cmd_len = lua_rawlen(L, 1) + 1; + const char** cmd = malloc(sizeof(char *) * cmd_len); + ASSERT_MALLOC(cmd); + cmd[cmd_len] = NULL; + + for(int i = 0; i < cmd_len; i++) { + lua_rawgeti(L, 1, i + 1); + + cmd[i] = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + + int deadline = L_GETNUM(L, 2, "timeout", 0); + const char* cwd =L_GETSTR(L, 2, "cwd", NULL); + int redirect_in = L_GETNUM(L, 2, "stdin", REPROC_REDIRECT_DEFAULT); + int redirect_out = L_GETNUM(L, 2, "stdout", REPROC_REDIRECT_DEFAULT); + int redirect_err = L_GETNUM(L, 2, "stderr", REPROC_REDIRECT_DEFAULT); + lua_pop(L, 5); // remove args we just read + + if ( + redirect_in > REPROC_REDIRECT_STDOUT + || redirect_out > REPROC_REDIRECT_STDOUT + || redirect_err > REPROC_REDIRECT_STDOUT) + { + lua_pushnil(L); + lua_pushliteral(L, "redirect to handles, FILE* and paths are not supported"); + return 2; + } + + // env + luaL_getsubtable(L, 2, "env"); + + lua_pushnil(L); + int env_len = 1; + while (lua_next(L, -2) != 0) { + env_len++; + lua_pop(L, 1); + } + + const char** env = malloc(sizeof(char *) * env_len); + ASSERT_MALLOC(env); + env[env_len] = NULL; + + int i = 0; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + lua_pushliteral(L, "="); + lua_pushvalue(L, -3); // push the key to the top + lua_concat(L, 3); // key=value + + env[i++] = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + + reproc_t* proc = reproc_new(); + int out = reproc_start( + proc, + (const char* const*) cmd, + (reproc_options) { + .working_directory = cwd, + .deadline = deadline, + .nonblocking = true, + .env = { + .behavior = REPROC_ENV_EXTEND, + .extra = env + }, + .redirect = { + .in.type = redirect_in, + .out.type = redirect_out, + .err.type = redirect_err + } + } ); - memset(self, 0, sizeof(process_t)); + ASSERT_REPROC_ERRNO(L, out); - self->process = NULL; - self->L = L; - - luaL_getmetatable(L, API_TYPE_PROCESS); - lua_setmetatable(L, -2); + process_t* self = lua_newuserdata(L, sizeof(process_t)); + self->process = proc; + self->running = true; + // this is equivalent to using lua_setmetatable() + luaL_setmetatable(L, API_TYPE_PROCESS); return 1; } @@ -39,24 +146,24 @@ static int process_strerror(lua_State* L) { int error_code = luaL_checknumber(L, 1); - if(error_code){ - lua_pushstring( - L, - reproc_strerror(error_code) - ); - } else { + if (error_code < 0) + lua_pushstring(L, reproc_strerror(error_code)); + else lua_pushnil(L); - } return 1; } -static int process_gc(lua_State* L) +static int f_gc(lua_State* L) { process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); - if(self->process){ - reproc_kill(self->process); + if(self->process) { + reproc_stop(self->process, (reproc_stop_actions) { + { REPROC_STOP_KILL, 0 }, + { REPROC_STOP_KILL, 0 }, + { REPROC_STOP_TERMINATE, 0 } + }); reproc_destroy(self->process); self->process = NULL; } @@ -64,78 +171,23 @@ static int process_gc(lua_State* L) return 0; } -static int process_start(lua_State* L) +static int f_pid(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - luaL_checktype(L, 2, LUA_TTABLE); - - char* path = NULL; - size_t path_len = 0; - - if(lua_type(L, 3) == LUA_TSTRING){ - path = (char*) lua_tolstring(L, 3, &path_len); - } - - size_t deadline = 0; - - if(lua_type(L, 4) == LUA_TNUMBER){ - deadline = lua_tonumber(L, 4); - } - - size_t table_len = luaL_len(L, 2); - char* command[table_len+1]; - command[table_len] = NULL; - - int i; - for(i=1; i<=table_len; i++){ - lua_pushnumber(L, i); - lua_gettable(L, 2); - - command[i-1] = (char*) lua_tostring(L, -1); - - lua_remove(L, -1); - } - - if(self->process){ - reproc_kill(self->process); - reproc_destroy(self->process); - } - - self->process = reproc_new(); - - int out = reproc_start( - self->process, - (const char* const*) command, - (reproc_options){ - .working_directory = path, - .deadline = deadline, - .nonblocking=true, - .redirect.err.type=REPROC_REDIRECT_PIPE - } - ); - - if(out > 0) { - lua_pushboolean(L, 1); - } - else { - reproc_destroy(self->process); - self->process = NULL; - lua_pushnumber(L, out); - } - + lua_pushnumber(L, reproc_pid(self->process)); return 1; } -static int process_pid(lua_State* L) +static int f_returncode(lua_State *L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); + int ret = poll_process(self, 0); - if(self->process){ - lua_pushnumber(L, reproc_pid(self->process)); - } else { - lua_pushnumber(L, 0); - } + if (self->running) + lua_pushnil(L); + else + lua_pushnumber(L, ret); return 1; } @@ -143,239 +195,180 @@ static int process_pid(lua_State* L) static int g_read(lua_State* L, int stream) { process_t* self = (process_t*) lua_touserdata(L, 1); + unsigned long read_size = luaL_optunsigned(L, 2, READ_BUF_SIZE); - if(self->process){ - int read_size = READ_BUF_SIZE; - if (lua_type(L, 2) == LUA_TNUMBER){ - read_size = (int) lua_tonumber(L, 2); - } + luaL_Buffer b; + uint8_t* buffer = (uint8_t*) luaL_buffinitsize(L, &b, read_size); - int tries = 1; - if (lua_type(L, 3) == LUA_TNUMBER){ - tries = (int) lua_tonumber(L, 3); - } + int out = reproc_read( + self->process, + stream, + buffer, + read_size + ); - int out = 0; - uint8_t buffer[read_size]; + if (out >= 0) + luaL_addsize(&b, out); + luaL_pushresult(&b); - int runs; - for (runs=0; runsprocess, - REPROC_STREAM_OUT, - buffer, - read_size - ); - - if (out >= 0) - break; - } - - if(out == REPROC_EPIPE){ - reproc_kill(self->process); - reproc_destroy(self->process); - self->process = NULL; - - lua_pushnil(L); - } else if(out > 0) { - lua_pushlstring(L, (const char*) buffer, out); - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } + if (out == REPROC_EPIPE) + ASSERT_REPROC_ERRNO(L, out); return 1; } -static int process_read(lua_State* L) +static int f_read_stdout(lua_State* L) { return g_read(L, REPROC_STREAM_OUT); } -static int process_read_errors(lua_State* L) +static int f_read_stderr(lua_State* L) { return g_read(L, REPROC_STREAM_ERR); } -static int process_write(lua_State* L) +static int f_read(lua_State* L) +{ + int stream = luaL_checknumber(L, 1); + lua_remove(L, 1); // remove the number we just read + return g_read(L, stream); +} + +static int f_write(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - if(self->process){ - size_t data_size = 0; - const char* data = luaL_checklstring(L, 2, &data_size); + size_t data_size = 0; + const char* data = luaL_checklstring(L, 2, &data_size); - int out = 0; - - out = reproc_write( - self->process, - (uint8_t*) data, - data_size - ); - - if(out == REPROC_EPIPE){ - reproc_kill(self->process); - reproc_destroy(self->process); - self->process = NULL; - } - - lua_pushnumber(L, out); - } else { - lua_pushnumber(L, REPROC_EPIPE); - } + int out = reproc_write( + self->process, + (uint8_t*) data, + data_size + ); + if (out == REPROC_EPIPE) + L_RETURN_REPROC_ERROR(L, out); + lua_pushnumber(L, out); return 1; } -static int process_close_stream(lua_State* L) +static int f_close_stream(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - if(self->process){ - size_t stream = luaL_checknumber(L, 2); - - int out = reproc_close(self->process, stream); - - lua_pushnumber(L, out); - } else { - lua_pushnumber(L, REPROC_EINVAL); - } + int stream = luaL_checknumber(L, 2); + int out = reproc_close(self->process, stream); + ASSERT_REPROC_ERRNO(L, out); + lua_pushboolean(L, 1); return 1; } -static int process_wait(lua_State* L) +static int f_wait(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); + + int timeout = luaL_optnumber(L, 2, 0); + + int ret = poll_process(self, timeout); + // negative returncode is also used for signals on POSIX + if (ret == REPROC_ETIMEDOUT) + ASSERT_REPROC_ERRNO(L, ret); - if(self->process){ - size_t timeout = luaL_checknumber(L, 2); - - int out = reproc_wait(self->process, timeout); - - if(out >= 0){ - reproc_destroy(self->process); - self->process = NULL; - } - - lua_pushnumber(L, out); - } else { - lua_pushnumber(L, REPROC_EINVAL); - } - + lua_pushnumber(L, ret); return 1; } -static int process_terminate(lua_State* L) +static int f_terminate(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - if(self->process){ - int out = reproc_terminate(self->process); + int out = reproc_terminate(self->process); + ASSERT_REPROC_ERRNO(L, out); - if(out < 0){ - lua_pushnumber(L, out); - } else { - reproc_destroy(self->process); - self->process = NULL; - lua_pushboolean(L, 1); - } - } else { - lua_pushnumber(L, REPROC_EINVAL); - } + poll_process(self, 0); + lua_pushboolean(L, 1); return 1; } -static int process_kill(lua_State* L) +static int f_kill(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - if(self->process){ - int out = reproc_kill(self->process); + int out = reproc_kill(self->process); + ASSERT_REPROC_ERRNO(L, out); - if(out < 0){ - lua_pushnumber(L, out); - } else { - reproc_destroy(self->process); - self->process = NULL; - lua_pushboolean(L, 1); - } - } else { - lua_pushnumber(L, REPROC_EINVAL); - } + poll_process(self, 0); + lua_pushboolean(L, 1); return 1; } -static int process_running(lua_State* L) +static int f_running(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); - - if(self->process){ - lua_pushboolean(L, 1); - } else { - lua_pushboolean(L, 0); - } + + poll_process(self, 0); + lua_pushboolean(L, self->running); return 1; } static const struct luaL_Reg process_methods[] = { - { "__gc", process_gc}, - {"start", process_start}, - {"pid", process_pid}, - {"read", process_read}, - {"read_errors", process_read_errors}, - {"write", process_write}, - {"close_stream", process_close_stream}, - {"wait", process_wait}, - {"terminate", process_terminate}, - {"kill", process_kill}, - {"running", process_running}, + { "__call", process_new }, + { "__gc", f_gc}, + {"pid", f_pid}, + {"returncode", f_returncode}, + {"read", f_read}, + {"read_stdout", f_read_stdout}, + {"read_stderr", f_read_stderr}, + {"write", f_write}, + {"close_stream", f_close_stream}, + {"wait", f_wait}, + {"terminate", f_terminate}, + {"kill", f_kill}, + {"running", f_running}, {NULL, NULL} }; -static const struct luaL_Reg process[] = { - {"new", process_new}, +static const struct luaL_Reg lib[] = { {"strerror", process_strerror}, - {"ERROR_PIPE", NULL}, - {"ERROR_WOULDBLOCK", NULL}, - {"ERROR_TIMEDOUT", NULL}, - {"STREAM_STDIN", NULL}, - {"STREAM_STDOUT", NULL}, - {"STREAM_STDERR", NULL}, - {NULL, NULL} + {"Process", NULL}, + {"CONSTANTS", NULL} }; int luaopen_process(lua_State *L) { + luaL_newlib(L, lib); + luaL_newmetatable(L, API_TYPE_PROCESS); luaL_setfuncs(L, process_methods, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); + lua_setfield(L, -2, "Process"); // process.Process - luaL_newlib(L, process); + lua_newtable(L); + L_SETNUM(L, -1, "ERROR_INVAL", REPROC_EINVAL); + L_SETNUM(L, -1, "ERROR_TIMEDOUT", REPROC_ETIMEDOUT); + L_SETNUM(L, -1, "ERROR_PIPE", REPROC_EPIPE); + L_SETNUM(L, -1, "ERROR_NOMEM", REPROC_ENOMEM); + L_SETNUM(L, -1, "ERROR_WOULDBLOCK", REPROC_EWOULDBLOCK); - lua_pushnumber(L, REPROC_EPIPE); - lua_setfield(L, -2, "ERROR_PIPE"); + L_SETNUM(L, -1, "WAIT_INFINITE", REPROC_INFINITE); + L_SETNUM(L, -1, "WAIT_DEADLINE", REPROC_DEADLINE); - lua_pushnumber(L, REPROC_EWOULDBLOCK); - lua_setfield(L, -2, "ERROR_WOULDBLOCK"); + L_SETNUM(L, -1, "STREAM_STDIN", REPROC_STREAM_IN); + L_SETNUM(L, -1, "STREAM_STDOUT", REPROC_STREAM_OUT); + L_SETNUM(L, -1, "STREAM_STDERR", REPROC_STREAM_ERR); - lua_pushnumber(L, REPROC_ETIMEDOUT); - lua_setfield(L, -2, "ERROR_TIMEDOUT"); - - lua_pushnumber(L, REPROC_STREAM_IN); - lua_setfield(L, -2, "STREAM_STDIN"); - - lua_pushnumber(L, REPROC_STREAM_OUT); - lua_setfield(L, -2, "STREAM_STDOUT"); - - lua_pushnumber(L, REPROC_STREAM_ERR); - lua_setfield(L, -2, "STREAM_STDERR"); + L_SETNUM(L, -1, "REDIRECT_DEFAULT", REPROC_REDIRECT_DEFAULT); + L_SETNUM(L, -1, "REDIRECT_PIPE", REPROC_REDIRECT_PIPE); + L_SETNUM(L, -1, "REDIRECT_PARENT", REPROC_REDIRECT_PARENT); + L_SETNUM(L, -1, "REDIRECT_DISCARD", REPROC_REDIRECT_DISCARD); + L_SETNUM(L, -1, "REDIRECT_STDOUT", REPROC_REDIRECT_STDOUT); + lua_setfield(L, -2, "CONSTANTS"); // process.CONSTANTS return 1; } From 116109f069ac3b4517d9710514ef45b1fcf8627c Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 7 Jul 2021 01:17:11 +0800 Subject: [PATCH 06/19] move constants to the process table reduce code clutter because they're already namespaced --- src/api/process.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index f2a1b1c2..f43a316f 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -335,8 +335,7 @@ static const struct luaL_Reg process_methods[] = { static const struct luaL_Reg lib[] = { {"strerror", process_strerror}, - {"Process", NULL}, - {"CONSTANTS", NULL} + {NULL, NULL} }; int luaopen_process(lua_State *L) @@ -349,7 +348,7 @@ int luaopen_process(lua_State *L) lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "Process"); // process.Process - lua_newtable(L); + // constants L_SETNUM(L, -1, "ERROR_INVAL", REPROC_EINVAL); L_SETNUM(L, -1, "ERROR_TIMEDOUT", REPROC_ETIMEDOUT); L_SETNUM(L, -1, "ERROR_PIPE", REPROC_EPIPE); @@ -368,7 +367,6 @@ int luaopen_process(lua_State *L) L_SETNUM(L, -1, "REDIRECT_PARENT", REPROC_REDIRECT_PARENT); L_SETNUM(L, -1, "REDIRECT_DISCARD", REPROC_REDIRECT_DISCARD); L_SETNUM(L, -1, "REDIRECT_STDOUT", REPROC_REDIRECT_STDOUT); - lua_setfield(L, -2, "CONSTANTS"); // process.CONSTANTS return 1; } From 0059b4a1ccc0d18f2aec13160caceda8884a6a66 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 7 Jul 2021 12:37:07 +0800 Subject: [PATCH 07/19] remove duplicated constants --- src/api/process.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 772bd1fe..aabf3ed1 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -368,11 +368,5 @@ int luaopen_process(lua_State *L) L_SETNUM(L, -1, "REDIRECT_DISCARD", REPROC_REDIRECT_DISCARD); L_SETNUM(L, -1, "REDIRECT_STDOUT", REPROC_REDIRECT_STDOUT); - lua_pushnumber(L, REPROC_INFINITE); - lua_setfield(L, -2, "WAIT_INFINITE"); - - lua_pushnumber(L, REPROC_DEADLINE); - lua_setfield(L, -2, "WAIT_DEADLINE"); - return 1; } From 9e5b6c72a7a3817d0633f9aa580a0c55cef9ce45 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:15:02 +0800 Subject: [PATCH 08/19] replace Process() constructor with process.start() fixes wrong metatable and removes redundancy writing process.Process() --- src/api/process.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index aabf3ed1..5b26871f 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -55,7 +55,7 @@ static int poll_process(process_t* proc, int timeout) return ret; } -static int process_new(lua_State* L) +static int process_start(lua_State* L) { int cmd_len = lua_rawlen(L, 1) + 1; const char** cmd = malloc(sizeof(char *) * cmd_len); @@ -316,9 +316,10 @@ static int f_running(lua_State* L) return 1; } -static const struct luaL_Reg process_methods[] = { - { "__call", process_new }, - { "__gc", f_gc}, +static const struct luaL_Reg lib[] = { + {"start", process_start}, + {"strerror", process_strerror}, + {"__gc", f_gc}, {"pid", f_pid}, {"returncode", f_returncode}, {"read", f_read}, @@ -333,20 +334,12 @@ static const struct luaL_Reg process_methods[] = { {NULL, NULL} }; -static const struct luaL_Reg lib[] = { - {"strerror", process_strerror}, - {NULL, NULL} -}; - int luaopen_process(lua_State *L) { - luaL_newlib(L, lib); - luaL_newmetatable(L, API_TYPE_PROCESS); - luaL_setfuncs(L, process_methods, 0); + luaL_setfuncs(L, lib, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); - lua_setfield(L, -2, "Process"); // process.Process // constants L_SETNUM(L, -1, "ERROR_INVAL", REPROC_EINVAL); From b38add8de55c7dd90e0f2a200f8c5a19a494d088 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:20:08 +0800 Subject: [PATCH 09/19] move arg checking code to the top --- src/api/process.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/api/process.c b/src/api/process.c index 5b26871f..168fcf9c 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -59,6 +59,13 @@ static int process_start(lua_State* L) { int cmd_len = lua_rawlen(L, 1) + 1; const char** cmd = malloc(sizeof(char *) * cmd_len); + luaL_checktype(L, 1, LUA_TTABLE); + if (lua_isnoneornil(L, 2)) { + lua_settop(L, 1); // remove the nil if it's there + lua_newtable(L); + } + luaL_checktype(L, 2, LUA_TTABLE); + ASSERT_MALLOC(cmd); cmd[cmd_len] = NULL; From 453df84c8254dbc18ad9f7277d8a442d5c96ba49 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:21:11 +0800 Subject: [PATCH 10/19] fix bugs with args and env array --- src/api/process.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 168fcf9c..8460740f 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -57,8 +57,6 @@ static int poll_process(process_t* proc, int timeout) static int process_start(lua_State* L) { - int cmd_len = lua_rawlen(L, 1) + 1; - const char** cmd = malloc(sizeof(char *) * cmd_len); luaL_checktype(L, 1, LUA_TTABLE); if (lua_isnoneornil(L, 2)) { lua_settop(L, 1); // remove the nil if it's there @@ -66,6 +64,8 @@ static int process_start(lua_State* L) } luaL_checktype(L, 2, LUA_TTABLE); + int cmd_len = lua_rawlen(L, 1); + const char** cmd = malloc(sizeof(char *) * (cmd_len + 1)); ASSERT_MALLOC(cmd); cmd[cmd_len] = NULL; @@ -95,27 +95,29 @@ static int process_start(lua_State* L) // env luaL_getsubtable(L, 2, "env"); + const char **env = NULL; + int env_len = 0; lua_pushnil(L); - int env_len = 1; while (lua_next(L, -2) != 0) { env_len++; lua_pop(L, 1); } - const char** env = malloc(sizeof(char *) * env_len); - ASSERT_MALLOC(env); - env[env_len] = NULL; + if (env_len > 0) { + env = malloc(sizeof(char*) * (env_len + 1)); + env[env_len] = NULL; - int i = 0; - lua_pushnil(L); - while (lua_next(L, -2) != 0) { - lua_pushliteral(L, "="); - lua_pushvalue(L, -3); // push the key to the top - lua_concat(L, 3); // key=value + int i = 0; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + lua_pushliteral(L, "="); + lua_pushvalue(L, -3); // push the key to the top + lua_concat(L, 3); // key=value - env[i++] = luaL_checkstring(L, -1); - lua_pop(L, 1); + env[i++] = luaL_checkstring(L, -1); + lua_pop(L, 1); + } } reproc_t* proc = reproc_new(); From 81157b5d4f523e709ff365c742cff90442059deb Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:22:33 +0800 Subject: [PATCH 11/19] fix memory leak when reproc_start() fails --- src/api/process.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/process.c b/src/api/process.c index 8460740f..6442d0d2 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -140,7 +140,10 @@ static int process_start(lua_State* L) } ); - ASSERT_REPROC_ERRNO(L, out); + if (out < 0) { + reproc_destroy(proc); + L_RETURN_REPROC_ERROR(L, out); + } process_t* self = lua_newuserdata(L, sizeof(process_t)); self->process = proc; From e0099485ab6fc3fa20a7f6f132365518b700d423 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:22:56 +0800 Subject: [PATCH 12/19] kill process when pipe is closed --- src/api/process.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 6442d0d2..0dce6908 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -223,8 +223,16 @@ static int g_read(lua_State* L, int stream) luaL_addsize(&b, out); luaL_pushresult(&b); - if (out == REPROC_EPIPE) + if (out == REPROC_EPIPE) { + int ret = reproc_stop(self->process, (reproc_stop_actions) { + { REPROC_STOP_KILL, 0 }, + { REPROC_STOP_KILL, 0 }, + { REPROC_STOP_TERMINATE, 0 } + }); + self->running = false; + self->returncode = ret; ASSERT_REPROC_ERRNO(L, out); + } return 1; } @@ -300,8 +308,8 @@ static int f_terminate(lua_State* L) ASSERT_REPROC_ERRNO(L, out); poll_process(self, 0); - lua_pushboolean(L, 1); + return 1; } @@ -313,8 +321,8 @@ static int f_kill(lua_State* L) ASSERT_REPROC_ERRNO(L, out); poll_process(self, 0); - lua_pushboolean(L, 1); + return 1; } From 4815cff5035df657d194aec5b81a3f5f5492bc5b Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:43:05 +0800 Subject: [PATCH 13/19] kill process if EPIPE is encountered --- src/api/process.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 0dce6908..1250ee3b 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -55,6 +55,25 @@ static int poll_process(process_t* proc, int timeout) return ret; } +static int kill_process(process_t* proc) +{ + int ret = reproc_stop( + proc->process, + (reproc_stop_actions) { + {REPROC_STOP_KILL, 0}, + {REPROC_STOP_TERMINATE, 0}, + {REPROC_STOP_NOOP, 0} + } + ); + + if (ret != REPROC_ETIMEDOUT) { + proc->running = false; + proc->returncode = ret; + } + + return ret; +} + static int process_start(lua_State* L) { luaL_checktype(L, 1, LUA_TTABLE); @@ -171,11 +190,7 @@ static int f_gc(lua_State* L) process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); if(self->process) { - reproc_stop(self->process, (reproc_stop_actions) { - { REPROC_STOP_KILL, 0 }, - { REPROC_STOP_KILL, 0 }, - { REPROC_STOP_TERMINATE, 0 } - }); + kill_process(self); reproc_destroy(self->process); self->process = NULL; } @@ -224,13 +239,7 @@ static int g_read(lua_State* L, int stream) luaL_pushresult(&b); if (out == REPROC_EPIPE) { - int ret = reproc_stop(self->process, (reproc_stop_actions) { - { REPROC_STOP_KILL, 0 }, - { REPROC_STOP_KILL, 0 }, - { REPROC_STOP_TERMINATE, 0 } - }); - self->running = false; - self->returncode = ret; + kill_process(self); ASSERT_REPROC_ERRNO(L, out); } @@ -266,8 +275,10 @@ static int f_write(lua_State* L) (uint8_t*) data, data_size ); - if (out == REPROC_EPIPE) + if (out == REPROC_EPIPE) { + kill_process(self); L_RETURN_REPROC_ERROR(L, out); + } lua_pushnumber(L, out); return 1; From 60d3f2cac1342cc25b46ea0eb5a854590e2b5d7d Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:55:43 +0800 Subject: [PATCH 14/19] add __tostring metamethod --- src/api/process.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/api/process.c b/src/api/process.c index 1250ee3b..60b3f0b6 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -198,6 +198,14 @@ static int f_gc(lua_State* L) return 0; } +static int f_tostring(lua_State* L) +{ + luaL_checkudata(L, 1, API_TYPE_PROCESS); + + lua_pushliteral(L, API_TYPE_PROCESS); + return 1; +} + static int f_pid(lua_State* L) { process_t* self = (process_t*) lua_touserdata(L, 1); @@ -351,6 +359,7 @@ static const struct luaL_Reg lib[] = { {"start", process_start}, {"strerror", process_strerror}, {"__gc", f_gc}, + {"__tostring", f_tostring}, {"pid", f_pid}, {"returncode", f_returncode}, {"read", f_read}, From 8995a2614fc054e5dfb1db6acd190e1b19a677e9 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:05:12 +0800 Subject: [PATCH 15/19] remove unecessary assertion --- src/api/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/process.c b/src/api/process.c index 60b3f0b6..95d5ff63 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -313,7 +313,7 @@ static int f_wait(lua_State* L) int ret = poll_process(self, timeout); // negative returncode is also used for signals on POSIX if (ret == REPROC_ETIMEDOUT) - ASSERT_REPROC_ERRNO(L, ret); + L_RETURN_REPROC_ERROR(L, ret); lua_pushnumber(L, ret); return 1; From d4673f065aaf7599c06a5e034fb9b555c88a4170 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:23:41 +0800 Subject: [PATCH 16/19] remove default read buffer size This prevents newcomers from doing process:read(BUF_SIZE) and cause the whole process to hang because process:read() accepts a stream parameter --- src/api/process.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index 95d5ff63..ea43b52e 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -10,8 +10,6 @@ #include #include "api.h" -#define READ_BUF_SIZE 4096 - #define L_GETTABLE(L, idx, key, conv, def) ( \ lua_getfield(L, idx, key), \ conv(L, -1, def) \ @@ -230,7 +228,7 @@ static int f_returncode(lua_State *L) static int g_read(lua_State* L, int stream) { process_t* self = (process_t*) lua_touserdata(L, 1); - unsigned long read_size = luaL_optunsigned(L, 2, READ_BUF_SIZE); + unsigned long read_size = luaL_checkunsigned(L, 2); luaL_Buffer b; uint8_t* buffer = (uint8_t*) luaL_buffinitsize(L, &b, read_size); From d20a9c3faf8edf83c195a9657bb84d5fcc7b3bee Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 17:43:44 +0800 Subject: [PATCH 17/19] use luaL_checkudata() instead of lua_touserdata --- src/api/process.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index ea43b52e..fb8665bd 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -206,7 +206,7 @@ static int f_tostring(lua_State* L) static int f_pid(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); lua_pushnumber(L, reproc_pid(self->process)); return 1; @@ -227,7 +227,7 @@ static int f_returncode(lua_State *L) static int g_read(lua_State* L, int stream) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); unsigned long read_size = luaL_checkunsigned(L, 2); luaL_Buffer b; @@ -271,7 +271,7 @@ static int f_read(lua_State* L) static int f_write(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); size_t data_size = 0; const char* data = luaL_checklstring(L, 2, &data_size); @@ -292,7 +292,7 @@ static int f_write(lua_State* L) static int f_close_stream(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); int stream = luaL_checknumber(L, 2); int out = reproc_close(self->process, stream); @@ -304,7 +304,7 @@ static int f_close_stream(lua_State* L) static int f_wait(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); int timeout = luaL_optnumber(L, 2, 0); @@ -319,7 +319,7 @@ static int f_wait(lua_State* L) static int f_terminate(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); int out = reproc_terminate(self->process); ASSERT_REPROC_ERRNO(L, out); @@ -332,7 +332,7 @@ static int f_terminate(lua_State* L) static int f_kill(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); int out = reproc_kill(self->process); ASSERT_REPROC_ERRNO(L, out); @@ -345,7 +345,7 @@ static int f_kill(lua_State* L) static int f_running(lua_State* L) { - process_t* self = (process_t*) lua_touserdata(L, 1); + process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); poll_process(self, 0); lua_pushboolean(L, self->running); From 8ebb40798bb34b4d7f77ef464283b5de72bdd0b0 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 8 Jul 2021 17:47:18 +0800 Subject: [PATCH 18/19] fix lua stack corruption --- src/api/process.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/process.c b/src/api/process.c index fb8665bd..ad2a7321 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -264,8 +264,8 @@ static int f_read_stderr(lua_State* L) static int f_read(lua_State* L) { - int stream = luaL_checknumber(L, 1); - lua_remove(L, 1); // remove the number we just read + int stream = luaL_checknumber(L, 2); + lua_remove(L, 2); return g_read(L, stream); } From 3c15d99cec8b5ee2d117d9ebbe20d7031acbd131 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Fri, 9 Jul 2021 08:37:56 +0800 Subject: [PATCH 19/19] add default value for process:read* functions This time with more checks against scrutiny --- src/api/process.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/process.c b/src/api/process.c index ad2a7321..4b018e4c 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -10,6 +10,8 @@ #include #include "api.h" +#define READ_BUF_SIZE 2048 + #define L_GETTABLE(L, idx, key, conv, def) ( \ lua_getfield(L, idx, key), \ conv(L, -1, def) \ @@ -228,7 +230,7 @@ static int f_returncode(lua_State *L) static int g_read(lua_State* L, int stream) { process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); - unsigned long read_size = luaL_checkunsigned(L, 2); + unsigned long read_size = luaL_optunsigned(L, 2, READ_BUF_SIZE); luaL_Buffer b; uint8_t* buffer = (uint8_t*) luaL_buffinitsize(L, &b, read_size); @@ -266,6 +268,9 @@ static int f_read(lua_State* L) { int stream = luaL_checknumber(L, 2); lua_remove(L, 2); + if (stream > REPROC_STREAM_ERR) + L_RETURN_REPROC_ERROR(L, REPROC_EINVAL); + return g_read(L, stream); }