mirror of
https://github.com/containers/crun.git
synced 2026-02-05 06:45:40 +01:00
lua: add Lua bindings
Signed-off-by: Rubicon Rowe <l1589002388@gmail.com>
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -69,3 +69,12 @@ tests/tests_*
|
||||
tests/*.log
|
||||
tests/*.trs
|
||||
test-suite.log
|
||||
|
||||
# Cached files
|
||||
.cache/
|
||||
compile_commands.json
|
||||
|
||||
# LuaRocks development files
|
||||
/luarocks
|
||||
/lua_modules
|
||||
/.luarocks
|
||||
|
||||
14
Makefile.am
14
Makefile.am
@@ -86,6 +86,20 @@ python_crun_la_LDFLAGS = -avoid-version -module $(PYTHON_LDFLAGS)
|
||||
python_crun_la_LIBADD = libcrun.la $(PYTHON_LIBS) $(FOUND_LIBS) $(maybe_libyajl.la)
|
||||
endif
|
||||
|
||||
if LUA_BINDINGS
|
||||
noinst_LTLIBRARIES = libcrun-bundled.la
|
||||
luaexec_LTLIBRARIES = luacrun.la
|
||||
luacrun_la_SOURCES = lua/lua_crun.c
|
||||
luacrun_la_CFLAGS = -I $(abs_top_srcdir)/libocispec/src -I $(abs_top_builddir)/libocispec/src -I $(abs_top_builddir)/src $(LUA_INCLUDE)
|
||||
luacrun_la_LDFLAGS = -avoid-version -module
|
||||
luacrun_la_LIBADD = libcrun-bundled.la $(LUA_LIB) $(FOUND_LIBS)
|
||||
|
||||
libcrun_bundled_la_SOURCES = $(libcrun_SOURCES)
|
||||
libcrun_bundled_la_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -fvisibility=hidden
|
||||
libcrun_bundled_la_LIBADD = libocispec/libocispec.la $(FOUND_LIBS) $(maybe_libyajl.la)
|
||||
libcrun_bundled_la_LDFLAGS = -Wl,--version-script=$(abs_top_srcdir)/libcrun.lds
|
||||
endif
|
||||
|
||||
crun_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -D CRUN_LIBDIR="\"$(CRUN_LIBDIR)\""
|
||||
crun_SOURCES = src/crun.c src/run.c src/delete.c src/kill.c src/pause.c src/unpause.c src/spec.c \
|
||||
src/exec.c src/list.c src/create.c src/start.c src/state.c src/update.c src/ps.c \
|
||||
|
||||
@@ -176,3 +176,7 @@ $ sudo su -
|
||||
# molecule converge
|
||||
# molecule verify
|
||||
```
|
||||
|
||||
## Lua bindings
|
||||
|
||||
A Lua binding is available. See [the README](lua/README.md) for more information.
|
||||
|
||||
10
configure.ac
10
configure.ac
@@ -173,6 +173,15 @@ AS_IF([test "x$with_python_bindings" = "xyes"], [
|
||||
use_fPIC=yes
|
||||
])
|
||||
|
||||
AC_ARG_WITH([lua-bindings], AS_HELP_STRING([--with-lua-bindings], [build the Lua bindings]))
|
||||
|
||||
AS_IF([test "x$with_lua_bindings" = "xyes"], [
|
||||
AX_PROG_LUA([5.4], [5.5], [], [AC_MSG_ERROR([*** lua interpreter not found])])
|
||||
AX_LUA_HEADERS([], [AC_MSG_ERROR([*** lua headers not found])])
|
||||
AX_LUA_LIBS([], [AC_MSG_ERROR([*** lua libs not found])])
|
||||
use_fPIC=yes
|
||||
])
|
||||
|
||||
AS_IF([test "x$use_fPIC" = "xyes"], [
|
||||
# configure should not touch CFLAGS/LDFLAGS but we need it to propagate it
|
||||
# to libocispec.
|
||||
@@ -253,6 +262,7 @@ fi
|
||||
AC_SEARCH_LIBS([argp_parse], [argp], [], [AC_MSG_ERROR([*** argp functions not found - install libargp or argp_standalone])])
|
||||
|
||||
AM_CONDITIONAL([PYTHON_BINDINGS], [test "x$with_python_bindings" = "xyes"])
|
||||
AM_CONDITIONAL([LUA_BINDINGS], [test "x$with_lua_bindings" = "xyes"])
|
||||
AM_CONDITIONAL([CRIU_SUPPORT], [test "x$have_criu" = "xyes"])
|
||||
AM_CONDITIONAL([SHARED_LIBCRUN], [test "x$enable_shared" = "xyes"])
|
||||
|
||||
|
||||
120
lua/README.md
Normal file
120
lua/README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Lua binding for libcrun
|
||||
|
||||
Bare libcrun interface for Lua.
|
||||
|
||||
There are some problems still and the API is a subject to change.
|
||||
|
||||
## Build
|
||||
|
||||
Only build static archive, to be bundled with another program:
|
||||
|
||||
````sh
|
||||
./configure --with-lua-bindings
|
||||
make && make install
|
||||
````
|
||||
|
||||
Since the final product bundle libcrun, you don't need to link libcrun at runtime.
|
||||
|
||||
|
||||
For the library using at runtime, add option `--enable-shared`:
|
||||
````sh
|
||||
./configure --with-lua-bindings --enable-shared
|
||||
make && make install
|
||||
````
|
||||
|
||||
Other options to build libcrun may affect the bundled libcrun.
|
||||
|
||||
## Usage
|
||||
|
||||
See `luacrun.d.tl`.
|
||||
|
||||
## Works with your LuaRocks project
|
||||
|
||||
You can configure the prefix as your `lua_modules` to access this library in your project.
|
||||
|
||||
````sh
|
||||
./configure --with-lua-bindings --enable-shared --prefix $(pwd)/lua_modules
|
||||
````
|
||||
|
||||
## Interpreter may restart?
|
||||
|
||||
Related issue: [#695: [Python bindings] Python interpreter restarts (?) after first import of python_crun](https://github.com/containers/crun/issues/695)
|
||||
|
||||
The lua interpreter may restart at the first open of the library (may not happen if statically linked into your program). It's side effect of [a protection (click for the code)](https://github.com/containers/crun/blob/923447b691dbd7c5bffbaee1427460d62d848047/src/libcrun/linux.c#L3881-L3891) to avoid attacks like [CVE-2019-5736](https://nvd.nist.gov/vuln/detail/CVE-2019-5736).
|
||||
|
||||
To ease the hurt, always place the `require` call at the start of your program:
|
||||
|
||||
````lua
|
||||
-- entry point module for luabundler
|
||||
require "luacrun"
|
||||
|
||||
return function(...) -- the entry point
|
||||
print("Hello World!")
|
||||
end
|
||||
````
|
||||
|
||||
It's not required to use the library at the moment. Since it is cached, the `require()` will not open the library again.
|
||||
|
||||
If the entry point is at the C-side, you may use the `luaL_requiref` to open the library instead of `require()` call in Lua code.
|
||||
````c
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
extern int luaopen_luacrun(lua_State *S);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
lua_State *S = luaL_newstate();
|
||||
// Open the library before any actual logic,
|
||||
// to make sure users will not notice the program have actually started twice
|
||||
luaL_requiref(S, "luacrun", &luaopen_luacrun, false);
|
||||
lua_pop(S, 1);
|
||||
// ...your code
|
||||
}
|
||||
````
|
||||
|
||||
The protection might cause another problem in REPL:
|
||||
````
|
||||
$ lua
|
||||
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
|
||||
> luacrun = require "luacrun"
|
||||
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
|
||||
> luacrun
|
||||
nil
|
||||
>
|
||||
````
|
||||
|
||||
When you call `require "luacrun"` at the first time, the REPL restarted and the state have been reset.
|
||||
|
||||
The workaround is `require "luacrun"` again and you get the library this time. The protection will not apply again if it's already applied.
|
||||
````
|
||||
$ lua
|
||||
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
|
||||
> luacrun = require "luacrun"
|
||||
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
|
||||
> luacrun
|
||||
nil
|
||||
> luacrun = require "luacrun"
|
||||
> luacrun
|
||||
table: 0x561edad470d0
|
||||
>
|
||||
````
|
||||
|
||||
It's safe to use luacrun in multi-state usage, the program restarts only once.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
You need [busted](https://lunarmodules.github.io/busted/) and below dependencies to run tests:
|
||||
|
||||
- [dkjson](https://luarocks.org/modules/dhkolf/dkjson)
|
||||
- [luaposix](https://luaposix.github.io)
|
||||
|
||||
````sh
|
||||
luarocks install busted dkjson luaposix
|
||||
````
|
||||
|
||||
The tests assume environment variable `INIT` exists. It's the `init` program compiled in `tests`.
|
||||
|
||||
````sh
|
||||
INIT=$(pwd)/tests/init lua_modules/bin/busted lua
|
||||
````
|
||||
823
lua/lua_crun.c
Normal file
823
lua/lua_crun.c
Normal file
@@ -0,0 +1,823 @@
|
||||
/*
|
||||
*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; \
|
||||
}
|
||||
|
||||
#if __STDC_VERSION__ < 201112L
|
||||
# define LUACRUN_NoRet
|
||||
#elif __STDC_VERSION__ < 202300L
|
||||
# define LUACRUN_NoRet _Noreturn
|
||||
#else
|
||||
# define LUACRUN_NoRet [[noreturn]]
|
||||
#endif
|
||||
|
||||
extern LUACRUN_NoRet int lua_error (lua_State *L);
|
||||
extern LUACRUN_NoRet 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)->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 LUACRUN_NoRet 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 return 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);
|
||||
}
|
||||
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); // the crun_err is not used
|
||||
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 cleanup 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)
|
||||
{
|
||||
lua_pushnil (S);
|
||||
return luacrun_error (S, &crun_err) + 1;
|
||||
}
|
||||
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);
|
||||
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1);
|
||||
lua_pushboolean (S, true);
|
||||
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 here 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, lua_pushnil (S), 1);
|
||||
|
||||
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;
|
||||
|
||||
dir = libcrun_get_state_directory (state_root, id);
|
||||
if (dir == NULL)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
luacrun_set_error (S, &crun_err);
|
||||
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_sepc_process;
|
||||
parser_error p_err = NULL;
|
||||
rt_sepc_process = make_runtime_spec_schema_config_schema_process (parsed_json, &parser_ctx, &p_err);
|
||||
yajl_tree_free (parsed_json);
|
||||
if (rt_sepc_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_sepc_process, &crun_err);
|
||||
free_runtime_spec_schema_config_schema_process (rt_sepc_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 regconize 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");
|
||||
|
||||
luacrun_setup_ctx_metatable (S);
|
||||
luacrun_setup_cont_metatable (S);
|
||||
luacrun_setup_ctx_iter_metatable (S);
|
||||
return 1;
|
||||
}
|
||||
164
lua/luacrun.d.tl
Normal file
164
lua/luacrun.d.tl
Normal file
@@ -0,0 +1,164 @@
|
||||
--[[
|
||||
Copyright (c) Rubicon Rowe
|
||||
|
||||
This file is part of crun. You can choose one of the following licenses for this file.
|
||||
|
||||
1. The MIT License
|
||||
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.
|
||||
|
||||
2. GNU Lesser General Public License v2.1 or later
|
||||
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
|
||||
NU 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/>.
|
||||
]]
|
||||
|
||||
local record luacrun
|
||||
VERBOSITY_ERROR: integer
|
||||
VERBOSITY_WARNING: integer
|
||||
|
||||
enum ContainerWorkState
|
||||
"stopped"
|
||||
"created"
|
||||
"paused"
|
||||
"running"
|
||||
end
|
||||
|
||||
record Ctx userdata
|
||||
run: (function (ctx: Ctx, cont: Container, flags: ContainerRunFlags | nil): number | nil, string | nil)
|
||||
create: (function (ctx: Ctx, cont: Container, flags: ContainerRunFlags | nil): number | nil, string | nil)
|
||||
delete: (function (ctx: Ctx, id: string, force: boolean | nil): boolean, string | nil)
|
||||
kill: (function (ctx: Ctx, id: string, signame: string): boolean, string | nil)
|
||||
status: (function (ctx: Ctx, id: string): ContainerStat | nil, string | nil)
|
||||
start: (function (ctx: Ctx, id: string): boolean, string | nil)
|
||||
iter_names: (function (ctx: Ctx): any...)
|
||||
update: (function (ctx: Ctx, id: string, content: string): boolean, string | nil)
|
||||
|
||||
-- Accessors
|
||||
-- All setters will return the old value.
|
||||
state_root: function(ctx: Ctx): string | nil
|
||||
set_state_root: function(ctx: Ctx, val: string | nil): string | nil
|
||||
id: function(ctx: Ctx): string | nil
|
||||
set_id: function(ctx: Ctx, val: string | nil): string | nil
|
||||
bundle: function(ctx: Ctx) : string | nil
|
||||
set_bundle: function(ctx: Ctx, val: string | nil): string | nil
|
||||
console_socket: function(ctx: Ctx): string | nil
|
||||
set_console_socket: function(ctx: Ctx, val: string | nil): string | nil
|
||||
pid_file: function(ctx: Ctx): string | nil
|
||||
set_pid_file: function(ctx: Ctx, val: string | nil): string | nil
|
||||
notify_socket: function(ctx: Ctx): string | nil
|
||||
set_notify_socket: function(ctx: Ctx, val: string | nil): string | nil
|
||||
handler: function(ctx: Ctx): string | nil
|
||||
set_handler: function(ctx: Ctx, val: string | nil): string | nil
|
||||
systemd_cgroup: function(ctx: Ctx): boolean
|
||||
set_systemd_cgroup: function(ctx: Ctx, val: boolean): boolean
|
||||
end
|
||||
|
||||
record Container userdata
|
||||
end
|
||||
|
||||
record ContainerStat
|
||||
ociVersion: string
|
||||
id: string
|
||||
pid: integer
|
||||
status: ContainerWorkState
|
||||
bundle: string
|
||||
rootfs: string
|
||||
["systemd-scope"]: string | nil
|
||||
owner: string | nil
|
||||
annotations: {string: string} | nil
|
||||
end
|
||||
|
||||
record ContainerRunFlags
|
||||
prefork: boolean | nil
|
||||
end
|
||||
|
||||
record CtxOpts
|
||||
state_root: string | nil
|
||||
id: string | nil
|
||||
bundle: string | nil
|
||||
console_socket: string | nil
|
||||
pid_file: string | nil
|
||||
notify_socket: string | nil
|
||||
handler: string | nil
|
||||
systemd_cgroup: boolean | nil
|
||||
detach: boolean | nil
|
||||
args: {string}
|
||||
end
|
||||
|
||||
new_ctx: (function (
|
||||
opts: CtxOpts
|
||||
): Ctx)
|
||||
|
||||
container_spec: (function (rootless: boolean | nil): string)
|
||||
|
||||
-- New container object from spec string or spec file.
|
||||
-- These functions does not create container in context (and in environment),
|
||||
-- just make a new object for the spec.
|
||||
new_container_from_string: (function (json_string: string): Container)
|
||||
new_container_from_file: (function (file_path: string): Container)
|
||||
|
||||
get_verbosity: (function (): integer)
|
||||
set_verbosity: (function (new_verbosity: integer): nil)
|
||||
|
||||
-- Run a container in a context.
|
||||
-- Return 1 if success; `nil` and the error message if failed.
|
||||
run: (function (ctx: Ctx, cont: Container, flags: ContainerRunFlags | nil): number | nil, string | nil)
|
||||
|
||||
-- Create the container described by `cont`.
|
||||
-- Return number (>= 0) if success; `nil` and the error message if failed
|
||||
create_container: (function (ctx: Ctx, cont: Container, flags: ContainerRunFlags | nil): number | nil, string | nil)
|
||||
|
||||
-- Delete the container `id`.
|
||||
-- Return `true` if success; `false` and the error message if failed.
|
||||
delete_container: (function (ctx: Ctx, id: string, force: boolean | nil): boolean, string | nil)
|
||||
|
||||
-- Kill the container `id` by signal `signame`.
|
||||
-- `signame` can be any name of supported Linux signals, or a string of the integer.
|
||||
-- If it's the name, it should be upper case (`"KILL"` not `"kill"`).
|
||||
-- Return `true` if success; `false` and the error message if failed.
|
||||
kill_container: (function (ctx: Ctx, id: string, signame: string): boolean, string | nil)
|
||||
|
||||
-- Get the container status.
|
||||
-- Return a table if success; `nil` and the error message if failed.
|
||||
status_container: (function (ctx: Ctx, id: string): ContainerStat | nil, string | nil)
|
||||
|
||||
-- Start the container.
|
||||
-- Return `true` if success; `false` and the error messgae if failed
|
||||
start_container: (function (ctx: Ctx, id: string): boolean, string | nil)
|
||||
|
||||
-- Iterate all container names in `ctx`.
|
||||
-- Return iterator.
|
||||
iter_containers_names: (function (ctx: Ctx): any...)
|
||||
|
||||
-- Update the container.
|
||||
-- Return `true` if success, `false` and the error message if failed.
|
||||
update_container: (function (ctx: Ctx, id: string, content: string): boolean, string | nil)
|
||||
end
|
||||
|
||||
|
||||
return luacrun
|
||||
176
lua/luacrun_spec.lua
Normal file
176
lua/luacrun_spec.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
local dkjson = require "dkjson"
|
||||
local posix = require "posix"
|
||||
local stdlib = require "posix.stdlib"
|
||||
local unistd = require "posix.unistd"
|
||||
local libgen = require "posix.libgen"
|
||||
|
||||
local function is_file_exists(path)
|
||||
local f = io.open(path, 'rb')
|
||||
if f then
|
||||
f:close()
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function makedirs(path)
|
||||
if not is_file_exists(path) then
|
||||
makedirs(libgen.dirname(path))
|
||||
local path, errmsg = posix.mkdir(path)
|
||||
assert(path, errmsg)
|
||||
end
|
||||
end
|
||||
|
||||
local function cp(src, dest)
|
||||
local stat, exitm, retc = os.execute(
|
||||
string.format("cp \"%s\" \"%s\"", src, dest))
|
||||
return retc
|
||||
end
|
||||
|
||||
local function mktestenv()
|
||||
local format = string.format
|
||||
local init_path = os.getenv("INIT")
|
||||
assert(init_path, "no INIT env var for init program")
|
||||
local path, errmsg = stdlib.mkdtemp("/tmp/luacrun-test-XXXXXX")
|
||||
assert(path, errmsg)
|
||||
local rootfs_path = string.format("%s/%s", path, "rootfs")
|
||||
makedirs(rootfs_path)
|
||||
for i, p in ipairs({
|
||||
"usr/bin", "etc", "var", "lib", "lib64", "usr/share/zoneinfo/Europe",
|
||||
"proc", "sys", "dev"
|
||||
}) do makedirs(string.format("%s/%s", rootfs_path, p)) end
|
||||
local sbin_path = string.format("%s/%s", rootfs_path, "sbin")
|
||||
makedirs(sbin_path)
|
||||
|
||||
local retc = cp(init_path, format("%s/%s", rootfs_path, "init"))
|
||||
assert(retc == 0, "cp init to rootfs return " .. retc)
|
||||
local retc = cp(init_path, format("%s/%s", sbin_path, "init"))
|
||||
assert(retc == 0, "cp init to sbin return " .. retc)
|
||||
|
||||
unistd.link("../usr/share/zoneinfo/Europe/Rome",
|
||||
format("%s/%s", rootfs_path, "etc/localtime"))
|
||||
|
||||
local passwd, errmsg = io.open(format("%s/%s", rootfs_path,
|
||||
"usr/share/passwd"), "w")
|
||||
assert(passwd, errmsg)
|
||||
passwd, errmsg = passwd:write("root:x:0:0:root:/root:/bin/bash\n")
|
||||
assert(passwd, errmsg)
|
||||
passwd:close()
|
||||
unistd.link("../usr/share/passwd",
|
||||
format("%s/%s", rootfs_path, "etc/passwd"))
|
||||
return path
|
||||
end
|
||||
|
||||
insulate("luacrun", function()
|
||||
local luacrun = require "luacrun"
|
||||
|
||||
describe("container_spec", function()
|
||||
it("returns a string", function()
|
||||
local s = luacrun.container_spec()
|
||||
assert.are.equals("string", type(s))
|
||||
end)
|
||||
|
||||
it("returns a json object", function()
|
||||
local s = luacrun.container_spec()
|
||||
local t = dkjson.decode(s)
|
||||
assert.are.equals("table", type(t))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("new_ctx", function()
|
||||
it("can create context with only id", function()
|
||||
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
||||
assert.are.equals("userdata", type(ctx))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("new_container_from_string", function()
|
||||
it("can create container object from json string", function()
|
||||
local spec = dkjson.decode(luacrun.container_spec())
|
||||
spec.root.path = "/"
|
||||
spec.process.args = {'/bin/echo', "Hello World!"}
|
||||
local cont, err = luacrun.new_container_from_string(dkjson.encode(
|
||||
spec))
|
||||
assert(cont, err)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("create_container", function()
|
||||
it("is same to ctx.create", function()
|
||||
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
||||
assert.are.equals(ctx.create, luacrun.create_container)
|
||||
end)
|
||||
end)
|
||||
|
||||
it("can create container and delete container", function()
|
||||
local temproot = mktestenv()
|
||||
local ctx = luacrun.new_ctx {state_root = temproot, id = "luacrun-test"}
|
||||
assert(ctx)
|
||||
local spec =
|
||||
dkjson.decode(luacrun.container_spec(unistd.geteuid() == 0))
|
||||
spec.root = {
|
||||
path = string.format("%s/%s", temproot, "rootfs"),
|
||||
readonly = true
|
||||
}
|
||||
spec.process.args = {'/init', "true"}
|
||||
spec.process.terminal = false
|
||||
spec.process.user = {uid = unistd.geteuid(), gid = unistd.getegid()}
|
||||
spec.linux.rootfsPropagation = "rprivate"
|
||||
local json_spec = dkjson.encode(spec)
|
||||
local cont, err = luacrun.new_container_from_string(json_spec)
|
||||
assert(cont, err)
|
||||
local stat, err = luacrun.create_container(ctx, cont)
|
||||
assert(stat == 0, string.format("%s, %d", err, stat))
|
||||
local names = {}
|
||||
for i, name in ctx:iter_names() do names[#names + 1] = name end
|
||||
assert(#names == 1, string.format("names length is %d", #names))
|
||||
local status, err = ctx:status(names[1])
|
||||
assert(status, err)
|
||||
assert.are.equals("created", status.status)
|
||||
local stat, err = ctx:start(names[1])
|
||||
assert(stat, err)
|
||||
local status, err = ctx:status(names[1])
|
||||
assert(status, err)
|
||||
assert.are.equals("running", status.status)
|
||||
while true do -- wait for container exited
|
||||
local status, err = ctx:status(names[1])
|
||||
assert(status, err)
|
||||
if status.status == "created" or status.status == "stopped" then
|
||||
break
|
||||
end
|
||||
end
|
||||
local stat, err = luacrun.delete_container(ctx, names[1])
|
||||
assert(stat, err)
|
||||
end)
|
||||
|
||||
describe("Ctx", function()
|
||||
it("id() can get id", function()
|
||||
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
||||
assert.are.equals("luacrun-test", ctx:id())
|
||||
end)
|
||||
|
||||
it("id() can read the id set by set_id()", function()
|
||||
local ctx = luacrun.new_ctx {id = "luacrun-test1"}
|
||||
assert.are.equals("luacrun-test1", ctx:id())
|
||||
ctx:set_id("luacrun-test2")
|
||||
assert.are.equals("luacrun-test2", ctx:id())
|
||||
end)
|
||||
|
||||
it("systemd_cgroup() can get a boolean", function()
|
||||
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
||||
assert.are.equals("boolean", type(ctx:systemd_cgroup()))
|
||||
end)
|
||||
|
||||
it("systemd_cgroup() can read the value set by set_system_cgroupd()",
|
||||
function()
|
||||
local ctx = luacrun.new_ctx {
|
||||
id = "luacrun-test",
|
||||
systemd_cgroup = false
|
||||
}
|
||||
assert.is_false(ctx:systemd_cgroup())
|
||||
ctx:set_systemd_cgroup(true)
|
||||
assert.is_true(ctx:systemd_cgroup())
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
663
m4/ax_lua.m4
Normal file
663
m4/ax_lua.m4
Normal file
@@ -0,0 +1,663 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_lua.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
|
||||
# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
|
||||
# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
|
||||
# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Detect a Lua interpreter, optionally specifying a minimum and maximum
|
||||
# version number. Set up important Lua paths, such as the directories in
|
||||
# which to install scripts and modules (shared libraries).
|
||||
#
|
||||
# Also detect Lua headers and libraries. The Lua version contained in the
|
||||
# header is checked to match the Lua interpreter version exactly. When
|
||||
# searching for Lua libraries, the version number is used as a suffix.
|
||||
# This is done with the goal of supporting multiple Lua installs (5.1,
|
||||
# 5.2, and 5.3 side-by-side).
|
||||
#
|
||||
# A note on compatibility with previous versions: This file has been
|
||||
# mostly rewritten for serial 18. Most developers should be able to use
|
||||
# these macros without needing to modify configure.ac. Care has been taken
|
||||
# to preserve each macro's behavior, but there are some differences:
|
||||
#
|
||||
# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
|
||||
# AX_PROG_LUA with no arguments.
|
||||
#
|
||||
# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
|
||||
# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
|
||||
# unnecessary, so it is deprecated and does not expand to anything.
|
||||
#
|
||||
# 3) The configure flag --with-lua-suffix no longer exists; the user
|
||||
# should instead specify the LUA precious variable on the command line.
|
||||
# See the AX_PROG_LUA description for details.
|
||||
#
|
||||
# Please read the macro descriptions below for more information.
|
||||
#
|
||||
# This file was inspired by Andrew Dalke's and James Henstridge's
|
||||
# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
|
||||
# (serial 17). Basically, this file is a mash-up of those two files. I
|
||||
# like to think it combines the best of the two!
|
||||
#
|
||||
# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
|
||||
# paths. Adds precious variable LUA, which may contain the path of the Lua
|
||||
# interpreter. If LUA is blank, the user's path is searched for an
|
||||
# suitable interpreter.
|
||||
#
|
||||
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
|
||||
# version number greater or equal to MINIMUM-VERSION will be accepted. If
|
||||
# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
|
||||
# version number greater or equal to MINIMUM-VERSION and less than
|
||||
# TOO-BIG-VERSION will be accepted.
|
||||
#
|
||||
# The Lua version number, LUA_VERSION, is found from the interpreter, and
|
||||
# substituted. LUA_PLATFORM is also found, but not currently supported (no
|
||||
# standard representation).
|
||||
#
|
||||
# Finally, the macro finds four paths:
|
||||
#
|
||||
# luadir Directory to install Lua scripts.
|
||||
# pkgluadir $luadir/$PACKAGE
|
||||
# luaexecdir Directory to install Lua modules.
|
||||
# pkgluaexecdir $luaexecdir/$PACKAGE
|
||||
#
|
||||
# These paths are found based on $prefix, $exec_prefix, Lua's
|
||||
# package.path, and package.cpath. The first path of package.path
|
||||
# beginning with $prefix is selected as luadir. The first path of
|
||||
# package.cpath beginning with $exec_prefix is used as luaexecdir. This
|
||||
# should work on all reasonable Lua installations. If a path cannot be
|
||||
# determined, a default path is used. Of course, the user can override
|
||||
# these later when invoking make.
|
||||
#
|
||||
# luadir Default: $prefix/share/lua/$LUA_VERSION
|
||||
# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
|
||||
#
|
||||
# These directories can be used by Automake as install destinations. The
|
||||
# variable name minus 'dir' needs to be used as a prefix to the
|
||||
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
|
||||
#
|
||||
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
|
||||
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
|
||||
# FOUND is blank, then it will default to printing an error. To prevent
|
||||
# the default behavior, give ':' as an action.
|
||||
#
|
||||
# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
|
||||
# expanded before this macro. Adds precious variable LUA_INCLUDE, which
|
||||
# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
|
||||
# LUA_INCLUDE is blank, then this macro will attempt to find suitable
|
||||
# flags.
|
||||
#
|
||||
# LUA_INCLUDE can be used by Automake to compile Lua modules or
|
||||
# executables with embedded interpreters. The *_CPPFLAGS variables should
|
||||
# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
|
||||
#
|
||||
# This macro searches for the header lua.h (and others). The search is
|
||||
# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
|
||||
# If the search is unsuccessful, then some common directories are tried.
|
||||
# If the headers are then found, then LUA_INCLUDE is set accordingly.
|
||||
#
|
||||
# The paths automatically searched are:
|
||||
#
|
||||
# * /usr/include/luaX.Y
|
||||
# * /usr/include/lua/X.Y
|
||||
# * /usr/include/luaXY
|
||||
# * /usr/local/include/luaX.Y
|
||||
# * /usr/local/include/lua-X.Y
|
||||
# * /usr/local/include/lua/X.Y
|
||||
# * /usr/local/include/luaXY
|
||||
#
|
||||
# (Where X.Y is the Lua version number, e.g. 5.1.)
|
||||
#
|
||||
# The Lua version number found in the headers is always checked to match
|
||||
# the Lua interpreter's version number. Lua headers with mismatched
|
||||
# version numbers are not accepted.
|
||||
#
|
||||
# If headers are found, then ACTION-IF-FOUND is performed, otherwise
|
||||
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
|
||||
# it will default to printing an error. To prevent the default behavior,
|
||||
# set the action to ':'.
|
||||
#
|
||||
# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
|
||||
# expanded before this macro. Adds precious variable LUA_LIB, which may
|
||||
# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
|
||||
# then this macro will attempt to find suitable flags.
|
||||
#
|
||||
# LUA_LIB can be used by Automake to link Lua modules or executables with
|
||||
# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
|
||||
# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
|
||||
#
|
||||
# This macro searches for the Lua library. More technically, it searches
|
||||
# for a library containing the function lua_load. The search is performed
|
||||
# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
|
||||
#
|
||||
# If the search determines that some linker flags are missing, then those
|
||||
# flags will be added to LUA_LIB.
|
||||
#
|
||||
# If libraries are found, then ACTION-IF-FOUND is performed, otherwise
|
||||
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
|
||||
# it will default to printing an error. To prevent the default behavior,
|
||||
# set the action to ':'.
|
||||
#
|
||||
# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
|
||||
# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
|
||||
# Autoconf Archive.
|
||||
#
|
||||
# If a readline compatible library is found, then ACTION-IF-FOUND is
|
||||
# performed, otherwise ACTION-IF-NOT-FOUND is performed.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
|
||||
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
|
||||
# Copyright (c) Rubicon Rowe <l1589002388@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program 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 General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
#
|
||||
# Changelog
|
||||
# * Supported Lua 5.4 (Rubicon Rowe)
|
||||
|
||||
#serial 42
|
||||
|
||||
dnl =========================================================================
|
||||
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([AX_PROG_LUA],
|
||||
[
|
||||
dnl Check for required tools.
|
||||
AC_REQUIRE([AC_PROG_GREP])
|
||||
AC_REQUIRE([AC_PROG_SED])
|
||||
|
||||
dnl Make LUA a precious variable.
|
||||
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
|
||||
|
||||
dnl Find a Lua interpreter.
|
||||
m4_define_default([_AX_LUA_INTERPRETER_LIST],
|
||||
[lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
|
||||
|
||||
m4_if([$1], [],
|
||||
[ dnl No version check is needed. Find any Lua interpreter.
|
||||
AS_IF([test "x$LUA" = 'x'],
|
||||
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
|
||||
ax_display_LUA='lua'
|
||||
|
||||
AS_IF([test "x$LUA" != 'x:'],
|
||||
[ dnl At least check if this is a Lua interpreter.
|
||||
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
|
||||
_AX_LUA_CHK_IS_INTRP([$LUA],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([not a Lua interpreter])
|
||||
])
|
||||
])
|
||||
],
|
||||
[ dnl A version check is needed.
|
||||
AS_IF([test "x$LUA" != 'x'],
|
||||
[ dnl Check if this is a Lua interpreter.
|
||||
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
|
||||
_AX_LUA_CHK_IS_INTRP([$LUA],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([not a Lua interpreter])
|
||||
])
|
||||
dnl Check the version.
|
||||
m4_if([$2], [],
|
||||
[_ax_check_text="whether $LUA version >= $1"],
|
||||
[_ax_check_text="whether $LUA version >= $1, < $2"])
|
||||
AC_MSG_CHECKING([$_ax_check_text])
|
||||
_AX_LUA_CHK_VER([$LUA], [$1], [$2],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([version is out of range for specified LUA])])
|
||||
ax_display_LUA=$LUA
|
||||
],
|
||||
[ dnl Try each interpreter until we find one that satisfies VERSION.
|
||||
m4_if([$2], [],
|
||||
[_ax_check_text="for a Lua interpreter with version >= $1"],
|
||||
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
|
||||
AC_CACHE_CHECK([$_ax_check_text],
|
||||
[ax_cv_pathless_LUA],
|
||||
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
|
||||
test "x$ax_cv_pathless_LUA" = 'xnone' && break
|
||||
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
|
||||
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
|
||||
done
|
||||
])
|
||||
dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
|
||||
AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
|
||||
[LUA=':'],
|
||||
[AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
|
||||
ax_display_LUA=$ax_cv_pathless_LUA
|
||||
])
|
||||
])
|
||||
|
||||
AS_IF([test "x$LUA" = 'x:'],
|
||||
[ dnl Run any user-specified action, or abort.
|
||||
m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
|
||||
],
|
||||
[ dnl Query Lua for its version number.
|
||||
AC_CACHE_CHECK([for $ax_display_LUA version],
|
||||
[ax_cv_lua_version],
|
||||
[ dnl Get the interpreter version in X.Y format. This should work for
|
||||
dnl interpreters version 5.0 and beyond.
|
||||
ax_cv_lua_version=[`$LUA -e '
|
||||
-- return a version number in X.Y format
|
||||
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
|
||||
print(ver)'`]
|
||||
])
|
||||
AS_IF([test "x$ax_cv_lua_version" = 'x'],
|
||||
[AC_MSG_ERROR([invalid Lua version number])])
|
||||
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
|
||||
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
|
||||
|
||||
dnl The following check is not supported:
|
||||
dnl At times (like when building shared libraries) you may want to know
|
||||
dnl which OS platform Lua thinks this is.
|
||||
AC_CACHE_CHECK([for $ax_display_LUA platform],
|
||||
[ax_cv_lua_platform],
|
||||
[ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]])
|
||||
AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
|
||||
|
||||
dnl Use the values of $prefix and $exec_prefix for the corresponding
|
||||
dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
|
||||
dnl variables so they can be overridden if need be. However, the general
|
||||
dnl consensus is that you shouldn't need this ability.
|
||||
AC_SUBST([LUA_PREFIX], ['${prefix}'])
|
||||
AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
|
||||
|
||||
dnl Lua provides no way to query the script directory, and instead
|
||||
dnl provides LUA_PATH. However, we should be able to make a safe educated
|
||||
dnl guess. If the built-in search path contains a directory which is
|
||||
dnl prefixed by $prefix, then we can store scripts there. The first
|
||||
dnl matching path will be used.
|
||||
AC_CACHE_CHECK([for $ax_display_LUA script directory],
|
||||
[ax_cv_lua_luadir],
|
||||
[ AS_IF([test "x$prefix" = 'xNONE'],
|
||||
[ax_lua_prefix=$ac_default_prefix],
|
||||
[ax_lua_prefix=$prefix])
|
||||
|
||||
dnl Initialize to the default path.
|
||||
ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
|
||||
|
||||
dnl Try to find a path with the prefix.
|
||||
_AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
|
||||
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
|
||||
[ dnl Fix the prefix.
|
||||
_ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
|
||||
ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
|
||||
$SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"`
|
||||
])
|
||||
])
|
||||
AC_SUBST([luadir], [$ax_cv_lua_luadir])
|
||||
AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
|
||||
|
||||
dnl Lua provides no way to query the module directory, and instead
|
||||
dnl provides LUA_PATH. However, we should be able to make a safe educated
|
||||
dnl guess. If the built-in search path contains a directory which is
|
||||
dnl prefixed by $exec_prefix, then we can store modules there. The first
|
||||
dnl matching path will be used.
|
||||
AC_CACHE_CHECK([for $ax_display_LUA module directory],
|
||||
[ax_cv_lua_luaexecdir],
|
||||
[ AS_IF([test "x$exec_prefix" = 'xNONE'],
|
||||
[ax_lua_exec_prefix=$ax_lua_prefix],
|
||||
[ax_lua_exec_prefix=$exec_prefix])
|
||||
|
||||
dnl Initialize to the default path.
|
||||
ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
|
||||
|
||||
dnl Try to find a path with the prefix.
|
||||
_AX_LUA_FND_PRFX_PTH([$LUA],
|
||||
[$ax_lua_exec_prefix], [module])
|
||||
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
|
||||
[ dnl Fix the prefix.
|
||||
_ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
|
||||
ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
|
||||
$SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"`
|
||||
])
|
||||
])
|
||||
AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
|
||||
AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
|
||||
|
||||
dnl Run any user specified action.
|
||||
$3
|
||||
])
|
||||
])
|
||||
|
||||
dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
|
||||
AC_DEFUN([AX_WITH_LUA],
|
||||
[
|
||||
AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]])
|
||||
AX_PROG_LUA
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
|
||||
[
|
||||
dnl A minimal Lua factorial to prove this is an interpreter. This should work
|
||||
dnl for Lua interpreters version 5.0 and beyond.
|
||||
_ax_lua_factorial=[`$1 2>/dev/null -e '
|
||||
-- a simple factorial
|
||||
function fact (n)
|
||||
if n == 0 then
|
||||
return 1
|
||||
else
|
||||
return n * fact(n-1)
|
||||
end
|
||||
end
|
||||
print("fact(5) is " .. fact(5))'`]
|
||||
AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'],
|
||||
[$2], [$3])
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
|
||||
dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([_AX_LUA_CHK_VER],
|
||||
[
|
||||
dnl Check that the Lua version is within the bounds. Only the major and minor
|
||||
dnl version numbers are considered. This should work for Lua interpreters
|
||||
dnl version 5.0 and beyond.
|
||||
_ax_lua_good_version=[`$1 -e '
|
||||
-- a script to compare versions
|
||||
function verstr2num(verstr)
|
||||
local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)")
|
||||
if majorver and minorver then
|
||||
return tonumber(majorver) * 100 + tonumber(minorver)
|
||||
end
|
||||
end
|
||||
local minver = verstr2num("$2")
|
||||
local _, _, trimver = string.find(_VERSION, "^Lua (.*)")
|
||||
local ver = verstr2num(trimver)
|
||||
local maxver = verstr2num("$3") or 1e9
|
||||
if minver <= ver and ver < maxver then
|
||||
print("yes")
|
||||
else
|
||||
print("no")
|
||||
end'`]
|
||||
AS_IF([test "x$_ax_lua_good_version" = "xyes"],
|
||||
[$4], [$5])
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR)
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
|
||||
[
|
||||
dnl Get the script or module directory by querying the Lua interpreter,
|
||||
dnl filtering on the given prefix, and selecting the shallowest path. If no
|
||||
dnl path is found matching the prefix, the result will be an empty string.
|
||||
dnl The third argument determines the type of search, it can be 'script' or
|
||||
dnl 'module'. Supplying 'script' will perform the search with package.path
|
||||
dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
|
||||
dnl LUA_CPATH. This is done for compatibility with Lua 5.0.
|
||||
|
||||
ax_lua_prefixed_path=[`$1 -e '
|
||||
-- get the path based on search type
|
||||
local searchtype = "$3"
|
||||
local paths = ""
|
||||
if searchtype == "script" then
|
||||
paths = (package and package.path) or LUA_PATH
|
||||
elseif searchtype == "module" then
|
||||
paths = (package and package.cpath) or LUA_CPATH
|
||||
end
|
||||
-- search for the prefix
|
||||
local prefix = "'$2'"
|
||||
local minpath = ""
|
||||
local mindepth = 1e9
|
||||
string.gsub(paths, "(@<:@^;@:>@+)",
|
||||
function (path)
|
||||
path = string.gsub(path, "%?.*$", "")
|
||||
path = string.gsub(path, "/@<:@^/@:>@*$", "")
|
||||
if string.find(path, prefix) then
|
||||
local depth = string.len(string.gsub(path, "@<:@^/@:>@", ""))
|
||||
if depth < mindepth then
|
||||
minpath = path
|
||||
mindepth = depth
|
||||
end
|
||||
end
|
||||
end)
|
||||
print(minpath)'`]
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([AX_LUA_HEADERS],
|
||||
[
|
||||
dnl Check for LUA_VERSION.
|
||||
AC_MSG_CHECKING([if LUA_VERSION is defined])
|
||||
AS_IF([test "x$LUA_VERSION" != 'x'],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
|
||||
])
|
||||
|
||||
dnl Make LUA_INCLUDE a precious variable.
|
||||
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
|
||||
|
||||
dnl Some default directories to search.
|
||||
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
|
||||
m4_define_default([_AX_LUA_INCLUDE_LIST],
|
||||
[ /usr/include/lua$LUA_VERSION \
|
||||
/usr/include/lua-$LUA_VERSION \
|
||||
/usr/include/lua/$LUA_VERSION \
|
||||
/usr/include/lua$LUA_SHORT_VERSION \
|
||||
/usr/local/include/lua$LUA_VERSION \
|
||||
/usr/local/include/lua-$LUA_VERSION \
|
||||
/usr/local/include/lua/$LUA_VERSION \
|
||||
/usr/local/include/lua$LUA_SHORT_VERSION \
|
||||
])
|
||||
|
||||
dnl Try to find the headers.
|
||||
_ax_lua_saved_cppflags=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
|
||||
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
|
||||
CPPFLAGS=$_ax_lua_saved_cppflags
|
||||
|
||||
dnl Try some other directories if LUA_INCLUDE was not set.
|
||||
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
|
||||
test "x$ac_cv_header_lua_h" != 'xyes'],
|
||||
[ dnl Try some common include paths.
|
||||
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
|
||||
test ! -d "$_ax_include_path" && continue
|
||||
|
||||
AC_MSG_CHECKING([for Lua headers in])
|
||||
AC_MSG_RESULT([$_ax_include_path])
|
||||
|
||||
AS_UNSET([ac_cv_header_lua_h])
|
||||
AS_UNSET([ac_cv_header_lualib_h])
|
||||
AS_UNSET([ac_cv_header_lauxlib_h])
|
||||
AS_UNSET([ac_cv_header_luaconf_h])
|
||||
|
||||
_ax_lua_saved_cppflags=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
|
||||
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
|
||||
CPPFLAGS=$_ax_lua_saved_cppflags
|
||||
|
||||
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
|
||||
[ LUA_INCLUDE="-I$_ax_include_path"
|
||||
break
|
||||
])
|
||||
done
|
||||
])
|
||||
|
||||
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
|
||||
[ dnl Make a program to print LUA_VERSION defined in the header.
|
||||
dnl TODO It would be really nice if we could do this without compiling a
|
||||
dnl program, then it would work when cross compiling. But I'm not sure how
|
||||
dnl to do this reliably. For now, assume versions match when cross compiling.
|
||||
|
||||
AS_IF([test "x$cross_compiling" != 'xyes'],
|
||||
[ AC_CACHE_CHECK([for Lua header version],
|
||||
[ax_cv_lua_header_version],
|
||||
[ _ax_lua_saved_cppflags=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
|
||||
AC_COMPUTE_INT(ax_cv_lua_header_version_major,[LUA_VERSION_NUM/100],[AC_INCLUDES_DEFAULT
|
||||
#include <lua.h>
|
||||
],[ax_cv_lua_header_version_major=unknown])
|
||||
AC_COMPUTE_INT(ax_cv_lua_header_version_minor,[LUA_VERSION_NUM%100],[AC_INCLUDES_DEFAULT
|
||||
#include <lua.h>
|
||||
],[ax_cv_lua_header_version_minor=unknown])
|
||||
AS_IF([test "x$ax_cv_lua_header_version_major" = xunknown || test "x$ax_cv_lua_header_version_minor" = xunknown],[
|
||||
ax_cv_lua_header_version=unknown
|
||||
],[
|
||||
ax_cv_lua_header_version="$ax_cv_lua_header_version_major.$ax_cv_lua_header_version_minor"
|
||||
])
|
||||
CPPFLAGS=$_ax_lua_saved_cppflags
|
||||
])
|
||||
|
||||
dnl Compare this to the previously found LUA_VERSION.
|
||||
AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
|
||||
AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
|
||||
[ AC_MSG_RESULT([yes])
|
||||
ax_header_version_match='yes'
|
||||
],
|
||||
[ AC_MSG_RESULT([no])
|
||||
ax_header_version_match='no'
|
||||
])
|
||||
],
|
||||
[ AC_MSG_WARN([cross compiling so assuming header version number matches])
|
||||
ax_header_version_match='yes'
|
||||
])
|
||||
])
|
||||
|
||||
dnl Was LUA_INCLUDE specified?
|
||||
AS_IF([test "x$ax_header_version_match" != 'xyes' &&
|
||||
test "x$LUA_INCLUDE" != 'x'],
|
||||
[AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
|
||||
|
||||
dnl Test the final result and run user code.
|
||||
AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
|
||||
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
|
||||
])
|
||||
|
||||
dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
|
||||
AC_DEFUN([AX_LUA_HEADERS_VERSION],
|
||||
[
|
||||
AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]])
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([AX_LUA_LIBS],
|
||||
[
|
||||
dnl TODO Should this macro also check various -L flags?
|
||||
|
||||
dnl Check for LUA_VERSION.
|
||||
AC_MSG_CHECKING([if LUA_VERSION is defined])
|
||||
AS_IF([test "x$LUA_VERSION" != 'x'],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
|
||||
])
|
||||
|
||||
dnl Make LUA_LIB a precious variable.
|
||||
AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
|
||||
|
||||
AS_IF([test "x$LUA_LIB" != 'x'],
|
||||
[ dnl Check that LUA_LIBS works.
|
||||
_ax_lua_saved_libs=$LIBS
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
AC_SEARCH_LIBS([lua_load], [],
|
||||
[_ax_found_lua_libs='yes'],
|
||||
[_ax_found_lua_libs='no'])
|
||||
LIBS=$_ax_lua_saved_libs
|
||||
|
||||
dnl Check the result.
|
||||
AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
|
||||
[AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
|
||||
],
|
||||
[ dnl First search for extra libs.
|
||||
_ax_lua_extra_libs=''
|
||||
|
||||
_ax_lua_saved_libs=$LIBS
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
AC_SEARCH_LIBS([exp], [m])
|
||||
AC_SEARCH_LIBS([dlopen], [dl])
|
||||
LIBS=$_ax_lua_saved_libs
|
||||
|
||||
AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
|
||||
test "x$ac_cv_search_exp" != 'xnone required'],
|
||||
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
|
||||
|
||||
AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
|
||||
test "x$ac_cv_search_dlopen" != 'xnone required'],
|
||||
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
|
||||
|
||||
dnl Try to find the Lua libs.
|
||||
_ax_lua_saved_libs=$LIBS
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
AC_SEARCH_LIBS([lua_load],
|
||||
[ lua$LUA_VERSION \
|
||||
lua$LUA_SHORT_VERSION \
|
||||
lua-$LUA_VERSION \
|
||||
lua-$LUA_SHORT_VERSION \
|
||||
lua \
|
||||
],
|
||||
[_ax_found_lua_libs='yes'],
|
||||
[_ax_found_lua_libs='no'],
|
||||
[$_ax_lua_extra_libs])
|
||||
LIBS=$_ax_lua_saved_libs
|
||||
|
||||
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
|
||||
test "x$ac_cv_search_lua_load" != 'xnone required'],
|
||||
[LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
|
||||
])
|
||||
|
||||
dnl Test the result and run user code.
|
||||
AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
|
||||
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
|
||||
])
|
||||
|
||||
|
||||
dnl =========================================================================
|
||||
dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl =========================================================================
|
||||
AC_DEFUN([AX_LUA_READLINE],
|
||||
[
|
||||
AX_LIB_READLINE
|
||||
AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
|
||||
test "x$ac_cv_header_readline_history_h" != 'x'],
|
||||
[ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
|
||||
$1
|
||||
],
|
||||
[$2])
|
||||
])
|
||||
Reference in New Issue
Block a user