Page MenuHomeFreeBSD

D26756.diff
No OneTemporary

D26756.diff

diff --git a/lib/flua/libjail/jail.3lua b/lib/flua/libjail/jail.3lua
--- a/lib/flua/libjail/jail.3lua
+++ b/lib/flua/libjail/jail.3lua
@@ -32,6 +32,7 @@
.Sh NAME
.Nm getid ,
.Nm getname ,
+.Nm list ,
.Nm allparams ,
.Nm getparams ,
.Nm setparams ,
@@ -50,6 +51,7 @@
.It Dv jid, err = jail.getid(name)
.It Dv name, err = jail.getname(jid)
.It Dv params, err = jail.allparams()
+.It Dv iter, jail_obj = jail.list([params])
.It Dv jid, res = jail.getparams(jid|name, params [, flags ] )
.It Dv jid, err = jail.setparams(jid|name, params, flags )
.It Dv jail.CREATE
@@ -79,6 +81,21 @@
Get the name of a jail as a string for the given
.Fa jid
.Pq an integer .
+.It Dv iter, jail_obj = jail.list([params])
+Returns an iterator over running jails on the system.
+.Dv params
+is a list of parameters to fetch for each jail as we iterate.
+.Dv jid
+and
+.Dv name
+will always be returned, and may be omitted from
+.Dv params .
+Additionally,
+.Dv params
+may be omitted or an empty table, but not nil.
+.Pp
+See
+.Sx EXAMPLES .
.It Dv params, err = jail.allparams()
Get a list of all supported parameter names
.Pq as strings .
@@ -167,6 +184,10 @@
with parameter name strings as keys and strings for values on success, or
.Dv nil
and an error message string if an error occurred.
+.Pp
+The
+.Fn list
+function returns an iterator over the list of running jails.
.Sh EXAMPLES
Set the hostname of jail
.Dq foo
@@ -193,6 +214,32 @@
end
print(res["host.hostname"])
.Ed
+.Pp
+Iterate over jails on the system:
+.Bd -literal -offset indent
+local jail = require('jail')
+
+-- Recommended: just loop over it
+for jparams in jail.list() do
+ print(jparams["jid"] .. " = " .. jparams["name"])
+end
+
+-- Request path and hostname, too
+for jparams in jail.list({"path", "host.hostname"}) do
+ print(jparams["host.hostname"] .. " mounted at " .. jparams["path"])
+end
+
+-- Raw iteration protocol
+local iter, jail_obj = jail.list()
+
+-- Request the first params
+local jparams = jail_obj:next()
+while jparams do
+ print(jparams["jid"] .. " = " .. jparams["name"])
+ -- Subsequent calls may return nil
+ jparams = jail_obj:next()
+end
+.Ed
.Sh SEE ALSO
.Xr jail 2 ,
.Xr jail 3 ,
diff --git a/lib/flua/libjail/lua_jail.c b/lib/flua/libjail/lua_jail.c
--- a/lib/flua/libjail/lua_jail.c
+++ b/lib/flua/libjail/lua_jail.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
+ * Copyright (c) 2020, Kyle Evans <kevans@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,6 +35,7 @@
#include <sys/jail.h>
#include <errno.h>
#include <jail.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -41,8 +43,222 @@
#include <lauxlib.h>
#include <lualib.h>
+#define JAIL_METATABLE "jail iterator metatable"
+
+/*
+ * Taken from RhodiumToad's lspawn implementation, let static analyzers make
+ * better decisions about the behavior after we raise an error.
+ */
+#if defined(LUA_VERSION_NUM) && defined(LUA_API)
+LUA_API int (lua_error) (lua_State *L) __dead2;
+#endif
+#if defined(LUA_ERRFILE) && defined(LUALIB_API)
+LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg) __dead2;
+LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname) __dead2;
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...) __dead2;
+#endif
+
int luaopen_jail(lua_State *);
+typedef bool (*getparam_filter)(const char *, void *);
+
+static void getparam_table(lua_State *L, int paramindex,
+ struct jailparam *params, size_t paramoff, size_t *params_countp,
+ getparam_filter keyfilt, void *udata);
+
+struct l_jail_iter {
+ struct jailparam *params;
+ size_t params_count;
+ int jid;
+};
+
+static bool
+l_jail_filter(const char *param_name, void *data __unused)
+{
+
+ /*
+ * Allowing lastjid will mess up our iteration over all jails on the
+ * system, as this is a special paramter that indicates where the search
+ * starts from. We'll always add jid and name, so just silently remove
+ * these.
+ */
+ return (strcmp(param_name, "lastjid") != 0 &&
+ strcmp(param_name, "jid") != 0 &&
+ strcmp(param_name, "name") != 0);
+}
+
+static int
+l_jail_iter_next(lua_State *L)
+{
+ struct l_jail_iter *iter, **iterp;
+ struct jailparam *jp;
+ int serrno;
+
+ iterp = (struct l_jail_iter **)luaL_checkudata(L, 1, JAIL_METATABLE);
+ iter = *iterp;
+ luaL_argcheck(L, iter != NULL, 1, "closed jail iterator");
+
+ jp = iter->params;
+ /* Populate lastjid; we must keep it in params[0] for our sake. */
+ if (jailparam_import_raw(&jp[0], &iter->jid, sizeof(iter->jid))) {
+ jailparam_free(jp, iter->params_count);
+ free(jp);
+ free(iter);
+ *iterp = NULL;
+ return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg));
+ }
+
+ /* The list of requested params was populated back in l_list(). */
+ iter->jid = jailparam_get(jp, iter->params_count, 0);
+ if (iter->jid == -1) {
+ /*
+ * We probably got an ENOENT to signify the end of the jail
+ * listing, but just in case we didn't; stash it off and start
+ * cleaning up. We'll handle non-ENOENT errors later.
+ */
+ serrno = errno;
+ jailparam_free(jp, iter->params_count);
+ free(iter->params);
+ free(iter);
+ *iterp = NULL;
+ if (serrno != ENOENT)
+ return (luaL_error(L, "jailparam_get: %s",
+ strerror(serrno)));
+ return (0);
+ }
+
+ /*
+ * Finally, we'll fill in the return table with whatever parameters the
+ * user requested, in addition to the ones we forced with exception to
+ * lastjid.
+ */
+ lua_newtable(L);
+ for (size_t i = 0; i < iter->params_count; ++i) {
+ char *value;
+
+ jp = &iter->params[i];
+ if (strcmp(jp->jp_name, "lastjid") == 0)
+ continue;
+ value = jailparam_export(jp);
+ lua_pushstring(L, value);
+ lua_setfield(L, -2, jp->jp_name);
+ free(value);
+ }
+
+ return (1);
+}
+
+static int
+l_jail_iter_close(lua_State *L)
+{
+ struct l_jail_iter *iter, **iterp;
+
+ /*
+ * Since we're using this as the __gc method as well, there's a good
+ * chance that it's already been cleaned up by iterating to the end of
+ * the list.
+ */
+ iterp = (struct l_jail_iter **)lua_touserdata(L, 1);
+ iter = *iterp;
+ if (iter == NULL)
+ return (0);
+
+ jailparam_free(iter->params, iter->params_count);
+ free(iter->params);
+ free(iter);
+ *iterp = NULL;
+ return (0);
+}
+
+static int
+l_list(lua_State *L)
+{
+ struct l_jail_iter *iter;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs >= 1)
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ iter = malloc(sizeof(*iter));
+ if (iter == NULL)
+ return (luaL_error(L, "malloc: %s", strerror(errno)));
+
+ /*
+ * lastjid, jid, name + length of the table. This may be too much if
+ * we have duplicated one of those fixed parameters.
+ */
+ iter->params_count = 3 + (nargs != 0 ? lua_rawlen(L, 1) : 0);
+ iter->params = malloc(iter->params_count * sizeof(*iter->params));
+ if (iter->params == NULL) {
+ free(iter);
+ return (luaL_error(L, "malloc params: %s", strerror(errno)));
+ }
+
+ /* The :next() method will populate lastjid before jail_getparam(). */
+ if (jailparam_init(&iter->params[0], "lastjid") == -1) {
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s", jail_errmsg));
+ }
+ /* These two will get populated by jail_getparam(). */
+ if (jailparam_init(&iter->params[1], "jid") == -1) {
+ jailparam_free(iter->params, 1);
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ if (jailparam_init(&iter->params[2], "name") == -1) {
+ jailparam_free(iter->params, 2);
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+
+ /*
+ * We only need to process additional arguments if we were given any.
+ * That is, we don't descend into getparam_table if we're passed nothing
+ * or an empty table.
+ */
+ iter->jid = 0;
+ if (iter->params_count != 3)
+ getparam_table(L, 1, iter->params, 2, &iter->params_count,
+ l_jail_filter, NULL);
+
+ /*
+ * Part of the iterator magic. We give it an iterator function with a
+ * metatable defining next() and close() that can be used for manual
+ * iteration. iter->jid is how we track which jail we last iterated, to
+ * be supplied as "lastjid".
+ */
+ lua_pushcfunction(L, l_jail_iter_next);
+ *(struct l_jail_iter **)lua_newuserdata(L,
+ sizeof(struct l_jail_iter **)) = iter;
+ luaL_getmetatable(L, JAIL_METATABLE);
+ lua_setmetatable(L, -2);
+ return (2);
+}
+
+static void
+register_jail_metatable(lua_State *L)
+{
+ luaL_newmetatable(L, JAIL_METATABLE);
+ lua_newtable(L);
+ lua_pushcfunction(L, l_jail_iter_next);
+ lua_setfield(L, -2, "next");
+ lua_pushcfunction(L, l_jail_iter_close);
+ lua_setfield(L, -2, "close");
+
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, l_jail_iter_close);
+ lua_setfield(L, -2, "__gc");
+
+ lua_pop(L, 1);
+}
+
static int
l_getid(lua_State *L)
{
@@ -100,12 +316,71 @@
return (1);
}
+static void
+getparam_table(lua_State *L, int paramindex, struct jailparam *params,
+ size_t params_off, size_t *params_countp, getparam_filter keyfilt,
+ void *udata)
+{
+ size_t params_count;
+ int skipped;
+
+ params_count = *params_countp;
+ skipped = 0;
+ for (size_t i = 1 + params_off; i < params_count; ++i) {
+ const char *param_name;
+
+ lua_rawgeti(L, -1, i - params_off);
+ param_name = lua_tostring(L, -1);
+ if (param_name == NULL) {
+ jailparam_free(params, i - skipped);
+ free(params);
+ luaL_argerror(L, paramindex,
+ "param names must be strings");
+ }
+ lua_pop(L, 1);
+ if (keyfilt != NULL && !keyfilt(param_name, udata)) {
+ ++skipped;
+ continue;
+ }
+ if (jailparam_init(&params[i - skipped], param_name) == -1) {
+ jailparam_free(params, i - skipped);
+ free(params);
+ luaL_error(L, "jailparam_init: %s", jail_errmsg);
+ }
+ }
+ *params_countp -= skipped;
+}
+
+struct getparams_filter_args {
+ int filter_type;
+};
+
+static bool
+l_getparams_filter(const char *param_name, void *udata)
+{
+ struct getparams_filter_args *gpa;
+
+ gpa = udata;
+
+ /* Skip name or jid, whichever was given. */
+ if (gpa->filter_type == LUA_TSTRING) {
+ if (strcmp(param_name, "name") == 0)
+ return (false);
+ } else /* type == LUA_TNUMBER */ {
+ if (strcmp(param_name, "jid") == 0)
+ return (false);
+ }
+
+ return (true);
+}
+
static int
l_getparams(lua_State *L)
{
const char *name;
struct jailparam *params;
- size_t params_count, skipped;
+ size_t params_count;
+ struct getparams_filter_args gpa;
int flags, jid, type;
type = lua_type(L, 1);
@@ -154,40 +429,8 @@
/*
* Set the remaining param names being requested.
*/
-
- skipped = 0;
- for (size_t i = 1; i < params_count; ++i) {
- const char *param_name;
-
- lua_rawgeti(L, -1, i);
- param_name = lua_tostring(L, -1);
- if (param_name == NULL) {
- jailparam_free(params, i - skipped);
- free(params);
- return (luaL_argerror(L, 2,
- "param names must be strings"));
- }
- lua_pop(L, 1);
- /* Skip name or jid, whichever was given. */
- if (type == LUA_TSTRING) {
- if (strcmp(param_name, "name") == 0) {
- ++skipped;
- continue;
- }
- } else /* type == LUA_TNUMBER */ {
- if (strcmp(param_name, "jid") == 0) {
- ++skipped;
- continue;
- }
- }
- if (jailparam_init(&params[i - skipped], param_name) == -1) {
- jailparam_free(params, i - skipped);
- free(params);
- return (luaL_error(L, "jailparam_init: %s",
- jail_errmsg));
- }
- }
- params_count -= skipped;
+ gpa.filter_type = type;
+ getparam_table(L, 2, params, 0, &params_count, l_getparams_filter, &gpa);
/*
* Get the values and convert to a table.
@@ -366,6 +609,13 @@
* or nil, error (string) on error
*/
{"setparams", l_setparams},
+ /** Get a list of jail parameters for running jails on the system.
+ * @param params optional list of parameter names (table of
+ * strings)
+ * @return iterator (function), jail_obj (object) with next and
+ * close methods
+ */
+ {"list", l_list},
{NULL, NULL}
};
@@ -385,5 +635,7 @@
lua_pushinteger(L, JAIL_DYING);
lua_setfield(L, -2, "DYING");
+ register_jail_metatable(L);
+
return (1);
}
diff --git a/share/examples/flua/libjail.lua b/share/examples/flua/libjail.lua
--- a/share/examples/flua/libjail.lua
+++ b/share/examples/flua/libjail.lua
@@ -35,10 +35,23 @@
name = "demo"
--- Create a persistent jail named "demo" with all other parameters default.
-jid, err = jail.setparams(name, {persist = "true"}, jail.CREATE)
-if not jid then
- error(err)
+local has_demo = false
+
+-- Make sure we don't have a demo jail to start with; "jid" and "name" are
+-- always present.
+for jparams in jail.list() do
+ if jparams["name"] == name then
+ has_demo = true
+ break
+ end
+end
+
+if not has_demo then
+ -- Create a persistent jail named "demo" with all other parameters default.
+ jid, err = jail.setparams(name, {persist = "true"}, jail.CREATE)
+ if not jid then
+ error(err)
+ end
end
-- Get a list of all known jail parameter names.
@@ -53,8 +66,42 @@
-- Display the jail's parameters as a pretty-printed JSON object.
print(ucl.to_json(res))
+-- Confirm that we still have it for now.
+has_demo = false
+for jparams in jail.list() do
+ if jparams["name"] == name then
+ has_demo = true
+ break
+ end
+end
+
+if not has_demo then
+ print("demo does not exist")
+end
+
-- Update the "persist" parameter to "false" to remove the jail.
jid, err = jail.setparams(name, {persist = "false"}, jail.UPDATE)
if not jid then
error(err)
end
+
+-- Verify that the jail is no longer on the system.
+local is_persistent = false
+has_demo = false
+for jparams in jail.list({"persist"}) do
+ if jparams["name"] == name then
+ has_demo = true
+ jid = jparams["jid"]
+ is_persistent = jparams["persist"] ~= "false"
+ end
+end
+
+-- In fact, it does remain until this process ends -- c'est la vie.
+if has_demo then
+ io.write("demo still exists, jid " .. jid .. ", ")
+ if is_persistent then
+ io.write("persistent\n")
+ else
+ io.write("not persistent\n")
+ end
+end

File Metadata

Mime Type
text/plain
Expires
Fri, May 2, 5:02 PM (12 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17908522
Default Alt Text
D26756.diff (13 KB)

Event Timeline