app: add experimental support for pkg removals

This is one more step towards making rpm-ostree more powerful in its
quest to be the ultimate *hybrid* image/package system. Package layering
allows us to add packages on top of the base package set received from
the content provider. However, we're not able to remove or replace
packages in the base set itself.

This patch introduces a new `override` command, which is for now nested
under the experimental `ex` command. The `override` command will allow
users to modify the base package set itself. The first implemented
subcommands are `remove` and `reset`.

A stub has been provided for the more useful `replace` subcommand,
though much of the needed logic for that operation are implemented in
this patch as part of the `remove` subcommand.

Part of: https://github.com/projectatomic/rpm-ostree/issues/485

Closes: #797
Approved by: cgwalters
This commit is contained in:
Jonathan Lebon 2017-06-05 12:37:56 -04:00 committed by Atomic Bot
parent 7d1424e0c7
commit cde3295e26
28 changed files with 1244 additions and 275 deletions

View File

@ -31,6 +31,7 @@ rpm_ostree_SOURCES = src/app/main.c \
src/app/rpmostree-builtin-cleanup.c \
src/app/rpmostree-builtin-initramfs.c \
src/app/rpmostree-builtin-livefs.c \
src/app/rpmostree-builtin-override.c \
src/app/rpmostree-pkg-builtins.c \
src/app/rpmostree-builtin-status.c \
src/app/rpmostree-builtin-ex.c \
@ -44,6 +45,8 @@ rpm_ostree_SOURCES = src/app/main.c \
src/app/rpmostree-dbus-helpers.h \
src/app/rpmostree-container-builtins.h \
src/app/rpmostree-container-builtins.c \
src/app/rpmostree-override-builtins.h \
src/app/rpmostree-override-builtins.c \
src/app/rpmostree-ex-builtin-unpack.c \
src/app/rpmostree-libbuiltin.c \
src/app/rpmostree-libbuiltin.h \

View File

@ -118,7 +118,8 @@ rpmostree_builtin_deploy (int argc,
TRUE, /* allow-downgrade */
FALSE, /* skip-purge */
FALSE, /* no-pull-base */
FALSE); /* dry-run */
FALSE, /* dry-run */
FALSE); /* no-overrides */
/* This will set the GVariant if the default deployment changes. */
g_signal_connect (os_proxy, "notify::default-deployment",
@ -133,6 +134,9 @@ rpmostree_builtin_deploy (int argc,
revision,
install_pkgs,
uninstall_pkgs,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
options,
&transaction_address,
cancellable,

View File

@ -25,6 +25,8 @@
static RpmOstreeCommand ex_subcommands[] = {
{ "livefs", RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT,
rpmostree_ex_builtin_livefs },
{ "override", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
rpmostree_ex_builtin_override },
{ "unpack", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
rpmostree_ex_builtin_unpack },
{ "container", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,

View File

@ -0,0 +1,48 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Red Hat Inc.
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "rpmostree-ex-builtins.h"
#include "rpmostree-override-builtins.h"
static RpmOstreeCommand override_subcommands[] = {
{ "replace", RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT |
RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS |
RPM_OSTREE_BUILTIN_FLAG_HIDDEN, /* XXX UNDER CONSTRUCTION XXX */
rpmostree_override_builtin_replace },
{ "remove", RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT |
RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS,
rpmostree_override_builtin_remove },
{ "reset", RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT |
RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS,
rpmostree_override_builtin_reset },
{ NULL, 0, NULL }
};
int
rpmostree_ex_builtin_override (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable, GError **error)
{
return rpmostree_handle_subcommand (argc, argv, override_subcommands,
invocation, cancellable, error);
}

View File

@ -94,7 +94,8 @@ rpmostree_builtin_rebase (int argc,
TRUE, /* allow-downgrade */
opt_skip_purge,
FALSE, /* no-pull-base */
FALSE); /* dry-run */
FALSE, /* dry-run */
FALSE); /* no-overrides */
/* Use newer D-Bus API only if we have to. */
if (install_pkgs || uninstall_pkgs)
@ -104,6 +105,9 @@ rpmostree_builtin_rebase (int argc,
revision,
install_pkgs,
uninstall_pkgs,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
options,
&transaction_address,
cancellable,

View File

@ -213,6 +213,7 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
g_autofree const gchar **origin_packages = NULL;
g_autofree const gchar **origin_requested_packages = NULL;
g_autofree const gchar **origin_requested_local_packages = NULL;
g_autofree const gchar **origin_removed_base_packages = NULL;
const gchar *origin_refspec;
const gchar *id;
const gchar *os_name;
@ -228,7 +229,8 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
gboolean is_booted;
const gboolean was_first = first;
/* Add the long keys here */
const guint max_key_len = MAX (strlen ("PendingBaseVersion"), strlen ("InterruptedLiveCommit"));
const guint max_key_len = MAX (strlen ("RemovedBasePackages"),
strlen ("InterruptedLiveCommit"));
g_autoptr(GVariant) signatures = NULL;
g_autofree char *timestamp_string = NULL;
@ -251,6 +253,8 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
lookup_array_and_canonicalize (dict, "requested-packages");
origin_requested_local_packages =
lookup_array_and_canonicalize (dict, "requested-local-packages");
origin_removed_base_packages =
lookup_array_and_canonicalize (dict, "removed-base-packages");
}
else
origin_refspec = NULL;
@ -411,6 +415,11 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
}
}
/* print base overrides before overlays */
if (origin_removed_base_packages)
print_packages ("RemovedBasePackages", max_key_len,
origin_removed_base_packages, NULL);
/* let's be nice and only print requested - layered, rather than repeating
* the ones in layered twice */
if (origin_requested_packages)

View File

@ -125,7 +125,8 @@ rpmostree_builtin_upgrade (int argc,
opt_allow_downgrade,
FALSE, /* skip-purge */
FALSE, /* no-pull-base */
FALSE); /* dry-run */
FALSE, /* dry-run */
FALSE); /* no-overrides */
/* Use newer D-Bus API only if we have to. */
if (install_pkgs || uninstall_pkgs)
@ -135,6 +136,9 @@ rpmostree_builtin_upgrade (int argc,
NULL, /* revision */
install_pkgs,
uninstall_pkgs,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
options,
&transaction_address,
cancellable,

View File

@ -799,11 +799,23 @@ rpmostree_sort_pkgs_strv (const char *const* pkgs,
return TRUE;
}
static void
vardict_insert_strv (GVariantDict *dict,
const char *key,
const char *const* strv)
{
if (strv && *strv)
g_variant_dict_insert (dict, key, "^as", (char**)strv);
}
static gboolean
get_modifiers_variant (const char *set_refspec,
const char *set_revision,
const char *const* install_pkgs,
const char *const* uninstall_pkgs,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
GVariant **out_modifiers,
GUnixFDList **out_fd_list,
GError **error)
@ -833,9 +845,14 @@ get_modifiers_variant (const char *set_refspec,
g_variant_dict_insert (&dict, "set-refspec", "s", set_refspec);
if (set_revision)
g_variant_dict_insert (&dict, "set-revision", "s", set_revision);
if (uninstall_pkgs != NULL && g_strv_length ((char**)uninstall_pkgs) > 0)
g_variant_dict_insert (&dict, "uninstall-packages", "^as",
(char**)uninstall_pkgs);
vardict_insert_strv (&dict, "uninstall-packages", uninstall_pkgs);
/* NB: when we implement this, we'll have to pass it through sort_pkgs_strv to
* split out local pkgs */
vardict_insert_strv (&dict, "override-replace-packages", override_replace_pkgs);
vardict_insert_strv (&dict, "override-remove-packages", override_remove_pkgs);
vardict_insert_strv (&dict, "override-reset-packages", override_reset_pkgs);
*out_fd_list = g_steal_pointer (&fd_list);
*out_modifiers = g_variant_ref_sink (g_variant_dict_end (&dict));
@ -847,7 +864,8 @@ rpmostree_get_options_variant (gboolean reboot,
gboolean allow_downgrade,
gboolean skip_purge,
gboolean no_pull_base,
gboolean dry_run)
gboolean dry_run,
gboolean no_overrides)
{
GVariantDict dict;
g_variant_dict_init (&dict, NULL);
@ -856,6 +874,7 @@ rpmostree_get_options_variant (gboolean reboot,
g_variant_dict_insert (&dict, "skip-purge", "b", skip_purge);
g_variant_dict_insert (&dict, "no-pull-base", "b", no_pull_base);
g_variant_dict_insert (&dict, "dry-run", "b", dry_run);
g_variant_dict_insert (&dict, "no-overrides", "b", no_overrides);
return g_variant_ref_sink (g_variant_dict_end (&dict));
}
@ -865,6 +884,9 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
const char *set_revision,
const char *const* install_pkgs,
const char *const* uninstall_pkgs,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
GVariant *options,
char **out_transaction_address,
GCancellable *cancellable,
@ -874,6 +896,9 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
glnx_unref_object GUnixFDList *fd_list = NULL;
if (!get_modifiers_variant (set_refspec, set_revision,
install_pkgs, uninstall_pkgs,
override_replace_pkgs,
override_remove_pkgs,
override_reset_pkgs,
&modifiers, &fd_list, error))
return FALSE;

View File

@ -87,7 +87,8 @@ rpmostree_get_options_variant (gboolean reboot,
gboolean allow_downgrade,
gboolean skip_purge,
gboolean no_pull_base,
gboolean dry_run);
gboolean dry_run,
gboolean no_overrides);
gboolean
rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
@ -95,6 +96,9 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
const char *set_revision,
const char *const* install_pkgs,
const char *const* uninstall_pkgs,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
GVariant *options,
char **out_transaction_address,
GCancellable *cancellable,

View File

@ -32,6 +32,7 @@ G_BEGIN_DECLS
BUILTINPROTO(unpack);
BUILTINPROTO(livefs);
BUILTINPROTO(override);
#undef BUILTINPROTO

View File

@ -0,0 +1,239 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Red Hat, Inc.
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "rpmostree-override-builtins.h"
#include "rpmostree-libbuiltin.h"
#include <libglnx.h>
static char *opt_osname;
static gboolean opt_reboot;
static gboolean opt_dry_run;
static gboolean opt_reset_all;
static const char *const *install_pkgs;
static const char *const *uninstall_pkgs;
static GOptionEntry option_entries[] = {
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Operate on provided OSNAME", "OSNAME" },
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after upgrade is prepared", NULL },
{ "dry-run", 'n', 0, G_OPTION_ARG_NONE, &opt_dry_run, "Exit after printing the transaction", NULL },
{ NULL }
};
static GOptionEntry reset_option_entries[] = {
{ "all", 'a', 0, G_OPTION_ARG_NONE, &opt_reset_all, "Reset all active overrides", NULL },
{ NULL }
};
static gboolean
handle_override (RPMOSTreeSysroot *sysroot_proxy,
const char *const *override_remove,
const char *const *override_replace,
const char *const *override_reset,
GCancellable *cancellable,
GError **error)
{
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
cancellable, &os_proxy, error))
return EXIT_FAILURE;
g_autoptr(GVariant) options =
rpmostree_get_options_variant (opt_reboot,
FALSE, /* allow-downgrade */
FALSE, /* skip-purge */
TRUE, /* no-pull-base */
opt_dry_run,
opt_reset_all);
g_autofree char *transaction_address = NULL;
if (!rpmostree_update_deployment (os_proxy,
NULL, /* set-refspec */
NULL, /* set-revision */
install_pkgs,
uninstall_pkgs,
override_replace,
override_remove,
override_reset,
options,
&transaction_address,
cancellable,
error))
return EXIT_FAILURE;
if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
transaction_address,
cancellable,
error))
return EXIT_FAILURE;
if (opt_dry_run)
{
g_print ("Exiting because of '--dry-run' option\n");
}
else if (!opt_reboot)
{
const char *sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);
if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
cancellable,
error))
return EXIT_FAILURE;
g_print ("Run \"systemctl reboot\" to start a reboot\n");
}
return EXIT_SUCCESS;
}
gboolean
rpmostree_override_builtin_replace (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
{
GOptionContext *context;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
_cleanup_peer_ GPid peer_pid = 0;
context = g_option_context_new ("PACKAGE [PACKAGE...] - "
"Remove packages from the base layer");
if (!rpmostree_option_context_parse (context,
option_entries,
&argc, &argv,
invocation,
cancellable,
&install_pkgs,
&uninstall_pkgs,
&sysroot_proxy,
&peer_pid,
error))
return EXIT_FAILURE;
if (argc < 2)
{
rpmostree_usage_error (context, "At least one PACKAGE must be specified",
error);
return EXIT_FAILURE;
}
/* shift to first pkgspec and ensure it's a proper strv (previous parsing
* might have moved args around) */
argv++; argc--;
argv[argc] = NULL;
return handle_override (sysroot_proxy,
NULL, (const char *const*)argv, NULL,
cancellable, error);
}
gboolean
rpmostree_override_builtin_remove (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
{
GOptionContext *context;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
_cleanup_peer_ GPid peer_pid = 0;
context = g_option_context_new ("PACKAGE [PACKAGE...] - "
"Remove packages from the base layer");
if (!rpmostree_option_context_parse (context,
option_entries,
&argc, &argv,
invocation,
cancellable,
&install_pkgs,
&uninstall_pkgs,
&sysroot_proxy,
&peer_pid,
error))
return EXIT_FAILURE;
if (argc < 2)
{
rpmostree_usage_error (context, "At least one PACKAGE must be specified",
error);
return EXIT_FAILURE;
}
/* shift to first pkgspec and ensure it's a proper strv (previous parsing
* might have moved args around) */
argv++; argc--;
argv[argc] = NULL;
return handle_override (sysroot_proxy,
(const char *const*)argv, NULL, NULL,
cancellable, error);
}
gboolean
rpmostree_override_builtin_reset (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
{
GOptionContext *context;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
_cleanup_peer_ GPid peer_pid = 0;
context = g_option_context_new ("PACKAGE [PACKAGE...] - "
"Reset currently active package overrides");
g_option_context_add_main_entries (context, reset_option_entries, NULL);
if (!rpmostree_option_context_parse (context,
option_entries,
&argc, &argv,
invocation,
cancellable,
&install_pkgs,
&uninstall_pkgs,
&sysroot_proxy,
&peer_pid,
error))
return EXIT_FAILURE;
if (argc < 2 && !opt_reset_all)
{
rpmostree_usage_error (context, "At least one PACKAGE must be specified",
error);
return EXIT_FAILURE;
}
else if (opt_reset_all && argc >= 2)
{
rpmostree_usage_error (context, "Cannot specify PACKAGEs with --all",
error);
return EXIT_FAILURE;
}
/* shift to first pkgspec and ensure it's a proper strv (previous parsing
* might have moved args around) */
argv++; argc--;
argv[argc] = NULL;
return handle_override (sysroot_proxy,
NULL, NULL, (const char *const*)argv,
cancellable, error);
}

View File

@ -0,0 +1,46 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Red Hat, Inc.
*
* 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.
*/
#pragma once
#include <ostree.h>
#include "rpmostree-builtins.h"
G_BEGIN_DECLS
gboolean
rpmostree_override_builtin_replace (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_override_builtin_remove (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_override_builtin_reset (int argc, char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error);
G_END_DECLS

View File

@ -76,7 +76,8 @@ pkg_change (RPMOSTreeSysroot *sysroot_proxy,
FALSE, /* allow-downgrade */
FALSE, /* skip-purge */
TRUE, /* no-pull-base */
opt_dry_run);
opt_dry_run,
FALSE); /* no-overrides */
gboolean met_local_pkg = FALSE;
for (const char *const* it = packages_to_add; it && *it; it++)
@ -91,6 +92,9 @@ pkg_change (RPMOSTreeSysroot *sysroot_proxy,
NULL, /* revision */
packages_to_add,
packages_to_remove,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
options,
&transaction_address,
cancellable,

View File

@ -231,6 +231,10 @@
"install-packages" (type 'as')
"uninstall-packages" (type 'as')
"install-local-packages" (type 'ah')
"override-remove-packages" (type 'as')
"override-reset-packages" (type 'as')
"override-replace-packages" (type 'as')
"override-replace-local-packages" (type 'ah')
Available options:
"reboot" (type 'b')
@ -248,6 +252,9 @@
"dry-run" (type 'b')
Stop short of deploying the new tree. If
layering packages, the pkg diff is printed.
"no-overrides" (type 'b')
Remove all active overrides. Not valid if any override
modifiers are specified.
-->
<method name="UpdateDeployment">
<arg type="a{sv}" name="modifiers" direction="in"/>

View File

@ -96,7 +96,7 @@ generate_baselayer_refs (OstreeSysroot *sysroot,
g_autofree char *base_rev = NULL;
if (!rpmostree_deployment_get_layered_info (repo, deployment, NULL,
&base_rev, NULL, error))
&base_rev, NULL, NULL, error))
goto out;
if (base_rev)
@ -192,7 +192,7 @@ clean_pkgcache_orphans (OstreeSysroot *sysroot,
gboolean is_layered;
if (!rpmostree_deployment_get_layered_info (repo, deployment, &is_layered,
NULL, NULL, error))
NULL, NULL, NULL, error))
return FALSE;
if (is_layered)

View File

@ -67,6 +67,7 @@ struct RpmOstreeSysrootUpgrader {
/* Used during tree construction */
OstreeRepoDevInoCache *devino_cache;
int tmprootfs_dfd;
RpmOstreeRefSack *rsack;
GPtrArray *overlay_packages; /* Finalized list of pkgs to overlay */
@ -164,7 +165,7 @@ rpmostree_sysroot_upgrader_initable_init (GInitable *initable,
if (!rpmostree_deployment_get_layered_info (self->repo,
self->origin_merge_deployment,
&is_layered, &self->base_revision,
NULL, error))
NULL, NULL, error))
return FALSE;
if (is_layered)
@ -188,6 +189,8 @@ rpmostree_sysroot_upgrader_finalize (GObject *object)
{
RpmOstreeSysrootUpgrader *self = RPMOSTREE_SYSROOT_UPGRADER (object);
g_clear_pointer (&self->rsack, rpmostree_refsack_unref);
if (self->tmprootfs_dfd != -1)
(void)close (self->tmprootfs_dfd);
@ -337,6 +340,12 @@ rpmostree_sysroot_upgrader_set_origin (RpmOstreeSysrootUpgrader *self,
self->origin = rpmostree_origin_dup (new_origin);
}
const char *
rpmostree_sysroot_upgrader_get_base (RpmOstreeSysrootUpgrader *self)
{
return self->base_revision;
}
OstreeDeployment*
rpmostree_sysroot_upgrader_get_merge_deployment (RpmOstreeSysrootUpgrader *self)
{
@ -461,6 +470,12 @@ checkout_base_tree (RpmOstreeSysrootUpgrader *self,
&self->tmprootfs_dfd, error))
return FALSE;
/* build a centralized rsack for it, since we need it in a few places */
self->rsack = rpmostree_get_refsack_for_root (self->tmprootfs_dfd, ".",
cancellable, error);
if (self->rsack == NULL)
return FALSE;
rpmostree_output_task_end ("done");
return TRUE;
@ -501,9 +516,64 @@ generate_treespec (RpmOstreeSysrootUpgrader *self)
sha256_nevra->len);
}
GHashTable *overrides_remove =
rpmostree_origin_get_overrides_remove (self->origin);
if (g_hash_table_size (overrides_remove) > 0)
{
g_autofree char **pkgv =
(char**) g_hash_table_get_keys_as_array (overrides_remove, NULL);
g_key_file_set_string_list (treespec, "tree",
"removed-base-packages",
(const char* const*)pkgv,
g_strv_length (pkgv));
}
return rpmostree_treespec_new_from_keyfile (treespec, NULL);
}
static gboolean
finalize_overrides (RpmOstreeSysrootUpgrader *self,
GCancellable *cancellable,
GError **error)
{
/* Removal overrides have the magical property that they drop out if the base layer no
* longer has them. This keeps the origin consistent. New removal overrides are checked in
* deploy_transaction_execute() to ensure they're valid; i.e. the pkgs exist. */
GHashTable *removals = rpmostree_origin_get_overrides_remove (self->origin);
if (g_hash_table_size (removals) == 0)
return TRUE;
/* NB: strings are owned by hash table */
g_autoptr(GPtrArray) removals_to_remove = g_ptr_array_new ();
GHashTableIter it;
gpointer itkey;
g_hash_table_iter_init (&it, removals);
while (g_hash_table_iter_next (&it, &itkey, NULL))
{
/* only match pkgname */
const char *pkgname = itkey;
hy_autoquery HyQuery query = hy_query_create (self->rsack->sack);
hy_query_filter (query, HY_PKG_NAME, HY_EQ, pkgname);
g_autoptr(GPtrArray) pkgs = hy_query_run (query);
if (pkgs->len == 0)
g_ptr_array_add (removals_to_remove, (gpointer)pkgname);
}
if (removals_to_remove->len > 0)
{
g_ptr_array_add (removals_to_remove, NULL);
if (!rpmostree_origin_remove_overrides (self->origin,
(char**)removals_to_remove->pdata, error))
return FALSE;
}
return TRUE;
}
/* Go through rpmdb and jot down the missing pkgs from the given set. Really, we
* don't *have* to do this: we could just give everything to libdnf and let it
* figure out what is already installed. The advantage of doing it ourselves is
@ -526,12 +596,6 @@ finalize_packages_to_overlay (RpmOstreeSysrootUpgrader *self,
GHashTableIter it;
gpointer itkey, itval;
g_autoptr(RpmOstreeRefSack) rsack =
rpmostree_get_refsack_for_root (self->tmprootfs_dfd, ".",
cancellable, error);
if (rsack == NULL)
goto out;
/* Add the local pkgs as if they were installed: since they're unconditionally
* layered, we treat them as part of the base wrt regular requested pkgs. E.g.
* you can have foo-1.0-1.x86_64 layered, and foo or /usr/bin/foo as dormant.
@ -571,23 +635,53 @@ finalize_packages_to_overlay (RpmOstreeSysrootUpgrader *self,
/* Also check if that exact NEVRA is already in the root (if the pkg
* exists, but is a different EVR, depsolve will catch that). In the
* future, we'll allow packages to replace base pkgs. */
if (rpmostree_sack_has_subject (rsack->sack, nevra))
if (rpmostree_sack_has_subject (self->rsack->sack, nevra))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Package '%s' is already in the base", nevra);
goto out;
}
dnf_sack_add_cmdline_package (rsack->sack, path);
dnf_sack_add_cmdline_package (self->rsack->sack, path);
}
}
GHashTable *removals = rpmostree_origin_get_overrides_remove (self->origin);
/* check for each package if we have a provides or a path match */
g_hash_table_iter_init (&it, rpmostree_origin_get_packages (self->origin));
while (g_hash_table_iter_next (&it, &itkey, NULL))
{
if (!rpmostree_sack_has_subject (rsack->sack, itkey))
g_ptr_array_add (ret_missing_pkgs, g_strdup (itkey));
const char *pattern = itkey;
g_autoptr(GPtrArray) matches =
rpmostree_get_matching_packages (self->rsack->sack, pattern);
if (matches->len == 0)
{
/* no matches, so we'll need to layer it */
g_ptr_array_add (ret_missing_pkgs, g_strdup (itkey));
continue;
}
/* Error out if it matches a base package that was also requested to be removed.
* Conceptually, we want users to use override replacements, not remove+overlay. */
for (guint i = 0; i < matches->len; i++)
{
DnfPackage *pkg = matches->pdata[i];
const char *name = dnf_package_get_name (pkg);
const char *repo = dnf_package_get_reponame (pkg);
if (g_strcmp0 (repo, HY_CMDLINE_REPO_NAME) == 0)
continue; /* local RPM added up above */
if (g_hash_table_contains (removals, name))
{
glnx_throw (error, "Cannot request '%s' provided by removed package '%s'",
pattern, dnf_package_get_nevra (pkg));
}
}
/* otherwise, it's a dormant package */
}
self->overlay_packages = g_steal_pointer (&ret_missing_pkgs);
@ -665,15 +759,18 @@ do_local_assembly (RpmOstreeSysrootUpgrader *self,
treespec, cancellable, error))
return FALSE;
glnx_unref_object OstreeRepo *pkgcache_repo = NULL;
g_autoptr(OstreeRepo) pkgcache_repo = NULL;
if (!rpmostree_get_pkgcache_repo (self->repo, &pkgcache_repo,
cancellable, error))
return FALSE;
rpmostree_context_set_repos (ctx, self->repo, pkgcache_repo);
GHashTable *overrides_remove =
rpmostree_origin_get_overrides_remove (self->origin);
const gboolean have_packages = (self->overlay_packages->len > 0 ||
g_hash_table_size (local_pkgs) > 0);
g_hash_table_size (local_pkgs) > 0 ||
g_hash_table_size (overrides_remove) > 0);
if (have_packages)
{
@ -699,10 +796,11 @@ do_local_assembly (RpmOstreeSysrootUpgrader *self,
if (!rpmostree_context_relabel (ctx, cancellable, error))
return FALSE;
/* --- Overlay and commit --- */
g_clear_pointer (&self->final_revision, g_free);
gboolean noscripts =
(self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGOVERLAY_NOSCRIPTS) > 0;
/* --- override/overlay and commit --- */
if (!rpmostree_context_assemble_tmprootfs (ctx, self->tmprootfs_dfd,
self->devino_cache, noscripts,
cancellable, error))
@ -761,6 +859,8 @@ static gboolean
requires_local_assembly (RpmOstreeSysrootUpgrader *self)
{
GHashTable *local_pkgs = rpmostree_origin_get_local_packages (self->origin);
GHashTable *overrides_remove =
rpmostree_origin_get_overrides_remove (self->origin);
/* Now, it's possible all requested packages are in the new tree, so we have
* another optimization here for that case. This is a bit tricky: assuming we
@ -772,6 +872,7 @@ requires_local_assembly (RpmOstreeSysrootUpgrader *self)
*/
return g_hash_table_size (local_pkgs) > 0 ||
g_hash_table_size (overrides_remove) > 0 ||
self->overlay_packages->len > 0 ||
rpmostree_origin_get_regenerate_initramfs (self->origin);
}
@ -785,6 +886,9 @@ maybe_do_local_assembly (RpmOstreeSysrootUpgrader *self,
if (!checkout_base_tree (self, cancellable, error))
return FALSE;
if (!finalize_overrides (self, cancellable, error))
return FALSE;
if (!finalize_packages_to_overlay (self, cancellable, error))
return FALSE;

View File

@ -72,6 +72,9 @@ void
rpmostree_sysroot_upgrader_set_origin (RpmOstreeSysrootUpgrader *self,
RpmOstreeOrigin *origin);
const char *
rpmostree_sysroot_upgrader_get_base (RpmOstreeSysrootUpgrader *self);
gboolean
rpmostree_sysroot_upgrader_pull (RpmOstreeSysrootUpgrader *self,
const char *dir_to_pull,

View File

@ -158,6 +158,15 @@ variant_add_commit_details (GVariantDict *dict,
"t", timestamp);
}
static void
variant_add_from_hash_table (GVariantDict *dict,
const char *key,
GHashTable *table)
{
g_autofree char **values = (char**)g_hash_table_get_keys_as_array (table, NULL);
g_variant_dict_insert (dict, key, "^as", values);
}
GVariant *
rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot,
OstreeDeployment *deployment,
@ -184,6 +193,8 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot,
g_autofree char *live_inprogress = NULL;
g_autofree char *live_replaced = NULL;
g_auto(GStrv) layered_pkgs = NULL;
g_auto(GStrv) removed_base_pkgs = NULL;
const char *const empty_v[] = { NULL };
if (!ostree_repo_load_variant (repo,
OSTREE_OBJECT_TYPE_COMMIT,
@ -208,9 +219,8 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot,
g_variant_dict_insert (&dict, "serial", "i", serial);
g_variant_dict_insert (&dict, "checksum", "s", csum);
if (!rpmostree_deployment_get_layered_info (repo, deployment, &is_layered,
&base_checksum, &layered_pkgs,
error))
if (!rpmostree_deployment_get_layered_info (repo, deployment, &is_layered, &base_checksum,
&layered_pkgs, &removed_base_pkgs, error))
return NULL;
if (is_layered)
@ -264,28 +274,21 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot,
g_variant_dict_insert (&dict, "origin", "s", refspec);
g_autofree char **requested_pkgs =
(char**)g_hash_table_get_keys_as_array (rpmostree_origin_get_packages (origin), NULL);
g_variant_dict_insert (&dict, "requested-packages", "^as", requested_pkgs);
variant_add_from_hash_table (&dict, "requested-packages",
rpmostree_origin_get_packages (origin));
variant_add_from_hash_table (&dict, "requested-local-packages",
rpmostree_origin_get_local_packages (origin));
g_autofree char **pkgs =
(char**)g_hash_table_get_keys_as_array (rpmostree_origin_get_local_packages (origin), NULL);
g_variant_dict_insert (&dict, "requested-local-packages", "^as", pkgs);
if (is_layered && g_strv_length (layered_pkgs) > 0)
g_variant_dict_insert (&dict, "packages", "^as", layered_pkgs);
else
{
const char *const p[] = { NULL };
g_variant_dict_insert (&dict, "packages", "^as", p);
}
g_variant_dict_insert (&dict, "packages", "^as", layered_pkgs ?: (char**)empty_v);
g_variant_dict_insert (&dict, "removed-base-packages", "^as",
removed_base_pkgs ?: (char**)empty_v);
if (sigs != NULL)
g_variant_dict_insert_value (&dict, "signatures", sigs);
g_variant_dict_insert (&dict, "gpg-enabled", "b", gpg_enabled);
g_variant_dict_insert (&dict, "unlocked", "s",
ostree_deployment_unlocked_state_to_string (ostree_deployment_get_unlocked (deployment)));
ostree_deployment_unlocked_state_to_string (ostree_deployment_get_unlocked (deployment)));
g_variant_dict_insert (&dict, "regenerate-initramfs", "b",
rpmostree_origin_get_regenerate_initramfs (origin));

View File

@ -198,7 +198,7 @@ out:
else
{
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@a(sua{sv}))", value));
g_variant_new ("(@a(sua{sv}))", value));
}
return TRUE;
@ -264,7 +264,7 @@ os_handle_get_cached_update_rpm_diff (RPMOSTreeOS *interface,
details = rpmostreed_commit_generate_cached_details_variant (base_deployment,
ot_repo,
rpmostree_origin_get_refspec (origin),
&local_error);
&local_error);
if (!details)
goto out;
@ -276,7 +276,7 @@ out:
else
{
g_dbus_method_invocation_return_value (invocation,
new_variant_diff_result (value, details));
new_variant_diff_result (value, details));
}
return TRUE;
@ -390,6 +390,8 @@ deploy_flags_from_options (GVariant *options,
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE;
if (vardict_lookup_bool (&dict, "dry-run", FALSE))
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN;
if (vardict_lookup_bool (&dict, "no-overrides", FALSE))
ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES;
return ret;
}
@ -403,6 +405,10 @@ start_deployment_txn (GDBusMethodInvocation *invocation,
const char *const *pkgs_to_add,
const char *const *pkgs_to_remove,
GVariant *local_pkgs_to_add,
const char *const *override_replace_pkgs,
GVariant *override_replace_local_pkgs,
const char *const *override_remove_pkgs,
const char *const *override_reset_pkgs,
GUnixFDList *fd_list,
GError **error)
{
@ -442,6 +448,16 @@ start_deployment_txn (GDBusMethodInvocation *invocation,
return glnx_null_throw (error, "Can't specify no-pull-base if setting a "
"new refspec or revision");
/* NB: remove HIDDEN attribute on "replace" cmdline once we implement this */
if (override_replace_pkgs || override_replace_local_pkgs)
return glnx_null_throw (error, "Replacement overrides not implemented yet");
if (vardict_lookup_bool (&options_dict, "no-overrides", FALSE) &&
(override_remove_pkgs || override_reset_pkgs ||
override_replace_pkgs || override_replace_local_pkgs))
return glnx_null_throw (error, "Can't specify no-overrides if setting "
"override modifiers");
/* default to allowing downgrades for rebases & deploys */
if (vardict_lookup_bool (&options_dict, "allow-downgrade", refspec ||
revision))
@ -456,6 +472,8 @@ start_deployment_txn (GDBusMethodInvocation *invocation,
pkgs_to_add,
pkgs_to_remove,
fd_list,
override_remove_pkgs,
override_reset_pkgs,
cancellable, error);
}
@ -474,6 +492,10 @@ os_merge_or_start_deployment_txn (RPMOSTreeOS *interface,
const char *const *pkgs_to_add,
const char *const *pkgs_to_remove,
GVariant *local_pkgs_to_add,
const char *const *override_replace_pkgs,
GVariant *override_replace_local_pkgs,
const char *const *override_remove_pkgs,
const char *const *override_reset_pkgs,
GUnixFDList *fd_list,
InvocationCompleter completer)
{
@ -490,7 +512,12 @@ os_merge_or_start_deployment_txn (RPMOSTreeOS *interface,
rpmostree_os_get_name (interface),
refspec, revision, default_flags,
options, pkgs_to_add, pkgs_to_remove,
local_pkgs_to_add, fd_list,
local_pkgs_to_add,
override_replace_pkgs,
override_replace_local_pkgs,
override_remove_pkgs,
override_reset_pkgs,
fd_list,
&local_error);
if (transaction)
rpmostreed_transaction_monitor_add (self->transaction_monitor,
@ -533,6 +560,10 @@ os_handle_deploy (RPMOSTreeOS *interface,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
rpmostree_os_complete_deploy);
}
@ -553,6 +584,10 @@ os_handle_upgrade (RPMOSTreeOS *interface,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
rpmostree_os_complete_upgrade);
}
@ -582,6 +617,10 @@ os_handle_rebase (RPMOSTreeOS *interface,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
rpmostree_os_complete_rebase);
}
@ -604,6 +643,10 @@ os_handle_pkg_change (RPMOSTreeOS *interface,
arg_packages_removed,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
rpmostree_os_complete_pkg_change);
}
@ -624,9 +667,18 @@ os_handle_update_deployment (RPMOSTreeOS *interface,
vardict_lookup_ptr (&dict, "install-packages", "^a&s");
g_autofree const char *const *uninstall_pkgs =
vardict_lookup_ptr (&dict, "uninstall-packages", "^a&s");
g_autofree const char *const *override_replace_pkgs =
vardict_lookup_ptr (&dict, "override-replace-packages", "^a&s");
g_autofree const char *const *override_remove_pkgs =
vardict_lookup_ptr (&dict, "override-remove-packages", "^a&s");
g_autofree const char *const *override_reset_pkgs =
vardict_lookup_ptr (&dict, "override-reset-packages", "^a&s");
g_autoptr(GVariant) install_local_pkgs =
g_variant_dict_lookup_value (&dict, "install-local-packages",
G_VARIANT_TYPE("ah"));
g_autoptr(GVariant) override_replace_local_pkgs =
g_variant_dict_lookup_value (&dict, "override-replace-local-packages",
G_VARIANT_TYPE("ah"));
return os_merge_or_start_deployment_txn (
interface,
@ -638,6 +690,10 @@ os_handle_update_deployment (RPMOSTreeOS *interface,
install_pkgs,
uninstall_pkgs,
install_local_pkgs,
override_replace_pkgs,
override_replace_local_pkgs,
override_remove_pkgs,
override_reset_pkgs,
fd_list,
rpmostree_os_complete_update_deployment);
}

View File

@ -27,6 +27,7 @@
#include "rpmostreed-sysroot.h"
#include "rpmostree-sysroot-upgrader.h"
#include "rpmostree-sysroot-core.h"
#include "rpmostree-rpm-util.h"
#include "rpmostree-util.h"
#include "rpmostree-output.h"
#include "rpmostree-core.h"
@ -419,6 +420,8 @@ typedef struct {
char *revision; /* NULL for upgrade */
char **packages_added;
char **packages_removed;
char **override_remove_packages;
char **override_reset_packages;
GUnixFDList *local_packages_added;
} DeployTransaction;
@ -441,6 +444,8 @@ deploy_transaction_finalize (GObject *object)
g_free (self->revision);
g_strfreev (self->packages_added);
g_strfreev (self->packages_removed);
g_strfreev (self->override_remove_packages);
g_strfreev (self->override_reset_packages);
g_clear_pointer (&self->local_packages_added, g_object_unref);
G_OBJECT_CLASS (deploy_transaction_parent_class)->finalize (object);
@ -509,6 +514,15 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN;
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NOSCRIPTS)
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGOVERLAY_NOSCRIPTS;
gboolean no_overrides =
((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES) > 0);
/* this should have been checked already */
if (no_overrides)
{
g_assert (self->override_remove_packages == NULL);
g_assert (self->override_reset_packages == NULL);
}
if (self->refspec)
{
@ -555,14 +569,27 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
{
rpmostree_origin_set_override_commit (origin, NULL, NULL);
}
gboolean is_install = FALSE;
gboolean is_override = FALSE;
/* In practice today */
const gboolean is_install = self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE
&& !self->revision;
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE)
{
/* this is a heuristic; by the end, once the proper switches are added, the two
* commands can look indistinguishable at the D-Bus level */
is_override = (self->override_reset_packages ||
self->override_remove_packages ||
no_overrides);
is_install = !is_override;
}
/* https://github.com/projectatomic/rpm-ostree/issues/454 */
g_autoptr(GString) txn_title = g_string_new ("");
if (is_install)
g_string_append (txn_title, "install");
else if (is_override)
g_string_append (txn_title, "override");
else if (self->refspec)
g_string_append (txn_title, "rebase");
else if (self->revision)
@ -578,7 +605,8 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
/* in reality, there may not be any new layer required (if e.g. we're
* removing a duplicate provides), though the origin has changed so we
* need to create a new deployment */
* need to create a new deployment -- see also
* https://github.com/projectatomic/rpm-ostree/issues/753 */
changed = TRUE;
}
@ -587,6 +615,8 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
if (!rpmostree_origin_add_packages (origin, self->packages_added, FALSE, error))
return FALSE;
/* here too -- we could optimize this under certain conditions
* (see related blurb in maybe_do_local_assembly()) */
changed = TRUE;
}
@ -620,6 +650,21 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
changed = TRUE;
}
if (no_overrides)
{
gboolean overrides_changed = FALSE;
if (!rpmostree_origin_remove_all_overrides (origin, &overrides_changed, error))
return FALSE;
changed = changed || overrides_changed;
}
else if (self->override_reset_packages)
{
if (!rpmostree_origin_remove_overrides (origin, self->override_reset_packages, error))
return FALSE;
changed = TRUE;
}
if (self->packages_removed || self->packages_added || self->local_packages_added)
g_string_append_printf (txn_title, "; remove: %u install: %u; localinstall: %u",
@ -631,7 +676,7 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
rpmostree_sysroot_upgrader_set_origin (upgrader, origin);
/* Mainly for the `install` command */
/* Mainly for the `install` and `override` commands */
if (!(self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE))
{
gboolean base_changed;
@ -643,6 +688,51 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
changed = changed || base_changed;
}
/* let's figure out if those new overrides are valid and if so, canonicalize
* them -- we could have just pulled the rpmdb dir before to do this, and then
* do the full pull afterwards, though that would complicate the pull code and
* anyway in the common case even if there's an error with the overrides,
* users will fix it and try again, so the second pull will be a no-op */
if (self->override_remove_packages)
{
const char *base = rpmostree_sysroot_upgrader_get_base (upgrader);
g_autoptr(RpmOstreeRefSack) rsack =
rpmostree_get_refsack_for_commit (repo, base, cancellable, error);
if (rsack == NULL)
return FALSE;
/* NB: the strings are owned by the sack pool */
g_autoptr(GPtrArray) pkgnames = g_ptr_array_new ();
for (char **it = self->override_remove_packages; it && *it; it++)
{
const char *pkg = *it;
g_autoptr(GPtrArray) pkgs =
rpmostree_get_matching_packages (rsack->sack, pkg);
if (pkgs->len == 0)
return glnx_throw (error, "No package \"%s\" in base commit %.7s", pkg, base);
/* either the subject was somehow too broad, or it's one of the rare
* packages that supports installonly (e.g. kernel, though that one
* specifically should never have multiple instances in a compose),
* which you'd never want to remove */
if (pkgs->len > 1)
return glnx_throw (error, "Multiple packages match \"%s\"", pkg);
/* canonicalize to just the pkg name */
const char *pkgname = dnf_package_get_name (pkgs->pdata[0]);
g_ptr_array_add (pkgnames, (void*)pkgname);
}
g_ptr_array_add (pkgnames, NULL);
if (!rpmostree_origin_add_overrides (origin, (char**)pkgnames->pdata, TRUE, error))
return FALSE;
rpmostree_sysroot_upgrader_set_origin (upgrader, origin);
changed = TRUE;
}
rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction));
/* TODO - better logic for "changed" based on deployments */
@ -716,6 +806,8 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
const char *const *packages_added,
const char *const *packages_removed,
GUnixFDList *local_packages_added,
const char *const *override_remove_packages,
const char *const *override_reset_packages,
GCancellable *cancellable,
GError **error)
{
@ -741,6 +833,10 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
self->packages_removed = strdupv_canonicalize (packages_removed);
if (local_packages_added != NULL)
self->local_packages_added = g_object_ref (local_packages_added);
self->override_remove_packages =
strdupv_canonicalize (override_remove_packages);
self->override_reset_packages =
strdupv_canonicalize (override_reset_packages);
}
return (RpmostreedTransaction *) self;

View File

@ -53,22 +53,25 @@ typedef enum {
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_ALLOW_DOWNGRADE = (1 << 2),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE = (1 << 4),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN = (1 << 5),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NOSCRIPTS = (1 << 6)
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NOSCRIPTS = (1 << 6),
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES = (1 << 7)
} RpmOstreeTransactionDeployFlags;
RpmostreedTransaction *
rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
OstreeSysroot *sysroot,
RpmOstreeTransactionDeployFlags flags,
const char *osname,
const char *refspec,
const char *revision,
const char *const *packages_added,
const char *const *packages_removed,
GUnixFDList *local_packages_added,
GCancellable *cancellable,
GError **error);
rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
OstreeSysroot *sysroot,
RpmOstreeTransactionDeployFlags flags,
const char *osname,
const char *refspec,
const char *revision,
const char *const *packages_added,
const char *const *packages_removed,
GUnixFDList *local_packages_added,
const char *const *override_remove_packages,
const char *const *override_reset_packages,
GCancellable *cancellable,
GError **error);
RpmostreedTransaction *
rpmostreed_transaction_new_initramfs_state (GDBusMethodInvocation *invocation,

View File

@ -126,10 +126,11 @@ add_canonicalized_string_array (GVariantBuilder *builder,
}
sorted = (char**)g_hash_table_get_keys_as_array (set, &count);
g_qsort_with_data (sorted, count, sizeof (void*), qsort_cmpstr, NULL);
if (count > 1)
g_qsort_with_data (sorted, count, sizeof (void*), qsort_cmpstr, NULL);
g_variant_builder_add (builder, "{sv}", key,
g_variant_new_strv ((const char*const*)sorted, g_strv_length (sorted)));
g_variant_new_strv ((const char*const*)sorted, count));
}
static GPtrArray *
@ -165,6 +166,8 @@ rpmostree_treespec_new_from_keyfile (GKeyFile *keyfile,
add_canonicalized_string_array (&builder, "packages", NULL, keyfile);
add_canonicalized_string_array (&builder, "cached-packages", NULL, keyfile);
add_canonicalized_string_array (&builder, "removed-base-packages",
NULL, keyfile);
/* We allow the "repo" key to be missing. This means that we rely on hif's
* normal behaviour (i.e. look at repos in repodir with enabled=1). */
@ -1246,26 +1249,38 @@ join_package_list (const char *prefix, char **pkgs, int len)
static gboolean
check_goal_solution (HyGoal goal,
const char *const *allowed_base_removals,
GError **error)
{
g_autoptr(GPtrArray) packages = NULL;
/* Now we need to make sure that none of the pkgs in the base are marked for
* removal. The issue is that marking a package for DNF_INSTALL could
* uninstall a base pkg if it's an update or obsoletes it. There doesn't seem
* to be a way to tell libsolv to never touch pkgs in the base layer, so we
* just inspect its solution in retrospect. libdnf has the concept of
/* Now we need to make sure that only the pkgs in the base allowed to be
* removed are removed. The issue is that marking a package for DNF_INSTALL
* could uninstall a base pkg if it's an update or obsoletes it. There doesn't
* seem to be a way to tell libsolv to not touch some pkgs in the base layer,
* so we just inspect its solution in retrospect. libdnf has the concept of
* protected packages, but it still allows updating protected packages. */
packages = dnf_goal_get_packages (goal,
DNF_PACKAGE_INFO_REMOVE,
DNF_PACKAGE_INFO_OBSOLETE,
-1);
if (packages->len > 0)
g_autoptr(GPtrArray) filtered_packages = g_ptr_array_new ();
for (guint i = 0; i < packages->len; i++)
{
DnfPackage *pkg = packages->pdata[i];
if (g_strv_contains (allowed_base_removals, dnf_package_get_name (pkg)))
continue;
g_ptr_array_add (filtered_packages, pkg);
}
if (filtered_packages->len > 0)
{
g_autofree char *msg = join_package_list (
"The following base packages would be removed: ",
(char**)packages->pdata, packages->len);
(char**)filtered_packages->pdata, filtered_packages->len);
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg);
return FALSE;
}
@ -1310,7 +1325,6 @@ rpmostree_context_prepare (RpmOstreeContext *self,
g_assert (!self->empty);
DnfContext *hifctx = self->hifctx;
g_autofree char **pkgnames = NULL;
g_assert (g_variant_dict_lookup (self->spec->dict, "packages",
"^a&s", &pkgnames));
@ -1319,6 +1333,10 @@ rpmostree_context_prepare (RpmOstreeContext *self,
g_assert (g_variant_dict_lookup (self->spec->dict, "cached-packages",
"^a&s", &cached_pkgnames));
g_autofree char **removed_base_pkgnames = NULL;
g_assert (g_variant_dict_lookup (self->spec->dict, "removed-base-packages",
"^a&s", &removed_base_pkgnames));
/* setup sack if not yet set up */
if (dnf_context_get_sack (hifctx) == NULL)
{
@ -1391,29 +1409,35 @@ rpmostree_context_prepare (RpmOstreeContext *self,
NULL);
}
{ const char *const*strviter = (const char *const*)pkgnames;
for (; strviter && *strviter; strviter++)
{
const char *pkgname = *strviter;
if (!dnf_context_install (hifctx, pkgname, error))
return FALSE;
}
}
for (char **it = pkgnames; it && *it; it++)
{
const char *pkgname = *it;
if (!dnf_context_install (hifctx, pkgname, error))
return FALSE;
}
for (char **it = removed_base_pkgnames; it && *it; it++)
{
const char *pkgname = *it;
if (!dnf_context_remove (hifctx, pkgname, error))
return FALSE;
}
rpmostree_output_task_begin ("Resolving dependencies");
if (!dnf_goal_depsolve (goal, DNF_INSTALL, error) ||
!check_goal_solution (goal, error))
/* XXX: consider a --allow-uninstall switch? */
if (!dnf_goal_depsolve (goal, DNF_INSTALL | DNF_ALLOW_UNINSTALL, error) ||
!check_goal_solution (goal, (const char *const*)removed_base_pkgnames, error))
{
g_print ("failed\n");
return FALSE;
}
rpmostree_output_task_end ("done");
if (!sort_packages (self, error))
return FALSE;
rpmostree_output_task_end ("done");
return TRUE;
}
@ -1715,6 +1739,88 @@ checkout_package_into_root (RpmOstreeContext *self,
return TRUE;
}
static Header
get_rpmdb_pkg_header (rpmts rpmdb_ts,
DnfPackage *pkg,
GCancellable *cancellable,
GError **error)
{
g_auto(rpmts) rpmdb_ts_owned = NULL;
if (!rpmdb_ts) /* allow callers to pass NULL */
rpmdb_ts = rpmdb_ts_owned = rpmtsCreate ();
unsigned int dbid = dnf_package_get_rpmdbid (pkg);
g_assert (dbid > 0);
g_auto(rpmdbMatchIterator) it =
rpmtsInitIterator (rpmdb_ts, RPMDBI_PACKAGES, &dbid, sizeof(dbid));
Header hdr = it ? rpmdbNextIterator (it) : NULL;
if (hdr == NULL)
return glnx_null_throw (error, "Failed to find package '%s' in rpmdb",
dnf_package_get_nevra (pkg));
return headerLink (hdr);
}
static gboolean
delete_package_from_root (RpmOstreeContext *self,
rpmte pkg,
int rootfs_dfd,
GCancellable *cancellable,
GError **error)
{
g_auto(rpmfiles) files = rpmteFiles (pkg);
g_auto(rpmfi) fi = rpmfilesIter (files, RPMFI_ITER_FWD);
g_autoptr(GPtrArray) deleted_dirs = g_ptr_array_new_with_free_func (g_free);
int i;
while ((i = rpmfiNext (fi)) >= 0)
{
/* see also apply_rpmfi_overrides() for a commented version of the loop */
const char *fn = rpmfiFN (fi);
rpm_mode_t mode = rpmfiFMode (fi);
if (!(S_ISREG (mode) ||
S_ISLNK (mode) ||
S_ISDIR (mode)))
continue;
g_assert (fn != NULL);
fn += strspn (fn, "/");
g_assert (fn[0]);
g_autofree char *fn_owned = NULL;
if (g_str_has_prefix (fn, "etc/"))
fn = fn_owned = g_strconcat ("usr/", fn, NULL);
/* for now, we only remove files from /usr */
if (!g_str_has_prefix (fn, "usr/"))
continue;
/* avoiding the stat syscall is worth a bit of userspace computation */
if (rpmostree_str_has_prefix_in_ptrarray (fn, deleted_dirs))
continue;
struct stat stbuf;
if (fstatat (rootfs_dfd, fn, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
{
if (errno == ENOENT)
continue; /* a job well done */
return glnx_throw_errno_prefix (error, "fstatat(%s)", fn);
}
if (!glnx_shutil_rm_rf_at (rootfs_dfd, fn, cancellable, error))
return FALSE;
if (S_ISDIR (mode))
g_ptr_array_add (deleted_dirs, g_strconcat (fn, "/", NULL));
}
return TRUE;
}
/* Given a path to a file/symlink, make a copy (reflink if possible)
* of it if it's a hard link. We need this for three places right now:
* - The RPM database
@ -2159,17 +2265,16 @@ get_package_metainfo (RpmOstreeContext *self,
}
static gboolean
add_to_transaction (RpmOstreeContext *self,
rpmts ts,
DnfPackage *pkg,
gboolean noscripts,
GHashTable *ignore_scripts,
GCancellable *cancellable,
GError **error)
rpmts_add_install (RpmOstreeContext *self,
rpmts ts,
DnfPackage *pkg,
gboolean noscripts,
GHashTable *ignore_scripts,
GCancellable *cancellable,
GError **error)
{
g_auto(Header) hdr = NULL;
g_autofree char *path = get_package_relpath (pkg);
int r;
if (!get_package_metainfo (self, path, &hdr, NULL, error))
return FALSE;
@ -2180,16 +2285,28 @@ add_to_transaction (RpmOstreeContext *self,
return FALSE;
}
r = rpmtsAddInstallElement (ts, hdr, pkg, TRUE, NULL);
if (r != 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to add install element for %s",
dnf_package_get_filename (pkg));
return FALSE;
}
if (rpmtsAddInstallElement (ts, hdr, pkg, TRUE, NULL) != 0)
return glnx_throw (error, "Failed to add install element for %s",
dnf_package_get_filename (pkg));
return TRUE;
}
static gboolean
rpmts_add_erase (RpmOstreeContext *self,
rpmts ts,
DnfPackage *pkg,
GCancellable *cancellable,
GError **error)
{
/* NB: we're not running any %*un scriptlets right now */
g_auto(Header) hdr = get_rpmdb_pkg_header (ts, pkg, cancellable, error);
if (hdr == NULL)
return FALSE;
if (rpmtsAddEraseElement (ts, hdr, -1))
return glnx_throw (error, "Failed to add erase element for package '%s'",
dnf_package_get_nevra (pkg));
return TRUE;
}
@ -2427,6 +2544,7 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
g_auto(rpmts) ordering_ts = rpmtsCreate ();
rpmtsSetRootDir (ordering_ts, dnf_context_get_install_root (hifctx));
/* First for the ordering TS, set the dbpath to relative, which will also gain
* the root dir.
*/
@ -2438,63 +2556,84 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
*/
rpmtsSetVSFlags (ordering_ts, _RPMVSF_NOSIGNATURES | _RPMVSF_NODIGESTS | RPMTRANS_FLAG_TEST);
g_autoptr(GPtrArray) overlays =
dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_INSTALL,
-1);
g_autoptr(GPtrArray) overrides_remove =
dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_REMOVE,
DNF_PACKAGE_INFO_OBSOLETE,
-1);
if (overlays->len == 0 && overrides_remove->len == 0)
return glnx_throw (error, "No packages in transaction");
/* Tell librpm about each one so it can tsort them. What we really
* want is to do this from the rpm-md metadata so that we can fully
* parallelize download + unpack.
*/
{ g_autoptr(GPtrArray) package_list = NULL;
package_list = dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_INSTALL,
-1);
for (guint i = 0; i < overrides_remove->len; i++)
{
DnfPackage *pkg = overrides_remove->pdata[i];
if (!rpmts_add_erase (self, ordering_ts, pkg, cancellable, error))
return FALSE;
}
if (package_list->len == 0)
return glnx_throw (error, "No packages in installation set");
for (guint i = 0; i < overlays->len; i++)
{
DnfPackage *pkg = overlays->pdata[i];
g_autofree char *cachebranch = rpmostree_get_cache_branch_pkg (pkg);
g_autofree char *cached_rev = NULL;
gboolean sepolicy_matches;
for (guint i = 0; i < package_list->len; i++)
{
DnfPackage *pkg = package_list->pdata[i];
g_autofree char *cachebranch = rpmostree_get_cache_branch_pkg (pkg);
g_autofree char *cached_rev = NULL;
gboolean sepolicy_matches;
if (!ostree_repo_resolve_rev (pkgcache_repo, cachebranch, FALSE,
&cached_rev, error))
return FALSE;
if (!ostree_repo_resolve_rev (pkgcache_repo, cachebranch, FALSE,
&cached_rev, error))
return FALSE;
if (self->sepolicy)
{
if (!commit_has_matching_sepolicy (pkgcache_repo, cached_rev,
self->sepolicy, &sepolicy_matches,
error))
return FALSE;
if (self->sepolicy)
{
if (!commit_has_matching_sepolicy (pkgcache_repo, cached_rev,
self->sepolicy, &sepolicy_matches,
error))
return FALSE;
/* We already did any relabeling/reimporting above */
g_assert (sepolicy_matches);
}
/* We already did any relabeling/reimporting above */
g_assert (sepolicy_matches);
}
if (!checkout_pkg_metadata_by_dnfpkg (self, pkg, cancellable, error))
return FALSE;
if (!checkout_pkg_metadata_by_dnfpkg (self, pkg, cancellable, error))
return FALSE;
if (!rpmts_add_install (self, ordering_ts, pkg,
noscripts, self->ignore_scripts,
cancellable, error))
return FALSE;
if (!add_to_transaction (self, ordering_ts, pkg,
noscripts, self->ignore_scripts,
cancellable, error))
return FALSE;
g_hash_table_insert (pkg_to_ostree_commit, g_object_ref (pkg), g_steal_pointer (&cached_rev));
g_hash_table_insert (pkg_to_ostree_commit, g_object_ref (pkg), g_steal_pointer (&cached_rev));
if (strcmp (dnf_package_get_name (pkg), "filesystem") == 0)
filesystem_package = g_object_ref (pkg);
}
}
if (strcmp (dnf_package_get_name (pkg), "filesystem") == 0)
filesystem_package = g_object_ref (pkg);
}
{ DECLARE_RPMSIGHANDLER_RESET;
rpmtsOrder (ordering_ts);
}
rpmostree_output_task_begin ("Overlaying");
if (overrides_remove->len > 0 && overlays->len > 0)
rpmostree_output_task_begin ("Applying %u overrides and %u overlays",
overrides_remove->len, overlays->len);
else if (overrides_remove->len > 0)
rpmostree_output_task_begin ("Applying %u overrides", overrides_remove->len);
else if (overlays->len > 0)
rpmostree_output_task_begin ("Applying %u overlays", overlays->len);
else
g_assert_not_reached ();
guint n_rpmts_elements = (guint)rpmtsNElements (ordering_ts);
g_assert (n_rpmts_elements > 0);
/* Okay so what's going on in Fedora with incestuous relationship
* between the `filesystem`, `setup`, `libgcc` RPMs is actively
@ -2515,42 +2654,29 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
cancellable, error))
return FALSE;
}
else
{
/* Otherwise, we unpack the first package to get the initial
* rootfs dir.
*/
rpmte te = rpmtsElement (ordering_ts, 0);
DnfPackage *pkg = (void*)rpmteKey (te);
g_assert (pkg);
if (!checkout_package_into_root (self, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
return FALSE;
filesystem_package = g_object_ref (pkg);
}
for (guint i = 0; i < n_rpmts_elements; i++)
{
rpmte te = rpmtsElement (ordering_ts, i);
DnfPackage *pkg = (void*)rpmteKey (te);
rpmElementType type = rpmteType (te);
g_assert (pkg);
if (type == TR_ADDED)
{
DnfPackage *pkg = (void*)rpmteKey (te);
if (pkg == filesystem_package)
continue;
if (pkg == filesystem_package)
continue;
if (!checkout_package_into_root (self, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
return FALSE;
if (!checkout_package_into_root (self, pkg, tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit, pkg),
cancellable, error))
return FALSE;
}
else
{
g_assert (type == TR_REMOVED);
if (!delete_package_from_root (self, te, tmprootfs_dfd, cancellable, error))
return FALSE;
}
}
rpmostree_output_task_end ("done");
@ -2558,7 +2684,9 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
if (!rpmostree_rootfs_prepare_links (tmprootfs_dfd, cancellable, error))
return FALSE;
if (!noscripts)
/* NB: we're not running scripts right now for removals, so this is only for
* overlays */
if (!noscripts && overlays->len > 0)
{
gboolean have_passwd;
gboolean have_systemctl;
@ -2607,12 +2735,13 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
for (guint i = 0; i < n_rpmts_elements; i++)
{
rpmte te = rpmtsElement (ordering_ts, i);
DnfPackage *pkg = (void*)rpmteKey (te);
if (rpmteType (te) != TR_ADDED)
continue;
DnfPackage *pkg = (void*)rpmteKey (te);
g_assert (pkg);
if (!run_pre_sync (self, tmprootfs_dfd, pkg,
cancellable, error))
if (!run_pre_sync (self, tmprootfs_dfd, pkg, cancellable, error))
return FALSE;
}
@ -2653,18 +2782,19 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
for (guint i = 0; i < n_rpmts_elements; i++)
{
rpmte te = rpmtsElement (ordering_ts, i);
if (rpmteType (te) != TR_ADDED)
continue;
DnfPackage *pkg = (void*)rpmteKey (te);
g_assert (pkg);
if (!apply_rpmfi_overrides (self, tmprootfs_dfd,
pkg, passwdents, groupents,
if (!apply_rpmfi_overrides (self, tmprootfs_dfd, pkg, passwdents, groupents,
cancellable, error))
return glnx_prefix_error (error, "While applying overrides for pkg %s: ",
dnf_package_get_name (pkg));
if (!run_posttrans_sync (self, tmprootfs_dfd, pkg,
cancellable, error))
if (!run_posttrans_sync (self, tmprootfs_dfd, pkg, cancellable, error))
return FALSE;
}
@ -2716,20 +2846,23 @@ rpmostree_context_assemble_tmprootfs (RpmOstreeContext *self,
tdata.ctx = self;
rpmtsSetNotifyCallback (rpmdb_ts, ts_callback, &tdata);
{ gpointer k;
GHashTableIter hiter;
for (guint i = 0; i < overlays->len; i++)
{
DnfPackage *pkg = overlays->pdata[i];
g_hash_table_iter_init (&hiter, pkg_to_ostree_commit);
while (g_hash_table_iter_next (&hiter, &k, NULL))
{
DnfPackage *pkg = k;
/* Set noscripts since we already validated them above */
if (!rpmts_add_install (self, rpmdb_ts, pkg, TRUE, NULL,
cancellable, error))
return FALSE;
}
/* Set noscripts since we already validated them above */
if (!add_to_transaction (self, rpmdb_ts, pkg, TRUE, NULL,
cancellable, error))
return FALSE;
}
}
/* and mark removed packages as such so they drop out of rpmdb */
for (guint i = 0; i < overrides_remove->len; i++)
{
DnfPackage *pkg = overrides_remove->pdata[i];
if (!rpmts_add_erase (self, rpmdb_ts, pkg, cancellable, error))
return FALSE;
}
rpmtsOrder (rpmdb_ts);
@ -2800,27 +2933,50 @@ rpmostree_context_commit_tmprootfs (RpmOstreeContext *self,
g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.clientlayer",
g_variant_new_boolean (TRUE));
if (!self->empty)
{
g_autoptr(GVariant) pkgs =
g_variant_dict_lookup_value (self->spec->dict, "packages",
G_VARIANT_TYPE ("as"));
g_assert (pkgs);
g_variant_builder_add (&metadata_builder, "{sv}",
"rpmostree.packages", pkgs);
}
else
{
const char *const p[] = { NULL };
g_variant_builder_add (&metadata_builder, "{sv}",
"rpmostree.packages",
g_variant_new_strv (p, -1));
}
/* embed packages (really, "patterns") layered */
g_autoptr(GVariant) pkgs =
g_variant_dict_lookup_value (self->spec->dict, "packages", G_VARIANT_TYPE ("as"));
g_assert (pkgs);
g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.packages", pkgs);
/* embed the full NEVRA names of the base pkgs removed to make it easier to display
* to users (this does not include reverse deps that were also removed -- users can
* do `db diff` for the full list, much like regular pkglayering) */
{
g_autofree char **removed_base_pkgnames = NULL;
g_assert (g_variant_dict_lookup (self->spec->dict, "removed-base-packages",
"^a&s", &removed_base_pkgnames));
/* NB: strings owned by solv pool */
g_autoptr(GPtrArray) removed_base_pkgnevras = g_ptr_array_new ();
if (!self->empty)
{
g_autoptr(GPtrArray) packages =
dnf_goal_get_packages (dnf_context_get_goal (self->hifctx),
DNF_PACKAGE_INFO_REMOVE,
DNF_PACKAGE_INFO_OBSOLETE, -1);
for (guint i = 0; i < packages->len; i++)
{
DnfPackage *pkg = packages->pdata[i];
const char *name = dnf_package_get_name (pkg);
const char *nevra = dnf_package_get_nevra (pkg);
if (g_strv_contains ((const char*const*)removed_base_pkgnames, name))
g_ptr_array_add (removed_base_pkgnevras, (gpointer)nevra);
}
}
g_variant_builder_add (&metadata_builder, "{sv}",
"rpmostree.removed-base-packages",
g_variant_new_strv (
(const char *const*)removed_base_pkgnevras->pdata,
removed_base_pkgnevras->len));
}
/* be nice to our future selves */
g_variant_builder_add (&metadata_builder, "{sv}",
"rpmostree.clientlayer_version",
g_variant_new_uint32 (1));
g_variant_new_uint32 (2));
}
else if (assemble_type == RPMOSTREE_ASSEMBLE_TYPE_SERVER_BASE)
{

View File

@ -36,8 +36,9 @@ struct RpmOstreeOrigin {
char *cached_override_commit;
char *cached_unconfigured_state;
char **cached_initramfs_args;
GHashTable *cached_packages;
GHashTable *cached_local_packages;
GHashTable *cached_packages; /* set of reldeps */
GHashTable *cached_local_packages; /* NEVRA --> header sha256 */
GHashTable *cached_overrides_remove; /* set of pkgnames (no EVRA) */
};
static GKeyFile *
@ -65,6 +66,8 @@ rpmostree_origin_parse_keyfile (GKeyFile *origin,
g_free, NULL);
ret->cached_local_packages = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
ret->cached_overrides_remove = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
/* NOTE hack here - see https://github.com/ostreedev/ostree/pull/343 */
g_key_file_remove_key (ret->kf, "origin", "unlocked", NULL);
@ -74,29 +77,37 @@ rpmostree_origin_parse_keyfile (GKeyFile *origin,
ret->cached_refspec = g_key_file_get_string (ret->kf, "origin", "refspec", NULL);
if (!ret->cached_refspec)
{
g_auto(GStrv) packages = NULL;
ret->cached_refspec = g_key_file_get_string (ret->kf, "origin", "baserefspec", NULL);
if (!ret->cached_refspec)
return glnx_null_throw (error, "No origin/refspec or origin/baserefspec in current deployment origin; cannot handle via rpm-ostree");
packages = g_key_file_get_string_list (ret->kf, "packages", "requested", NULL, NULL);
for (char **it = packages; it && *it; it++)
g_hash_table_add (ret->cached_packages, g_steal_pointer (it));
{ g_auto(GStrv) packages =
g_key_file_get_string_list (ret->kf, "packages", "requested", NULL, NULL);
for (char **it = packages; it && *it; it++)
g_hash_table_add (ret->cached_packages, g_steal_pointer (it));
}
g_strfreev (packages);
packages = g_key_file_get_string_list (ret->kf, "packages", "requested-local", NULL, NULL);
for (char **it = packages; it && *it; it++)
{
const char *nevra = *it;
g_autofree char *sha256 = NULL;
{ g_auto(GStrv) packages =
g_key_file_get_string_list (ret->kf, "packages", "requested-local", NULL, NULL);
for (char **it = packages; it && *it; it++)
{
const char *nevra = *it;
g_autofree char *sha256 = NULL;
if (!rpmostree_decompose_sha256_nevra (&nevra, &sha256, error))
return glnx_null_throw (error, "Invalid SHA-256 NEVRA string: %s", nevra);
if (!rpmostree_decompose_sha256_nevra (&nevra, &sha256, error))
return glnx_null_throw (error, "Invalid SHA-256 NEVRA string: %s",
nevra);
g_hash_table_replace (ret->cached_local_packages,
g_strdup (nevra), g_steal_pointer (&sha256));
}
g_hash_table_replace (ret->cached_local_packages,
g_strdup (nevra), g_steal_pointer (&sha256));
}
}
{ g_auto(GStrv) overrides =
g_key_file_get_string_list (ret->kf, "overrides", "remove", NULL, NULL);
for (char **it = overrides; it && *it; it++)
g_hash_table_add (ret->cached_overrides_remove, g_steal_pointer (it));
}
}
ret->cached_override_commit =
@ -132,6 +143,12 @@ rpmostree_origin_get_local_packages (RpmOstreeOrigin *origin)
return origin->cached_local_packages;
}
GHashTable *
rpmostree_origin_get_overrides_remove (RpmOstreeOrigin *origin)
{
return origin->cached_overrides_remove;
}
const char *
rpmostree_origin_get_override_commit (RpmOstreeOrigin *origin)
{
@ -164,7 +181,8 @@ rpmostree_origin_may_require_local_assembly (RpmOstreeOrigin *origin)
{
return rpmostree_origin_get_regenerate_initramfs (origin) ||
(g_hash_table_size (origin->cached_packages) > 0) ||
(g_hash_table_size (origin->cached_local_packages) > 0);
(g_hash_table_size (origin->cached_local_packages) > 0) ||
(g_hash_table_size (origin->cached_overrides_remove) > 0);
}
void
@ -342,35 +360,41 @@ rpmostree_origin_set_live_state (RpmOstreeOrigin *origin,
static void
update_keyfile_pkgs_from_cache (RpmOstreeOrigin *origin,
gboolean local)
const char *group,
const char *key,
GHashTable *pkgs,
gboolean has_csum)
{
GHashTable *pkgs = local ? origin->cached_local_packages
: origin->cached_packages;
/* we're abusing a bit the concept of cache here, though
* it's just easier to go from cache to origin */
if (local)
if (g_hash_table_size (pkgs) == 0)
{
g_key_file_remove_key (origin->kf, group, key, NULL);
return;
}
if (has_csum)
{
GHashTableIter it;
gpointer k, v;
g_autoptr(GPtrArray) sha256_nevra =
g_autoptr(GPtrArray) pkg_csum =
g_ptr_array_new_with_free_func (g_free);
g_hash_table_iter_init (&it, origin->cached_local_packages);
g_hash_table_iter_init (&it, pkgs);
while (g_hash_table_iter_next (&it, &k, &v))
g_ptr_array_add (sha256_nevra, g_strconcat (v, ":", k, NULL));
g_ptr_array_add (pkg_csum, g_strconcat (v, ":", k, NULL));
g_key_file_set_string_list (origin->kf, "packages", "requested-local",
(const char *const*)sha256_nevra->pdata,
sha256_nevra->len);
g_key_file_set_string_list (origin->kf, group, key,
(const char *const*)pkg_csum->pdata,
pkg_csum->len);
}
else
{
g_autofree char **pkgv =
(char**)g_hash_table_get_keys_as_array (pkgs, NULL);
g_key_file_set_string_list (origin->kf, "packages", "requested",
g_key_file_set_string_list (origin->kf, group, key,
(const char *const*)pkgv,
g_strv_length (pkgv));
}
@ -436,7 +460,10 @@ rpmostree_origin_add_packages (RpmOstreeOrigin *origin,
}
if (changed)
update_keyfile_pkgs_from_cache (origin, local);
update_keyfile_pkgs_from_cache (origin, "packages",
local ? "requested-local" : "requested",
local ? origin->cached_local_packages
: origin->cached_packages, local);
return TRUE;
}
@ -468,9 +495,77 @@ rpmostree_origin_remove_packages (RpmOstreeOrigin *origin,
}
if (changed)
update_keyfile_pkgs_from_cache (origin, FALSE);
update_keyfile_pkgs_from_cache (origin, "packages", "requested",
origin->cached_packages, FALSE);
if (local_changed)
update_keyfile_pkgs_from_cache (origin, TRUE);
update_keyfile_pkgs_from_cache (origin, "packages", "requested-local",
origin->cached_local_packages, TRUE);
return TRUE;
}
gboolean
rpmostree_origin_add_overrides (RpmOstreeOrigin *origin,
char **packages,
gboolean is_remove_override,
GError **error)
{
/* that's all we support for now */
g_assert (is_remove_override);
gboolean changed = FALSE;
for (char **it = packages; it && *it; it++)
{
const char *pkg = *it;
if (g_hash_table_contains (origin->cached_overrides_remove, pkg))
return glnx_throw (error, "Package '%s' is already removed", pkg);
g_hash_table_add (origin->cached_overrides_remove, g_strdup (pkg));
changed = TRUE;
}
if (changed)
update_keyfile_pkgs_from_cache (origin, "overrides", "remove",
origin->cached_overrides_remove, FALSE);
return TRUE;
}
gboolean
rpmostree_origin_remove_overrides (RpmOstreeOrigin *origin,
char **packages,
GError **error)
{
gboolean changed = FALSE;
for (char **it = packages; it && *it; it++)
{
const char *pkg = *it;
if (!g_hash_table_contains (origin->cached_overrides_remove, pkg))
return glnx_throw (error, "No overrides for package '%s'", pkg);
g_hash_table_remove (origin->cached_overrides_remove, pkg);
changed = TRUE;
}
if (changed)
update_keyfile_pkgs_from_cache (origin, "overrides", "remove",
origin->cached_overrides_remove, FALSE);
return TRUE;
}
gboolean
rpmostree_origin_remove_all_overrides (RpmOstreeOrigin *origin,
gboolean *out_changed,
GError **error)
{
gboolean changed = (g_hash_table_size (origin->cached_overrides_remove) > 0);
g_hash_table_remove_all (origin->cached_overrides_remove);
if (changed)
update_keyfile_pkgs_from_cache (origin, "overrides", "remove",
origin->cached_overrides_remove, FALSE);
if (out_changed)
*out_changed = changed;
return TRUE;
}

View File

@ -60,6 +60,9 @@ rpmostree_origin_get_packages (RpmOstreeOrigin *origin);
GHashTable *
rpmostree_origin_get_local_packages (RpmOstreeOrigin *origin);
GHashTable *
rpmostree_origin_get_overrides_remove (RpmOstreeOrigin *origin);
const char *
rpmostree_origin_get_override_commit (RpmOstreeOrigin *origin);
@ -114,6 +117,22 @@ rpmostree_origin_remove_packages (RpmOstreeOrigin *origin,
char **packages,
GError **error);
gboolean
rpmostree_origin_add_overrides (RpmOstreeOrigin *origin,
char **packages,
gboolean is_remove_override,
GError **error);
gboolean
rpmostree_origin_remove_overrides (RpmOstreeOrigin *origin,
char **packages,
GError **error);
gboolean
rpmostree_origin_remove_all_overrides (RpmOstreeOrigin *origin,
gboolean *out_changed,
GError **error);
void
rpmostree_origin_set_live_state (RpmOstreeOrigin *origin,
const char *inprogress,

View File

@ -28,7 +28,6 @@
#include <sys/capability.h>
#include <libglnx.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmts.h>
/* FIXME: */
@ -931,35 +930,6 @@ pkg_array_compare (DnfPackage **p_pkg1,
return dnf_package_cmp (*p_pkg1, *p_pkg2);
}
void
rpmostree_print_transaction (DnfContext *hifctx)
{
guint i;
g_autoptr(GPtrArray) install = NULL;
install = dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_INSTALL,
DNF_PACKAGE_INFO_REINSTALL,
DNF_PACKAGE_INFO_DOWNGRADE,
DNF_PACKAGE_INFO_UPDATE,
-1);
g_print ("Transaction: %u packages\n", install->len);
if (install->len == 0)
g_print (" (empty)\n");
else
{
g_ptr_array_sort (install, (GCompareFunc) pkg_array_compare);
for (i = 0; i < install->len; i++)
{
DnfPackage *pkg = install->pdata[i];
g_print (" %s (%s)\n", dnf_package_get_nevra (pkg), dnf_package_get_reponame (pkg));
}
}
}
void
rpmostree_sighandler_reset_cleanup (RpmSighandlerResetCleanup *cleanup)
{
@ -972,6 +942,56 @@ rpmostree_sighandler_reset_cleanup (RpmSighandlerResetCleanup *cleanup)
#endif
}
static void
print_pkglist (GPtrArray *pkglist)
{
g_ptr_array_sort (pkglist, (GCompareFunc) pkg_array_compare);
for (guint i = 0; i < pkglist->len; i++)
{
DnfPackage *pkg = pkglist->pdata[i];
g_print (" %s (%s)\n", dnf_package_get_nevra (pkg),
dnf_package_get_reponame (pkg));
}
}
void
rpmostree_print_transaction (DnfContext *hifctx)
{
gboolean empty = TRUE;
{ g_autoptr(GPtrArray) packages = NULL;
packages = dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_INSTALL,
DNF_PACKAGE_INFO_REINSTALL,
DNF_PACKAGE_INFO_DOWNGRADE,
DNF_PACKAGE_INFO_UPDATE,
-1);
if (packages->len > 0)
empty = FALSE;
g_print ("Installing %u packages:\n", packages->len);
print_pkglist (packages);
}
{ g_autoptr(GPtrArray) packages = NULL;
packages = dnf_goal_get_packages (dnf_context_get_goal (hifctx),
DNF_PACKAGE_INFO_REMOVE,
DNF_PACKAGE_INFO_OBSOLETE,
-1);
if (packages->len > 0)
empty = FALSE;
g_print ("Removing %u packages:\n", packages->len);
print_pkglist (packages);
}
if (empty)
g_print ("Empty transaction\n");
}
struct _cap_struct {
struct __user_cap_header_struct head;
union {

View File

@ -24,6 +24,7 @@
#include <rpm/rpmlib.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmdb.h>
#include "libglnx.h"
#include "rpmostree-util.h"
#include "rpmostree-refsack.h"
@ -67,7 +68,9 @@ rpmhdrs_diff_prnt_block (gboolean changelogs, struct RpmHeadersDiff *diff);
* itself. TODO: Move them to libdnf */
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(Header, headerFree, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(rpmfi, rpmfiFree, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(rpmfiles, rpmfilesFree, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(rpmts, rpmtsFree, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(rpmdbMatchIterator, rpmdbFreeIterator, NULL)
void
rpmhdrs_diff_prnt_diff (struct RpmHeadersDiff *diff);

View File

@ -440,12 +440,14 @@ rpmostree_deployment_get_layered_info (OstreeRepo *repo,
gboolean *out_is_layered,
char **out_base_layer,
char ***out_layered_pkgs,
char ***out_removed_base_pkgs,
GError **error)
{
g_autoptr(GVariant) commit = NULL;
g_autoptr(GVariant) metadata = NULL;
g_autoptr(GVariantDict) dict = NULL;
g_auto(GStrv) layered_pkgs = NULL;
g_auto(GStrv) removed_base_pkgs = NULL;
const char *csum = ostree_deployment_get_csum (deployment);
gboolean is_layered = FALSE;
g_autofree char *base_layer = NULL;
@ -473,11 +475,11 @@ rpmostree_deployment_get_layered_info (OstreeRepo *repo,
g_assert (base_layer);
}
/* only fetch the pkgs if we have to */
if (is_layered && out_layered_pkgs != NULL)
/* only fetch pkgs if we have to */
if (is_layered && (out_layered_pkgs != NULL || out_removed_base_pkgs != NULL))
{
/* starting from v1, we no longer embed a treespec in client layers */
if (clientlayer_version > 0)
if (clientlayer_version >= 1)
{
g_assert (g_variant_dict_lookup (dict, "rpmostree.packages", "^as",
&layered_pkgs));
@ -499,6 +501,12 @@ rpmostree_deployment_get_layered_info (OstreeRepo *repo,
g_assert (g_variant_dict_lookup (treespec, "packages", "^as",
&layered_pkgs));
}
if (clientlayer_version >= 2)
{
g_assert (g_variant_dict_lookup (dict, "rpmostree.removed-base-packages", "^as",
&removed_base_pkgs));
}
}
if (out_is_layered != NULL)
@ -507,6 +515,8 @@ rpmostree_deployment_get_layered_info (OstreeRepo *repo,
*out_base_layer = g_steal_pointer (&base_layer);
if (out_layered_pkgs != NULL)
*out_layered_pkgs = g_steal_pointer (&layered_pkgs);
if (out_removed_base_pkgs != NULL)
*out_removed_base_pkgs = g_steal_pointer (&removed_base_pkgs);
return TRUE;
}

View File

@ -101,6 +101,7 @@ rpmostree_deployment_get_layered_info (OstreeRepo *repo,
gboolean *out_is_layered,
char **out_base_layer,
char ***out_layered_pkgs,
char ***out_removed_base_pkgs,
GError **error);
gboolean