From 3bc59a52068ed52e063d99ebe2492eae6f6f92c0 Mon Sep 17 00:00:00 2001 From: Huijing Hei Date: Thu, 2 Jun 2022 15:30:20 +0800 Subject: [PATCH] RFE: Add a hidden option to `ostree admin kargs edit-in-place` to update all existing deployments in place Example: $ sudo ostree admin kargs edit-in-place --append-if-missing=rw See https://github.com/ostreedev/ostree/issues/2617 This will not add duplicate key, if there is `TESTARG=VAL1` in the kernel arguments, `--append-if-missing=TESTARG=VAL2` will be ignored. --- Makefile-libostree.am | 3 + Makefile-ostree.am | 3 + Makefile-tests.am | 1 + apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 4 + src/libostree/ostree-kernel-args.c | 25 ++++ src/libostree/ostree-kernel-args.h | 4 + src/ostree/ot-admin-builtin-kargs.c | 134 ++++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + .../ot-admin-kargs-builtin-edit-in-place.c | 80 +++++++++++ src/ostree/ot-admin-kargs-builtins.h | 35 +++++ src/ostree/ot-builtin-admin.c | 3 + tests/test-admin-kargs.sh | 44 ++++++ 13 files changed, 338 insertions(+) create mode 100644 src/ostree/ot-admin-builtin-kargs.c create mode 100644 src/ostree/ot-admin-kargs-builtin-edit-in-place.c create mode 100644 src/ostree/ot-admin-kargs-builtins.h create mode 100755 tests/test-admin-kargs.sh diff --git a/Makefile-libostree.am b/Makefile-libostree.am index f93f712aa1..dd7ed8df7e 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -174,6 +174,9 @@ symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym #if BUILDOPT_IS_DEVEL_BUILD #symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym #endif +if BUILDOPT_IS_DEVEL_BUILD +symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym +endif # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html wl_versionscript_arg = -Wl,--version-script= diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 0fe2c5f86c..fb377075eb 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -73,6 +73,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-boot-complete.c \ src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-instutil.c \ + src/ostree/ot-admin-builtin-kargs.c \ src/ostree/ot-admin-builtin-cleanup.c \ src/ostree/ot-admin-builtin-os-init.c \ src/ostree/ot-admin-builtin-set-origin.c \ @@ -88,6 +89,8 @@ ostree_SOURCES += \ src/ostree/ot-admin-instutil-builtins.h \ src/ostree/ot-admin-functions.h \ src/ostree/ot-admin-functions.c \ + src/ostree/ot-admin-kargs-builtins.h \ + src/ostree/ot-admin-kargs-builtin-edit-in-place.c \ $(NULL) # Remote subcommand diff --git a/Makefile-tests.am b/Makefile-tests.am index 29de6c7aac..c87893ee28 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -114,6 +114,7 @@ _installed_or_uninstalled_test_scripts = \ tests/test-admin-pull-deploy-split.sh \ tests/test-admin-locking.sh \ tests/test-admin-deploy-clean.sh \ + tests/test-admin-kargs.sh \ tests/test-reset-nonlinear.sh \ tests/test-oldstyle-partial.sh \ tests/test-delta.sh \ diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index adf52557bb..5af8e68773 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -727,6 +727,7 @@ ostree_kernel_args_replace_argv ostree_kernel_args_append ostree_kernel_args_append_argv ostree_kernel_args_append_argv_filtered +ostree_kernel_args_append_if_missing ostree_kernel_args_new_replace ostree_kernel_args_delete ostree_kernel_args_delete_key_entry diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index eef5cba0b7..54945ecaf6 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -20,6 +20,10 @@ - uncomment the include in Makefile-libostree.am */ +LIBOSTREE_2022.5 { +global: + ostree_kernel_args_append_if_missing; +} LIBOSTREE_2022.4; /* Stub section for the stable release *after* this development one; don't * edit this other than to update the year. This is just a copy/paste diff --git a/src/libostree/ostree-kernel-args.c b/src/libostree/ostree-kernel-args.c index 40f11e9977..08dcf992f1 100644 --- a/src/libostree/ostree-kernel-args.c +++ b/src/libostree/ostree-kernel-args.c @@ -804,3 +804,28 @@ ostree_kernel_args_get_last_value (OstreeKernelArgs *kargs, const char *key) const OstreeKernelArgsEntry *e = entries->pdata[entries->len-1]; return _ostree_kernel_args_entry_get_value (e); } + +/** + * ostree_kernel_args_append_if_missing: + * @kargs: a OstreeKernelArgs instance + * @arg: key or key/value pair to be added + * + * Appends @arg which is in the form of key=value pair to the hash table kargs->table + * (appends to the value list if key is not in the hash table) + * and appends key to kargs->order if it is not in the hash table. + * + * Since: 2022.5 + **/ +void +ostree_kernel_args_append_if_missing (OstreeKernelArgs *kargs, + const char *arg) +{ + g_autofree char *key = g_strdup (arg); + split_keyeq (key); + + // Don't insert a duplicate key. + if (g_hash_table_contains (kargs->table, key)) + return; + + ostree_kernel_args_append (kargs, arg); +} diff --git a/src/libostree/ostree-kernel-args.h b/src/libostree/ostree-kernel-args.h index dde0312fc2..2e1b249a95 100644 --- a/src/libostree/ostree-kernel-args.h +++ b/src/libostree/ostree-kernel-args.h @@ -130,4 +130,8 @@ char **ostree_kernel_args_to_strv (OstreeKernelArgs *kargs); _OSTREE_PUBLIC char *ostree_kernel_args_to_string (OstreeKernelArgs *kargs); +_OSTREE_PUBLIC +void ostree_kernel_args_append_if_missing (OstreeKernelArgs *kargs, + const char *arg); + G_END_DECLS diff --git a/src/ostree/ot-admin-builtin-kargs.c b/src/ostree/ot-admin-builtin-kargs.c new file mode 100644 index 0000000000..4afef29492 --- /dev/null +++ b/src/ostree/ot-admin-builtin-kargs.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Colin Walters + * Copyright (C) 2022 Huijing Hei + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "ot-main.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ot-admin-kargs-builtins.h" +#include "otutil.h" + +#include + +static OstreeCommand admin_kargs_subcommands[] = { + { "edit-in-place", OSTREE_BUILTIN_FLAG_NO_REPO | OSTREE_BUILTIN_FLAG_HIDDEN, + ot_admin_kargs_builtin_edit_in_place, + "Set new kernel command line arguments in place (applies to all deployments by default)" }, + { NULL, 0, NULL, NULL } +}; + +static GOptionContext * +ostree_admin_kargs_option_context_new_with_commands (void) +{ + OstreeCommand *command = admin_kargs_subcommands; + GOptionContext *context = g_option_context_new ("COMMAND"); + + g_autoptr(GString) summary = g_string_new ("Builtin \"admin kargs\" Commands:"); + + while (command->name != NULL) + { + if ((command->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + { + g_string_append_printf (summary, "\n %-24s", command->name); + if (command->description != NULL) + g_string_append_printf (summary, "%s", command->description); + } + + command++; + } + + g_option_context_set_summary (context, summary->str); + + return context; +} + +gboolean +ot_admin_builtin_kargs (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + const char *subcommand_name = NULL; + int in, out; + for (in = 1, out = 1; in < argc; in++, out++) + { + /* The non-option is the command, take it out of the arguments */ + if (argv[in][0] != '-') + { + if (subcommand_name == NULL) + { + subcommand_name = argv[in]; + out--; + continue; + } + } + + else if (g_str_equal (argv[in], "--")) + { + break; + } + + argv[out] = argv[in]; + } + + argc = out; + + OstreeCommand *subcommand = admin_kargs_subcommands; + while (subcommand->name) + { + if (g_strcmp0 (subcommand_name, subcommand->name) == 0) + break; + subcommand++; + } + + if (!subcommand->name) + { + g_autoptr(GOptionContext) context = + ostree_admin_kargs_option_context_new_with_commands (); + + /* This will not return for some options (e.g. --version). */ + if (ostree_admin_option_context_parse (context, NULL, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT, + invocation, NULL, cancellable, error)) + { + if (subcommand_name == NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No \"admin kargs\" subcommand specified"); + } + else + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Unknown \"admin kargs\" subcommand '%s'", subcommand_name); + } + } + + g_autofree char *help = g_option_context_get_help (context, FALSE, NULL); + g_printerr ("%s", help); + return FALSE; + } + + g_autofree char *prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name); + g_set_prgname (prgname); + + OstreeCommandInvocation sub_invocation = { .command = subcommand }; + if (!subcommand->fn (argc, argv, &sub_invocation, cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index 8d9451bec6..8bac1c5625 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -46,6 +46,7 @@ BUILTINPROTO(set_origin); BUILTINPROTO(diff); BUILTINPROTO(switch); BUILTINPROTO(upgrade); +BUILTINPROTO(kargs); #undef BUILTINPROTO diff --git a/src/ostree/ot-admin-kargs-builtin-edit-in-place.c b/src/ostree/ot-admin-kargs-builtin-edit-in-place.c new file mode 100644 index 0000000000..40ada02fc6 --- /dev/null +++ b/src/ostree/ot-admin-kargs-builtin-edit-in-place.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 Colin Walters + * Copyright (C) 2022 Huijing Hei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library. If not, see . + */ + +#include "config.h" + +#include "ot-main.h" +#include "ot-admin-kargs-builtins.h" + +#include "ostree.h" +#include "otutil.h" + +static char **opt_kargs_edit_in_place_append; + +static GOptionEntry options[] = { + { "append-if-missing", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kargs_edit_in_place_append, "Append kernel arguments if they do not exist", "NAME=VALUE" }, + { NULL } +}; + +gboolean +ot_admin_kargs_builtin_edit_in_place (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + g_autoptr(OstreeSysroot) sysroot = NULL; + + g_autoptr(GOptionContext) context = g_option_context_new ("ARGS"); + + if (!ostree_admin_option_context_parse (context, options, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, + invocation, &sysroot, cancellable, error)) + return FALSE; + + g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot); + if (deployments->len == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unable to find a deployment in sysroot"); + return FALSE; + } + + // set kargs for each deployment + for (guint i = 0; i < deployments->len; i++) + { + OstreeDeployment *deployment = deployments->pdata[i]; + OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_from_string (ostree_bootconfig_parser_get (bootconfig, "options")); + + if (opt_kargs_edit_in_place_append) + { + for (char **strviter = opt_kargs_edit_in_place_append; strviter && *strviter; strviter++) + { + const char *arg = *strviter; + ostree_kernel_args_append_if_missing (kargs, arg); + } + } + + g_auto(GStrv) kargs_strv = ostree_kernel_args_to_strv (kargs); + + if (!ostree_sysroot_deployment_set_kargs (sysroot, deployment, + kargs_strv, + cancellable, error)) + return FALSE; + + } + + return TRUE; +} diff --git a/src/ostree/ot-admin-kargs-builtins.h b/src/ostree/ot-admin-kargs-builtins.h new file mode 100644 index 0000000000..f3837c34ce --- /dev/null +++ b/src/ostree/ot-admin-kargs-builtins.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Colin Walters + * Copyright (C) 2022 Huijing Hei + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define BUILTINPROTO(name) gboolean ot_admin_kargs_builtin_ ## name (int argc, char **argv, \ + OstreeCommandInvocation *invocation, \ + GCancellable *cancellable, GError **error) + +BUILTINPROTO(edit_in_place); + +#undef BUILTINPROTO + +G_END_DECLS diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index af09a61416..503fb9a7be 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -76,6 +76,9 @@ static OstreeCommand admin_subcommands[] = { { "upgrade", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_upgrade, "Construct new tree from current origin and deploy it, if it changed" }, + { "kargs", OSTREE_BUILTIN_FLAG_NO_REPO, + ot_admin_builtin_kargs, + "Change kernel arguments" }, { NULL, 0, NULL, NULL } }; diff --git a/tests/test-admin-kargs.sh b/tests/test-admin-kargs.sh new file mode 100755 index 0000000000..afcfc05a04 --- /dev/null +++ b/tests/test-admin-kargs.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Copyright (C) 2011 Colin Walters +# Copyright (C) 2022 Huijing Hei +# +# SPDX-License-Identifier: LGPL-2.0+ +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +# Exports OSTREE_SYSROOT so --sysroot not needed. +setup_os_repository "archive" "syslinux" + +echo "1..2" + +${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmain/x86_64-runtime +${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmain/x86_64-runtime +${CMD_PREFIX} ostree admin kargs edit-in-place --append-if-missing=TESTARG=TESTVALUE --append-if-missing=ARGWITHOUTKEY testos:testos/buildmain/x86_64-runtime + +assert_file_has_content sysroot/boot/loader/entries/ostree-1-testos.conf 'options.*TESTARG=TESTVALUE .*ARGWITHOUTKEY' + +echo "ok kargs edit-in-place (basic)" + +${CMD_PREFIX} ostree admin kargs edit-in-place --append-if-missing=quiet testos:testos/buildmain/x86_64-runtime +assert_not_file_has_content sysroot/boot/loader/entries/ostree-1-testos.conf 'quiet$' + +${CMD_PREFIX} ostree admin kargs edit-in-place --append-if-missing=TESTARG=TESTVALUE testos:testos/buildmain/x86_64-runtime +assert_not_file_has_content sysroot/boot/loader/entries/ostree-1-testos.conf 'TESTARG=TESTVALUE$' + +echo "ok kargs edit-in-place (duplicate)"