diff --git a/libexec/flua/linit_flua.c b/libexec/flua/linit_flua.c --- a/libexec/flua/linit_flua.c +++ b/libexec/flua/linit_flua.c @@ -58,7 +58,10 @@ #endif /* FreeBSD Extensions */ {"lfs", luaopen_lfs}, + {"posix.libgen", luaopen_posix_libgen}, + {"posix.stdlib", luaopen_posix_stdlib}, {"posix.sys.stat", luaopen_posix_sys_stat}, + {"posix.sys.wait", luaopen_posix_sys_wait}, {"posix.unistd", luaopen_posix_unistd}, {"ucl", luaopen_ucl}, {NULL, NULL} diff --git a/libexec/flua/modules/lposix.h b/libexec/flua/modules/lposix.h --- a/libexec/flua/modules/lposix.h +++ b/libexec/flua/modules/lposix.h @@ -8,5 +8,8 @@ #include +int luaopen_posix_libgen(lua_State *L); +int luaopen_posix_stdlib(lua_State *L); int luaopen_posix_sys_stat(lua_State *L); +int luaopen_posix_sys_wait(lua_State *L); int luaopen_posix_unistd(lua_State *L); 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 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2019 Kyle Evans + * + * Copyright (c) 2019, 2023 Kyle Evans * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,10 +29,13 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include +#include #include +#include #include #include @@ -42,6 +46,68 @@ /* * Minimal implementation of luaposix needed for internal FreeBSD bits. */ +static int +lposix_alloc(lua_State *L, size_t sz, char **buf) +{ + char *nbuf; + void *ud; + lua_Alloc allocf; + + if (sz == 0) + return EINVAL; + + allocf = lua_getallocf(L, &ud); + + nbuf = allocf(ud, NULL, 0, sz); + if (nbuf == NULL) + return ENOMEM; + + *buf = nbuf; + return 0; +} + +static void +lposix_free(lua_State *L, size_t sz, char *buf) +{ + void *ud; + lua_Alloc allocf; + + allocf = lua_getallocf(L, &ud); + (void)allocf(ud, buf, sz, 0); +} + +static int +lua__exit(lua_State *L) +{ + int code, n; + + n = lua_gettop(L); + luaL_argcheck(L, n == 1, 1, "_exit takes exactly one argument"); + + code = luaL_checkinteger(L, 1); + _exit(code); +} + +static int +lua_basename(lua_State *L) +{ + char *inpath, *outpath; + int n; + + n = lua_gettop(L); + luaL_argcheck(L, n > 0, 1, "at least one argument required"); + inpath = strdup(luaL_checkstring(L, 1)); + if (inpath == NULL) { + lua_pushnil(L); + lua_pushstring(L, strerror(ENOMEM)); + lua_pushinteger(L, ENOMEM); + } + + outpath = basename(inpath); + lua_pushstring(L, outpath); + free(inpath); + return 1; +} static int lua_chmod(lua_State *L) @@ -115,10 +181,81 @@ lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); - return (3); + return 3; } lua_pushinteger(L, 0); - return (1); + return 1; +} + +static int +lua_pclose(lua_State *L) +{ + int error, fd, n; + + n = lua_gettop(L); + luaL_argcheck(L, n == 1, 1, + "close takes exactly one arguments (fd)"); + + fd = luaL_checkinteger(L, 1); + if (fd < 0) { + error = EBADF; + goto err; + } + + if (close(fd) == 0) { + lua_pushinteger(L, 0); + return 1; + } + + error = errno; +err: + lua_pushnil(L); + lua_pushstring(L, strerror(error)); + lua_pushinteger(L, error); + return 3; + +} + +static int +lua_dirname(lua_State *L) +{ + char *inpath, *outpath; + int n; + + n = lua_gettop(L); + luaL_argcheck(L, n > 0, 1, "at least one argument required"); + inpath = strdup(luaL_checkstring(L, 1)); + if (inpath == NULL) { + lua_pushnil(L); + lua_pushstring(L, strerror(ENOMEM)); + lua_pushinteger(L, ENOMEM); + } + + outpath = dirname(inpath); + lua_pushstring(L, outpath); + free(inpath); + return 1; +} + +static int +lua_fork(lua_State *L) +{ + pid_t pid; + int n; + + n = lua_gettop(L); + luaL_argcheck(L, n == 0, 1, "too many arguments"); + + pid = fork(); + if (pid < 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return 1; + } + + lua_pushinteger(L, pid); + return 1; } static int @@ -132,18 +269,253 @@ return 1; } -#define REG_SIMPLE(n) { #n, lua_ ## n } +static int +lua_pipe(lua_State *L) +{ + int error, fd[2], n; + + n = lua_gettop(L); + luaL_argcheck(L, n == 0, 1, "too many arguments"); + + error = pipe(fd); + if (error != 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return 1; + } + + lua_pushinteger(L, fd[0]); + lua_pushinteger(L, fd[1]); + return 2; +} + +static int +lua_read(lua_State *L) +{ + char *buf; + ssize_t ret; + size_t sz; + int error, fd, n; + + n = lua_gettop(L); + luaL_argcheck(L, n == 2, 1, + "read takes exactly two arguments (fd, size)"); + + fd = luaL_checkinteger(L, 1); + sz = luaL_checkinteger(L, 2); + + if (fd < 0) { + error = EBADF; + goto err; + } + + error = lposix_alloc(L, sz, &buf); + if (error != 0) + goto err; + + /* + * For 0-byte reads, we'll still push the empty string and let the + * caller deal with EOF to match lposix semantics. + */ + ret = read(fd, buf, sz); + if (ret >= 0) + lua_pushlstring(L, buf, ret); + else if (ret < 0) + error = errno; /* Save to avoid clobber by free() */ + + lposix_free(L, sz, buf); + if (error != 0) + goto err; + + /* Just the string pushed. */ + return 1; +err: + lua_pushnil(L); + lua_pushstring(L, strerror(error)); + lua_pushinteger(L, error); + return 3; +} + +static int +lua_realpath(lua_State *L) +{ + const char *inpath; + char *outpath; + int n; + + n = lua_gettop(L); + luaL_argcheck(L, n > 0, 1, "at least one argument required"); + inpath = luaL_checkstring(L, 1); + + outpath = realpath(inpath, NULL); + if (outpath == NULL) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + } + + lua_pushstring(L, outpath); + free(outpath); + return 1; +} + +static int +lua_wait(lua_State *L) +{ + pid_t pid; + int options, status; + int n; + + n = lua_gettop(L); + + pid = -1; + status = options = 0; + if (n >= 1 && !lua_isnil(L, 1)) + pid = luaL_checkinteger(L, 1); + if (n >= 2 && !lua_isnil(L, 2)) + options = luaL_checkinteger(L, 2); + + pid = waitpid(pid, &status, options); + if (pid < 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return 3; + } + + lua_pushinteger(L, pid); + if (pid == 0) { + lua_pushliteral(L, "running"); + return 2; + } + + if (WIFCONTINUED(status)) { + lua_pushliteral(L, "continued"); + return 2; + } else if(WIFSTOPPED(status)) { + lua_pushliteral(L, "stopped"); + lua_pushinteger(L, WSTOPSIG(status)); + return 3; + } else if (WIFEXITED(status)) { + lua_pushliteral(L, "exited"); + lua_pushinteger(L, WEXITSTATUS(status)); + return 3; + } else if (WIFSIGNALED(status)) { + lua_pushliteral(L, "killed"); + lua_pushinteger(L, WTERMSIG(status)); + return 3; + } + + return 1; +} + +static int +lua_write(lua_State *L) +{ + const char *buf; + size_t bufsz, sz; + ssize_t ret; + off_t offset; + int error, fd, n; + + n = lua_gettop(L); + luaL_argcheck(L, n >= 2, 1, + "write takes at least two arguments (fd, buf, sz, off)"); + luaL_argcheck(L, n <= 4, 5, + "write takes no more than four arguments (fd, buf, sz, off)"); + + fd = luaL_checkinteger(L, 1); + if (fd < 0) { + error = EBADF; + goto err; + } + + buf = luaL_checkstring(L, 2); + + bufsz = sz = lua_rawlen(L, 2); + if (n >= 3 && !lua_isnil(L, 3)) + sz = luaL_checkinteger(L, 3); + + offset = 0; + if (n >= 4 && !lua_isnil(L, 4)) + offset = luaL_checkinteger(L, 4); + + if (offset > bufsz || offset + sz > bufsz) { + lua_pushnil(L); + lua_pushfstring(L, + "write: invalid access offset %zu, size %zu in a buffer size %zu", + offset, sz, bufsz); + lua_pushinteger(L, EINVAL); + return 3; + } + + ret = write(fd, buf + offset, sz); + if (ret < 0) { + error = errno; + goto err; + } + + lua_pushinteger(L, ret); + return 1; +err: + lua_pushnil(L); + lua_pushstring(L, strerror(error)); + lua_pushinteger(L, error); + return 3; +} + +#define REG_DEF(n, func) { #n, func } +#define REG_SIMPLE(n) REG_DEF(n, lua_ ## n) +static const struct luaL_Reg libgenlib[] = { + REG_SIMPLE(basename), + REG_SIMPLE(dirname), + { NULL, NULL }, +}; + +static const struct luaL_Reg stdliblib[] = { + REG_SIMPLE(realpath), + { NULL, NULL }, +}; + static const struct luaL_Reg sys_statlib[] = { REG_SIMPLE(chmod), { NULL, NULL }, }; +static const struct luaL_Reg sys_waitlib[] = { + REG_SIMPLE(wait), + {NULL, NULL}, +}; + static const struct luaL_Reg unistdlib[] = { - REG_SIMPLE(getpid), + REG_SIMPLE(_exit), REG_SIMPLE(chown), + REG_DEF(close, lua_pclose), + REG_SIMPLE(fork), + REG_SIMPLE(getpid), + REG_SIMPLE(pipe), + REG_SIMPLE(read), + REG_SIMPLE(write), { NULL, NULL }, }; + #undef REG_SIMPLE +#undef REG_DEF + +int +luaopen_posix_libgen(lua_State *L) +{ + luaL_newlib(L, libgenlib); + return 1; +} + +int +luaopen_posix_stdlib(lua_State *L) +{ + luaL_newlib(L, stdliblib); + return 1; +} int luaopen_posix_sys_stat(lua_State *L) @@ -152,6 +524,30 @@ return 1; } +int +luaopen_posix_sys_wait(lua_State *L) +{ + luaL_newlib(L, sys_waitlib); + +#define lua_pushflag(L, flag) do { \ + lua_pushinteger(L, flag); \ + lua_setfield(L, -2, #flag); \ +} while(0) + + /* Only these two exported by lposix */ + lua_pushflag(L, WNOHANG); + lua_pushflag(L, WUNTRACED); + + lua_pushflag(L, WCONTINUED); + lua_pushflag(L, WSTOPPED); + lua_pushflag(L, WTRAPPED); + lua_pushflag(L, WEXITED); + lua_pushflag(L, WNOWAIT); +#undef lua_pushflag + + return 1; +} + int luaopen_posix_unistd(lua_State *L) {