diff --git a/Makefile-tests.am b/Makefile-tests.am
index 417a304a42..2c0916f620 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -102,6 +102,7 @@ _installed_or_uninstalled_test_scripts = \
tests/test-admin-deploy-etcmerge-cornercases.sh \
tests/test-admin-deploy-uboot.sh \
tests/test-admin-deploy-grub2.sh \
+ tests/test-admin-deploy-none.sh \
tests/test-admin-deploy-bootid-gc.sh \
tests/test-admin-instutil-set-kargs.sh \
tests/test-admin-upgrade-not-backwards.sh \
diff --git a/man/ostree.repo-config.xml b/man/ostree.repo-config.xml
index 618b524b69..90ac908399 100644
--- a/man/ostree.repo-config.xml
+++ b/man/ostree.repo-config.xml
@@ -109,22 +109,22 @@ Boston, MA 02111-1307, USA.
ensure files are on stable storage when performing operations
such as commits, pulls, and checkouts. Defaults to
true.
-
- If you disable fsync, OSTree will no longer be robust
- against kernel crashes or power loss.
-
-
- You might choose to disable this for local development
- repositories, under the assumption they can be recreated from
- source. Similarly, you could disable for a mirror where you could
- re-pull.
-
-
- For the system repository, you might choose to disable fsync
- if you have uninterruptable power supplies and a well tested
- kernel.
-
-
+
+ If you disable fsync, OSTree will no longer be robust
+ against kernel crashes or power loss.
+
+
+ You might choose to disable this for local development
+ repositories, under the assumption they can be recreated from
+ source. Similarly, you could disable for a mirror where you could
+ re-pull.
+
+
+ For the system repository, you might choose to disable fsync
+ if you have uninterruptable power supplies and a well tested
+ kernel.
+
+
@@ -333,6 +333,42 @@ Boston, MA 02111-1307, USA.
+
+ [sysroot] Section Options
+
+
+ Options for the sysroot, which contains the OSTree repository,
+ deployments, and stateroots. The following entries are defined:
+
+
+
+
+
+ bootloader
+ Configure the bootloader that OSTree uses when
+ deploying the sysroot. This may take the values
+ bootloader=none or bootloader=auto.
+ Default is auto.
+
+
+ If none, then OSTree will generate only BLS (Boot
+ Loader Specification) fragments in sysroot/boot/loader/entries/
+ for the deployment.
+
+
+ If auto, then in addition to generating BLS
+ fragments, OSTree will dynamically check for the existence of grub2,
+ uboot, and syslinux bootloaders. If one of the bootloaders is found,
+ then OSTree will generate a config for the bootloader found. For
+ example, grub2-mkconfig is run for the grub2 case.
+
+
+
+
+
+
+
+
/etc/ostree/remotes.d
diff --git a/src/boot/grub2/ostree-grub-generator b/src/boot/grub2/ostree-grub-generator
index 10645c7419..d1436b65f9 100644
--- a/src/boot/grub2/ostree-grub-generator
+++ b/src/boot/grub2/ostree-grub-generator
@@ -65,7 +65,7 @@ read_config()
populate_menu()
{
- # Default to /boot if OSTREE_BOOT_PARTITION is not set and /boot is on the same device than ostree/repo
+ # Default to /boot if OSTREE_BOOT_PARTITION is not set and /boot is on the same device as /ostree/repo
if [ -z ${OSTREE_BOOT_PARTITION+x} ] && [ -d /boot/ostree ] && [ -d /ostree/repo ] && [ $(stat -c '%d' /boot/ostree) -eq $(stat -c '%d' /ostree/repo) ]; then
boot_prefix="/boot"
else
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index 40be72633d..6bc74c2d23 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -169,6 +169,7 @@ struct OstreeRepo {
guint64 payload_link_threshold;
gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */
gchar **repo_finders;
+ gchar *bootloader; /* Configure which bootloader to use. */
OstreeRepo *parent_repo;
};
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 37a6cdf0d2..003365031c 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -3113,6 +3113,32 @@ reload_remote_config (OstreeRepo *self,
return TRUE;
}
+static gboolean
+reload_sysroot_config (OstreeRepo *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ { g_autofree char *bootloader = NULL;
+
+ if (!ot_keyfile_get_value_with_default_group_optional (self->config, "sysroot",
+ "bootloader", "auto",
+ &bootloader, error))
+ return FALSE;
+
+ /* TODO: possibly later add support for specifying a generic bootloader
+ * binary "x" in /usr/lib/ostree/bootloaders/x). See:
+ * https://github.com/ostreedev/ostree/issues/1719
+ * https://github.com/ostreedev/ostree/issues/1801
+ */
+ if (!(g_str_equal (bootloader, "auto") || g_str_equal (bootloader, "none")))
+ return glnx_throw (error, "Invalid bootloader configuration: '%s'", bootloader);
+
+ self->bootloader = g_steal_pointer (&bootloader);
+ }
+
+ return TRUE;
+}
+
/**
* ostree_repo_reload_config:
* @self: repo
@@ -3131,6 +3157,8 @@ ostree_repo_reload_config (OstreeRepo *self,
return FALSE;
if (!reload_remote_config (self, cancellable, error))
return FALSE;
+ if (!reload_sysroot_config (self, cancellable, error))
+ return FALSE;
return TRUE;
}
@@ -5186,8 +5214,12 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
g_auto(GStrv) gpgkeypath_list = NULL;
- if (!ot_keyfile_get_string_as_list (remote->options, remote->group, "gpgkeypath",
- ";,", &gpgkeypath_list, error))
+ if (!ot_keyfile_get_string_list_with_separator_choice (remote->options,
+ remote->group,
+ "gpgkeypath",
+ ";,",
+ &gpgkeypath_list,
+ error))
return NULL;
if (gpgkeypath_list)
@@ -6060,3 +6092,21 @@ ostree_repo_get_default_repo_finders (OstreeRepo *self)
return (const gchar * const *)self->repo_finders;
}
+
+/**
+ * ostree_repo_get_bootloader:
+ * @self: an #OstreeRepo
+ *
+ * Get the bootloader configured. See the documentation for the
+ * "sysroot.bootloader" config key.
+ *
+ * Returns: bootloader configuration for the sysroot
+ * Since: 2019.2
+ */
+const gchar *
+ostree_repo_get_bootloader (OstreeRepo *self)
+{
+ g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
+
+ return self->bootloader;
+}
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 6e325b8b30..7eb983cfa0 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -115,6 +115,9 @@ gboolean ostree_repo_set_collection_id (OstreeRepo *self,
_OSTREE_PUBLIC
const gchar * const * ostree_repo_get_default_repo_finders (OstreeRepo *self);
+_OSTREE_PUBLIC
+const gchar * ostree_repo_get_bootloader (OstreeRepo *self);
+
_OSTREE_PUBLIC
GFile * ostree_repo_get_path (OstreeRepo *self);
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index b16f65b334..ec9d1592ac 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -2310,6 +2310,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
gboolean bootloader_is_atomic = FALSE;
SyncStats syncstats = { 0, };
g_autoptr(OstreeBootloader) bootloader = NULL;
+ const char *bootloader_config = NULL;
if (!requires_new_bootversion)
{
if (!create_new_bootlinks (self, self->bootversion,
@@ -2342,8 +2343,22 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
return glnx_throw_errno_prefix (error, "Remounting /boot read-write");
}
- if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error))
- return FALSE;
+ OstreeRepo *repo = ostree_sysroot_repo (self);
+
+ bootloader_config = ostree_repo_get_bootloader (repo);
+
+ g_debug ("Using bootloader configuration: %s", bootloader_config);
+
+ if (g_str_equal (bootloader_config, "auto"))
+ {
+ if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error))
+ return FALSE;
+ }
+ else if (g_str_equal (bootloader_config, "none"))
+ {
+ /* No bootloader specified; do not query bootloaders to run. */
+ }
+
bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader);
/* Note equivalent of try/finally here */
@@ -2375,6 +2390,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
sd_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(OSTREE_DEPLOYMENT_COMPLETE_ID),
"MESSAGE=%s", msg,
"OSTREE_BOOTLOADER=%s", bootloader ? _ostree_bootloader_get_name (bootloader) : "none",
+ "OSTREE_BOOTLOADER_CONFIG=%s", bootloader_config,
"OSTREE_BOOTLOADER_ATOMIC=%s", bootloader_is_atomic ? "yes" : "no",
"OSTREE_DID_BOOTSWAP=%s", requires_new_bootversion ? "yes" : "no",
"OSTREE_N_DEPLOYMENTS=%u", new_deployments->len,
diff --git a/src/libotutil/ot-keyfile-utils.c b/src/libotutil/ot-keyfile-utils.c
index a0ab75cc0c..9d5903cefb 100644
--- a/src/libotutil/ot-keyfile-utils.c
+++ b/src/libotutil/ot-keyfile-utils.c
@@ -101,20 +101,55 @@ ot_keyfile_get_value_with_default (GKeyFile *keyfile,
return ret;
}
-/* Read the value of key as a string. If the value string contains
- * one of the separators and none of the others, read the
- * string as a NULL-terminated array out_value. If the value string contains
- * none of the separators, read the string as a single entry into a
- * NULL-terminated array out_value. If the value string contains multiple of
- * the separators, an error is given.
+gboolean
+ot_keyfile_get_value_with_default_group_optional (GKeyFile *keyfile,
+ const char *section,
+ const char *value,
+ const char *default_value,
+ char **out_value,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *local_error = NULL;
+ g_autofree char *ret_value = NULL;
+
+ g_return_val_if_fail (keyfile != NULL, ret);
+ g_return_val_if_fail (section != NULL, ret);
+ g_return_val_if_fail (value != NULL, ret);
+
+ if (!ot_keyfile_get_value_with_default (keyfile, section, value, default_value, &ret_value, &local_error))
+ {
+ if (g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND))
+ {
+ g_clear_error (&local_error);
+ ret_value = g_strdup (default_value);
+ }
+ else
+ {
+ g_propagate_error (error, local_error);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ ot_transfer_out_value(out_value, &ret_value);
+ out:
+ return ret;
+}
+
+/* Read the value of key as a string. If the value string contains
+ * zero or one of the separators and none of the others, read the
+ * string as a NULL-terminated array out_value. If the value string
+ * contains multiple of the separators, give an error.
+ *
* Returns TRUE on success, FALSE on error. */
gboolean
-ot_keyfile_get_string_as_list (GKeyFile *keyfile,
- const char *section,
- const char *key,
- const char *separators,
- char ***out_value,
- GError **error)
+ot_keyfile_get_string_list_with_separator_choice (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ const char *separators,
+ char ***out_value,
+ GError **error)
{
guint sep_count = 0;
gchar sep = '\0';
diff --git a/src/libotutil/ot-keyfile-utils.h b/src/libotutil/ot-keyfile-utils.h
index 2dcb899cf7..b16571df46 100644
--- a/src/libotutil/ot-keyfile-utils.h
+++ b/src/libotutil/ot-keyfile-utils.h
@@ -45,12 +45,20 @@ ot_keyfile_get_value_with_default (GKeyFile *keyfile,
GError **error);
gboolean
-ot_keyfile_get_string_as_list (GKeyFile *keyfile,
- const char *section,
- const char *key,
- const char *separators,
- char ***out_value_list,
- GError **error);
+ot_keyfile_get_value_with_default_group_optional (GKeyFile *keyfile,
+ const char *section,
+ const char *value,
+ const char *default_value,
+ char **out_value,
+ GError **error);
+
+gboolean
+ot_keyfile_get_string_list_with_separator_choice (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ const char *separators,
+ char ***out_value_list,
+ GError **error);
gboolean
ot_keyfile_get_string_list_with_default (GKeyFile *keyfile,
diff --git a/tests/libtest.sh b/tests/libtest.sh
index f09f4a2c52..a737a9bb5a 100755
--- a/tests/libtest.sh
+++ b/tests/libtest.sh
@@ -355,6 +355,11 @@ setup_os_boot_grub2() {
esac
}
+setup_os_boot_configured_bootloader() {
+ bootloader_keyval=$1
+ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo config set ${bootloader_keyval}
+}
+
setup_os_repository () {
mode=$1
shift
@@ -448,6 +453,9 @@ EOF
*grub2*)
setup_os_boot_grub2 "${bootmode}"
;;
+ sysroot\.bootloader*)
+ setup_os_boot_configured_bootloader "${bootmode}"
+ ;;
esac
cd ${test_tmpdir}
diff --git a/tests/test-admin-deploy-none.sh b/tests/test-admin-deploy-none.sh
new file mode 100755
index 0000000000..9094036ae1
--- /dev/null
+++ b/tests/test-admin-deploy-none.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 Robert Fairley
+#
+# 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, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+# Exports OSTREE_SYSROOT so --sysroot not needed.
+setup_os_repository "archive" "sysroot.bootloader none"
+
+extra_admin_tests=1
+
+. $(dirname $0)/admin-test.sh
+
+# Test that the bootloader configuration "none" generates BLS config snippets.
+cd ${test_tmpdir}
+rm httpd osdata testos-repo sysroot -rf
+setup_os_repository "archive" "sysroot.bootloader none"
+${CMD_PREFIX} ostree pull-local --repo=sysroot/ostree/repo --remote testos testos-repo testos/buildmaster/x86_64-runtime
+# Test grub2 does not get detected with bootloader configuration "none"
+# (see https://github.com/ostreedev/ostree/issues/1774)
+mkdir -p sysroot/boot/grub2 && touch sysroot/boot/grub2/grub.cfg
+${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os testos testos/buildmaster/x86_64-runtime > out.txt
+assert_file_has_content out.txt "Bootloader updated.*"
+assert_file_has_content sysroot/boot/loader/entries/ostree-1-testos.conf 'options.* root=LABEL=MOO'
+assert_file_has_content sysroot/boot/ostree/testos-${bootcsum}/vmlinuz-3.6.0 'a kernel'
+assert_file_has_content sysroot/boot/ostree/testos-${bootcsum}/initramfs-3.6.0.img 'an initramfs'
+echo "ok generate bls config on first deployment"
+
+# TODO: add tests to try setting with an unsupported bootloader config,
+# once https://github.com/ostreedev/ostree/issues/1827 is solved.
+# The tests should check that the following commands fail:
+# ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo config set sysroot.bootloader unsupported_bootloader
+# ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo config set sysroot.bootloader ""
diff --git a/tests/test-config.sh b/tests/test-config.sh
index b1ea3e5e35..2de841227c 100755
--- a/tests/test-config.sh
+++ b/tests/test-config.sh
@@ -34,7 +34,7 @@ ${CMD_PREFIX} ostree config --repo=repo get --group=core repo_version >> list.tx
${CMD_PREFIX} ostree config --repo=repo get --group='remote "flathub"' 'xa.title' >> list.txt
${CMD_PREFIX} ostree config --repo=repo get --group='remote "flathub"' 'xa.title-is-set' >> list.txt
${CMD_PREFIX} ostree config --repo=repo get --group='remote "org.mozilla.FirefoxRepo"' url >> list.txt
-${CMD_PREFIX} cat list.txt
+${CMD_PREFIX} cat list.txt
assert_file_has_content list.txt "bare"
assert_file_has_content list.txt "1"
diff --git a/tests/test-keyfile-utils.c b/tests/test-keyfile-utils.c
index 8acd0a1cd3..258e7dd8ff 100644
--- a/tests/test-keyfile-utils.c
+++ b/tests/test-keyfile-utils.c
@@ -79,7 +79,6 @@ test_get_value_with_default (void)
/* Avoid that g_return_val_if_fail causes the test to fail. */
always_fatal_mask = g_log_set_always_fatal (0);
-
g_assert_false (ot_keyfile_get_value_with_default (g_keyfile,
NULL,
"value_foo",
@@ -124,11 +123,75 @@ test_get_value_with_default (void)
g_clear_pointer (&out, g_free);
g_assert_false (ot_keyfile_get_value_with_default (g_keyfile,
- "a_fake_section",
- "a_value_true",
- "no value",
- &out,
- &error));
+ "a_fake_section",
+ "a_value_true",
+ "no value",
+ &out,
+ &error));
+ g_clear_error (&error);
+ g_clear_pointer (&out, g_free);
+}
+
+static void
+test_get_value_with_default_group_optional (void)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree char *out = NULL;
+ GLogLevelFlags always_fatal_mask;
+ const char *section = "section";
+
+/* Avoid that g_return_val_if_fail causes the test to fail. */
+ always_fatal_mask = g_log_set_always_fatal (0);
+
+ g_assert_false (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ NULL,
+ "value_foo",
+ "none",
+ &out,
+ &error));
+ g_clear_pointer (&out, g_free);
+ g_assert_false (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ section,
+ NULL,
+ "none",
+ &out,
+ &error));
+ g_clear_pointer (&out, g_free);
+ g_assert_false (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ section,
+ NULL,
+ "something",
+ &out,
+ &error));
+ g_clear_pointer (&out, g_free);
+
+ /* Restore the old mask. */
+ g_log_set_always_fatal (always_fatal_mask);
+
+ g_assert (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ section,
+ "value_foo",
+ "none",
+ &out,
+ &error));
+ g_assert_cmpstr (out, ==, "foo");
+ g_clear_pointer (&out, g_free);
+
+ g_assert (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ section,
+ "a_not_existing_value",
+ "correct",
+ &out,
+ &error));
+ g_assert_cmpstr (out, ==, "correct");
+ g_clear_pointer (&out, g_free);
+
+ g_assert (ot_keyfile_get_value_with_default_group_optional (g_keyfile,
+ "an_optional_section",
+ "a_value_true",
+ "no value",
+ &out,
+ &error));
g_clear_error (&error);
g_clear_pointer (&out, g_free);
}
@@ -191,6 +254,7 @@ int main (int argc, char **argv)
g_test_add_func ("/keyfile-utils/get-boolean-with-default", test_get_boolean_with_default);
g_test_add_func ("/keyfile-utils/get-value-with-default", test_get_value_with_default);
+ g_test_add_func ("/keyfile-utils/get-value-with-default-group-optional", test_get_value_with_default_group_optional);
g_test_add_func ("/keyfile-utils/copy-group", test_copy_group);
ret = g_test_run();