Skip site navigation (1)Skip section navigation (2)


index | | raw e-mail

    bectl: log modifying functions to zpool history
    
    Modeled directly after the method used by the zfs/zpool commands: flag
    commands with a "please log me" flag, and when there, reconstruct the
    command line. On success, call the library function to add it to the
    log.
    
    (Majority of the change by Rob; minor edits by kevans@)
    
    Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
    Co-authored-by: Kyle Evans <kevans@FreeBSD.org>
    Sponsored by:   Modirum MDPay
    Sponsored by:   Klara, Inc.
---
 lib/libbe/be.c     | 12 +++++++++
 lib/libbe/be.h     |  2 ++
 lib/libbe/libbe.3  | 16 ++++++++++--
 sbin/bectl/bectl.c | 73 ++++++++++++++++++++++++++++++++++++++++++------------
 4 files changed, 85 insertions(+), 18 deletions(-)

diff --git a/lib/libbe/be.c b/lib/libbe/be.c
index 613235d5e908..4a7c2e43b2c1 100644
--- a/lib/libbe/be.c
+++ b/lib/libbe/be.c
@@ -1343,3 +1343,15 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
 
 	return (BE_ERR_SUCCESS);
 }
+
+int
+be_log_history(libbe_handle_t *lbh, const char *message)
+{
+	int err;
+
+	err = zpool_log_history(lbh->lzh, message);
+	if (err)
+		return (set_error(lbh, BE_ERR_UNKNOWN));
+
+	return (BE_ERR_SUCCESS);
+}
diff --git a/lib/libbe/be.h b/lib/libbe/be.h
index 01ee94fd03ca..d3f47c0604fe 100644
--- a/lib/libbe/be.h
+++ b/lib/libbe/be.h
@@ -107,6 +107,8 @@ int be_exists(libbe_handle_t *, const char *);
 int be_export(libbe_handle_t *, const char *, int fd);
 int be_import(libbe_handle_t *, const char *, int fd);
 
+int be_log_history(libbe_handle_t *, const char *);
+
 #if SOON
 int be_add_child(libbe_handle_t *, const char *, bool);
 #endif
diff --git a/lib/libbe/libbe.3 b/lib/libbe/libbe.3
index 3b10711dd0f9..4331713e9227 100644
--- a/lib/libbe/libbe.3
+++ b/lib/libbe/libbe.3
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd April 20, 2025
+.Dd December 11, 2025
 .Dt LIBBE 3
 .Os
 .Sh NAME
@@ -144,6 +144,9 @@
 .Pp
 .Ft void
 .Fn be_prop_list_free "nvlist_t *prop_list"
+.Pp
+.Ft int
+.Fn be_log_history "libbe_handle_t *hdl" "const char *message"
 .Sh DESCRIPTION
 .Nm
 interfaces with libzfs to provide a set of functions for various operations
@@ -536,6 +539,14 @@ exactly as specified by
 The
 .Fn be_prop_list_free
 function will free the property list.
+.Pp
+The
+.Fn be_log_history
+function will log the given
+.Fa message
+to the zpool history, which can be later retrieved using the
+.Xr zpool-history 8
+command.
 .Sh DIAGNOSTICS
 Upon error, one of the following values will be returned:
 .Bl -bullet -offset indent -compact
@@ -583,7 +594,8 @@ BE_ERR_UNKNOWN
 BE_ERR_INVORIGIN
 .El
 .Sh SEE ALSO
-.Xr bectl 8
+.Xr bectl 8 ,
+.Xr zpool-history 8
 .Sh HISTORY
 .Xr bectl 8
 and
diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c
index 95715b34336b..28483dae17b2 100644
--- a/sbin/bectl/bectl.c
+++ b/sbin/bectl/bectl.c
@@ -6,6 +6,7 @@
 
 #include <sys/param.h>
 #include <sys/mount.h>
+#include <err.h>
 #include <errno.h>
 #include <libutil.h>
 #include <stdbool.h>
@@ -76,27 +77,28 @@ struct command_map_entry {
 	int (*fn)(int argc, char *argv[]);
 	/* True if libbe_print_on_error should be disabled */
 	bool silent;
+	bool save_history;
 };
 
 static struct command_map_entry command_map[] =
 {
-	{ "activate", bectl_cmd_activate,false   },
-	{ "create",   bectl_cmd_create,  false   },
-	{ "destroy",  bectl_cmd_destroy, false   },
-	{ "export",   bectl_cmd_export,  false   },
-	{ "import",   bectl_cmd_import,  false   },
+	{ "activate", bectl_cmd_activate,false, true    },
+	{ "create",   bectl_cmd_create,  false, true    },
+	{ "destroy",  bectl_cmd_destroy, false, true    },
+	{ "export",   bectl_cmd_export,  false, true    },
+	{ "import",   bectl_cmd_import,  false, true    },
 #if SOON
-	{ "add",      bectl_cmd_add,     false   },
+	{ "add",      bectl_cmd_add,     false, true    },
 #endif
-	{ "jail",     bectl_cmd_jail,    false   },
-	{ "list",     bectl_cmd_list,    false   },
-	{ "mount",    bectl_cmd_mount,   false   },
-	{ "rename",   bectl_cmd_rename,  false   },
-	{ "unjail",   bectl_cmd_unjail,  false   },
-	{ "ujail",    bectl_cmd_unjail,  false   },
-	{ "unmount",  bectl_cmd_unmount, false   },
-	{ "umount",   bectl_cmd_unmount, false   },
-	{ "check",    bectl_cmd_check,   true    },
+	{ "jail",     bectl_cmd_jail,    false, false   },
+	{ "list",     bectl_cmd_list,    false, false   },
+	{ "mount",    bectl_cmd_mount,   false, false   },
+	{ "rename",   bectl_cmd_rename,  false, true    },
+	{ "unjail",   bectl_cmd_unjail,  false, false   },
+	{ "ujail",    bectl_cmd_unjail,  false, false   },
+	{ "unmount",  bectl_cmd_unmount, false, false   },
+	{ "umount",   bectl_cmd_unmount, false, false   },
+	{ "check",    bectl_cmd_check,   true,  false   },
 };
 
 static struct command_map_entry *
@@ -523,12 +525,42 @@ bectl_cmd_check(int argc, char *argv[] __unused)
 	return (0);
 }
 
+static char *
+save_cmdline(int argc, char *argv[])
+{
+	char *cmdline, *basename, *p;
+	int len, n, i;
+
+	len = MAXPATHLEN * 2 + 1; /* HIS_MAX_RECORD_LEN from zfs.h */
+	cmdline = p = malloc(len);
+	if (cmdline == NULL)
+		err(2, "malloc");
+
+	basename = strrchr(argv[0], '/');
+	if (basename == NULL)
+		basename = argv[0];
+	else
+		basename++;
+
+	n = strlcpy(p, basename, len);
+	for (i = 1; i < argc; i++) {
+		if (n >= len)
+			break;
+		p += n;
+		*p++ = ' ';
+		len -= (n + 1);
+		n = strlcpy(p, argv[i], len);
+	}
+
+	return (cmdline);
+}
+
 int
 main(int argc, char *argv[])
 {
 	struct command_map_entry *cmd;
 	const char *command;
-	char *root = NULL;
+	char *root = NULL, *cmdline = NULL;
 	int opt, rc;
 
 	while ((opt = getopt(argc, argv, "hr:")) != -1) {
@@ -565,10 +597,19 @@ main(int argc, char *argv[])
 		return (-1);
 	}
 
+	if (cmd->save_history)
+		cmdline = save_cmdline(argc+optind, argv-optind);
+
 	libbe_print_on_error(be, !cmd->silent);
 
 	rc = cmd->fn(argc, argv);
 
+	if (cmd->save_history) {
+		if (rc == 0)
+			be_log_history(be, cmdline);
+		free(cmdline);
+	}
+
 	libbe_close(be);
 	return (rc);
 }


help