/* -*- 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dnf-backend-vendor.h" #include "dnf-backend.h" #include "pk-backend-dnf-common.h" #define DNF_SACK_MAX_AGE 600 /* seconds */ typedef struct { DnfSack *sack; gchar *key; GTimer *timer; } DnfSackCacheItem; typedef struct { GKeyFile *conf; DnfContext *context; GHashTable *sack_cache; /* of DnfSackCacheItem */ GMutex sack_mutex; GTimer *repos_timer; gchar *release_ver; guint sack_expire_id; } PkBackendDnfPrivate; typedef struct { DnfContext *context; DnfTransaction *transaction; DnfState *state; PkBackend *backend; PkBitfield transaction_flags; HyGoal goal; } PkBackendDnfJobData; static GPtrArray * pk_backend_find_refresh_repos (PkBackendJob *job, DnfState *state, GPtrArray *repos, gboolean force, GError **error); const gchar * pk_backend_get_description (PkBackend *backend) { return "Dnf"; } const gchar * pk_backend_get_author (PkBackend *backend) { return "Richard Hughes "; } gboolean pk_backend_supports_parallelization (PkBackend *backend) { return FALSE; } static gboolean pk_backend_check_sack_timer (gpointer key, gpointer value, gpointer user_data) { DnfSackCacheItem *cache_item = value; if (g_timer_elapsed (cache_item->timer, NULL) > DNF_SACK_MAX_AGE) { g_debug ("invalidating %s as expired", (char *)key); return TRUE; } return FALSE; } static gboolean pk_backend_sack_expire (gpointer user_data) { PkBackendDnfPrivate *priv = user_data; g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->sack_mutex); g_hash_table_foreach_remove (priv->sack_cache, pk_backend_check_sack_timer, NULL); return TRUE; } static void pk_backend_sack_cache_invalidate (PkBackend *backend, const gchar *why) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->sack_mutex); /* remove all cached sacks */ g_debug ("removing all dnf sack caches"); g_hash_table_remove_all (priv->sack_cache); } static void pk_backend_yum_repos_changed_cb (DnfRepoLoader *repo_loader, PkBackend *backend) { pk_backend_sack_cache_invalidate (backend, "yum.repos.d changed"); pk_backend_repo_list_changed (backend); } static void dnf_sack_cache_item_free (DnfSackCacheItem *cache_item) { g_timer_destroy (cache_item->timer); g_object_unref (cache_item->sack); g_free (cache_item->key); g_slice_free (DnfSackCacheItem, cache_item); } static void pk_backend_context_invalidate_cb (DnfContext *context, const gchar *message, PkBackend *backend) { pk_backend_sack_cache_invalidate (backend, message); pk_backend_installed_db_changed (backend); } static void remove_old_cache_directories (PkBackend *backend, const gchar *release_ver) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); gboolean keep_cache; const gchar *filename; g_autofree gchar *destdir = NULL; g_autoptr(GDir) cache_dir = NULL; g_autoptr(GError) error = NULL; g_assert (priv->conf != NULL); /* cache cleanup disabled? */ keep_cache = g_key_file_get_boolean (priv->conf, "Daemon", "KeepCache", NULL); if (keep_cache) { g_debug ("KeepCache config option set; skipping old cache directory cleanup"); return; } /* only do cache cleanup for regular installs */ destdir = g_key_file_get_string (priv->conf, "Daemon", "DestDir", NULL); if (destdir != NULL) { g_debug ("DestDir config option set; skipping old cache directory cleanup"); return; } /* open directory */ cache_dir = g_dir_open ("/var/cache/PackageKit", 0, &error); if (cache_dir == NULL) { g_warning ("cannot open directory: %s", error->message); return; } /* look at each subdirectory */ while ((filename = g_dir_read_name (cache_dir))) { g_autofree gchar *dir = g_build_filename ("/var/cache/PackageKit", filename, NULL); if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) continue; /* is the version older than the current release ver? */ if (rpmvercmp (filename, release_ver) < 0) { g_debug ("removing old cache directory %s", dir); pk_directory_remove_contents (dir); if (g_remove (dir) != 0) g_warning ("failed to remove directory %s", dir); } } } static gboolean pk_backend_ensure_default_dnf_context (PkBackend *backend, GError **error) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(DnfContext) context = NULL; /* already set */ if (priv->context != NULL) return TRUE; g_assert (priv->conf != NULL); g_assert (priv->release_ver != NULL); /* set defaults */ context = dnf_context_new (); if (!pk_backend_setup_dnf_context (context, priv->conf, priv->release_ver, error)) return FALSE; /* setup succeeded: store in priv and connect signals */ priv->context = g_steal_pointer (&context); g_signal_connect (priv->context, "invalidate", G_CALLBACK (pk_backend_context_invalidate_cb), backend); g_signal_connect (dnf_context_get_repo_loader (priv->context), "changed", G_CALLBACK (pk_backend_yum_repos_changed_cb), backend); return TRUE; } void pk_backend_initialize (GKeyFile *conf, PkBackend *backend) { PkBackendDnfPrivate *priv; g_autoptr(GError) error = NULL; /* use logging */ pk_debug_add_log_domain (G_LOG_DOMAIN); pk_debug_add_log_domain ("Dnf"); /* create private area */ priv = g_new0 (PkBackendDnfPrivate, 1); pk_backend_set_user_data (backend, priv); priv->conf = g_key_file_ref (conf); priv->repos_timer = g_timer_new (); g_debug ("Using libdnf %i.%i.%i", LIBDNF_MAJOR_VERSION, LIBDNF_MINOR_VERSION, LIBDNF_MICRO_VERSION); g_debug ("Using librepo %i.%i.%i", LR_VERSION_MAJOR, LR_VERSION_MINOR, LR_VERSION_PATCH); priv->release_ver = pk_get_distro_version_id (&error); if (priv->release_ver == NULL) g_error ("Failed to parse os-release: %s", error->message); /* clean up any cache directories left over from a distro upgrade */ remove_old_cache_directories (backend, priv->release_ver); /* a cache of DnfSacks 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) dnf_sack_cache_item_free); priv->sack_expire_id = g_timeout_add_seconds (DNF_SACK_MAX_AGE / 2, pk_backend_sack_expire, priv); if (!pk_backend_ensure_default_dnf_context (backend, &error)) g_warning ("failed to setup context: %s", error->message); } void pk_backend_destroy (PkBackend *backend) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); if (priv->conf != NULL) g_key_file_unref (priv->conf); if (priv->context != NULL) g_object_unref (priv->context); if (priv->sack_expire_id > 0) g_source_remove (priv->sack_expire_id); g_timer_destroy (priv->repos_timer); g_mutex_clear (&priv->sack_mutex); g_hash_table_unref (priv->sack_cache); g_free (priv->release_ver); g_free (priv); } static void pk_backend_state_percentage_changed_cb (DnfState *state, guint percentage, PkBackendJob *job) { pk_backend_job_set_percentage (job, percentage); } static void pk_backend_download_percentage_changed_cb (DnfState *state, guint percentage, PkBackendJob *job) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); GPtrArray *remote_pkgs; guint64 download_size; guint64 download_size_remaining; remote_pkgs = dnf_transaction_get_remote_pkgs (job_data->transaction); download_size = dnf_package_array_get_download_size (remote_pkgs); if (download_size == 0) return; download_size_remaining = download_size - (download_size / 100.0f * percentage); pk_backend_job_set_download_size_remaining (job, download_size_remaining); } static void pk_backend_state_action_changed_cb (DnfState *state, DnfStateAction action, const gchar *action_hint, PkBackendJob *job) { if (action != DNF_STATE_ACTION_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 DNF_STATE_ACTION_DOWNLOAD_PACKAGES: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_DOWNLOADING, action_hint, ""); } break; case DNF_STATE_ACTION_INSTALL: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_INSTALLING, action_hint, ""); } break; case DNF_STATE_ACTION_REINSTALL: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_REINSTALLING, action_hint, ""); } break; case DNF_STATE_ACTION_REMOVE: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_REMOVING, action_hint, ""); } break; case DNF_STATE_ACTION_DOWNGRADE: case DNF_STATE_ACTION_UPDATE: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_UPDATING, action_hint, ""); } break; case DNF_STATE_ACTION_CLEANUP: if (pk_package_id_check (action_hint)) { pk_backend_job_package (job, PK_INFO_ENUM_CLEANUP, action_hint, ""); } break; default: break; } } static void pk_backend_speed_changed_cb (DnfState *state, GParamSpec *pspec, PkBackendJob *job) { pk_backend_job_set_speed (job, dnf_state_get_speed (state)); } static void pk_backend_state_allow_cancel_changed_cb (DnfState *state, gboolean allow_cancel, PkBackendJob *job) { pk_backend_job_set_allow_cancel (job, allow_cancel); } static void pk_backend_job_set_context (PkBackendJob *job, DnfContext *context) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); const gchar *value; /* DnfContext */ g_set_object (&job_data->context, context); /* set proxy */ value = pk_backend_job_get_proxy_http (job); if (value != NULL) { g_autofree gchar *uri = pk_backend_convert_uri (value); dnf_context_set_http_proxy (job_data->context, uri); } /* transaction */ g_clear_object (&job_data->transaction); job_data->transaction = dnf_transaction_new (job_data->context); dnf_transaction_set_repos (job_data->transaction, dnf_context_get_repos (job_data->context)); dnf_transaction_set_uid (job_data->transaction, pk_backend_job_get_uid (job)); } void pk_backend_start_job (PkBackend *backend, PkBackendJob *job) { PkBackendDnfJobData *job_data; job_data = g_new0 (PkBackendDnfJobData, 1); job_data->backend = backend; pk_backend_job_set_user_data (job, job_data); /* DnfState */ job_data->state = dnf_state_new (); dnf_state_set_cancellable (job_data->state, pk_backend_job_get_cancellable (job)); 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, "allow-cancel-changed", G_CALLBACK (pk_backend_state_allow_cancel_changed_cb), job); g_signal_connect (job_data->state, "notify::speed", G_CALLBACK (pk_backend_speed_changed_cb), job); #ifdef PK_BUILD_LOCAL /* we don't want to enable this for normal runtime */ dnf_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); } void pk_backend_stop_job (PkBackend *backend, PkBackendJob *job) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); if (job_data->state != NULL) { dnf_state_release_locks (job_data->state); g_object_unref (job_data->state); } if (job_data->transaction != NULL) g_object_unref (job_data->transaction); if (job_data->context != NULL) g_object_unref (job_data->context); if (job_data->goal != NULL) hy_goal_free (job_data->goal); g_free (job_data); pk_backend_job_set_user_data (job, NULL); } static gboolean dnf_utils_add_remote (PkBackendJob *job, DnfSack *sack, DnfSackAddFlags flags, DnfState *state, GError **error) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); gboolean ret; DnfState *state_local; g_autoptr(GPtrArray) repos = NULL; g_autoptr(GPtrArray) refresh_repos = NULL; /* set state */ ret = dnf_state_set_steps (state, error, 2, /* load files */ 1, /* count */ 97, /* add repos */ -1); if (!ret) return FALSE; /* ask the context's repo loader for new repos, forcing it to reload them */ repos = dnf_repo_loader_get_repos (dnf_context_get_repo_loader (job_data->context), error); if (repos == NULL) return FALSE; /* done */ if (!dnf_state_done (state, error)) return FALSE; /* Find out what repositories potentially will get downloaded - we might need to update * the appstream data for these repositories. Note that there is a small chance that the * repo metadata could expire between the call to dnf_repo_check() at this point, and * the call to dnf_repo_check() inside dnf_sack_add_repos() - in this case we'll end up * with stale appstream data until the next metadata refresh. */ refresh_repos = pk_backend_find_refresh_repos (job, state, repos, FALSE /* !force */, error); if (refresh_repos == NULL) return FALSE; /* add each repo */ state_local = dnf_state_get_child (state); ret = dnf_sack_add_repos (sack, repos, pk_backend_job_get_cache_age (job), flags, state_local, error); if (!ret) return FALSE; for (guint i = 0; i < refresh_repos->len; i++) { DnfRepo *repo = g_ptr_array_index (refresh_repos, i); if (!dnf_utils_refresh_repo_appstream (repo, error)) return FALSE; } /* done */ if (!dnf_state_done (state, error)) return FALSE; return TRUE; } typedef enum { DNF_CREATE_SACK_FLAG_NONE, DNF_CREATE_SACK_FLAG_USE_CACHE, DNF_CREATE_SACK_FLAG_LAST } DnfCreateSackFlags; static gchar * dnf_utils_create_cache_key (const gchar *release_ver, DnfSackAddFlags flags) { GString *key; key = g_string_new ("DnfSack::"); g_string_append_printf (key, "release_ver[%s]::", release_ver); if (flags == DNF_SACK_ADD_FLAG_NONE) { g_string_append (key, "none"); } else { if (flags & DNF_SACK_ADD_FLAG_FILELISTS) g_string_append (key, "filelists|"); if (flags & DNF_SACK_ADD_FLAG_UPDATEINFO) g_string_append (key, "updateinfo|"); if (flags & DNF_SACK_ADD_FLAG_REMOTE) g_string_append (key, "remote|"); if (flags & DNF_SACK_ADD_FLAG_UNAVAILABLE) g_string_append (key, "unavailable|"); g_string_truncate (key, key->len - 1); } return g_string_free (key, FALSE); } static gchar * dnf_utils_real_path (const gchar *path) { gchar *real = NULL; char *temp; /* don't trust realpath one little bit */ if (path == NULL) return NULL; /* glibc allocates us a buffer to try and fix some brain damage */ temp = realpath (path, NULL); if (temp == NULL) return NULL; real = g_strdup (temp); free (temp); return real; } static DnfSack * dnf_utils_create_sack_for_filters (PkBackendJob *job, PkBitfield filters, DnfCreateSackFlags create_flags, DnfState *state, GError **error) { gboolean ret; DnfSackAddFlags flags = DNF_SACK_ADD_FLAG_FILELISTS; DnfSackCacheItem *cache_item = NULL; DnfState *state_local; PkBackend *backend = pk_backend_job_get_backend (job); PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autofree gchar *cache_key = NULL; g_autofree gchar *install_root = NULL; g_autofree gchar *solv_dir = NULL; g_autoptr(DnfSack) sack = NULL; /* don't add if we're going to filter out anyway */ if (!pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED)) flags |= DNF_SACK_ADD_FLAG_REMOTE; /* only load updateinfo when required */ if (pk_backend_job_get_role (job) == PK_ROLE_ENUM_GET_UPDATE_DETAIL || pk_backend_job_get_role (job) == PK_ROLE_ENUM_GET_UPDATES) flags |= DNF_SACK_ADD_FLAG_UPDATEINFO; /* only use unavailble packages for queries */ switch (pk_backend_job_get_role (job)) { case PK_ROLE_ENUM_RESOLVE: case PK_ROLE_ENUM_SEARCH_NAME: case PK_ROLE_ENUM_SEARCH_DETAILS: case PK_ROLE_ENUM_SEARCH_FILE: case PK_ROLE_ENUM_GET_DETAILS: case PK_ROLE_ENUM_WHAT_PROVIDES: flags |= DNF_SACK_ADD_FLAG_UNAVAILABLE; break; default: break; } /* media repos could disappear at any time */ if ((create_flags & DNF_CREATE_SACK_FLAG_USE_CACHE) > 0 && dnf_repo_loader_has_removable_repos (dnf_context_get_repo_loader (job_data->context)) && g_timer_elapsed (priv->repos_timer, NULL) > 1.0f) { g_debug ("not reusing sack as media may have disappeared"); create_flags &= ~DNF_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 & DNF_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 &= ~DNF_CREATE_SACK_FLAG_USE_CACHE; } /* do we have anything in the cache */ cache_key = dnf_utils_create_cache_key (dnf_context_get_release_ver (job_data->context), flags); if ((create_flags & DNF_CREATE_SACK_FLAG_USE_CACHE) > 0) { g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->sack_mutex); cache_item = g_hash_table_lookup (priv->sack_cache, cache_key); if (cache_item != NULL && cache_item->sack != NULL) { g_debug ("using cached sack %s", cache_key); g_timer_start (cache_item->timer); return g_object_ref (cache_item->sack); } } /* update status */ dnf_state_action_start (state, DNF_STATE_ACTION_QUERY, NULL); /* set state */ if ((flags & DNF_SACK_ADD_FLAG_REMOTE) > 0) { ret = dnf_state_set_steps (state, error, 8, /* add installed */ 92, /* add remote */ -1); if (!ret) return NULL; } else { dnf_state_set_number_steps (state, 1); } /* create empty sack */ solv_dir = dnf_utils_real_path (dnf_context_get_solv_dir (job_data->context)); install_root = dnf_utils_real_path (dnf_context_get_install_root (job_data->context)); sack = dnf_sack_new (); dnf_sack_set_cachedir (sack, solv_dir); dnf_sack_set_rootdir (sack, install_root); ret = dnf_sack_setup (sack, DNF_SACK_SETUP_FLAG_MAKE_CACHE_DIR, error); if (!ret) { g_prefix_error (error, "failed to create sack in %s for %s: ", dnf_context_get_solv_dir (job_data->context), dnf_context_get_install_root (job_data->context)); return NULL; } /* add installed packages */ ret = dnf_sack_load_system_repo (sack, NULL, DNF_SACK_LOAD_FLAG_BUILD_CACHE, error); if (!ret) { g_prefix_error (error, "Failed to load system repo: "); return NULL; } /* done */ ret = dnf_state_done (state, error); if (!ret) return NULL; /* add remote packages */ if ((flags & DNF_SACK_ADD_FLAG_REMOTE) > 0) { state_local = dnf_state_get_child (state); ret = dnf_utils_add_remote (job, sack, flags, state_local, error); if (!ret) return NULL; /* done */ ret = dnf_state_done (state, error); if (!ret) return NULL; } dnf_sack_filter_modules (sack, dnf_context_get_repos (job_data->context), install_root, NULL); /* save in cache */ g_mutex_lock (&priv->sack_mutex); cache_item = g_slice_new (DnfSackCacheItem); cache_item->key = g_strdup (cache_key); cache_item->sack = g_object_ref (sack); cache_item->timer = g_timer_new (); 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); return g_steal_pointer (&sack); } static GPtrArray * dnf_utils_run_query_with_newest_filter (DnfSack *sack, HyQuery query) { GPtrArray *results; GPtrArray *results_tmp; DnfPackageSet *pkgset; DnfPackage *pkg; HyQuery query_tmp; guint i; /* Run the prepared query */ pkgset = hy_query_run_set (query); /* Filter latest system packages */ query_tmp = hy_query_create (sack); hy_query_filter_package_in (query_tmp, HY_PKG, HY_EQ, pkgset); hy_query_filter (query_tmp, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); hy_query_filter_latest_per_arch (query_tmp, TRUE); results = hy_query_run (query_tmp); hy_query_free (query_tmp); /* Filter latest available packages */ query_tmp = hy_query_create (sack); hy_query_filter_package_in (query_tmp, HY_PKG, HY_EQ, pkgset); hy_query_filter (query_tmp, HY_PKG_REPONAME, HY_NEQ, HY_SYSTEM_REPO_NAME); hy_query_filter_latest_per_arch (query_tmp, TRUE); results_tmp = hy_query_run (query_tmp); /* ... and add to the previous results */ for (i = 0; i < results_tmp->len; i++) { pkg = g_ptr_array_index (results_tmp, i); g_ptr_array_add (results, g_object_ref (pkg)); } hy_query_free (query_tmp); g_ptr_array_unref (results_tmp); dnf_packageset_free (pkgset); return results; } static gboolean dnf_utils_force_distupgrade_on_upgrade (DnfSack *sack) { g_autoptr(GPtrArray) plist = NULL; gint candidates; const gchar *distroverpkg_names[] = { "system-release", "distribution-release", NULL }; const gchar *distupgrade_provides[] = { "system-upgrade(dsync)", "product-upgrade() = dup", NULL }; HyQuery query_tmp = hy_query_create (sack); hy_query_filter (query_tmp, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); hy_query_filter_provides_in (query_tmp, (gchar**) distroverpkg_names); hy_query_filter_provides_in (query_tmp, (gchar**) distupgrade_provides); plist = hy_query_run (query_tmp); candidates = plist->len; hy_query_free (query_tmp); if (candidates > 0) return TRUE; return FALSE; } static GPtrArray * dnf_utils_run_query_with_filters (PkBackendJob *job, DnfSack *sack, HyQuery query, PkBitfield filters) { GPtrArray *results; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); const gchar *application_glob = "/usr/share/applications/*.desktop"; /* arch */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_ARCH)) { hy_query_filter_in (query, HY_PKG_ARCH, HY_EQ, dnf_context_get_native_arches (job_data->context)); } else if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_ARCH)) { hy_query_filter_in (query, HY_PKG_ARCH, HY_NEQ, dnf_context_get_native_arches (job_data->context)); } /* 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); /* newest */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NEWEST)) results = dnf_utils_run_query_with_newest_filter (sack, query); else results = hy_query_run (query); return results; } static gchar ** pk_backend_what_provides_decompose (gchar **values, GError **error) { guint i; GPtrArray *array; /* iter on each provide string, and wrap it with the fedora prefix */ array = g_ptr_array_new (); for (i = 0; values[i] != NULL; 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])); } g_ptr_array_add (array, NULL); return (gchar **) g_ptr_array_free (array, FALSE); } static GHashTable * pk_backend_dnf_cache_advisories (DnfSack *sack) { #ifdef HAVE_HY_QUERY_GET_ADVISORY_PKGS g_autoptr(GPtrArray) array = NULL; GHashTable *hash; HyQuery query; guint ii; hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) dnf_advisory_free); query = hy_query_create (sack); array = hy_query_get_advisory_pkgs (query, HY_EQ); for (ii = 0; ii < array->len; ii++) { DnfAdvisoryPkg *advpkg = g_ptr_array_index (array, ii); gchar *id; id = g_strdup_printf ("%s;%s;%s", dnf_advisorypkg_get_name (advpkg), dnf_advisorypkg_get_evr (advpkg), dnf_advisorypkg_get_arch (advpkg)); g_hash_table_insert (hash, id, dnf_advisorypkg_get_advisory (advpkg)); } hy_query_free (query); return hash; #else return NULL; #endif } static DnfAdvisory * pk_backend_dnf_get_advisory (GHashTable *advisories_hash, DnfPackage *pkg) { #ifdef HAVE_HY_QUERY_GET_ADVISORY_PKGS g_autofree gchar *id = NULL; if (pkg == NULL) return NULL; id = g_strdup_printf ("%s;%s;%s", dnf_package_get_name (pkg), dnf_package_get_evr (pkg), dnf_package_get_arch (pkg)); return g_hash_table_lookup (advisories_hash, id); #else GPtrArray *advisorylist; DnfAdvisory *advisory = NULL; advisorylist = dnf_package_get_advisories (pkg, HY_EQ); if (advisorylist->len > 0) advisory = g_steal_pointer (&g_ptr_array_index (advisorylist, 0)); g_ptr_array_unref (advisorylist); return advisory; #endif } static void pk_backend_search_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; DnfDb *db; DnfState *state_local; DnfGoalActions flags; GPtrArray *installs = NULL; GPtrArray *pkglist = NULL; HyQuery query = NULL; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters = 0; g_autofree gchar **search_tmp = NULL; g_autoptr(GError) error = NULL; g_autoptr(DnfSack) sack = NULL; g_auto(GStrv) search = NULL; /* set state */ ret = dnf_state_set_steps (job_data->state, NULL, 39, /* add repos */ 50, /* query */ 1, /* ensure repo 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); goto out; } break; default: g_variant_get (params, "(t^as)", &filters, &search); break; } /* get sack */ state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* run query */ query = hy_query_create (sack); switch (pk_backend_job_get_role (job)) { case PK_ROLE_ENUM_GET_PACKAGES: pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_RESOLVE: hy_query_filter_in (query, HY_PKG_NAME, HY_EQ, (const gchar **) search); pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_SEARCH_FILE: hy_query_filter_in (query, HY_PKG_FILE, HY_EQ, (const gchar **) search); pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_SEARCH_DETAILS: hy_query_filter_in (query, HY_PKG_DESCRIPTION, HY_ICASE | HY_SUBSTR, (const gchar **) search); pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_SEARCH_NAME: hy_query_filter_in (query, HY_PKG_NAME, HY_ICASE | HY_SUBSTR, (const gchar **) search); pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_WHAT_PROVIDES: hy_query_filter_provides_in (query, search); pkglist = dnf_utils_run_query_with_filters (job, sack, query, filters); break; case PK_ROLE_ENUM_GET_UPDATES: /* set up the sack for packages that should only ever be installed, never updated */ dnf_sack_set_installonly (sack, dnf_context_get_installonly_pkgs (job_data->context)); dnf_sack_set_installonly_limit (sack, dnf_context_get_installonly_limit (job_data->context)); job_data->goal = hy_goal_create (sack); if (dnf_utils_force_distupgrade_on_upgrade (sack)) { hy_goal_distupgrade_all (job_data->goal); } else { hy_goal_upgrade_all (job_data->goal); } flags = DNF_ALLOW_UNINSTALL; if (!dnf_context_get_install_weak_deps ()) flags |= DNF_IGNORE_WEAK_DEPS; ret = dnf_goal_depsolve (job_data->goal, flags, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* get packages marked for upgrade */ pkglist = hy_goal_list_upgrades (job_data->goal, NULL); /* add any packages marked for install */ installs = hy_goal_list_installs (job_data->goal, NULL); if (installs != NULL) { guint i; DnfPackage *pkg; for (i = 0; i < installs->len; i++) { pkg = g_ptr_array_index (installs, i); g_ptr_array_add (pkglist, g_object_ref (pkg)); } } break; default: g_assert_not_reached (); break; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* set the repo on each package */ ret = dnf_transaction_ensure_repo_list (job_data->transaction, pkglist, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* set the origin on each package */ db = dnf_transaction_get_db (job_data->transaction); dnf_db_ensure_origin_pkglist (db, pkglist); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } if (pk_backend_job_get_role (job) == PK_ROLE_ENUM_GET_UPDATES) { guint i; DnfPackage *pkg; DnfAdvisory *advisory; DnfAdvisoryKind kind; PkInfoEnum info_enum; g_autoptr(GHashTable) advisories_hash = pk_backend_dnf_cache_advisories (sack); for (i = 0; i < pkglist->len; i++) { pkg = g_ptr_array_index (pkglist, i); advisory = pk_backend_dnf_get_advisory (advisories_hash, pkg); if (advisory != NULL) { kind = dnf_advisory_get_kind (advisory); g_object_set_data (G_OBJECT (pkg), PK_DNF_UPDATE_SEVERITY_KEY, GUINT_TO_POINTER (dnf_update_severity_to_enum (dnf_advisory_get_severity (advisory)))); #ifndef HAVE_HY_QUERY_GET_ADVISORY_PKGS dnf_advisory_free (advisory); #endif info_enum = dnf_advisory_kind_to_info_enum (kind); dnf_package_set_info (pkg, info_enum); } } } dnf_emit_package_list_filter (job, filters, pkglist); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } out: if (installs != NULL) g_ptr_array_unref (installs); if (pkglist != NULL) g_ptr_array_unref (pkglist); if (query != NULL) hy_query_free (query); } void pk_backend_get_packages (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_resolve (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_search_details (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_search_files (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_what_provides (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } void pk_backend_get_updates (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_search_thread, NULL, NULL); } /* Obviously hardcoded based on the repository ID labels. * Colin Walters thinks this concept should be based on * user's trust of a GPG key or something more flexible. */ static gboolean repo_is_supported (DnfRepo *repo) { return dnf_validate_supported_repo (dnf_repo_get_id (repo)); } static gboolean pk_backend_repo_filter (DnfRepo *repo, PkBitfield filters) { /* devel and ~devel */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_DEVELOPMENT) && !dnf_repo_is_devel (repo)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_DEVELOPMENT) && dnf_repo_is_devel (repo)) return FALSE; /* source and ~source */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SOURCE) && !dnf_repo_is_source (repo)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SOURCE) && dnf_repo_is_source (repo)) return FALSE; /* installed and ~installed == enabled */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && dnf_repo_get_enabled (repo) == DNF_REPO_ENABLED_NONE) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && dnf_repo_get_enabled (repo) != DNF_REPO_ENABLED_NONE) return FALSE; /* supported and ~supported == core */ if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SUPPORTED) && !repo_is_supported (repo)) return FALSE; if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SUPPORTED) && repo_is_supported (repo)) return FALSE; /* not filtered */ return TRUE; } static void pk_backend_get_repo_list_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean enabled; guint i; DnfRepo *repo; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) repos = NULL; g_variant_get (params, "(t)", &filters); /* set the list of repos */ pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); /* ask the context's repo loader for new repos, forcing it to reload them */ repos = dnf_repo_loader_get_repos (dnf_context_get_repo_loader (job_data->context), &error); if (repos == NULL) { pk_backend_job_error_code (job, error->code, "failed to load repos: %s", error->message); return; } /* emit each repo */ for (i = 0; i < repos->len; i++) { g_autofree gchar *description = NULL; repo = g_ptr_array_index (repos, i); if (!pk_backend_repo_filter (repo, filters)) continue; description = dnf_repo_get_description (repo); enabled = (dnf_repo_get_enabled (repo) & DNF_REPO_ENABLED_PACKAGES) > 0; pk_backend_job_repo_detail (job, dnf_repo_get_id (repo), description, enabled); } } void pk_backend_get_repo_list (PkBackend *backend, PkBackendJob *job, PkBitfield filters) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_get_repo_list_thread, NULL, NULL); } 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; DnfRepo *repo; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); g_autoptr(GError) error = NULL; /* get arguments */ switch (pk_backend_job_get_role (job)) { case PK_ROLE_ENUM_REPO_ENABLE: { gboolean enabled; g_variant_get (params, "(&sb)", &repo_id, &enabled); if (enabled) value = "1"; else value = "0"; parameter = "enabled"; } break; default: g_variant_get (params, "(&s&s&s)", &repo_id, ¶meter, &value); break; } /* take lock */ ret = dnf_state_take_lock (job_data->state, DNF_LOCK_TYPE_REPO, DNF_LOCK_MODE_PROCESS, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "failed to get lock: %s", error->message); 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 */ repo = dnf_repo_loader_get_repo_by_id (dnf_context_get_repo_loader (job_data->context), repo_id, &error); if (repo == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* check this isn't a waste of time */ if (g_strcmp0 (parameter, "enabled") == 0) { ret = (dnf_repo_get_enabled (repo) & DNF_REPO_ENABLED_PACKAGES) > 0; if (g_strcmp0 (value, "1") == 0 && ret) { pk_backend_job_error_code (job, PK_ERROR_ENUM_REPO_ALREADY_SET, "repo already enabled"); goto out; } if (g_strcmp0 (value, "0") == 0 && !ret) { pk_backend_job_error_code (job, PK_ERROR_ENUM_REPO_ALREADY_SET, "repo already disabled"); goto out; } } ret = dnf_repo_set_data (repo, parameter, value, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "failed to write repo file: %s", error->message); goto out; } ret = dnf_repo_commit (repo, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "failed to write repo file: %s", error->message); goto out; } /* nothing found */ pk_backend_job_set_percentage (job, 100); out: dnf_state_release_locks (job_data->state); } void pk_backend_repo_set_data (PkBackend *backend, PkBackendJob *job, const gchar *repo_id, const gchar *parameter, const gchar *value) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_repo_set_data_thread, NULL, NULL); } void pk_backend_repo_enable (PkBackend *backend, PkBackendJob *job, const gchar *repo_id, gboolean enabled) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_repo_set_data_thread, NULL, NULL); } 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); } gchar ** pk_backend_get_mime_types (PkBackend *backend) { const gchar *mime_types[] = { "application/x-rpm", NULL }; return g_strdupv ((gchar **) mime_types); } static void pk_backend_refresh_subman (PkBackendJob *job) { PkBackend *backend = pk_backend_job_get_backend (job); const gchar *argv[] = { "/usr/sbin/subscription-manager", "sync", NULL }; g_autofree gchar *err = NULL; g_autofree gchar *out = NULL; g_autoptr(GError) error_local = NULL; if (!g_file_test (argv[0], G_FILE_TEST_EXISTS)) return; if (!g_spawn_sync (NULL, (gchar **) argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, &out, &err, NULL, &error_local)) { g_autofree gchar *cmd = g_strjoinv (" ", (gchar **) argv); g_warning ("failed to run '%s': %s [stdout:%s, stderr:%s]", cmd, error_local->message, out, err); return; } pk_backend_sack_cache_invalidate (backend, "subscription-manager ran"); pk_backend_repo_list_changed (backend); } static GPtrArray * pk_backend_find_refresh_repos (PkBackendJob *job, DnfState *state, GPtrArray *repos, gboolean force, GError **error) { g_autoptr(GPtrArray) refresh_repos = NULL; DnfState *state_local; DnfState *state_loop; guint cnt = 0; guint i; /* count the enabled repos */ for (i = 0; i < repos->len; i++) { DnfRepo *repo = g_ptr_array_index (repos, i); if (dnf_repo_get_enabled (repo) == DNF_REPO_ENABLED_NONE) continue; if (dnf_repo_get_kind (repo) == DNF_REPO_KIND_MEDIA) continue; if (dnf_repo_get_kind (repo) == DNF_REPO_KIND_LOCAL) continue; cnt++; } /* figure out which repos need refreshing */ refresh_repos = g_ptr_array_new (); state_local = dnf_state_get_child (state); dnf_state_set_number_steps (state_local, cnt); for (i = 0; i < repos->len; i++) { DnfRepo *repo = g_ptr_array_index (repos, i); gboolean repo_okay; if (dnf_repo_get_enabled (repo) == DNF_REPO_ENABLED_NONE) continue; if (dnf_repo_get_kind (repo) == DNF_REPO_KIND_MEDIA) continue; if (dnf_repo_get_kind (repo) == DNF_REPO_KIND_LOCAL) continue; /* is the repo up to date? */ state_loop = dnf_state_get_child (state_local); repo_okay = dnf_repo_check (repo, pk_backend_job_get_cache_age (job), state_loop, NULL); if (!repo_okay || force) g_ptr_array_add (refresh_repos, g_ptr_array_index (repos, i)); /* done */ if (!dnf_state_done (state_local, error)) return NULL; } /* done */ if (!dnf_state_done (state, error)) return NULL; return g_steal_pointer (&refresh_repos); } static void pk_backend_refresh_cache_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBackend *backend = pk_backend_job_get_backend (job); PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); DnfRepo *repo; DnfState *state_local; gboolean force; gboolean ret; guint i; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) refresh_repos = NULL; g_autoptr(GPtrArray) repos = NULL; g_autofree gchar *max_cache_age = NULL; /* set state */ dnf_state_set_steps (job_data->state, NULL, 1, /* count */ 95, /* download */ 4, /* rebuild SAT */ -1); g_variant_get (params, "(b)", &force); /* kick subscription-manager if it exists */ pk_backend_refresh_subman (job); /* ask the context's repo loader for new repos, forcing it to reload them */ repos = dnf_repo_loader_get_repos (dnf_context_get_repo_loader (job_data->context), &error); if (repos == NULL) { pk_backend_job_error_code (job, error->code, "failed to load repos: %s", error->message); return; } /* figure out which repos need refreshing */ refresh_repos = pk_backend_find_refresh_repos (job, job_data->state, repos, force, &error); if (refresh_repos == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* is everything up to date? */ if (refresh_repos->len == 0) { if (!dnf_state_finished (job_data->state, &error)) pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* refresh each repo */ state_local = dnf_state_get_child (job_data->state); dnf_state_set_number_steps (state_local, refresh_repos->len); max_cache_age = g_strdup_printf ("%u", pk_backend_job_get_cache_age (job)); for (i = 0; i < refresh_repos->len; i++) { gint wait_status; const gchar *refresh[5]; repo = g_ptr_array_index (refresh_repos, i); /* delete content even if up to date */ if (force) { g_debug ("Deleting contents of %s as forced", dnf_repo_get_id (repo)); ret = dnf_repo_clean (repo, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } /* check and download */ refresh[0] = LIBEXECDIR "/packagekit-dnf-refresh-repo"; refresh[1] = max_cache_age; refresh[2] = dnf_repo_get_id (repo); refresh[3] = priv->release_ver; refresh[4] = NULL; if (!g_spawn_sync (NULL, (gchar **) refresh, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL, &wait_status, &error)) pk_backend_job_error_code (job, error->code, "%s", "unkown error in pk_backend_refresh_repo"); /* done */ ret = dnf_state_done (state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } /* done */ ret = dnf_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* invalidate the sack cache after downloading new metadata */ pk_backend_sack_cache_invalidate (backend, "downloaded new metadata"); /* We just downloaded our cache, avoid doing so again */ pk_backend_job_set_cache_age(job, G_MAXUINT); /* regenerate the libsolv metadata */ state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, 0, DNF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ ret = dnf_state_done (job_data->state, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_refresh_cache (PkBackend *backend, PkBackendJob *job, gboolean force) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_refresh_cache_thread, NULL, NULL); } /** * dnf_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 * dnf_utils_find_package_ids (DnfSack *sack, gchar **package_ids, GError **error) { const gchar *reponame; gboolean ret = TRUE; GHashTable *hash; guint i; GPtrArray *pkglist = NULL; DnfPackage *pkg; HyQuery query = NULL; /* run query */ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); query = hy_query_create (sack); for (i = 0; package_ids[i] != NULL; i++) { g_auto(GStrv) split = NULL; 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); /* no matches */ if (pkglist->len == 0) { g_ptr_array_unref (pkglist); continue; } /* multiple matches */ if (pkglist->len > 1) { ret = FALSE; g_set_error (error, DNF_ERROR, PK_ERROR_ENUM_PACKAGE_CONFLICTS, "Multiple matches of %s", package_ids[i]); for (i = 0; i < pkglist->len; i++) { pkg = g_ptr_array_index (pkglist, i); g_debug ("possible matches: %s", dnf_package_get_package_id (pkg)); } goto out; } /* add to results */ pkg = g_ptr_array_index (pkglist, 0); g_hash_table_insert (hash, g_strdup (package_ids[i]), g_object_ref (pkg)); g_ptr_array_unref (pkglist); } out: if (!ret && hash != NULL) { g_hash_table_unref (hash); hash = NULL; } if (query != NULL) hy_query_free (query); return hash; } static void backend_get_details_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; guint i; DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; g_variant_get (params, "(^a&s)", &package_ids); /* set state */ ret = dnf_state_set_steps (job_data->state, NULL, 50, /* add repos */ 49, /* find packages */ 1, /* emit */ -1); g_assert (ret); /* get sack */ filters = dnf_get_filter_for_ids (package_ids); state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* emit details */ for (i = 0; package_ids[i] != NULL; i++) { guint64 download_size; pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) continue; download_size = dnf_package_is_downloaded (pkg) ? 0 : dnf_package_get_downloadsize (pkg); if (pk_backend_job_get_details_with_deps_size (job) && !dnf_package_installed (pkg)) { g_autoptr(DnfReldepList) requires = NULL; requires = dnf_package_get_requires (pkg); if (requires) { g_autoptr(GPtrArray) pkglist_reqs = NULL; HyQuery query = hy_query_create (sack); guint j; hy_query_filter_reldep_in (query, HY_PKG_PROVIDES, requires); pkglist_reqs = hy_query_run (query); for (j = 0; pkglist_reqs && j < pkglist_reqs->len; j++) { DnfPackage *pkg_req = g_ptr_array_index (pkglist_reqs, j); if (pkg_req && !dnf_package_installed (pkg_req) && !dnf_package_is_downloaded (pkg_req)) download_size += dnf_package_get_downloadsize (pkg_req); } hy_query_free (query); } } pk_backend_job_details_full (job, package_ids[i], dnf_package_get_summary (pkg), dnf_package_get_license (pkg), PK_GROUP_ENUM_UNKNOWN, dnf_package_get_description (pkg), dnf_package_get_url (pkg), (gulong) dnf_package_get_installsize (pkg), download_size); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_get_details (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, backend_get_details_thread, NULL, NULL); } static void backend_get_details_local_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; guint i; DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autofree gchar **full_paths = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; 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 = dnf_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 = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* ensure packages are not already installed */ if (!pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ALLOW_REINSTALL)) { for (i = 0; full_paths[i] != NULL; i++) { pkg = dnf_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]); return; } pk_backend_job_details_full (job, dnf_package_get_package_id (pkg), dnf_package_get_summary (pkg), dnf_package_get_license (pkg), PK_GROUP_ENUM_UNKNOWN, dnf_package_get_description (pkg), dnf_package_get_url (pkg), (gulong) dnf_package_get_installsize (pkg), dnf_package_is_downloaded (pkg) ? 0 : dnf_package_get_downloadsize (pkg)); } } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_get_details_local (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, backend_get_details_local_thread, NULL, NULL); } static void backend_get_files_local_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **files_array; guint i; DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autofree gchar **full_paths = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; 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 = dnf_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 = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* ensure packages are not already installed */ for (i = 0; full_paths[i] != NULL; i++) { pkg = dnf_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]); return; } /* sort and list according to name */ files_array = dnf_package_get_files (pkg); pk_backend_job_files (job, dnf_package_get_package_id (pkg), files_array); g_strfreev (files_array); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_get_files_local (PkBackend *backend, PkBackendJob *job, gchar **files) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, backend_get_files_local_thread, NULL, NULL); } static void pk_backend_download_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { const gchar *directory; gboolean ret; gchar *tmp; guint i; DnfRepo *repo; DnfState *state_local; DnfState *state_loop; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; g_autoptr(GPtrArray) files = NULL; g_variant_get (params, "(^a&ss)", &package_ids, &directory); /* set state */ ret = dnf_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); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* get sack */ state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* download packages */ files = g_ptr_array_new_with_free_func (g_free); state_local = dnf_state_get_child (job_data->state); dnf_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]); return; } dnf_emit_package (job, PK_INFO_ENUM_DOWNLOADING, pkg); /* get correct package repo */ repo = dnf_repo_loader_get_repo_by_id (dnf_context_get_repo_loader (job_data->context), dnf_package_get_reponame (pkg), &error); if (repo == NULL) { g_prefix_error (&error, "Not sure where to download %s: ", dnf_package_get_name (pkg)); pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* download */ state_loop = dnf_state_get_child (state_local); tmp = dnf_repo_download_package (repo, pkg, directory, state_loop, &error); if (tmp == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* add to download list */ g_ptr_array_add (files, tmp); /* done */ ret = dnf_state_done (state_local, &error); if (!ret) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } g_ptr_array_add (files, NULL); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* emit files so that the daemon will copy these */ pk_backend_job_files (job, NULL, (gchar **) files->pdata); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_download_packages (PkBackend *backend, PkBackendJob *job, gchar **package_ids, const gchar *directory) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_download_packages_thread, NULL, NULL); } void pk_backend_cancel (PkBackend *backend, PkBackendJob *job) { } static GPtrArray * pk_backend_transaction_check_untrusted_repos (PkBackendJob *job, GError **error) { gboolean ret = TRUE; GPtrArray *array = NULL; guint i; DnfRepo *repo; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); g_autoptr(GPtrArray) install = NULL; /* find any packages in untrusted repos */ install = dnf_goal_get_packages (job_data->goal, DNF_PACKAGE_INFO_INSTALL, DNF_PACKAGE_INFO_REINSTALL, DNF_PACKAGE_INFO_DOWNGRADE, DNF_PACKAGE_INFO_UPDATE, -1); array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); 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 (dnf_package_get_reponame (pkg), HY_CMDLINE_REPO_NAME) == 0) { g_ptr_array_add (array, g_object_ref (pkg)); continue; } /* find repo */ repo = dnf_repo_loader_get_repo_by_id (dnf_context_get_repo_loader (job_data->context), dnf_package_get_reponame (pkg), error); if (repo == NULL) { g_prefix_error (error, "Can't GPG check %s: ", dnf_package_get_name (pkg)); ret = FALSE; goto out; } /* repo has no gpg key */ if (!dnf_repo_get_gpgcheck (repo)) g_ptr_array_add (array, g_object_ref (pkg)); } out: if (array != NULL && !ret) { g_ptr_array_unref (array); array = NULL; } return array; } static gboolean pk_backend_transaction_simulate (PkBackendJob *job, DnfState *state, GError **error) { DnfDb *db; GPtrArray *pkglist; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); gboolean ret; g_autoptr(GPtrArray) untrusted = NULL; /* set state */ ret = dnf_state_set_steps (state, error, 99, /* check for untrusted repos */ 1, /* emit */ -1); if (!ret) return FALSE; /* 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, error); if (untrusted == NULL) return FALSE; /* done */ if (!dnf_state_done (state, error)) return FALSE; /* emit what we're going to do */ db = dnf_transaction_get_db (job_data->transaction); dnf_emit_package_array (job, PK_INFO_ENUM_UNTRUSTED, untrusted); /* remove */ pkglist = hy_goal_list_erasures (job_data->goal, NULL); dnf_db_ensure_origin_pkglist (db, pkglist); dnf_emit_package_list (job, PK_INFO_ENUM_REMOVING, pkglist); g_ptr_array_unref (pkglist); /* install */ pkglist = hy_goal_list_installs (job_data->goal, NULL); dnf_db_ensure_origin_pkglist (db, pkglist); dnf_emit_package_list (job, PK_INFO_ENUM_INSTALLING, pkglist); g_ptr_array_unref (pkglist); /* obsolete */ pkglist = hy_goal_list_obsoleted (job_data->goal, NULL); dnf_emit_package_list (job, PK_INFO_ENUM_OBSOLETING, pkglist); g_ptr_array_unref (pkglist); /* reinstall */ pkglist = hy_goal_list_reinstalls (job_data->goal, NULL); dnf_db_ensure_origin_pkglist (db, pkglist); dnf_emit_package_list (job, PK_INFO_ENUM_REINSTALLING, pkglist); g_ptr_array_unref (pkglist); /* update */ pkglist = hy_goal_list_upgrades (job_data->goal, NULL); dnf_db_ensure_origin_pkglist (db, pkglist); dnf_emit_package_list (job, PK_INFO_ENUM_UPDATING, pkglist); g_ptr_array_unref (pkglist); /* downgrade */ pkglist = hy_goal_list_downgrades (job_data->goal, NULL); dnf_db_ensure_origin_pkglist (db, pkglist); dnf_emit_package_list (job, PK_INFO_ENUM_DOWNGRADING, pkglist); g_ptr_array_unref (pkglist); /* done */ return dnf_state_done (state, error); } static gboolean pk_backend_transaction_download_commit (PkBackendJob *job, DnfState *state, GError **error) { gboolean ret = TRUE; DnfState *state_local; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); /* nothing to download */ if (dnf_transaction_get_remote_pkgs (job_data->transaction)->len == 0) { pk_backend_transaction_inhibit_start (job_data->backend); ret = dnf_transaction_commit (job_data->transaction, job_data->goal, state, error); pk_backend_transaction_inhibit_end (job_data->backend); return ret; } /* set state */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) { ret = dnf_state_set_steps (state, error, 90, /* download */ 10, /* transaction test */ -1); } else { ret = dnf_state_set_steps (state, error, 50, /* download */ 50, /* install/remove */ -1); } if (!ret) return FALSE; /* download */ state_local = dnf_state_get_child (state); g_signal_connect (state_local, "percentage-changed", G_CALLBACK (pk_backend_download_percentage_changed_cb), job); pk_backend_download_percentage_changed_cb (state, 0, job); ret = dnf_transaction_download (job_data->transaction, state_local, error); if (!ret) return FALSE; pk_backend_download_percentage_changed_cb (state, 100, job); /* done */ if (!dnf_state_done (state, error)) return FALSE; /* run transaction */ state_local = dnf_state_get_child (state); pk_backend_transaction_inhibit_start (job_data->backend); ret = dnf_transaction_commit (job_data->transaction, job_data->goal, state_local, error); pk_backend_transaction_inhibit_end (job_data->backend); if (!ret) return FALSE; /* done */ return dnf_state_done (state, error); } static void pk_backend_clean_cached_rpms (PkBackendJob *job, GPtrArray *keep_rpms) { PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); const gchar *cache_dir; g_autoptr(GHashTable) keep_rpms_hash = NULL; g_autoptr(GPtrArray) found_rpms = NULL; /* cache cleanup disabled? */ if (dnf_context_get_keep_cache (job_data->context)) { g_debug ("KeepCache config option set; skipping cached rpms cleanup"); return; } /* create a hash table for fast lookup */ keep_rpms_hash = g_hash_table_new (g_str_hash, g_str_equal); for (guint i = 0; i < keep_rpms->len; i++) { g_hash_table_insert (keep_rpms_hash, g_ptr_array_index (keep_rpms, i), GINT_TO_POINTER (1)); } cache_dir = dnf_context_get_cache_dir (job_data->context); g_assert (cache_dir != NULL); /* find all the rpms in the cache directory */ found_rpms = pk_directory_find_files_with_suffix (cache_dir, ".rpm"); /* remove all cached rpms, except for those in the keep_rpms_hash */ for (guint i = 0; i < found_rpms->len; i++) { const gchar *fn = g_ptr_array_index (found_rpms, i); g_autofree gchar *basename = NULL; basename = g_path_get_basename (fn); if (g_hash_table_contains (keep_rpms_hash, basename)) continue; g_debug ("removing cached rpm: %s", fn); g_assert (g_str_has_prefix (fn, cache_dir)); if (g_unlink (fn) != 0) g_warning ("failed to remove %s", fn); } } static GPtrArray * pk_backend_get_download_rpms (HyGoal goal) { g_autoptr(GPtrArray) download_rpms = g_ptr_array_new_with_free_func (g_free); g_autoptr(GPtrArray) packages = NULL; packages = dnf_goal_get_packages (goal, DNF_PACKAGE_INFO_INSTALL, DNF_PACKAGE_INFO_REINSTALL, DNF_PACKAGE_INFO_DOWNGRADE, DNF_PACKAGE_INFO_UPDATE, -1); for (guint i = 0; i < packages->len; i++) { DnfPackage *pkg = g_ptr_array_index (packages, i); g_autofree gchar *basename = NULL; basename = g_path_get_basename (dnf_package_get_location (pkg)); g_ptr_array_add (download_rpms, g_steal_pointer (&basename)); } return g_steal_pointer (&download_rpms); } static gboolean pk_backend_transaction_run (PkBackendJob *job, DnfState *state, GError **error) { DnfState *state_local; DnfGoalActions dnf_flags = DNF_ALLOW_UNINSTALL; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); gboolean ret = TRUE; /* allow downgrades for all transaction types */ int flags = DNF_TRANSACTION_FLAG_ALLOW_DOWNGRADE; /* set state */ ret = dnf_state_set_steps (state, error, 5, /* depsolve */ 95, /* everything else */ -1); if (!ret) return FALSE; /* depsolve */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED)) flags |= DNF_TRANSACTION_FLAG_ONLY_TRUSTED; if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ALLOW_REINSTALL)) flags |= DNF_TRANSACTION_FLAG_ALLOW_REINSTALL; /* only download packages and run a transaction test */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) flags |= DNF_TRANSACTION_FLAG_TEST; dnf_transaction_set_flags (job_data->transaction, flags); state_local = dnf_state_get_child (state); /* we solve the goal ourselves, so we can deal with flags */ dnf_transaction_set_dont_solve_goal (job_data->transaction, TRUE); if (!dnf_context_get_install_weak_deps ()) dnf_flags |= DNF_IGNORE_WEAK_DEPS; ret = dnf_goal_depsolve (job_data->goal, dnf_flags, error); if (!ret) return FALSE; ret = dnf_transaction_depsolve (job_data->transaction, job_data->goal, state_local, error); if (!ret) return FALSE; /* done */ if (!dnf_state_done (state, error)) return FALSE; /* just simulate */ if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_SIMULATE)) { state_local = dnf_state_get_child (state); ret = pk_backend_transaction_simulate (job, state_local, error); if (!ret) return FALSE; return dnf_state_done (state, error); } /* download and commit transaction */ state_local = dnf_state_get_child (state); ret = pk_backend_transaction_download_commit (job, state_local, error); if (!ret) return FALSE; if (pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) { g_autoptr(GPtrArray) keep_rpms = NULL; /* now that an offline update has been fully downloaded, clean up any leftover * rpms from a previously downloaded (but not installed) offline update */ keep_rpms = pk_backend_get_download_rpms (job_data->goal); pk_backend_clean_cached_rpms (job, keep_rpms); } /* done */ return dnf_state_done (state, error); } static void pk_backend_repo_remove_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfDb *db; DnfRepo *repo; DnfState *state_local; DnfPackage *pkg; GPtrArray *pkglist = NULL; GPtrArray *pkglist_releases = NULL; HyQuery query = NULL; HyQuery query_release = NULL; PkBackendDnfJobData *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; guint cnt = 0; guint i; guint j; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) removed_id = NULL; g_autoptr(GPtrArray) repos = NULL; g_auto(GStrv) search = NULL; g_variant_get (params, "(t&sb)", &job_data->transaction_flags, &repo_id, &autoremove); /* set state */ ret = dnf_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 */ repo = dnf_repo_loader_get_repo_by_id (dnf_context_get_repo_loader (job_data->context), repo_id, &error); if (repo == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } repo_filename = dnf_repo_get_filename (repo); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* ask the context's repo loader for new repos, forcing it to reload them */ repos = dnf_repo_loader_get_repos (dnf_context_get_repo_loader (job_data->context), &error); if (repos == NULL) { pk_backend_job_error_code (job, error->code, "failed to load repos: %s", error->message); goto out; } /* find all the .repo files the repo-release package installed */ search = g_new0 (gchar *, repos->len + 0); removed_id = g_ptr_array_new_with_free_func (g_free); for (i = 0; i < repos->len; i++) { repo = g_ptr_array_index (repos, i); if (g_strcmp0 (dnf_repo_get_filename (repo), repo_filename) != 0) continue; /* this repo_id will get purged */ tmp = dnf_repo_get_id (repo); 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 = dnf_repo_get_filename (repo); 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 */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* remove all the packages installed from all these repos */ state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } job_data->goal = hy_goal_create (sack); query = hy_query_create (sack); pkglist = hy_query_run (query); db = dnf_transaction_get_db (job_data->transaction); for (i = 0; i < pkglist->len; i++) { pkg = g_ptr_array_index (pkglist, i); dnf_db_ensure_origin_pkg (db, pkg); from_repo = dnf_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", dnf_package_get_name (pkg), from_repo); if (autoremove) { hy_goal_erase (job_data->goal, pkg); } break; } } } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); 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_releases = hy_query_run (query_release); for (i = 0; i < pkglist_releases->len; i++) { pkg = g_ptr_array_index (pkglist, i); dnf_db_ensure_origin_pkg (db, pkg); g_debug ("removing %s as installed for repo", dnf_package_get_name (pkg)); hy_goal_erase (job_data->goal, pkg); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } /* run transaction */ state_local = dnf_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); goto out; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); goto out; } out: if (pkglist != NULL) g_ptr_array_unref (pkglist); if (pkglist_releases != NULL) g_ptr_array_unref (pkglist_releases); if (query != NULL) hy_query_free (query); if (query_release != NULL) hy_query_free (query_release); } void pk_backend_repo_remove (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, const gchar *repo_id, gboolean autoremove) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_repo_remove_thread, NULL, NULL); } static gboolean dnf_is_installed_package_id_name_arch (DnfSack *sack, const gchar *package_id) { gboolean ret; GPtrArray *pkglist = NULL; HyQuery query = NULL; g_auto(GStrv) split = 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 = pkglist->len > 0; g_ptr_array_unref (pkglist); hy_query_free (query); return ret; } /** * pk_backend_remove_packages_thread: * * FIXME: Use allow_deps */ static void pk_backend_remove_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean allow_deps; gboolean autoremove; gboolean ret; guint i; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; 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 = dnf_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 (!allow_deps) { pk_backend_job_error_code (job, PK_ERROR_ENUM_NOT_SUPPORTED, "!allow_deps is not supported"); return; } /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_INSTALLED); state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } //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 = dnf_is_installed_package_id_name_arch (sack, package_ids[i]); if (!ret) { g_autofree gchar *printable_tmp = NULL; 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); return; } } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* 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]); return; } if (autoremove) { hy_goal_erase_flags (job_data->goal, pkg, HY_CLEAN_DEPS); } else { hy_goal_erase (job_data->goal, pkg); } } /* run transaction */ state_local = dnf_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); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_remove_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids, gboolean allow_deps, gboolean autoremove) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_remove_packages_thread, NULL, NULL); } static void pk_backend_install_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; guint i; g_autofree enum _hy_comparison_type_e *relations = NULL; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = 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 = dnf_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 = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } relations = g_new0 (enum _hy_comparison_type_e, g_strv_length (package_ids)); /** * HY_EQ - the same version of package is installed -> reinstallation * HY_GT - higher version of package is installed -> update * HY_LT - older version of package is installed -> downgrade * 0 - package is not installed */ /* ensure packages are not already installed */ for (i = 0; package_ids[i] != NULL; i++) { HyQuery query = NULL; DnfPackage *inst_pkg = NULL; DnfPackage *latest = NULL; GPtrArray *pkglist = NULL; guint pli; g_auto(GStrv) split = NULL; split = pk_package_id_split (package_ids[i]); query = hy_query_create (sack); 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); hy_query_free (query); for (pli = 0; pli < pkglist->len; ++pli) { inst_pkg = g_ptr_array_index (pkglist, pli); ret = dnf_sack_evr_cmp (sack, split[PK_PACKAGE_ID_VERSION], dnf_package_get_evr (inst_pkg)); if (relations[i] == 0 && ret > 0) { relations[i] = HY_GT; } else if (relations[i] != HY_EQ && ret < 0) { relations[i] = HY_LT; if (!latest || dnf_package_evr_cmp (latest, inst_pkg) < 0) latest = inst_pkg; } else if (ret == 0) { relations[i] = HY_EQ; break; } } if (relations[i] == HY_EQ && !pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ALLOW_REINSTALL)) { g_autofree gchar *printable_tmp = pk_package_id_to_printable (package_ids[i]); pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_ALREADY_INSTALLED, "%s is already installed", printable_tmp); return; } if (relations[i] == HY_LT && !pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_ALLOW_DOWNGRADE)) { pk_backend_job_error_code (job, PK_ERROR_ENUM_PACKAGE_ALREADY_INSTALLED, "higher version \"%s\" of package %s.%s is already installed", dnf_package_get_evr (latest), split[PK_PACKAGE_ID_NAME], split[PK_PACKAGE_ID_ARCH]); return; } if (relations[i] && relations[i] != HY_EQ && pk_bitfield_contain (job_data->transaction_flags, PK_TRANSACTION_FLAG_ENUM_JUST_REINSTALL)) { pk_backend_job_error_code (job, PK_ERROR_ENUM_NOT_AUTHORIZED, "missing authorization to update or downgrade software"); return; } g_ptr_array_unref (pkglist); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find remote packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* 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]); return; } if (relations[i] == HY_EQ) { dnf_package_set_action (pkg, DNF_STATE_ACTION_REINSTALL); } hy_goal_install (job_data->goal, pkg); } /* run transaction */ state_local = dnf_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); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_install_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_install_packages_thread, NULL, NULL); } static void pk_backend_install_files_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; guint i; g_autofree gchar **full_paths = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; g_autoptr(GPtrArray) array = NULL; 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 = dnf_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 = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_NONE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* ensure packages are not already installed */ array = g_ptr_array_new (); for (i = 0; full_paths[i] != NULL; i++) { pkg = dnf_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]); return; } /* we don't download this, we just use it */ dnf_package_set_filename (pkg, full_paths[i]); g_ptr_array_add (array, pkg); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* 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 = dnf_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); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_install_files (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **full_paths) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_install_files_thread, NULL, NULL); } static void pk_backend_update_packages_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; gboolean ret; guint i; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = 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 = dnf_state_set_steps (job_data->state, NULL, 9, /* add repos */ 1, /* find packages */ 90, /* run transaction */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* set up the sack for packages that should only ever be installed, never updated */ dnf_sack_set_installonly (sack, dnf_context_get_installonly_pkgs (job_data->context)); dnf_sack_set_installonly_limit (sack, dnf_context_get_installonly_limit (job_data->context)); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* 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]); return; } /* allow some packages to have multiple versions installed */ if (dnf_package_is_installonly (pkg)) hy_goal_install (job_data->goal, pkg); else hy_goal_upgrade_to (job_data->goal, pkg); } /* run transaction */ state_local = dnf_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); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_update_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_update_packages_thread, NULL, NULL); } static void pk_backend_upgrade_system_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { DnfState *state_local; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBackendDnfPrivate *priv = pk_backend_get_user_data (job_data->backend); PkBitfield filters; gboolean ret; const gchar *release_ver = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; /* get arguments */ g_variant_get (params, "(t&su)", &job_data->transaction_flags, &release_ver, NULL); pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); pk_backend_job_set_percentage (job, 0); /* create a new context for the passed in release ver */ if (release_ver != NULL) { g_autoptr(DnfContext) context = NULL; context = dnf_context_new (); dnf_context_set_release_ver (context, release_ver); ret = pk_backend_setup_dnf_context (context, priv->conf, release_ver, &error); if (!ret) { g_debug ("failed to setup context: %s", error->message); pk_backend_job_error_code (job, error->code, "%s", error->message); return; } pk_backend_job_set_context (job, context); } /* set state */ ret = dnf_state_set_steps (job_data->state, NULL, 10, /* add repos */ 90, /* run transaction */ -1); g_assert (ret); /* get sack */ filters = pk_bitfield_value (PK_FILTER_ENUM_NOT_INSTALLED); state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* set up the sack for packages that should only ever be installed, never updated */ dnf_sack_set_installonly (sack, dnf_context_get_installonly_pkgs (job_data->context)); /* set the installonly limit one higher than usual to avoid removing any kernels during system upgrades */ dnf_sack_set_installonly_limit (sack, dnf_context_get_installonly_limit (job_data->context) + 1); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* set up the distupgrade goal */ job_data->goal = hy_goal_create (sack); hy_goal_distupgrade_all (job_data->goal); /* run transaction */ state_local = dnf_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); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_upgrade_system (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, const gchar *distro_id, PkUpgradeKindEnum upgrade_kind) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_upgrade_system_thread, NULL, NULL); } 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); } static gint pk_backend_sort_string_cb (const gchar **a, const gchar **b) { return g_strcmp0 (*a, *b); } static void pk_backend_get_files_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; gchar **files_array; guint i; guint j; DnfState *state_local; DnfPackage *pkg; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; /* set state */ ret = dnf_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 = dnf_get_filter_for_ids (package_ids); state_local = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find packages */ hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* 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]); return; } /* sort and list according to name */ files_array = dnf_package_get_files (pkg); if (FALSE) { g_autoptr(GPtrArray) files = NULL; 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); } else { pk_backend_job_files (job, package_ids[i], files_array); } g_strfreev (files_array); } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_get_files (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_get_files_thread, NULL, NULL); } static void pk_backend_get_update_detail_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { gboolean ret; guint i; guint j; DnfState *state_local; DnfPackage *pkg; DnfAdvisory *advisory; GPtrArray *references; PkBackendDnfJobData *job_data = pk_backend_job_get_user_data (job); PkBitfield filters; g_autofree gchar **package_ids = NULL; g_autoptr(DnfSack) sack = NULL; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) hash = NULL; g_autoptr(GHashTable) advisories_hash = NULL; g_autoptr(GPtrArray) update_details_array = NULL; /* set state */ ret = dnf_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 = dnf_state_get_child (job_data->state); sack = dnf_utils_create_sack_for_filters (job, filters, DNF_CREATE_SACK_FLAG_USE_CACHE, state_local, &error); if (sack == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* find remote packages */ g_variant_get (params, "(^a&s)", &package_ids); hash = dnf_utils_find_package_ids (sack, package_ids, &error); if (hash == NULL) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } advisories_hash = pk_backend_dnf_cache_advisories (sack); update_details_array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); /* Build array of details for each */ for (i = 0; package_ids[i] != NULL; i++) { g_autoptr(GPtrArray) vendor_urls = NULL; g_autoptr(GPtrArray) bugzilla_urls = NULL; g_autoptr(GPtrArray) cve_urls = NULL; g_autoptr(PkUpdateDetail) item = NULL; pkg = g_hash_table_lookup (hash, package_ids[i]); if (pkg == NULL) continue; advisory = pk_backend_dnf_get_advisory (advisories_hash, pkg); if (advisory == NULL) continue; references = dnf_advisory_get_references (advisory); vendor_urls = g_ptr_array_new_with_free_func (g_free); bugzilla_urls = g_ptr_array_new_with_free_func (g_free); cve_urls = g_ptr_array_new_with_free_func (g_free); for (j = 0; j < references->len; j++) { DnfAdvisoryRef *reference; DnfAdvisoryRefKind kind; const gchar *url; reference = g_ptr_array_index (references, j); kind = dnf_advisoryref_get_kind (reference); url = dnf_advisoryref_get_url (reference); if (url == NULL) continue; switch (kind) { case DNF_REFERENCE_KIND_VENDOR: g_ptr_array_add (vendor_urls, g_strdup (url)); break; case DNF_REFERENCE_KIND_BUGZILLA: g_ptr_array_add (bugzilla_urls, g_strdup (url)); break; case DNF_REFERENCE_KIND_CVE: g_ptr_array_add (cve_urls, g_strdup (url)); break; default: break; } } g_ptr_array_add (vendor_urls, NULL); g_ptr_array_add (bugzilla_urls, NULL); g_ptr_array_add (cve_urls, NULL); /* form PkUpdateDetail struct */ item = pk_update_detail_new (); g_object_set (item, "package-id", package_ids[i], "updates", NULL, "obsoletes", NULL, "vendor-urls", (gchar **) vendor_urls->pdata, "bugzilla-urls", (gchar **) bugzilla_urls->pdata, "cve-urls", (gchar **) cve_urls->pdata, "restart", PK_RESTART_ENUM_NONE, /* FIXME */ "update-text", dnf_advisory_get_description (advisory), "changelog", NULL, "state", PK_UPDATE_STATE_ENUM_STABLE, /* FIXME */ "issued", NULL, "updated", NULL, NULL); g_ptr_array_add (update_details_array, g_steal_pointer (&item)); g_ptr_array_unref (references); #ifndef HAVE_HY_QUERY_GET_ADVISORY_PKGS dnf_advisory_free (advisory); #endif } /* Emit the signal */ pk_backend_job_update_details (job, update_details_array); /* done */ if (!dnf_state_done (job_data->state, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); return; } } void pk_backend_get_update_detail (PkBackend *backend, PkBackendJob *job, gchar **package_ids) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_get_update_detail_thread, NULL, NULL); } static gboolean pk_backend_repair_remove_rpm_index (const gchar *index_fn, GError **error) { g_autofree gchar *path = NULL; g_autoptr(GFile) file = NULL; path = g_build_filename ("/var/lib/rpm", index_fn, NULL); g_debug ("deleting %s", path); file = g_file_new_for_path (path); return g_file_delete (file, NULL, error); } static void pk_backend_repair_system_thread (PkBackendJob *job, GVariant *params, gpointer user_data) { PkBitfield transaction_flags; const gchar *tmp; gboolean ret; g_autoptr(GDir) dir = NULL; g_autoptr(GError) error = NULL; /* 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)) return; /* 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); return; } /* 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); return; } } } void pk_backend_repair_system (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags) { PkBackendDnfPrivate *priv = pk_backend_get_user_data (backend); g_autoptr(GError) error = NULL; if (!pk_backend_ensure_default_dnf_context (backend, &error)) { pk_backend_job_error_code (job, error->code, "%s", error->message); pk_backend_job_finished (job); return; } pk_backend_job_set_context (job, priv->context); pk_backend_job_thread_create (job, pk_backend_repair_system_thread, NULL, NULL); }