git: c2caf3b3313f - main - flua: lposix: add more useful functions for general purpose scripts
Date: Mon, 30 Sep 2024 03:53:35 UTC
The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=c2caf3b3313fe73bfc9b4b984c21da8d571f8798 commit c2caf3b3313fe73bfc9b4b984c21da8d571f8798 Author: Kyle Evans <kevans@FreeBSD.org> AuthorDate: 2023-03-11 06:03:02 +0000 Commit: Kyle Evans <kevans@FreeBSD.org> CommitDate: 2024-09-30 03:52:20 +0000 flua: lposix: add more useful functions for general purpose scripts unistd: - _exit - close - fork - getpid - pipe - read - write libgen: - basename, dirname stdlib: - realpath These are sufficient for a number of real world scenarios. In our first application of them, we use the libgen+stdlib additions to grab the script dir based on argv[0]. The unistd assortment is then used to outsource a bunch of work to forks and report back to the main process. Reviewed by: emaste, imp Differential Revision: https://reviews.freebsd.org/D39083 --- libexec/flua/linit_flua.c | 3 + libexec/flua/modules/lposix.c | 415 ++++++++++++++++++++++++++++++++++++++---- libexec/flua/modules/lposix.h | 3 + 3 files changed, 388 insertions(+), 33 deletions(-) diff --git a/libexec/flua/linit_flua.c b/libexec/flua/linit_flua.c index 4635970d1fd7..5cae7531b7da 100644 --- a/libexec/flua/linit_flua.c +++ b/libexec/flua/linit_flua.c @@ -57,8 +57,11 @@ static const luaL_Reg loadedlibs[] = { #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.utsname", luaopen_posix_sys_utsname}, + {"posix.sys.wait", luaopen_posix_sys_wait}, {"posix.unistd", luaopen_posix_unistd}, {"fbsd", luaopen_fbsd}, {NULL, NULL} diff --git a/libexec/flua/modules/lposix.c b/libexec/flua/modules/lposix.c index fa3fd5f8e589..9ca57d409d10 100644 --- a/libexec/flua/modules/lposix.c +++ b/libexec/flua/modules/lposix.c @@ -1,35 +1,18 @@ /*- - * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Copyright (c) 2019, 2023 Kyle Evans <kevans@FreeBSD.org> * + * SPDX-License-Identifier: BSD-2-Clause */ #include <sys/stat.h> #include <sys/utsname.h> +#include <sys/wait.h> #include <errno.h> #include <grp.h> +#include <libgen.h> #include <pwd.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -40,6 +23,39 @@ /* * Minimal implementation of luaposix needed for internal FreeBSD bits. */ +static int +lua__exit(lua_State *L) +{ + int code, narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg == 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 narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg > 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); + return (3); + } + + outpath = basename(inpath); + lua_pushstring(L, outpath); + free(inpath); + return (1); +} static int lua_chmod(lua_State *L) @@ -57,10 +73,10 @@ lua_chmod(lua_State *L) 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 @@ -120,14 +136,32 @@ lua_chown(lua_State *L) } static int -lua_getpid(lua_State *L) +lua_pclose(lua_State *L) { - int n; + int error, fd, n; n = lua_gettop(L); - luaL_argcheck(L, n == 0, 1, "too many arguments"); - lua_pushinteger(L, getpid()); - return 1; + luaL_argcheck(L, n == 1, 1, + "close takes exactly one argument (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 @@ -163,7 +197,271 @@ lua_uname(lua_State *L) return (1); } -#define REG_SIMPLE(n) { #n, lua_ ## n } +static int +lua_dirname(lua_State *L) +{ + char *inpath, *outpath; + int narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg > 0, 1, + "dirname takes at least one argument (path)"); + inpath = strdup(luaL_checkstring(L, 1)); + if (inpath == NULL) { + lua_pushnil(L); + lua_pushstring(L, strerror(ENOMEM)); + lua_pushinteger(L, ENOMEM); + return (3); + } + + outpath = dirname(inpath); + lua_pushstring(L, outpath); + free(inpath); + return (1); +} + +static int +lua_fork(lua_State *L) +{ + pid_t pid; + int narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg == 0, 1, "too many arguments"); + + pid = fork(); + if (pid < 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return (3); + } + + lua_pushinteger(L, pid); + return (1); +} + +static int +lua_getpid(lua_State *L) +{ + int narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg == 0, 1, "too many arguments"); + lua_pushinteger(L, getpid()); + return (1); +} + +static int +lua_pipe(lua_State *L) +{ + int error, fd[2], narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg == 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, narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg == 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; + } + + buf = malloc(sz); + if (buf == NULL) + 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() */ + + free(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 narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg > 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); + return (3); + } + + lua_pushstring(L, outpath); + free(outpath); + return (1); +} + +static int +lua_wait(lua_State *L) +{ + pid_t pid; + int options, status; + int narg; + + narg = lua_gettop(L); + + pid = -1; + status = options = 0; + if (narg >= 1 && !lua_isnil(L, 1)) + pid = luaL_checkinteger(L, 1); + if (narg >= 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, narg; + + narg = lua_gettop(L); + luaL_argcheck(L, narg >= 2, 1, + "write takes at least two arguments (fd, buf, sz, off)"); + luaL_argcheck(L, narg <= 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 (narg >= 3 && !lua_isnil(L, 3)) + sz = luaL_checkinteger(L, 3); + + offset = 0; + if (narg >= 4 && !lua_isnil(L, 4)) + offset = luaL_checkinteger(L, 4); + + if ((size_t)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 }, @@ -174,18 +472,69 @@ static const struct luaL_Reg sys_utsnamelib[] = { { 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) { luaL_newlib(L, sys_statlib); - return 1; + 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 @@ -199,5 +548,5 @@ int luaopen_posix_unistd(lua_State *L) { luaL_newlib(L, unistdlib); - return 1; + return (1); } diff --git a/libexec/flua/modules/lposix.h b/libexec/flua/modules/lposix.h index e37caaae9d04..9d2c97b0d2dc 100644 --- a/libexec/flua/modules/lposix.h +++ b/libexec/flua/modules/lposix.h @@ -7,6 +7,9 @@ #include <lua.h> +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_utsname(lua_State *L); +int luaopen_posix_sys_wait(lua_State *L); int luaopen_posix_unistd(lua_State *L);