/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2013-2014 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hif-backend.h" typedef struct { HySack sack; gboolean valid; gchar *key; } HifSackCacheItem; typedef struct { HifContext *context; GFileMonitor *monitor_rpmdb; GHashTable *sack_cache; /* of HifSackCacheItem */ GMutex sack_mutex; gchar **native_arches; HifRepos *repos; GTimer *repos_timer; } PkBackendHifPrivate; typedef struct { GPtrArray *sources; GCancellable *cancellable; HifDb *db; HifState *state; rpmts ts; rpmKeyring keyring; GPtrArray *packages_to_download; PkBitfield transaction_flags; HyGoal goal; } PkBackendHifJobData; static PkBackendHifPrivate *priv; /** * pk_backend_get_description: */ const gchar * pk_backend_get_description (PkBackend *backend) { return g_strdup ("Hif"); } /** * pk_backend_get_author: */ const gchar * pk_backend_get_author (PkBackend *backend) { return g_strdup ("Richard Hughes "); } /** * pk_backend_supports_parallelization: */ gboolean pk_backend_supports_parallelization (PkBackend *backend) { return FALSE; } /** * pk_backend_sack_cache_invalidate: **/ static void pk_backend_sack_cache_invalidate (const gchar *why) { GList *values; GList *l; HifSackCacheItem *cache_item; /* set all the cached sacks as invalid */ g_mutex_lock (&priv->sack_mutex); values = g_hash_table_get_values (priv->sack_cache); for (l = values; l != NULL; l = l->next) { cache_item = l->data; if (cache_item->valid) { g_debug ("invalidating %s as %s", cache_item->key, why); cache_item->valid = FALSE; } } g_mutex_unlock (&priv->sack_mutex); } /** * pk_backend_yum_repos_changed_cb: **/ static void pk_backend_yum_repos_changed_cb (HifRepos *self, PkBackend *backend) { pk_backend_sack_cache_invalidate ("yum.repos.d changed"); pk_backend_repo_list_changed (backend); } /** * pk_backend_rpmdb_changed_cb: **/ static void pk_backend_rpmdb_changed_cb (GFileMonitor *monitor_, GFile *file, GFile *other_file, GFileMonitorEvent event_type, PkBackend *backend) { pk_backend_sack_cache_invalidate ("rpmdb changed"); } /** * hif_sack_cache_item_free: */ static void hif_sack_cache_item_free (HifSackCacheItem *cache_item) { hy_sack_free (cache_item->sack); g_free (cache_item->key); g_slice_free (HifSackCacheItem, cache_item); } /** * pk_backend_initialize: */ void pk_backend_initialize (GKeyFile *conf, PkBackend *backend) { GError *error = NULL; GFile *file_rpmdb = NULL; gboolean ret; /* use logging */ pk_debug_add_log_domain (G_LOG_DOMAIN); pk_debug_add_log_domain ("Hif"); /* create private area */ priv = g_new0 (PkBackendHifPrivate, 1); g_debug ("Using Hif %i.%i.%i", HIF_MAJOR_VERSION, HIF_MINOR_VERSION, HIF_MICRO_VERSION); g_debug ("Using Hawkey %i.%i.%i", HY_VERSION_MAJOR, HY_VERSION_MINOR, HY_VERSION_PATCH); g_debug ("Using librepo %i.%i.%i", LR_VERSION_MAJOR, LR_VERSION_MINOR, LR_VERSION_PATCH); /* a cache of HySacks with the key being which sacks are loaded * * notes: * - this deals with deallocating the sack when the backend is unloaded * - all the cached sacks are dropped on any transaction that can * modify state or if the repos or rpmdb are changed */ g_mutex_init (&priv->sack_mutex); priv->sack_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) hif_sack_cache_item_free); /* set defaults */ priv->context = hif_context_new (); ret = hif_context_setup (priv->context, NULL, &error); if (!ret) { g_error ("failed to setup context: %s", error->message); g_error_free (error); } hif_context_set_cache_dir (priv->context, "/var/cache/PackageKit/metadata"); hif_context_set_repo_dir (priv->context, "/etc/yum.repos.d"); hif_context_set_rpm_verbosity (priv->context, "info"); /* used a cached list of sources */ priv->repos = hif_repos_new (priv->context); priv->repos_timer = g_timer_new (); g_signal_connect (priv->repos, "changed", G_CALLBACK (pk_backend_yum_repos_changed_cb), backend); /* setup native arches */ priv->native_arches = g_new0 (gchar *, 3); priv->native_arches[0] = g_strdup (hif_context_get_arch_info (priv->context)); priv->native_arches[1] = g_strdup ("noarch"); /* setup a file monitor on the rpmdb */ file_rpmdb = g_file_new_for_path ("/var/lib/rpm/Packages"); priv->monitor_rpmdb = g_file_monitor_file (file_rpmdb, G_FILE_MONITOR_NONE, NULL, &error); if (priv->monitor_rpmdb != NULL) { g_signal_connect (priv->monitor_rpmdb, "changed", G_CALLBACK (pk_backend_rpmdb_changed_cb), backend); } else { g_warning ("failed to setup monitor: %s", error->message); g_error_free (error); } lr_global_init (); if (file_rpmdb != NULL) g_object_unref (file_rpmdb); } /** * pk_backend_destroy: */ void pk_backend_destroy (PkBackend *backend) { if (priv->context != NULL) g_object_unref (priv->context); if (priv->monitor_rpmdb != NULL) g_object_unref (priv->monitor_rpmdb); g_timer_destroy (priv->repos_timer); g_object_unref (priv->repos); g_strfreev (priv->native_arches); g_mutex_clear (&priv->sack_mutex); g_hash_table_unref (priv->sack_cache); g_free (priv); } /** * pk_backend_state_percentage_changed_cb: */ static void pk_backend_state_percentage_changed_cb (HifState *state, guint percentage, PkBackendJob *job) { pk_backend_job_set_percentage (job, percentage); } /** * pk_backend_state_action_changed_cb: **/ static void pk_backend_state_action_changed_cb (HifState *state, PkStatusEnum action, const gchar *action_hint, PkBackendJob *job) { if (action != PK_STATUS_ENUM_UNKNOWN) { g_debug ("got state %s with hint %s", pk_status_enum_to_string (action), action_hint); pk_backend_job_set_status (job, action); } switch (action) { case PK_STATUS_ENUM_DOWNLOAD: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_DOWNLOADING, action_hint, ""); } break; case PK_STATUS_ENUM_INSTALL: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_INSTALLING, action_hint, ""); } break; case PK_STATUS_ENUM_REMOVE: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_REMOVING, action_hint, ""); } break; case PK_STATUS_ENUM_UPDATE: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_UPDATING, action_hint, ""); } break; case PK_STATUS_ENUM_CLEANUP: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_CLEANUP, action_hint, ""); } break; default: break; } } /** * pk_backend_speed_changed_cb: **/ static void pk_backend_speed_changed_cb (HifState *state, GParamSpec *pspec, PkBackendJob *job) { pk_backend_job_set_speed (job, hif_state_get_speed (state)); } /** * pk_backend_start_job: */ void pk_backend_start_job (PkBackend *backend, PkBackendJob *job) { PkBackendHifJobData *job_data; job_data = g_new0 (PkBackendHifJobData, 1); pk_backend_job_set_user_data (job, job_data); job_data->cancellable = g_cancellable_new (); /* setup RPM */ job_data->ts = rpmtsCreate (); job_data->keyring = rpmtsGetKeyring (job_data->ts, 1); /* HifState */ job_data->packages_to_download = g_ptr_array_new_with_free_func ((GDestroyNotify) hy_package_free); job_data->state = hif_state_new (); hif_state_set_cancellable (job_data->state, job_data->cancellable); g_signal_connect (job_data->state, "percentage-changed", G_CALLBACK (pk_backend_state_percentage_changed_cb), job); g_signal_connect (job_data->state, "action-changed", G_CALLBACK (pk_backend_state_action_changed_cb), job); g_signal_connect (job_data->state, "notify::speed", G_CALLBACK (pk_backend_speed_changed_cb), job); /* #HifDb is a simple flat file 'database' for stroring details about * installed packages, such as the command line that installed them, * the uid of the user performing the action and the repository they * came from. * * A yumdb is not really a database at all, and is really slow to read * and especially slow to write data for packages. It is provided for * compatibility with existing users of yum, but long term this * functionality should either be folded into rpm itself, or just put * into an actual database format like sqlite. * * Using the filesystem as a database probably wasn't a great design * decision. */ job_data->db = hif_db_new (priv->context); #ifdef PK_BUILD_LOCAL /* we don't want to enable this for normal runtime */ hif_state_set_enable_profile (job_data->state, TRUE); #endif /* no locks to get, so jump straight to 'running' */ pk_backend_job_set_status (job, PK_STATUS_ENUM_RUNNING); } #if 0 /** * pk_backend_reset_job: */ void pk_backend_reset_job (PkBackend *backend, PkBackendJob *job) { PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); hif_state_reset (job_data->state); g_cancellable_reset (job_data->cancellable); } #endif /** * pk_backend_stop_job: */ void pk_backend_stop_job (PkBackend *backend, PkBackendJob *job) { PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); g_object_unref (job_data->cancellable); if (job_data->state != NULL) { hif_state_release_locks (job_data->state); g_object_unref (job_data->state); } if (job_data->sources != NULL) g_ptr_array_unref (job_data->sources); g_ptr_array_unref (job_data->packages_to_download); if (job_data->goal != NULL) hy_goal_free (job_data->goal); rpmtsFree (job_data->ts); rpmKeyringFree (job_data->keyring); g_object_unref (job_data->db); g_free (job_data); pk_backend_job_set_user_data (job, NULL); } /** * pk_backend_ensure_sources: */ static gboolean pk_backend_ensure_sources (PkBackendHifJobData *job_data, GError **error) { gboolean ret = TRUE; /* already set */ if (job_data->sources != NULL) goto out; /* set the list of repos */ job_data->sources = hif_repos_get_sources (priv->repos, error); if (job_data->sources == NULL) { ret = FALSE; goto out; } out: return ret; } /** * pk_backend_ensure_origin_pkg: */ static void pk_backend_ensure_origin_pkg (HifDb *db, HyPackage pkg) { gchar *tmp; GError *error = NULL; /* already set */ if (hif_package_get_origin (pkg) != NULL) return; if (!hy_package_installed (pkg)) return; /* set from the database if available */ tmp = hif_db_get_string (db, pkg, "from_repo", &error); if (tmp == NULL) { g_debug ("no origin for %s: %s", hif_package_get_id (pkg), error->message); g_error_free (error); } else { hif_package_set_origin (pkg, tmp); } g_free (tmp); } /** * pk_backend_ensure_origin_pkglist: */ static void pk_backend_ensure_origin_pkglist (HifDb *db, HyPackageList pkglist) { HyPackage pkg; gint i; FOR_PACKAGELIST(pkg, pkglist, i) pk_backend_ensure_origin_pkg (db, pkg); } /** * hif_utils_add_remote: */ static gboolean hif_utils_add_remote (PkBackendJob *job, HySack sack, HifSackAddFlags flags, HifState *state, GError **error) { gboolean ret = TRUE; HifState *state_local; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); /* set state */ ret = hif_state_set_steps (state, error, 2, /* load files */ 98, /* add sources */ -1); if (!ret) goto out; /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* add each repo */ state_local = hif_state_get_child (state); ret = hif_sack_add_sources (sack, job_data->sources, pk_backend_job_get_cache_age (job), flags, state_local, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; out: return ret; } typedef enum { HIF_CREATE_SACK_FLAG_NONE, HIF_CREATE_SACK_FLAG_USE_CACHE, HIF_CREATE_SACK_FLAG_LAST } HifCreateSackFlags; /** * hif_utils_create_cache_key: */ static gchar * hif_utils_create_cache_key (HifSackAddFlags flags) { GString *key; key = g_string_new ("HySack::"); if (flags == HIF_SACK_ADD_FLAG_NONE) { g_string_append (key, "none"); } else { if (flags & HIF_SACK_ADD_FLAG_FILELISTS) g_string_append (key, "filelists|"); if (flags & HIF_SACK_ADD_FLAG_UPDATEINFO) g_string_append (key, "updateinfo|"); if (flags & HIF_SACK_ADD_FLAG_REMOTE) g_string_append (key, "remote|"); g_string_truncate (key, key->len - 1); } return g_string_free (key, FALSE); } /** * hif_utils_create_sack_for_filters: */ static HySack hif_utils_create_sack_for_filters (PkBackendJob *job, PkBitfield filters, HifCreateSackFlags create_flags, HifState *state, GError **error) { const gchar *cachedir = "/var/cache/PackageKit/hif"; gboolean ret; gchar *cache_key = NULL; gint rc; HifSackAddFlags flags = HIF_SACK_ADD_FLAG_FILELISTS; HifSackCacheItem *cache_item = NULL; HifState *state_local; HySack sack = NULL; /* don't add if we're going to filter out anyway */ if (!pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED)) flags |= HIF_SACK_ADD_FLAG_REMOTE; /* only load updateinfo when required */ if (pk_backend_job_get_role (job) == PK_ROLE_ENUM_GET_UPDATE_DETAIL) flags |= HIF_SACK_ADD_FLAG_UPDATEINFO; /* media repos could disappear at any time */ if ((create_flags & HIF_CREATE_SACK_FLAG_USE_CACHE) > 0 && hif_repos_has_removable (priv->repos) && g_timer_elapsed (priv->repos_timer, NULL) > 1.0f) { g_debug ("not reusing sack as media may have disappeared"); create_flags &= ~HIF_CREATE_SACK_FLAG_USE_CACHE; } g_timer_reset (priv->repos_timer); /* if we've specified a specific cache-age then do not use the cache */ if ((flags & HIF_SACK_ADD_FLAG_REMOTE) > 0 && pk_backend_job_get_cache_age (job) != G_MAXUINT) { g_debug ("not reusing sack specific cache age requested"); create_flags &= ~HIF_CREATE_SACK_FLAG_USE_CACHE; } /* do we have anything in the cache */ cache_key = hif_utils_create_cache_key (flags); if ((create_flags & HIF_CREATE_SACK_FLAG_USE_CACHE) > 0) cache_item = g_hash_table_lookup (priv->sack_cache, cache_key); if (cache_item != NULL && cache_item->sack != NULL) { if (cache_item->valid) { ret = TRUE; g_debug ("using cached sack %s", cache_key); sack = cache_item->sack; goto out; } else { /* we have to do this now rather than rely on the * callback of the hash table */ g_hash_table_remove (priv->sack_cache, cache_key); } } /* update status */ hif_state_action_start (state, PK_STATUS_ENUM_QUERY, NULL); /* set state */ if ((flags & HIF_SACK_ADD_FLAG_REMOTE) > 0) { ret = hif_state_set_steps (state, error, 8, /* add installed */ 92, /* add remote */ -1); if (!ret) goto out; } else { hif_state_set_number_steps (state, 1); } /* create empty sack */ sack = hy_sack_create (cachedir, NULL, NULL, HY_MAKE_CACHE_DIR); if (sack == NULL) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_INTERNAL_ERROR, "failed to create sack cache"); goto out; } /* add installed packages */ rc = hy_sack_load_system_repo (sack, NULL, HY_BUILD_CACHE); ret = hif_rc_to_gerror (rc, error); if (!ret) { g_prefix_error (error, "Failed to load system repo: "); goto out; } /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* add remote packages */ if ((flags & HIF_SACK_ADD_FLAG_REMOTE) > 0) { state_local = hif_state_get_child (state); ret = hif_utils_add_remote (job, sack, flags, state_local, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; } /* creates repo for command line rpms */ hy_sack_create_cmdline_repo (sack); /* save in cache */ g_mutex_lock (&priv->sack_mutex); cache_item = g_slice_new (HifSackCacheItem); cache_item->key = g_strdup (cache_key); cache_item->sack = sack; cache_item->valid = TRUE; g_debug ("created cached sack %s", cache_item->key); g_hash_table_insert (priv->sack_cache, g_strdup (cache_key), cache_item); g_mutex_unlock (&priv->sack_mutex); out: g_free (cache_key); if (!ret && sack != NULL) { hy_sack_free (sack); sack = NULL; } return sack; } /** * hif_utils_add_query_filters: */ static void hif_utils_add_query_filters (HyQuery query, PkBitfield filters) { const gchar *application_glob = "/usr/share/applications/*.desktop"; /* newest */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NEWEST)) hy_query_filter_latest_per_arch (query, TRUE); /* arch */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_ARCH)) { hy_query_filter_in (query, HY_PKG_ARCH, HY_EQ, (const gchar **) priv->native_arches); } else if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_ARCH)) { hy_query_filter_in (query, HY_PKG_ARCH, HY_NEQ, (const gchar **) priv->native_arches); } /* installed */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED)) hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); else if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED)) hy_query_filter (query, HY_PKG_REPONAME, HY_NEQ, HY_SYSTEM_REPO_NAME); /* source */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SOURCE)) hy_query_filter (query, HY_PKG_ARCH, HY_EQ, "src"); if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SOURCE)) hy_query_filter (query, HY_PKG_ARCH, HY_NEQ, "src"); /* application */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_APPLICATION)) hy_query_filter (query, HY_PKG_FILE, HY_GLOB, application_glob); else if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_APPLICATION)) hy_query_filter (query, HY_PKG_FILE, HY_NOT | HY_GLOB, application_glob); } /** * pk_backend_what_provides_decompose: */ static gchar ** pk_backend_what_provides_decompose (gchar **values, GError **error) { guint i; guint len; gchar **search = NULL; GPtrArray *array = NULL; /* iter on each provide string, and wrap it with the fedora prefix */ len = g_strv_length (values); array = g_ptr_array_new_with_free_func (g_free); for (i = 0; i < len; i++) { g_ptr_array_add (array, g_strdup (values[i])); g_ptr_array_add (array, g_strdup_printf ("gstreamer0.10(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("gstreamer1(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("font(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("mimehandler(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("postscriptdriver(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("plasma4(%s)", values[i])); g_ptr_array_add (array, g_strdup_printf ("plasma5(%s)", values[i])); } search = pk_ptr_array_to_strv (array); for (i = 0; search[i] != NULL; i++) g_debug ("Querying provide '%s'", search[i]); return search; } /** * hif_package_ensure_source: */ static gboolean hif_package_ensure_source (GPtrArray *sources, HyPackage pkg, GError **error) { HifSource *src; char *location; gboolean ret = TRUE; /* this is a local file */ if (g_strcmp0 (hy_package_get_reponame (pkg), HY_CMDLINE_REPO_NAME) == 0) { location = hy_package_get_location (pkg); hif_package_set_filename (pkg, location); hy_free (location); goto out; } /* get repo */ if (hy_package_installed (pkg)) goto out; src = hif_repos_get_source_by_id (priv->repos, hy_package_get_reponame (pkg), error); if (src == NULL) { g_prefix_error (error, "Failed to ensure %s: ", hy_package_get_name (pkg)); ret = FALSE; goto out; } hif_package_set_source (pkg, src); out: return ret; } /** * hif_package_ensure_source_list: */ static gboolean hif_package_ensure_source_list (GPtrArray *sources, HyPackageList pkglist, GError **error) { gboolean ret = TRUE; guint i; HyPackage pkg; FOR_PACKAGELIST(pkg, pkglist, i) { ret = hif_package_ensure_source (sources, pkg, error); if (!ret) goto out; } out: return ret; } /** * pk_backend_search_thread: */ static void pk_backend_search_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **search = NULL; gchar **search_tmp; GError *error = NULL; HifState *state_local; HyPackageList pkglist = NULL; HyQuery query = NULL; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters = 0; /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 39, /* add repos */ 50, /* query */ 1, /* ensure source list */ 1, /* ensure origin */ 9, /* emit */ -1); g_assert (ret); /* get arguments */ switch (pk_backend_job_get_role (job)) { case PK_ROLE_ENUM_GET_UPDATES: case PK_ROLE_ENUM_GET_PACKAGES: g_variant_get (params, "(t)", &filters); break; case PK_ROLE_ENUM_WHAT_PROVIDES: g_variant_get (params, "(t^a&s)", &filters, &search_tmp); search = pk_backend_what_provides_decompose (search_tmp, &error); if (search == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } break; default: g_variant_get (params, "(t^as)", &filters, &search); break; } /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* get sack */ state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* run query */ query = hy_query_create (sack); hif_utils_add_query_filters (query, filters); switch (pk_backend_job_get_role (job)) { case PK_ROLE_ENUM_GET_PACKAGES: pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_RESOLVE: hy_query_filter_in (query, HY_PKG_NAME, HY_EQ, (const gchar **) search); pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_SEARCH_FILE: hy_query_filter_in (query, HY_PKG_FILE, HY_EQ, (const gchar **) search); pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_SEARCH_DETAILS: hy_query_filter_in (query, HY_PKG_DESCRIPTION, HY_SUBSTR, (const gchar **) search); pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_SEARCH_NAME: hy_query_filter_in (query, HY_PKG_NAME, HY_SUBSTR, (const gchar **) search); pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_WHAT_PROVIDES: hy_query_filter_provides_in (query, search); pkglist = hy_query_run (query); break; case PK_ROLE_ENUM_GET_UPDATES: job_data->goal = hy_goal_create (sack); hy_goal_upgrade_all (job_data->goal); ret = hif_goal_depsolve (job_data->goal, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } pkglist = hy_goal_list_upgrades (job_data->goal); break; default: g_assert_not_reached (); break; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* set the src on each package */ ret = hif_package_ensure_source_list (job_data->sources, pkglist, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* set the origin on each package */ pk_backend_ensure_origin_pkglist (job_data->db, pkglist); /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* FIXME: actually get the right update severity */ if (pk_backend_job_get_role (job) == PK_ROLE_ENUM_GET_UPDATES) { guint i; HyPackage pkg; HyUpdateSeverity severity; PkInfoEnum info_enum; FOR_PACKAGELIST(pkg, pkglist, i) { severity = hy_package_get_update_severity (pkg); info_enum = hif_update_severity_to_info_enum (severity); hif_package_set_info (pkg, info_enum); } } hif_emit_package_list_filter (job, filters, pkglist); /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: g_strfreev (search); if (pkglist != NULL) hy_packagelist_free (pkglist); if (query != NULL) hy_query_free (query); pk_backend_job_finished (job); } /** * pk_backend_get_packages: */ void pk_backend_get_packages (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_resolve: */ void pk_backend_resolve (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_search_names: */ void pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_search_details: */ void pk_backend_search_details (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_search_files: */ void pk_backend_search_files (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_what_provides: */ void pk_backend_what_provides (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_get_updates: */ void pk_backend_get_updates (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /** * pk_backend_source_filter: */ static gboolean pk_backend_source_filter (HifSource *src, PkBitfield filters) { /* devel and ~devel */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_DEVELOPMENT) && !hif_source_is_devel (src)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_DEVELOPMENT) && hif_source_is_devel (src)) return FALSE; /* source and ~source */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SOURCE) && !hif_source_is_source (src)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SOURCE) && hif_source_is_source (src)) return FALSE; /* installed and ~installed == enabled */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && !hif_source_get_enabled (src)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && hif_source_get_enabled (src)) return FALSE; /* supported and ~supported == core */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SUPPORTED) && !hif_source_is_supported (src)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SUPPORTED) && hif_source_is_supported (src)) return FALSE; /* not filtered */ return TRUE; } /** * pk_backend_get_repo_list_thread: */ static void pk_backend_get_repo_list_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gchar *description; GError *error = NULL; GPtrArray *sources; guint i; HifSource *src; PkBitfield filters; g_variant_get (params, "(t)", &filters); /* set the list of repos */ pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); sources = hif_repos_get_sources (priv->repos, &error); if (sources == NULL) { pk_backend_job_error_code (job, error->code, "failed to scan yum.repos.d: %s", error->message); g_error_free (error); goto out; } /* none? */ if (sources->len == 0) { pk_backend_job_error_code (job, PK_ERROR_ENUM_REPO_NOT_FOUND, "failed to find any repos"); goto out; } /* emit each repo */ for (i = 0; i < sources->len; i++) { src = g_ptr_array_index (sources, i); if (!pk_backend_source_filter (src, filters)) continue; description = hif_source_get_description (src); pk_backend_job_repo_detail (job, hif_source_get_id (src), description, hif_source_get_enabled (src)); g_free (description); } out: if (sources != NULL) g_ptr_array_unref (sources); pk_backend_job_finished (job); } /** * pk_backend_get_repo_list: */ void pk_backend_get_repo_list (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { pk_backend_job_thread_create (job, pk_backend_get_repo_list_thread, NULL, NULL); } /** * pk_backend_repo_set_data_thread: */ static void pk_backend_repo_set_data_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { const gchar *repo_id; const gchar *parameter; const gchar *value; gboolean ret = FALSE; GError *error = NULL; HifSource *src; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); g_variant_get (params, "(&s&s&s)", &repo_id, ¶meter, &value); /* take lock */ ret = hif_state_take_lock (job_data->state, HIF_LOCK_TYPE_REPO, HIF_LOCK_MODE_PROCESS, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "failed to get lock: %s", error->message); g_error_free (error); goto out; } /* set the list of repos */ pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* find the correct repo */ src = hif_repos_get_source_by_id (priv->repos, repo_id, &error); if (src == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } ret = hif_source_set_data (src, parameter, value, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "failed to write repo file: %s", error->message); g_error_free (error); goto out; } /* nothing found */ pk_backend_job_set_percentage (job, 100); out: hif_state_release_locks (job_data->state); pk_backend_job_finished (job); } /** * pk_backend_repo_set_data: */ void pk_backend_repo_set_data (PkBackend *backend, PkBackendJob *job, const gchar *repo_id, const gchar *parameter, const gchar *value) { pk_backend_job_thread_create (job, pk_backend_repo_set_data_thread, NULL, NULL); } /** * pk_backend_repo_enable: */ void pk_backend_repo_enable (PkBackend *backend, PkBackendJob *job, const gchar *repo_id, gboolean enabled) { pk_backend_repo_set_data (backend, job, repo_id, "enabled", enabled ? "1" : "0"); } /** * pk_backend_get_filters: */ PkBitfield pk_backend_get_filters (PkBackend *backend) { return pk_bitfield_from_enums ( PK_FILTER_ENUM_APPLICATION, PK_FILTER_ENUM_ARCH, PK_FILTER_ENUM_DEVELOPMENT, PK_FILTER_ENUM_GUI, PK_FILTER_ENUM_INSTALLED, PK_FILTER_ENUM_SOURCE, PK_FILTER_ENUM_DOWNLOADED, -1); } /** * pk_backend_get_mime_types: */ gchar ** pk_backend_get_mime_types (PkBackend *backend) { const gchar *mime_types[] = { "application/x-rpm", NULL }; return g_strdupv ((gchar **) mime_types); } /** * pk_backend_refresh_source: */ static gboolean pk_backend_refresh_source (PkBackendJob *job, HifSource *src, HifState *state, GError **error) { gboolean ret; gboolean src_okay; HifState *state_local; GError *error_local = NULL; /* set state */ ret = hif_state_set_steps (state, error, 50, /* check */ 50, /* download */ -1); if (!ret) goto out; /* is the source up to date? */ state_local = hif_state_get_child (state); src_okay = hif_source_check (src, pk_backend_job_get_cache_age (job), state_local, &error_local); if (!src_okay) { g_debug ("repo %s not okay [%s], refreshing", hif_source_get_id (src), error_local->message); g_clear_error (&error_local); } /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* update repo, TODO: if we have network access */ if (!src_okay) { state_local = hif_state_get_child (state); ret = hif_source_update (src, HIF_SOURCE_UPDATE_FLAG_NONE, state_local, &error_local); if (!ret) { if (g_error_matches (error_local, HIF_ERROR, PK_ERROR_ENUM_CANNOT_FETCH_SOURCES)) { g_warning ("Skipping refresh of %s: %s", hif_source_get_id (src), error_local->message); g_clear_error (&error_local); } else { g_propagate_error (error, error_local); goto out; } } } /* done */ ret = hif_state_done (state, error); if (!ret) goto out; out: return ret; } /** * pk_backend_refresh_cache_thread: */ static void pk_backend_refresh_cache_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; HifSource *src; HifState *state_local; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); gboolean force; gboolean ret; guint cnt = 0; guint i; g_variant_get (params, "(b)", &force); /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* count the enabled sources */ for (i = 0; i < job_data->sources->len; i++) { src = g_ptr_array_index (job_data->sources, i); if (!hif_source_get_enabled (src)) continue; if (hif_source_get_kind (src) == HIF_SOURCE_KIND_MEDIA) continue; cnt++; } /* refresh each repo */ hif_state_set_number_steps (job_data->state, cnt); for (i = 0; i < job_data->sources->len; i++) { src = g_ptr_array_index (job_data->sources, i); if (!hif_source_get_enabled (src)) continue; if (hif_source_get_kind (src) == HIF_SOURCE_KIND_MEDIA) continue; /* delete content even if up to date */ if (force) { g_debug ("Deleting contents of %s as forced", hif_source_get_id (src)); ret = hif_source_clean (src, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } } /* check and download */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_refresh_source (job, src, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } } out: pk_backend_job_finished (job); } /** * pk_backend_refresh_cache: */ void pk_backend_refresh_cache (PkBackend *backend, PkBackendJob *job, gboolean force) { pk_backend_job_thread_create (job, pk_backend_refresh_cache_thread, NULL, NULL); } /** * hif_utils_find_package_ids: * * Returns a hash table of all the packages found in the sack. * If a specific package-id is not found then the method does not fail, but * no package will be inserted into the hash table. * * If multiple packages are found, an error is returned, as the package-id is * supposed to uniquely identify the package across all repos. */ static GHashTable * hif_utils_find_package_ids (HySack sack, gchar **package_ids, GError **error) { const gchar *reponame; gboolean ret = TRUE; gchar **split; GHashTable *hash; guint i; HyPackageList pkglist = NULL; HyPackage pkg; HyQuery query = NULL; /* run query */ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) hy_package_free); query = hy_query_create (sack); for (i = 0; package_ids[i] != NULL; i++) { hy_query_clear (query); split = pk_package_id_split (package_ids[i]); reponame = split[PK_PACKAGE_ID_DATA]; if (g_strcmp0 (reponame, "installed") == 0 || g_str_has_prefix (reponame, "installed:")) reponame = HY_SYSTEM_REPO_NAME; else if (g_strcmp0 (reponame, "local") == 0) reponame = HY_CMDLINE_REPO_NAME; hy_query_filter (query, HY_PKG_NAME, HY_EQ, split[PK_PACKAGE_ID_NAME]); hy_query_filter (query, HY_PKG_EVR, HY_EQ, split[PK_PACKAGE_ID_VERSION]); hy_query_filter (query, HY_PKG_ARCH, HY_EQ, split[PK_PACKAGE_ID_ARCH]); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, reponame); pkglist = hy_query_run (query); g_strfreev (split); /* no matches */ if (hy_packagelist_count (pkglist) == 0) { hy_packagelist_free (pkglist); continue; } /* multiple matches */ if (hy_packagelist_count (pkglist) > 1) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_PACKAGE_CONFLICTS, "Multiple matches of %s", package_ids[i]); FOR_PACKAGELIST(pkg, pkglist, i) { g_debug ("possible matches: %s", hif_package_get_id (pkg)); } goto out; } /* add to results */ pkg = hy_packagelist_get (pkglist, 0); g_hash_table_insert (hash, g_strdup (package_ids[i]), hy_package_link (pkg)); hy_packagelist_free (pkglist); } out: if (!ret && hash != NULL) { g_hash_table_unref (hash); hash = NULL; } if (query != NULL) hy_query_free (query); return hash; } /** * backend_get_details_thread: */ static void backend_get_details_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **package_ids; GError *error = NULL; GHashTable *hash = NULL; guint i; HifState *state_local; HyPackage pkg; HySack sack; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_variant_get (params, "(^a&s)", &package_ids); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 50, /* add repos */ 49, /* find packages */ 1, /* emit */ -1); /* get sack */ filters = hif_get_filter_for_ids (package_ids); g_assert (ret); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* emit details */ for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) continue; pk_backend_job_details (job, package_ids[i], hy_package_get_summary (pkg), hy_package_get_license (pkg), PK_GROUP_ENUM_UNKNOWN, hif_package_get_description (pkg), hy_package_get_url (pkg), (gulong) hy_package_get_size (pkg)); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_get_details: */ void pk_backend_get_details (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { pk_backend_job_thread_create (job, backend_get_details_thread, NULL, NULL); } /** * backend_get_details_local_thread: */ static void backend_get_details_local_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **full_paths; GError *error = NULL; guint i; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_variant_get (params, "(^a&s)", &full_paths); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 50, /* create sack */ 50, /* get details */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* ensure packages are not already installed */ for (i = 0; full_paths[i] != NULL; i++) { pkg = hy_sack_add_cmdline_package (sack, full_paths[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_FILE_NOT_FOUND, "Failed to open %s", full_paths[i]); goto out; } pk_backend_job_details (job, hif_package_get_id (pkg), hy_package_get_summary (pkg), hy_package_get_license (pkg), PK_GROUP_ENUM_UNKNOWN, hif_package_get_description (pkg), hy_package_get_url (pkg), (gulong) hy_package_get_size (pkg)); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: pk_backend_job_finished (job); } /** * pk_backend_get_details_local: */ void pk_backend_get_details_local (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { pk_backend_job_thread_create (job, backend_get_details_local_thread, NULL, NULL); } /** * backend_get_files_local_thread: */ static void backend_get_files_local_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **full_paths; GError *error = NULL; guint i; HifState *state_local; HyPackage pkg; HySack sack = NULL; HyStringArray files_array; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_variant_get (params, "(^a&s)", &full_paths); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 50, /* create sack */ 50, /* get details */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* ensure packages are not already installed */ for (i = 0; full_paths[i] != NULL; i++) { pkg = hy_sack_add_cmdline_package (sack, full_paths[i]); g_warning ("full_paths[i]=%s", full_paths[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_FILE_NOT_FOUND, "Failed to open %s", full_paths[i]); goto out; } /* sort and list according to name */ files_array = hy_package_get_files (pkg); pk_backend_job_files (job, hif_package_get_id (pkg), (gchar **) files_array); hy_stringarray_free (files_array); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: pk_backend_job_finished (job); } /** * pk_backend_get_files_local: */ void pk_backend_get_files_local (PkBackend *backend, PkBackendJob *job, gchar **files) { pk_backend_job_thread_create (job, backend_get_files_local_thread, NULL, NULL); } /** * pk_backend_download_packages_thread: */ static void pk_backend_download_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { const gchar *directory; gboolean ret; gchar **package_ids; gchar *tmp; GError *error = NULL; GHashTable *hash = NULL; GPtrArray *files = NULL; guint i; HifSource *src; HifState *state_local; HifState *state_loop; HyPackage pkg; HySack sack; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); g_variant_get (params, "(^a&ss)", &package_ids, &directory); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 1, /* ensure repos */ 3, /* get sack */ 5, /* find packages */ 90, /* download packages */ 1, /* emit */ -1); g_assert (ret); /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* get sack */ state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* download packages */ files = g_ptr_array_new_with_free_func (g_free); state_local = hif_state_get_child (job_data->state); hif_state_set_number_steps (state_local, g_strv_length (package_ids)); for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find %s", package_ids[i]); goto out; } hif_emit_package (job, PK_INFO_ENUM_DOWNLOADING, pkg); /* get correct package source */ src = hif_repos_get_source_by_id (priv->repos, hy_package_get_reponame (pkg), &error); if (src == NULL) { g_prefix_error (&error, "Not sure where to download %s: ", hy_package_get_name (pkg)); pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* download */ state_loop = hif_state_get_child (state_local); tmp = hif_source_download_package (src, pkg, directory, state_loop, &error); if (tmp == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* add to download list */ g_ptr_array_add (files, tmp); /* done */ ret = hif_state_done (state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } } g_ptr_array_add (files, NULL); /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* emit files so that the daemon will copy these */ pk_backend_job_files (job, NULL, (gchar **) files->pdata); /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (files != NULL) g_ptr_array_unref (files); if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_download_packages: */ void pk_backend_download_packages (PkBackend *backend, PkBackendJob *job, gchar **package_ids, const gchar *directory) { pk_backend_job_thread_create (job, pk_backend_download_packages_thread, NULL, NULL); } /** * pk_backend_cancel: */ void pk_backend_cancel (PkBackend *backend, PkBackendJob *job) { PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); g_cancellable_cancel (job_data->cancellable); } /** * pk_backend_transaction_check_untrusted_repos: */ static GPtrArray * pk_backend_transaction_check_untrusted_repos (GPtrArray *sources, HyGoal goal, GError **error) { gboolean ret = TRUE; GPtrArray *array = NULL; GPtrArray *install; guint i; HifSource *src; HyPackage pkg; PkBitfield types; /* find a list of all the packages we might have to download */ types = pk_bitfield_from_enums (PK_INFO_ENUM_INSTALLING, PK_INFO_ENUM_REINSTALLING, PK_INFO_ENUM_DOWNGRADING, PK_INFO_ENUM_UPDATING, -1); /* find any packages in untrusted repos */ install = hif_goal_get_packages (goal, types); array = g_ptr_array_new (); for (i = 0; i < install->len; i++) { pkg = g_ptr_array_index (install, i); /* this is a standalone file, so by definition is from an * untrusted repo */ if (g_strcmp0 (hy_package_get_reponame (pkg), HY_CMDLINE_REPO_NAME) == 0) { g_ptr_array_add (array, pkg); continue; } /* find repo */ src = hif_repos_get_source_by_id (priv->repos, hy_package_get_reponame (pkg), error); if (src == NULL) { g_prefix_error (error, "Can't GPG check %s: ", hy_package_get_name (pkg)); ret = FALSE; goto out; } /* repo has no gpg key */ if (!hif_source_get_gpgcheck (src)) g_ptr_array_add (array, pkg); } out: if (array != NULL && !ret) { g_ptr_array_unref (array); array = NULL; } g_ptr_array_unref (install); return array; } /** * pk_backend_transaction_check_untrusted: */ static gboolean pk_backend_transaction_check_untrusted (rpmKeyring keyring, GPtrArray *sources, HyGoal goal, GError **error) { const gchar *filename; gboolean ret = TRUE; GPtrArray *install; guint i; HyPackage pkg; PkBitfield types; /* find a list of all the packages we might have to download */ types = pk_bitfield_from_enums (PK_INFO_ENUM_INSTALLING, PK_INFO_ENUM_REINSTALLING, PK_INFO_ENUM_DOWNGRADING, PK_INFO_ENUM_UPDATING, -1); install = hif_goal_get_packages (goal, types); if (install->len == 0) goto out; /* find any packages in untrusted repos */ for (i = 0; i < install->len; i++) { pkg = g_ptr_array_index (install, i); /* ensure the filename is set */ ret = hif_package_ensure_source (sources, pkg, error); if (!ret) { g_prefix_error (error, "Failed to check untrusted: "); goto out; } /* find the location of the local file */ filename = hif_package_get_filename (pkg); if (filename == NULL) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_FILE_NOT_FOUND, "Downloaded file for %s not found", hy_package_get_name (pkg)); goto out; } /* check file */ ret = hif_keyring_check_untrusted_file (keyring, filename, error); if (!ret) goto out; } out: g_ptr_array_unref (install); return ret; } typedef enum { HIF_TRANSACTION_STEP_STARTED, HIF_TRANSACTION_STEP_PREPARING, HIF_TRANSACTION_STEP_WRITING, HIF_TRANSACTION_STEP_IGNORE } HifTransactionStep; typedef struct { HifState *state; HifState *child; FD_t fd; HifTransactionStep step; GTimer *timer; guint last_progress; GPtrArray *remove; GPtrArray *remove_helper; GPtrArray *install; } HifTransactionCommit; /** * hif_commit_rpmcb_type_to_string: **/ static const gchar * hif_commit_rpmcb_type_to_string (const rpmCallbackType what) { const gchar *type = NULL; switch (what) { case RPMCALLBACK_UNKNOWN: type = "unknown"; break; case RPMCALLBACK_INST_PROGRESS: type = "install-progress"; break; case RPMCALLBACK_INST_START: type = "install-start"; break; case RPMCALLBACK_INST_OPEN_FILE: type = "install-open-file"; break; case RPMCALLBACK_INST_CLOSE_FILE: type = "install-close-file"; break; case RPMCALLBACK_TRANS_PROGRESS: type = "transaction-progress"; break; case RPMCALLBACK_TRANS_START: type = "transaction-start"; break; case RPMCALLBACK_TRANS_STOP: type = "transaction-stop"; break; case RPMCALLBACK_UNINST_PROGRESS: type = "uninstall-progress"; break; case RPMCALLBACK_UNINST_START: type = "uninstall-start"; break; case RPMCALLBACK_UNINST_STOP: type = "uninstall-stop"; break; case RPMCALLBACK_REPACKAGE_PROGRESS: type = "repackage-progress"; break; case RPMCALLBACK_REPACKAGE_START: type = "repackage-start"; break; case RPMCALLBACK_REPACKAGE_STOP: type = "repackage-stop"; break; case RPMCALLBACK_UNPACK_ERROR: type = "unpack-error"; break; case RPMCALLBACK_CPIO_ERROR: type = "cpio-error"; break; case RPMCALLBACK_SCRIPT_ERROR: type = "script-error"; break; case RPMCALLBACK_SCRIPT_START: type = "script-start"; break; case RPMCALLBACK_SCRIPT_STOP: type = "script-stop"; break; case RPMCALLBACK_INST_STOP: type = "install-stop"; break; } return type; } /** * hif_find_pkg_from_header: **/ static HyPackage hif_find_pkg_from_header (GPtrArray *array, Header hdr) { const gchar *arch; const gchar *name; const gchar *release; const gchar *version; guint epoch; guint i; HyPackage pkg; /* get details */ name = headerGetString (hdr, RPMTAG_NAME); epoch = headerGetNumber (hdr, RPMTAG_EPOCH); version = headerGetString (hdr, RPMTAG_VERSION); release = headerGetString (hdr, RPMTAG_RELEASE); arch = headerGetString (hdr, RPMTAG_ARCH); /* find in array */ for (i = 0; i < array->len; i++) { pkg = g_ptr_array_index (array, i); if (g_strcmp0 (name, hy_package_get_name (pkg)) != 0) continue; if (g_strcmp0 (version, hy_package_get_version (pkg)) != 0) continue; if (g_strcmp0 (release, hy_package_get_release (pkg)) != 0) continue; if (g_strcmp0 (arch, hy_package_get_arch (pkg)) != 0) continue; if (epoch != hy_package_get_epoch (pkg)) continue; return pkg; } return NULL; } /** * hif_find_pkg_from_filename_suffix: **/ static HyPackage hif_find_pkg_from_filename_suffix (GPtrArray *array, const gchar *filename_suffix) { const gchar *filename; guint i; HyPackage pkg; /* find in array */ for (i = 0; i < array->len; i++) { pkg = g_ptr_array_index (array, i); filename = hif_package_get_filename (pkg); if (filename == NULL) continue; if (g_str_has_suffix (filename, filename_suffix)) return pkg; } return NULL; } /** * hif_find_pkg_from_name: **/ static HyPackage hif_find_pkg_from_name (GPtrArray *array, const gchar *pkgname) { guint i; HyPackage pkg; /* find in array */ for (i = 0; i < array->len; i++) { pkg = g_ptr_array_index (array, i); if (g_strcmp0 (hy_package_get_name (pkg), pkgname) == 0) return pkg; } return NULL; } /** * hif_commit_ts_progress_cb: **/ static void * hif_commit_ts_progress_cb (const void *arg, const rpmCallbackType what, const rpm_loff_t amount, const rpm_loff_t total, fnpyKey key, void *data) { const char *filename = (const char *) key; const gchar *name = NULL; gboolean ret; GError *error_local = NULL; guint percentage; guint speed; Header hdr = (Header) arg; HyPackage pkg; PkStatusEnum action; void *rc = NULL; HifTransactionCommit *commit = (HifTransactionCommit *) data; if (hdr != NULL) name = headerGetString (hdr, RPMTAG_NAME); g_debug ("phase: %s (%i/%i, %s/%s)", hif_commit_rpmcb_type_to_string (what), (gint32) amount, (gint32) total, (const gchar *) key, name); switch (what) { case RPMCALLBACK_INST_OPEN_FILE: /* valid? */ if (filename == NULL || filename[0] == '\0') return NULL; /* open the file and return file descriptor */ commit->fd = Fopen (filename, "r.ufdio"); return (void *) commit->fd; break; case RPMCALLBACK_INST_CLOSE_FILE: /* just close the file */ if (commit->fd != NULL) { Fclose (commit->fd); commit->fd = NULL; } break; case RPMCALLBACK_INST_START: /* find pkg */ pkg = hif_find_pkg_from_filename_suffix (commit->install, filename); if (pkg == NULL) g_assert_not_reached (); /* map to correct action code */ action = hif_package_get_status (pkg); if (action == PK_STATUS_ENUM_UNKNOWN) action = PK_STATUS_ENUM_INSTALL; /* install start */ commit->step = HIF_TRANSACTION_STEP_WRITING; commit->child = hif_state_get_child (commit->state); hif_state_action_start (commit->child, action, hif_package_get_id (pkg)); g_debug ("install start: %s size=%i", filename, (gint32) total); break; case RPMCALLBACK_UNINST_START: /* invalid? */ if (filename == NULL) { g_debug ("no filename set in uninst-start with total %i", (gint32) total); commit->step = HIF_TRANSACTION_STEP_WRITING; break; } /* find pkg */ pkg = hif_find_pkg_from_name (commit->remove, name); if (pkg != NULL) pkg = hif_find_pkg_from_name (commit->remove_helper, name); if (pkg == NULL) { pkg = hif_find_pkg_from_filename_suffix (commit->remove, filename); } if (pkg == NULL) { g_debug ("cannot find %s", filename); break; } /* map to correct action code */ action = hif_package_get_status (pkg); if (action == PK_STATUS_ENUM_UNKNOWN) action = PK_STATUS_ENUM_REMOVE; /* remove start */ commit->step = HIF_TRANSACTION_STEP_WRITING; commit->child = hif_state_get_child (commit->state); hif_state_action_start (commit->child, action, hif_package_get_id (pkg)); g_debug ("remove start: %s size=%i", filename, (gint32) total); break; case RPMCALLBACK_TRANS_PROGRESS: case RPMCALLBACK_INST_PROGRESS: /* we're preparing the transaction */ if (commit->step == HIF_TRANSACTION_STEP_PREPARING || commit->step == HIF_TRANSACTION_STEP_IGNORE) { g_debug ("ignoring preparing %i / %i", (gint32) amount, (gint32) total); break; } /* work out speed */ speed = (amount - commit->last_progress) / g_timer_elapsed (commit->timer, NULL); hif_state_set_speed (commit->state, speed); commit->last_progress = amount; g_timer_reset (commit->timer); /* progress */ percentage = (100.0f / (gfloat) total) * (gfloat) amount; if (commit->child != NULL) hif_state_set_percentage (commit->child, percentage); /* update UI */ pkg = hif_find_pkg_from_header (commit->install, hdr); if (pkg == NULL) { pkg = hif_find_pkg_from_filename_suffix (commit->install, filename); } if (pkg == NULL) { g_debug ("cannot find %s (%s)", filename, name); break; } hif_state_set_package_progress (commit->state, hif_package_get_id (pkg), PK_STATUS_ENUM_INSTALL, percentage); break; case RPMCALLBACK_UNINST_PROGRESS: /* we're preparing the transaction */ if (commit->step == HIF_TRANSACTION_STEP_PREPARING || commit->step == HIF_TRANSACTION_STEP_IGNORE) { g_debug ("ignoring preparing %i / %i", (gint32) amount, (gint32) total); break; } /* progress */ percentage = (100.0f / (gfloat) total) * (gfloat) amount; if (commit->child != NULL) hif_state_set_percentage (commit->child, percentage); /* update UI */ pkg = hif_find_pkg_from_header (commit->remove, hdr); if (pkg == NULL && filename != NULL) { pkg = hif_find_pkg_from_filename_suffix (commit->remove, filename); } if (pkg == NULL && name != NULL) pkg = hif_find_pkg_from_name (commit->remove, name); if (pkg == NULL && name != NULL) pkg = hif_find_pkg_from_name (commit->remove_helper, name); if (pkg == NULL) { g_warning ("cannot find %s", name); break; } hif_state_set_package_progress (commit->state, hif_package_get_id (pkg), PK_STATUS_ENUM_REMOVE, percentage); break; case RPMCALLBACK_TRANS_START: /* we setup the state */ g_debug ("preparing transaction with %i items", (gint32) total); if (commit->step == HIF_TRANSACTION_STEP_IGNORE) break; hif_state_set_number_steps (commit->state, total); commit->step = HIF_TRANSACTION_STEP_PREPARING; break; case RPMCALLBACK_TRANS_STOP: /* don't do anything */ break; case RPMCALLBACK_INST_STOP: case RPMCALLBACK_UNINST_STOP: /* phase complete */ ret = hif_state_done (commit->state, &error_local); if (!ret) { g_warning ("state increment failed: %s", error_local->message); g_error_free (error_local); } break; case RPMCALLBACK_UNPACK_ERROR: case RPMCALLBACK_CPIO_ERROR: case RPMCALLBACK_SCRIPT_ERROR: case RPMCALLBACK_SCRIPT_START: case RPMCALLBACK_SCRIPT_STOP: case RPMCALLBACK_UNKNOWN: case RPMCALLBACK_REPACKAGE_PROGRESS: case RPMCALLBACK_REPACKAGE_START: case RPMCALLBACK_REPACKAGE_STOP: g_debug ("%s uninteresting", hif_commit_rpmcb_type_to_string (what)); break; default: g_warning ("unknown transaction phase: %u (%s)", what, hif_commit_rpmcb_type_to_string (what)); break; } return rc; } /** * hif_rpm_verbosity_string_to_value: **/ static gint hif_rpm_verbosity_string_to_value (const gchar *value) { if (g_strcmp0 (value, "critical") == 0) return RPMLOG_CRIT; if (g_strcmp0 (value, "emergency") == 0) return RPMLOG_EMERG; if (g_strcmp0 (value, "error") == 0) return RPMLOG_ERR; if (g_strcmp0 (value, "warn") == 0) return RPMLOG_WARNING; if (g_strcmp0 (value, "debug") == 0) return RPMLOG_DEBUG; if (g_strcmp0 (value, "info") == 0) return RPMLOG_INFO; return RPMLOG_EMERG; } /** * hif_transaction_delete_packages: **/ static gboolean hif_transaction_delete_packages (GPtrArray *install, HifState *state, GError **error) { const gchar *filename; const gchar *cachedir; GFile *file; guint i; guint ret = TRUE; HifState *state_local; HyPackage pkg; /* nothing to delete? */ if (install->len == 0) goto out; /* get the cachedir so we only delete packages in the actual * cache, not local-install packages */ cachedir = hif_context_get_cache_dir (priv->context); if (cachedir == NULL) { ret = FALSE; g_set_error_literal (error, HIF_ERROR, PK_ERROR_ENUM_FAILED_CONFIG_PARSING, "Failed to get value for CacheDir"); goto out; } /* delete each downloaded file */ state_local = hif_state_get_child (state); hif_state_set_number_steps (state_local, install->len); for (i = 0; i < install->len; i++) { pkg = g_ptr_array_index (install, i); /* don't delete files not in the repo */ filename = hif_package_get_filename (pkg); if (g_str_has_prefix (filename, cachedir)) { file = g_file_new_for_path (filename); ret = g_file_delete (file, NULL, error); g_object_unref (file); if (!ret) goto out; } /* done */ ret = hif_state_done (state_local, error); if (!ret) goto out; } out: return ret; } /** * pk_hy_convert_to_system_repo: **/ static HyPackage pk_hy_convert_to_system_repo (PkBackendJob *job, HyPackage pkg, HifState *state, GError **error) { HyPackageList pkglist = NULL; HyPackage pkg_installed = NULL; HyQuery query = NULL; HySack sack = NULL; /* get local packages */ sack = hif_utils_create_sack_for_filters (job, pk_bitfield_value (PK_FILTER_ENUM_INSTALLED), HIF_CREATE_SACK_FLAG_USE_CACHE, state, error); if (sack == NULL) goto out; /* find exact package */ query = hy_query_create (sack); hy_query_filter (query, HY_PKG_NAME, HY_EQ, hy_package_get_name (pkg)); hy_query_filter (query, HY_PKG_EVR, HY_EQ, hy_package_get_evr (pkg)); hy_query_filter (query, HY_PKG_ARCH, HY_EQ, hy_package_get_arch (pkg)); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); pkglist = hy_query_run (query); if (hy_packagelist_count (pkglist) != 1) { g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find installed version of %s [%i]", hy_package_get_name (pkg), hy_packagelist_count (pkglist)); goto out; } /* success */ pkg_installed = hy_package_link (hy_packagelist_get (pkglist, 0)); out: if (query != NULL) hy_query_free (query); if (pkglist != NULL) hy_packagelist_free (pkglist); return pkg_installed; } /** * hif_transaction_write_yumdb_install_item: **/ static gboolean hif_transaction_write_yumdb_install_item (PkBackendJob *job, HifTransactionCommit *commit, HyPackage pkg, HifState *state, GError **error) { const gchar *reason; gboolean ret; gchar *tmp; HifState *state_local; HyPackage pkg_installed = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); /* set steps */ hif_state_set_number_steps (state, 5); /* need to find the HyPackage in the rpmdb, not the remote one that we * just installed */ state_local = hif_state_get_child (state); pkg_installed = pk_hy_convert_to_system_repo (job, pkg, state_local, error); if (pkg_installed == NULL) { ret = FALSE; goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* set the repo this came from */ ret = hif_db_set_string (job_data->db, pkg_installed, "from_repo", hy_package_get_reponame (pkg), error); if (!ret) goto out; /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* write euid */ tmp = g_strdup_printf ("%i", pk_backend_job_get_uid (job)); ret = hif_db_set_string (job_data->db, pkg_installed, "installed_by", tmp, error); g_free (tmp); if (!ret) goto out; /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* set the correct reason */ if (hif_package_get_user_action (pkg)) { reason = "user"; } else { reason = "dep"; } ret = hif_db_set_string (job_data->db, pkg_installed, "reason", reason, error); if (!ret) goto out; /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* set the correct release */ ret = hif_db_set_string (job_data->db, pkg_installed, "releasever", hif_context_get_release_ver (priv->context), error); if (!ret) goto out; /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; out: if (pkg_installed != NULL) hy_package_free (pkg_installed); return ret; } /** * _hif_state_get_step_multiple_pair: * * 3,3 = 50 * 3,0 = 99 (we can't use 100 as an index value) * 0,3 = 1 (we can't use 0 as an index value) **/ static guint _hif_state_get_step_multiple_pair (guint first, guint second) { return 1 + (first * 98.0 / (first + second)); } /** * hif_transaction_write_yumdb: **/ static gboolean hif_transaction_write_yumdb (PkBackendJob *job, HifTransactionCommit *commit, HifState *state, GError **error) { HifState *state_local; HifState *state_loop; HyPackage pkg; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); gboolean ret; guint i; guint steps_auto; steps_auto = _hif_state_get_step_multiple_pair (commit->remove->len, commit->install->len); ret = hif_state_set_steps (state, error, steps_auto, /* remove */ 100 - steps_auto, /* install */ -1); if (!ret) goto out; /* remove all the old entries */ state_local = hif_state_get_child (state); if (commit->remove->len > 0) hif_state_set_number_steps (state_local, commit->remove->len); for (i = 0; i < commit->remove->len; i++) { pkg = g_ptr_array_index (commit->remove, i); ret = hif_package_ensure_source (job_data->sources, pkg, error); if (!ret) goto out; ret = hif_db_remove_all (job_data->db, pkg, error); if (!ret) goto out; ret = hif_state_done (state_local, error); if (!ret) goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* add all the new entries */ if (commit->install->len > 0) hif_state_set_number_steps (state_local, commit->install->len); for (i = 0; i < commit->install->len; i++) { pkg = g_ptr_array_index (commit->install, i); state_loop = hif_state_get_child (state_local); ret = hif_transaction_write_yumdb_install_item (job, commit, pkg, state_loop, error); if (!ret) goto out; ret = hif_state_done (state_local, error); if (!ret) goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; out: return ret; } /** * pk_backend_transaction_commit: */ static gboolean pk_backend_transaction_commit (PkBackendJob *job, HifState *state, GError **error) { const gchar *filename; const gchar *tmp; gboolean allow_untrusted; gboolean is_update; gboolean ret = FALSE; gint rc; gint verbosity; gint vs_flags; guint i; guint j; HifState *state_local; HifTransactionCommit *commit = NULL; HyPackageList pkglist; HyPackage pkg; HyPackage pkg_tmp; PkBitfield selector; rpmprobFilterFlags problems_filter = 0; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); /* take lock */ ret = hif_state_take_lock (state, HIF_LOCK_TYPE_RPMDB, HIF_LOCK_MODE_PROCESS, error); if (!ret) goto out; /* set state */ ret = hif_state_set_steps (state, error, 2, /* install */ 2, /* remove */ 10, /* test-commit */ 83, /* commit */ 1, /* write yumDB */ 2, /* delete files */ -1); if (!ret) goto out; /* import all GPG keys */ ret = hif_keyring_add_public_keys (job_data->keyring, error); if (!ret) goto out; /* find any packages without valid GPG signatures */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED)) { ret = pk_backend_transaction_check_untrusted (job_data->keyring, job_data->sources, job_data->goal, error); if (!ret) goto out; } hif_state_action_start (state, PK_STATUS_ENUM_REQUEST, NULL); /* get verbosity from the config file */ tmp = hif_context_get_rpm_verbosity (priv->context); verbosity = hif_rpm_verbosity_string_to_value (tmp); rpmSetVerbosity (verbosity); /* setup the transaction */ commit = g_new0 (HifTransactionCommit, 1); commit->timer = g_timer_new (); tmp = hif_context_get_install_root (priv->context); rc = rpmtsSetRootDir (job_data->ts, tmp); if (rc < 0) { ret = FALSE; g_set_error_literal (error, HIF_ERROR, PK_ERROR_ENUM_INTERNAL_ERROR, "failed to set root"); goto out; } rpmtsSetNotifyCallback (job_data->ts, hif_commit_ts_progress_cb, commit); /* add things to install */ state_local = hif_state_get_child (state); selector = pk_bitfield_from_enums (PK_INFO_ENUM_INSTALLING, PK_INFO_ENUM_REINSTALLING, PK_INFO_ENUM_DOWNGRADING, PK_INFO_ENUM_UPDATING, -1); commit->install = hif_goal_get_packages (job_data->goal, selector); if (commit->install->len > 0) hif_state_set_number_steps (state_local, commit->install->len); for (i = 0; i < commit->install->len; i++) { pkg = g_ptr_array_index (commit->install, i); ret = hif_package_ensure_source (job_data->sources, pkg, error); if (!ret) goto out; /* add the install */ filename = hif_package_get_filename (pkg); allow_untrusted = !pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED); is_update = hif_package_get_status (pkg) == PK_STATUS_ENUM_UPDATE; ret = hif_rpmts_add_install_filename (job_data->ts, filename, allow_untrusted, is_update, error); if (!ret) goto out; /* this section done */ ret = hif_state_done (state_local, error); if (!ret) goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* add things to remove */ selector = pk_bitfield_from_enums (PK_INFO_ENUM_REMOVING, -1); commit->remove = hif_goal_get_packages (job_data->goal, selector); for (i = 0; i < commit->remove->len; i++) { pkg = g_ptr_array_index (commit->remove, i); ret = hif_rpmts_add_remove_pkg (job_data->ts, pkg, error); if (!ret) goto out; /* pre-get the pkgid, as this isn't possible to get after * the sack is invalidated */ if (hif_package_get_pkgid (pkg) == NULL) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_INTERNAL_ERROR, "failed to pre-get pkgid for %s", hif_package_get_id (pkg)); goto out; } /* are the things being removed actually being upgraded */ pkg_tmp = hif_find_pkg_from_name (commit->install, hy_package_get_name (pkg)); if (pkg_tmp != NULL) hif_package_set_status (pkg, PK_STATUS_ENUM_CLEANUP); } /* add anything that gets obsoleted to a helper array which is used to * map removed packages auto-added by rpm to actual HyPackage's */ commit->remove_helper = g_ptr_array_new (); for (i = 0; i < commit->install->len; i++) { pkg = g_ptr_array_index (commit->install, i); is_update = hif_package_get_status (pkg) == PK_STATUS_ENUM_UPDATE; if (!is_update) continue; pkglist = hy_goal_list_obsoleted_by_package (job_data->goal, pkg); FOR_PACKAGELIST(pkg_tmp, pkglist, j) { g_ptr_array_add (commit->remove_helper, pkg); hif_package_set_status (pkg, PK_STATUS_ENUM_CLEANUP); } hy_packagelist_free (pkglist); } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* generate ordering for the transaction */ rpmtsOrder (job_data->ts); /* run the test transaction */ if (hif_context_get_check_transaction (priv->context)) { g_debug ("running test transaction"); pk_backend_job_set_status (job, PK_STATUS_ENUM_TEST_COMMIT); commit->state = hif_state_get_child (state); commit->step = HIF_TRANSACTION_STEP_IGNORE; /* the output value of rpmtsCheck is not meaningful */ rpmtsCheck (job_data->ts); ret = hif_rpmts_look_for_problems (job_data->ts, error); if (!ret) goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* no signature checking, we've handled that already */ vs_flags = rpmtsSetVSFlags (job_data->ts, _RPMVSF_NOSIGNATURES | _RPMVSF_NODIGESTS); rpmtsSetVSFlags (job_data->ts, vs_flags); /* filter diskspace */ if (!hif_context_get_check_disk_space (priv->context)) problems_filter += RPMPROB_FILTER_DISKSPACE; /* run the transaction */ commit->state = hif_state_get_child (state); commit->step = HIF_TRANSACTION_STEP_STARTED; rpmtsSetFlags (job_data->ts, RPMTRANS_FLAG_NONE); g_debug ("Running actual transaction"); rc = rpmtsRun (job_data->ts, NULL, problems_filter); if (rc < 0) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_INTERNAL_ERROR, "Error %i running transaction", rc); goto out; } if (rc > 0) { ret = hif_rpmts_look_for_problems (job_data->ts, error); if (!ret) goto out; } /* hmm, nothing was done... */ if (commit->step != HIF_TRANSACTION_STEP_WRITING) { ret = FALSE; g_set_error (error, HIF_ERROR, PK_ERROR_ENUM_INTERNAL_ERROR, "Transaction did not go to writing phase, " "but returned no error (%i)", commit->step); goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* all sacks are invalid now */ pk_backend_sack_cache_invalidate ("transaction performed"); /* write to the yumDB */ state_local = hif_state_get_child (state); ret = hif_transaction_write_yumdb (job, commit, state_local, error); if (!ret) goto out; /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; /* remove the files we downloaded */ if (!hif_context_get_keep_cache (priv->context)) { state_local = hif_state_get_child (state); ret = hif_transaction_delete_packages (commit->install, state_local, error); if (!ret) goto out; } /* this section done */ ret = hif_state_done (state, error); if (!ret) goto out; out: hif_state_release_locks (state); if (commit != NULL) { g_timer_destroy (commit->timer); if (commit->install != NULL) g_ptr_array_unref (commit->install); if (commit->remove != NULL) g_ptr_array_unref (commit->remove); if (commit->remove_helper != NULL) g_ptr_array_unref (commit->remove_helper); g_free (commit); } return ret; } /** * pk_backend_transaction_simulate: */ static gboolean pk_backend_transaction_simulate (PkBackendJob *job, HifState *state, GError **error) { GPtrArray *untrusted = NULL; HyPackageList pkglist; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); gboolean ret; /* set state */ ret = hif_state_set_steps (state, error, 99, /* check for untrusted repos */ 1, /* emit */ -1); if (!ret) goto out; /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, error); if (!ret) goto out; /* mark any explicitly-untrusted packages so that the transaction skips * straight to only_trusted=FALSE after simulate */ untrusted = pk_backend_transaction_check_untrusted_repos (job_data->sources, job_data->goal, error); if (untrusted == NULL) { ret = FALSE; goto out; } /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* emit what we're going to do */ hif_emit_package_array (job, PK_INFO_ENUM_UNTRUSTED, untrusted); pkglist = hy_goal_list_erasures (job_data->goal); pk_backend_ensure_origin_pkglist (job_data->db, pkglist); hif_emit_package_list (job, PK_INFO_ENUM_REMOVING, pkglist); pkglist = hy_goal_list_installs (job_data->goal); pk_backend_ensure_origin_pkglist (job_data->db, pkglist); hif_emit_package_list (job, PK_INFO_ENUM_INSTALLING, pkglist); pkglist = hy_goal_list_obsoleted (job_data->goal); hif_emit_package_list (job, PK_INFO_ENUM_OBSOLETING, pkglist); pkglist = hy_goal_list_reinstalls (job_data->goal); pk_backend_ensure_origin_pkglist (job_data->db, pkglist); hif_emit_package_list (job, PK_INFO_ENUM_REINSTALLING, pkglist); pkglist = hy_goal_list_upgrades (job_data->goal); pk_backend_ensure_origin_pkglist (job_data->db, pkglist); hif_emit_package_list (job, PK_INFO_ENUM_UPDATING, pkglist); pkglist = hy_goal_list_downgrades (job_data->goal); pk_backend_ensure_origin_pkglist (job_data->db, pkglist); hif_emit_package_list (job, PK_INFO_ENUM_DOWNGRADING, pkglist); /* done */ ret = hif_state_done (state, error); if (!ret) goto out; out: if (untrusted != NULL) g_ptr_array_unref (untrusted); return ret; } /** * pk_backend_transaction_download_commit: */ static gboolean pk_backend_transaction_download_commit (PkBackendJob *job, HifState *state, GError **error) { gboolean ret = TRUE; HifState *state_local; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); /* nothing to download */ if (job_data->packages_to_download->len == 0) return pk_backend_transaction_commit (job, state, error); /* set state */ ret = hif_state_set_steps (state, error, 50, /* download */ 50, /* install/remove */ -1); if (!ret) goto out; /* download */ state_local = hif_state_get_child (state); ret = hif_package_array_download (job_data->packages_to_download, NULL, state_local, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* run transaction */ state_local = hif_state_get_child (state); ret = pk_backend_transaction_commit (job, state_local, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; out: return ret; } /** * pk_backend_transaction_run: */ static gboolean pk_backend_transaction_run (PkBackendJob *job, HifState *state, GError **error) { GPtrArray *packages = NULL; HifState *state_local; HyPackage pkg; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield types; gboolean ret = TRUE; gboolean valid; guint i; /* set state */ ret = hif_state_set_steps (state, error, 5, /* depsolve */ 95, /* everything else */ -1); if (!ret) goto out; /* depsolve */ ret = hif_goal_depsolve (job_data->goal, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; /* just simulate */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_SIMULATE)) { state_local = hif_state_get_child (state); ret = pk_backend_transaction_simulate (job, state_local, error); if (!ret) goto out; return hif_state_done (state, error); } /* set the list of repos */ ret = pk_backend_ensure_sources (job_data, error); if (!ret) goto out; /* find a list of all the packages we have to download */ types = pk_bitfield_from_enums (PK_INFO_ENUM_INSTALLING, PK_INFO_ENUM_REINSTALLING, PK_INFO_ENUM_DOWNGRADING, PK_INFO_ENUM_UPDATING, -1); packages = hif_goal_get_packages (job_data->goal, types); for (i = 0; i < packages->len; i++) { pkg = g_ptr_array_index (packages, i); /* get correct package source */ ret = hif_package_ensure_source (job_data->sources, pkg, error); if (!ret) goto out; /* this is a local file */ if (g_strcmp0 (hy_package_get_reponame (pkg), HY_CMDLINE_REPO_NAME) == 0) { continue; } /* check package exists and checksum is okay */ ret = hif_package_check_filename (pkg, &valid, error); if (!ret) goto out; /* package needs to be downloaded */ if (!valid) { g_ptr_array_add (job_data->packages_to_download, hy_package_link (pkg)); } } /* just download */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) { state_local = hif_state_get_child (state); ret = hif_package_array_download (job_data->packages_to_download, NULL, state_local, error); if (!ret) goto out; return hif_state_done (state, error); } /* download and commit transaction */ state_local = hif_state_get_child (state); ret = pk_backend_transaction_download_commit (job, state_local, error); if (!ret) goto out; /* done */ ret = hif_state_done (state, error); if (!ret) goto out; out: if (packages != NULL) g_ptr_array_unref (packages); return ret; } /** * pk_backend_repo_remove_thread: */ static void pk_backend_repo_remove_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; GPtrArray *removed_id = NULL; GPtrArray *sources = NULL; HifSource *src; HifState *state_local; HyPackage pkg; HyPackageList pkglist; HyQuery query = NULL; HyQuery query_release = NULL; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters = pk_bitfield_from_enums (PK_FILTER_ENUM_INSTALLED, -1); const gchar *from_repo; const gchar *repo_filename; const gchar *repo_id; const gchar *tmp; gboolean autoremove; gboolean ret; gboolean found; gchar **search = NULL; guint cnt = 0; guint i; guint j; g_variant_get (params, "(t&sb)", &job_data->transaction_flags, &repo_id, &autoremove); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 1, /* get the .repo filename for @repo_id */ 1, /* find any other repos in the same file */ 10, /* remove any packages from repos */ 3, /* remove repo-releases */ 85, /* run transaction */ -1); g_assert (ret); /* find the repo-release package name for @repo_id */ src = hif_repos_get_source_by_id (priv->repos, repo_id, &error); if (src == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find all the .repo files the repo-release package installed */ sources = hif_repos_get_sources (priv->repos, &error); search = g_new0 (gchar *, sources->len + 0); removed_id = g_ptr_array_new_with_free_func (g_free); repo_filename = hif_source_get_filename (src); for (i = 0; i < sources->len; i++) { src = g_ptr_array_index (sources, i); if (g_strcmp0 (hif_source_get_filename (src), repo_filename) != 0) continue; /* this repo_id will get purged */ tmp = hif_source_get_id (src); g_debug ("adding id %s to check", tmp); g_ptr_array_add (removed_id, g_strdup (tmp)); /* the package that installed the .repo file will be removed */ tmp = hif_source_get_filename (src); for (j = 0, found = FALSE; search[j] != NULL; j++) { if (g_strcmp0 (tmp, search[j]) == 0) found = TRUE; } if (!found) { g_debug ("adding filename %s to search", tmp); search[cnt++] = g_strdup (tmp); } } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* remove all the packages installed from all these repos */ state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } job_data->goal = hy_goal_create (sack); query = hy_query_create (sack); pkglist = hy_query_run (query); FOR_PACKAGELIST(pkg, pkglist, i) { pk_backend_ensure_origin_pkg (job_data->db, pkg); from_repo = hif_package_get_origin (pkg); if (from_repo == NULL) continue; for (j = 0; j < removed_id->len; j++) { tmp = g_ptr_array_index (removed_id, j); if (g_strcmp0 (tmp, from_repo) == 0) { g_debug ("%s %s as installed from %s", autoremove ? "removing" : "ignoring", hy_package_get_name (pkg), from_repo); if (autoremove) { hif_package_set_user_action (pkg, TRUE); hy_goal_erase (job_data->goal, pkg); } break; } } } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* remove the repo-releases */ query_release = hy_query_create (sack); hy_query_filter_in (query_release, HY_PKG_FILE, HY_EQ, (const gchar **) search); pkglist = hy_query_run (query_release); FOR_PACKAGELIST(pkg, pkglist, i) { pk_backend_ensure_origin_pkg (job_data->db, pkg); g_debug ("removing %s as installed for repo", hy_package_get_name (pkg)); hif_package_set_user_action (pkg, TRUE); hy_goal_erase (job_data->goal, pkg); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* run transaction */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_transaction_run (job, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: g_strfreev (search); if (query != NULL) hy_query_free (query); if (query_release != NULL) hy_query_free (query_release); if (sources != NULL) g_ptr_array_unref (sources); if (removed_id != NULL) g_ptr_array_unref (removed_id); pk_backend_job_finished (job); } /** * pk_backend_repo_remove: */ void pk_backend_repo_remove (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, const gchar *repo_id, gboolean autoremove) { pk_backend_job_thread_create (job, pk_backend_repo_remove_thread, NULL, NULL); } /** * hif_is_installed_package_id_name: */ static gboolean hif_is_installed_package_id_name (HySack sack, const gchar *package_id) { gboolean ret; gchar **split; HyPackageList pkglist = NULL; HyQuery query = NULL; /* run query */ query = hy_query_create (sack); split = pk_package_id_split (package_id); hy_query_filter (query, HY_PKG_NAME, HY_EQ, split[PK_PACKAGE_ID_NAME]); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); pkglist = hy_query_run (query); /* any matches? */ ret = hy_packagelist_count (pkglist) > 0; hy_packagelist_free (pkglist); hy_query_free (query); g_strfreev (split); return ret; } /** * hif_is_installed_package_id_name_arch: */ static gboolean hif_is_installed_package_id_name_arch (HySack sack, const gchar *package_id) { gboolean ret; gchar **split; HyPackageList pkglist = NULL; HyQuery query = NULL; /* run query */ query = hy_query_create (sack); split = pk_package_id_split (package_id); hy_query_filter (query, HY_PKG_NAME, HY_EQ, split[PK_PACKAGE_ID_NAME]); hy_query_filter (query, HY_PKG_ARCH, HY_EQ, split[PK_PACKAGE_ID_ARCH]); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); pkglist = hy_query_run (query); /* any matches? */ ret = hy_packagelist_count (pkglist) > 0; hy_packagelist_free (pkglist); hy_query_free (query); g_strfreev (split); return ret; } /** * pk_backend_remove_packages_thread: * * FIXME: Use autoremove * FIXME: Use allow_deps */ static void pk_backend_remove_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; GHashTable *hash = NULL; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean allow_deps; gboolean autoremove; gboolean ret; gchar **package_ids; guint i; g_variant_get (params, "(t^a&sbb)", &job_data->transaction_flags, &package_ids, &allow_deps, &autoremove); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 3, /* add repos */ 1, /* check installed */ 1, /* find packages */ 95, /* run transaction */ -1); g_assert (ret); /* not supported */ if (autoremove) { pk_backend_job_error_code (job, PK_ERROR_ENUM_NOT_SUPPORTED, "autoremove is not supported"); goto out; } if (!allow_deps) { pk_backend_job_error_code (job, PK_ERROR_ENUM_NOT_SUPPORTED, "!allow_deps is not supported"); goto out; } /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } //TODO: check if we're trying to remove protected packages like: //glibc, kernel, etc /* ensure packages are already installed */ for (i = 0; package_ids[i] != NULL; i++) { ret = hif_is_installed_package_id_name_arch (sack, package_ids[i]); if (!ret) { gchar *printable_tmp; printable_tmp = pk_package_id_to_printable (package_ids[i]); pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_INSTALLED, "%s is not already installed", printable_tmp); g_free (printable_tmp); goto out; } } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* remove packages */ job_data->goal = hy_goal_create (sack); for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find %s", package_ids[i]); goto out; } hif_package_set_user_action (pkg, TRUE); hy_goal_erase (job_data->goal, pkg); } /* run transaction */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_transaction_run (job, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_remove_packages: */ void pk_backend_remove_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids, gboolean allow_deps, gboolean autoremove) { pk_backend_job_thread_create (job, pk_backend_remove_packages_thread, NULL, NULL); } /** * pk_backend_install_packages_thread: */ static void pk_backend_install_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; GHashTable *hash = NULL; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; gchar **package_ids; guint i; g_variant_get (params, "(t^a&s)", &job_data->transaction_flags, &package_ids); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 3, /* add repos */ 1, /* check installed */ 1, /* find packages */ 95, /* run transaction */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* ensure packages are not already installed */ for (i = 0; package_ids[i] != NULL; i++) { ret = hif_is_installed_package_id_name_arch (sack, package_ids[i]); if (ret) { gchar *printable_tmp; printable_tmp = pk_package_id_to_printable (package_ids[i]); pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_ALREADY_INSTALLED, "%s is aleady installed", printable_tmp); g_free (printable_tmp); goto out; } } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find remote packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* install packages */ job_data->goal = hy_goal_create (sack); for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find %s", package_ids[i]); goto out; } hif_package_set_user_action (pkg, TRUE); hy_goal_install (job_data->goal, pkg); } /* run transaction */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_transaction_run (job, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_install_packages: */ void pk_backend_install_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) { pk_backend_job_thread_create (job, pk_backend_install_packages_thread, NULL, NULL); } /** * pk_backend_install_files_thread: */ static void pk_backend_install_files_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; GHashTable *hash = NULL; GPtrArray *array = NULL; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; gchar **full_paths; guint i; g_variant_get (params, "(t^a&s)", &job_data->transaction_flags, &full_paths); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 50, /* add repos */ 25, /* check installed */ 24, /* run transaction */ 1, /* emit */ -1); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); g_assert (ret); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* ensure packages are not already installed */ array = g_ptr_array_new (); for (i = 0; full_paths[i] != NULL; i++) { pkg = hy_sack_add_cmdline_package (sack, full_paths[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_FILE_NOT_FOUND, "Failed to open %s", full_paths[i]); goto out; } /* we don't download this, we just use it */ hif_package_set_filename (pkg, full_paths[i]); g_ptr_array_add (array, pkg); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* install packages */ job_data->goal = hy_goal_create (sack); for (i = 0; i < array->len; i++) { pkg = g_ptr_array_index (array, i); hy_goal_install (job_data->goal, pkg); } /* run transaction */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_transaction_run (job, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (array != NULL) g_ptr_array_unref (array); if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_install_files: */ void pk_backend_install_files (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **full_paths) { pk_backend_job_thread_create (job, pk_backend_install_files_thread, NULL, NULL); } /** * hy_package_evr_sort_newest_cb: */ static gint hy_package_evr_sort_newest_cb (gconstpointer a, gconstpointer b) { HyPackage *pa = (HyPackage *) a; HyPackage *pb = (HyPackage *) b; return -hy_package_evr_cmp (*pa, *pb); } /** * hif_goal_erase_only_n: */ static void hif_goal_erase_only_n (HyGoal goal, HySack sack_installed, const gchar *package_name, guint only_n) { GPtrArray *array = NULL; HyPackageList pkglist = NULL; HyPackage pkg; HyQuery query = NULL; guint i; /* run query */ query = hy_query_create (sack_installed); hy_query_filter (query, HY_PKG_NAME, HY_EQ, package_name); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); pkglist = hy_query_run (query); /* any matches? */ if ((guint) hy_packagelist_count (pkglist) < only_n) { g_debug ("only %i %s packages, not removing any", hy_packagelist_count (pkglist), package_name); goto out; } /* remove the oldest */ array = g_ptr_array_new (); FOR_PACKAGELIST (pkg, pkglist, i) g_ptr_array_add (array, pkg); g_ptr_array_sort (array, hy_package_evr_sort_newest_cb); for (i = 0; i < array->len; i++) { pkg = g_ptr_array_index (array, i); if (i >= only_n - 1) { g_debug ("removing %s", hif_package_get_nevra (pkg)); hy_goal_erase (goal, pkg); } } out: if (array != NULL) g_ptr_array_unref (array); hy_packagelist_free (pkglist); hy_query_free (query); } /** * pk_backend_update_packages_thread: */ static void pk_backend_update_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GError *error = NULL; GHashTable *hash = NULL; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; gchar **package_ids; guint i; guint j; const gchar *only_n_pkgnames[] = { "kernel", "kernel-source", "kernel-devel", NULL }; g_variant_get (params, "(t^a&s)", &job_data->transaction_flags, &package_ids); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 8, /* add repos */ 1, /* check installed */ 1, /* find packages */ 90, /* run transaction */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* ensure packages are not already installed */ for (i = 0; package_ids[i] != NULL; i++) { ret = hif_is_installed_package_id_name (sack, package_ids[i]); if (!ret) { gchar *printable_tmp; printable_tmp = pk_package_id_to_printable (package_ids[i]); pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_INSTALLED, "cannot update: %s is not already installed", printable_tmp); g_free (printable_tmp); goto out; } } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* install packages */ job_data->goal = hy_goal_create (sack); for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find %s", package_ids[i]); goto out; } hif_package_set_user_action (pkg, TRUE); /* allow some packages to have multiple versions installed, * but remove any older than the only_n limit */ for (j = 0; only_n_pkgnames[j] != NULL; j++) { if (g_strcmp0 (hy_package_get_name (pkg), only_n_pkgnames[j]) == 0) { hy_goal_install (job_data->goal, pkg); hif_goal_erase_only_n (job_data->goal, sack, only_n_pkgnames[j], 5); } else { hy_goal_upgrade_to (job_data->goal, pkg); } } } /* run transaction */ state_local = hif_state_get_child (job_data->state); ret = pk_backend_transaction_run (job, state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_update_packages: */ void pk_backend_update_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) { pk_backend_job_thread_create (job, pk_backend_update_packages_thread, NULL, NULL); } /** * pk_backend_get_groups: */ PkBitfield pk_backend_get_groups (PkBackend *backend) { return pk_bitfield_from_enums ( PK_GROUP_ENUM_COLLECTIONS, PK_GROUP_ENUM_NEWEST, PK_GROUP_ENUM_ADMIN_TOOLS, PK_GROUP_ENUM_DESKTOP_GNOME, PK_GROUP_ENUM_DESKTOP_KDE, PK_GROUP_ENUM_DESKTOP_XFCE, PK_GROUP_ENUM_DESKTOP_OTHER, PK_GROUP_ENUM_EDUCATION, PK_GROUP_ENUM_FONTS, PK_GROUP_ENUM_GAMES, PK_GROUP_ENUM_GRAPHICS, PK_GROUP_ENUM_INTERNET, PK_GROUP_ENUM_LEGACY, PK_GROUP_ENUM_LOCALIZATION, PK_GROUP_ENUM_MULTIMEDIA, PK_GROUP_ENUM_OFFICE, PK_GROUP_ENUM_OTHER, PK_GROUP_ENUM_PROGRAMMING, PK_GROUP_ENUM_PUBLISHING, PK_GROUP_ENUM_SERVERS, PK_GROUP_ENUM_SYSTEM, PK_GROUP_ENUM_VIRTUALIZATION, -1); } /** * pk_backend_sort_string_cb: **/ static gint pk_backend_sort_string_cb (const gchar **a, const gchar **b) { return g_strcmp0 (*a, *b); } /** * pk_backend_get_files_thread: */ static void pk_backend_get_files_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **package_ids; GError *error = NULL; GHashTable *hash = NULL; GPtrArray *files; guint i; guint j; HifState *state_local; HyPackage pkg; HySack sack; HyStringArray files_array; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 90, /* add repos */ 5, /* find packages */ 5, /* emit files */ -1); g_assert (ret); /* get sack */ g_variant_get (params, "(^a&s)", &package_ids); filters = hif_get_filter_for_ids (package_ids); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find packages */ hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* emit details */ for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, "Failed to find %s", package_ids[i]); goto out; } /* sort and list according to name */ files_array = hy_package_get_files (pkg); if (FALSE) { files = g_ptr_array_new (); for (j = 0; files_array[j] != NULL; j++) g_ptr_array_add (files, files_array[j]); g_ptr_array_sort (files, (GCompareFunc) pk_backend_sort_string_cb); g_ptr_array_add (files, NULL); pk_backend_job_files (job, package_ids[i], (gchar **) files->pdata); g_ptr_array_unref (files); } else { pk_backend_job_files (job, package_ids[i], (gchar **) files_array); } hy_stringarray_free (files_array); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_get_files: */ void pk_backend_get_files (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { pk_backend_job_thread_create (job, pk_backend_get_files_thread, NULL, NULL); } /** * pk_backend_get_update_detail_thread: */ static void pk_backend_get_update_detail_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gchar **package_ids; gboolean ret; GError *error = NULL; GHashTable *hash = NULL; guint i; HifState *state_local; HyPackage pkg; HySack sack = NULL; PkBackendHifJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; /* set state */ ret = hif_state_set_steps (job_data->state, NULL, 50, /* add repos */ 49, /* find packages */ 1, /* emit update details */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = hif_state_get_child (job_data->state); sack = hif_utils_create_sack_for_filters (job, filters, HIF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* find remote packages */ g_variant_get (params, "(^a&s)", &package_ids); hash = hif_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } /* emit details for each */ for (i = 0; package_ids[i] != NULL; i++) { pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) continue; pk_backend_job_update_detail (job, package_ids[i], NULL, NULL, hy_package_get_update_urls_vendor (pkg), hy_package_get_update_urls_bugzilla (pkg), hy_package_get_update_urls_cve (pkg), PK_RESTART_ENUM_NONE, /* FIXME */ hy_package_get_update_description (pkg), NULL, PK_UPDATE_STATE_ENUM_STABLE, /* FIXME */ NULL, /* issued */ NULL /* updated */); } /* done */ ret = hif_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); g_error_free (error); goto out; } out: if (hash != NULL) g_hash_table_unref (hash); pk_backend_job_finished (job); } /** * pk_backend_get_update_detail: */ void pk_backend_get_update_detail (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { pk_backend_job_thread_create (job, pk_backend_get_update_detail_thread, NULL, NULL); } /** * pk_backend_repair_remove_rpm_index: */ static gboolean pk_backend_repair_remove_rpm_index (const gchar *index_fn, GError **error) { GFile *file; gboolean ret; gchar *path; path = g_build_filename ("/var/lib/rpm", index_fn, NULL); g_debug ("deleting %s", path); file = g_file_new_for_path (path); ret = g_file_delete (file, NULL, error); if (!ret) goto out; out: g_object_unref (file); g_free (path); return ret; } /** * pk_backend_repair_system_thread: */ static void pk_backend_repair_system_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { GDir *dir = NULL; GError *error = NULL; PkBitfield transaction_flags; const gchar *tmp; gboolean ret; /* don't do anything when simulating */ pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); transaction_flags = pk_backend_job_get_transaction_flags (job); if (pk_bitfield_contain (transaction_flags, PK_TRANSACTION_FLAG_ENUM_SIMULATE)) goto out; /* open the directory */ dir = g_dir_open ("/var/lib/rpm", 0, &error); if (dir == NULL) { pk_backend_job_error_code (job, PK_ERROR_ENUM_INSTALL_ROOT_INVALID, "%s", error->message); g_error_free (error); goto out; } /* remove the indexes */ while ((tmp = g_dir_read_name (dir)) != NULL) { if (!g_str_has_prefix (tmp, "__db.")) continue; pk_backend_job_set_status (job, PK_STATUS_ENUM_CLEANUP); ret = pk_backend_repair_remove_rpm_index (tmp, &error); if (!ret) { pk_backend_job_error_code (job, PK_ERROR_ENUM_FILE_CONFLICTS, "Failed to delete %s: %s", tmp, error->message); g_error_free (error); goto out; } } out: if (dir != NULL) g_dir_close (dir); pk_backend_job_finished (job); } /** * pk_backend_repair_system: */ void pk_backend_repair_system (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags) { pk_backend_job_thread_create (job, pk_backend_repair_system_thread, NULL, NULL); }