diff --git a/Makefile.am b/Makefile.am index d14d4cf4e..698c4f1fb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -219,6 +219,7 @@ EXTRA_DIST += tap-t \ test/sharness.sh \ test/system.conf \ test/test.conf \ + test/test-custom.conf \ test/test-global.conf \ test/test-datadir.conf \ test/test-dummy-handler.conf \ @@ -226,7 +227,8 @@ EXTRA_DIST += tap-t \ test/openssl-ca-create.sh \ test/openssl-ca-check.sh \ test/openssl-enc-create.sh \ - test/run-coverity.sh + test/run-coverity.sh \ + test/custom-state-current librauctest_la_SOURCES = \ test/common.c \ diff --git a/configure.ac b/configure.ac index b74e0ea83..306404db8 100644 --- a/configure.ac +++ b/configure.ac @@ -273,9 +273,11 @@ AC_CONFIG_LINKS([test/rauc.t:test/rauc.t]) AC_CONFIG_LINKS([test/rootfs.raucs:test/rootfs.raucs]) AC_CONFIG_LINKS([test/sharness.sh:test/sharness.sh]) AC_CONFIG_LINKS([test/test.conf:test/test.conf]) +AC_CONFIG_LINKS([test/test-custom.conf:test/test-custom.conf]) AC_CONFIG_LINKS([test/test-global.conf:test/test-global.conf]) AC_CONFIG_LINKS([test/test-datadir.conf:test/test-datadir.conf]) AC_CONFIG_LINKS([test/test-dummy-handler.conf:test/test-dummy-handler.conf]) +AC_CONFIG_LINKS([test/custom-state-current:test/custom-state-current]) AC_CONFIG_FILES([ Makefile diff --git a/docs/integration.rst b/docs/integration.rst index 3e6faae77..4a8c44542 100644 --- a/docs/integration.rst +++ b/docs/integration.rst @@ -485,6 +485,16 @@ See the `systemd-veritysetup-generator documentation `_ for details. +Identification via custom backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using the custom bootloader backend and the information about the +currently booted slot cannot be derived from the kernel command line, +RAUC will try to query the custom bootloader backend to get this information. + +See the :ref:`sec-custom-bootloader-backend` bootloader section on how +to implement a custom bootloader handler. + Barebox ~~~~~~~ @@ -1114,6 +1124,7 @@ RAUC to trigger the following actions: * set the primary slot * get the boot state * set the boot state +* get the current booted slot (optional) To get the primary slot, the handler is called with the argument ``get-primary``. The handler must output the current primary slot's bootname on the `stdout`, @@ -1147,6 +1158,12 @@ The ```` argument corresponds to one of the following values: The return value must be ``0`` if the boot state was set successfully, or non-zero if an error occurred. +To get the current running slot, the handler must be called with the argument +``get-current``. The handler must output the current running slot's bootname on +the `stdout`, and return ``0`` on exit, if no error occurred. Implementing this +is only needed when the /proc/cmdline is not providing information about current +booted slot. + Init System and Service Startup ------------------------------- diff --git a/src/config_file.c b/src/config_file.c index 65d4e81fc..f4162768d 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -906,5 +906,6 @@ void free_config(RaucConfig *config) g_free(config->encryption_key); g_free(config->encryption_cert); g_clear_pointer(&config->slots, g_hash_table_destroy); + g_free(config->custom_bootloader_backend); g_free(config); } diff --git a/src/context.c b/src/context.c index 74ad77478..117772925 100644 --- a/src/context.c +++ b/src/context.c @@ -140,6 +140,57 @@ static gchar* get_cmdline_bootname(void) return bootname; } +static gchar* get_custom_bootname(void) +{ + g_autoptr(GSubprocessLauncher) launcher = NULL; + g_autoptr(GSubprocess) handle = NULL; + g_autoptr(GDataInputStream) datainstream = NULL; + g_autoptr(GPtrArray) args_array = NULL; + g_autofree gchar *outline = NULL; + GError *ierror = NULL; + GInputStream *instream; + int res; + + args_array = g_ptr_array_new(); + g_ptr_array_add(args_array, context->config->custom_bootloader_backend); + g_ptr_array_add(args_array, (gchar *)("get-current")); + g_ptr_array_add(args_array, NULL); + + launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE); + handle = r_subprocess_launcher_spawnv(launcher, args_array, NULL); + + if (handle == NULL) { + g_message("Failed to run custom backend '%s'", + context->config->custom_bootloader_backend); + return NULL; + } + + instream = g_subprocess_get_stdout_pipe(handle); + datainstream = g_data_input_stream_new(instream); + + outline = g_data_input_stream_read_line(datainstream, NULL, NULL, &ierror); + + if (ierror) { + g_message("Failed to read custom backend output"); + return NULL; + } + + res = g_subprocess_wait_check(handle, NULL, NULL); + if (!res) { + g_message("Failed to get custom backend output"); + return NULL; + } + + if (!outline) { + g_message("Failed to get custom backend bootname: no output"); + return NULL; + } + + g_debug("Resolved custom backend bootname to %s", outline); + + return g_steal_pointer(&outline); +} + /** * Launches a handler and obtains variables from output by looking for * 'RAUC_=value' lines to put them into a key/value store (GHashTable). @@ -362,6 +413,10 @@ static gboolean r_context_configure_target(GError **error) context->bootslot = get_cmdline_bootname(); } + if (context->bootslot == NULL && (g_strcmp0(context->config->system_bootloader, "custom") == 0)) { + context->bootslot = get_custom_bootname(); + } + g_clear_pointer(&context->boot_id, g_free); g_clear_pointer(&context->machine_id, g_free); diff --git a/test/bin/custom-bootloader-script b/test/bin/custom-bootloader-script index 0975014cb..8b09e823f 100755 --- a/test/bin/custom-bootloader-script +++ b/test/bin/custom-bootloader-script @@ -33,6 +33,12 @@ function custom_set_primary () sed -i "s/\(PRIMARY=\).*/\1${bootname}/" "${CUSTOM_STATE_PATH}" } +function custom_get_current () +{ + echo $(grep CURRENT "$CUSTOM_STATE_PATH" | sed 's/.*=\(.*\)/\1/') +} + + if [ "$1" = "get-state" ]; then shift custom_get_state "$@" @@ -45,4 +51,7 @@ elif [ "$1" = "get-primary" ]; then elif [ "$1" = "set-primary" ]; then shift custom_set_primary "$@" +elif [ "$1" = "get-current" ]; then + shift + custom_get_current "$@" fi diff --git a/test/context.c b/test/context.c index f9ed49a4e..d1b26441c 100644 --- a/test/context.c +++ b/test/context.c @@ -66,6 +66,19 @@ static void test_bootslot_no_bootslot(void) r_context_clean(); } +static void test_bootslot_custom_bootloader(void) +{ + r_context_conf()->configpath = g_strdup("test/test-custom.conf"); + r_context_conf()->configmode = R_CONTEXT_CONFIG_MODE_REQUIRED; + g_assert_true(g_setenv("CUSTOM_STATE_PATH", "test/custom-state-current", TRUE)); + r_context_conf()->mock.proc_cmdline = "quiet"; + g_clear_pointer(&r_context_conf()->bootslot, g_free); + + g_assert_cmpstr(r_context()->bootslot, ==, "A"); + + r_context_clean(); +} + /* Tests that the infos provided by the configured system-info handler make it * into RAUC's system information. @@ -125,6 +138,8 @@ int main(int argc, char *argv[]) g_test_add_func("/context/bootslot/no-bootslot", test_bootslot_no_bootslot); + g_test_add_func("/context/bootslot/custom-bootslot", test_bootslot_custom_bootloader); + g_test_add_func("/context/system-info", test_context_system_info); g_test_add_func("/context/system-info-dummy", test_context_system_info_dummy); diff --git a/test/custom-state-current b/test/custom-state-current new file mode 100644 index 000000000..26ddf052d --- /dev/null +++ b/test/custom-state-current @@ -0,0 +1 @@ +CURRENT=A diff --git a/test/test-custom.conf b/test/test-custom.conf new file mode 100644 index 000000000..75183d5e5 --- /dev/null +++ b/test/test-custom.conf @@ -0,0 +1,23 @@ +# testsuite system configuration + +[system] +compatible=Test Config +bootloader=custom +variant-name=Default Variant + +[handlers] +bootloader-custom-backend=bin/custom-bootloader-script + +[keyring] +path=openssl-ca/dev-ca.pem +check-crl=true + +[slot.rootfs.0] +device=images/rootfs-0 +type=raw +bootname=system0 + +[slot.rootfs.1] +device=images/rootfs-1 +type=raw +bootname=system1