XBPS Library API 20240111
The X Binary Package System
transaction_ops.c
1/*-
2 * Copyright (c) 2009-2020 Juan Romero Pardines.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <stdio.h>
27#include <stdbool.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <fnmatch.h>
32
33#include "xbps_api_impl.h"
34
35/**
36 * @file lib/transaction_ops.c
37 * @brief Transaction handling routines
38 * @defgroup transaction Transaction handling functions
39 *
40 * The following image shows off the full transaction dictionary returned
41 * by xbps_transaction_prepare().
42 *
43 * @image html images/xbps_transaction_dictionary.png
44 *
45 * Legend:
46 * - <b>Salmon bg box</b>: The transaction dictionary.
47 * - <b>White bg box</b>: mandatory objects.
48 * - <b>Grey bg box</b>: optional objects.
49 * - <b>Green bg box</b>: possible value set in the object, only one of them
50 * will be set.
51 *
52 * Text inside of white boxes are the key associated with the object, its
53 * data type is specified on its edge, i.e string, array, integer, dictionary.
54 */
55static int
56trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
57{
58 xbps_dictionary_t pkg_pkgdb = NULL, pkg_repod = NULL;
59 xbps_object_t obj;
60 xbps_array_t pkgs;
61 pkg_state_t state = 0;
63 const char *repoloc, *repopkgver, *instpkgver, *pkgname;
64 char buf[XBPS_NAME_SIZE] = {0};
65 bool autoinst = false;
66 int rv = 0;
67
68 assert(pkg != NULL);
69
70 /*
71 * Find out if pkg is installed first.
72 */
73 if (xbps_pkg_name(buf, sizeof(buf), pkg)) {
74 pkg_pkgdb = xbps_pkgdb_get_pkg(xhp, buf);
75 } else {
76 pkg_pkgdb = xbps_pkgdb_get_pkg(xhp, pkg);
77 }
78
79 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
80 pkg_pkgdb = NULL;
81 ttype = XBPS_TRANS_DOWNLOAD;
82 }
83
84 /*
85 * Find out if the pkg has been found in repository pool.
86 */
87 if (pkg_pkgdb == NULL) {
88 /* pkg not installed, perform installation */
89 ttype = XBPS_TRANS_INSTALL;
90 if (((pkg_repod = xbps_rpool_get_pkg(xhp, pkg)) == NULL) &&
91 ((pkg_repod = xbps_rpool_get_virtualpkg(xhp, pkg)) == NULL)) {
92 /* not found */
93 return ENOENT;
94 }
95 } else {
96 if (force) {
97 ttype = XBPS_TRANS_REINSTALL;
98 } else {
99 ttype = XBPS_TRANS_UPDATE;
100 }
101 if (xbps_dictionary_get(pkg_pkgdb, "repolock")) {
102 struct xbps_repo *repo;
103 /* find update from repo */
104 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "repository", &repoloc);
106 if ((repo = xbps_regget_repo(xhp, repoloc)) == NULL) {
107 /* not found */
108 return ENOENT;
109 }
111 } else {
112 /* find update from rpool */
114 }
115 if (pkg_repod == NULL) {
116 /* not found */
117 return ENOENT;
118 }
119 }
120
121 xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgver", &repopkgver);
122
123 if (ttype == XBPS_TRANS_UPDATE) {
124 /*
125 * Compare installed version vs best pkg available in repos
126 * for pkg updates.
127 */
128 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb,
129 "pkgver", &instpkgver);
130 if (xbps_cmpver(repopkgver, instpkgver) <= 0 &&
132 xbps_dictionary_get_cstring_nocopy(pkg_repod,
133 "repository", &repoloc);
134 xbps_dbg_printf("[rpool] Skipping `%s' "
135 "(installed: %s) from repository `%s'\n",
137 return EEXIST;
138 }
139 } else if (ttype == XBPS_TRANS_REINSTALL) {
140 /*
141 * For reinstallation check if installed version is less than
142 * or equal to the pkg in repos, if true, continue with reinstallation;
143 * otherwise perform an update.
144 */
145 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "pkgver", &instpkgver);
146 if (xbps_cmpver(repopkgver, instpkgver) == 1) {
147 ttype = XBPS_TRANS_UPDATE;
148 }
149 }
150
151 if (pkg_pkgdb) {
152 /*
153 * If pkg is already installed, respect some properties.
154 */
155 if ((obj = xbps_dictionary_get(pkg_pkgdb, "automatic-install")))
156 xbps_dictionary_set(pkg_repod, "automatic-install", obj);
157 if ((obj = xbps_dictionary_get(pkg_pkgdb, "hold")))
158 xbps_dictionary_set(pkg_repod, "hold", obj);
159 if ((obj = xbps_dictionary_get(pkg_pkgdb, "repolock")))
160 xbps_dictionary_set(pkg_repod, "repolock", obj);
161 }
162 /*
163 * Prepare transaction dictionary.
164 */
165 if ((rv = xbps_transaction_init(xhp)) != 0)
166 return rv;
167
168 pkgs = xbps_dictionary_get(xhp->transd, "packages");
169 /*
170 * Find out if package being updated matches the one already
171 * in transaction, in that case ignore it.
172 */
173 if (ttype == XBPS_TRANS_UPDATE) {
174 if (xbps_find_pkg_in_array(pkgs, repopkgver, 0)) {
175 xbps_dbg_printf("[update] `%s' already queued in "
176 "transaction.\n", repopkgver);
177 return EEXIST;
178 }
179 }
180
181 if (!xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgname", &pkgname)) {
182 return EINVAL;
183 }
184 /*
185 * Set package state in dictionary with same state than the
186 * package currently uses, otherwise not-installed.
187 */
188 if ((rv = xbps_pkg_state_installed(xhp, pkgname, &state)) != 0) {
189 if (rv != ENOENT) {
190 return rv;
191 }
192 /* Package not installed, don't error out */
193 state = XBPS_PKG_STATE_NOT_INSTALLED;
194 }
195 if ((rv = xbps_set_pkg_state_dictionary(pkg_repod, state)) != 0) {
196 return rv;
197 }
198
199 if (state == XBPS_PKG_STATE_NOT_INSTALLED)
200 ttype = XBPS_TRANS_INSTALL;
201
202 if (!force && xbps_dictionary_get(pkg_repod, "hold"))
203 ttype = XBPS_TRANS_HOLD;
204
205 /*
206 * Store pkgd from repo into the transaction.
207 */
209 return EINVAL;
210 }
211
212 /*
213 * Set automatic-install to true if it was requested and this is a new install.
214 */
215 if (ttype == XBPS_TRANS_INSTALL)
216 autoinst = xhp->flags & XBPS_FLAG_INSTALL_AUTO;
217
218 if (!xbps_transaction_store(xhp, pkgs, pkg_repod, autoinst)) {
219 return EINVAL;
220 }
221
222 return 0;
223}
224
225/*
226 * Returns 1 if there's an update, 0 if none or -1 on error.
227 */
228static int
229xbps_autoupdate(struct xbps_handle *xhp)
230{
231 xbps_array_t rdeps;
232 xbps_dictionary_t pkgd;
233 const char *pkgver = NULL, *pkgname = NULL;
234 int rv;
235
236 /*
237 * Check if there's a new update for XBPS before starting
238 * another transaction.
239 */
240 if (((pkgd = xbps_pkgdb_get_pkg(xhp, "xbps")) == NULL) &&
241 ((pkgd = xbps_pkgdb_get_virtualpkg(xhp, "xbps")) == NULL))
242 return 0;
243
244 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
245 return EINVAL;
246 }
247 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname)) {
248 return EINVAL;
249 }
250
251 rv = trans_find_pkg(xhp, pkgname, false);
252
253 xbps_dbg_printf("%s: trans_find_pkg xbps: %d\n", __func__, rv);
254
255 if (rv == 0) {
256 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
257 return 0;
258 }
259 /* a new xbps version is available, check its revdeps */
260 rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, "xbps");
261 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
262 const char *curpkgver = NULL;
263 char curpkgn[XBPS_NAME_SIZE] = {0};
264
265 xbps_array_get_cstring_nocopy(rdeps, i, &curpkgver);
266 xbps_dbg_printf("%s: processing revdep %s\n", __func__, curpkgver);
267
268 if (!xbps_pkg_name(curpkgn, sizeof(curpkgn), curpkgver)) {
269 abort();
270 }
271 rv = trans_find_pkg(xhp, curpkgn, false);
272 xbps_dbg_printf("%s: trans_find_pkg revdep %s: %d\n", __func__, curpkgver, rv);
273 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV)
274 return -1;
275 }
276 /*
277 * Set XBPS_FLAG_FORCE_REMOVE_REVDEPS to ignore broken
278 * reverse dependencies in xbps_transaction_prepare().
279 *
280 * This won't skip revdeps of the xbps pkg, rather other
281 * packages in rootdir that could be broken indirectly.
282 *
283 * A sysup transaction after updating xbps should fix them
284 * again.
285 */
286 xhp->flags |= XBPS_FLAG_FORCE_REMOVE_REVDEPS;
287 return 1;
288 } else if (rv == ENOENT || rv == EEXIST || rv == ENODEV) {
289 /* no update */
290 return 0;
291 } else {
292 /* error */
293 return -1;
294 }
295
296 return 0;
297}
298
299int
301{
302 xbps_object_t obj;
303 xbps_object_iterator_t iter;
304 xbps_dictionary_t pkgd;
305 bool newpkg_found = false;
306 int rv = 0;
307
308 rv = xbps_autoupdate(xhp);
309 switch (rv) {
310 case 1:
311 /* xbps needs to be updated, don't allow any other update */
312 return EBUSY;
313 case -1:
314 /* error */
315 return EINVAL;
316 default:
317 break;
318 }
319
320 iter = xbps_dictionary_iterator(xhp->pkgdb);
321 assert(iter);
322
323 while ((obj = xbps_object_iterator_next(iter))) {
324 const char *pkgver = NULL;
325 char pkgname[XBPS_NAME_SIZE] = {0};
326
327 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
328 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
329 continue;
330 }
331 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
332 rv = EINVAL;
333 break;
334 }
335 rv = trans_find_pkg(xhp, pkgname, false);
336 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
337 if (rv == 0) {
338 newpkg_found = true;
339 } else if (rv == ENOENT || rv == EEXIST || rv == ENODEV) {
340 /*
341 * missing pkg or installed version is greater than or
342 * equal than pkg in repositories.
343 */
344 rv = 0;
345 }
346 }
347 xbps_object_iterator_release(iter);
348
349 return newpkg_found ? rv : EEXIST;
350}
351
352int
354{
355 xbps_array_t rdeps;
356 int rv;
357
358 rv = xbps_autoupdate(xhp);
359 xbps_dbg_printf("%s: xbps_autoupdate %d\n", __func__, rv);
360 switch (rv) {
361 case 1:
362 /* xbps needs to be updated, only allow xbps to be updated */
363 if (strcmp(pkg, "xbps"))
364 return EBUSY;
365 return 0;
366 case -1:
367 /* error */
368 return EINVAL;
369 default:
370 /* no update */
371 break;
372 }
373
374 /* update its reverse dependencies */
376 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
377 rdeps = NULL;
378 }
379 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
380 const char *pkgver = NULL;
381 char pkgname[XBPS_NAME_SIZE] = {0};
382
383 if (!xbps_array_get_cstring_nocopy(rdeps, i, &pkgver)) {
384 rv = EINVAL;
385 break;
386 }
387 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
388 rv = EINVAL;
389 break;
390 }
391 rv = trans_find_pkg(xhp, pkgname, false);
392 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
393 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV) {
394 return rv;
395 }
396 }
397 /* add pkg repod */
398 rv = trans_find_pkg(xhp, pkg, force);
399 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkg, rv);
400 return rv;
401}
402
403int
405{
406 xbps_array_t rdeps;
407 int rv;
408
409 rv = xbps_autoupdate(xhp);
410 switch (rv) {
411 case 1:
412 /* xbps needs to be updated, only allow xbps to be updated */
413 if (strcmp(pkg, "xbps"))
414 return EBUSY;
415 return 0;
416 case -1:
417 /* error */
418 return EINVAL;
419 default:
420 /* no update */
421 break;
422 }
423
424 /* update its reverse dependencies */
426 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
427 rdeps = NULL;
428 }
429 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
430 const char *pkgver = NULL;
431 char pkgname[XBPS_NAME_SIZE] = {0};
432
433 if (!xbps_array_get_cstring_nocopy(rdeps, i, &pkgver)) {
434 rv = EINVAL;
435 break;
436 }
437 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
438 rv = EINVAL;
439 break;
440 }
441 rv = trans_find_pkg(xhp, pkgname, false);
442 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
443 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV) {
444 return rv;
445 }
446 }
447 rv = trans_find_pkg(xhp, pkg, force);
448 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkg, rv);
449 return rv;
450}
451
452int
454 const char *pkgname,
455 bool recursive)
456{
457 xbps_dictionary_t pkgd;
458 xbps_array_t pkgs, orphans, orphans_pkg;
459 xbps_object_t obj;
460 int rv = 0;
461
462 assert(xhp);
463 assert(pkgname);
464
465 if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL) {
466 /* pkg not installed */
467 return ENOENT;
468 }
469 /*
470 * Prepare transaction dictionary and missing deps array.
471 */
472 if ((rv = xbps_transaction_init(xhp)) != 0)
473 return rv;
474
475 pkgs = xbps_dictionary_get(xhp->transd, "packages");
476
477 if (!recursive)
478 goto rmpkg;
479 /*
480 * If recursive is set, find out which packages would be orphans
481 * if the supplied package were already removed.
482 */
483 if ((orphans_pkg = xbps_array_create()) == NULL)
484 return ENOMEM;
485
486 xbps_array_set_cstring_nocopy(orphans_pkg, 0, pkgname);
488 xbps_object_release(orphans_pkg);
489 if (xbps_object_type(orphans) != XBPS_TYPE_ARRAY)
490 return EINVAL;
491
492 for (unsigned int i = 0; i < xbps_array_count(orphans); i++) {
493 obj = xbps_array_get(orphans, i);
494 xbps_transaction_pkg_type_set(obj, XBPS_TRANS_REMOVE);
495 if (!xbps_transaction_store(xhp, pkgs, obj, false)) {
496 return EINVAL;
497 }
498 }
499 xbps_object_release(orphans);
500 return rv;
501
502rmpkg:
503 /*
504 * Add pkg dictionary into the transaction pkgs queue.
505 */
506 xbps_transaction_pkg_type_set(pkgd, XBPS_TRANS_REMOVE);
507 if (!xbps_transaction_store(xhp, pkgs, pkgd, false)) {
508 return EINVAL;
509 }
510 return rv;
511}
512
513int
515{
516 xbps_array_t orphans, pkgs;
517 xbps_object_t obj;
518 int rv = 0;
519
521 if (xbps_array_count(orphans) == 0) {
522 /* no orphans? we are done */
523 goto out;
524 }
525 /*
526 * Prepare transaction dictionary and missing deps array.
527 */
528 if ((rv = xbps_transaction_init(xhp)) != 0)
529 goto out;
530
531 pkgs = xbps_dictionary_get(xhp->transd, "packages");
532 /*
533 * Add pkg orphan dictionary into the transaction pkgs queue.
534 */
535 for (unsigned int i = 0; i < xbps_array_count(orphans); i++) {
536 obj = xbps_array_get(orphans, i);
537 xbps_transaction_pkg_type_set(obj, XBPS_TRANS_REMOVE);
538 if (!xbps_transaction_store(xhp, pkgs, obj, false)) {
539 rv = EINVAL;
540 goto out;
541 }
542 }
543out:
544 if (orphans)
545 xbps_object_release(orphans);
546
547 return rv;
548}
549
552{
553 uint8_t r;
554
555 if (xbps_object_type(pkg_repod) != XBPS_TYPE_DICTIONARY)
556 return 0;
557
558 if (!xbps_dictionary_get_uint8(pkg_repod, "transaction", &r))
559 return 0;
560
561 return r;
562}
563
564bool
566{
567 uint8_t r;
568
569 if (xbps_object_type(pkg_repod) != XBPS_TYPE_DICTIONARY)
570 return false;
571
572 switch (ttype) {
573 case XBPS_TRANS_INSTALL:
574 case XBPS_TRANS_UPDATE:
575 case XBPS_TRANS_CONFIGURE:
576 case XBPS_TRANS_REMOVE:
577 case XBPS_TRANS_REINSTALL:
578 case XBPS_TRANS_HOLD:
579 case XBPS_TRANS_DOWNLOAD:
580 break;
581 default:
582 return false;
583 }
584 r = ttype;
585 if (!xbps_dictionary_set_uint8(pkg_repod, "transaction", r))
586 return false;
587
588 return true;
589}
int flags
Definition xbps.h:679
xbps_dictionary_t transd
Definition xbps.h:583
xbps_dictionary_t pkgdb
Definition xbps.h:576
Generic XBPS structure handler for initialization.
Definition xbps.h:550
xbps_array_t xbps_find_pkg_orphans(struct xbps_handle *xhp, xbps_array_t orphans)
xbps_dictionary_t xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:388
xbps_array_t xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:458
xbps_dictionary_t xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:379
int xbps_pkg_state_installed(struct xbps_handle *xhp, const char *pkgname, pkg_state_t *state)
int xbps_set_pkg_state_dictionary(xbps_dictionary_t dict, pkg_state_t state)
pkg_state_t
Definition xbps.h:1768
xbps_dictionary_t xbps_repo_get_pkg(struct xbps_repo *repo, const char *pkg)
Definition repo.c:441
struct xbps_handle * xhp
Definition xbps.h:1422
xbps_dictionary_t idx
Definition xbps.h:1428
Repository structure.
Definition xbps.h:1409
xbps_dictionary_t xbps_rpool_get_virtualpkg(struct xbps_handle *xhp, const char *pkg)
Definition rpool.c:323
xbps_dictionary_t xbps_rpool_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition rpool.c:329
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
int xbps_transaction_install_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
int xbps_transaction_autoremove_pkgs(struct xbps_handle *xhp)
xbps_trans_type_t
Definition xbps.h:1320
int xbps_transaction_remove_pkg(struct xbps_handle *xhp, const char *pkgname, bool recursive)
bool xbps_transaction_pkg_type_set(xbps_dictionary_t pkg_repod, xbps_trans_type_t ttype)
int xbps_transaction_update_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
int xbps_transaction_update_packages(struct xbps_handle *xhp)
bool xbps_pkg_name(char *dst, size_t len, const char *pkg)
Definition util.c:253
bool xbps_pkg_reverts(xbps_dictionary_t pkg, const char *pkgver)
Definition util.c:587
int xbps_cmpver(const char *pkg1, const char *pkg2)
Definition dewey.c:273