Bind output core into Rust, use in apply-live

Originally the Rust apply-live code was exposed from Rust to C
via bindgen.  But when working on that, I hit the problem
that our output infrastructure was C...and the "reverse direction"
binding stuff was just ugly.

This PR again IMO shows the value of the investment in cxx-rs
because we can now seamlessly call back from the Rust side
into a "C++-ish" progress API, which the C++ side is updated
to use.

The level of indirection here is obviously pretty silly
because the main thing on the C++ output side is basically
a function dispatcher, but...I didn't want to try to rework
that into Rust fully yet.  (But, the moment we do this
whole area will get a *lot* cleaner)

Anyways, in the end this makes it easy for the apply-live
code to output progress to the user which was sorely
needed.
This commit is contained in:
Colin Walters 2021-01-31 15:03:00 +00:00 committed by OpenShift Merge Robot
parent c9e9269770
commit 2f6b5a654d
11 changed files with 220 additions and 154 deletions

View File

@ -226,6 +226,14 @@ pub mod ffi {
fn main_print_error(msg: &str);
}
unsafe extern "C++" {
include!("rpmostree-output.h");
type Progress;
fn progress_begin_task(msg: &str) -> UniquePtr<Progress>;
fn end(self: Pin<&mut Progress>, msg: &str);
}
// rpmostree-rpm-util.h
unsafe extern "C++" {
include!("rpmostree-rpm-util.h");

View File

@ -445,17 +445,22 @@ pub(crate) fn transaction_apply_live(
// Gather the current diff of /etc - we need to avoid changing
// any files which are locally modified.
let task = crate::ffi::progress_begin_task("Computing /etc diff to preserve");
let config_diff = {
let usretc = &rootfs_dfd.sub_dir("usr/etc")?;
let etc = &rootfs_dfd.sub_dir("etc")?;
crate::dirdiff::diff(usretc, etc)?
};
println!("Computed /etc diff: {}", &config_diff);
std::mem::drop(task);
// The heart of things: updating the overlayfs on /usr
let task = crate::ffi::progress_begin_task("Updating /usr");
apply_diff(repo, &diff, &target_commit, &openat::Dir::open("/usr")?)?;
std::mem::drop(task);
// The other important bits are /etc and /var
let task = crate::ffi::progress_begin_task("Updating /etc");
update_etc(
repo,
&diff,
@ -464,7 +469,10 @@ pub(crate) fn transaction_apply_live(
&target_commit,
&openat::Dir::open("/etc")?,
)?;
std::mem::drop(task);
let task = crate::ffi::progress_begin_task("Running systemd-tmpfiles for /var");
rerun_tmpfiles()?;
std::mem::drop(task);
// Success! Update the recorded state.
state.commit = target_commit.to_string();

View File

@ -192,14 +192,14 @@ pub(crate) fn console_progress_begin_percent(msg: &str) {
pub(crate) fn console_progress_set_message(msg: &str) {
let mut lock = PROGRESS.lock().unwrap();
let state = lock.as_mut().expect("progress to update");
let state = lock.as_mut().expect("progress to set message");
state.set_message(msg);
}
pub(crate) fn console_progress_set_sub_message(msg: &str) {
let msg = optional_str(msg);
let mut lock = PROGRESS.lock().unwrap();
let state = lock.as_mut().expect("progress to update");
let state = lock.as_mut().expect("progress sub-msg update");
state.set_sub_message(msg);
}

View File

@ -559,8 +559,8 @@ checkout_base_tree (RpmOstreeSysrootUpgrader *self,
return TRUE; /* already checked out! */
/* let's give the user some feedback so they don't think we're blocked */
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Checking out tree %.7s", self->base_revision);
auto msg = g_strdup_printf ("Checking out tree %.7s", self->base_revision);
auto task = rpmostreecxx::progress_begin_task(msg);
int repo_dfd = ostree_repo_get_dfd (self->repo); /* borrowed */
/* Always delete this */
@ -1168,8 +1168,7 @@ perform_local_assembly (RpmOstreeSysrootUpgrader *self,
g_ptr_array_add (initramfs_args, g_strdup (*it));
g_ptr_array_add (initramfs_args, NULL);
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Generating initramfs");
auto task = rpmostreecxx::progress_begin_task("Generating initramfs");
g_assert (kernel_state && kernel_path);
@ -1490,8 +1489,7 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self,
_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
}
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Staging deployment");
auto task = rpmostreecxx::progress_begin_task("Staging deployment");
if (!ostree_sysroot_stage_tree_with_options (self->sysroot, self->osname,
target_revision, origin,
self->cfg_merge_deployment,

View File

@ -23,6 +23,7 @@
#include "libglnx.h"
#include "rpmostree-rojig-core.h"
#include "rpmostree-core.h"
#include "rpmostree-output.h"
G_BEGIN_DECLS
@ -63,6 +64,7 @@ struct _RpmOstreeContext {
guint n_async_max;
gboolean async_running;
GCancellable *async_cancellable;
std::unique_ptr<rpmostreecxx::Progress> async_progress;
GError *async_error;
GPtrArray *pkgs; /* All packages */
GPtrArray *pkgs_to_download;

View File

@ -882,7 +882,8 @@ on_hifstate_percentage_changed (DnfState *hifstate,
guint percentage,
gpointer user_data)
{
rpmostree_output_progress_percent (percentage);
auto progress = (rpmostreecxx::Progress*)user_data;
progress->percent_update(percentage);
}
static gboolean
@ -1162,18 +1163,18 @@ rpmostree_context_download_metadata (RpmOstreeContext *self,
NULL))
{
dnf_state_reset (hifstate);
auto msg = g_strdup_printf ("Updating metadata for '%s'", dnf_repo_get_id (repo));
auto progress = rpmostreecxx::progress_percent_begin(msg);
guint progress_sigid = g_signal_connect (hifstate, "percentage-changed",
G_CALLBACK (on_hifstate_percentage_changed),
NULL);
g_auto(RpmOstreeProgress) progress = { 0, };
rpmostree_output_progress_percent_begin (&progress, "Updating metadata for '%s'",
dnf_repo_get_id (repo));
(void*)progress.get());
if (!dnf_repo_update (repo, DNF_REPO_UPDATE_FLAG_FORCE, hifstate, error))
return glnx_prefix_error (error, "Updating rpm-md repo '%s'", dnf_repo_get_id (repo));
did_update = TRUE;
g_signal_handler_disconnect (hifstate, progress_sigid);
progress->end("");
}
guint64 ts = dnf_repo_get_timestamp_generated (repo);
@ -1188,11 +1189,10 @@ rpmostree_context_download_metadata (RpmOstreeContext *self,
/* The _setup_sack function among other things imports the metadata into libsolv */
{ g_autoptr(DnfState) hifstate = dnf_state_new ();
auto progress = rpmostreecxx::progress_percent_begin("Importing rpm-md");
guint progress_sigid = g_signal_connect (hifstate, "percentage-changed",
G_CALLBACK (on_hifstate_percentage_changed),
NULL);
g_auto(RpmOstreeProgress) progress = { 0, };
rpmostree_output_progress_percent_begin (&progress, "Importing rpm-md");
(void*)progress.get());
/* We already explictly checked the repos above; don't try to check them
* again.
@ -2247,8 +2247,7 @@ rpmostree_context_prepare (RpmOstreeContext *self,
if (!recommends)
actions = static_cast<DnfGoalActions>(static_cast<int>(actions) | DNF_IGNORE_WEAK_DEPS);
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Resolving dependencies");
auto task = rpmostreecxx::progress_begin_task("Resolving dependencies");
/* XXX: consider a --allow-uninstall switch? */
if (!dnf_goal_depsolve (goal, actions, error) ||
@ -2479,22 +2478,18 @@ rpmostree_download_packages (GPtrArray *packages,
g_autoptr(GHashTable) source_to_packages = gather_source_to_packages (packages);
GLNX_HASH_TABLE_FOREACH_KV (source_to_packages, DnfRepo*, src, GPtrArray*, src_packages)
{
g_autofree char *target_dir = NULL;
glnx_unref_object DnfState *hifstate = dnf_state_new ();
auto msg = g_strdup_printf("Downloading from '%s'", dnf_repo_get_id(src));
auto progress = rpmostreecxx::progress_percent_begin(msg);
progress_sigid = g_signal_connect (hifstate, "percentage-changed",
G_CALLBACK (on_hifstate_percentage_changed),
NULL);
g_auto(RpmOstreeProgress) progress = { 0, };
rpmostree_output_progress_percent_begin (&progress, "Downloading from '%s'",
dnf_repo_get_id (src));
target_dir = g_build_filename (dnf_repo_get_location (src), "/packages/", NULL);
G_CALLBACK (on_hifstate_percentage_changed),
(void*)progress.get());
g_autofree char *target_dir = g_build_filename (dnf_repo_get_location (src), "/packages/", NULL);
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, target_dir, 0755, cancellable, error))
return FALSE;
if (!dnf_repo_download_packages (src, src_packages, target_dir,
hifstate, error))
hifstate, error))
return FALSE;
g_signal_handler_disconnect (hifstate, progress_sigid);
@ -2572,7 +2567,7 @@ on_async_import_done (GObject *obj,
self->n_async_pkgs_imported++;
g_assert_cmpint (self->n_async_running, >, 0);
self->n_async_running--;
rpmostree_output_progress_n_items (self->n_async_pkgs_imported);
self->async_progress->nitems_update(self->n_async_pkgs_imported);
async_imports_mainctx_iter (self);
}
@ -2697,8 +2692,7 @@ rpmostree_context_import_rojig (RpmOstreeContext *self,
self->n_async_max = g_get_num_processors ();
self->async_cancellable = cancellable;
g_auto(RpmOstreeProgress) progress = { 0, };
rpmostree_output_progress_nitems_begin (&progress, self->pkgs_to_import->len, "Importing packages");
self->async_progress = rpmostreecxx::progress_nitems_begin(self->pkgs_to_import->len, "Importing packages");
/* Process imports */
GMainContext *mainctx = g_main_context_get_thread_default ();
@ -2717,7 +2711,8 @@ rpmostree_context_import_rojig (RpmOstreeContext *self,
return glnx_prefix_error (error, "importing RPMs");
}
rpmostree_output_progress_end (&progress);
self->async_progress->end("");
self->async_progress.release();
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
return FALSE;
@ -3448,7 +3443,7 @@ on_async_relabel_done (GObject *obj,
data->n_changed_files += n_relabeled;
data->n_changed_pkgs++;
}
rpmostree_output_progress_n_items (self->n_async_pkgs_relabeled);
self->async_progress->nitems_update(self->n_async_pkgs_relabeled);
if (self->n_async_pkgs_relabeled == self->pkgs_to_relabel->len)
self->async_running = FALSE;
}
@ -3486,8 +3481,7 @@ relabel_if_necessary (RpmOstreeContext *self,
RpmOstreeAsyncRelabelData data = { self, 0, };
const guint n_to_relabel = self->pkgs_to_relabel->len;
g_auto(RpmOstreeProgress) progress = { 0, };
rpmostree_output_progress_nitems_begin (&progress, n_to_relabel, "Relabeling");
self->async_progress = rpmostreecxx::progress_nitems_begin(n_to_relabel, "Relabeling");
for (guint i = 0; i < n_to_relabel; i++)
{
auto pkg = static_cast<DnfPackage *>(self->pkgs_to_relabel->pdata[i]);
@ -3506,7 +3500,8 @@ relabel_if_necessary (RpmOstreeContext *self,
return glnx_prefix_error (error, "relabeling");
}
rpmostree_output_progress_end (&progress);
self->async_progress->end("");
self->async_progress.release();
/* Commit */
if (!ostree_repo_commit_transaction (ostreerepo, NULL, cancellable, error))
@ -4061,8 +4056,7 @@ process_ostree_layers (RpmOstreeContext *self,
if (n == 0)
return TRUE;
g_auto(RpmOstreeProgress) checkout_progress = { 0, };
rpmostree_output_progress_nitems_begin (&checkout_progress, n, "Checking out ostree layers");
auto progress = rpmostreecxx::progress_nitems_begin(n, "Checking out ostree layers");
size_t i = 0;
for (char **iter = layers; iter && *iter; iter++)
{
@ -4072,7 +4066,7 @@ process_ostree_layers (RpmOstreeContext *self,
cancellable, error))
return FALSE;
i++;
rpmostree_output_progress_n_items (i);
progress->nitems_update(i);
}
for (char **iter = override_layers; iter && *iter; iter++)
{
@ -4082,9 +4076,9 @@ process_ostree_layers (RpmOstreeContext *self,
cancellable, error))
return FALSE;
i++;
rpmostree_output_progress_n_items (i);
progress->nitems_update(i);
}
rpmostree_output_progress_end (&checkout_progress);
progress->end("");
return TRUE;
}
@ -4231,8 +4225,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
g_assert (n_rpmts_elements > 0);
guint n_rpmts_done = 0;
g_auto(RpmOstreeProgress) checkout_progress = { 0, };
rpmostree_output_progress_nitems_begin (&checkout_progress, n_rpmts_elements, "%s", progress_msg);
auto progress = rpmostreecxx::progress_nitems_begin(n_rpmts_elements, progress_msg);
/* Okay so what's going on in Fedora with incestuous relationship
* between the `filesystem`, `setup`, `libgcc` RPMs is actively
@ -4246,7 +4239,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
*/
if (filesystem_package)
{
rpmostree_output_set_sub_message ("filesystem");
progress->set_sub_message("filesystem");
auto c = static_cast<const char*>(g_hash_table_lookup (pkg_to_ostree_commit, filesystem_package));
if (!checkout_package_into_root (self, filesystem_package,
tmprootfs_dfd, ".", self->devino_cache,
@ -4255,7 +4248,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
cancellable, error))
return FALSE;
n_rpmts_done++;
rpmostree_output_progress_n_items (n_rpmts_done);
progress->nitems_update(n_rpmts_done);
}
g_autoptr(GHashTable) files_skip_add = NULL;
@ -4290,7 +4283,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
dirs_to_remove, cancellable, error))
return FALSE;
n_rpmts_done++;
rpmostree_output_progress_n_items (n_rpmts_done);
progress->nitems_update(n_rpmts_done);
}
g_clear_pointer (&files_skip_delete, g_hash_table_unref);
@ -4322,16 +4315,16 @@ rpmostree_context_assemble (RpmOstreeContext *self,
(pkg == setup_package) ? OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES :
OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL;
rpmostree_output_set_sub_message (dnf_package_get_name (pkg));
progress->set_sub_message(dnf_package_get_name (pkg));
if (!checkout_package_into_root (self, pkg, tmprootfs_dfd, ".", self->devino_cache,
static_cast<const char*>(g_hash_table_lookup (pkg_to_ostree_commit, pkg)),
files_skip_add, ovwmode, cancellable, error))
return FALSE;
n_rpmts_done++;
rpmostree_output_progress_n_items (n_rpmts_done);
progress->nitems_update(n_rpmts_done);
}
rpmostree_output_progress_end (&checkout_progress);
progress->end("");
/* Some packages expect to be able to make temporary files here
* for obvious reasons, but we otherwise make `/var` read-only.
@ -4431,8 +4424,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
* this way is that we only need to read the passwd/group files once
* before applying the overrides, rather than after each %pre.
*/
{ g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Running pre scripts");
{ auto task = rpmostreecxx::progress_begin_task("Running pre scripts");
guint n_pre_scripts_run = 0;
for (guint i = 0; i < n_rpmts_elements; i++)
{
@ -4443,13 +4435,14 @@ rpmostree_context_assemble (RpmOstreeContext *self,
DnfPackage *pkg = (DnfPackage*)rpmteKey (te);
g_assert (pkg);
rpmostree_output_set_sub_message (dnf_package_get_name (pkg));
task->set_sub_message(dnf_package_get_name(pkg));
if (!run_script_sync (self, tmprootfs_dfd, &var_lib_rpm_statedir,
pkg, RPMOSTREE_SCRIPT_PREIN,
&n_pre_scripts_run, cancellable, error))
return FALSE;
}
rpmostree_output_progress_end_msg (&task, "%u done", n_pre_scripts_run);
auto msg = g_strdup_printf ("%u done", n_pre_scripts_run);
task->end(msg);
}
/* Now undo our hack above */
@ -4492,8 +4485,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
}
{
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Running post scripts");
auto task = rpmostreecxx::progress_begin_task("Running post scripts");
guint n_post_scripts_run = 0;
/* %post */
@ -4506,7 +4498,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
auto pkg = (DnfPackage *)(rpmteKey (te));
g_assert (pkg);
rpmostree_output_set_sub_message (dnf_package_get_name (pkg));
task->set_sub_message(dnf_package_get_name(pkg));
if (!apply_rpmfi_overrides (self, tmprootfs_dfd, pkg, passwdents, groupents,
cancellable, error))
return glnx_prefix_error (error, "While applying overrides for pkg %s",
@ -4524,8 +4516,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
return FALSE;
{
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Running posttrans scripts");
auto task = rpmostreecxx::progress_begin_task("Running posttrans scripts");
guint n_posttrans_scripts_run = 0;
/* %posttrans */
@ -4538,7 +4529,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
auto pkg = (DnfPackage *)(rpmteKey (te));
g_assert (pkg);
rpmostree_output_set_sub_message (dnf_package_get_name (pkg));
task->set_sub_message(dnf_package_get_name(pkg));
if (!run_script_sync (self, tmprootfs_dfd, &var_lib_rpm_statedir,
pkg, RPMOSTREE_SCRIPT_POSTTRANS,
&n_posttrans_scripts_run, cancellable, error))
@ -4550,7 +4541,8 @@ rpmostree_context_assemble (RpmOstreeContext *self,
&n_posttrans_scripts_run, cancellable, error))
return FALSE;
rpmostree_output_progress_end_msg (&task, "%u done", n_posttrans_scripts_run);
auto msg = g_strdup_printf ("%u done", n_posttrans_scripts_run);
task->end(msg);
}
/* We want this to be the first error message if something went wrong
@ -4596,8 +4588,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
g_clear_pointer (&ordering_ts, rpmtsFree);
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Writing rpmdb");
auto task = rpmostreecxx::progress_begin_task("Writing rpmdb");
if (!glnx_shutil_mkdir_p_at (tmprootfs_dfd, RPMOSTREE_RPMDB_LOCATION, 0755, cancellable, error))
return FALSE;
@ -4689,7 +4680,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
return FALSE;
}
rpmostree_output_progress_end (&task);
task->end("");
/* And finally revert the _dbpath setting because libsolv relies on it as well
* to find the rpmdb and RPM macros are global state. */
@ -4717,8 +4708,7 @@ rpmostree_context_commit (RpmOstreeContext *self,
g_autoptr(OstreeRepoCommitModifier) commit_modifier = NULL;
g_autofree char *ret_commit_checksum = NULL;
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Writing OSTree commit");
auto task = rpmostreecxx::progress_begin_task("Writing OSTree commit");
g_auto(RpmOstreeRepoAutoTransaction) txn = { 0, };
if (!rpmostree_repo_auto_transaction_start (&txn, self->ostreerepo, FALSE, cancellable, error))

View File

@ -20,8 +20,10 @@
#include <ostree.h>
#include <libglnx.h>
#include <memory>
#include "rpmostree-output.h"
#include "rpmostree-util.h"
#include "rpmostree-rust.h"
#include "rpmostree-cxxrs.h"
@ -60,13 +62,13 @@ rpmostree_output_default_handler (RpmOstreeOutputType type,
case RPMOSTREE_OUTPUT_PROGRESS_SUB_MESSAGE:
{
auto msg = static_cast<const char *>(data);
rpmostreecxx::console_progress_set_sub_message (rust::Str(msg ?: ""));
rpmostreecxx::console_progress_set_sub_message (util::ruststr_or_empty(msg));
}
break;
case RPMOSTREE_OUTPUT_PROGRESS_END:
{
auto end = static_cast<RpmOstreeOutputProgressEnd *>(data);
rpmostreecxx::console_progress_end (rust::Str(end->msg ?: ""));
rpmostreecxx::console_progress_end (util::ruststr_or_empty(end->msg));
break;
}
}
@ -90,6 +92,9 @@ rpmostree_output_set_callback (void (*cb)(RpmOstreeOutputType, void*, void*),
char *s = g_strdup_vprintf (format, args); \
va_end (args); s; })
// For a lot of originally-C code it's just convenient to
// use this global C-style format API and not have to deal
// with a progress reference.
void
rpmostree_output_message (const char *format, ...)
{
@ -98,69 +103,91 @@ rpmostree_output_message (const char *format, ...)
active_cb (RPMOSTREE_OUTPUT_MESSAGE, &task, active_cb_opaque);
}
namespace rpmostreecxx {
void
rpmostree_output_task_begin (RpmOstreeProgress *taskp, const char *format, ...)
output_message (const rust::Str msg)
{
g_assert (taskp && !taskp->initialized);
taskp->initialized = TRUE;
taskp->type = RPMOSTREE_PROGRESS_TASK;
g_autofree char *msg = strdup_vprintf (format);
RpmOstreeOutputProgressBegin begin = { msg, false, 0 };
auto msg_c = std::string(msg);
RpmOstreeOutputMessage task = { msg_c.c_str() };
active_cb (RPMOSTREE_OUTPUT_MESSAGE, &task, active_cb_opaque);
}
// Begin a task (that can't easily be "nitems" or percentage).
// This will render as a spinner.
std::unique_ptr<Progress>
progress_begin_task(const rust::Str msg) noexcept
{
auto msg_c = std::string(msg);
RpmOstreeOutputProgressBegin begin = { msg_c.c_str(), false, 0 };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_BEGIN, &begin, active_cb_opaque);
return std::make_unique<Progress>(ProgressType::TASK);
}
void
rpmostree_output_set_sub_message (const char *sub_message)
// Output a string. Note that this should not be called when
// a "task" is active.
void
Progress::message(const rust::Str msg)
{
active_cb (RPMOSTREE_OUTPUT_PROGRESS_SUB_MESSAGE, (void*)sub_message, active_cb_opaque);
auto msg_c = std::string(msg);
RpmOstreeOutputMessage task = { msg_c.c_str() };
active_cb (RPMOSTREE_OUTPUT_MESSAGE, &task, active_cb_opaque);
}
void
rpmostree_output_progress_end_msg (RpmOstreeProgress *taskp, const char *format, ...)
// When working on a task/percent/nitems, often we want to display a particular
// item (such as a package).
void
Progress::set_sub_message(const rust::Str msg)
{
g_assert (taskp);
if (!taskp->initialized)
return;
taskp->initialized = false;
g_autofree char *final_msg = format ? strdup_vprintf (format) : NULL;
g_autofree char *msg_c = util::ruststr_dup_c_optempty(msg);
active_cb (RPMOSTREE_OUTPUT_PROGRESS_SUB_MESSAGE, (void*)msg_c, active_cb_opaque);
}
// Start working on a 0-n task.
std::unique_ptr<Progress>
progress_nitems_begin(guint n, const rust::Str msg) noexcept
{
auto msg_c = std::string(msg);
RpmOstreeOutputProgressBegin begin = { msg_c.c_str(), false, n };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_BEGIN, &begin, active_cb_opaque);
return std::make_unique<Progress>(ProgressType::N_ITEMS);
}
// Update the nitems counter.
void
Progress::nitems_update(guint n)
{
RpmOstreeOutputProgressUpdate progress = { n };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_UPDATE, &progress, active_cb_opaque);
}
// Start a percentage task.
std::unique_ptr<Progress>
progress_percent_begin(const rust::Str msg) noexcept
{
auto msg_c = std::string(msg);
RpmOstreeOutputProgressBegin begin = { msg_c.c_str(), true, 0 };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_BEGIN, &begin, active_cb_opaque);
return std::make_unique<Progress>(ProgressType::PERCENT);
}
// Update the percentage.
void
Progress::percent_update(guint n)
{
RpmOstreeOutputProgressUpdate progress = { (guint)n };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_UPDATE, &progress, active_cb_opaque);
}
// End the current task.
void
Progress::end(const rust::Str msg)
{
g_assert (!this->ended);
g_autofree char *final_msg = util::ruststr_dup_c_optempty(msg);
RpmOstreeOutputProgressEnd done = { final_msg };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_END, &done, active_cb_opaque);
this->ended = true;
}
void
rpmostree_output_progress_percent (int percentage)
{
RpmOstreeOutputProgressUpdate progress = { (guint)percentage };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_UPDATE, &progress, active_cb_opaque);
}
void
rpmostree_output_progress_nitems_begin (RpmOstreeProgress *taskp,
guint n, const char *format, ...)
{
g_assert (taskp && !taskp->initialized);
taskp->initialized = TRUE;
taskp->type = RPMOSTREE_PROGRESS_N_ITEMS;
g_autofree char *msg = strdup_vprintf (format);
RpmOstreeOutputProgressBegin begin = { msg, false, n };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_BEGIN, &begin, active_cb_opaque);
}
void
rpmostree_output_progress_percent_begin (RpmOstreeProgress *taskp,
const char *format, ...)
{
g_assert (taskp && !taskp->initialized);
taskp->initialized = TRUE;
taskp->type = RPMOSTREE_PROGRESS_PERCENT;
g_autofree char *msg = strdup_vprintf (format);
RpmOstreeOutputProgressBegin begin = { msg, true, 0 };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_BEGIN, &begin, active_cb_opaque);
}
void
rpmostree_output_progress_n_items (guint current)
{
RpmOstreeOutputProgressUpdate progress = { current };
active_cb (RPMOSTREE_OUTPUT_PROGRESS_UPDATE, &progress, active_cb_opaque);
}
} /* namespace */

View File

@ -19,7 +19,46 @@
#pragma once
#include <stdbool.h>
#include <memory>
#include "rust/cxx.h"
// C++ APIs here
namespace rpmostreecxx {
enum class ProgressType {
TASK,
N_ITEMS,
PERCENT,
};
void output_message (rust::Str msg);
struct Progress {
public:
void set_sub_message(rust::Str msg);
void message (rust::Str msg);
void nitems_update(guint n);
void percent_update(guint n);
void end(rust::Str msg);
~Progress() {
if (!this->ended)
this->end("");
}
Progress(ProgressType t) {
ptype = t;
ended = false;
}
ProgressType ptype;
bool ended;
};
std::unique_ptr<Progress> progress_begin_task(rust::Str msg) noexcept;
std::unique_ptr<Progress> progress_nitems_begin(guint n, rust::Str msg) noexcept;
std::unique_ptr<Progress> progress_percent_begin(rust::Str msg) noexcept;
}
// C APIs
G_BEGIN_DECLS
typedef enum {
@ -30,12 +69,6 @@ typedef enum {
RPMOSTREE_OUTPUT_PROGRESS_END,
} RpmOstreeOutputType;
typedef enum {
RPMOSTREE_PROGRESS_TASK,
RPMOSTREE_PROGRESS_N_ITEMS,
RPMOSTREE_PROGRESS_PERCENT,
} RpmOstreeProgressType;
void
rpmostree_output_default_handler (RpmOstreeOutputType type, void *data, void *opaque);
@ -49,27 +82,6 @@ typedef struct {
void
rpmostree_output_message (const char *format, ...) G_GNUC_PRINTF (1,2);
typedef struct {
bool initialized;
RpmOstreeProgressType type;
} RpmOstreeProgress;
void rpmostree_output_task_begin (RpmOstreeProgress *prog, const char *format, ...) G_GNUC_PRINTF (2,3);
void rpmostree_output_set_sub_message (const char *sub_message);
void rpmostree_output_progress_nitems_begin (RpmOstreeProgress *prog, guint n, const char *format, ...) G_GNUC_PRINTF (3,4);
void rpmostree_output_progress_n_items (guint i);
void rpmostree_output_progress_percent_begin (RpmOstreeProgress *prog, const char *format, ...) G_GNUC_PRINTF (2,3);
void rpmostree_output_progress_percent (int percentage);
void rpmostree_output_progress_end_msg (RpmOstreeProgress *prog, const char *format, ...) G_GNUC_PRINTF (2,3);
static inline void rpmostree_output_progress_end (RpmOstreeProgress *task)
{
rpmostree_output_progress_end_msg (task, NULL);
}
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (RpmOstreeProgress, rpmostree_output_progress_end)
/* For implementers of the output backend. If percent is TRUE, then n is
* ignored. If n is zero, then it is taken to be an indefinite task. Otherwise,
* n is used for n_items.

View File

@ -1823,6 +1823,7 @@ struct CommitThreadData {
off_t n_bytes;
off_t n_processed;
gint percent; /* atomic */
std::unique_ptr<rpmostreecxx::Progress> progress;
OstreeRepo *repo;
int rootfs_fd;
OstreeMutableTree *mtree;
@ -1968,7 +1969,7 @@ on_progress_timeout (gpointer datap)
const gint percent = g_atomic_int_get (&data->percent);
/* clamp to 100 if it somehow goes over (XXX: bad counting?) */
rpmostree_output_progress_percent (MIN(percent, 100));
data->progress->percent_update(MIN(percent, 100));
return TRUE;
}
@ -2038,8 +2039,7 @@ rpmostree_compose_commit (int rootfs_fd,
{
g_autoptr(GThread) commit_thread = g_thread_new ("commit", write_dfd_thread, &tdata);
g_auto(RpmOstreeProgress) commit_progress = { 0, };
rpmostree_output_progress_percent_begin (&commit_progress, "Committing");
tdata.progress = rpmostreecxx::progress_percent_begin("Committing");
g_autoptr(GSource) progress_src = g_timeout_source_new_seconds (1);
g_source_set_callback (progress_src, on_progress_timeout, &tdata, NULL);
@ -2051,7 +2051,7 @@ rpmostree_compose_commit (int rootfs_fd,
g_source_destroy (progress_src);
g_thread_join (util::move_nullify (commit_thread));
rpmostree_output_progress_percent (100);
tdata.progress->percent_update(100);
}
if (!tdata.success)

View File

@ -673,8 +673,7 @@ rpmostree_migrate_pkgcache_repo (OstreeRepo *repo,
{
if (S_ISDIR (stbuf.st_mode))
{
g_auto(RpmOstreeProgress) task = { 0, };
rpmostree_output_task_begin (&task, "Migrating pkgcache");
auto task = rpmostreecxx::progress_begin_task("Migrating pkgcache");
g_autoptr(OstreeRepo) pkgcache = ostree_repo_open_at (repo_dfd,
RPMOSTREE_OLD_PKGCACHE_DIR,
@ -686,7 +685,8 @@ rpmostree_migrate_pkgcache_repo (OstreeRepo *repo,
if (!do_pkgcache_migration (repo, pkgcache, &n_migrated, cancellable, error))
return FALSE;
rpmostree_output_progress_end_msg (&task, "%u done", n_migrated);
auto msg = g_strdup_printf("%u done", n_migrated);
task->end(msg);
if (n_migrated > 0)
sd_journal_print (LOG_INFO, "migrated %u cached package%s to system repo",
n_migrated, _NS(n_migrated));

View File

@ -70,6 +70,27 @@ throw_gerror (GError *&error)
error = NULL;
throw std::runtime_error (s);
}
// Duplicate a non-empty Rust Str to a NUL-terminated C string.
// The empty string is converted to a NULL pointer.
// This method should be viewed as a workaround for the lack
// of Option<> binding in cxx-rs.
static inline char *
ruststr_dup_c_optempty(const rust::Str s) {
if (s.length() > 0)
return g_strndup(s.data(), s.length());
return NULL;
}
// Return a Rust string pointer from a possibly-NULL C string.
// The NULL C string is converted to the empty string.
// This method should be viewed as a workaround for the lack
// of Option<> binding in cxx-rs.
static inline rust::Str
ruststr_or_empty (const char *c_str) {
return rust::Str(c_str ?: "");
}
}
namespace rpmostreecxx {