/* *crun - OCI runtime written in C * *Copyright (C) Rubicon Rowe *crun is free software; you can redistribute it and/or modify *it under the terms of the GNU Lesser General Public License as published by *the Free Software Foundation; either version 2.1 of the License, or *(at your option) any later version. * *crun is distributed in the hope that it will be useful, *but WITHOUT ANY WARRANTY; without even the implied warranty of *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *GNU Lesser General Public License for more details. * *You should have received a copy of the GNU Lesser General Public License *along with crun. If not, see . */ /* This library is a bare libcrun interface and can be further wrapped by other libraries. * * There are some problems still and the API is a subject to change. */ #include #include #include #include #include #include #include #include #include #include static const char *LUA_CRUN_TAG_CTX = "crun-ctx"; static const char *LUA_CRUN_TAG_CONT = "crun-container"; static const char *LUA_CRUN_TAG_CONTS_ITER = "crun-containers-iterator"; #define luacrunL_optboolean(L, n, d) luaL_opt (S, lua_toboolean, n, d) // Soft error = return an error. // When `expr` is false, run `onfailed` and push the string from `crun_err`. // Return `addret + 1`. #define luacrun_SoftErrIf(S, expr, crun_err, onfailed, addret) \ if (expr) \ { \ onfailed; \ return luacrun_error (S, crun_err) + addret; \ } extern int lua_error (lua_State *L); extern int luaL_error (lua_State *L, const char *fmt, ...); /* Build the error string, push onto stack. */ LUA_API int luacrun_error (lua_State *S, libcrun_error_t *err) { luaL_checkstack (S, 1, NULL); if (*err == NULL) { lua_pushstring (S, "the error is NULL, this may be a bug in luacrun"); return 1; } if ((*err)->status == 0) { lua_pushfstring (S, "crun: %s", (*err)->msg); } else { lua_pushfstring (S, "crun: %s(%s)", (*err)->msg, strerror ((*err)->status)); } libcrun_error_release (err); return 1; } LUA_API void luacrun_set_error (lua_State *S, libcrun_error_t *err) { luacrun_error (S, err); lua_error (S); } /* This is a custom version of `xstrdup`(in src/libcrun/utils.h), return a Lua userdata. * Push the userdata onto stack, or nil if `s` is `NULL`. * This function does not check stack. * -0, +1, - */ static char * luacrun_xstrdup (lua_State *S, const char *s) { if (s != NULL) { size_t size = strlen (s) + 1; char *ret = lua_newuserdata (S, size); /* `lua_newuserdatauv` always returns a valid address, no need to check if the allocation is success */ memcpy (ret, s, size); return ret; } else { lua_pushnil (S); return NULL; } } struct luacrun_args_holder { const char **argv; int argc; }; /* uservalues used by the ctx, the index + 1 is the uservalue idx. the definition here is not stable. */ const char *luacrun_ctx_uservalues[] = { "state_root", "id", "bundle", "console_socket", "pid_file", "notify_socket", "handler", "args", // argv and argc }; #define luacrun_CtxSetupStringField(S, ret, ctxidx, tabidx, field_name, name, uvalidx) \ ret = lua_getfield (S, tabidx, field_name); \ if (ret == LUA_TSTRING) \ { \ ctx->name = luacrun_xstrdup (S, lua_tostring (S, -1)); \ } \ else if (ret == LUA_TNIL) \ { \ lua_pushnil (S); \ ctx->name = NULL; \ } \ else \ { \ lua_pop (S, 1); \ luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), field_name); \ } \ lua_setiuservalue (S, ctxidx, uvalidx); \ lua_pop (S, 1) #define luacrun_CtxSetupBoolField(S, ret, ctxidx, tabidx, field_name, name) \ ret = lua_getfield (S, tab_idx, field_name); \ if (ret == LUA_TBOOLEAN) \ { \ ctx->name = lua_toboolean (S, -1); \ } \ else if (ret != LUA_TNIL) \ { \ lua_pop (S, 1); \ luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), field_name); \ } \ lua_pop (S, 1) /* Setup context_t by a table. The table must be the stack top. [-0, +0] */ static void luacrun_ctx_setup (lua_State *S, int ctxidx, int tab_idx) { libcrun_context_t *ctx = lua_touserdata (S, ctxidx); int ret; luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "state_root", state_root, 1); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "id", id, 2); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "bundle", bundle, 3); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "console_socket", console_socket, 4); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "pid_file", pid_file, 5); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "notify_socket", notify_socket, 6); luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "handler", handler, 7); luacrun_CtxSetupBoolField (S, ret, ctxidx, tabidx, "systemd_cgroup", systemd_cgroup); luacrun_CtxSetupBoolField (S, ret, ctxidx, tabidx, "detach", detach); ret = lua_getfield (S, tab_idx, "args"); if (ret == LUA_TTABLE) { lua_Integer length = luaL_len (S, -1); if (length < 0 || length > (INT_MAX - 1)) { /* A userdata can have INT_MAX uservalues */ luaL_error (S, "field \"args\": length should be <= %d and > 0", INT_MAX - 1); } int argc = (int) length; const char **argv = lua_newuserdatauv (S, sizeof (char *) * argc, argc); int argv_idx = lua_gettop (S); for (int i = argc; i > 0; i--) { lua_geti (S, tab_idx, i); const char *arg = luaL_tolstring (S, -1, NULL); if (arg != NULL) { const char *copy = luacrun_xstrdup (S, arg); argv[i] = copy; lua_setiuservalue (S, argv_idx, i); lua_pop (S, 2); /* pop arg and result from lua_geti */ } else { luaL_error (S, "field \"args\": failed to convert value (index %d) to string", i); } } /* Stack top: argv */ struct luacrun_args_holder *args = lua_newuserdatauv (S, sizeof (struct luacrun_args_holder), 1); int args_idx = lua_gettop (S); args->argc = argc; args->argv = argv; lua_pushvalue (S, argv_idx); lua_setiuservalue (S, args_idx, 1); /* Stack top: args */ lua_setiuservalue (S, ctxidx, 8); /* -1 */ lua_pop (S, 1); } else if (ret != LUA_TNIL) { lua_pop (S, 1); luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), "args"); } lua_pop (S, 1); } /* Create a crun context. */ LUA_API int luacrun_new_ctx (lua_State *S) { if (! (lua_isnil (S, 1) || lua_istable (S, 1))) { luaL_typeerror (S, 1, "table or nil"); } luaL_checkstack (S, 1, NULL); /* Lua does not guarantee that string addresses will be valid for the lifetime of libcrun_context_t, * but we must respect the memory management function set by the user in lua_State. * (For the string memory guarantees: https://www.lua.org/manual/5.4/manual.html#4.1.3) * * - Use userdata as string to ensure it will not be moved. * ("Lua ensures that this address is valid as long as the corresponding userdata is alive": * https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv) * - Use uservalue to prevent being collected by GC. * */ libcrun_context_t *ctx = lua_newuserdatauv (S, sizeof (libcrun_context_t), 8); int ctx_idx = lua_gettop (S); memset (ctx, 0, sizeof (libcrun_context_t)); ctx->fifo_exec_wait_fd = -1; if (lua_istable (S, 1)) { luacrun_ctx_setup (S, ctx_idx, 1); } else if (!lua_isnoneornil(S, 1)) { luaL_argerror(S, 1, "expect table, nil or none"); } luaL_setmetatable (S, LUA_CRUN_TAG_CTX); return 1; } /*Grab a basic container spec.*/ LUA_API int luacrun_container_spec (lua_State *S) { bool rootless = luacrunL_optboolean (S, 1, true); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 1, NULL); char buf[4096] = {}; FILE *memfile = fmemopen (buf, 4095, "w"); int ret = libcrun_container_spec (rootless, memfile, &crun_err); fclose (memfile); luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushnil (S), 1); lua_pushlstring (S, buf, ret); return 1; } LUA_API int luacrun_new_container_from_string (lua_State *S) { libcrun_error_t crun_err = NULL; const char *def = luaL_checkstring (S, 1); libcrun_container_t **cont = lua_newuserdata (S, sizeof (libcrun_container_t *)); luaL_setmetatable (S, LUA_CRUN_TAG_CONT); *cont = libcrun_container_load_from_memory (def, &crun_err); if (*cont == NULL) { lua_pushnil (S); return luacrun_error (S, &crun_err) + 1; } return 1; } LUA_API int luacrun_new_container_from_file (lua_State *S) { libcrun_error_t crun_err = NULL; const char *path = luaL_checkstring (S, 1); libcrun_container_t **cont = lua_newuserdata (S, sizeof (libcrun_container_t *)); luaL_setmetatable (S, LUA_CRUN_TAG_CONT); // create the userdata before calling crun, so we don't need to clean up when Lua failed *cont = libcrun_container_load_from_file (path, &crun_err); if (*cont == NULL) { lua_pushnil (S); return luacrun_error (S, &crun_err) + 1; } return 1; } /*Release resource linked with container userdata. Double use is supported.*/ LUA_API int luacrun_container_finalizer (lua_State *S) { libcrun_container_t **cont = luaL_checkudata (S, 1, LUA_CRUN_TAG_CONT); if (*cont != NULL) { free_runtime_spec_schema_config_schema ((*cont)->container_def); *cont = NULL; } return 0; } LUA_API int luacrun_set_verbosity (lua_State *S) { lua_Integer verbosity = luaL_checkinteger (S, 1); if (verbosity >= INT_MIN && verbosity <= INT_MAX) { libcrun_set_verbosity (verbosity); } else { luaL_error (S, "verbosity should be >= %d and <= %d", INT_MIN, INT_MAX); } return 0; } LUA_API int luacrun_get_verbosity (lua_State *S) { int verbosity = libcrun_get_verbosity (); lua_pushinteger (S, verbosity); return 1; } static unsigned int luacrun_build_run_flags (lua_State *S, int idx) { luaL_checktype (S, idx, LUA_TTABLE); lua_getfield (S, idx, "prefork"); bool prefork = lua_toboolean (S, -1); lua_pop (S, 1); return (prefork ? LIBCRUN_RUN_OPTIONS_PREFORK : 0) | 0; } LUA_API int luacrun_ctx_run (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); libcrun_container_t **cont = luaL_checkudata (S, 2, LUA_CRUN_TAG_CONT); unsigned int flags = luaL_opt (S, luacrun_build_run_flags, 3, 0); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 1, NULL); int ret = libcrun_container_run (ctx, *cont, flags, &crun_err); if (ret < 0) { if (crun_err != NULL) { lua_pushnil (S); return luacrun_error (S, &crun_err) + 1; } else { luaL_error(S, "failed to run container"); } } else { lua_pushinteger (S, ret); return 1; } } LUA_API int luacrun_ctx_create_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); libcrun_container_t **cont = luaL_checkudata (S, 2, LUA_CRUN_TAG_CONT); unsigned int flags = luaL_opt (S, luacrun_build_run_flags, 3, LIBCRUN_RUN_OPTIONS_PREFORK); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 1, NULL); int ret = libcrun_container_create (ctx, *cont, flags, &crun_err); if (ret < 0) { lua_pushnil (S); return luacrun_error (S, &crun_err) + 1; } else { lua_pushinteger (S, ret); return 1; } } LUA_API int luacrun_ctx_delete_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); const char *id = luaL_checkstring (S, 2); bool force = luaL_opt (S, lua_toboolean, 3, false); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 1, NULL); int ret = libcrun_container_delete (ctx, NULL, id, force, &crun_err); bool has_error = ret < 0 && crun_err != NULL; luacrun_SoftErrIf (S, has_error, &crun_err, lua_pushboolean (S, false), 1); lua_pushboolean (S, ret >= 0); // false if failed to read the exec.fifo in the state dir return 1; } LUA_API int luacrun_ctx_kill_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); const char *id = luaL_checkstring (S, 2); const char *signame = luaL_checkstring (S, 3); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 1, NULL); int ret = libcrun_container_kill (ctx, id, signame, &crun_err); luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1); lua_pushboolean (S, true); return 1; } /* Get the container status. (ctx: userdata, id: string) [-0, +1, -] This function is a rewrite of `libcrun_container_state` for Lua. `libcrun_container_state` receives `FILE*` and writes JSON. We could not use `fmemopen` like `luacrun_container_spec` since the final size of the string is 100% unpredictable. */ LUA_API int luacrun_ctx_status_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); const char *id = luaL_checkstring (S, 2); luaL_checkstack (S, 3, NULL); libcrun_error_t crun_err = NULL; int ret; lua_createtable (S, 0, 0); int tabidx = lua_gettop (S); // We know there are two frames available on the stack from this point. lua_pushstring (S, "1.0.0"); lua_setfield (S, tabidx, "ociVersion"); lua_pushvalue (S, 2); lua_setfield (S, tabidx, "id"); libcrun_container_status_t status = {}; const char *state_root = ctx->state_root; ret = libcrun_read_container_status (&status, state_root, id, &crun_err); luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushnil (S), 1); const char *container_status = NULL; int running; ret = libcrun_get_container_state_string (id, &status, state_root, &container_status, &running, &crun_err); luacrun_SoftErrIf (S, ret < 0 && crun_err != NULL, &crun_err, lua_pushnil (S), 1); if (ret < 0) { // Failed to read the exec.fifo in the state dir lua_pushnil(S); lua_pushstring(S, "failed to read state"); return 2; } lua_pushinteger (S, running ? status.pid : 0); lua_setfield (S, tabidx, "pid"); struct luacrun_string_pair { const char *k; const char *v; }; const struct luacrun_string_pair values[] = { { "status", container_status }, { "bundle", status.bundle }, { "rootfs", status.rootfs }, { "created", status.created }, { "systemd-scope", status.scope }, /* maybe NULL*/ { "owner", status.owner }, /* maybe NULL */ { NULL, NULL }, }; for (int i = 0; values[i].k != NULL; i++) { const struct luacrun_string_pair p = values[i]; if (p.v != NULL) { lua_pushstring (S, p.v); lua_setfield (S, tabidx, p.k); } } { cleanup_container libcrun_container_t *container = NULL; cleanup_free char *dir = NULL; ret = libcrun_get_state_directory (&dir, state_root, id, &crun_err); if (UNLIKELY (ret < 0)) { libcrun_error_release (&crun_err); lua_pushnil (S); lua_pushstring (S, "cannot get state directory"); return 2; } const char *config_file = lua_pushfstring (S, "%s/%s", dir, "config.json"); container = libcrun_container_load_from_file (config_file, &crun_err); lua_pop (S, 1); if (container == NULL) { libcrun_error_release (&crun_err); lua_pushnil (S); lua_pushstring (S, "error loading config.json"); return 2; } if (container->container_def->annotations && container->container_def->annotations->len) { /* Check stack again, we need three available frames here. */ luaL_checkstack (S, 3, NULL); lua_createtable (S, 0, container->container_def->annotations->len); for (size_t i = 0; i < container->container_def->annotations->len; i++) { const char *key = container->container_def->annotations->keys[i]; const char *val = container->container_def->annotations->values[i]; lua_pushstring (S, val); lua_setfield (S, tabidx, key); } lua_setfield (S, tabidx, "annotations"); } } return 1; } LUA_API int luacrun_ctx_start_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); const char *id = luaL_checkstring (S, 2); libcrun_error_t crun_err = NULL; luaL_checkstack (S, 2, NULL); int ret = libcrun_container_start (ctx, id, &crun_err); luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1); lua_pushboolean (S, true); return 1; } struct luacrun_ctx_containers_iterator { bool closed; // flag for if the libcrun_container_list_t free'd libcrun_container_list_t *start; libcrun_container_list_t *curr; lua_Integer counter; }; static int luacrun_ctx_containers_iteratorf (lua_State *S) { // params: userdata integer luaL_checktype (S, 1, LUA_TUSERDATA); struct luacrun_ctx_containers_iterator *it = lua_touserdata (S, 1); luaL_checkstack (S, 2, NULL); if (it->curr != NULL) { lua_pushinteger (S, ++(it->counter)); lua_pushstring (S, it->curr->name); it->curr = it->curr->next; return 2; } else { it->closed = true; libcrun_free_containers_list (it->start); lua_pushnil (S); return 1; } } static int luacrun_ctx_containers_finalizer (lua_State *S) { luaL_checktype (S, 1, LUA_TUSERDATA); struct luacrun_ctx_containers_iterator *iter = lua_touserdata (S, 1); if (! iter->closed) { libcrun_free_containers_list (iter->start); } return 0; } static const luaL_Reg luacrun_ctx_containers_iterator_metamethods[] = { { "__gc", &luacrun_ctx_containers_finalizer }, { NULL, NULL }, }; static int luacrun_setup_ctx_iter_metatable (lua_State *S) { luaL_newmetatable (S, LUA_CRUN_TAG_CONTS_ITER); luaL_setfuncs (S, luacrun_ctx_containers_iterator_metamethods, 0); lua_pop (S, 1); return 0; } LUA_API int luacrun_ctx_iter_containers (lua_State *S) { libcrun_error_t crun_err = NULL; libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); luaL_checkstack (S, 4, NULL); lua_pushcfunction (S, &luacrun_ctx_containers_iteratorf); libcrun_container_list_t *containers; int ret = libcrun_get_containers_list (&containers, ctx->state_root, &crun_err); if (ret < 0 && crun_err != NULL) luacrun_set_error (S, &crun_err); // ret < 0 && crun_err == NULL: the status file does not exists // The `containers` is still NULL, // the iterator function knows how to handle the situation struct luacrun_ctx_containers_iterator *it = lua_newuserdata (S, sizeof (struct luacrun_ctx_containers_iterator)); *it = (struct luacrun_ctx_containers_iterator){ .closed = false, .counter = 0, .curr = containers, .start = containers, }; luaL_setmetatable (S, LUA_CRUN_TAG_CONTS_ITER); lua_pushinteger (S, it->counter); lua_pushnil (S); return 4; } LUA_API int luacrun_ctx_update_container (lua_State *S) { libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); const char *id = luaL_checkstring (S, 2); const char *content = luaL_checkstring (S, 3); luaL_checkstack (S, 2, NULL); char errbuf[1024] = {}; yajl_val parsed_json = yajl_tree_parse (content, errbuf, sizeof (errbuf)); if (parsed_json == NULL) { lua_pushboolean (S, false); lua_pushfstring (S, "cannot parse the data: \"%s\"", errbuf); return 2; } struct parser_context parser_ctx = { .options = 0, .errfile = stderr }; runtime_spec_schema_config_schema_process *rt_spec_process; parser_error p_err = NULL; rt_spec_process = make_runtime_spec_schema_config_schema_process (parsed_json, &parser_ctx, &p_err); yajl_tree_free (parsed_json); if (rt_spec_process == NULL) { lua_pushboolean (S, false); lua_pushfstring (S, "cannot parse process: \"%s\"", p_err); free (p_err); return 2; } libcrun_error_t crun_err = NULL; int ret = libcrun_container_exec (ctx, id, rt_spec_process, &crun_err); free_runtime_spec_schema_config_schema_process (rt_spec_process); luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1); lua_pushboolean (S, true); return 1; } #define luacrun_CtxStringAccessor(name, uval_idx) \ LUA_API int luacrun_ctx_get_##name (lua_State *S) \ { \ libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \ if (ctx->name != NULL) \ { \ luaL_checkstack (S, 1, NULL); \ lua_pushstring (S, ctx->name); \ return 1; \ } \ else \ { \ return 0; \ } \ } \ LUA_API int luacrun_ctx_set_##name (lua_State *S) \ { \ libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \ const char *val = luaL_optstring (S, 2, NULL); \ luaL_checkstack (S, 2, NULL); \ if (ctx->name != NULL) \ { \ lua_pushstring (S, ctx->name); \ } \ else \ { \ lua_pushnil (S); \ } \ const char *copy = luacrun_xstrdup (S, val); \ ctx->name = copy; \ lua_setiuservalue (S, 1, uval_idx); \ return 1; \ } #define luacrun_CtxBoolAccessor(name) \ LUA_API int luacrun_ctx_get_##name (lua_State *S) \ { \ libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \ luaL_checkstack (S, 1, NULL); \ lua_pushboolean (S, ctx->name); \ return 1; \ } \ LUA_API int luacrun_ctx_set_##name (lua_State *S) \ { \ libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \ luaL_checktype (S, 2, LUA_TBOOLEAN); \ luaL_checkstack (S, 1, NULL); \ bool oldval = ctx->name; \ ctx->name = lua_toboolean (S, 2); \ lua_pushboolean (S, oldval); \ return 1; \ } luacrun_CtxStringAccessor (state_root, 1); luacrun_CtxStringAccessor (id, 2); luacrun_CtxStringAccessor (bundle, 3); luacrun_CtxStringAccessor (console_socket, 4); luacrun_CtxStringAccessor (pid_file, 5); luacrun_CtxStringAccessor (notify_socket, 6); luacrun_CtxStringAccessor (handler, 7); luacrun_CtxBoolAccessor (systemd_cgroup); #define luacrun_RegAddCtxAccessor(method_name, name) \ { method_name, &luacrun_ctx_get_##name }, \ { \ "set_" method_name, &luacrun_ctx_set_##name \ } static const luaL_Reg luacrun_ctx_index[] = { { "run", &luacrun_ctx_run }, { "create", &luacrun_ctx_create_container }, { "delete", &luacrun_ctx_delete_container }, { "kill", &luacrun_ctx_kill_container }, { "start", &luacrun_ctx_start_container }, { "status", &luacrun_ctx_status_container }, { "iter_names", &luacrun_ctx_iter_containers }, { "update", &luacrun_ctx_update_container }, luacrun_RegAddCtxAccessor ("state_root", state_root), luacrun_RegAddCtxAccessor ("id", id), luacrun_RegAddCtxAccessor ("bundle", bundle), luacrun_RegAddCtxAccessor ("console_socket", console_socket), luacrun_RegAddCtxAccessor ("pid_file", pid_file), luacrun_RegAddCtxAccessor ("notify_socket", notify_socket), luacrun_RegAddCtxAccessor ("handler", handler), luacrun_RegAddCtxAccessor ("systemd_cgroup", systemd_cgroup), { NULL, NULL }, }; LUA_API int luacrun_setup_ctx_metatable (lua_State *S) { luaL_checkstack (S, 3, NULL); luaL_newmetatable (S, LUA_CRUN_TAG_CTX); int mtab_idx = lua_gettop (S); lua_newtable (S); luaL_setfuncs (S, luacrun_ctx_index, 0); lua_setfield (S, mtab_idx, "__index"); lua_pop (S, 1); return 0; } LUA_API int luacrun_setup_cont_metatable (lua_State *S) { luaL_checkstack (S, 2, NULL); luaL_newmetatable (S, LUA_CRUN_TAG_CONT); int mtab_idx = lua_gettop (S); lua_pushcfunction (S, &luacrun_container_finalizer); lua_setfield (S, mtab_idx, "__gc"); // Can we do better than a finalizer? // Indirect pointer and wild memory make // Lua GC could not recognize the memory usage. lua_pop (S, 1); return 0; } static const luaL_Reg luacrun_library_reg[] = { { .name = "new_ctx", .func = &luacrun_new_ctx }, { .name = "container_spec", .func = &luacrun_container_spec }, { .name = "new_container_from_string", .func = &luacrun_new_container_from_string }, { .name = "new_container_from_file", .func = &luacrun_new_container_from_file }, { .name = "get_verbosity", .func = &luacrun_get_verbosity }, { .name = "set_verbosity", .func = &luacrun_set_verbosity }, { .name = "run", .func = &luacrun_ctx_run }, { .name = "create_container", .func = &luacrun_ctx_create_container }, { .name = "delete_container", .func = &luacrun_ctx_delete_container }, { .name = "kill_container", .func = &luacrun_ctx_kill_container }, { .name = "start_container", .func = &luacrun_ctx_start_container }, { .name = "status_container", .func = &luacrun_ctx_status_container }, { .name = "iter_container_names", .func = &luacrun_ctx_iter_containers }, { .name = "update_container", .func = &luacrun_ctx_update_container }, { NULL, NULL }, }; LUA_API int luaopen_luacrun (lua_State *S) { luaL_checkstack (S, 2, NULL); luaL_newlib (S, luacrun_library_reg); int libtab_idx = lua_gettop (S); lua_pushinteger (S, LIBCRUN_VERBOSITY_ERROR); lua_setfield (S, libtab_idx, "VERBOSITY_ERROR"); lua_pushinteger (S, LIBCRUN_VERBOSITY_WARNING); lua_setfield (S, libtab_idx, "VERBOSITY_WARNING"); lua_pushinteger (S, LIBCRUN_VERBOSITY_DEBUG); lua_setfield (S, libtab_idx, "VERBOSITY_DEBUG"); luacrun_setup_ctx_metatable (S); luacrun_setup_cont_metatable (S); luacrun_setup_ctx_iter_metatable (S); return 1; }