diff --git a/libexec/flua/modules/lposix.c b/libexec/flua/modules/lposix.c --- a/libexec/flua/modules/lposix.c +++ b/libexec/flua/modules/lposix.c @@ -201,6 +201,60 @@ } +static int +lua_execp(lua_State *L) +{ + int n, argc; + const char *file; + const char **argv; + + n = lua_gettop(L); + luaL_argcheck(L, n == 2, n > 2 ? 3 : n, + "execp takes exactly two arguments"); + + file = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TTABLE); + + lua_len(L, 2); + argc = lua_tointeger(L, -1); + + /* Use lua_newuserdatauv() to allocate a scratch buffer that is tracked + and freed by lua's GC. It is necessary to use this approach rather than + a plain malloc in order to avoid a leak if a lua error is raised later + in this function (e.g. by luaL_argerror()). + The (argc + 2) size gives enough space in the buffer for argv[0] and + the terminating NULL. */ + argv = lua_newuserdatauv(L, (argc + 2) * sizeof(char *), 0); + + /* Sequential tables in lua start at index 1 by convention. + If there happens to be a string at index 0, use that to + override the default argv[0]. This matches the lposix API. */ + lua_pushinteger(L, 0); + lua_gettable(L, 2); + argv[0] = lua_tostring(L, -1); + if (argv[0] == NULL) { + argv[0] = file; + } + + for (int i = 1; i <= argc; i++) { + lua_pushinteger(L, i); + lua_gettable(L, 2); + argv[i] = lua_tostring(L, -1); + if (argv[i] == NULL) { + luaL_argerror(L, 2, + "argv table must contain only strings"); + } + } + argv[argc + 1] = NULL; + + execvp(file, (char **)argv); + + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return (3); +} + static int lua_fnmatch(lua_State *L) { @@ -541,6 +595,7 @@ REG_SIMPLE(chown), REG_DEF(close, lua_pclose), REG_SIMPLE(dup2), + REG_SIMPLE(execp), REG_SIMPLE(fork), REG_SIMPLE(getpid), REG_SIMPLE(pipe),