The branch main has been updated by kevans:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=6a7647eccd3ef35189c63a61b0ec8865fd559839

commit 6a7647eccd3ef35189c63a61b0ec8865fd559839
Author:     Kyle Evans <kev...@freebsd.org>
AuthorDate: 2020-10-13 02:11:14 +0000
Commit:     Kyle Evans <kev...@freebsd.org>
CommitDate: 2021-09-30 21:30:57 +0000

    jail(3lua): add a jail.list() method
    
    This is implemented as an iterator, reusing parts of the earlier logic
    to populate jailparams from a passed in table.
    
    The user may request any number of parameters to pull in while we're
    searching, but we'll force jid and name to appear at a minimum.
    
    Reviewed by:    freqlabs
    Differential Revision:  https://reviews.freebsd.org/D26756
---
 lib/flua/libjail/jail.3lua      |  47 ++++++
 lib/flua/libjail/lua_jail.c     | 322 +++++++++++++++++++++++++++++++++++-----
 share/examples/flua/libjail.lua |  55 ++++++-
 3 files changed, 385 insertions(+), 39 deletions(-)

diff --git a/lib/flua/libjail/jail.3lua b/lib/flua/libjail/jail.3lua
index fb54b94844f5..aa1e0ec49616 100644
--- 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 @@ local jail = require('jail')
 .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 @@ is the name of a jail or a jid in the form of a string.
 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 @@ function returns a jail identifier integer and a table of 
jail parameters
 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 @@ if not jid then
 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
index b66c60b43bc8..7bb0e13cceea 100644
--- 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 <freql...@freebsd.org>
+ * Copyright (c) 2020, Kyle Evans <kev...@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 @@ __FBSDID("$FreeBSD$");
 #include <sys/jail.h>
 #include <errno.h>
 #include <jail.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -41,8 +43,222 @@ __FBSDID("$FreeBSD$");
 #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 @@ l_allparams(lua_State *L)
        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 @@ l_getparams(lua_State *L)
        /*
         * 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 @@ static const struct luaL_Reg l_jail[] = {
         *              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 @@ luaopen_jail(lua_State *L)
        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
index 3ff878460d2f..1761f5c86b24 100644
--- a/share/examples/flua/libjail.lua
+++ b/share/examples/flua/libjail.lua
@@ -35,10 +35,23 @@ ucl = require("ucl")
 
 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 @@ end
 -- 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
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to