1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2013-2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * Most of this code was taken from Zif, libzif/zif-transaction.c
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or(at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /**
25 * SECTION:dnf-goal
26 * @short_description: Helper methods for dealing with rpm transactions.
27 * @include: libdnf.h
28 * @stability: Unstable
29 *
30 * These methods make it easier to deal with rpm transactions.
31 */
32
33
34 #include <glib.h>
35 #include <rpm/rpmlib.h>
36 #include <rpm/rpmlog.h>
37 #include <rpm/rpmdb.h>
38
39 #include "catch-error.hpp"
40 #include "sack/packageset.hpp"
41 #include "hy-package-private.hpp"
42 #include "dnf-rpmts-private.hpp"
43 #include "dnf-types.h"
44 #include "dnf-utils.h"
45
46 #include "utils/bgettext/bgettext-lib.h"
47 #include "dnf-package.h"
48
49 // older RPM doesn't have RPMTAG_MODULARITYLABEL defined
50 #ifndef RPMTAG_MODULARITYLABEL
51 #define RPMTAG_MODULARITYLABEL 5096
52 #endif
53
54
55 static gboolean
test_fail_safe(Header * hdr,DnfPackage * pkg,GError ** error)56 test_fail_safe(Header * hdr, DnfPackage * pkg, GError **error)
57 {
58 if (dnf_package_installed(pkg)) {
59 return TRUE;
60 }
61 if (strcmp(dnf_package_get_reponame(pkg), HY_CMDLINE_REPO_NAME) == 0) {
62 return TRUE;
63 }
64 if (auto repo = dnf_package_get_repo(pkg)) {
65 if (dnf_repo_get_module_hotfixes(repo)) {
66 return TRUE;
67 }
68 } else {
69 return TRUE;
70 }
71 rpmtd td = rpmtdNew();
72 gboolean ret = TRUE;
73 if (headerGet(*hdr, RPMTAG_MODULARITYLABEL, td, HEADERGET_MINMEM)) {
74 if (rpmtdGetString(td)) {
75 DnfSack * sack = dnf_package_get_sack(pkg);
76 std::unique_ptr<libdnf::PackageSet> includes(dnf_sack_get_module_includes(sack));
77 if (!includes || !includes->has(dnf_package_get_id(pkg))) {
78 g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
79 _("No available modular metadata for modular package '%s'; "
80 "cannot be installed on the system"),
81 dnf_package_get_nevra(pkg));
82 ret = FALSE;
83 }
84 }
85 }
86 rpmtdFreeData(td);
87 rpmtdFree(td);
88 return ret;
89 }
90
91 gboolean
dnf_rpmts_add_install_filename2(rpmts ts,const gchar * filename,gboolean allow_untrusted,gboolean is_update,DnfPackage * pkg,GError ** error)92 dnf_rpmts_add_install_filename2(rpmts ts,
93 const gchar *filename,
94 gboolean allow_untrusted,
95 gboolean is_update,
96 DnfPackage * pkg,
97 GError **error) try
98 {
99 gboolean ret = TRUE;
100 gint res;
101 Header hdr;
102 FD_t fd;
103
104 /* open this */
105 fd = Fopen(filename, "r.ufdio");
106 res = rpmReadPackageFile(ts, fd, filename, &hdr);
107
108 /* be less strict when we're allowing untrusted transactions */
109 if (allow_untrusted) {
110 switch(res) {
111 case RPMRC_NOKEY:
112 case RPMRC_NOTFOUND:
113 case RPMRC_NOTTRUSTED:
114 case RPMRC_OK:
115 break;
116 case RPMRC_FAIL:
117 ret = FALSE;
118 g_set_error(error,
119 DNF_ERROR,
120 DNF_ERROR_INTERNAL_ERROR,
121 _("signature does not verify for %s"),
122 filename);
123 goto out;
124 default:
125 ret = FALSE;
126 g_set_error(error,
127 DNF_ERROR,
128 DNF_ERROR_INTERNAL_ERROR,
129 _("failed to open(generic error): %s"),
130 filename);
131 goto out;
132 }
133 } else {
134 switch(res) {
135 case RPMRC_OK:
136 break;
137 case RPMRC_NOTTRUSTED:
138 ret = FALSE;
139 g_set_error(error,
140 DNF_ERROR,
141 DNF_ERROR_INTERNAL_ERROR,
142 _("failed to verify key for %s"),
143 filename);
144 goto out;
145 case RPMRC_NOKEY:
146 ret = FALSE;
147 g_set_error(error,
148 DNF_ERROR,
149 DNF_ERROR_INTERNAL_ERROR,
150 _("public key unavailable for %s"),
151 filename);
152 goto out;
153 case RPMRC_NOTFOUND:
154 ret = FALSE;
155 g_set_error(error,
156 DNF_ERROR,
157 DNF_ERROR_INTERNAL_ERROR,
158 _("signature not found for %s"),
159 filename);
160 goto out;
161 case RPMRC_FAIL:
162 ret = FALSE;
163 g_set_error(error,
164 DNF_ERROR,
165 DNF_ERROR_INTERNAL_ERROR,
166 _("signature does not verify for %s"),
167 filename);
168 goto out;
169 default:
170 ret = FALSE;
171 g_set_error(error,
172 DNF_ERROR,
173 DNF_ERROR_INTERNAL_ERROR,
174 _("failed to open(generic error): %s"),
175 filename);
176 goto out;
177 }
178 }
179 if (pkg) {
180 if (!test_fail_safe(&hdr, pkg, error)) {
181 ret = FALSE;
182 goto out;
183 }
184 }
185
186 /* add to the transaction */
187 res = rpmtsAddInstallElement(ts, hdr, (fnpyKey) filename, is_update, NULL);
188 if (res != 0) {
189 ret = FALSE;
190 g_set_error(error,
191 DNF_ERROR,
192 DNF_ERROR_INTERNAL_ERROR,
193 _("failed to add install element: %1$s [%2$i]"),
194 filename, res);
195 goto out;
196 }
197 out:
198 Fclose(fd);
199 headerFree(hdr);
200 return ret;
201 } CATCH_TO_GERROR(FALSE)
202
203 /**
204 * dnf_rpmts_add_install_filename:
205 * @ts: a #rpmts instance.
206 * @filename: the package.
207 * @allow_untrusted: is we can add untrusted packages.
208 * @is_update: if the package is an update.
209 * @error: a #GError or %NULL..
210 *
211 * Add to the transaction a package to be installed.
212 *
213 * Returns: %TRUE for success, %FALSE otherwise
214 *
215 * Since: 0.1.0
216 **/
217 gboolean
218 dnf_rpmts_add_install_filename(rpmts ts,
219 const gchar *filename,
220 gboolean allow_untrusted,
221 gboolean is_update,
222 GError **error) try
223 {
224 return dnf_rpmts_add_install_filename2(ts, filename, allow_untrusted, is_update, NULL, error);
CATCH_TO_GERROR(FALSE)225 } CATCH_TO_GERROR(FALSE)
226
227
228 /**
229 * dnf_rpmts_look_for_problems:
230 * @ts: a #rpmts instance.
231 * @error: a #GError or %NULL..
232 *
233 * Look for problems in the transaction.
234 *
235 * Returns: %TRUE for success, %FALSE otherwise
236 *
237 * Since: 0.1.0
238 **/
239 gboolean
240 dnf_rpmts_look_for_problems(rpmts ts, GError **error) try
241 {
242 gboolean ret = TRUE;
243 rpmProblem prob;
244 rpmpsi psi;
245 rpmps probs = NULL;
246 g_autoptr(GString) string = NULL;
247
248 /* get a list of problems */
249 probs = rpmtsProblems(ts);
250 if (rpmpsNumProblems(probs) == 0)
251 goto out;
252
253 /* parse problems */
254 string = g_string_new("");
255 psi = rpmpsInitIterator(probs);
256 while (rpmpsNextIterator(psi) >= 0) {
257 g_autofree gchar *msg = NULL;
258 prob = rpmpsGetProblem(psi);
259 msg = rpmProblemString(prob);
260 g_string_append(string, msg);
261 g_string_append(string, "\n");
262 }
263 rpmpsFreeIterator(psi);
264
265 /* set error */
266 ret = FALSE;
267
268 /* we failed, and got a reason to report */
269 if (string->len > 0) {
270 g_string_set_size(string, string->len - 1);
271 g_set_error(error,
272 DNF_ERROR,
273 DNF_ERROR_INTERNAL_ERROR,
274 _("Error running transaction: %s"),
275 string->str);
276 goto out;
277 }
278
279 /* we failed, and got no reason why */
280 g_set_error_literal(error,
281 DNF_ERROR,
282 DNF_ERROR_INTERNAL_ERROR,
283 _("Error running transaction and no problems were reported!"));
284 out:
285 rpmpsFree(probs);
286 return ret;
287 } CATCH_TO_GERROR(FALSE)
288
289 /**
290 * dnf_rpmts_log_handler_cb:
291 **/
292 static int
293 dnf_rpmts_log_handler_cb(rpmlogRec rec, rpmlogCallbackData data)
294 {
295 GString **string =(GString **) data;
296
297 /* only log errors */
298 if (rpmlogRecPriority(rec) != RPMLOG_ERR)
299 return RPMLOG_DEFAULT;
300
301 /* do not log internal BDB errors */
302 if (g_strstr_len(rpmlogRecMessage(rec), -1, "BDB") != NULL)
303 return 0;
304
305 /* create string if required */
306 if (*string == NULL)
307 *string = g_string_new("");
308
309 /* if text already exists, join them */
310 if ((*string)->len > 0)
311 g_string_append(*string, ": ");
312 g_string_append(*string, rpmlogRecMessage(rec));
313
314 /* remove the trailing /n which rpm does */
315 if ((*string)->len > 0)
316 g_string_truncate(*string,(*string)->len - 1);
317 return 0;
318 }
319
320 /**
321 * dnf_rpmts_find_package:
322 **/
323 static Header
dnf_rpmts_find_package(rpmts ts,DnfPackage * pkg,GError ** error)324 dnf_rpmts_find_package(rpmts ts, DnfPackage *pkg, GError **error)
325 {
326 Header hdr = NULL;
327 rpmdbMatchIterator iter;
328 unsigned int recOffset;
329 g_autoptr(GString) rpm_error = NULL;
330
331 /* find package by db-id */
332 recOffset = dnf_package_get_rpmdbid(pkg);
333 rpmlogSetCallback(dnf_rpmts_log_handler_cb, &rpm_error);
334 iter = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
335 &recOffset, sizeof(recOffset));
336 if (iter == NULL) {
337 if (rpm_error != NULL) {
338 g_set_error_literal(error,
339 DNF_ERROR,
340 DNF_ERROR_UNFINISHED_TRANSACTION,
341 rpm_error->str);
342 } else {
343 g_set_error_literal(error,
344 DNF_ERROR,
345 DNF_ERROR_UNFINISHED_TRANSACTION,
346 _("Fatal error, run database recovery"));
347 }
348 goto out;
349 }
350 hdr = rpmdbNextIterator(iter);
351 if (hdr == NULL) {
352 g_set_error(error,
353 DNF_ERROR,
354 DNF_ERROR_FILE_NOT_FOUND,
355 _("failed to find package %s"),
356 dnf_package_get_name(pkg));
357 goto out;
358 }
359
360 /* success */
361 headerLink(hdr);
362 out:
363 rpmlogSetCallback(NULL, NULL);
364 if (iter != NULL)
365 rpmdbFreeIterator(iter);
366 return hdr;
367 }
368
369 /**
370 * dnf_rpmts_add_remove_pkg:
371 * @ts: a #rpmts instance.
372 * @pkg: a #DnfPackage *instance.
373 * @error: a #GError or %NULL..
374 *
375 * Adds to the transaction a package to be removed.
376 *
377 * Returns: %TRUE for success, %FALSE otherwise
378 *
379 * Since: 0.1.0
380 **/
381 gboolean
dnf_rpmts_add_remove_pkg(rpmts ts,DnfPackage * pkg,GError ** error)382 dnf_rpmts_add_remove_pkg(rpmts ts, DnfPackage *pkg, GError **error) try
383 {
384 gboolean ret = TRUE;
385 gint retval;
386 Header hdr;
387
388 hdr = dnf_rpmts_find_package(ts, pkg, error);
389 if (hdr == NULL) {
390 ret = FALSE;
391 goto out;
392 }
393
394 /* remove it */
395 retval = rpmtsAddEraseElement(ts, hdr, -1);
396 if (retval != 0) {
397 ret = FALSE;
398 g_set_error(error,
399 DNF_ERROR,
400 DNF_ERROR_INTERNAL_ERROR,
401 _("could not add erase element %1$s(%2$i)"),
402 dnf_package_get_name(pkg), retval);
403 goto out;
404 }
405 out:
406 if (hdr != NULL)
407 headerFree(hdr);
408 return ret;
409 } CATCH_TO_GERROR(FALSE)
410