Skip to content

Commit

Permalink
Support kargs.d directories for default kargs
Browse files Browse the repository at this point in the history
Add the following config directories for setting default kernel
arguments:

/etc/ostree/kargs.d (host config)
/usr/lib/ostree-boot/kargs.d (base config)

These directories contain files whose contents consist of a karg
of the form:

KEY=VALUE
KEY=VALUE2

Where KEY is a kernel argument and VALUE is a comma-separated
list of arguments passed to the karg. Files in the kargs.d
directories holding karg KEY must be named KEY. Multiple lines
of KEY= may be specified in a single file.

A flag `ostree-kargs-generated-from-config` is written to the
ostree BLS fragment to indicate that the kargs of that BLS
fragment were generated by ostree from the kargs.d directories.
If the flag is set to true, then the next deployment's kargs
will be sourced from the kargs.d directories (previously,
the kargs were simply copied from the previous deployment). If a
kargs override is given (e.g. `ostree admin dpeploy --karg-append`),
then the flag is set to false, and future deployments will
have kargs copied from the previous deployment.

Closes: #479
  • Loading branch information
Robert Fairley committed May 10, 2019
1 parent 82eccad commit 483d369
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 14 deletions.
1 change: 1 addition & 0 deletions Makefile-tests.am
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ _installed_or_uninstalled_test_scripts = \
tests/test-admin-deploy-syslinux.sh \
tests/test-admin-deploy-2.sh \
tests/test-admin-deploy-karg.sh \
tests/test-admin-deploy-karg-default.sh \
tests/test-admin-deploy-switch.sh \
tests/test-admin-deploy-etcmerge-cornercases.sh \
tests/test-admin-deploy-uboot.sh \
Expand Down
204 changes: 192 additions & 12 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -2465,6 +2465,11 @@ _ostree_deployment_set_bootconfig_from_kargs (OstreeDeployment *deployment,
_ostree_kernel_args_append_argv (kargs, override_kernel_argv);
g_autofree char *new_options = _ostree_kernel_args_to_string (kargs);
ostree_bootconfig_parser_set (bootconfig, "options", new_options);
/* Indicate explicitly that the kargs we just set were not generated from
* the config directories. */
ostree_bootconfig_parser_set (bootconfig,
"ostree-kargs-generated-from-config",
"false");
}
}

Expand Down Expand Up @@ -2562,9 +2567,151 @@ get_var_dfd (OstreeSysroot *self,
return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error);
}

/* Get a GFile* referring to the commit object at ref for the file at path. */
static GFile*
get_file_from_repo (OstreeRepo *repo,
const char *ref,
const char *path,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFile) root = NULL;
if (!ostree_repo_read_commit (repo, ref, &root,
NULL, cancellable, error))
return NULL;
return g_file_resolve_relative_path (root, path);
}

static gboolean
sysroot_regenerate_kargs (OstreeSysroot *self,
const char *revision,
int deployment_dfd,
char **out_kargs,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GHashTable) kargs_configs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };

gboolean host_file_exists = FALSE;
if (!ot_dfd_iter_init_allow_noent (deployment_dfd,
_OSTREE_SYSROOT_KARGS_HOST,
&dfd_iter, &host_file_exists,
error))
return FALSE;

if (host_file_exists)
{
while (TRUE)
{
struct dirent *dent = NULL;

if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent,
cancellable, error))
return FALSE;
if (dent == NULL)
break;

if (dent->d_type != DT_REG)
continue;

const char *host_karg_name = dent->d_name;

g_autofree char *host_karg_filepath =
g_build_filename (_OSTREE_SYSROOT_KARGS_HOST, host_karg_name, NULL);

g_autofree char *host_karg_contents = NULL;
host_karg_contents = glnx_file_get_contents_utf8_at (deployment_dfd,
host_karg_filepath,
NULL, cancellable,
error);
if (!host_karg_contents)
return FALSE;

// TODO: make the assert less strong and into an error, and actually parse the karg file properly for a list of karg values that the file could contain.
g_assert (g_str_equal (host_karg_contents, "") == 0 || g_str_has_prefix (host_karg_contents, host_karg_name));
g_hash_table_insert (kargs_configs, g_strdup (host_karg_name),
g_strstrip (g_steal_pointer (&host_karg_contents)));
}
}

struct stat stbuf;
if (!glnx_fstatat_allow_noent (deployment_dfd, _OSTREE_SYSROOT_KARGS_BASE,
&stbuf, 0, error))
return FALSE;
const gboolean base_file_exists = (errno == 0);

if (base_file_exists)
{
/* Load base kargs configuration from the current commit, so that kargs will
* be written to the bootconfig as soon as the current deployment is
* finished (in install_deployment_kernel()). */
g_autofree char *base_karg_contents = NULL;
g_autoptr(GFile) base_karg_file = get_file_from_repo (ostree_sysroot_repo (self),
revision,
_OSTREE_SYSROOT_KARGS_BASE,
cancellable, error);

g_autoptr(GFileEnumerator) dir_enum = NULL;
g_autoptr(GFile) child = NULL;
g_autoptr(GFileInfo) child_info = NULL;
g_autoptr(GError) temp_error = NULL;
dir_enum = g_file_enumerate_children (base_karg_file,
OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, error);
if (!dir_enum)
return FALSE;

while ((child_info = g_file_enumerator_next_file (dir_enum, NULL, &temp_error)) != NULL)
{
g_clear_object (&child);
const char *base_karg_name = g_file_info_get_name (child_info);
child = g_file_get_child (base_karg_file, base_karg_name);

g_autofree char *base_karg_contents = NULL;
if (!g_file_load_contents (child, cancellable,
&base_karg_contents, NULL, NULL, &temp_error))
{
g_propagate_error (error, g_steal_pointer (&temp_error));
return FALSE;
}
if (base_karg_contents && !g_hash_table_contains (kargs_configs, base_karg_name))
{
// TODO: make the assert less strong and into an error, and actually parse the karg file properly for a list of karg values that the file could contain.
g_assert (g_str_equal (base_karg_contents, "") == 0 || g_str_has_prefix (base_karg_contents, base_karg_name));
g_hash_table_insert (kargs_configs, g_strdup (base_karg_name),
g_strstrip (g_steal_pointer (&base_karg_contents)));
}
g_clear_object (&child_info);
}
if (temp_error)
{
g_propagate_error (error, g_steal_pointer (&temp_error));
return FALSE;
}
}

g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_new ();
GHashTableIter karg_config_iter;
const gchar *karg_name = NULL;
const gchar *karg_contents = NULL;

g_hash_table_iter_init (&karg_config_iter, kargs_configs);
while (g_hash_table_iter_next (&karg_config_iter, (gpointer *) &karg_name,
(gpointer *) &karg_contents))
_ostree_kernel_args_parse_append (kargs, karg_contents);

g_autofree char *kargs_contents = _ostree_kernel_args_to_string (kargs);

ot_transfer_out_value (out_kargs, &kargs_contents);
return TRUE;
}

static gboolean
sysroot_finalize_deployment (OstreeSysroot *self,
OstreeDeployment *deployment,
const char *revision,
char **override_kernel_argv,
OstreeDeployment *merge_deployment,
GCancellable *cancellable,
Expand All @@ -2575,25 +2722,57 @@ sysroot_finalize_deployment (OstreeSysroot *self,
if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE, &deployment_dfd, error))
return FALSE;

/* Only use the merge if we didn't get an override */
if (merge_deployment)
{
/* Do the /etc merge. */
if (!merge_configuration_from (self, merge_deployment, deployment, deployment_dfd,
cancellable, error))
return FALSE;
}

/* If we didn't get an override in this deployment, and we got a merge
* deployment, check if we should copy the kargs from the merge
* deployment. */
gboolean kargs_generated_from_config = FALSE;
if (!override_kernel_argv && merge_deployment)
{
/* Override the bootloader arguments */
OstreeBootconfigParser *merge_bootconfig = ostree_deployment_get_bootconfig (merge_deployment);
if (merge_bootconfig)
{
const char *opts = ostree_bootconfig_parser_get (merge_bootconfig, "options");
ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options", opts);
/* Flag that indicates that the kargs in the previous deployment were
* generated from the deployment config. If this is the case, then
* we continue to generate from the config rather than copy over. */
kargs_generated_from_config = g_strcmp0 (ostree_bootconfig_parser_get (merge_bootconfig,
"ostree-kargs-generated-from-config"),
"true") == 0;
if (!kargs_generated_from_config)
{
/* Copy kargs from the merge deployment. */
const char *opts = ostree_bootconfig_parser_get (merge_bootconfig, "options");
ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment),
"options", opts);
ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment),
"ostree-kargs-generated-from-config",
"false");
}
}

}

if (merge_deployment)
/* If we didn't get an override in this deployment, and we received
* indication to regenerate kargs from the deployment config, regenerate
* the kargs. Also if there is no merge deployment, then default to
* regenerating kargs from this deployment's config. */
if (!override_kernel_argv && (kargs_generated_from_config || !merge_deployment))
{
/* And do the /etc merge */
if (!merge_configuration_from (self, merge_deployment, deployment, deployment_dfd,
cancellable, error))
/* Note this occurs after merge_configuration_from() so that
* /etc/ostree/kargs as a result of the /etc merge is used. */
g_autofree char *opts = NULL;
if (!sysroot_regenerate_kargs (self, revision, deployment_dfd, &opts,
cancellable, error))
return FALSE;
ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options", opts);
ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment),
"ostree-kargs-generated-from-config", "true");
}

const char *osdeploypath = glnx_strjoina ("ostree/deploy/", ostree_deployment_get_osname (deployment));
Expand Down Expand Up @@ -2674,7 +2853,8 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
&deployment, cancellable, error))
return FALSE;

if (!sysroot_finalize_deployment (self, deployment, override_kernel_argv,
if (!sysroot_finalize_deployment (self, deployment, revision,
override_kernel_argv,
provided_merge_deployment,
cancellable, error))
return FALSE;
Expand Down Expand Up @@ -2945,8 +3125,8 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self,
if (!glnx_unlinkat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, 0, error))
return FALSE;

if (!sysroot_finalize_deployment (self, self->staged_deployment, kargs, merge_deployment,
cancellable, error))
if (!sysroot_finalize_deployment (self, self->staged_deployment, self->staged_deployment->csum,
kargs, merge_deployment, cancellable, error))
return FALSE;

/* Now, take ownership of the staged state, as normally the API below strips
Expand Down
2 changes: 2 additions & 0 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ struct OstreeSysroot {
#define _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED "/run/ostree/staged-deployment-locked"
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/"
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development"
#define _OSTREE_SYSROOT_KARGS_HOST "etc/ostree/kargs.d"
#define _OSTREE_SYSROOT_KARGS_BASE "usr/lib/ostree-boot/kargs.d"

void
_ostree_sysroot_emit_journal_msg (OstreeSysroot *self,
Expand Down
49 changes: 49 additions & 0 deletions tests/libtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,55 @@ os_repository_new_commit ()
cd ${test_tmpdir}
}

# TODO: deduplicate this with os_repository_new_commit()
os_repository_commit ()
{
repo=${1:-testos-repo}
boot_checksum_iteration=${2:-0}
content_iteration=${3:-0}
branch=${4:-testos/buildmaster/x86_64-runtime}
export version=${5:-$(date "+%Y%m%d.${content_iteration}")}
echo "BOOT ITERATION: $boot_checksum_iteration"
cd ${test_tmpdir}/osdata
kver=3.6.0
if test -f usr/lib/modules/${kver}/vmlinuz; then
bootdir=usr/lib/modules/${kver}
else
if test -d usr/lib/ostree-boot; then
bootdir=usr/lib/ostree-boot
else
bootdir=boot
fi
fi
rm ${bootdir}/*
kernel_path=${bootdir}/vmlinuz
initramfs_path=${bootdir}/initramfs.img
if [[ $bootdir != usr/lib/modules/* ]]; then
kernel_path=${kernel_path}-${kver}
initramfs_path=${bootdir}/initramfs-${kver}.img
fi
echo "new: a kernel ${boot_checksum_iteration}" > ${kernel_path}
echo "new: an initramfs ${boot_checksum_iteration}" > ${initramfs_path}
bootcsum=$(cat ${kernel_path} ${initramfs_path} | sha256sum | cut -f 1 -d ' ')
export bootcsum
if [[ $bootdir != usr/lib/modules/* ]]; then
mv ${kernel_path}{,-${bootcsum}}
mv ${initramfs_path}{,-${bootcsum}}
fi

${CMD_PREFIX} ostree --repo=${test_tmpdir}/${repo} commit --add-metadata-string "version=${version}" -b $branch -s "Build"
cd ${test_tmpdir}
}

os_tree_write_file ()
{
path=${1}
contents="${2}"
cd ${test_tmpdir}/osdata
echo "${contents}" > ${path}
cd ${test_tmpdir}
}

_have_user_xattrs=''
have_user_xattrs() {
assert_has_setfattr
Expand Down
Loading

0 comments on commit 483d369

Please sign in to comment.