diff --git a/contrib/libucl/lua/lua_ucl.c b/contrib/libucl/lua/lua_ucl.c index d6be69e42a71..1b3f9dfd111c 100644 --- a/contrib/libucl/lua/lua_ucl.c +++ b/contrib/libucl/lua/lua_ucl.c @@ -1,1573 +1,1577 @@ /* Copyright (c) 2014, Vsevolod Stakhov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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 ''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 AUTHOR 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. */ /** * @file lua ucl bindings */ #include "ucl.h" #include "ucl_internal.h" #include "lua_ucl.h" #include +#include "bootstrap.h" + /*** * @module ucl * This lua module allows to parse objects from strings and to store data into * ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects. * @example local ucl = require("ucl") local parser = ucl.parser() local res,err = parser:parse_string('{key=value}') if not res then print('parser error: ' .. err) else local obj = parser:get_object() local got = ucl.to_format(obj, 'json') endif local table = { str = 'value', num = 100500, null = ucl.null, func = function () return 'huh' end } print(ucl.to_format(table, 'ucl')) -- Output: --[[ num = 100500; str = "value"; null = null; func = "huh"; --]] */ #define PARSER_META "ucl.parser.meta" #define EMITTER_META "ucl.emitter.meta" #define NULL_META "ucl.null.meta" #define OBJECT_META "ucl.object.meta" #define UCL_OBJECT_TYPE_META "ucl.type.object" #define UCL_ARRAY_TYPE_META "ucl.type.array" #define UCL_IMPL_ARRAY_TYPE_META "ucl.type.impl_array" static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags); static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, int flags); static int ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags); static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags); static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags); static void *ucl_null; struct _rspamd_lua_text { const char *start; unsigned int len; unsigned int flags; }; enum lua_ucl_push_flags { LUA_UCL_DEFAULT_FLAGS = 0, LUA_UCL_ALLOW_ARRAY = (1u << 0u), LUA_UCL_CONVERT_NIL = (1u << 1u), }; /** * Push a single element of an object to lua * @param L * @param key * @param obj */ static void ucl_object_lua_push_element (lua_State *L, const char *key, const ucl_object_t *obj, int flags) { lua_pushstring (L, key); ucl_object_push_lua_common (L, obj, flags|LUA_UCL_ALLOW_ARRAY); lua_settable (L, -3); } static void lua_ucl_userdata_dtor (void *ud) { struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud; luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx); if (fd->ret != NULL) { free (fd->ret); } free (fd); } static const char * lua_ucl_userdata_emitter (void *ud) { struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud; const char *out = ""; lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx); lua_pcall (fd->L, 0, 1, 0); out = lua_tostring (fd->L, -1); if (out != NULL) { /* We need to store temporary string in a more appropriate place */ if (fd->ret) { free (fd->ret); } fd->ret = strdup (out); } lua_settop (fd->L, 0); return fd->ret; } /** * Push a single object to lua * @param L * @param obj * @return */ static int ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj, int flags) { const ucl_object_t *cur; ucl_object_iter_t it = NULL; if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) { /* Actually we need to push this as an array */ return ucl_object_lua_push_array (L, obj, flags); } lua_createtable (L, 0, obj->len); it = NULL; while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) { ucl_object_lua_push_element (L, ucl_object_key (cur), cur, flags); } luaL_getmetatable (L, UCL_OBJECT_TYPE_META); lua_setmetatable (L, -2); return 1; } /** * Push an array to lua as table indexed by integers * @param L * @param obj * @return */ static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags) { const ucl_object_t *cur; ucl_object_iter_t it; int i = 1, nelt = 0; if (obj->type == UCL_ARRAY) { nelt = obj->len; it = ucl_object_iterate_new (obj); lua_createtable (L, nelt, 0); while ((cur = ucl_object_iterate_safe (it, true))) { ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY)); lua_rawseti (L, -2, i); i ++; } luaL_getmetatable (L, UCL_ARRAY_TYPE_META); lua_setmetatable (L, -2); ucl_object_iterate_free (it); } else { /* Optimize allocation by preallocation of table */ LL_FOREACH (obj, cur) { nelt ++; } lua_createtable (L, nelt, 0); LL_FOREACH (obj, cur) { ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY)); lua_rawseti (L, -2, i); i ++; } luaL_getmetatable (L, UCL_IMPL_ARRAY_TYPE_META); lua_setmetatable (L, -2); } return 1; } /** * Push a simple object to lua depending on its actual type */ static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, int flags) { struct ucl_lua_funcdata *fd; if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) { /* Actually we need to push this as an array */ return ucl_object_lua_push_array (L, obj, flags); } switch (obj->type) { case UCL_BOOLEAN: lua_pushboolean (L, ucl_obj_toboolean (obj)); break; case UCL_STRING: lua_pushlstring (L, ucl_obj_tostring (obj), obj->len); break; case UCL_INT: #if LUA_VERSION_NUM >= 501 lua_pushinteger (L, ucl_obj_toint (obj)); #else lua_pushnumber (L, ucl_obj_toint (obj)); #endif break; case UCL_FLOAT: case UCL_TIME: lua_pushnumber (L, ucl_obj_todouble (obj)); break; case UCL_NULL: if (flags & LUA_UCL_CONVERT_NIL) { lua_pushboolean (L, false); } else { lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null"); } break; case UCL_USERDATA: fd = (struct ucl_lua_funcdata *)obj->value.ud; lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx); break; default: lua_pushnil (L); break; } return 1; } static int ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags) { switch (obj->type) { case UCL_OBJECT: return ucl_object_lua_push_object (L, obj, flags); case UCL_ARRAY: return ucl_object_lua_push_array (L, obj, flags); default: return ucl_object_lua_push_scalar (L, obj, flags); } } /*** * @function ucl_object_push_lua(L, obj, allow_array) * This is a `C` function to push `UCL` object as lua variable. This function * converts `obj` to lua representation using the following conversions: * * - *scalar* values are directly presented by lua objects * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`, * this can be used to pass functions from lua to c and vice-versa * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations * - *objects* are converted to lua tables with string indicies * @param {lua_State} L lua state pointer * @param {ucl_object_t} obj object to push * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays) * @return {int} `1` if an object is pushed to lua */ int ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array) { return ucl_object_push_lua_common (L, obj, allow_array ? LUA_UCL_ALLOW_ARRAY : LUA_UCL_DEFAULT_FLAGS); } int ucl_object_push_lua_filter_nil (lua_State *L, const ucl_object_t *obj, bool allow_array) { return ucl_object_push_lua_common (L, obj, allow_array ? (LUA_UCL_ALLOW_ARRAY|LUA_UCL_CONVERT_NIL) : (LUA_UCL_DEFAULT_FLAGS|LUA_UCL_CONVERT_NIL)); } /** * Parse lua table into object top * @param L * @param top * @param idx */ static ucl_object_t * ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags) { ucl_object_t *obj, *top = NULL, *cur; size_t keylen; const char *k; bool is_array = true, is_implicit = false, found_mt = false; size_t max = 0, nelts = 0; if (idx < 0) { /* For negative indicies we want to invert them */ idx = lua_gettop (L) + idx + 1; } /* First, we check from metatable */ if (luaL_getmetafield (L, idx, "class") != 0) { if (lua_type (L, -1) == LUA_TSTRING) { const char *classname = lua_tostring (L, -1); if (strcmp (classname, UCL_OBJECT_TYPE_META) == 0) { is_array = false; found_mt = true; } else if (strcmp (classname, UCL_ARRAY_TYPE_META) == 0) { is_array = true; found_mt = true; #if LUA_VERSION_NUM >= 502 max = lua_rawlen (L, idx); #else max = lua_objlen (L, idx); #endif nelts = max; } else if (strcmp (classname, UCL_IMPL_ARRAY_TYPE_META) == 0) { is_array = true; is_implicit = true; found_mt = true; #if LUA_VERSION_NUM >= 502 max = lua_rawlen (L, idx); #else max = lua_objlen (L, idx); #endif nelts = max; } } lua_pop (L, 1); } if (!found_mt) { /* Check for array (it is all inefficient) */ lua_pushnil (L); while (lua_next (L, idx) != 0) { lua_pushvalue (L, -2); if (lua_type (L, -1) == LUA_TNUMBER) { double num = lua_tonumber (L, -1); if (num == (int) num) { if (num > max) { max = num; } } else { /* Keys are not integer */ is_array = false; } } else { /* Keys are not numeric */ is_array = false; } lua_pop (L, 2); nelts ++; } } /* Table iterate */ if (is_array) { if (!is_implicit) { top = ucl_object_typed_new (UCL_ARRAY); ucl_object_reserve (top, nelts); } else { top = NULL; } for (size_t i = 1; i <= max; i ++) { lua_pushinteger (L, i); lua_gettable (L, idx); obj = ucl_object_lua_fromelt (L, lua_gettop (L), flags); if (obj != NULL) { if (is_implicit) { DL_APPEND (top, obj); } else { ucl_array_append (top, obj); } } lua_pop (L, 1); } } else { lua_pushnil (L); top = ucl_object_typed_new (UCL_OBJECT); ucl_object_reserve (top, nelts); while (lua_next (L, idx) != 0) { /* copy key to avoid modifications */ lua_pushvalue (L, -2); k = lua_tolstring (L, -1, &keylen); obj = ucl_object_lua_fromelt (L, lua_gettop (L) - 1, flags); if (obj != NULL) { ucl_object_insert_key (top, obj, k, keylen, true); DL_FOREACH (obj, cur) { if (cur->keylen == 0) { cur->keylen = obj->keylen; cur->key = obj->key; } } } lua_pop (L, 2); } } return top; } /** * Get a single element from lua to object obj * @param L * @param obj * @param idx */ static ucl_object_t * ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags) { int type; double num; ucl_object_t *obj = NULL; struct ucl_lua_funcdata *fd; const char *str; size_t sz; type = lua_type (L, idx); switch (type) { case LUA_TSTRING: str = lua_tolstring (L, idx, &sz); if (str) { /* * ucl_object_fromstring_common has a `logic` to use strlen if sz is zero * which is totally broken... */ if (sz > 0) { obj = ucl_object_fromstring_common(str, sz, flags); } else { obj = ucl_object_fromstring_common("", sz, flags); } } else { obj = ucl_object_typed_new (UCL_NULL); } break; case LUA_TNUMBER: num = lua_tonumber (L, idx); if (num == (int64_t)num) { obj = ucl_object_fromint (num); } else { obj = ucl_object_fromdouble (num); } break; case LUA_TBOOLEAN: obj = ucl_object_frombool (lua_toboolean (L, idx)); break; case LUA_TUSERDATA: if (lua_topointer (L, idx) == ucl_null) { obj = ucl_object_typed_new (UCL_NULL); } else { /* Assume it is a text like object */ struct _rspamd_lua_text *t = lua_touserdata (L, idx); if (t) { if (t->len >0) { obj = ucl_object_fromstring_common(t->start, t->len, 0); } else { obj = ucl_object_fromstring_common("", 0, 0); } /* Binary text */ if (t->flags & (1u << 5u)) { obj->flags |= UCL_OBJECT_BINARY; } } } break; case LUA_TTABLE: case LUA_TFUNCTION: case LUA_TTHREAD: if (luaL_getmetafield (L, idx, "__gen_ucl")) { if (lua_isfunction (L, -1)) { lua_settop (L, 3); /* gen, obj, func */ lua_insert (L, 1); /* func, gen, obj */ lua_insert (L, 2); /* func, obj, gen */ lua_call(L, 2, 1); obj = ucl_object_lua_fromelt (L, 1, flags); } lua_pop (L, 2); } else { if (type == LUA_TTABLE) { obj = ucl_object_lua_fromtable (L, idx, flags); } else if (type == LUA_TFUNCTION) { fd = malloc (sizeof (*fd)); if (fd != NULL) { lua_pushvalue (L, idx); fd->L = L; fd->ret = NULL; fd->idx = luaL_ref (L, LUA_REGISTRYINDEX); obj = ucl_object_new_userdata (lua_ucl_userdata_dtor, lua_ucl_userdata_emitter, (void *)fd); } } } break; } return obj; } /** * @function ucl_object_lua_import(L, idx) * Extracts ucl object from lua variable at `idx` position, * @see ucl_object_push_lua for conversion definitions * @param {lua_state} L lua state machine pointer * @param {int} idx index where the source variable is placed * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1, * this object thus needs to be unref'ed after usage. */ ucl_object_t * ucl_object_lua_import (lua_State *L, int idx) { ucl_object_t *obj; int t; t = lua_type (L, idx); switch (t) { case LUA_TTABLE: obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW); break; default: obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW); break; } return obj; } /** * @function ucl_object_lua_import_escape(L, idx) * Extracts ucl object from lua variable at `idx` position escaping JSON strings * @see ucl_object_push_lua for conversion definitions * @param {lua_state} L lua state machine pointer * @param {int} idx index where the source variable is placed * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1, * this object thus needs to be unref'ed after usage. */ ucl_object_t * ucl_object_lua_import_escape (lua_State *L, int idx) { ucl_object_t *obj; int t; t = lua_type (L, idx); switch (t) { case LUA_TTABLE: obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_ESCAPE); break; default: obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_ESCAPE); break; } return obj; } static int lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type) { unsigned char *result; size_t outlen; result = ucl_object_emit_len (obj, type, &outlen); if (result != NULL) { lua_pushlstring (L, (const char *)result, outlen); free (result); } else { lua_pushnil (L); } return 1; } static int lua_ucl_parser_init (lua_State *L) { struct ucl_parser *parser, **pparser; int flags = UCL_PARSER_NO_FILEVARS; if (lua_gettop (L) >= 1) { flags = lua_tonumber (L, 1); } parser = ucl_parser_new (flags); if (parser == NULL) { lua_pushnil (L); } pparser = lua_newuserdata (L, sizeof (parser)); *pparser = parser; luaL_getmetatable (L, PARSER_META); lua_setmetatable (L, -2); return 1; } static struct ucl_parser * lua_ucl_parser_get (lua_State *L, int index) { return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META)); } static ucl_object_t * lua_ucl_object_get (lua_State *L, int index) { return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META)); } static void lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj) { ucl_object_t **pobj; pobj = lua_newuserdata (L, sizeof (*pobj)); *pobj = obj; luaL_getmetatable (L, OBJECT_META); lua_setmetatable (L, -2); } static inline enum ucl_parse_type lua_ucl_str_to_parse_type (const char *str) { enum ucl_parse_type type = UCL_PARSE_UCL; if (str != NULL) { if (strcasecmp (str, "msgpack") == 0) { type = UCL_PARSE_MSGPACK; } else if (strcasecmp (str, "sexp") == 0 || strcasecmp (str, "csexp") == 0) { type = UCL_PARSE_CSEXP; } else if (strcasecmp (str, "auto") == 0) { type = UCL_PARSE_AUTO; } } return type; } /*** * @method parser:parse_file(name) * Parse UCL object from file. * @param {string} name filename to parse * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned @example local parser = ucl.parser() local res,err = parser:parse_file('/some/file.conf') if not res then print('parser error: ' .. err) else -- Do something with object end */ static int lua_ucl_parser_parse_file (lua_State *L) { struct ucl_parser *parser; const char *file; int ret = 2; parser = lua_ucl_parser_get (L, 1); file = luaL_checkstring (L, 2); if (parser != NULL && file != NULL) { if (ucl_parser_add_file (parser, file)) { lua_pushboolean (L, true); ret = 1; } else { lua_pushboolean (L, false); lua_pushstring (L, ucl_parser_get_error (parser)); } } else { lua_pushboolean (L, false); lua_pushstring (L, "invalid arguments"); } return ret; } /*** * @method parser:register_variable(name, value) * Register parser variable * @param {string} name name of variable * @param {string} value value of variable * @return {bool} success @example local parser = ucl.parser() local res = parser:register_variable('CONFDIR', '/etc/foo') */ static int lua_ucl_parser_register_variable (lua_State *L) { struct ucl_parser *parser; const char *name, *value; int ret = 2; parser = lua_ucl_parser_get (L, 1); name = luaL_checkstring (L, 2); value = luaL_checkstring (L, 3); if (parser != NULL && name != NULL && value != NULL) { ucl_parser_register_variable (parser, name, value); lua_pushboolean (L, true); ret = 1; } else { return luaL_error (L, "invalid arguments"); } return ret; } /*** * @method parser:register_variables(vars) * Register parser variables * @param {table} vars names/values of variables * @return {bool} success @example local parser = ucl.parser() local res = parser:register_variables({CONFDIR = '/etc/foo', VARDIR = '/var'}) */ static int lua_ucl_parser_register_variables (lua_State *L) { struct ucl_parser *parser; const char *name, *value; int ret = 2; parser = lua_ucl_parser_get (L, 1); if (parser != NULL && lua_type (L, 2) == LUA_TTABLE) { for (lua_pushnil (L); lua_next (L, 2); lua_pop (L, 1)) { lua_pushvalue (L, -2); name = luaL_checkstring (L, -1); value = luaL_checkstring (L, -2); ucl_parser_register_variable (parser, name, value); lua_pop (L, 1); } lua_pushboolean (L, true); ret = 1; } else { return luaL_error (L, "invalid arguments"); } return ret; } /*** * @method parser:parse_string(input) * Parse UCL object from file. * @param {string} input string to parse * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned */ static int lua_ucl_parser_parse_string (lua_State *L) { struct ucl_parser *parser; const char *string; size_t llen; enum ucl_parse_type type = UCL_PARSE_UCL; int ret = 2; parser = lua_ucl_parser_get (L, 1); string = luaL_checklstring (L, 2, &llen); if (lua_type (L, 3) == LUA_TSTRING) { type = lua_ucl_str_to_parse_type (lua_tostring (L, 3)); } if (parser != NULL && string != NULL) { if (ucl_parser_add_chunk_full (parser, (const unsigned char *)string, llen, 0, UCL_DUPLICATE_APPEND, type)) { lua_pushboolean (L, true); ret = 1; } else { lua_pushboolean (L, false); lua_pushstring (L, ucl_parser_get_error (parser)); } } else { lua_pushboolean (L, false); lua_pushstring (L, "invalid arguments"); } return ret; } /*** * @method parser:parse_text(input) * Parse UCL object from text object (Rspamd specific). * @param {rspamd_text} input text to parse * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned */ static int lua_ucl_parser_parse_text (lua_State *L) { struct ucl_parser *parser; struct _rspamd_lua_text *t; enum ucl_parse_type type = UCL_PARSE_UCL; int ret = 2; parser = lua_ucl_parser_get (L, 1); if (lua_type (L, 2) == LUA_TUSERDATA) { t = lua_touserdata (L, 2); } else if (lua_type (L, 2) == LUA_TSTRING) { const char *s; size_t len; static struct _rspamd_lua_text st_t; s = lua_tolstring (L, 2, &len); st_t.start = s; st_t.len = len; t = &st_t; } else { return luaL_error(L, "invalid argument as input, expected userdata or a string"); } if (lua_type (L, 3) == LUA_TSTRING) { type = lua_ucl_str_to_parse_type (lua_tostring (L, 3)); } if (parser != NULL && t != NULL) { if (ucl_parser_add_chunk_full (parser, (const unsigned char *)t->start, t->len, 0, UCL_DUPLICATE_APPEND, type)) { lua_pushboolean (L, true); ret = 1; } else { lua_pushboolean (L, false); lua_pushstring (L, ucl_parser_get_error (parser)); } } else { lua_pushboolean (L, false); lua_pushstring (L, "invalid arguments"); } return ret; } /*** * @method parser:get_object() * Get top object from parser and export it to lua representation. * @return {variant or nil} ucl object as lua native variable */ static int lua_ucl_parser_get_object (lua_State *L) { struct ucl_parser *parser; ucl_object_t *obj; int ret = 1; parser = lua_ucl_parser_get (L, 1); obj = ucl_parser_get_object (parser); if (obj != NULL) { ret = ucl_object_push_lua (L, obj, false); /* no need to keep reference */ ucl_object_unref (obj); } else { lua_pushnil (L); } return ret; } /*** * @method parser:get_object_wrapped() * Get top object from parser and export it to userdata object without * unwrapping to lua. * @return {ucl.object or nil} ucl object wrapped variable */ static int lua_ucl_parser_get_object_wrapped (lua_State *L) { struct ucl_parser *parser; ucl_object_t *obj; int ret = 1; parser = lua_ucl_parser_get (L, 1); obj = ucl_parser_get_object (parser); if (obj != NULL) { lua_ucl_push_opaque (L, obj); } else { lua_pushnil (L); } return ret; } /*** * @method parser:validate(schema) * Validates the top object in the parser against schema. Schema might be * another object or a string that represents file to load schema from. * * @param {string/table} schema input schema * @return {result,err} two values: boolean result and the corresponding error * */ static int lua_ucl_parser_validate (lua_State *L) { struct ucl_parser *parser, *schema_parser; ucl_object_t *schema; const char *schema_file; struct ucl_schema_error err; parser = lua_ucl_parser_get (L, 1); if (parser && parser->top_obj) { if (lua_type (L, 2) == LUA_TTABLE) { schema = ucl_object_lua_import (L, 2); if (schema == NULL) { lua_pushboolean (L, false); lua_pushstring (L, "cannot load schema from lua table"); return 2; } } else if (lua_type (L, 2) == LUA_TSTRING) { schema_parser = ucl_parser_new (0); schema_file = luaL_checkstring (L, 2); if (!ucl_parser_add_file (schema_parser, schema_file)) { lua_pushboolean (L, false); lua_pushfstring (L, "cannot parse schema file \"%s\": " "%s", schema_file, ucl_parser_get_error (parser)); ucl_parser_free (schema_parser); return 2; } schema = ucl_parser_get_object (schema_parser); ucl_parser_free (schema_parser); } else { lua_pushboolean (L, false); lua_pushstring (L, "invalid schema argument"); return 2; } if (!ucl_object_validate (schema, parser->top_obj, &err)) { lua_pushboolean (L, false); lua_pushfstring (L, "validation error: " "%s", err.msg); } else { lua_pushboolean (L, true); lua_pushnil (L); } ucl_object_unref (schema); } else { lua_pushboolean (L, false); lua_pushstring (L, "invalid parser or empty top object"); } return 2; } static int lua_ucl_parser_gc (lua_State *L) { struct ucl_parser *parser; parser = lua_ucl_parser_get (L, 1); ucl_parser_free (parser); return 0; } /*** * @method object:unwrap() * Unwraps opaque ucl object to the native lua object (performing copying) * @return {variant} any lua object */ static int lua_ucl_object_unwrap (lua_State *L) { ucl_object_t *obj; obj = lua_ucl_object_get (L, 1); if (obj) { ucl_object_push_lua (L, obj, true); } else { lua_pushnil (L); } return 1; } static inline enum ucl_emitter lua_ucl_str_to_emit_type (const char *strtype) { enum ucl_emitter format = UCL_EMIT_JSON_COMPACT; if (strcasecmp (strtype, "json") == 0) { format = UCL_EMIT_JSON; } else if (strcasecmp (strtype, "json-compact") == 0) { format = UCL_EMIT_JSON_COMPACT; } else if (strcasecmp (strtype, "yaml") == 0) { format = UCL_EMIT_YAML; } else if (strcasecmp (strtype, "config") == 0 || strcasecmp (strtype, "ucl") == 0) { format = UCL_EMIT_CONFIG; } return format; } /*** * @method object:tostring(type) * Unwraps opaque ucl object to string (json by default). Optionally you can * specify output format: * * - `json` - fine printed json * - `json-compact` - compacted json * - `config` - fine printed configuration * - `ucl` - same as `config` * - `yaml` - embedded yaml * @param {string} type optional * @return {string} string representation of the opaque ucl object */ static int lua_ucl_object_tostring (lua_State *L) { ucl_object_t *obj; enum ucl_emitter format = UCL_EMIT_JSON_COMPACT; obj = lua_ucl_object_get (L, 1); if (obj) { if (lua_gettop (L) > 1) { if (lua_type (L, 2) == LUA_TSTRING) { const char *strtype = lua_tostring (L, 2); format = lua_ucl_str_to_emit_type (strtype); } } return lua_ucl_to_string (L, obj, format); } else { lua_pushnil (L); } return 1; } /*** * @method object:validate(schema[, path[, ext_refs]]) * Validates the given ucl object using schema object represented as another * opaque ucl object. You can also specify path in the form `#/path/def` to * specify the specific schema element to perform validation. * * @param {ucl.object} schema schema object * @param {string} path optional path for validation procedure * @return {result,err} two values: boolean result and the corresponding * error, if `ext_refs` are also specified, then they are returned as opaque * ucl object as {result,err,ext_refs} */ static int lua_ucl_object_validate (lua_State *L) { ucl_object_t *obj, *schema, *ext_refs = NULL; const ucl_object_t *schema_elt; bool res = false; struct ucl_schema_error err; const char *path = NULL; obj = lua_ucl_object_get (L, 1); schema = lua_ucl_object_get (L, 2); if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) { if (lua_gettop (L) > 2) { if (lua_type (L, 3) == LUA_TSTRING) { path = lua_tostring (L, 3); if (path[0] == '#') { path++; } } else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) == LUA_TTABLE) { /* External refs */ ext_refs = lua_ucl_object_get (L, 3); } if (lua_gettop (L) > 3) { if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) == LUA_TTABLE) { /* External refs */ ext_refs = lua_ucl_object_get (L, 4); } } } if (path) { schema_elt = ucl_object_lookup_path_char (schema, path, '/'); } else { /* Use the top object */ schema_elt = schema; } if (schema_elt) { res = ucl_object_validate_root_ext (schema_elt, obj, schema, ext_refs, &err); if (res) { lua_pushboolean (L, res); lua_pushnil (L); if (ext_refs) { lua_ucl_push_opaque (L, ext_refs); } } else { lua_pushboolean (L, res); lua_pushfstring (L, "validation error: %s", err.msg); if (ext_refs) { lua_ucl_push_opaque (L, ext_refs); } } } else { lua_pushboolean (L, res); lua_pushfstring (L, "cannot find the requested path: %s", path); if (ext_refs) { lua_ucl_push_opaque (L, ext_refs); } } } else { lua_pushboolean (L, res); lua_pushstring (L, "invalid object or schema"); } if (ext_refs) { return 3; } return 2; } static int lua_ucl_object_gc (lua_State *L) { ucl_object_t *obj; obj = lua_ucl_object_get (L, 1); ucl_object_unref (obj); return 0; } static void lua_ucl_parser_mt (lua_State *L) { luaL_newmetatable (L, PARSER_META); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushcfunction (L, lua_ucl_parser_gc); lua_setfield (L, -2, "__gc"); lua_pushcfunction (L, lua_ucl_parser_parse_file); lua_setfield (L, -2, "parse_file"); lua_pushcfunction (L, lua_ucl_parser_parse_string); lua_setfield (L, -2, "parse_string"); lua_pushcfunction (L, lua_ucl_parser_parse_text); lua_setfield (L, -2, "parse_text"); lua_pushcfunction (L, lua_ucl_parser_register_variable); lua_setfield (L, -2, "register_variable"); lua_pushcfunction (L, lua_ucl_parser_register_variables); lua_setfield (L, -2, "register_variables"); lua_pushcfunction (L, lua_ucl_parser_get_object); lua_setfield (L, -2, "get_object"); lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped); lua_setfield (L, -2, "get_object_wrapped"); lua_pushcfunction (L, lua_ucl_parser_validate); lua_setfield (L, -2, "validate"); lua_pop (L, 1); } static void lua_ucl_object_mt (lua_State *L) { luaL_newmetatable (L, OBJECT_META); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushcfunction (L, lua_ucl_object_gc); lua_setfield (L, -2, "__gc"); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "__tostring"); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "tostring"); lua_pushcfunction (L, lua_ucl_object_unwrap); lua_setfield (L, -2, "unwrap"); lua_pushcfunction (L, lua_ucl_object_unwrap); lua_setfield (L, -2, "tolua"); lua_pushcfunction (L, lua_ucl_object_validate); lua_setfield (L, -2, "validate"); lua_pushstring (L, OBJECT_META); lua_setfield (L, -2, "class"); lua_pop (L, 1); } static void lua_ucl_types_mt (lua_State *L) { luaL_newmetatable (L, UCL_OBJECT_TYPE_META); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "__tostring"); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "tostring"); lua_pushstring (L, UCL_OBJECT_TYPE_META); lua_setfield (L, -2, "class"); lua_pop (L, 1); luaL_newmetatable (L, UCL_ARRAY_TYPE_META); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "__tostring"); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "tostring"); lua_pushstring (L, UCL_ARRAY_TYPE_META); lua_setfield (L, -2, "class"); lua_pop (L, 1); luaL_newmetatable (L, UCL_IMPL_ARRAY_TYPE_META); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "__tostring"); lua_pushcfunction (L, lua_ucl_object_tostring); lua_setfield (L, -2, "tostring"); lua_pushstring (L, UCL_IMPL_ARRAY_TYPE_META); lua_setfield (L, -2, "class"); lua_pop (L, 1); } static int lua_ucl_to_json (lua_State *L) { ucl_object_t *obj; int format = UCL_EMIT_JSON; if (lua_gettop (L) > 1) { if (lua_toboolean (L, 2)) { format = UCL_EMIT_JSON_COMPACT; } } obj = ucl_object_lua_import (L, 1); if (obj != NULL) { lua_ucl_to_string (L, obj, format); ucl_object_unref (obj); } else { lua_pushnil (L); } return 1; } static int lua_ucl_to_config (lua_State *L) { ucl_object_t *obj; obj = ucl_object_lua_import (L, 1); if (obj != NULL) { lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG); ucl_object_unref (obj); } else { lua_pushnil (L); } return 1; } /*** * @function ucl.to_format(var, format) * Converts lua variable `var` to the specified `format`. Formats supported are: * * - `json` - fine printed json * - `json-compact` - compacted json * - `config` - fine printed configuration * - `ucl` - same as `config` * - `yaml` - embedded yaml * * If `var` contains function, they are called during output formatting and if * they return string value, then this value is used for output. * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output) * @param {string} format any available format * @return {string} string representation of `var` in the specific `format`. * @example local table = { str = 'value', num = 100500, null = ucl.null, func = function () return 'huh' end } print(ucl.to_format(table, 'ucl')) -- Output: --[[ num = 100500; str = "value"; null = null; func = "huh"; --]] */ static int lua_ucl_to_format (lua_State *L) { ucl_object_t *obj; int format = UCL_EMIT_JSON; bool sort = false; if (lua_gettop (L) > 1) { if (lua_type (L, 2) == LUA_TNUMBER) { format = lua_tonumber (L, 2); if (format < 0 || format >= UCL_EMIT_YAML) { lua_pushnil (L); return 1; } } else if (lua_type (L, 2) == LUA_TSTRING) { const char *strtype = lua_tostring (L, 2); if (strcasecmp (strtype, "json") == 0) { format = UCL_EMIT_JSON; } else if (strcasecmp (strtype, "json-compact") == 0) { format = UCL_EMIT_JSON_COMPACT; } else if (strcasecmp (strtype, "yaml") == 0) { format = UCL_EMIT_YAML; } else if (strcasecmp (strtype, "config") == 0 || strcasecmp (strtype, "ucl") == 0) { format = UCL_EMIT_CONFIG; } else if (strcasecmp (strtype, "msgpack") == 0 || strcasecmp (strtype, "messagepack") == 0) { format = UCL_EMIT_MSGPACK; } } if (lua_isboolean (L, 3)) { sort = lua_toboolean (L, 3); } } obj = ucl_object_lua_import (L, 1); if (obj != NULL) { if (sort) { if (ucl_object_type (obj) == UCL_OBJECT) { ucl_object_sort_keys (obj, UCL_SORT_KEYS_RECURSIVE); } } lua_ucl_to_string (L, obj, format); ucl_object_unref (obj); } else { lua_pushnil (L); } return 1; } static int lua_ucl_null_tostring (lua_State* L) { lua_pushstring (L, "null"); return 1; } static void lua_ucl_null_mt (lua_State *L) { luaL_newmetatable (L, NULL_META); lua_pushcfunction (L, lua_ucl_null_tostring); lua_setfield (L, -2, "__tostring"); lua_pop (L, 1); } int luaopen_ucl (lua_State *L) { lua_ucl_parser_mt (L); lua_ucl_null_mt (L); lua_ucl_object_mt (L); lua_ucl_types_mt (L); /* Create the refs weak table: */ lua_createtable (L, 0, 2); lua_pushliteral (L, "v"); /* tbl, "v" */ lua_setfield (L, -2, "__mode"); lua_pushvalue (L, -1); /* tbl, tbl */ lua_setmetatable (L, -2); /* tbl */ lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs"); lua_newtable (L); lua_pushcfunction (L, lua_ucl_parser_init); lua_setfield (L, -2, "parser"); lua_pushcfunction (L, lua_ucl_to_json); lua_setfield (L, -2, "to_json"); lua_pushcfunction (L, lua_ucl_to_config); lua_setfield (L, -2, "to_config"); lua_pushcfunction (L, lua_ucl_to_format); lua_setfield (L, -2, "to_format"); ucl_null = lua_newuserdata (L, 0); luaL_getmetatable (L, NULL_META); lua_setmetatable (L, -2); lua_pushvalue (L, -1); lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null"); lua_setfield (L, -2, "null"); return 1; } struct ucl_lua_funcdata* ucl_object_toclosure (const ucl_object_t *obj) { if (obj == NULL || obj->type != UCL_USERDATA) { return NULL; } return (struct ucl_lua_funcdata*)obj->value.ud; } + +FLUA_MODULE(ucl); diff --git a/contrib/lyaml/ext/yaml/yaml.c b/contrib/lyaml/ext/yaml/yaml.c index 54478610134f..6a5ddc605e0f 100644 --- a/contrib/lyaml/ext/yaml/yaml.c +++ b/contrib/lyaml/ext/yaml/yaml.c @@ -1,66 +1,70 @@ /* * yaml.c, LibYAML binding for Lua * Written by Andrew Danforth, 2009 * * Copyright (C) 2014-2022 Gary V. Vaughan * Copyright (C) 2009 Andrew Danforth * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Portions of this software were inspired by Perl's YAML::LibYAML module by * Ingy döt Net * */ #include #include #include #include "lyaml.h" +#include "bootstrap.h" + #define MYNAME "yaml" #define MYVERSION MYNAME " library for " LUA_VERSION " / " VERSION #define LYAML__STR_1(_s) (#_s + 1) #define LYAML_STR_1(_s) LYAML__STR_1(_s) static const luaL_Reg R[] = { #define MENTRY(_s) {LYAML_STR_1(_s), (_s)} MENTRY( Pemitter ), MENTRY( Pparser ), MENTRY( Pscanner ), #undef MENTRY {NULL, NULL} }; LUALIB_API int luaopen_yaml (lua_State *L) { parser_init (L); scanner_init (L); luaL_register(L, "yaml", R); lua_pushliteral(L, MYVERSION); lua_setfield(L, -2, "version"); return 1; } + +FLUA_MODULE(yaml); diff --git a/libexec/flua/Makefile b/libexec/flua/Makefile index 86d27c0653d4..231abe5dafa2 100644 --- a/libexec/flua/Makefile +++ b/libexec/flua/Makefile @@ -1,39 +1,64 @@ .include -SUBDIR+= libfreebsd -SUBDIR+= libhash -SUBDIR+= libjail -SUBDIR+= libucl -SUBDIR+= liblyaml +# New flua modules should be added here rather than to SUBDIR so that we can do +# the right thing for both bootstrap flua and target flua. The former does not +# do any shared libs, so we just build them all straight into flua itself rather +# than mucking about with the infrastructure to make them linkable -- thus, why +# these are all structured to have a Makefile that describes what we want +# *installed*, and a Makefile.inc that describes what we need to *build*. +FLUA_MODULES+= libhash +FLUA_MODULES+= libjail +FLUA_MODULES+= libucl +FLUA_MODULES+= liblyaml + +.ifdef BOOTSTRAPPING +FLUA_MODULES+= libfreebsd/sys/linker +FLUA_MODULES+= libfreebsd/kenv +CFLAGS+= -I${.CURDIR} -DBOOTSTRAPPING + +SHAREDIR= ${WORLDTMP}/legacy/usr/share/flua +FLUA_PATH= ${SHAREDIR}/?.lua;${SHAREDIR}/?/init.lua +CFLAGS+= -DBOOTSTRAP_FLUA_PATH=\"${FLUA_PATH:Q}\" + +.for mod in ${FLUA_MODULES} +.include "${mod}/Makefile.inc" +.endfor + +.else + +FLUA_MODULES+= libfreebsd +SUBDIR+= ${FLUA_MODULES} + +.endif LUASRC?= ${SRCTOP}/contrib/lua/src .PATH: ${LUASRC} PROG= flua WARNS?= 3 CWARNFLAGS.gcc+= -Wno-format-nonliteral -LIBADD= lua +LIBADD+= lua # Entry point SRCS+= lua.c # FreeBSD Extensions .PATH: ${.CURDIR}/modules SRCS+= linit_flua.c SRCS+= lfs.c lposix.c lfbsd.c CFLAGS+= -I${SRCTOP}/lib/liblua -I${.CURDIR}/modules -I${LUASRC} CFLAGS+= -DLUA_PROGNAME="\"${PROG}\"" # readline bits; these aren't needed if we're building a bootstrap flua, as we # don't expect that one to see any REPL usage. .if !defined(BOOTSTRAPPING) CFLAGS+= -DLUA_USE_READLINE CFLAGS+= -I${SRCTOP}/lib/libedit -I${SRCTOP}/contrib/libedit LIBADD+= edit LDFLAGS+= -Wl,-E .endif .include diff --git a/libexec/flua/Makefile.inc b/libexec/flua/Makefile.inc index 34505d54d7df..37a49e258ecb 100644 --- a/libexec/flua/Makefile.inc +++ b/libexec/flua/Makefile.inc @@ -1,5 +1,10 @@ SHLIBDIR?= ${LIBDIR}/flua CFLAGS+= \ -I${SRCTOP}/contrib/lua/src \ - -I${SRCTOP}/lib/liblua + -I${SRCTOP}/lib/liblua \ + -I${SRCTOP}/libexec/flua + +.ifdef BOOTSTRAPPING +CFLAGS+= -DBOOTSTRAPPING +.endif diff --git a/libexec/flua/bootstrap.h b/libexec/flua/bootstrap.h new file mode 100644 index 000000000000..74356d8677b3 --- /dev/null +++ b/libexec/flua/bootstrap.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2025 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef FLUA_BOOTSTRAP_H +#define FLUA_BOOTSTRAP_H + +#ifdef BOOTSTRAPPING +#include + +#include + +SET_DECLARE(flua_module_set, const luaL_Reg); +#define FLUA_MODULE_DEF(ident, modname, openfn) \ + static const luaL_Reg ident = { modname, openfn }; \ + DATA_SET(flua_module_set, ident) + +#define FLUA_MODULE_NAMED(mod, name) \ + FLUA_MODULE_DEF(module_ ## mod, name, luaopen_ ## mod) +#define FLUA_MODULE(mod) \ + FLUA_MODULE_DEF(module_ ## mod, #mod, luaopen_ ## mod) +#else /* !BOOTSTRAPPING */ +#define FLUA_MODULE_DEF(ident, modname, openfn) +#define FLUA_MODULE_NAMED(mod, name) +#define FLUA_MODULE(modname) +#endif /* BOOTSTRAPPING */ + +#endif /* FLUA_BOOTSTRAP_H */ diff --git a/libexec/flua/libfreebsd/kenv/Makefile b/libexec/flua/libfreebsd/kenv/Makefile index 1726c892c515..a1b388bb3612 100644 --- a/libexec/flua/libfreebsd/kenv/Makefile +++ b/libexec/flua/libfreebsd/kenv/Makefile @@ -1,5 +1,5 @@ SHLIB_NAME= kenv.so -SRCS+= kenv.c MAN= freebsd.kenv.3lua +.include "Makefile.inc" .include diff --git a/libexec/flua/libfreebsd/kenv/Makefile.inc b/libexec/flua/libfreebsd/kenv/Makefile.inc new file mode 100644 index 000000000000..05819c5280d9 --- /dev/null +++ b/libexec/flua/libfreebsd/kenv/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= kenv.c diff --git a/libexec/flua/libfreebsd/kenv/kenv.c b/libexec/flua/libfreebsd/kenv/kenv.c index 954baa00facb..56b24c72904a 100644 --- a/libexec/flua/libfreebsd/kenv/kenv.c +++ b/libexec/flua/libfreebsd/kenv/kenv.c @@ -1,96 +1,100 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024, Baptiste Daroussin */ #include #include #include #include #include #include #include #include +#include "bootstrap.h" + int luaopen_freebsd_kenv(lua_State *L); static int lua_kenv_get(lua_State *L) { const char *env; int ret, n; char value[1024]; n = lua_gettop(L); if (n == 0) { char *buf, *bp, *cp; int size; size = kenv(KENV_DUMP, NULL, NULL, 0); if (size < 0) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } size += 1; buf = malloc(size); if (buf == NULL) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } if (kenv(KENV_DUMP, NULL, buf, size) < 0) { free(buf); lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } lua_newtable(L); for (bp = buf; *bp != '\0'; bp += strlen(bp) + 1) { cp = strchr(bp, '='); if (cp == NULL) continue; *cp++ = '\0'; lua_pushstring(L, cp); lua_setfield(L, -2, bp); bp = cp; } free(buf); return (1); } env = luaL_checkstring(L, 1); ret = kenv(KENV_GET, env, value, sizeof(value)); if (ret == -1) { if (errno == ENOENT) { lua_pushnil(L); return (1); } lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } lua_pushstring(L, value); return (1); } #define REG_SIMPLE(n) { #n, lua_kenv_ ## n } static const struct luaL_Reg freebsd_kenv[] = { REG_SIMPLE(get), { NULL, NULL }, }; #undef REG_SIMPLE int luaopen_freebsd_kenv(lua_State *L) { luaL_newlib(L, freebsd_kenv); return (1); } + +FLUA_MODULE_NAMED(freebsd_kenv, "freebsd.kenv"); diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile b/libexec/flua/libfreebsd/sys/linker/Makefile index 1adf547b503c..f1f65ad5f6c1 100644 --- a/libexec/flua/libfreebsd/sys/linker/Makefile +++ b/libexec/flua/libfreebsd/sys/linker/Makefile @@ -1,7 +1,6 @@ SHLIB_NAME= linker.so -SRCS+= linker.c - MAN= freebsd.sys.linker.3lua +.include "Makefile.inc" .include diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile.inc b/libexec/flua/libfreebsd/sys/linker/Makefile.inc new file mode 100644 index 000000000000..da65c0070170 --- /dev/null +++ b/libexec/flua/libfreebsd/sys/linker/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= linker.c diff --git a/libexec/flua/libfreebsd/sys/linker/linker.c b/libexec/flua/libfreebsd/sys/linker/linker.c index 87eccfb651f0..c78fbb2b39d2 100644 --- a/libexec/flua/libfreebsd/sys/linker/linker.c +++ b/libexec/flua/libfreebsd/sys/linker/linker.c @@ -1,82 +1,86 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024, Baptiste Daroussin */ #include #include #include #include #include #include #include #include +#include "bootstrap.h" + int luaopen_freebsd_sys_linker(lua_State *L); static int lua_kldload(lua_State *L) { const char *name; int ret; name = luaL_checkstring(L, 1); ret = kldload(name); if (ret == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } lua_pushinteger(L, ret); return (1); } static int lua_kldunload(lua_State *L) { const char *name; int ret, fileid; if (lua_isinteger(L, 1)) { fileid = lua_tointeger(L, 1); } else { name = luaL_checkstring(L, 1); fileid = kldfind(name); } if (fileid == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } ret = kldunload(fileid); lua_pushinteger(L, ret); if (ret == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } lua_pushinteger(L, 0); return (1); } #define REG_SIMPLE(n) { #n, lua_ ## n } static const struct luaL_Reg freebsd_sys_linker[] = { REG_SIMPLE(kldload), REG_SIMPLE(kldunload), { NULL, NULL }, }; #undef REG_SIMPLE int luaopen_freebsd_sys_linker(lua_State *L) { luaL_newlib(L, freebsd_sys_linker); return (1); } + +FLUA_MODULE_NAMED(freebsd_sys_linker, "freebsd.sys.linker"); diff --git a/libexec/flua/libhash/Makefile b/libexec/flua/libhash/Makefile index b7c8d7ee9948..9cbd6f15acae 100644 --- a/libexec/flua/libhash/Makefile +++ b/libexec/flua/libhash/Makefile @@ -1,9 +1,6 @@ SHLIB_NAME= hash.so -SRCS+= lhash.c - -LIBADD+= md - MAN= hash.3lua +.include "Makefile.inc" .include diff --git a/libexec/flua/libhash/Makefile.inc b/libexec/flua/libhash/Makefile.inc new file mode 100644 index 000000000000..d112dfe7df33 --- /dev/null +++ b/libexec/flua/libhash/Makefile.inc @@ -0,0 +1,3 @@ +.PATH: ${.PARSEDIR} +SRCS+= lhash.c +LIBADD+= md diff --git a/libexec/flua/libhash/lhash.c b/libexec/flua/libhash/lhash.c index 4587961fe8a0..7127ddc1d530 100644 --- a/libexec/flua/libhash/lhash.c +++ b/libexec/flua/libhash/lhash.c @@ -1,177 +1,181 @@ /*- * Copyright (c) 2024 Netflix, Inc * * SPDX-License-Identifier: BSD-2-Clause */ #include #include "lauxlib.h" #include "lhash.h" #include #include +#include "bootstrap.h" + #define SHA256_META "SHA256 meta table" #define SHA256_DIGEST_LEN 32 /* * Note C++ comments indicate the before -- after state of the stack, in with a * similar convention to forth's ( ) comments. Lua indexes are from 1 and can be * read left to right (leftmost is 1). Negative are relative to the end (-1 is * rightmost). A '.' indicates a return value left on the stack (all values to * its right). Trivial functions don't do this. */ /* * Updates the digest with the new data passed in. Takes 1 argument, which * is converted to a string. */ static int lua_sha256_update(lua_State *L) { size_t len; const unsigned char *data; SHA256_CTX *ctx; ctx = luaL_checkudata(L, 1, SHA256_META); data = luaL_checklstring(L, 2, &len); SHA256_Update(ctx, data, len); lua_settop(L, 1); return (1); } /* * Finalizes the digest value and returns it as a 32-byte binary string. The ctx * is zeroed. */ static int lua_sha256_digest(lua_State *L) { SHA256_CTX *ctx; unsigned char digest[SHA256_DIGEST_LEN]; ctx = luaL_checkudata(L, 1, SHA256_META); SHA256_Final(digest, ctx); lua_pushlstring(L, digest, sizeof(digest)); return (1); } /* * Finalizes the digest value and returns it as a 64-byte ascii string of hex * numbers. The ctx is zeroed. */ static int lua_sha256_hexdigest(lua_State *L) { SHA256_CTX *ctx; char buf[SHA256_DIGEST_LEN * 2 + 1]; unsigned char digest[SHA256_DIGEST_LEN]; static const char hex[]="0123456789abcdef"; int i; ctx = luaL_checkudata(L, 1, SHA256_META); SHA256_Final(digest, ctx); for (i = 0; i < SHA256_DIGEST_LEN; i++) { buf[i+i] = hex[digest[i] >> 4]; buf[i+i+1] = hex[digest[i] & 0x0f]; } buf[i+i] = '\0'; lua_pushstring(L, buf); return (1); } /* * Zeros out the ctx before garbage collection. Normally this is done in * obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua * manages freeing the ctx memory. */ static int lua_sha256_done(lua_State *L) { SHA256_CTX *ctx; ctx = luaL_checkudata(L, 1, SHA256_META); memset(ctx, 0, sizeof(*ctx)); return (0); } /* * Create object obj which accumulates the state of the sha256 digest * for its contents and any subsequent obj:update call. It takes zero * or 1 arguments. */ static int lua_sha256(lua_State *L) { SHA256_CTX *ctx; int top; /* We take 0 or 1 args */ top = lua_gettop(L); // data -- data if (top > 1) { lua_pushnil(L); return (1); } ctx = lua_newuserdata(L, sizeof(*ctx)); // data -- data ctx SHA256_Init(ctx); if (top == 1) { size_t len; const unsigned char *data; data = luaL_checklstring(L, 1, &len); SHA256_Update(ctx, data, len); } luaL_setmetatable(L, SHA256_META); // data ctx -- data ctx return (1); // data . ctx } /* * Setup the metatable to manage our userdata that we create in lua_sha256. We * request a finalization call with __gc so we can zero out the ctx buffer so * that we don't leak secrets if obj:digest or obj:hexdigest aren't called. */ static void register_metatable_sha256(lua_State *L) { luaL_newmetatable(L, SHA256_META); // -- meta lua_newtable(L); // meta -- meta tbl lua_pushcfunction(L, lua_sha256_update); // meta tbl -- meta tbl fn lua_setfield(L, -2, "update"); // meta tbl fn -- meta tbl lua_pushcfunction(L, lua_sha256_digest); // meta tbl -- meta tbl fn lua_setfield(L, -2, "digest"); // meta tbl fn -- meta tbl lua_pushcfunction(L, lua_sha256_hexdigest); // meta tbl -- meta tbl fn lua_setfield(L, -2, "hexdigest"); // meta tbl fn -- meta tbl /* Associate tbl with metatable */ lua_setfield(L, -2, "__index"); // meta tbl -- meta lua_pushcfunction(L, lua_sha256_done); // meta -- meta fn lua_setfield(L, -2, "__gc"); // meta fn -- meta lua_pop(L, 1); // meta -- } #define REG_SIMPLE(n) { #n, lua_ ## n } static const struct luaL_Reg hashlib[] = { REG_SIMPLE(sha256), { NULL, NULL }, }; #undef REG_SIMPLE int luaopen_hash(lua_State *L) { register_metatable_sha256(L); luaL_newlib(L, hashlib); return 1; } + +FLUA_MODULE(hash); diff --git a/libexec/flua/libjail/Makefile b/libexec/flua/libjail/Makefile index 20cd9f5f1429..b9c8bdc39095 100644 --- a/libexec/flua/libjail/Makefile +++ b/libexec/flua/libjail/Makefile @@ -1,9 +1,6 @@ SHLIB_NAME= jail.so -SRCS+= lua_jail.c - -LIBADD+= jail - MAN= jail.3lua +.include "Makefile.inc" .include diff --git a/libexec/flua/libjail/Makefile.inc b/libexec/flua/libjail/Makefile.inc new file mode 100644 index 000000000000..a896bf38c65b --- /dev/null +++ b/libexec/flua/libjail/Makefile.inc @@ -0,0 +1,3 @@ +.PATH: ${.PARSEDIR} +SRCS+= lua_jail.c +LIBADD+= jail diff --git a/libexec/flua/libjail/lua_jail.c b/libexec/flua/libjail/lua_jail.c index 9632db795775..8c3ec6c1d500 100644 --- a/libexec/flua/libjail/lua_jail.c +++ b/libexec/flua/libjail/lua_jail.c @@ -1,718 +1,722 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020, Ryan Moeller * Copyright (c) 2020, Kyle Evans * * 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 REGENTS 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 REGENTS 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 #include #include #include #include #include #include #include #include +#include "bootstrap.h" + #define JAIL_METATABLE "jail iterator metatable" /* * Taken from RhodiumToad's lspawn implementation, let static analyzers make * better decisions about the behavior after we raise an error. */ #if defined(LUA_VERSION_NUM) && defined(LUA_API) LUA_API int (lua_error) (lua_State *L) __dead2; #endif #if defined(LUA_ERRFILE) && defined(LUALIB_API) LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg) __dead2; LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname) __dead2; LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...) __dead2; #endif int luaopen_jail(lua_State *); typedef bool (*getparam_filter)(const char *, void *); static void getparam_table(lua_State *L, int paramindex, struct jailparam *params, size_t paramoff, size_t *params_countp, getparam_filter keyfilt, void *udata); struct l_jail_iter { struct jailparam *params; size_t params_count; int jid; }; static bool l_jail_filter(const char *param_name, void *data __unused) { /* * Allowing lastjid will mess up our iteration over all jails on the * system, as this is a special parameter that indicates where the search * starts from. We'll always add jid and name, so just silently remove * these. */ return (strcmp(param_name, "lastjid") != 0 && strcmp(param_name, "jid") != 0 && strcmp(param_name, "name") != 0); } static int l_jail_iter_next(lua_State *L) { struct l_jail_iter *iter, **iterp; struct jailparam *jp; int serrno; iterp = (struct l_jail_iter **)luaL_checkudata(L, 1, JAIL_METATABLE); iter = *iterp; luaL_argcheck(L, iter != NULL, 1, "closed jail iterator"); jp = iter->params; /* Populate lastjid; we must keep it in params[0] for our sake. */ if (jailparam_import_raw(&jp[0], &iter->jid, sizeof(iter->jid))) { jailparam_free(jp, iter->params_count); free(jp); free(iter); *iterp = NULL; return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg)); } /* The list of requested params was populated back in l_list(). */ iter->jid = jailparam_get(jp, iter->params_count, 0); if (iter->jid == -1) { /* * We probably got an ENOENT to signify the end of the jail * listing, but just in case we didn't; stash it off and start * cleaning up. We'll handle non-ENOENT errors later. */ serrno = errno; jailparam_free(jp, iter->params_count); free(iter->params); free(iter); *iterp = NULL; if (serrno != ENOENT) return (luaL_error(L, "jailparam_get: %s", strerror(serrno))); return (0); } /* * Finally, we'll fill in the return table with whatever parameters the * user requested, in addition to the ones we forced with exception to * lastjid. */ lua_newtable(L); for (size_t i = 0; i < iter->params_count; ++i) { char *value; jp = &iter->params[i]; if (strcmp(jp->jp_name, "lastjid") == 0) continue; value = jailparam_export(jp); lua_pushstring(L, value); lua_setfield(L, -2, jp->jp_name); free(value); } return (1); } static int l_jail_iter_close(lua_State *L) { struct l_jail_iter *iter, **iterp; /* * Since we're using this as the __gc method as well, there's a good * chance that it's already been cleaned up by iterating to the end of * the list. */ iterp = (struct l_jail_iter **)lua_touserdata(L, 1); iter = *iterp; if (iter == NULL) return (0); jailparam_free(iter->params, iter->params_count); free(iter->params); free(iter); *iterp = NULL; return (0); } static int l_list(lua_State *L) { struct l_jail_iter *iter; int nargs; nargs = lua_gettop(L); if (nargs >= 1) luaL_checktype(L, 1, LUA_TTABLE); iter = malloc(sizeof(*iter)); if (iter == NULL) return (luaL_error(L, "malloc: %s", strerror(errno))); /* * lastjid, jid, name + length of the table. This may be too much if * we have duplicated one of those fixed parameters. */ iter->params_count = 3 + (nargs != 0 ? lua_rawlen(L, 1) : 0); iter->params = malloc(iter->params_count * sizeof(*iter->params)); if (iter->params == NULL) { free(iter); return (luaL_error(L, "malloc params: %s", strerror(errno))); } /* The :next() method will populate lastjid before jail_getparam(). */ if (jailparam_init(&iter->params[0], "lastjid") == -1) { free(iter->params); free(iter); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } /* These two will get populated by jail_getparam(). */ if (jailparam_init(&iter->params[1], "jid") == -1) { jailparam_free(iter->params, 1); free(iter->params); free(iter); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } if (jailparam_init(&iter->params[2], "name") == -1) { jailparam_free(iter->params, 2); free(iter->params); free(iter); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } /* * We only need to process additional arguments if we were given any. * That is, we don't descend into getparam_table if we're passed nothing * or an empty table. */ iter->jid = 0; if (iter->params_count != 3) getparam_table(L, 1, iter->params, 2, &iter->params_count, l_jail_filter, NULL); /* * Part of the iterator magic. We give it an iterator function with a * metatable defining next() and close() that can be used for manual * iteration. iter->jid is how we track which jail we last iterated, to * be supplied as "lastjid". */ lua_pushcfunction(L, l_jail_iter_next); *(struct l_jail_iter **)lua_newuserdata(L, sizeof(struct l_jail_iter **)) = iter; luaL_getmetatable(L, JAIL_METATABLE); lua_setmetatable(L, -2); return (2); } static void register_jail_metatable(lua_State *L) { luaL_newmetatable(L, JAIL_METATABLE); lua_newtable(L); lua_pushcfunction(L, l_jail_iter_next); lua_setfield(L, -2, "next"); lua_pushcfunction(L, l_jail_iter_close); lua_setfield(L, -2, "close"); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, l_jail_iter_close); lua_setfield(L, -2, "__gc"); lua_pop(L, 1); } static int l_getid(lua_State *L) { const char *name; int jid; name = luaL_checkstring(L, 1); jid = jail_getid(name); if (jid == -1) { lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } lua_pushinteger(L, jid); return (1); } static int l_getname(lua_State *L) { char *name; int jid; jid = luaL_checkinteger(L, 1); name = jail_getname(jid); if (name == NULL) { lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } lua_pushstring(L, name); free(name); return (1); } static int l_allparams(lua_State *L) { struct jailparam *params; int params_count; params_count = jailparam_all(¶ms); if (params_count == -1) { lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } lua_newtable(L); for (int i = 0; i < params_count; ++i) { lua_pushstring(L, params[i].jp_name); lua_rawseti(L, -2, i + 1); } jailparam_free(params, params_count); free(params); return (1); } static void getparam_table(lua_State *L, int paramindex, struct jailparam *params, size_t params_off, size_t *params_countp, getparam_filter keyfilt, void *udata) { size_t params_count; int skipped; params_count = *params_countp; skipped = 0; for (size_t i = 1 + params_off; i < params_count; ++i) { const char *param_name; lua_rawgeti(L, -1, i - params_off); param_name = lua_tostring(L, -1); if (param_name == NULL) { jailparam_free(params, i - skipped); free(params); luaL_argerror(L, paramindex, "param names must be strings"); } lua_pop(L, 1); if (keyfilt != NULL && !keyfilt(param_name, udata)) { ++skipped; continue; } if (jailparam_init(¶ms[i - skipped], param_name) == -1) { jailparam_free(params, i - skipped); free(params); luaL_error(L, "jailparam_init: %s", jail_errmsg); } } *params_countp -= skipped; } struct getparams_filter_args { int filter_type; }; static bool l_getparams_filter(const char *param_name, void *udata) { struct getparams_filter_args *gpa; gpa = udata; /* Skip name or jid, whichever was given. */ if (gpa->filter_type == LUA_TSTRING) { if (strcmp(param_name, "name") == 0) return (false); } else /* type == LUA_TNUMBER */ { if (strcmp(param_name, "jid") == 0) return (false); } return (true); } static int l_getparams(lua_State *L) { const char *name; struct jailparam *params; size_t params_count; struct getparams_filter_args gpa; int flags, jid, type; type = lua_type(L, 1); luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1, "expected a jail name (string) or id (integer)"); luaL_checktype(L, 2, LUA_TTABLE); params_count = 1 + lua_rawlen(L, 2); flags = luaL_optinteger(L, 3, 0); params = malloc(params_count * sizeof(struct jailparam)); if (params == NULL) return (luaL_error(L, "malloc: %s", strerror(errno))); /* * Set the jail name or id param as determined by the first arg. */ if (type == LUA_TSTRING) { if (jailparam_init(¶ms[0], "name") == -1) { free(params); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } name = lua_tostring(L, 1); if (jailparam_import(¶ms[0], name) == -1) { jailparam_free(params, 1); free(params); return (luaL_error(L, "jailparam_import: %s", jail_errmsg)); } } else /* type == LUA_TNUMBER */ { if (jailparam_init(¶ms[0], "jid") == -1) { free(params); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } jid = lua_tointeger(L, 1); if (jailparam_import_raw(¶ms[0], &jid, sizeof(jid)) == -1) { jailparam_free(params, 1); free(params); return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg)); } } /* * Set the remaining param names being requested. */ gpa.filter_type = type; getparam_table(L, 2, params, 0, ¶ms_count, l_getparams_filter, &gpa); /* * Get the values and convert to a table. */ jid = jailparam_get(params, params_count, flags); if (jid == -1) { jailparam_free(params, params_count); free(params); lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } lua_pushinteger(L, jid); lua_newtable(L); for (size_t i = 0; i < params_count; ++i) { char *value; if (params[i].jp_flags & JP_KEYVALUE && params[i].jp_valuelen == 0) { /* Communicate back a missing key. */ lua_pushnil(L); } else { value = jailparam_export(¶ms[i]); lua_pushstring(L, value); free(value); } lua_setfield(L, -2, params[i].jp_name); } jailparam_free(params, params_count); free(params); return (2); } static int l_setparams(lua_State *L) { const char *name; struct jailparam *params; size_t params_count; int flags, jid, type; type = lua_type(L, 1); luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1, "expected a jail name (string) or id (integer)"); luaL_checktype(L, 2, LUA_TTABLE); lua_pushnil(L); for (params_count = 1; lua_next(L, 2) != 0; ++params_count) lua_pop(L, 1); flags = luaL_optinteger(L, 3, 0); params = malloc(params_count * sizeof(struct jailparam)); if (params == NULL) return (luaL_error(L, "malloc: %s", strerror(errno))); /* * Set the jail name or id param as determined by the first arg. */ if (type == LUA_TSTRING) { if (jailparam_init(¶ms[0], "name") == -1) { free(params); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } name = lua_tostring(L, 1); if (jailparam_import(¶ms[0], name) == -1) { jailparam_free(params, 1); free(params); return (luaL_error(L, "jailparam_import: %s", jail_errmsg)); } } else /* type == LUA_TNUMBER */ { if (jailparam_init(¶ms[0], "jid") == -1) { free(params); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } jid = lua_tointeger(L, 1); if (jailparam_import_raw(¶ms[0], &jid, sizeof(jid)) == -1) { jailparam_free(params, 1); free(params); return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg)); } } /* * Set the rest of the provided params. */ lua_pushnil(L); for (size_t i = 1; i < params_count && lua_next(L, 2) != 0; ++i) { const char *value; name = lua_tostring(L, -2); if (name == NULL) { jailparam_free(params, i); free(params); return (luaL_argerror(L, 2, "param names must be strings")); } if (jailparam_init(¶ms[i], name) == -1) { jailparam_free(params, i); free(params); return (luaL_error(L, "jailparam_init: %s", jail_errmsg)); } value = lua_tostring(L, -1); /* Allow passing NULL for key removal. */ if (value == NULL && !(params[i].jp_flags & JP_KEYVALUE)) { jailparam_free(params, i + 1); free(params); return (luaL_argerror(L, 2, "param values must be strings")); } if (jailparam_import(¶ms[i], value) == -1) { jailparam_free(params, i + 1); free(params); return (luaL_error(L, "jailparam_import: %s", jail_errmsg)); } lua_pop(L, 1); } /* * Attempt to set the params. */ jid = jailparam_set(params, params_count, flags); if (jid == -1) { jailparam_free(params, params_count); free(params); lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } lua_pushinteger(L, jid); jailparam_free(params, params_count); free(params); return (1); } static int l_attach(lua_State *L) { int jid, type; type = lua_type(L, 1); luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1, "expected a jail name (string) or id (integer)"); if (lua_isstring(L, 1)) { /* Resolve it to a jid. */ jid = jail_getid(lua_tostring(L, 1)); if (jid == -1) { lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } } else { jid = lua_tointeger(L, 1); } if (jail_attach(jid) == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return (2); } lua_pushboolean(L, 1); return (1); } static int l_remove(lua_State *L) { int jid, type; type = lua_type(L, 1); luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1, "expected a jail name (string) or id (integer)"); if (lua_isstring(L, 1)) { /* Resolve it to a jid. */ jid = jail_getid(lua_tostring(L, 1)); if (jid == -1) { lua_pushnil(L); lua_pushstring(L, jail_errmsg); return (2); } } else { jid = lua_tointeger(L, 1); } if (jail_remove(jid) == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return (2); } lua_pushboolean(L, 1); return (1); } static const struct luaL_Reg l_jail[] = { /** Get id of a jail by name. * @param name jail name (string) * @return jail id (integer) * or nil, error (string) on error */ {"getid", l_getid}, /** Get name of a jail by id. * @param jid jail id (integer) * @return jail name (string) * or nil, error (string) on error */ {"getname", l_getname}, /** Get a list of all known jail parameters. * @return list of jail parameter names (table of strings) * or nil, error (string) on error */ {"allparams", l_allparams}, /** Get the listed params for a given jail. * @param jail jail name (string) or id (integer) * @param params list of parameter names (table of strings) * @param flags optional flags (integer) * @return jid (integer), params (table of [string] = string) * or nil, error (string) on error */ {"getparams", l_getparams}, /** Set params for a given jail. * @param jail jail name (string) or id (integer) * @param params params and values (table of [string] = string) * @param flags optional flags (integer) * @return jid (integer) * or nil, error (string) on error */ {"setparams", l_setparams}, /** Get a list of jail parameters for running jails on the system. * @param params optional list of parameter names (table of * strings) * @return iterator (function), jail_obj (object) with next and * close methods */ {"list", l_list}, /** Attach to a running jail. * @param jail jail name (string) or id (integer) * @return true (boolean) * or nil, error (string) on error */ {"attach", l_attach}, /** Remove a running jail. * @param jail jail name (string) or id (integer) * @return true (boolean) * or nil, error (string) on error */ {"remove", l_remove}, {NULL, NULL} }; int luaopen_jail(lua_State *L) { lua_newtable(L); luaL_setfuncs(L, l_jail, 0); lua_pushinteger(L, JAIL_CREATE); lua_setfield(L, -2, "CREATE"); lua_pushinteger(L, JAIL_UPDATE); lua_setfield(L, -2, "UPDATE"); lua_pushinteger(L, JAIL_ATTACH); lua_setfield(L, -2, "ATTACH"); lua_pushinteger(L, JAIL_DYING); lua_setfield(L, -2, "DYING"); register_jail_metatable(L); return (1); } + +FLUA_MODULE(jail); diff --git a/libexec/flua/liblyaml/Makefile b/libexec/flua/liblyaml/Makefile index e7a89d09bb9e..8d1432acd325 100644 --- a/libexec/flua/liblyaml/Makefile +++ b/libexec/flua/liblyaml/Makefile @@ -1,22 +1,4 @@ SHLIB_NAME= yaml.so -WARNS= 1 -LYAMLSRC?= ${SRCTOP}/contrib/lyaml -.PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml -SRCS= emitter.c \ - parser.c \ - scanner.c \ - yaml.c -CFLAGS+= \ - -I${LYAMLSRC}/ext/yaml \ - -I${SRCTOP}/contrib/libyaml/include \ - -DVERSION=\"6.2.8\" -LIBADD+= yaml - -FILES= explicit.lua \ - functional.lua \ - implicit.lua \ - init.lua -FILESDIR= ${SHAREDIR}/flua/lyaml - +.include "Makefile.inc" .include diff --git a/libexec/flua/liblyaml/Makefile b/libexec/flua/liblyaml/Makefile.inc similarity index 71% copy from libexec/flua/liblyaml/Makefile copy to libexec/flua/liblyaml/Makefile.inc index e7a89d09bb9e..caa1f37b57eb 100644 --- a/libexec/flua/liblyaml/Makefile +++ b/libexec/flua/liblyaml/Makefile.inc @@ -1,22 +1,20 @@ -SHLIB_NAME= yaml.so - WARNS= 1 + LYAMLSRC?= ${SRCTOP}/contrib/lyaml .PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml -SRCS= emitter.c \ +SRCS+= emitter.c \ parser.c \ scanner.c \ yaml.c CFLAGS+= \ -I${LYAMLSRC}/ext/yaml \ -I${SRCTOP}/contrib/libyaml/include \ -DVERSION=\"6.2.8\" LIBADD+= yaml -FILES= explicit.lua \ +FILESGROUPS+= YAML +YAML= explicit.lua \ functional.lua \ implicit.lua \ init.lua -FILESDIR= ${SHAREDIR}/flua/lyaml - -.include +YAMLDIR= ${SHAREDIR}/flua/lyaml diff --git a/libexec/flua/libucl/Makefile b/libexec/flua/libucl/Makefile index a88c8bda6bfc..32d76d1ea1ad 100644 --- a/libexec/flua/libucl/Makefile +++ b/libexec/flua/libucl/Makefile @@ -1,14 +1,4 @@ SHLIB_NAME= ucl.so -WARNS= 2 - -UCLSRC?= ${SRCTOP}/contrib/libucl -.PATH: ${UCLSRC}/lua -SRCS+= lua_ucl.c -CFLAGS+= \ - -I${UCLSRC}/include \ - -I${UCLSRC}/src \ - -I${UCLSRC}/uthash -LIBADD+= ucl - +.include "Makefile.inc" .include diff --git a/libexec/flua/libucl/Makefile b/libexec/flua/libucl/Makefile.inc similarity index 64% copy from libexec/flua/libucl/Makefile copy to libexec/flua/libucl/Makefile.inc index a88c8bda6bfc..70fb0f265635 100644 --- a/libexec/flua/libucl/Makefile +++ b/libexec/flua/libucl/Makefile.inc @@ -1,14 +1,12 @@ -SHLIB_NAME= ucl.so - +.if ${WARNS:U6} > 2 WARNS= 2 +.endif -UCLSRC?= ${SRCTOP}/contrib/libucl +UCLSRC?= ${SRCTOP}/contrib/libucl .PATH: ${UCLSRC}/lua SRCS+= lua_ucl.c CFLAGS+= \ -I${UCLSRC}/include \ -I${UCLSRC}/src \ -I${UCLSRC}/uthash LIBADD+= ucl - -.include diff --git a/libexec/flua/linit_flua.c b/libexec/flua/linit_flua.c index b466b7872158..027f1b305d09 100644 --- a/libexec/flua/linit_flua.c +++ b/libexec/flua/linit_flua.c @@ -1,72 +1,99 @@ /* ** $Id: linit.c,v 1.39.1.1 2017/04/19 17:20:42 roberto Exp $ ** Initialization of libraries for lua.c and other clients ** See Copyright Notice in lua.h */ #define linit_c #define LUA_LIB /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a ** different set of libraries, copy this file to your project and edit ** it to suit your needs. ** ** You can also *preload* libraries, so that a later 'require' can ** open the library, which is already linked to the application. ** For that, do the following code: ** ** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); ** lua_pushcfunction(L, luaopen_modname); ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ #include "lprefix.h" - #include +#include #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "lfs.h" #include "lposix.h" #include "lfbsd.h" +#include "bootstrap.h" + /* ** these libs are loaded by lua.c and are readily available to any Lua ** program */ static const luaL_Reg loadedlibs[] = { {"_G", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, #if defined(LUA_COMPAT_BITLIB) {LUA_BITLIBNAME, luaopen_bit32}, #endif /* FreeBSD Extensions */ {"lfs", luaopen_lfs}, {"posix", luaopen_posix}, {"fbsd", luaopen_fbsd}, {NULL, NULL} }; +#ifdef BOOTSTRAPPING +static void __attribute__((constructor)) flua_init_env(void) { + /* + * This happens in the middle of luaopen_package(). We could move it into + * flua_setup_mods(), but it seems better to avoid its timing being so + * important that it would break some of our bootstrap modules if someone + * were to reorder things. + */ + if (getenv("LUA_PATH") == NULL) + setenv("LUA_PATH", BOOTSTRAP_FLUA_PATH, 1); +} + +static void flua_setup_mods (lua_State *L) { + const luaL_Reg **flib; + + SET_FOREACH(flib, flua_module_set) { + luaL_requiref(L, (*flib)->name, (*flib)->func, 1); + lua_pop(L, 1); /* remove lib */ + } +}; +#endif + LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } +#ifdef BOOTSTRAPPING + flua_setup_mods(L); +#endif } diff --git a/tools/build/Makefile b/tools/build/Makefile index 3c4e07e3cfc2..09351900599a 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -1,528 +1,529 @@ .PATH: ${.CURDIR}/../../include # XXX We need to include this to avoid installing MIT KRB5 includes on # XXX Heimdal systems. Remove the following line when Heimdal is finally # XXX removed. .include LIB= egacy SRC= INCSGROUPS= INCS SYSINCS CASPERINC UFSINCS FFSINCS MSDOSFSINCS DISKINCS INCSGROUPS+= MACHINESYSINCS RPCINCS .if ${MK_MITKRB5} != "no" INCSGROUPS+= EDITINC K5GSSRPCINC GSSAPIINC INCSGROUPS+= K5GSSAPIINC K52GSSAPIINC KRB5INC .endif INCS= SYSINCSDIR= ${INCLUDEDIR}/sys CASPERINCDIR= ${INCLUDEDIR}/casper # Also add ufs/ffs/msdosfs/disk headers to allow building makefs as a bootstrap tool UFSINCSDIR= ${INCLUDEDIR}/ufs/ufs FFSINCSDIR= ${INCLUDEDIR}/ufs/ffs MSDOSFSINCSDIR= ${INCLUDEDIR}/fs/msdosfs DISKINCSDIR= ${INCLUDEDIR}/sys/disk MACHINESYSINCSDIR= ${INCLUDEDIR}/machine RPCINCSDIR= ${INCLUDEDIR}/rpc .if ${MK_MITKRB5} != "no" EDITINCDIR= ${INCLUDEDIR}/edit/readline K5GSSRPCINCDIR= ${INCLUDEDIR}/gssrpc GSSAPIINCDIR= ${INCLUDEDIR}/gssapi K5GSSAPIINCDIR= ${INCLUDEDIR}/gssapi_krb5 K52GSSAPIINCDIR=${INCLUDEDIR}/gssapi_krb5/gssapi KRB5INCDIR= ${INCLUDEDIR}/krb5 KDB5INCDIR= ${INCLUDEDIR}/kdb5 KADM5INCDIR= ${INCLUDEDIR}/kadm5 .endif BOOTSTRAPPING?= 0 .if ${.MAKE.OS} == "Darwin" _XCODE_ROOT!=xcode-select -p # since macOS 10.14 C headers are no longer installed in /usr but only # provided via the SDK .if ${_XCODE_ROOT} == "/Library/Developer/CommandLineTools" # Only command line tools installed -> host headers are in the SDKs directory _MACOS_SDK_DIR=${_XCODE_ROOT}/SDKs/MacOSX.sdk/ .else # Full XCode installed -> host headers are below Platforms/MacOSX.platform _MACOS_SDK_DIR=${_XCODE_ROOT}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk .endif HOST_INCLUDE_ROOT=${_MACOS_SDK_DIR}/usr/include .if !exists(${HOST_INCLUDE_ROOT}/stdio.h) .error You must install the macOS SDK (try xcode-select --install) .endif .else HOST_INCLUDE_ROOT=/usr/include .endif # Allow building libc-internal files (also on non-FreeBSD hosts) CFLAGS+= -I${.CURDIR}/libc-bootstrap # Symbol versioning is not required for -legacy (and macOS bootstrap) MK_SYMVER= no _WITH_PWCACHEDB!= grep -c pwcache_groupdb ${HOST_INCLUDE_ROOT}/grp.h || true .if ${_WITH_PWCACHEDB} == 0 .PATH: ${.CURDIR}/../../contrib/libc-pwcache CFLAGS.pwcache.c+= -I${.CURDIR}/../../contrib/libc-pwcache SRCS+= pwcache.c .endif _WITH_STRSVIS!= grep -c strsvis ${HOST_INCLUDE_ROOT}/vis.h 2>/dev/null || true .if ${_WITH_STRSVIS} == 0 .PATH: ${.CURDIR}/../../contrib/libc-vis INCS+= vis.h SRCS+= vis.c unvis.c CFLAGS.vis.c+= -I${.CURDIR}/../../contrib/libc-vis -DHAVE_VIS=0 -DHAVE_SVIS=0 CFLAGS.unvis.c+= -I${.CURDIR}/../../contrib/libc-vis -DHAVE_VIS=0 -DHAVE_SVIS=0 .endif _WITH_REALLOCARRAY!= grep -c reallocarray ${HOST_INCLUDE_ROOT}/stdlib.h || true .if ${_WITH_REALLOCARRAY} == 0 .PATH: ${.CURDIR}/../../lib/libc/stdlib INCS+= stdlib.h SRCS+= reallocarray.c .endif .if exists(${HOST_INCLUDE_ROOT}/sys/stat.h) _WITH_UTIMENS!= grep -c utimensat ${HOST_INCLUDE_ROOT}/sys/stat.h || true .else _WITH_UTIMENS= 0 .endif .if ${_WITH_UTIMENS} == 0 SYSINCS+= stat.h SRCS+= futimens.c utimensat.c .endif _WITH_EXPLICIT_BZERO!= cat ${HOST_INCLUDE_ROOT}/strings.h ${HOST_INCLUDE_ROOT}/string.h | grep -c explicit_bzero || true .if ${_WITH_EXPLICIT_BZERO} == 0 # .PATH: ${SRCTOP}/sys/libkern # Adding sys/libkern to .PATH breaks building the cross-build compat library # since that attempts to build strlcpy.c from libc and adding libkern here will # cause it to pick the file from libkern instead (which won't compile). # Avoid modifying .PATH by creating a copy in the build directory instead. explicit_bzero.c: ${SRCTOP}/sys/libkern/explicit_bzero.c cp ${.ALLSRC} ${.TARGET} CLEANFILES+= explicit_bzero.c INCS+= strings.h SRCS+= explicit_bzero.c .endif _WITH_FSPACECTL!= grep -c fspacectl ${HOST_INCLUDE_ROOT}/fcntl.h || true .if ${_WITH_FSPACECTL} == 0 INCS+= fcntl.h SRCS+= fspacectl.c .endif .if exists(${HOST_INCLUDE_ROOT}/capsicum_helpers.h) _WITH_CAPH_ENTER!= grep -c caph_enter ${HOST_INCLUDE_ROOT}/capsicum_helpers.h || true _WITH_CAPH_RIGHTS_LIMIT!= grep -c caph_rights_limit ${HOST_INCLUDE_ROOT}/capsicum_helpers.h || true .endif .if !defined(_WITH_CAPH_ENTER) || ${_WITH_CAPH_ENTER} == 0 || ${_WITH_CAPH_RIGHTS_LIMIT} == 0 .PATH: ${SRCTOP}/lib/libcapsicum INCS+= capsicum_helpers.h .PATH: ${SRCTOP}/lib/libcasper/libcasper INCS+= libcasper.h .endif # rpcgen should build against the source tree rpc/types.h and not the host. # This is especially important on non-FreeBSD systems where the types may # not match. RPCINCS+= ${SRCTOP}/sys/rpc/types.h INCS+= ${SRCTOP}/include/mpool.h INCS+= ${SRCTOP}/include/ndbm.h INCS+= ${SRCTOP}/include/err.h INCS+= ${SRCTOP}/include/stringlist.h # Needed to build arc4random.c INCSGROUPS+= CHACHA20INCS CHACHA20INCSDIR= ${INCLUDEDIR}/crypto/chacha20 CHACHA20INCS+= ${SRCTOP}/sys/crypto/chacha20/_chacha.h \ ${SRCTOP}/sys/crypto/chacha20/chacha.h .if ${MACHINE} == "host" _host_arch= ${_HOST_ARCH} .elif ${MACHINE} == "host32" _host_arch= ${_HOST_ARCH32} .else _host_arch=${MACHINE} .endif .if ${_host_arch} == "x86_64" # bmake on Linux/mac often prints that instead of amd64 _host_arch=amd64 .elif ${_host_arch} == "aarch64" # Linux calls arm64, aarch64, across the board _host_arch=arm64 .elif ${_host_arch:Mppc*} _host_arch=powerpc .endif .if ${_host_arch} == "unknown" # HACK: If MACHINE is unknown, assume we are building on x86 _host_arch=amd64 .endif MACHINESYSINCS+= ${SRCTOP}/sys/${_host_arch}/include/elf.h .if ${_host_arch} == "amd64" || ${_host_arch} == "i386" INCSGROUPS+= X86INCS X86INCSDIR= ${INCLUDEDIR}/x86 X86INCS+= ${SRCTOP}/sys/x86/include/elf.h .endif # needed for btxld: MACHINESYSINCS+= ${SRCTOP}/sys/${_host_arch}/include/exec.h MACHINESYSINCS+= ${SRCTOP}/sys/${_host_arch}/include/reloc.h INCS+= ${SRCTOP}/include/a.out.h INCS+= ${SRCTOP}/include/nlist.h SYSINCS+= ${SRCTOP}/sys/sys/imgact_aout.h SYSINCS+= ${SRCTOP}/sys/sys/nlist_aout.h # macOS's bitstring lacks FreeBSD-specific additions used by makefs's ZFS code # and Linux doesn't have it at all. Older FreeBSD versions lack recent # additions. INCS+= ${SRCTOP}/include/bitstring.h SYSINCS+= ${SRCTOP}/sys/sys/bitstring.h .if ${.MAKE.OS} != "FreeBSD" .PATH: ${.CURDIR}/cross-build # Needed by our sys/types.h wrapper SYSINCS+= ${SRCTOP}/sys/sys/bitcount.h # dbopen() behaves differently on Linux and FreeBSD so we ensure that we # bootstrap the FreeBSD db code. The cross-build headers #define dbopen() to # __freebsd_dbopen() so that we don't ever use the host version INCS+= ${SRCTOP}/include/db.h LIBC_SRCTOP= ${SRCTOP}/lib/libc/ .include "${LIBC_SRCTOP}/db/Makefile.inc" # Do the same as we did for dbopen() for getopt() on since it's not compatible # on Linux (and to avoid surprises also compile the FreeBSD code on macOS) .PATH: ${LIBC_SRCTOP}/stdlib SRCS+= getopt.c getopt_long.c INCS+= ${SRCTOP}/include/getopt.h # getcap.c is needed for cap_mkdb: .PATH: ${LIBC_SRCTOP}/gen SRCS+= getcap.c # Glibc does not provide all err*/warn* functions, and for macOS we need the # alias with the extra underscore. SRCS+= err.c # Add various libbc functions that are not available in glibc: SRCS+= stringlist.c setmode.c SRCS+= strtonum.c merge.c heapsort.c reallocf.c .PATH: ${LIBC_SRCTOP}/locale SRCS+= rpmatch.c .if ${.MAKE.OS} == "Linux" # On Linux, glibc does not provide strmode. It only provides strlcpy # and strlcat from glibc 2.38. .PATH: ${LIBC_SRCTOP}/string SRCS+= strmode.c # Assume if strlcpy exists so does strlcat _WITH_EXPLICIT_STRLCPY!= cat ${HOST_INCLUDE_ROOT}/strings.h ${HOST_INCLUDE_ROOT}/string.h | grep -c strlcpy || true .if ${_WITH_EXPLICIT_STRLCPY} == 0 SRCS+= strlcpy.c strlcat.c .endif # On Linux, glibc provides ffs* but not fls* SRCS+= fls.c flsl.c flsll.c # Compile the fgetln/fgetwln/closefrom fallback code from libbsd: SRCS+= fgetln_fallback.c fgetwln_fallback.c closefrom.c CFLAGS.closefrom.c+= -DSTDC_HEADERS -DHAVE_SYS_DIR_H -DHAVE_DIRENT_H \ -DHAVE_DIRFD -DHAVE_SYSCONF # Provide getprogname/setprograme SRCS+= progname.c # Provide fflagstostr/strtofflags for mtree and makefs # On macOS we use the host's so conflate host and target flags, which ideally # we'd avoid, but in practice these align for many flags, including # SF_IMMUTABLE, the only flag we currently set during install. SRCS+= strtofflags.c # macOS has a standalone cross-build implementation, but Linux can use the same # ELF one as FreeBSD SYSINCS+= ${SRCTOP}/sys/sys/linker_set.h .endif # ${MAKE.OS} == "Linux" .if ${.MAKE.OS} == "Darwin" # Standalone implementation of secure_getenv(), not available on MacOS. SRCS+= secure_getenv.c .endif # ${MAKE.OS} == "Darwin" # Provide the same arc4random implementation on Linux/macOS CFLAGS.arc4random.c+= -I${SRCTOP}/sys/crypto/chacha20 -D__isthreaded=1 SRCS+= arc4random.c arc4random_uniform.c # expand_number() is not provided by either Linux or MacOS libutil .PATH: ${SRCTOP}/lib/libutil SRCS+= expand_number.c # Linux libutil also doesn't have fparseln SRCS+= fparseln.c # A dummy sysctl for tzsetup: SRCS+= fake_sysctl.c # capsicum support SYSINCS+= ${SRCTOP}/sys/sys/capsicum.h SYSINCS+= ${SRCTOP}/sys/sys/caprights.h SRCS+= capsicum_stubs.c # XXX: we can't add ${SRCTOP}/sys/kern to .PATH since that will causes # conflicts with other files. Instead copy subr_capability to the build dir. subr_capability.c: ${SRCTOP}/sys/kern/subr_capability.c cp ${.ALLSRC} ${.TARGET} SRCS+= subr_capability.c CLEANFILES+= subr_capability.c # Headers needed for msdosfs use in makefs SYSINCS+= ${SRCTOP}/sys/sys/_callout.h SYSINCS+= ${SRCTOP}/sys/sys/_lock.h SYSINCS+= ${SRCTOP}/sys/sys/_lockmgr.h SYSINCS+= ${SRCTOP}/sys/sys/_task.h .endif # ${MAKE.OS} != "FreeBSD" CASPERINC+= ${SRCTOP}/lib/libcasper/services/cap_fileargs/cap_fileargs.h CASPERINC+= ${SRCTOP}/lib/libcasper/services/cap_net/cap_net.h .if ${MK_MITKRB5} != "no" EDITINC+= ${SRCTOP}/contrib/libedit/readline/readline.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/auth.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/auth_gss.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/auth_gssapi.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/auth_unix.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/clnt.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/netdb.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/pmap_clnt.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/pmap_prot.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/pmap_rmt.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/rpc_msg.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/rpc.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/svc_auth.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/svc.h K5GSSRPCINC+= ${SRCTOP}/krb5/include/gssrpc/types.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/xdr.h K5GSSRPCINC+= ${SRCTOP}/crypto/krb5/src/include/gssrpc/xdr.h GSSAPIINC+= ${SRCTOP}/crypto/krb5/src/lib/gssapi/krb5/gssapi_krb5.h GSSAPIINC+= ${SRCTOP}/crypto/krb5/src/lib/gssapi/generic/gssapi_alloc.h GSSAPIINC+= ${SRCTOP}/crypto/krb5/src/lib/gssapi/generic/gssapi_generic.h GSSAPIINC+= ${SRCTOP}/crypto/krb5/src/lib/gssapi/generic/gssapi_ext.h K5GSSAPIINC+= ${SRCTOP}/crypto/krb5/src/include/gssapi.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/ccselect_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/certauth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/clpreauth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/hostrealm_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/kadm5_auth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/kadm5_hook_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/kdcpolicy_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/kdcpreauth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/localauth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/locate_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/preauth_plugin.h KRB5INC+= ${SRCTOP}/crypto/krb5/src/include/krb5/pwqual_plugin.h INCS+= ${SRCTOP}/crypto/krb5/src/include/gssapi.h INCS+= ${SRCTOP}/crypto/krb5/src/include/kdb.h INCS+= ${SRCTOP}/crypto/krb5/src/include/krb5.h INCS+= ${SRCTOP}/crypto/krb5/src/include/krad.h INCS+= ${SRCTOP}/crypto/krb5/src/util/et/com_err.h INCS+= ${SRCTOP}/crypto/krb5/src/util/verto/verto-module.h INCS+= ${SRCTOP}/crypto/krb5/src/util/verto/verto.h .endif .if empty(SRCS) SRCS= dummy.c .endif .if defined(CROSS_BUILD_TESTING) SUBDIR= cross-build .endif # To allow bootstrapping makefs on FreeBSD 11 or non-FreeBSD systems: UFSINCS+= ${SRCTOP}/sys/ufs/ufs/dinode.h UFSINCS+= ${SRCTOP}/sys/ufs/ufs/dir.h FFSINCS+= ${SRCTOP}/sys/ufs/ffs/fs.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/bootsect.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/bpb.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/denode.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/direntry.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/fat.h MSDOSFSINCS+= ${SRCTOP}/sys/fs/msdosfs/msdosfsmount.h DISKINCS+= ${SRCTOP}/sys/sys/disk/bsd.h # Needed to build config (since it uses libnv) SYSINCS+= ${SRCTOP}/sys/sys/_nv.h SYSINCS+= ${SRCTOP}/sys/sys/nv.h ${SRCTOP}/sys/sys/cnv.h \ ${SRCTOP}/sys/sys/dnv.h ${SRCTOP}/sys/sys/nv_namespace.h # Needed when bootstrapping ldd (since it uses DF_1_PIE) SYSINCS+= ${SRCTOP}/sys/sys/elf32.h SYSINCS+= ${SRCTOP}/sys/sys/elf64.h SYSINCS+= ${SRCTOP}/sys/sys/elf_common.h SYSINCS+= ${SRCTOP}/sys/sys/elf_generic.h SYSINCS+= ${SRCTOP}/sys/sys/queue.h SYSINCS+= ${SRCTOP}/sys/sys/md5.h SYSINCS+= ${SRCTOP}/sys/sys/sbuf.h SYSINCS+= ${SRCTOP}/sys/sys/tree.h # vtfontcvt is using sys/font.h SYSINCS+= ${SRCTOP}/sys/sys/font.h # For mkscrfil.c: SYSINCS+= ${SRCTOP}/sys/sys/consio.h # for gencat: INCS+= ${SRCTOP}/include/nl_types.h # for vtfontcvt: SYSINCS+= ${SRCTOP}/sys/sys/fnv_hash.h # opensolaris compatibility INCS+= ${SRCTOP}/include/elf.h SYSINCS+= ${SRCTOP}/sys/sys/elf.h SYSINCS+= ${SRCTOP}/sys/sys/ctf.h # for kbdcontrol: SYSINCS+= ${SRCTOP}/sys/sys/kbio.h # for kldxref: SYSINCS+= ${SRCTOP}/sys/sys/module.h .if ${.MAKE.OS} != "FreeBSD" # for libmd: SYSINCS+= ${SRCTOP}/sys/sys/md4.h .endif # We want to run the build with only ${WORLDTMP} in $PATH to ensure we don't # accidentally run tools that are incompatible but happen to be in $PATH. # This is especially important when building on Linux/MacOS where many of the # programs used during the build accept different flags or generate different # output. On those platforms we only symlink the tools known to be compatible # (e.g. basic utilities such as mkdir) into ${WORLDTMP} and build all others # from the FreeBSD sources during the bootstrap-tools stage. # basic commands: It is fine to use the host version for all of these even on # Linux/MacOS since we only use flags that are supported by all of them. _host_tools_to_symlink= basename bzip2 bunzip2 chmod chown cmp comm cp date dd \ dirname echo env false find fmt gzip gunzip head hostname id ln ls \ mkdir mv nice patch rm sh sleep stat tee time touch tr true uname uniq \ unxz wc which xz # We also need a symlink to the absolute path to the make binary used for # the toplevel makefile. This is not necessarily the same as `which make` # since e.g. on Linux and MacOS that will be GNU make. _make_abs!= which "${MAKE}" _host_abs_tools_to_symlink= ${_make_abs}:make ${_make_abs}:bmake _LINK_HOST_TOOL= ln -sfn .if ${.MAKE.OS} == "FreeBSD" # When building on FreeBSD we always copy the host tools instead of linking # into WORLDTMP to avoid issues with incompatible libraries (see r364030). # Note: we could create links if we don't intend to update the current machine. _COPY_HOST_TOOL= cp -pf .else # However, this is not necessary on Linux/macOS. Additionally, copying the host # tools to another directory with cp -p results in freezes on macOS Big Sur for # some unknown reason. It can also break building inside docker containers if # there are ACLs on shared volumes. _COPY_HOST_TOOL= ${_LINK_HOST_TOOL} .if ${.MAKE.OS} == "Darwin" # /usr/bin/cpp may invoke xcrun: _host_tools_to_symlink+= xcrun .endif # ${.MAKE.OS} == "Darwin" # On Ubuntu /bin/sh is dash which is totally useless, and the same for modern # macOS. Let's just link bash as the build sh since that will work fine. _host_abs_tools_to_symlink+= /bin/bash:sh _host_tools_to_symlink:= ${_host_tools_to_symlink:Nsh} .endif # We also need to symlink any non-absolute toolchain commands. Clang finds its # resource directory relative to itself, so CC/CXX/CPP cannot be copied, and # there should be no concerns about installing over the current system since we # don't use the toolchain during install, so that's not an issue. However, # before Clang 13 there was no symlink detection for FreeBSD so that was broken # in the same way as copying (https://reviews.llvm.org/D103346), thus create a # wrapper script for each to work around this and behave like a symlink. # Remove this hack and just use a symlink once Clang 13 can be assumed. # For consistency, we use the same strategy for LD. .include .if ${.MAKE.OS} == "FreeBSD" && ${COMPILER_TYPE} == "clang" && \ ${COMPILER_VERSION} < 130000 _WRAP_HOST_TOOL= sh -c "printf '\#!/bin/sh\nexec \"\%s\" \"\$$@\"\n' \"\$$0\" > \"\$$1\" && chmod +x \"\$$1\"" .else _WRAP_HOST_TOOL= ${_LINK_HOST_TOOL} .endif .for var in CC CXX CPP LD .for X in $${_empty_var_} X .if !empty(${X}${var}) && !${${X}${var}:[1]:M/*} && \ !${_toolchain_tools_to_symlink:U:M${${X}${var}:[1]}} _toolchain_tools_to_symlink+= ${${X}${var}:[1]} .endif .endfor .endfor host-symlinks: @echo "Linking host tools into ${DESTDIR}/bin" .for _tool in ${_host_tools_to_symlink} @export PATH=$${PATH}:/usr/local/bin; \ source_path=`which ${_tool} || echo /dev/null/no/such`; \ if [ ! -e "$${source_path}" ] ; then \ echo "Cannot find host tool '${_tool}' in PATH ($$PATH)." >&2; false; \ fi; \ rm -f "${DESTDIR}/bin/${_tool}"; \ ${_COPY_HOST_TOOL} "$${source_path}" "${DESTDIR}/bin/${_tool}" .endfor .for _tool in ${_host_abs_tools_to_symlink} @source_path="${_tool:S/:/ /:[1]}"; \ target_path="${DESTDIR}/bin/${_tool:S/:/ /:[2]}"; \ if [ ! -e "$${source_path}" ] ; then \ echo "Host tool '$${source_path}' is missing"; false; \ fi; \ rm -f "$${target_path}"; \ ${_COPY_HOST_TOOL} "$${source_path}" "$${target_path}" .endfor .if exists(/usr/libexec/flua) rm -f ${DESTDIR}/usr/libexec/flua ${_COPY_HOST_TOOL} /usr/libexec/flua ${DESTDIR}/usr/libexec/flua .endif .for _tool in ${_toolchain_tools_to_symlink} @export PATH=$${PATH}:/usr/local/bin; \ source_path=`which ${_tool} || echo /dev/null/no/such`; \ if [ ! -e "$${source_path}" ] ; then \ echo "Cannot find host tool '${_tool}' in PATH ($$PATH)." >&2; false; \ fi; \ rm -f "${DESTDIR}/bin/${_tool}"; \ ${_WRAP_HOST_TOOL} "$${source_path}" "${DESTDIR}/bin/${_tool}" .endfor # Create all the directories that are needed during the legacy, bootstrap-tools # and cross-tools stages. We do this here using mkdir since mtree may not exist # yet (this happens if we are crossbuilding from Linux/Mac). INSTALLDIR_LIST= \ bin \ lib/geom \ usr/include/casper \ usr/include/openssl \ usr/include/private/ucl \ + usr/include/private/yaml \ usr/include/private/zstd \ usr/lib \ usr/libdata/pkgconfig \ usr/libexec installdirs: mkdir -p ${INSTALLDIR_LIST:S,^,${DESTDIR}/,} # Link usr/bin, sbin, and usr/sbin to bin so that it doesn't matter whether a # bootstrap tool was added to WORLTMP with a symlink or by building it in the # bootstrap-tools phase. We could also overrride BINDIR when building bootstrap # tools but adding the symlinks is easier and means all tools are also # in the directory that they are installed to normally. .for _dir in sbin usr/sbin usr/bin # delete existing directories from before r340157 @if [ -e ${DESTDIR}/${_dir} ] && [ ! -L ${DESTDIR}/${_dir} ]; then \ echo "removing old non-symlink ${DESTDIR}/${_dir}"; \ rm -rf "${DESTDIR}/${_dir}"; \ fi .endfor ln -sfn bin ${DESTDIR}/sbin ln -sfn ../bin ${DESTDIR}/usr/bin ln -sfn ../bin ${DESTDIR}/usr/sbin .for _group in ${INCSGROUPS:NINCS} mkdir -p "${DESTDIR}/${${_group}DIR}" .endfor .include