diff --git a/stand/liblua/lutils.c b/stand/liblua/lutils.c index 0be9f5f28ac3..874dc8bf7d5d 100644 --- a/stand/liblua/lutils.c +++ b/stand/liblua/lutils.c @@ -1,472 +1,480 @@ /*- * Copyright (c) 2014 Pedro Souza * All rights reserved. * * 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. * */ #include #include "lua.h" #include "lauxlib.h" #include "lstd.h" #include "lutils.h" #include "bootstrap.h" /* * Like loader.perform, except args are passed already parsed * on the stack. */ static int lua_command(lua_State *L) { int i; int res = 1; int argc = lua_gettop(L); char **argv; argv = malloc(sizeof(char *) * (argc + 1)); if (argv == NULL) return 0; for (i = 0; i < argc; i++) argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1); argv[argc] = NULL; res = interp_builtin_cmd(argc, argv); free(argv); lua_pushinteger(L, res); return 1; } static int lua_has_command(lua_State *L) { const char *cmd; cmd = luaL_checkstring(L, 1); if (interp_has_builtin_cmd(cmd)) { lua_pushboolean(L, 1); return 1; } lua_pushnil(L); lua_pushstring(L, "Builtin command not found"); return 2; } static int lua_has_feature(lua_State *L) { const char *feature; char *msg; feature = luaL_checkstring(L, 1); if (feature_name_is_enabled(feature)) { lua_pushboolean(L, 1); return 1; } lua_pushnil(L); lua_pushstring(L, "Feature not enabled"); return 2; } static int lua_perform(lua_State *L) { int argc; char **argv; int res = 1; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { res = interp_builtin_cmd(argc, argv); free(argv); } lua_pushinteger(L, res); return 1; } +static int +lua_exit(lua_State *L) +{ + exit(luaL_checkinteger(L, 1)); + return 0; +} + static int lua_command_error(lua_State *L) { lua_pushstring(L, command_errbuf); return 1; } /* * Accepts a space-delimited loader command and runs it through the standard * loader parsing, as if it were executed at the loader prompt by the user. */ static int lua_interpret(lua_State *L) { const char *interp_string; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } interp_string = luaL_checkstring(L, 1); lua_pushinteger(L, interp_run(interp_string)); return 1; } static int lua_parse(lua_State *L) { int argc, nargc; char **argv; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { for (nargc = 0; nargc < argc; ++nargc) { lua_pushstring(L, argv[nargc]); } free(argv); return nargc; } lua_pushnil(L); return 1; } static int lua_getchar(lua_State *L) { lua_pushinteger(L, getchar()); return 1; } static int lua_ischar(lua_State *L) { lua_pushboolean(L, ischar()); return 1; } static int lua_gets(lua_State *L) { char buf[129]; ngets(buf, 128); lua_pushstring(L, buf); return 1; } static int lua_time(lua_State *L) { lua_pushinteger(L, time(NULL)); return 1; } static int lua_delay(lua_State *L) { delay((int)luaL_checknumber(L, 1)); return 0; } static int lua_getenv(lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); return 1; } static int lua_setenv(lua_State *L) { const char *key, *val; key = luaL_checkstring(L, 1); val = luaL_checkstring(L, 2); lua_pushinteger(L, setenv(key, val, 1)); return 1; } static int lua_unsetenv(lua_State *L) { const char *ev; ev = luaL_checkstring(L, 1); lua_pushinteger(L, unsetenv(ev)); return 1; } static int lua_printc(lua_State *L) { ssize_t cur, l; const char *s = luaL_checklstring(L, 1, &l); for (cur = 0; cur < l; ++cur) putchar((unsigned char)*(s++)); return 1; } static int lua_openfile(lua_State *L) { const char *mode, *str; int nargs; nargs = lua_gettop(L); if (nargs < 1 || nargs > 2) { lua_pushnil(L); return 1; } str = lua_tostring(L, 1); mode = "r"; if (nargs > 1) { mode = lua_tostring(L, 2); if (mode == NULL) { lua_pushnil(L); return 1; } } FILE * f = fopen(str, mode); if (f != NULL) { FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**)); *ptr = f; } else lua_pushnil(L); return 1; } static int lua_closefile(lua_State *L) { FILE ** f; if (lua_gettop(L) != 1) { lua_pushboolean(L, 0); return 1; } f = (FILE**)lua_touserdata(L, 1); if (f != NULL && *f != NULL) { lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0); *f = NULL; } else lua_pushboolean(L, 0); return 1; } static int lua_readfile(lua_State *L) { FILE **f; size_t size, r; char * buf; if (lua_gettop(L) < 1 || lua_gettop(L) > 2) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } if (lua_gettop(L) == 2) size = (size_t)lua_tonumber(L, 2); else size = (*f)->size; buf = (char*)malloc(size); r = fread(buf, 1, size, *f); lua_pushlstring(L, buf, r); free(buf); lua_pushinteger(L, r); return 2; } /* * Implements io.write(file, ...) * Any number of string and number arguments may be passed to it, * and it will return the number of bytes written, or nil, an error string, and * the errno. */ static int lua_writefile(lua_State *L) { FILE **f; const char *buf; int i, nargs; size_t bufsz, w, wrsz; buf = NULL; bufsz = 0; w = 0; wrsz = 0; nargs = lua_gettop(L); if (nargs < 2) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } /* Do a validation pass first */ for (i = 0; i < nargs - 1; i++) { /* * With Lua's API, lua_isstring really checks if the argument * is a string or a number. The latter will be implicitly * converted to a string by our later call to lua_tolstring. */ if (!lua_isstring(L, i + 2)) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } } for (i = 0; i < nargs - 1; i++) { /* We've already validated; there's no chance of failure */ buf = lua_tolstring(L, i + 2, &bufsz); wrsz = fwrite(buf, 1, bufsz, *f); if (wrsz < bufsz) return luaL_fileresult(L, 0, NULL); w += wrsz; } lua_pushinteger(L, w); return 1; } #define REG_SIMPLE(n) { #n, lua_ ## n } static const struct luaL_Reg loaderlib[] = { - REG_SIMPLE(delay), - REG_SIMPLE(command_error), REG_SIMPLE(command), - REG_SIMPLE(interpret), - REG_SIMPLE(parse), + REG_SIMPLE(command_error), + REG_SIMPLE(delay), + REG_SIMPLE(exit), REG_SIMPLE(getenv), REG_SIMPLE(has_command), REG_SIMPLE(has_feature), + REG_SIMPLE(interpret), + REG_SIMPLE(parse), REG_SIMPLE(perform), REG_SIMPLE(printc), /* Also registered as the global 'printc' */ REG_SIMPLE(setenv), REG_SIMPLE(time), REG_SIMPLE(unsetenv), { NULL, NULL }, }; static const struct luaL_Reg iolib[] = { { "close", lua_closefile }, REG_SIMPLE(getchar), REG_SIMPLE(gets), REG_SIMPLE(ischar), { "open", lua_openfile }, { "read", lua_readfile }, { "write", lua_writefile }, { NULL, NULL }, }; #undef REG_SIMPLE static void lua_add_feature(void *cookie, const char *name, const char *desc, bool enabled) { lua_State *L = cookie; /* * The feature table consists solely of features that are enabled, and * their associated descriptions for debugging purposes. */ lua_pushstring(L, desc); lua_setfield(L, -2, name); } static void lua_add_features(lua_State *L) { lua_newtable(L); feature_iter(&lua_add_feature, L); /* * We should still have just the table on the stack after we're done * iterating. */ lua_setfield(L, -2, "features"); } int luaopen_loader(lua_State *L) { luaL_newlib(L, loaderlib); /* Add loader.machine and loader.machine_arch properties */ lua_pushstring(L, MACHINE); lua_setfield(L, -2, "machine"); lua_pushstring(L, MACHINE_ARCH); lua_setfield(L, -2, "machine_arch"); lua_pushstring(L, LUA_PATH); lua_setfield(L, -2, "lua_path"); lua_pushinteger(L, bootprog_rev); lua_setfield(L, -2, "version"); lua_pushinteger(L, CMD_OK); lua_setfield(L, -2, "CMD_OK"); lua_pushinteger(L, CMD_WARN); lua_setfield(L, -2, "CMD_WARN"); lua_pushinteger(L, CMD_ERROR); lua_setfield(L, -2, "CMD_ERROR"); lua_pushinteger(L, CMD_CRIT); lua_setfield(L, -2, "CMD_CRIT"); lua_pushinteger(L, CMD_FATAL); lua_setfield(L, -2, "CMD_FATAL"); lua_add_features(L); /* Set global printc to loader.printc */ lua_register(L, "printc", lua_printc); return 1; } int luaopen_io(lua_State *L) { luaL_newlib(L, iolib); return 1; } diff --git a/stand/lua/loader.lua.8 b/stand/lua/loader.lua.8 index ff3b91ddfb09..e5aee7e8602d 100644 --- a/stand/lua/loader.lua.8 +++ b/stand/lua/loader.lua.8 @@ -1,192 +1,196 @@ .\" .\" Copyright (c) 2024 Netflix, Inc. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd February 6, 2024 .Dt LOADER.LUA 8 .Os .Sh NAME .Nm loader.lua .Nd Fx Lua loader module .Sh DESCRIPTION The built-in Lua bindings for the .Fx boot loaders using the Lua interpreter are available via the .Ic loader table. .Pp The .Ic loader table is always available in Lua scripts. There is no need to require it like other loader-specific modules. .Ss Exported Variables The following variables are provided by the Lua interpreter in the .Nm loader table: .Bl -tag -width machine_arch .It Ic machine The target's .Va hw.machine .Xr sysctl 8 value. .It Ic machine_arch The target's .Va hw.machine_arch .Xr sysctl 8 value. Some boot loaders are 32-bit applications that then load a 64-bit kernel. In these cases, .Ic machine_arch represents the 32-bit architecture, not the 64-bit architecture. .It Ic lua_path The current lua loading path. .It Ic version The version of the boot program. .El .Ss Exported Functions The following functions are exported in the .Nm loader table. .Bl -tag -width term_putimage .It Fn delay usec Delay for .Va usec microseconds. .It Fn command_error Returns the error string from the last command to fail. .It Fn command argc argv Like .Fn perform but the arguments are already parsed onto the stack. +.It Fn exit status +Exit the boot loader back to the firmware with a status of +.Va status . +The interpretation of this value is firmware specific. .It Fn interpret str Execute the loader builtin command .Va str as if it were typed by the user. This will first try to execute .Va str as Lua. If that fails, it will attempt to execute it as a cli command, including those defined by the .Xr cli.lua 8 mechanism. If that fails, it will attempt to execute it as a builtin command and return the same values as .Fn perform . .It Fn parse str Parses the command .Va str into its words and return those words on the stack. .It Fn getenv name Obtains the value of the environment variable .Va name . .It Fn has_command cmd returns .Va true if .Va commmand is present in the interpreter as a builtin. Otherwise it returns .Va nil and an error string. It does not check the .Dq cli table to see if a user defined command has been created. .It Fn has_feature feature returns .Va true if the .Va feature is enabled. Otherwise it returns .Va nil and an error string. .It Fn perform str Execute the loader builtin command .Va str . Returns the result of the command, one of the following values: .Bl -tag -width loader -offset indent .It loader.CMD_OK The command completed successfully. .It loader.CMD_WARN The command was successful, but the user stopped its output prematurely. .It loader.CMD_ERROR The command did not complete successfully. Use .Va command_error to retrieve the error. .It loader.CMD_CRIT The command returned a critical error that was already printed. .It loader.CMD_FATAL The command determined continuation was not possible and the loader panicked. In practice, though, .Fn panic does not return. .El .It Fn printc str Outputs the string using the loader's .Fn putchar function. This function is also available globally as .Fn printc . .It Fn setenv name value Insert or reset the environment variable .Va name into the loader's environment list. If no environment variable with this name exists, one is created. If one exists, its value is replaced. .It Fn time Returns the loader's notion of time, in seconds since 1970. The value of loader's notion varies somewhat between different loading environments. .It Fn unsetenv name Removes the environment variable .Va name from the loader's environment list. .El .Ss Compatibility The functions .Fn fb_bezier , .Fn fb_drawrect , .Fn fb_line , .Fn fb_putimage , .Fn fb_set_pixel , .Fn term_drawrect , and .Fn term_putimage have moved to the .Ic gfx table. They remain in the .Ic loader table for a transition period and are documented in .Xr gfx.lua 8 . .Ss Default File In addition, the Lua interpreters start with the file .Pa /boot/lua/loader.lua when they start to boot the system. The default one will fixup the screen, load the configuration files, check for a password, and then load the menu or load the kernel file and then return. If autoboot is enabled, the loaded files will boot. .Sh SEE ALSO .Xr loader.conf 5 , .Xr core.lua 8 , .Xr gfx.lua 8 , .Xr loader 8 , .Xr sysctl 8 .Sh AUTHORS The .Nm man page was written by .An Warner Losh Aq Mt imp@FreeBSD.org . .Sh BUGS .Fn command and .Fn perform should return a tuple when there's .Va CMD_ERROR or worse.