Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F116019691
D26756.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D26756.diff
View Options
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(¶ms[i - skipped], param_name) == -1) {
+ jailparam_free(params, i - skipped);
+ free(params);
+ luaL_error(L, "jailparam_init: %s", jail_errmsg);
+ }
+ }
+ *params_countp -= skipped;
+}
+
+struct getparams_filter_args {
+ int filter_type;
+};
+
+static bool
+l_getparams_filter(const char *param_name, void *udata)
+{
+ struct getparams_filter_args *gpa;
+
+ gpa = udata;
+
+ /* Skip name or jid, whichever was given. */
+ if (gpa->filter_type == LUA_TSTRING) {
+ if (strcmp(param_name, "name") == 0)
+ return (false);
+ } else /* type == LUA_TNUMBER */ {
+ if (strcmp(param_name, "jid") == 0)
+ return (false);
+ }
+
+ return (true);
+}
+
static int
l_getparams(lua_State *L)
{
const char *name;
struct jailparam *params;
- size_t params_count, 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(¶ms[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, ¶ms_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
Details
Attached
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)
Attached To
Mode
D26756: jail(3lua): add a jail.list() method
Attached
Detach File
Event Timeline
Log In to Comment