Codebase list realmd / upstream/0.4 service / realm-packages.c
upstream/0.4

Tree @upstream/0.4 (Download .tar.gz)

realm-packages.c @upstream/0.4raw · history · blame

/* realmd -- Realm configuration service
 *
 * Copyright 2012 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.
 *
 * See the included COPYING file for more information.
 *
 * Author: Stef Walter <stefw@gnome.org>
 */

#include "config.h"

#define DEBUG_FLAG REALM_DEBUG_PACKAGES
#include "realm-debug.h"
#include "realm-diagnostics.h"
#include "realm-daemon.h"
#include "realm-packages.h"
#include "realm-settings.h"

#define I_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE
#include <packagekit-glib2/packagekit.h>

typedef struct {
	PkTask *task;
	gchar **packages;
	GDBusMethodInvocation *invocation;
} InstallClosure;

static void
install_closure_free (gpointer data)
{
	InstallClosure *install = data;
	g_object_ref (install->task);
	g_strfreev (install->packages);
	g_clear_object (&install->invocation);
	g_slice_free (InstallClosure, install);
}

static void
on_install_progress (PkProgress *progress,
                     PkProgressType type,
                     gpointer user_data)
{
	gchar *string;
	guint unumber;
	gint number;

	if (type == PK_PROGRESS_TYPE_STATUS) {
#ifdef TODO
		PkStatusEnum status;
		g_object_get (progress, "status", &status, NULL);
		switch (status) {
		case PK_STATUS_WAIT:
			realm_status (install->invocation, "Waiting for package system");
			break;
		case PK_STATUS_ENUM_WAITING_FOR_AUTH:
			pk_status_enum_to_localised_text ();
		};
#endif
	}

	switch (type) {
	case PK_PROGRESS_TYPE_PACKAGE_ID:
		g_object_get (progress, "package-id", &string, NULL);
		realm_debug ("package-id: %s", string);
		g_free (string);
		break;
	case PK_PROGRESS_TYPE_TRANSACTION_ID:
		g_object_get (progress, "transaction-id", &string, NULL);
		realm_debug ("transaction-id: %s", string);
		g_free (string);
		break;
	case PK_PROGRESS_TYPE_PERCENTAGE:
		g_object_get (progress, "percentage", &number, NULL);
		realm_debug ("percentage: %d", number);
		break;
	case PK_PROGRESS_TYPE_STATUS:
		g_object_get (progress, "status", &unumber, NULL);
		realm_debug ("status: %s", pk_status_enum_to_string (unumber));
		break;
	case PK_PROGRESS_TYPE_ELAPSED_TIME:
		g_object_get (progress, "elapsed-time", &unumber, NULL);
		realm_debug ("elapsed-time: %u", unumber);
		break;
	case PK_PROGRESS_TYPE_REMAINING_TIME:
		g_object_get (progress, "remaining-time", &unumber, NULL);
		realm_debug ("remaining-time: %u", unumber);
		break;
	case PK_PROGRESS_TYPE_SPEED:
		g_object_get (progress, "speed", &unumber, NULL);
		realm_debug ("speed: %u", unumber);
		break;
	case PK_PROGRESS_TYPE_INVALID:
	case PK_PROGRESS_TYPE_ALLOW_CANCEL:
	case PK_PROGRESS_TYPE_CALLER_ACTIVE:
	case PK_PROGRESS_TYPE_ROLE:
	case PK_PROGRESS_TYPE_UID:
	case PK_PROGRESS_TYPE_PACKAGE:
	case PK_PROGRESS_TYPE_ITEM_PROGRESS:
	default:
		break;
	}
}

static gchar **
extract_uninstalled_package_ids (PkResults *results,
                                 gchar **string)
{
	GPtrArray *packages;
	PkPackage *package;
	GPtrArray *ids;
	GString *desc;
	guint i;

	packages = pk_results_get_package_array (results);
	ids = g_ptr_array_new_with_free_func (g_free);
	desc = g_string_new ("");

	for (i = 0; i < packages->len; i++) {
		package = PK_PACKAGE (packages->pdata[i]);
		if (pk_package_get_info (package) != PK_INFO_ENUM_INSTALLED) {
			g_ptr_array_add (ids, g_strdup (pk_package_get_id (package)));
			if (desc->len)
				g_string_append (desc, ", ");
			g_string_append (desc, pk_package_get_name (package));
		}
	}

	g_ptr_array_add (ids, NULL);
	g_ptr_array_free (packages, TRUE);
	*string = g_string_free (desc, FALSE);
	return (gchar **)g_ptr_array_free (ids, FALSE);
}

static void
on_install_installed (GObject *source,
                      GAsyncResult *result,
                      gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
	InstallClosure *install = g_simple_async_result_get_op_res_gpointer (res);
	GError *error = NULL;
	PkResults *results;

	results = pk_task_generic_finish (install->task, result, &error);
	if (error == NULL)
		g_object_unref (results);
	else
		g_simple_async_result_take_error (res, error);

	g_simple_async_result_complete (res);
	g_object_unref (res);
}

static void
on_install_resolved (GObject *source,
                     GAsyncResult *result,
                     gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
	InstallClosure *install = g_simple_async_result_get_op_res_gpointer (res);
	gchar **package_ids;
	GError *error = NULL;
	PkResults *results;
	gchar *desc;

	results = pk_task_generic_finish (install->task, result, &error);
	if (error == NULL) {
		package_ids = extract_uninstalled_package_ids (results, &desc);
		if (package_ids == NULL || *package_ids == NULL) {
			g_simple_async_result_complete (res);

		} else {
			realm_diagnostics_info (install->invocation, "Installing: %s", desc);
			pk_task_install_packages_async (install->task, package_ids, NULL,
			                                on_install_progress, install,
			                                on_install_installed, g_object_ref (res));
		}

		g_strfreev (package_ids);
		g_object_unref (results);
		g_free (desc);

	} else {
		g_simple_async_result_take_error (res, error);
		g_simple_async_result_complete (res);
	}

	g_object_unref (res);
}

static void
on_install_refresh (GObject *source,
                    GAsyncResult *result,
                    gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
	InstallClosure *install = g_simple_async_result_get_op_res_gpointer (res);
	GError *error = NULL;
	PkResults *results;
	PkBitfield filter;

	results = pk_task_generic_finish (install->task, result, &error);
	if (error == NULL) {
		filter = pk_filter_bitfield_from_string ("arch");
		pk_task_resolve_async (install->task, filter, install->packages, NULL,
		                       on_install_progress, install, on_install_resolved, g_object_ref (res));
		g_object_unref (results);

	} else {
		g_simple_async_result_take_error (res, error);
		g_simple_async_result_complete (res);
	}

	g_object_unref (res);
}

static void
lookup_required_files_and_packages (gchar ***packages,
                                    gchar ***files,
                                    gboolean *unconditional)
{
	GHashTable *settings;
	GHashTableIter iter;
	gchar *package;
	gchar *file;
	gchar **f, **p;
	gsize length;

	*unconditional = FALSE;

	settings = realm_settings_section ("active-directory-packages");
	length = settings ? g_hash_table_size (settings) : 0;

	*packages = p = g_new0 (gchar *, length + 1);
	*files = f = g_new0 (gchar *, length + 1);

	g_hash_table_iter_init (&iter, settings);
	while (g_hash_table_iter_next (&iter, (gpointer *)&package, (gpointer *)&file)) {
		file = g_strstrip (g_strdup (file));
		if (g_str_equal (file, "")) {
			g_free (file);
			*unconditional = TRUE;
		} else {
			*(f++) = file;
		}
		package = g_strstrip (g_strdup (package));
		if (g_str_equal (package, ""))
			g_free (package);
		else
			*(p++) = package;

		/* As a logic double check */
		g_assert (length-- > 0);
	}
}

void
realm_packages_install_async (const gchar *package_set,
                              GDBusMethodInvocation *invocation,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
{
	GSimpleAsyncResult *res;
	InstallClosure *install;
	gboolean unconditional;
	gchar **required_files;
	gchar **packages;
	gchar *string;
	gboolean have;

	g_return_if_fail (package_set != NULL);
	g_return_if_fail (invocation == NULL || G_IS_DBUS_METHOD_INVOCATION (invocation));

	lookup_required_files_and_packages (&packages, &required_files, &unconditional);

	res = g_simple_async_result_new (NULL, callback, user_data, realm_packages_install_async);
	install = g_slice_new (InstallClosure);
	install->packages = packages;
	install->task = pk_task_new ();
	pk_task_set_interactive (install->task, FALSE);
	install->invocation = invocation ? g_object_ref (invocation) : NULL;
	g_simple_async_result_set_op_res_gpointer (res, install, install_closure_free);

	if (unconditional) {
		have = FALSE;
		realm_diagnostics_info (invocation, "Unconditionally checking packages");

	} else {
		have = realm_packages_check_paths ((const gchar **)required_files, invocation);
		string = g_strjoinv (", ", required_files);
		realm_diagnostics_info (invocation, "Required files %s: %s",
		                        have ? "present" : "not present, installing", string);
		g_free (string);
	}

	g_strfreev (required_files);

	if (have) {
		g_simple_async_result_complete_in_idle (res);
		g_object_unref (res);
		return;
	}

	realm_diagnostics_info (invocation, "Refreshing package cache");

	pk_task_refresh_cache_async (install->task, FALSE, NULL, on_install_progress, install,
	                             on_install_refresh, g_object_ref (res));

	g_object_unref (res);
}

gboolean
realm_packages_install_finish (GAsyncResult *result,
                               GError **error)
{
	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
	                      realm_packages_install_async), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	return TRUE;
}

gboolean
realm_packages_check_paths (const gchar **paths,
                            GDBusMethodInvocation *invocation)
{
	gint i;

	g_return_val_if_fail (paths != NULL, FALSE);
	g_return_val_if_fail (invocation == NULL || G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);

	for (i = 0; paths[i] != NULL; i++) {
		if (!g_file_test (paths[i], G_FILE_TEST_EXISTS)) {
			realm_diagnostics_info (invocation, "Couldn't find file: %s", paths[i]);
			return FALSE;
		}
	}

	return TRUE;
}