[gnome-software/eos-updater-upstream: 4/7] WIP: eos-updater add OS copying/USB support
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/eos-updater-upstream: 4/7] WIP: eos-updater add OS copying/USB support
- Date: Thu, 28 Mar 2019 17:15:10 +0000 (UTC)
commit 590f9f123e554b6827779ae58acfbba79b03d16a
Author: Philip Withnall <withnall endlessm com>
Date: Tue Mar 26 11:29:50 2019 +0000
WIP: eos-updater add OS copying/USB support
Signed-off-by: Philip Withnall <withnall endlessm com>
plugins/eos-updater/gs-plugin-eos-updater.c | 230 ++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
---
diff --git a/plugins/eos-updater/gs-plugin-eos-updater.c b/plugins/eos-updater/gs-plugin-eos-updater.c
index a59984df..7fe8b4d0 100644
--- a/plugins/eos-updater/gs-plugin-eos-updater.c
+++ b/plugins/eos-updater/gs-plugin-eos-updater.c
@@ -712,5 +712,235 @@ gs_plugin_app_upgrade_download (GsPlugin *plugin,
else
sync_state_from_updater (plugin);
+ return TRUE;
+}
+
+gboolean
+gs_plugin_file_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *content_type = NULL;
+ const gchar * const mimetypes_repo[] = {
+ "inode/directory",
+ NULL };
+
+ /* does this match any of the mimetypes we support */
+ content_type = gs_utils_get_content_type (file, cancellable, error);
+ if (content_type == NULL)
+ return FALSE;
+ if (g_strv_contains (mimetypes_repo, content_type)) {
+ /* If it looks like an ostree repo that could be on a USB drive,
+ * have eos-updater check it for available OS updates */
+ g_autoptr (GFile) repo_dir = NULL;
+
+ repo_dir = g_file_get_child (file, ".ostree");
+ if (g_file_query_exists (repo_dir, NULL))
+ return check_for_os_updates (plugin, cancellable, error);
+ }
+
+ return TRUE;
+}
+
+static char *
+get_os_collection_id (GError **error)
+{
+ OstreeDeployment *booted_deployment;
+ GKeyFile *origin;
+ g_autofree char *refspec = NULL;
+ g_autofree char *remote = NULL;
+ g_autofree char *collection_id = NULL;
+ g_autoptr(OstreeRepo) repo = NULL;
+ g_autoptr(OstreeSysroot) sysroot = NULL;
+
+ sysroot = ostree_sysroot_new_default ();
+ if (!ostree_sysroot_load (sysroot, NULL, error))
+ return NULL;
+
+ booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
+ if (booted_deployment == NULL) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Could not get booted deployment");
+ return NULL;
+ }
+
+ origin = ostree_deployment_get_origin (booted_deployment);
+ if (origin == NULL) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Could not get deployment origin");
+ return NULL;
+ }
+
+ refspec = g_key_file_get_string (origin, "origin", "refspec", error);
+ if (refspec == NULL)
+ return NULL;
+
+ ostree_parse_refspec (refspec, &remote, NULL, error);
+ if (remote == NULL)
+ return NULL;
+
+ repo = ostree_repo_new_default ();
+ if (!ostree_repo_open (repo, NULL, error))
+ return NULL;
+
+ if (!ostree_repo_get_remote_option (repo, remote, "collection-id", NULL, &collection_id, error))
+ return NULL;
+
+ return g_steal_pointer (&collection_id);
+}
+
+gboolean
+gs_plugin_os_get_copyable (GsPlugin *plugin,
+ const gchar *copy_dest,
+ gboolean *copyable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GError) local_error = NULL;
+ g_autofree char *collection_id = get_os_collection_id (&local_error);
+
+ if (local_error != NULL)
+ g_debug ("Failed to get OSTree collection ID: %s", local_error->message);
+
+ *copyable = (collection_id != NULL);
+
+ return TRUE;
+}
+
+typedef struct {
+ GCancellable *cancellable; /* (owned) */
+ gulong cancelled_id;
+ gboolean finished;
+ GError *error; /* (nullable) (owned) */
+ GMainContext *context; /* (owned) */
+} OsCopyProcessHelper;
+
+static void
+os_copy_process_helper_free (OsCopyProcessHelper *helper)
+{
+ g_clear_object (&helper->cancellable);
+ g_assert (helper->cancelled_id == 0); /* disconnected in watch_cb() */
+ g_clear_error (&helper->error);
+ g_main_context_unref (helper->context);
+ g_free (helper);
+}
+
+static OsCopyProcessHelper *
+os_copy_process_helper_new (GMainContext *context,
+ GCancellable *cancellable,
+ gulong cancelled_id)
+{
+ OsCopyProcessHelper *helper = g_new0 (OsCopyProcessHelper, 1);
+ helper->cancellable = g_object_ref (cancellable);
+ helper->cancelled_id = cancelled_id;
+ helper->finished = FALSE;
+ helper->error = NULL;
+ helper->context = g_main_context_ref (context);
+
+ return helper;
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OsCopyProcessHelper, os_copy_process_helper_free)
+
+static void
+os_copy_process_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+ OsCopyProcessHelper *helper = user_data;
+ g_autoptr(GError) error = NULL;
+
+ if (!g_cancellable_is_cancelled (helper->cancellable) && status != 0)
+ g_set_error (&helper->error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Failed to copy OS to removable media: command "
+ "failed with status %d", status);
+
+ g_cancellable_disconnect (helper->cancellable, helper->cancelled_id);
+ helper->cancelled_id = 0;
+ g_spawn_close_pid (pid);
+
+ /* once the copy terminates (successfully or not), set plugin status to
+ * update UI accordingly */
+
+ helper->finished = TRUE;
+ g_main_context_wakeup (helper->context);
+}
+
+static void
+os_copy_cancelled_cb (GCancellable *cancellable, gpointer user_data)
+{
+ GPid pid = GPOINTER_TO_INT (user_data);
+
+ /* terminate the process which is copying the OS */
+ kill (pid, SIGTERM);
+}
+
+gboolean
+gs_plugin_os_copy (GsPlugin *plugin,
+ const gchar *copy_dest,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* this is used in an async function but we block here until that
+ * returns so we won't auto-free while other threads depend on this */
+ g_autoptr(OsCopyProcessHelper) helper = NULL;
+ gboolean spawn_retval;
+ const gchar *argv[] = {"/usr/bin/pkexec",
+ "/usr/bin/eos-updater-prepare-volume",
+ copy_dest,
+ NULL};
+ GPid child_pid;
+ gulong cancelled_id;
+ g_autoptr(GMainContext) context = NULL;
+ g_autoptr(GSource) child_watch_source = NULL;
+
+ context = g_main_context_new ();
+ g_main_context_push_thread_default (context);
+
+ g_debug ("Copying OS to: %s", copy_dest);
+
+ spawn_retval = g_spawn_async (".",
+ (gchar **) argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &child_pid,
+ error);
+
+ if (spawn_retval) {
+ cancelled_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (os_copy_cancelled_cb),
+ GINT_TO_POINTER (child_pid),
+ NULL);
+
+ helper = os_copy_process_helper_new (context, cancellable, cancelled_id);
+ child_watch_source = g_child_watch_source_new (child_pid);
+ g_source_set_callback (child_watch_source,
+ G_SOURCE_FUNC (os_copy_process_watch_cb), helper, NULL);
+ g_source_attach (child_watch_source, context);
+ } else {
+ g_main_context_pop_thread_default (context);
+ return FALSE;
+ }
+
+ /* Iterate the main loop until either the copy process completes or the
+ * user cancels the copy. Without this, it is impossible to cancel the
+ * copy because we reach the end of this function, its parent GTask
+ * returns and we disconnect the handler that would kill the copy
+ * process. */
+ while (!helper->finished)
+ g_main_context_iteration (context, TRUE);
+
+ g_source_destroy (child_watch_source);
+ g_main_context_pop_thread_default (context);
+
+ if (helper->error) {
+ g_propagate_error (error, g_steal_pointer (&helper->error));
+ return FALSE;
+ }
+
return TRUE;
}
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]