@klarasystems.com> AuthorDate: 2025-12-17 17:00:47 +0000 Commit: Kyle Evans CommitDate: 2025-12-17 17:00:47 +0000 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 Co-authored-by: Kyle Evans 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 #include +#include #include #include #include @@ -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); }