Files
crun/lua/lua_crun.c
Heinz Wiesinger d51df096f9 lua: clean up unused defines
Signed-off-by: Heinz Wiesinger <pprkut@liwjatan.org>
2025-07-01 22:41:34 +02:00

841 lines
29 KiB
C

/*
*crun - OCI runtime written in C
*
*Copyright (C) Rubicon Rowe <l1589002388@gmail.com>
*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 <http://www.gnu.org/licenses/>.
*/
/* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <lua.h>
#include <lauxlib.h>
#include <libcrun/container.h>
#include <libcrun/status.h>
#include <libcrun/utils.h>
#include <libcrun/error.h>
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;
}