diff --git a/man/ostree.repo-config.xml b/man/ostree.repo-config.xml index e4984430..6fead168 100644 --- a/man/ostree.repo-config.xml +++ b/man/ostree.repo-config.xml @@ -373,7 +373,9 @@ Boston, MA 02111-1307, USA. bootloader Configure the bootloader that OSTree uses when deploying the sysroot. This may take the values - bootloader=none or bootloader=auto. + bootloader=none, bootloader=auto, + bootloader=grub2, bootloader=syslinux, + bootloader=uboot or bootloader=zipl. Default is auto. @@ -388,6 +390,11 @@ Boston, MA 02111-1307, USA. then OSTree will generate a config for the bootloader found. For example, grub2-mkconfig is run for the grub2 case. + + A specific bootloader type may also be explicitly requested by choosing + grub2, syslinux, uboot or + zipl. + diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index cbbe6971..628f2b46 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -110,6 +110,28 @@ typedef enum { _OSTREE_FEATURE_YES, } _OstreeFeatureSupport; +/* Possible values for the sysroot.bootloader configuration variable */ +typedef enum { + CFG_SYSROOT_BOOTLOADER_OPT_AUTO = 0, + CFG_SYSROOT_BOOTLOADER_OPT_NONE, + CFG_SYSROOT_BOOTLOADER_OPT_GRUB2, + CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX, + CFG_SYSROOT_BOOTLOADER_OPT_UBOOT, + CFG_SYSROOT_BOOTLOADER_OPT_ZIPL, + /* Non-exhaustive */ +} OstreeCfgSysrootBootloaderOpt; + +static const char* const CFG_SYSROOT_BOOTLOADER_OPTS_STR[] = { + /* This must be kept in the same order as the enum */ + "auto", + "none", + "grub2", + "syslinux", + "uboot", + "zipl", + NULL, +}; + /** * OstreeRepo: * @@ -193,7 +215,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. */ + OstreeCfgSysrootBootloaderOpt bootloader; /* Configure which bootloader to use. */ OstreeRepo *parent_repo; }; diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 3bbf5ea0..11a209a4 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1048,7 +1048,6 @@ ostree_repo_finalize (GObject *object) g_mutex_clear (&self->txn_lock); g_free (self->collection_id); g_strfreev (self->repo_finders); - g_free (self->bootloader); g_clear_pointer (&self->remotes, g_hash_table_destroy); g_mutex_clear (&self->remotes_lock); @@ -3186,28 +3185,28 @@ reload_sysroot_config (OstreeRepo *self, GCancellable *cancellable, GError **error) { - { g_autofree char *bootloader = NULL; + g_autofree char *bootloader = NULL; - if (!ot_keyfile_get_value_with_default_group_optional (self->config, "sysroot", - "bootloader", "auto", - &bootloader, error)) - return FALSE; + 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 - * Also, dedup these strings with the bootloader implementations - */ - if (!(g_str_equal (bootloader, "auto") || g_str_equal (bootloader, "none") - || g_str_equal (bootloader, "zipl"))) - return glnx_throw (error, "Invalid bootloader configuration: '%s'", bootloader); + /* 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 + */ + for (int i = 0; CFG_SYSROOT_BOOTLOADER_OPTS_STR[i]; i++) + { + if (g_str_equal (bootloader, CFG_SYSROOT_BOOTLOADER_OPTS_STR[i])) + { + self->bootloader = (OstreeCfgSysrootBootloaderOpt) i; + return TRUE; + } + } - g_free (self->bootloader); - self->bootloader = g_steal_pointer (&bootloader); - } - - return TRUE; + return glnx_throw (error, "Invalid bootloader configuration: '%s'", bootloader); } /** @@ -6323,7 +6322,7 @@ ostree_repo_get_default_repo_finders (OstreeRepo *self) * Get the bootloader configured. See the documentation for the * "sysroot.bootloader" config key. * - * Returns: bootloader configuration for the sysroot + * Returns: (transfer none): bootloader configuration for the sysroot * Since: 2019.2 */ const gchar * @@ -6331,7 +6330,7 @@ ostree_repo_get_bootloader (OstreeRepo *self) { g_return_val_if_fail (OSTREE_IS_REPO (self), NULL); - return self->bootloader; + return CFG_SYSROOT_BOOTLOADER_OPTS_STR[self->bootloader]; } diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 1a4a6369..900efe49 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -44,7 +44,6 @@ #include "ostree-repo-private.h" #include "ostree-sysroot-private.h" #include "ostree-sepolicy-private.h" -#include "ostree-bootloader-zipl.h" #include "ostree-deployment-private.h" #include "ostree-core-private.h" #include "ostree-linuxfsutil.h" @@ -2561,7 +2560,6 @@ 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, @@ -2593,29 +2591,8 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, return glnx_throw_errno_prefix (error, "Remounting /boot read-write"); } - 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. */ - } - else if (g_str_equal (bootloader_config, "zipl")) - { - /* Because we do not mark zipl as active by default, lets creating one here, - * which is basically the same what _ostree_sysroot_query_bootloader() does - * for other bootloaders if being activated. - * */ - bootloader = (OstreeBootloader*) _ostree_bootloader_zipl_new (self); - } + if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error)) + return FALSE; bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader); @@ -2646,6 +2623,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, (bootloader_is_atomic ? "Transaction complete" : "Bootloader updated"), requires_new_bootversion ? "yes" : "no", new_deployments->len - self->deployments->len); + const gchar *bootloader_config = ostree_repo_get_bootloader (ostree_sysroot_repo (self)); ot_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", diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index e0813b55..7062a218 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -36,6 +36,7 @@ #include "ostree-bootloader-uboot.h" #include "ostree-bootloader-syslinux.h" #include "ostree-bootloader-grub2.h" +#include "ostree-bootloader-zipl.h" /** * SECTION:ostree-sysroot @@ -1324,6 +1325,34 @@ ostree_sysroot_repo (OstreeSysroot *self) return self->repo; } +static OstreeBootloader* +_ostree_sysroot_new_bootloader_by_type ( + OstreeSysroot *sysroot, + OstreeCfgSysrootBootloaderOpt bl_type) +{ + switch (bl_type) + { + case CFG_SYSROOT_BOOTLOADER_OPT_NONE: + /* No bootloader specified; do not query bootloaders to run. */ + return NULL; + case CFG_SYSROOT_BOOTLOADER_OPT_GRUB2: + return (OstreeBootloader*) _ostree_bootloader_grub2_new (sysroot); + case CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX: + return (OstreeBootloader*) _ostree_bootloader_syslinux_new (sysroot); + case CFG_SYSROOT_BOOTLOADER_OPT_UBOOT: + return (OstreeBootloader*) _ostree_bootloader_uboot_new (sysroot); + case CFG_SYSROOT_BOOTLOADER_OPT_ZIPL: + /* We never consider zipl as active by default, so it can only be created + * if it's explicitly requested in the config */ + return (OstreeBootloader*) _ostree_bootloader_zipl_new (sysroot); + case CFG_SYSROOT_BOOTLOADER_OPT_AUTO: + /* "auto" is handled by ostree_sysroot_query_bootloader so we should + * never get here: Fallthrough */ + default: + g_assert_not_reached (); + } +} + /** * ostree_sysroot_query_bootloader: * @sysroot: Sysroot @@ -1337,32 +1366,38 @@ _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { - gboolean is_active; - g_autoptr(OstreeBootloader) ret_loader = - (OstreeBootloader*)_ostree_bootloader_syslinux_new (sysroot); - if (!_ostree_bootloader_query (ret_loader, &is_active, - cancellable, error)) - return FALSE; + OstreeRepo *repo = ostree_sysroot_repo (sysroot); + OstreeCfgSysrootBootloaderOpt bootloader_config = repo->bootloader; - if (!is_active) - { - g_object_unref (ret_loader); - ret_loader = (OstreeBootloader*)_ostree_bootloader_grub2_new (sysroot); - if (!_ostree_bootloader_query (ret_loader, &is_active, - cancellable, error)) - return FALSE; - } - if (!is_active) - { - g_object_unref (ret_loader); - ret_loader = (OstreeBootloader*)_ostree_bootloader_uboot_new (sysroot); - if (!_ostree_bootloader_query (ret_loader, &is_active, cancellable, error)) - return FALSE; - } - if (!is_active) - g_clear_object (&ret_loader); + g_debug ("Using bootloader configuration: %s", + CFG_SYSROOT_BOOTLOADER_OPTS_STR[bootloader_config]); - ot_transfer_out_value(out_bootloader, &ret_loader); + g_autoptr(OstreeBootloader) ret_loader = NULL; + if (bootloader_config == CFG_SYSROOT_BOOTLOADER_OPT_AUTO) + { + OstreeCfgSysrootBootloaderOpt probe[] = { + CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX, + CFG_SYSROOT_BOOTLOADER_OPT_GRUB2, + CFG_SYSROOT_BOOTLOADER_OPT_UBOOT, + }; + for (int i = 0; i < G_N_ELEMENTS (probe); i++) + { + g_autoptr(OstreeBootloader) bl = _ostree_sysroot_new_bootloader_by_type ( + sysroot, probe[i]); + gboolean is_active = FALSE; + if (!_ostree_bootloader_query (bl, &is_active, cancellable, error)) + return FALSE; + if (is_active) + { + ret_loader = g_steal_pointer (&bl); + break; + } + } + } + else + ret_loader = _ostree_sysroot_new_bootloader_by_type (sysroot, bootloader_config); + + ot_transfer_out_value (out_bootloader, &ret_loader) return TRUE; } diff --git a/tests/bootloader-entries-crosscheck.py b/tests/bootloader-entries-crosscheck.py index 605bd080..b5a02066 100755 --- a/tests/bootloader-entries-crosscheck.py +++ b/tests/bootloader-entries-crosscheck.py @@ -20,113 +20,118 @@ import os import sys -if len(sys.argv) == 1: - sysroot = '' -else: - sysroot = sys.argv[1] -bootloader = sys.argv[2] -loaderpath = sysroot + '/boot/loader/entries' -syslinuxpath = sysroot + '/boot/syslinux/syslinux.cfg' +def main(argv): + _, sysroot, bootloader = argv + + if bootloader == "grub2": + sys.stdout.write('GRUB2 configuration validation not implemented.\n') + return 0 + else: + return validate_syslinux(sysroot) -if bootloader == "grub2": - sys.stdout.write('GRUB2 configuration validation not implemented.\n') - sys.exit(0) def fatal(msg): sys.stderr.write(msg) sys.stderr.write('\n') sys.exit(1) -def entry_get_version(entry): - return int(entry['version']) def get_ostree_option(optionstring): for o in optionstring.split(): if o.startswith('ostree='): return o[8:] - raise ValueError('ostree= not found') - -entries = [] -syslinux_entries = [] + raise ValueError('ostree= not found in %r' % (optionstring,)) -# Parse loader configs -for fname in os.listdir(loaderpath): - path = os.path.join(loaderpath, fname) - with open(path) as f: + +def parse_loader_configs(sysroot): + loaderpath = sysroot + '/boot/loader/entries' + entries = [] + + # Parse loader configs + for fname in os.listdir(loaderpath): + path = os.path.join(loaderpath, fname) entry = {} - for line in f: - line = line.strip() - if (line == '' or line.startswith('#')): - continue - s = line.find(' ') - assert s > 0 - k = line[0:s] - v = line[s+1:] - entry[k] = v + with open(path) as f: + for line in f: + line = line.strip() + if (line == '' or line.startswith('#')): + continue + k, v = line.split(' ', 1) + entry[k] = v entries.append(entry) - entries.sort(key=entry_get_version, reverse=True) + entries.sort(key=lambda e: int(e['version']), reverse=True) + return entries -# Parse SYSLINUX config -with open(syslinuxpath) as f: - in_ostree_config = False - syslinux_entry = None - syslinux_default = None - for line in f: - try: - k, v = line.strip().split(" ", 1) - except ValueError: - continue - if k == 'DEFAULT': - if syslinux_entry is not None: - syslinux_default = v - elif k == 'LABEL': - if syslinux_entry is not None: - syslinux_entries.append(syslinux_entry) - syslinux_entry = {} - syslinux_entry['title'] = v - elif k == 'KERNEL': - syslinux_entry['linux'] = v - elif k == 'INITRD': - syslinux_entry['initrd'] = v - elif k == 'APPEND': - syslinux_entry['options'] = v - if syslinux_entry is not None: - syslinux_entries.append(syslinux_entry) -if len(entries) != len(syslinux_entries): - fatal("Found {0} loader entries, but {1} SYSLINUX entries\n".format(len(entries), len(syslinux_entries))) +def validate_syslinux(sysroot): + syslinuxpath = sysroot + '/boot/syslinux/syslinux.cfg' + + entries = parse_loader_configs(sysroot) + syslinux_entries = [] + + # Parse SYSLINUX config + with open(syslinuxpath) as f: + syslinux_entry = None + for line in f: + try: + k, v = line.strip().split(" ", 1) + except ValueError: + continue + if k == 'DEFAULT': + if syslinux_entry is not None: + syslinux_default = v + elif k == 'LABEL': + if syslinux_entry is not None: + syslinux_entries.append(syslinux_entry) + syslinux_entry = {} + syslinux_entry['title'] = v + elif k == 'KERNEL': + syslinux_entry['linux'] = v + elif k == 'INITRD': + syslinux_entry['initrd'] = v + elif k == 'APPEND': + syslinux_entry['options'] = v + if syslinux_entry is not None: + syslinux_entries.append(syslinux_entry) + + if len(entries) != len(syslinux_entries): + fatal("Found {0} loader entries, but {1} SYSLINUX entries\n".format( + len(entries), len(syslinux_entries))) + + def assert_key_same_file(a, b, key): + aval = a[key] + bval = b[key] + sys.stderr.write("aval: %r\nbval: %r\n" % (aval, bval)) + + # Paths in entries are always relative to /boot + entry = os.stat(sysroot + "/boot" + aval) + + # Syslinux entries can be relative to /boot (if it's on another filesystem) + # or relative to / if /boot is on /. + s1 = os.stat(sysroot + bval) + s2 = os.stat(sysroot + "/boot" + bval) + + # A symlink ensures that no matter what they point at the same file + assert_eq(entry, s1) + assert_eq(entry, s2) + + for i, (entry, syslinuxentry) in enumerate(zip(entries, syslinux_entries)): + assert_key_same_file(entry, syslinuxentry, 'linux') + assert_key_same_file(entry, syslinuxentry, 'initrd') + entry_ostree = get_ostree_option(entry['options']) + syslinux_ostree = get_ostree_option(syslinuxentry['options']) + if entry_ostree != syslinux_ostree: + fatal("Mismatch on ostree option: {0} != {1}".format( + entry_ostree, syslinux_ostree)) + + sys.stdout.write('SYSLINUX configuration validated\n') + return 0 def assert_eq(a, b): assert a == b, "%r == %r" % (a, b) -def assert_key_same_file(a, b, key): - aval = a[key] - bval = b[key] - sys.stderr.write("aval: %r\nbval: %r\n" % (aval, bval)) - - # Paths in entries are always relative to /boot - entry = os.stat(sysroot + "/boot" + aval) - - # Syslinux entries can be relative to /boot (if it's on another filesystem) - # or relative to / if /boot is on /. - s1 = os.stat(sysroot + bval) - s2 = os.stat(sysroot + "/boot" + bval) - - # A symlink ensures that no matter what they point at the same file - assert_eq(entry, s1) - assert_eq(entry, s2) - - -for i,(entry,syslinuxentry) in enumerate(zip(entries, syslinux_entries)): - assert_key_same_file(entry, syslinuxentry, 'linux') - assert_key_same_file(entry, syslinuxentry, 'initrd') - entry_ostree = get_ostree_option(entry['options']) - syslinux_ostree = get_ostree_option(syslinuxentry['options']) - if entry_ostree != syslinux_ostree: - fatal("Mismatch on ostree option: {0} != {1}".format(entry_ostree, syslinux_ostree)) - -sys.stdout.write('SYSLINUX configuration validated\n') -sys.exit(0) +if __name__ == '__main__': + sys.exit(main(sys.argv))