1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2014-2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or(at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * SECTION:dnf-transaction
24 * @short_description: High level interface to librpm.
25 * @include: libdnf.h
26 * @stability: Stable
27 *
28 * This object represents an RPM transaction.
29 */
30
31 #include <rpm/rpmlib.h>
32 #include <rpm/rpmlog.h>
33 #include <rpm/rpmts.h>
34
35 #include "catch-error.hpp"
36 #include "log.hpp"
37 #include "tinyformat/tinyformat.hpp"
38 #include "dnf-context.hpp"
39 #include "dnf-goal.h"
40 #include "dnf-keyring.h"
41 #include "dnf-package.h"
42 #include "dnf-rpmts-private.hpp"
43 #include "dnf-sack.h"
44 #include "dnf-sack-private.hpp"
45 #include "dnf-transaction.h"
46 #include "dnf-types.h"
47 #include "dnf-utils.h"
48 #include "hy-query.h"
49 #include "hy-util-private.hpp"
50 #include "plugin/plugin-private.hpp"
51
52 #include "module/ModulePackageContainer.hpp"
53 #include "transaction/Swdb.hpp"
54 #include "transaction/Transformer.hpp"
55 #include "utils/bgettext/bgettext-lib.h"
56
57 typedef enum {
58 DNF_TRANSACTION_STEP_STARTED,
59 DNF_TRANSACTION_STEP_PREPARING,
60 DNF_TRANSACTION_STEP_WRITING,
61 DNF_TRANSACTION_STEP_IGNORE
62 } DnfTransactionStep;
63
64 typedef struct {
65 rpmKeyring keyring;
66 rpmts ts;
67 DnfContext *context; /* weak reference */
68 GPtrArray *repos;
69 guint uid;
70
71 /* previously in the helper */
72 DnfState *state;
73 DnfState *child;
74 FD_t fd;
75 DnfTransactionStep step;
76 GTimer *timer;
77 guint last_progress;
78 GPtrArray *remove;
79 GPtrArray *remove_helper;
80 GPtrArray *install;
81 GPtrArray *pkgs_to_download;
82 GHashTable *erased_by_package_hash;
83 guint64 flags;
84 gboolean dont_solve_goal;
85 libdnf::Swdb *swdb;
86 } DnfTransactionPrivate;
87
88 G_DEFINE_TYPE_WITH_PRIVATE(DnfTransaction, dnf_transaction, G_TYPE_OBJECT)
89 #define GET_PRIVATE(o) \
90 (static_cast< DnfTransactionPrivate * >(dnf_transaction_get_instance_private(o)))
91
92 /**
93 * @brief Information about running transaction
94 *
95 * The structure is passed to pluginHook() as hookData, when id of hook
96 * is PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION or PLUGIN_HOOK_ID_CONTEXT_TRANSACTION.
97 */
98 struct PluginHookContextTransactionData : public libdnf::PluginHookData {
PluginHookContextTransactionDataPluginHookContextTransactionData99 PluginHookContextTransactionData(PluginHookId hookId, DnfTransaction * transaction,
100 HyGoal goal, DnfState * state)
101 : PluginHookData(hookId), transaction(transaction), goal(goal), state(state) {}
102
103 DnfTransaction * transaction;
104 HyGoal goal;
105 DnfState * state;
106 };
107
108 /**
109 * dnf_transaction_finalize:
110 **/
111 static void
dnf_transaction_finalize(GObject * object)112 dnf_transaction_finalize(GObject *object)
113 {
114 DnfTransaction *transaction = DNF_TRANSACTION(object);
115 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
116
117 g_ptr_array_unref(priv->pkgs_to_download);
118 g_timer_destroy(priv->timer);
119 rpmKeyringFree(priv->keyring);
120 rpmtsFree(priv->ts);
121
122 if (priv->swdb != NULL)
123 delete priv->swdb;
124 if (priv->repos != NULL)
125 g_ptr_array_unref(priv->repos);
126 if (priv->install != NULL)
127 g_ptr_array_unref(priv->install);
128 if (priv->remove != NULL)
129 g_ptr_array_unref(priv->remove);
130 if (priv->remove_helper != NULL)
131 g_ptr_array_unref(priv->remove_helper);
132 if (priv->erased_by_package_hash != NULL)
133 g_hash_table_unref(priv->erased_by_package_hash);
134 if (priv->context != NULL)
135 g_object_remove_weak_pointer(G_OBJECT(priv->context), (void **)&priv->context);
136
137 G_OBJECT_CLASS(dnf_transaction_parent_class)->finalize(object);
138 }
139
140 /**
141 * dnf_transaction_init:
142 **/
143 static void
dnf_transaction_init(DnfTransaction * transaction)144 dnf_transaction_init(DnfTransaction *transaction)
145 {
146 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
147 priv->timer = g_timer_new();
148 priv->pkgs_to_download = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
149 }
150
151 /**
152 * dnf_transaction_class_init:
153 **/
154 static void
dnf_transaction_class_init(DnfTransactionClass * klass)155 dnf_transaction_class_init(DnfTransactionClass *klass)
156 {
157 GObjectClass *object_class = G_OBJECT_CLASS(klass);
158 object_class->finalize = dnf_transaction_finalize;
159 }
160
161 /**
162 * dnf_transaction_get_flags:
163 * @transaction: a #DnfTransaction instance.
164 *
165 * Gets the transaction flags.
166 *
167 * Returns: the transaction flags used for this transaction
168 *
169 * Since: 0.1.0
170 **/
171 guint64
dnf_transaction_get_flags(DnfTransaction * transaction)172 dnf_transaction_get_flags(DnfTransaction *transaction)
173 {
174 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
175 return priv->flags;
176 }
177
178 /**
179 * dnf_transaction_get_remote_pkgs:
180 * @transaction: a #DnfTransaction instance.
181 *
182 * Gets the packages that will be downloaded in dnf_transaction_download().
183 *
184 * The dnf_transaction_depsolve() function must have been called before this
185 * function will return sensible results.
186 *
187 * Returns:(transfer none): the list of packages
188 *
189 * Since: 0.1.0
190 **/
191 GPtrArray *
dnf_transaction_get_remote_pkgs(DnfTransaction * transaction)192 dnf_transaction_get_remote_pkgs(DnfTransaction *transaction)
193 {
194 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
195 return priv->pkgs_to_download;
196 }
197
198 DnfDb *
dnf_transaction_get_db(DnfTransaction * transaction)199 dnf_transaction_get_db(DnfTransaction *transaction)
200 {
201 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
202 return priv->swdb;
203 }
204
205 /**
206 * dnf_transaction_set_repos:
207 * @transaction: a #DnfTransaction instance.
208 * @repos: the repos to use with the transaction
209 *
210 * Sets the list of repos.
211 *
212 * Since: 0.1.0
213 **/
214 void
dnf_transaction_set_repos(DnfTransaction * transaction,GPtrArray * repos)215 dnf_transaction_set_repos(DnfTransaction *transaction, GPtrArray *repos)
216 {
217 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
218 if (priv->repos != NULL)
219 g_ptr_array_unref(priv->repos);
220 priv->repos = g_ptr_array_ref(repos);
221 }
222
223 /**
224 * dnf_transaction_set_uid:
225 * @transaction: a #DnfTransaction instance.
226 * @uid: the uid
227 *
228 * Sets the user ID for the person who started this transaction.
229 *
230 * Since: 0.1.0
231 **/
232 void
dnf_transaction_set_uid(DnfTransaction * transaction,guint uid)233 dnf_transaction_set_uid(DnfTransaction *transaction, guint uid)
234 {
235 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
236 priv->uid = uid;
237 }
238
239 /**
240 * dnf_transaction_set_flags:
241 * @transaction: a #DnfTransaction instance.
242 * @flags: the flags, e.g. %DNF_TRANSACTION_FLAG_ONLY_TRUSTED
243 *
244 * Sets the flags used for this transaction.
245 *
246 * Since: 0.1.0
247 **/
248 void
dnf_transaction_set_flags(DnfTransaction * transaction,guint64 flags)249 dnf_transaction_set_flags(DnfTransaction *transaction, guint64 flags)
250 {
251 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
252 priv->flags = flags;
253 }
254
255 /**
256 * dnf_transaction_set_dont_solve_goal:
257 * @transaction: a #DnfTransaction instance.
258 * @dont_solve_goal: disable/enable calling dnf_goal_depsolve
259 *
260 * Enable/disable calling of dnf_goal_depsolve() in the dnf_transaction_depsolve().
261 *
262 * This function fixes the API problem. The dnf_transaction_depsolve() was allways calling
263 * dnf_goal_depsolve() with hardcoded DnfGoalActions = DNF_ALLOW_UNINSTALL. So, solution
264 * prepared by user was allways replaced.
265 * Default behaviour of existing functions was not changed to be sure of non breaking
266 * of existing API users.
267 *
268 * Since: 0.42.0
269 **/
270 void
dnf_transaction_set_dont_solve_goal(DnfTransaction * transaction,gboolean dont_solve_goal)271 dnf_transaction_set_dont_solve_goal(DnfTransaction *transaction, gboolean dont_solve_goal)
272 {
273 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
274 priv->dont_solve_goal = dont_solve_goal;
275 }
276
277 /**
278 * dnf_transaction_ensure_repo:
279 * @transaction: a #DnfTransaction instance.
280 * @pkg: A #DnfPackage
281 * @error: A #GError or %NULL
282 *
283 * Ensures the #DnfRepo is set on the #DnfPackage *if not already set.
284 *
285 * Returns: %TRUE for success, %FALSE otherwise
286 *
287 * Since: 0.1.0
288 */
289 gboolean
dnf_transaction_ensure_repo(DnfTransaction * transaction,DnfPackage * pkg,GError ** error)290 dnf_transaction_ensure_repo(DnfTransaction *transaction, DnfPackage *pkg, GError **error) try
291 {
292 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
293 guint i;
294
295 /* not set yet */
296 if (priv->repos == NULL) {
297 g_set_error(error,
298 DNF_ERROR,
299 DNF_ERROR_INTERNAL_ERROR,
300 _("Sources not set when trying to ensure package %s"),
301 dnf_package_get_name(pkg));
302 return FALSE;
303 }
304
305 /* this is a local file */
306 if (g_strcmp0(dnf_package_get_reponame(pkg), HY_CMDLINE_REPO_NAME) == 0) {
307 dnf_package_set_filename(pkg, dnf_package_get_location(pkg));
308 return TRUE;
309 }
310
311 /* get repo */
312 if (dnf_package_installed(pkg))
313 return TRUE;
314 for (i = 0; i < priv->repos->len; i++) {
315 auto repo = static_cast< DnfRepo * >(g_ptr_array_index(priv->repos, i));
316 if (g_strcmp0(dnf_package_get_reponame(pkg), dnf_repo_get_id(repo)) == 0) {
317 dnf_package_set_repo(pkg, repo);
318 return TRUE;
319 }
320 }
321
322 /* not found */
323 g_set_error(error,
324 DNF_ERROR,
325 DNF_ERROR_INTERNAL_ERROR,
326 _("Failed to ensure %1$s as repo %2$s not "
327 "found(%3$i repos loaded)"),
328 dnf_package_get_name(pkg),
329 dnf_package_get_reponame(pkg),
330 priv->repos->len);
331 return FALSE;
332 } CATCH_TO_GERROR(FALSE)
333
334 /**
335 * dnf_transaction_ensure_repo_list:
336 * @transaction: a #DnfTransaction instance.
337 * @pkglist: A #GPtrArray *
338 * @error: A #GError or %NULL
339 *
340 * Ensures the #DnfRepo is set on the #GPtrArray *if not already set.
341 *
342 * Returns: %TRUE for success, %FALSE otherwise
343 *
344 * Since: 0.1.0
345 */
346 gboolean
347 dnf_transaction_ensure_repo_list(DnfTransaction *transaction, GPtrArray *pkglist, GError **error) try
348 {
349 for (guint i = 0; i < pkglist->len; i++) {
350 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(pkglist, i));
351 if (!dnf_transaction_ensure_repo(transaction, pkg, error))
352 return FALSE;
353 }
354 return TRUE;
CATCH_TO_GERROR(FALSE)355 } CATCH_TO_GERROR(FALSE)
356
357 gboolean
358 dnf_transaction_gpgcheck_package(DnfTransaction *transaction, DnfPackage *pkg, GError **error) try
359 {
360 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
361 GError *error_local = NULL;
362 DnfRepo *repo;
363 const gchar *fn;
364
365 /* ensure the filename is set */
366 if (!dnf_transaction_ensure_repo(transaction, pkg, error)) {
367 g_prefix_error(error, _("Failed to check untrusted: "));
368 return FALSE;
369 }
370
371 /* find the location of the local file */
372 fn = dnf_package_get_filename(pkg);
373 if (fn == NULL) {
374 g_set_error(error,
375 DNF_ERROR,
376 DNF_ERROR_FILE_NOT_FOUND,
377 _("Downloaded file for %s not found"),
378 dnf_package_get_name(pkg));
379 return FALSE;
380 }
381
382 /* check file */
383 if (!dnf_keyring_check_untrusted_file(priv->keyring, fn, &error_local)) {
384
385 /* probably an i/o error */
386 if (!g_error_matches(error_local, DNF_ERROR, DNF_ERROR_GPG_SIGNATURE_INVALID)) {
387 g_propagate_error(error, error_local);
388 return FALSE;
389 }
390
391 /* if the repo is signed this is ALWAYS an error */
392 repo = dnf_package_get_repo(pkg);
393 if (repo != NULL && dnf_repo_get_gpgcheck(repo)) {
394 g_set_error(error,
395 DNF_ERROR,
396 DNF_ERROR_FILE_INVALID,
397 _("package %1$s cannot be verified "
398 "and repo %2$s is GPG enabled: %3$s"),
399 dnf_package_get_nevra(pkg),
400 dnf_repo_get_id(repo),
401 error_local->message);
402 g_error_free(error_local);
403 return FALSE;
404 }
405
406 /* we can only install signed packages in this mode */
407 if ((priv->flags & DNF_TRANSACTION_FLAG_ONLY_TRUSTED) > 0) {
408 g_propagate_error(error, error_local);
409 return FALSE;
410 } else {
411 g_clear_error(&error_local);
412 }
413 }
414
415 return TRUE;
416 } CATCH_TO_GERROR(FALSE)
417
418 /**
419 * dnf_transaction_check_untrusted:
420 * @transaction: Transaction
421 * @goal: Target goal
422 * @error: Error
423 *
424 * Verify GPG signatures for all pending packages to be changed as part
425 * of @goal.
426 */
427 gboolean
428 dnf_transaction_check_untrusted(DnfTransaction *transaction, HyGoal goal, GError **error) try
429 {
430 guint i;
431 g_autoptr(GPtrArray) install = NULL;
432
433 /* find a list of all the packages we might have to download */
434 install = dnf_goal_get_packages(goal,
435 DNF_PACKAGE_INFO_INSTALL,
436 DNF_PACKAGE_INFO_REINSTALL,
437 DNF_PACKAGE_INFO_DOWNGRADE,
438 DNF_PACKAGE_INFO_UPDATE,
439 -1);
440 if (install->len == 0)
441 return TRUE;
442
443 /* find any packages in untrusted repos */
444 for (i = 0; i < install->len; i++) {
445 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(install, i));
446
447 if (!dnf_transaction_gpgcheck_package(transaction, pkg, error))
448 return FALSE;
449 }
450 return TRUE;
CATCH_TO_GERROR(FALSE)451 } CATCH_TO_GERROR(FALSE)
452
453 /**
454 * dnf_find_pkg_from_header:
455 **/
456 static DnfPackage *
457 dnf_find_pkg_from_header(GPtrArray *array, Header hdr)
458 {
459 const gchar *arch;
460 const gchar *name;
461 const gchar *release;
462 const gchar *version;
463 guint epoch;
464 guint i;
465
466 /* get details */
467 name = headerGetString(hdr, RPMTAG_NAME);
468 epoch = headerGetNumber(hdr, RPMTAG_EPOCH);
469 version = headerGetString(hdr, RPMTAG_VERSION);
470 release = headerGetString(hdr, RPMTAG_RELEASE);
471 arch = headerGetString(hdr, RPMTAG_ARCH);
472
473 /* find in array */
474 for (i = 0; i < array->len; i++) {
475 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(array, i));
476 if (g_strcmp0(name, dnf_package_get_name(pkg)) != 0)
477 continue;
478 if (g_strcmp0(version, dnf_package_get_version(pkg)) != 0)
479 continue;
480 if (g_strcmp0(release, dnf_package_get_release(pkg)) != 0)
481 continue;
482 if (g_strcmp0(arch, dnf_package_get_arch(pkg)) != 0)
483 continue;
484 if (epoch != dnf_package_get_epoch(pkg))
485 continue;
486 return pkg;
487 }
488 return NULL;
489 }
490
491 /**
492 * dnf_find_pkg_from_filename_suffix:
493 **/
494 static DnfPackage *
dnf_find_pkg_from_filename_suffix(GPtrArray * array,const gchar * filename_suffix)495 dnf_find_pkg_from_filename_suffix(GPtrArray *array, const gchar *filename_suffix)
496 {
497 /* find in array */
498 for (guint i = 0; i < array->len; i++) {
499 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(array, i));
500 auto filename = dnf_package_get_filename(pkg);
501 if (filename == NULL)
502 continue;
503 if (g_str_has_suffix(filename, filename_suffix))
504 return pkg;
505 }
506 return NULL;
507 }
508
509 /**
510 * dnf_find_pkg_from_name:
511 **/
512 static DnfPackage *
dnf_find_pkg_from_name(GPtrArray * array,const gchar * pkgname)513 dnf_find_pkg_from_name(GPtrArray *array, const gchar *pkgname)
514 {
515 guint i;
516
517 /* find in array */
518 for (i = 0; i < array->len; i++) {
519 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(array, i));
520 if (g_strcmp0(dnf_package_get_name(pkg), pkgname) == 0)
521 return pkg;
522 }
523 return NULL;
524 }
525
526 static void
_swdb_transaction_item_progress(libdnf::Swdb * swdb,DnfPackage * pkg)527 _swdb_transaction_item_progress(libdnf::Swdb *swdb, DnfPackage *pkg)
528 {
529 if (pkg == NULL) {
530 return;
531 }
532 const char *nevra = dnf_package_get_nevra(pkg);
533 if (nevra == NULL) {
534 return;
535 }
536 swdb->setItemDone(nevra);
537 }
538
539 /**
540 * dnf_transaction_ts_progress_cb:
541 **/
542 static void *
dnf_transaction_ts_progress_cb(const void * arg,const rpmCallbackType what,const rpm_loff_t amount,const rpm_loff_t total,fnpyKey key,void * data)543 dnf_transaction_ts_progress_cb(const void *arg,
544 const rpmCallbackType what,
545 const rpm_loff_t amount,
546 const rpm_loff_t total,
547 fnpyKey key,
548 void *data)
549 {
550 DnfTransaction *transaction = DNF_TRANSACTION(data);
551 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
552 const char *filename = (const char *)key;
553 const gchar *name = NULL;
554 gboolean ret;
555 guint percentage;
556 guint speed;
557 Header hdr = (Header)arg;
558 DnfPackage *pkg = NULL;
559 DnfStateAction action;
560 void *rc = NULL;
561 libdnf::Swdb *swdb = priv->swdb;
562 g_autoptr(GError) error_local = NULL;
563
564 if (hdr != NULL)
565 name = headerGetString(hdr, RPMTAG_NAME);
566 g_debug("phase: %u(%i/%i, %s/%s)",
567 (uint)what,
568 (gint32)amount,
569 (gint32)total,
570 (const gchar *)key,
571 name);
572
573 switch (what) {
574 case RPMCALLBACK_INST_OPEN_FILE:
575
576 /* valid? */
577 if (filename == NULL || filename[0] == '\0')
578 return NULL;
579
580 /* open the file and return file descriptor */
581 priv->fd = Fopen(filename, "r.ufdio");
582 return (void *)priv->fd;
583 break;
584
585 case RPMCALLBACK_INST_CLOSE_FILE:
586
587 /* just close the file */
588 if (priv->fd != NULL) {
589 Fclose(priv->fd);
590 priv->fd = NULL;
591 }
592 break;
593
594 case RPMCALLBACK_INST_START:
595
596 /* find pkg */
597 pkg = dnf_find_pkg_from_filename_suffix(priv->install, filename);
598 if (pkg == NULL)
599 g_assert_not_reached();
600
601 /* map to correct action code */
602 action = dnf_package_get_action(pkg);
603 if (action == DNF_STATE_ACTION_UNKNOWN)
604 action = DNF_STATE_ACTION_INSTALL;
605
606 /* set the pkgid if not already set */
607 if (dnf_package_get_pkgid(pkg) == NULL) {
608 const gchar *pkgid;
609 pkgid = headerGetString(hdr, RPMTAG_SHA1HEADER);
610 if (pkgid != NULL) {
611 g_debug("setting %s pkgid %s", name, pkgid);
612 dnf_package_set_pkgid(pkg, pkgid);
613 }
614 }
615
616 /* install start */
617 priv->step = DNF_TRANSACTION_STEP_WRITING;
618 priv->child = dnf_state_get_child(priv->state);
619 dnf_state_action_start(priv->child, action, dnf_package_get_package_id(pkg));
620 g_debug("install start: %s size=%i", filename, (gint32)total);
621 break;
622
623 case RPMCALLBACK_UNINST_START:
624
625 /* find pkg */
626 pkg = dnf_find_pkg_from_header(priv->remove, hdr);
627 if (pkg == NULL && filename != NULL) {
628 pkg = dnf_find_pkg_from_filename_suffix(priv->remove, filename);
629 }
630 if (pkg == NULL && name != NULL)
631 pkg = dnf_find_pkg_from_name(priv->remove, name);
632 if (pkg == NULL && name != NULL)
633 pkg = dnf_find_pkg_from_name(priv->remove_helper, name);
634 if (pkg == NULL) {
635 g_warning("cannot find %s in uninst-start", name);
636 priv->step = DNF_TRANSACTION_STEP_WRITING;
637 break;
638 }
639
640 /* map to correct action code */
641 action = dnf_package_get_action(pkg);
642 if (action == DNF_STATE_ACTION_UNKNOWN)
643 action = DNF_STATE_ACTION_REMOVE;
644
645 /* remove start */
646 priv->step = DNF_TRANSACTION_STEP_WRITING;
647 priv->child = dnf_state_get_child(priv->state);
648 dnf_state_action_start(priv->child, action, dnf_package_get_package_id(pkg));
649 g_debug("remove start: %s size=%i", filename, (gint32)total);
650 break;
651
652 case RPMCALLBACK_TRANS_PROGRESS:
653 case RPMCALLBACK_INST_PROGRESS:
654
655 /* we're preparing the transaction */
656 if (priv->step == DNF_TRANSACTION_STEP_PREPARING ||
657 priv->step == DNF_TRANSACTION_STEP_IGNORE) {
658 g_debug("ignoring preparing %i / %i", (gint32)amount, (gint32)total);
659 break;
660 }
661
662 /* work out speed */
663 speed = (amount - priv->last_progress) / g_timer_elapsed(priv->timer, NULL);
664 dnf_state_set_speed(priv->state, speed);
665 priv->last_progress = amount;
666 g_timer_reset(priv->timer);
667
668 /* progress */
669 percentage = (100.0f / (gfloat)total) * (gfloat)amount;
670 if (priv->child != NULL)
671 dnf_state_set_percentage(priv->child, percentage);
672
673 /* update UI */
674 pkg = dnf_find_pkg_from_header(priv->install, hdr);
675 if (pkg == NULL) {
676 pkg = dnf_find_pkg_from_filename_suffix(priv->install, filename);
677 }
678 if (pkg == NULL) {
679 g_debug("cannot find %s(%s)", filename, name);
680 break;
681 }
682
683 dnf_state_set_package_progress(
684 priv->state, dnf_package_get_package_id(pkg), DNF_STATE_ACTION_INSTALL, percentage);
685 break;
686
687 case RPMCALLBACK_UNINST_PROGRESS:
688
689 /* we're preparing the transaction */
690 if (priv->step == DNF_TRANSACTION_STEP_PREPARING ||
691 priv->step == DNF_TRANSACTION_STEP_IGNORE) {
692 g_debug("ignoring preparing %i / %i", (gint32)amount, (gint32)total);
693 break;
694 }
695
696 /* progress */
697 percentage = (100.0f / (gfloat)total) * (gfloat)amount;
698 if (priv->child != NULL)
699 dnf_state_set_percentage(priv->child, percentage);
700
701 /* update UI */
702 pkg = dnf_find_pkg_from_header(priv->remove, hdr);
703 if (pkg == NULL && filename != NULL) {
704 pkg = dnf_find_pkg_from_filename_suffix(priv->remove, filename);
705 }
706 if (pkg == NULL && name != NULL)
707 pkg = dnf_find_pkg_from_name(priv->remove, name);
708 if (pkg == NULL && name != NULL)
709 pkg = dnf_find_pkg_from_name(priv->remove_helper, name);
710 if (pkg == NULL) {
711 g_warning("cannot find %s in uninst-progress", name);
712 break;
713 }
714
715 /* map to correct action code */
716 action = dnf_package_get_action(pkg);
717 if (action == DNF_STATE_ACTION_UNKNOWN)
718 action = DNF_STATE_ACTION_REMOVE;
719
720 dnf_state_set_package_progress(
721 priv->state, dnf_package_get_package_id(pkg), action, percentage);
722 break;
723
724 case RPMCALLBACK_TRANS_START:
725
726 /* we setup the state */
727 g_debug("preparing transaction with %i items", (gint32)total);
728 if (priv->step == DNF_TRANSACTION_STEP_IGNORE)
729 break;
730
731 dnf_state_set_number_steps(priv->state, total);
732 priv->step = DNF_TRANSACTION_STEP_PREPARING;
733 break;
734
735 case RPMCALLBACK_TRANS_STOP:
736
737 /* don't do anything */
738 break;
739
740 case RPMCALLBACK_INST_STOP:
741 pkg = dnf_find_pkg_from_header(priv->install, hdr);
742 if (pkg == NULL && filename != NULL) {
743 pkg = dnf_find_pkg_from_filename_suffix(priv->install, filename);
744 }
745
746 // transaction item install complete
747 _swdb_transaction_item_progress(swdb, pkg);
748
749 /* phase complete */
750 ret = dnf_state_done(priv->state, &error_local);
751 if (!ret) {
752 g_warning("state increment failed: %s", error_local->message);
753 }
754 break;
755
756 case RPMCALLBACK_UNINST_STOP:
757
758 pkg = dnf_find_pkg_from_header(priv->remove, hdr);
759 if (pkg == NULL) {
760 pkg = dnf_find_pkg_from_header(priv->remove_helper, hdr);
761 }
762 if (pkg == NULL && filename != NULL) {
763 pkg = dnf_find_pkg_from_filename_suffix(priv->remove, filename);
764 }
765 if (pkg == NULL && name != NULL) {
766 pkg = dnf_find_pkg_from_name(priv->remove, name);
767 }
768 if (pkg == NULL && name != NULL) {
769 pkg = dnf_find_pkg_from_name(priv->remove_helper, name);
770 }
771
772 // transaction item remove complete
773 _swdb_transaction_item_progress(swdb, pkg);
774
775 /* phase complete */
776 ret = dnf_state_done(priv->state, &error_local);
777 if (!ret) {
778 g_warning("state increment failed: %s", error_local->message);
779 }
780 break;
781
782 default:
783 break;
784 }
785 return rc;
786 }
787
788 /**
789 * dnf_rpm_verbosity_string_to_value:
790 **/
791 static gint
dnf_rpm_verbosity_string_to_value(const gchar * value)792 dnf_rpm_verbosity_string_to_value(const gchar *value)
793 {
794 if (g_strcmp0(value, "critical") == 0)
795 return RPMLOG_CRIT;
796 if (g_strcmp0(value, "emergency") == 0)
797 return RPMLOG_EMERG;
798 if (g_strcmp0(value, "error") == 0)
799 return RPMLOG_ERR;
800 if (g_strcmp0(value, "warn") == 0)
801 return RPMLOG_WARNING;
802 if (g_strcmp0(value, "debug") == 0)
803 return RPMLOG_DEBUG;
804 if (g_strcmp0(value, "info") == 0)
805 return RPMLOG_INFO;
806 return RPMLOG_EMERG;
807 }
808
809 /**
810 * dnf_transaction_delete_packages:
811 **/
812 static gboolean
dnf_transaction_delete_packages(DnfTransaction * transaction,DnfState * state,GError ** error)813 dnf_transaction_delete_packages(DnfTransaction *transaction, DnfState *state, GError **error)
814 {
815 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
816 DnfState *state_local;
817 const gchar *cachedir;
818 guint i;
819
820 /* nothing to delete? */
821 if (priv->install->len == 0)
822 return TRUE;
823
824 /* get the cachedir so we only delete packages in the actual
825 * cache, not local-install packages */
826 cachedir = dnf_context_get_cache_dir(priv->context);
827 if (cachedir == NULL) {
828 g_set_error_literal(error,
829 DNF_ERROR,
830 DNF_ERROR_FAILED_CONFIG_PARSING,
831 _("Failed to get value for CacheDir"));
832 return FALSE;
833 }
834
835 /* delete each downloaded file */
836 state_local = dnf_state_get_child(state);
837 dnf_state_set_number_steps(state_local, priv->install->len);
838 for (i = 0; i < priv->install->len; i++) {
839 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(priv->install, i));
840
841 /* don't delete files not in the repo */
842 auto filename = dnf_package_get_filename(pkg);
843 if (g_str_has_prefix(filename, cachedir)) {
844 g_autoptr(GFile) file = NULL;
845 file = g_file_new_for_path(filename);
846 if (!g_file_delete(file, NULL, error))
847 return FALSE;
848 }
849
850 /* done */
851 if (!dnf_state_done(state_local, error))
852 return FALSE;
853 }
854 return TRUE;
855 }
856
857 static int64_t
_get_current_time()858 _get_current_time()
859 {
860 g_autoptr(GDateTime) t_struct = g_date_time_new_now_utc();
861 return g_date_time_to_unix(t_struct);
862 }
863
864 /**
865 * We've used dnf_package_set_pkgid() when running the transaction so we can
866 * avoid the lookup in the rpmdb.
867 **/
868 static void
_history_write_item(DnfPackage * pkg,libdnf::Swdb * swdb,libdnf::TransactionItemAction action)869 _history_write_item(DnfPackage *pkg, libdnf::Swdb *swdb, libdnf::TransactionItemAction action)
870 {
871 auto rpm = swdb->createRPMItem();
872 rpm->setName(dnf_package_get_name(pkg));
873 rpm->setEpoch(dnf_package_get_epoch(pkg));
874 rpm->setVersion(dnf_package_get_version(pkg));
875 rpm->setRelease(dnf_package_get_release(pkg));
876 rpm->setArch(dnf_package_get_arch(pkg));
877 rpm->save();
878
879 libdnf::TransactionItemReason reason =
880 swdb->resolveRPMTransactionItemReason(rpm->getName(), rpm->getArch(), -2);
881
882 auto transItem = swdb->addItem(
883 std::dynamic_pointer_cast< libdnf::Item >(rpm), dnf_package_get_reponame(pkg), action, reason);
884 }
885
886 static gboolean
dnf_transaction_check_free_space(DnfTransaction * transaction,GError ** error)887 dnf_transaction_check_free_space(DnfTransaction *transaction, GError **error)
888 {
889 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
890 const gchar *cachedir;
891 guint64 download_size;
892 guint64 free_space;
893 g_autoptr(GFile) file = NULL;
894 g_autoptr(GFileInfo) filesystem_info = NULL;
895
896 download_size = dnf_package_array_get_download_size(priv->pkgs_to_download);
897
898 cachedir = dnf_context_get_cache_dir(priv->context);
899 if (cachedir == NULL) {
900 g_set_error_literal(error,
901 DNF_ERROR,
902 DNF_ERROR_FAILED_CONFIG_PARSING,
903 _("Failed to get value for CacheDir"));
904 return FALSE;
905 }
906
907 file = g_file_new_for_path(cachedir);
908 filesystem_info =
909 g_file_query_filesystem_info(file, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, error);
910 if (filesystem_info == NULL) {
911 g_prefix_error(error, _("Failed to get filesystem free size for %s: "), cachedir);
912 return FALSE;
913 }
914
915 if (!g_file_info_has_attribute(filesystem_info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE)) {
916 g_set_error(error,
917 DNF_ERROR,
918 DNF_ERROR_FAILED,
919 _("Failed to get filesystem free size for %s"),
920 cachedir);
921 return FALSE;
922 }
923
924 free_space =
925 g_file_info_get_attribute_uint64(filesystem_info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
926 if (free_space < download_size) {
927 g_autofree gchar *formatted_download_size = NULL;
928 g_autofree gchar *formatted_free_size = NULL;
929
930 formatted_download_size = g_format_size(download_size);
931 formatted_free_size = g_format_size(free_space);
932 g_set_error(error,
933 DNF_ERROR,
934 DNF_ERROR_NO_SPACE,
935 _("Not enough free space in %1$s: needed %2$s, available %3$s"),
936 cachedir,
937 formatted_download_size,
938 formatted_free_size);
939 return FALSE;
940 }
941
942 return TRUE;
943 }
944
945 /**
946 * dnf_transaction_download:
947 * @transaction: a #DnfTransaction instance.
948 * @state: A #DnfState
949 * @error: A #GError or %NULL
950 *
951 * Downloads all the packages needed for a transaction.
952 *
953 * Returns: %TRUE for success, %FALSE otherwise
954 *
955 * Since: 0.1.0
956 **/
957 gboolean
dnf_transaction_download(DnfTransaction * transaction,DnfState * state,GError ** error)958 dnf_transaction_download(DnfTransaction *transaction, DnfState *state, GError **error) try
959 {
960 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
961
962 /* check that we have enough free space */
963 if (!dnf_transaction_check_free_space(transaction, error))
964 return FALSE;
965
966 /* just download the list */
967 return dnf_package_array_download(priv->pkgs_to_download, NULL, state, error);
968 } CATCH_TO_GERROR(FALSE)
969
970 /**
971 * dnf_transaction_depsolve:
972 * @transaction: a #DnfTransaction instance.
973 * @goal: A #HyGoal
974 * @state: A #DnfState
975 * @error: A #GError or %NULL
976 *
977 * Depsolves the transaction.
978 *
979 * Returns: %TRUE for success, %FALSE otherwise
980 *
981 * Since: 0.1.0
982 **/
983 gboolean
984 dnf_transaction_depsolve(DnfTransaction *transaction, HyGoal goal, DnfState *state, GError **error) try
985 {
986 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
987 gboolean valid;
988 g_autoptr(GPtrArray) packages = NULL;
989
990 /* depsolve */
991 if (!priv->dont_solve_goal) {
992 if (!dnf_goal_depsolve(goal, DNF_ALLOW_UNINSTALL, error)) {
993 return FALSE;
994 }
995 }
996
997 /* find a list of all the packages we have to download */
998 g_ptr_array_set_size(priv->pkgs_to_download, 0);
999 packages = dnf_goal_get_packages(goal,
1000 DNF_PACKAGE_INFO_INSTALL,
1001 DNF_PACKAGE_INFO_REINSTALL,
1002 DNF_PACKAGE_INFO_DOWNGRADE,
1003 DNF_PACKAGE_INFO_UPDATE,
1004 -1);
1005 g_debug("Goal has %u packages", packages->len);
1006 for (guint i = 0; i < packages->len; i++) {
1007 auto pkg = static_cast< DnfPackage * >(g_ptr_array_index(packages, i));
1008
1009 /* get correct package repo */
1010 if (!dnf_transaction_ensure_repo(transaction, pkg, error))
1011 return FALSE;
1012
1013 /* this is a local file */
1014 if (g_strcmp0(dnf_package_get_reponame(pkg), HY_CMDLINE_REPO_NAME) == 0) {
1015 continue;
1016 }
1017
1018 /* check package exists and checksum is okay */
1019 if (!dnf_package_check_filename(pkg, &valid, error))
1020 return FALSE;
1021
1022 /* package needs to be downloaded */
1023 if (!valid) {
1024 g_ptr_array_add(priv->pkgs_to_download, g_object_ref(pkg));
1025 }
1026 }
1027 return TRUE;
CATCH_TO_GERROR(FALSE)1028 } CATCH_TO_GERROR(FALSE)
1029
1030 /**
1031 * dnf_transaction_reset:
1032 */
1033 static void
1034 dnf_transaction_reset(DnfTransaction *transaction)
1035 {
1036 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
1037
1038 /* reset */
1039 priv->child = NULL;
1040 g_ptr_array_set_size(priv->pkgs_to_download, 0);
1041 rpmtsEmpty(priv->ts);
1042 rpmtsSetNotifyCallback(priv->ts, NULL, NULL);
1043
1044 /* clear */
1045 if (priv->install != NULL) {
1046 g_ptr_array_unref(priv->install);
1047 priv->install = NULL;
1048 }
1049 if (priv->remove != NULL) {
1050 g_ptr_array_unref(priv->remove);
1051 priv->remove = NULL;
1052 }
1053 if (priv->remove_helper != NULL) {
1054 g_ptr_array_unref(priv->remove_helper);
1055 priv->remove_helper = NULL;
1056 }
1057 if (priv->erased_by_package_hash != NULL) {
1058 g_hash_table_unref(priv->erased_by_package_hash);
1059 priv->erased_by_package_hash = NULL;
1060 }
1061 }
1062
1063 /**
1064 * dnf_transaction_import_keys:
1065 * @transaction: a #DnfTransaction instance.
1066 * @error: A #GError or %NULL
1067 *
1068 * Imports all keys from /etc/pki/rpm-gpg as well as any
1069 * downloaded per-repo keys. Note this is called automatically
1070 * by dnf_transaction_commit().
1071 **/
1072 gboolean
dnf_transaction_import_keys(DnfTransaction * transaction,GError ** error)1073 dnf_transaction_import_keys(DnfTransaction *transaction, GError **error) try
1074 {
1075 guint i;
1076
1077 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
1078 /* import all system wide GPG keys */
1079 if (!dnf_keyring_add_public_keys(priv->keyring, error))
1080 return FALSE;
1081
1082 /* import downloaded repo GPG keys */
1083 for (i = 0; i < priv->repos->len; i++) {
1084 auto repo = static_cast< DnfRepo * >(g_ptr_array_index(priv->repos, i));
1085 g_auto(GStrv) pubkeys = dnf_repo_get_public_keys(repo);
1086
1087 /* does this file actually exist */
1088 for (char **iter = pubkeys; iter && *iter; iter++) {
1089 const char *pubkey = *iter;
1090 if (g_file_test(pubkey, G_FILE_TEST_EXISTS)) {
1091 /* import */
1092 if (!dnf_keyring_add_public_key(priv->keyring, pubkey, error))
1093 return FALSE;
1094 }
1095 }
1096 }
1097
1098 return TRUE;
1099 } CATCH_TO_GERROR(FALSE)
1100
1101 /**
1102 * dnf_transaction_commit:
1103 * @transaction: a #DnfTransaction instance.
1104 * @goal: A #HyGoal
1105 * @state: A #DnfState
1106 * @error: A #GError or %NULL
1107 *
1108 * Commits a transaction by installing and removing packages.
1109 *
1110 * NOTE: If this fails, you need to call dnf_transaction_depsolve() again.
1111 *
1112 * Returns: %TRUE for success, %FALSE otherwise
1113 *
1114 * Since: 0.1.0
1115 **/
1116 gboolean
1117 dnf_transaction_commit(DnfTransaction *transaction, HyGoal goal, DnfState *state, GError **error) try
1118 {
1119 const gchar *filename;
1120 const gchar *tmp;
1121 gboolean allow_untrusted;
1122 gboolean is_update;
1123 gboolean ret = FALSE;
1124 gint rc;
1125 gint verbosity;
1126 guint i;
1127 guint j;
1128 DnfState *state_local;
1129 GPtrArray *all_obsoleted;
1130 GPtrArray *pkglist;
1131 DnfPackage *pkg;
1132 DnfPackage *pkg_tmp;
1133 rpmprobFilterFlags problems_filter = 0;
1134 rpmtransFlags rpmts_flags = RPMTRANS_FLAG_NONE;
1135 DnfTransactionPrivate *priv = GET_PRIVATE(transaction);
1136 libdnf::Swdb *swdb = priv->swdb;
1137 PluginHookContextTransactionData data{PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION, transaction, goal, state};
1138 DnfSack * sack = hy_goal_get_sack(goal);
1139 DnfSack * rpmdb_version_sack = NULL;
1140 std::string rpmdb_begin;
1141 std::string rpmdb_end;
1142
1143 /* take lock */
1144 ret = dnf_state_take_lock(state, DNF_LOCK_TYPE_RPMDB, DNF_LOCK_MODE_PROCESS, error);
1145 if (!ret)
1146 goto out;
1147
1148 /* set state */
1149 if (priv->flags & DNF_TRANSACTION_FLAG_TEST) {
1150 ret = dnf_state_set_steps(state,
1151 error,
1152 2, /* install */
1153 2, /* remove */
1154 10, /* test-commit */
1155 86, /* commit */
1156 -1);
1157 } else {
1158 ret = dnf_state_set_steps(state,
1159 error,
1160 2, /* install */
1161 2, /* remove */
1162 10, /* test-commit */
1163 83, /* commit */
1164 1, /* write yumDB */
1165 2, /* delete files */
1166 -1);
1167 }
1168 if (!ret)
1169 goto out;
1170
1171 ret = dnf_transaction_import_keys(transaction, error);
1172 if (!ret)
1173 goto out;
1174
1175 /* find any packages without valid GPG signatures */
1176 ret = dnf_transaction_check_untrusted(transaction, goal, error);
1177 if (!ret)
1178 goto out;
1179
1180 // initialize SWDB transaction
1181 swdb->initTransaction();
1182
1183 dnf_state_action_start(state, DNF_STATE_ACTION_REQUEST, NULL);
1184
1185 /* get verbosity from the config file */
1186 tmp = dnf_context_get_rpm_verbosity(priv->context);
1187 verbosity = dnf_rpm_verbosity_string_to_value(tmp);
1188 rpmSetVerbosity(verbosity);
1189
1190 /* setup the transaction */
1191 tmp = dnf_context_get_install_root(priv->context);
1192 rc = rpmtsSetRootDir(priv->ts, tmp);
1193 if (rc < 0) {
1194 ret = FALSE;
1195 g_set_error_literal(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, _("failed to set root"));
1196 goto out;
1197 }
1198 rpmtsSetNotifyCallback(priv->ts, dnf_transaction_ts_progress_cb, transaction);
1199
1200 /* add things to install */
1201 state_local = dnf_state_get_child(state);
1202 priv->install = dnf_goal_get_packages(goal,
1203 DNF_PACKAGE_INFO_INSTALL,
1204 DNF_PACKAGE_INFO_REINSTALL,
1205 DNF_PACKAGE_INFO_DOWNGRADE,
1206 DNF_PACKAGE_INFO_UPDATE,
1207 -1);
1208 if (priv->install->len > 0)
1209 dnf_state_set_number_steps(state_local, priv->install->len);
1210 for (i = 0; i < priv->install->len; i++) {
1211
1212 pkg = static_cast< DnfPackage * >(g_ptr_array_index(priv->install, i));
1213 ret = dnf_transaction_ensure_repo(transaction, pkg, error);
1214 if (!ret)
1215 goto out;
1216
1217 DnfStateAction action = dnf_package_get_action(pkg);
1218
1219 /* add the install */
1220 filename = dnf_package_get_filename(pkg);
1221 allow_untrusted = (priv->flags & DNF_TRANSACTION_FLAG_ONLY_TRUSTED) == 0;
1222 is_update = action == DNF_STATE_ACTION_UPDATE || action == DNF_STATE_ACTION_DOWNGRADE;
1223 ret = dnf_rpmts_add_install_filename2(
1224 priv->ts, filename, allow_untrusted, is_update, pkg, error);
1225 if (!ret)
1226 goto out;
1227
1228 // resolve swdb reason
1229 libdnf::TransactionItemAction swdbAction = libdnf::TransactionItemAction::INSTALL;
1230 if (action == DNF_STATE_ACTION_UPDATE) {
1231 swdbAction = libdnf::TransactionItemAction::UPGRADE;
1232 } else if (action == DNF_STATE_ACTION_DOWNGRADE) {
1233 swdbAction = libdnf::TransactionItemAction::DOWNGRADE;
1234 } else if (action == DNF_STATE_ACTION_REINSTALL) {
1235 swdbAction = libdnf::TransactionItemAction::REINSTALL;
1236 }
1237
1238 // add item to swdb transaction
1239 _history_write_item(pkg, swdb, swdbAction);
1240
1241 /* this section done */
1242 ret = dnf_state_done(state_local, error);
1243 if (!ret)
1244 goto out;
1245 }
1246
1247 /* this section done */
1248 ret = dnf_state_done(state, error);
1249 if (!ret)
1250 goto out;
1251
1252 /* add things to remove */
1253 priv->remove =
1254 dnf_goal_get_packages(goal, DNF_PACKAGE_INFO_OBSOLETE, DNF_PACKAGE_INFO_REMOVE, -1);
1255 for (i = 0; i < priv->remove->len; i++) {
1256 pkg = static_cast< DnfPackage * >(g_ptr_array_index(priv->remove, i));
1257 ret = dnf_rpmts_add_remove_pkg(priv->ts, pkg, error);
1258 if (!ret)
1259 goto out;
1260
1261 /* pre-get the pkgid, as this isn't possible to get after
1262 * the sack is invalidated */
1263 if (dnf_package_get_pkgid(pkg) == NULL) {
1264 g_warning("failed to pre-get pkgid for %s", dnf_package_get_package_id(pkg));
1265 }
1266
1267 libdnf::TransactionItemAction swdbAction = libdnf::TransactionItemAction::REMOVE;
1268
1269 /* are the things being removed actually being upgraded */
1270 pkg_tmp = dnf_find_pkg_from_name(priv->install, dnf_package_get_name(pkg));
1271 if (pkg_tmp != NULL) {
1272 dnf_package_set_action(pkg, DNF_STATE_ACTION_CLEANUP);
1273 if (dnf_package_evr_cmp(pkg, pkg_tmp)) {
1274 swdbAction = libdnf::TransactionItemAction::UPGRADED;
1275 } else {
1276 swdbAction = libdnf::TransactionItemAction::DOWNGRADED;
1277 }
1278 }
1279 _history_write_item(pkg, swdb, swdbAction);
1280 }
1281
1282 /* add anything that gets obsoleted to a helper array which is used to
1283 * map removed packages auto-added by rpm to actual DnfPackage's */
1284 priv->remove_helper = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
1285 for (i = 0; i < priv->install->len; i++) {
1286 pkg = static_cast< DnfPackage * >(g_ptr_array_index(priv->install, i));
1287 is_update = dnf_package_get_action(pkg) == DNF_STATE_ACTION_UPDATE ||
1288 dnf_package_get_action(pkg) == DNF_STATE_ACTION_DOWNGRADE;
1289 if (!is_update)
1290 continue;
1291 pkglist = hy_goal_list_obsoleted_by_package(goal, pkg);
1292
1293 const char *pkg_name = dnf_package_get_name(pkg);
1294
1295 for (j = 0; j < pkglist->len; j++) {
1296 pkg_tmp = static_cast< DnfPackage * >(g_ptr_array_index(pkglist, j));
1297 g_ptr_array_add(priv->remove_helper, g_object_ref(pkg_tmp));
1298 dnf_package_set_action(pkg_tmp, DNF_STATE_ACTION_CLEANUP);
1299
1300 const char *pkg_tmp_name = dnf_package_get_name(pkg_tmp);
1301
1302 if (dnf_find_pkg_from_name(priv->remove, pkg_tmp_name) != NULL) {
1303 // package is already in remove set - skip resolution
1304 continue;
1305 }
1306
1307 libdnf::TransactionItemAction swdbAction = libdnf::TransactionItemAction::OBSOLETED;
1308 if (g_strcmp0(pkg_name, pkg_tmp_name) == 0) {
1309 // names are identical - package is upgraded/downgraded
1310 if (dnf_package_evr_cmp(pkg, pkg_tmp)) {
1311 swdbAction = libdnf::TransactionItemAction::UPGRADED;
1312 } else {
1313 swdbAction = libdnf::TransactionItemAction::DOWNGRADED;
1314 }
1315 }
1316
1317 if (swdbAction == libdnf::TransactionItemAction::OBSOLETED
1318 && dnf_find_pkg_from_name(priv->install, pkg_tmp_name) != NULL
1319 && g_strcmp0(pkg_name, pkg_tmp_name) != 0) {
1320 // If a package is obsoleted and there's a package with the same name
1321 // in the install set, skip recording the obsolete in the history db
1322 // because the package upgrade prevails over the obsolete.
1323 //
1324 // Example:
1325 // grub2-tools-efi obsoletes grub2-tools # skip as grub2-tools is also upgraded
1326 // grub2-tools upgrades grub2-tools
1327 continue;
1328 }
1329
1330 // TODO SWDB add pkg_tmp replaced_by pkg
1331 _history_write_item(pkg_tmp, swdb, swdbAction);
1332 }
1333 g_ptr_array_unref(pkglist);
1334 }
1335
1336 /* this section done */
1337 ret = dnf_state_done(state, error);
1338 if (!ret)
1339 goto out;
1340
1341 /* map updated packages to their previous versions */
1342 priv->erased_by_package_hash =
1343 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref);
1344 all_obsoleted = hy_goal_list_obsoleted(goal, NULL);
1345 for (i = 0; i < priv->install->len; i++) {
1346 pkg = static_cast< DnfPackage * >(g_ptr_array_index(priv->install, i));
1347 if (dnf_package_get_action(pkg) != DNF_STATE_ACTION_UPDATE &&
1348 dnf_package_get_action(pkg) != DNF_STATE_ACTION_DOWNGRADE &&
1349 dnf_package_get_action(pkg) != DNF_STATE_ACTION_REINSTALL)
1350 continue;
1351
1352 pkglist = hy_goal_list_obsoleted_by_package(goal, pkg);
1353 for (j = 0; j < pkglist->len; j++) {
1354 pkg_tmp = static_cast< DnfPackage * >(g_ptr_array_index(pkglist, j));
1355 if (!hy_packagelist_has(all_obsoleted, pkg_tmp)) {
1356 g_hash_table_insert(priv->erased_by_package_hash,
1357 g_strdup(dnf_package_get_package_id(pkg)),
1358 g_object_ref(pkg_tmp));
1359 }
1360 }
1361 g_ptr_array_unref(pkglist);
1362 }
1363 g_ptr_array_unref(all_obsoleted);
1364
1365 /* generate ordering for the transaction */
1366 rpmtsOrder(priv->ts);
1367
1368 /* run the test transaction */
1369 if (dnf_context_get_check_transaction(priv->context)) {
1370 g_debug("running test transaction");
1371 dnf_state_action_start(state, DNF_STATE_ACTION_TEST_COMMIT, NULL);
1372 priv->state = dnf_state_get_child(state);
1373 priv->step = DNF_TRANSACTION_STEP_IGNORE;
1374 /* the output value of rpmtsCheck is not meaningful */
1375 rpmtsCheck(priv->ts);
1376 dnf_state_action_stop(state);
1377 ret = dnf_rpmts_look_for_problems(priv->ts, error);
1378 if (!ret)
1379 goto out;
1380 }
1381
1382 /* this section done */
1383 ret = dnf_state_done(state, error);
1384 if (!ret)
1385 goto out;
1386
1387 /* no signature checking, we've handled that already */
1388 rpmtsSetVSFlags(priv->ts, _RPMVSF_NOSIGNATURES | _RPMVSF_NODIGESTS);
1389
1390 /* filter diskspace */
1391 if (!dnf_context_get_check_disk_space(priv->context))
1392 problems_filter |= RPMPROB_FILTER_DISKSPACE;
1393 if (priv->flags & DNF_TRANSACTION_FLAG_ALLOW_REINSTALL)
1394 problems_filter |= RPMPROB_FILTER_REPLACEPKG;
1395 if (priv->flags & DNF_TRANSACTION_FLAG_ALLOW_DOWNGRADE)
1396 problems_filter |= RPMPROB_FILTER_OLDPACKAGE;
1397
1398 if (priv->flags & DNF_TRANSACTION_FLAG_NODOCS)
1399 rpmts_flags |= RPMTRANS_FLAG_NODOCS;
1400
1401 if (priv->flags & DNF_TRANSACTION_FLAG_TEST) {
1402 /* run the transaction in test mode */
1403 rpmts_flags |= RPMTRANS_FLAG_TEST;
1404
1405 priv->state = dnf_state_get_child(state);
1406 priv->step = DNF_TRANSACTION_STEP_IGNORE;
1407 rpmtsSetFlags(priv->ts, rpmts_flags);
1408 g_debug("Running transaction in test mode");
1409 dnf_state_set_allow_cancel(state, FALSE);
1410 rc = rpmtsRun(priv->ts, NULL, problems_filter);
1411 if (rc < 0) {
1412 ret = FALSE;
1413 g_set_error(error,
1414 DNF_ERROR,
1415 DNF_ERROR_INTERNAL_ERROR,
1416 _("Error %i running transaction test"),
1417 rc);
1418 goto out;
1419 }
1420 if (rc > 0) {
1421 ret = dnf_rpmts_look_for_problems(priv->ts, error);
1422 if (!ret)
1423 goto out;
1424 }
1425
1426 /* transaction test done; return */
1427 ret = dnf_state_done(state, error);
1428 goto out;
1429 }
1430
1431 if (!dnf_context_plugin_hook(priv->context, PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION, &data, nullptr))
1432 goto out;
1433
1434 // FIXME get commandline
1435 if (sack) {
1436 rpmdb_begin = dnf_sack_get_rpmdb_version(sack);
1437 } else {
1438 // if sack is not available, create a custom instance
1439 rpmdb_version_sack = dnf_sack_new();
1440 dnf_sack_load_system_repo(rpmdb_version_sack, nullptr, DNF_SACK_LOAD_FLAG_NONE, nullptr);
1441 rpmdb_begin = dnf_sack_get_rpmdb_version(rpmdb_version_sack);
1442 g_object_unref(rpmdb_version_sack);
1443 }
1444 swdb->beginTransaction(_get_current_time(), rpmdb_begin, "", priv->uid);
1445
1446 /* run the transaction */
1447 priv->state = dnf_state_get_child(state);
1448 priv->step = DNF_TRANSACTION_STEP_STARTED;
1449 rpmtsSetFlags(priv->ts, rpmts_flags);
1450 g_debug("Running actual transaction");
1451 dnf_state_set_allow_cancel(state, FALSE);
1452 rc = rpmtsRun(priv->ts, NULL, problems_filter);
1453 if (rc < 0) {
1454 ret = FALSE;
1455 g_set_error(
1456 error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, _("Error %i running transaction"), rc);
1457 goto out;
1458 }
1459 if (rc > 0) {
1460 ret = dnf_rpmts_look_for_problems(priv->ts, error);
1461 if (!ret)
1462 goto out;
1463 }
1464
1465 /* hmm, nothing was done... */
1466 if (priv->step != DNF_TRANSACTION_STEP_WRITING) {
1467 if (priv->install->len > 0 || priv->remove->len > 0) {
1468 ret = FALSE;
1469 g_set_error(error,
1470 DNF_ERROR,
1471 DNF_ERROR_INTERNAL_ERROR,
1472 _("Transaction did not go to writing phase, "
1473 "but returned no error(%i)"),
1474 priv->step);
1475 goto out;
1476 }
1477 }
1478
1479 /* this section done */
1480 ret = dnf_state_done(state, error);
1481 if (!ret)
1482 goto out;
1483
1484 // finalize swdb transaction
1485 // always load a new sack with rpmdb state after the transaction
1486 rpmdb_version_sack = dnf_sack_new();
1487 dnf_sack_load_system_repo(rpmdb_version_sack, nullptr, DNF_SACK_LOAD_FLAG_NONE, nullptr);
1488 rpmdb_end = dnf_sack_get_rpmdb_version(rpmdb_version_sack);
1489 g_object_unref(rpmdb_version_sack);
1490
1491 swdb->endTransaction(_get_current_time(), rpmdb_end.c_str(), libdnf::TransactionState::DONE);
1492 swdb->closeTransaction();
1493
1494 data.hookId = PLUGIN_HOOK_ID_CONTEXT_TRANSACTION;
1495 if (!dnf_context_plugin_hook(priv->context, PLUGIN_HOOK_ID_CONTEXT_TRANSACTION, &data, nullptr))
1496 goto out;
1497
1498 /* this section done */
1499 ret = dnf_state_done(state, error);
1500 if (!ret)
1501 goto out;
1502
1503 /* remove the files we downloaded */
1504 if (!dnf_context_get_keep_cache(priv->context)) {
1505 state_local = dnf_state_get_child(state);
1506 ret = dnf_transaction_delete_packages(transaction, state_local, error);
1507 if (!ret)
1508 goto out;
1509 }
1510
1511 if (sack) {
1512 if (auto moduleContainer = dnf_sack_get_module_container(sack)) {
1513 moduleContainer->save();
1514 moduleContainer->updateFailSafeData();
1515 }
1516 }
1517
1518 /* all sacks are invalid now */
1519 dnf_context_invalidate_full(priv->context,
1520 "transaction performed",
1521 DNF_CONTEXT_INVALIDATE_FLAG_RPMDB |
1522 DNF_CONTEXT_INVALIDATE_FLAG_ENROLLMENT);
1523
1524 /* this section done */
1525 ret = dnf_state_done(state, error);
1526 out:
1527 dnf_transaction_reset(transaction);
1528 dnf_state_release_locks(state);
1529 return ret;
CATCH_TO_GERROR(FALSE)1530 } CATCH_TO_GERROR(FALSE)
1531
1532 /**
1533 * dnf_transaction_new:
1534 * @context: a #DnfContext instance.
1535 *
1536 * Creates a new #DnfTransaction.
1537 *
1538 * Returns:(transfer full): a #DnfTransaction
1539 *
1540 * Since: 0.1.0
1541 **/
1542 DnfTransaction *
1543 dnf_transaction_new(DnfContext *context)
1544 {
1545 auto transaction = DNF_TRANSACTION(g_object_new(DNF_TYPE_TRANSACTION, NULL));
1546 auto priv = GET_PRIVATE(transaction);
1547 auto install_root = dnf_context_get_install_root(context);
1548 std::string dbPath;
1549 if (dnf_context_get_write_history(context)) {
1550 gchar *tmp_path = g_build_filename(install_root, libdnf::Swdb::defaultPath, NULL);
1551 dbPath = std::string(tmp_path);
1552 g_free(tmp_path);
1553 } else {
1554 dbPath = ":memory:";
1555 }
1556 priv->swdb = new libdnf::Swdb(dbPath);
1557 priv->context = context;
1558 g_object_add_weak_pointer(G_OBJECT(priv->context), (void **)&priv->context);
1559 priv->ts = rpmtsCreate();
1560 rpmtsSetRootDir(priv->ts, install_root);
1561 priv->keyring = rpmtsGetKeyring(priv->ts, 1);
1562 return transaction;
1563 }
1564
1565 DnfTransaction *
hookContextTransactionGetTransaction(DnfPluginHookData * data)1566 hookContextTransactionGetTransaction(DnfPluginHookData * data)
1567 {
1568 if (!data) {
1569 auto logger(libdnf::Log::getLogger());
1570 logger->error(tfm::format("%s: was called with data == nullptr", __func__));
1571 return nullptr;
1572 }
1573 if (data->hookId != PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION &&
1574 data->hookId != PLUGIN_HOOK_ID_CONTEXT_TRANSACTION) {
1575 auto logger(libdnf::Log::getLogger());
1576 logger->error(tfm::format("%s: was called with hookId == %i", __func__, data->hookId));
1577 return nullptr;
1578 }
1579 return (static_cast<PluginHookContextTransactionData *>(data))->transaction;
1580 }
1581
1582 HyGoal
hookContextTransactionGetGoal(DnfPluginHookData * data)1583 hookContextTransactionGetGoal(DnfPluginHookData * data)
1584 {
1585 if (!data) {
1586 auto logger(libdnf::Log::getLogger());
1587 logger->error(tfm::format("%s: was called with data == nullptr", __func__));
1588 return nullptr;
1589 }
1590 if (data->hookId != PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION &&
1591 data->hookId != PLUGIN_HOOK_ID_CONTEXT_TRANSACTION) {
1592 auto logger(libdnf::Log::getLogger());
1593 logger->error(tfm::format("%s: was called with hookId == %i", __func__, data->hookId));
1594 return nullptr;
1595 }
1596 return (static_cast<PluginHookContextTransactionData *>(data))->goal;
1597 }
1598
1599 DnfState *
hookContextTransactionGetState(DnfPluginHookData * data)1600 hookContextTransactionGetState(DnfPluginHookData * data)
1601 {
1602 if (!data) {
1603 auto logger(libdnf::Log::getLogger());
1604 logger->error(tfm::format("%s: was called with data == nullptr", __func__));
1605 return nullptr;
1606 }
1607 if (data->hookId != PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION &&
1608 data->hookId != PLUGIN_HOOK_ID_CONTEXT_TRANSACTION) {
1609 auto logger(libdnf::Log::getLogger());
1610 logger->error(tfm::format("%s: was called with hookId == %i", __func__, data->hookId));
1611 return nullptr;
1612 }
1613 return (static_cast<PluginHookContextTransactionData *>(data))->state;
1614 }
1615