XBPS Library API 20250813
The X Binary Package System
pkgdb.c
1/*-
2 * Copyright (c) 2012-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 <sys/file.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29
30#include <errno.h>
31#include <fcntl.h>
32#include <limits.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include "xbps.h"
40#include "xbps_api_impl.h"
41
42/**
43 * @file lib/pkgdb.c
44 * @brief Package database handling routines
45 * @defgroup pkgdb Package database handling functions
46 *
47 * Functions to manipulate the main package database plist file (pkgdb).
48 *
49 * The following image shown below shows the proplib structure used
50 * by the main package database plist:
51 *
52 * @image html images/xbps_pkgdb_dictionary.png
53 *
54 * Legend:
55 * - <b>Salmon filled box</b>: \a pkgdb plist internalized.
56 * - <b>White filled box</b>: mandatory objects.
57 * - <b>Grey filled box</b>: optional objects.
58 * - <b>Green filled box</b>: possible value set in the object, only one
59 * of them is set.
60 *
61 * Text inside of white boxes are the key associated with the object, its
62 * data type is specified on its edge, i.e array, bool, integer, string,
63 * dictionary.
64 */
65
66int
68{
69 char path[PATH_MAX];
70 mode_t prev_umask;
71 int r = 0;
72
73 if (access(xhp->rootdir, W_OK) == -1 && errno != ENOENT) {
74 return xbps_error_errno(errno,
75 "failed to check whether the rootdir is writable: "
76 "%s: %s\n",
77 xhp->rootdir, strerror(errno));
78 }
79
80 if (xbps_path_join(path, sizeof(path), xhp->metadir, "lock", (char *)NULL) == -1) {
81 return xbps_error_errno(errno,
82 "failed to create lockfile path: %s\n", strerror(errno));
83 }
84
85 prev_umask = umask(022);
86
87 /* if metadir does not exist, create it */
88 if (access(xhp->metadir, R_OK|X_OK) == -1) {
89 if (errno != ENOENT) {
90 umask(prev_umask);
91 return xbps_error_errno(errno,
92 "failed to check access to metadir: %s: %s\n",
93 xhp->metadir, strerror(-r));
94 }
95 if (xbps_mkpath(xhp->metadir, 0755) == -1 && errno != EEXIST) {
96 umask(prev_umask);
97 return xbps_error_errno(errno,
98 "failed to create metadir: %s: %s\n",
99 xhp->metadir, strerror(errno));
100 }
101 }
102
103 xhp->lock_fd = open(path, O_CREAT|O_WRONLY|O_CLOEXEC, 0664);
104 if (xhp->lock_fd == -1) {
105 return xbps_error_errno(errno,
106 "failed to create lock file: %s: %s\n", path,
107 strerror(errno));
108 }
109 umask(prev_umask);
110
111 if (flock(xhp->lock_fd, LOCK_EX|LOCK_NB) == -1) {
112 if (errno != EWOULDBLOCK)
113 goto err;
114 xbps_warn_printf("package database locked, waiting...\n");
115 }
116
117 if (flock(xhp->lock_fd, LOCK_EX) == -1) {
118err:
119 close(xhp->lock_fd);
120 xhp->lock_fd = -1;
121 return xbps_error_errno(errno, "failed to lock file: %s: %s\n",
122 path, strerror(errno));
123 }
124
125 return 0;
126}
127
128void
130{
131 if (xhp->lock_fd == -1)
132 return;
133 close(xhp->lock_fd);
134 xhp->lock_fd = -1;
135}
136
137static int
138pkgdb_map_vpkgs(struct xbps_handle *xhp)
139{
140 xbps_object_iterator_t iter;
141 xbps_object_t obj;
142 int r = 0;
143
144 if (!xbps_dictionary_count(xhp->pkgdb))
145 return 0;
146
147 if (xhp->vpkgd == NULL) {
148 xhp->vpkgd = xbps_dictionary_create();
149 if (!xhp->vpkgd) {
150 r = -errno;
151 xbps_error_printf("failed to create dictionary\n");
152 return r;
153 }
154 }
155
156 /*
157 * This maps all pkgs that have virtualpkgs in pkgdb.
158 */
159 iter = xbps_dictionary_iterator(xhp->pkgdb);
160 if (!iter) {
161 r = -errno;
162 xbps_error_printf("failed to create iterator");
163 return r;
164 }
165
166 while ((obj = xbps_object_iterator_next(iter))) {
167 xbps_array_t provides;
168 xbps_dictionary_t pkgd;
169 const char *pkgver = NULL;
170 const char *pkgname = NULL;
171 unsigned int cnt;
172
173 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
174 provides = xbps_dictionary_get(pkgd, "provides");
175 cnt = xbps_array_count(provides);
176 if (!cnt)
177 continue;
178
179 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
180 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname);
181 assert(pkgname);
182
183 for (unsigned int i = 0; i < cnt; i++) {
184 char vpkgname[XBPS_NAME_SIZE];
185 const char *vpkg = NULL;
186 xbps_dictionary_t providers;
187 bool alloc = false;
188
189 xbps_array_get_cstring_nocopy(provides, i, &vpkg);
190 if (!xbps_pkg_name(vpkgname, sizeof(vpkgname), vpkg)) {
191 xbps_warn_printf("%s: invalid provides: %s\n", pkgver, vpkg);
192 continue;
193 }
194
195 providers = xbps_dictionary_get(xhp->vpkgd, vpkgname);
196 if (!providers) {
197 providers = xbps_dictionary_create();
198 if (!providers) {
199 r = -errno;
200 xbps_error_printf("failed to create dictionary\n");
201 goto out;
202 }
203 if (!xbps_dictionary_set(xhp->vpkgd, vpkgname, providers)) {
204 r = -errno;
205 xbps_error_printf("failed to set dictionary entry\n");
206 xbps_object_release(providers);
207 goto out;
208 }
209 alloc = true;
210 }
211
212 if (!xbps_dictionary_set_cstring(providers, vpkg, pkgname)) {
213 r = -errno;
214 xbps_error_printf("failed to set dictionary entry\n");
215 if (alloc)
216 xbps_object_release(providers);
217 goto out;
218 }
219 if (alloc)
220 xbps_object_release(providers);
221 xbps_dbg_printf("[pkgdb] added vpkg %s for %s\n", vpkg, pkgname);
222 }
223 }
224out:
225 xbps_object_iterator_release(iter);
226 return r;
227}
228
229static int
230pkgdb_map_names(struct xbps_handle *xhp)
231{
232 xbps_object_iterator_t iter;
233 xbps_object_t obj;
234 int rv = 0;
235
236 if (!xbps_dictionary_count(xhp->pkgdb))
237 return 0;
238
239 /*
240 * This maps all pkgs in pkgdb to have the "pkgname" string property.
241 * This way we do it once and not multiple times.
242 */
243 iter = xbps_dictionary_iterator(xhp->pkgdb);
244 assert(iter);
245
246 while ((obj = xbps_object_iterator_next(iter))) {
247 xbps_dictionary_t pkgd;
248 const char *pkgver;
249 char pkgname[XBPS_NAME_SIZE] = {0};
250
251 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
252 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
253 continue;
254 }
255 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
256 rv = EINVAL;
257 break;
258 }
259 if (!xbps_dictionary_set_cstring(pkgd, "pkgname", pkgname)) {
260 rv = EINVAL;
261 break;
262 }
263 }
264 xbps_object_iterator_release(iter);
265 return rv;
266}
267
268int HIDDEN
269xbps_pkgdb_init(struct xbps_handle *xhp)
270{
271 int rv;
272
273 assert(xhp);
274
275 if (xhp->pkgdb)
276 return 0;
277
278 if (!xhp->pkgdb_plist)
279 xhp->pkgdb_plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_PKGDB);
280
281#if 0
282 if ((rv = xbps_pkgdb_conversion(xhp)) != 0)
283 return rv;
284#endif
285
286
287 if ((rv = xbps_pkgdb_update(xhp, false, true)) != 0) {
288 if (rv != ENOENT)
289 xbps_error_printf("failed to initialize pkgdb: %s\n", strerror(rv));
290 return rv;
291 }
292 if ((rv = pkgdb_map_names(xhp)) != 0) {
293 xbps_dbg_printf("[pkgdb] pkgdb_map_names %s\n", strerror(rv));
294 return rv;
295 }
296 if ((rv = pkgdb_map_vpkgs(xhp)) != 0) {
297 xbps_dbg_printf("[pkgdb] pkgdb_map_vpkgs %s\n", strerror(rv));
298 return rv;
299 }
300 assert(xhp->pkgdb);
301 xbps_dbg_printf("[pkgdb] initialized ok.\n");
302
303 return 0;
304}
305
306int
307xbps_pkgdb_update(struct xbps_handle *xhp, bool flush, bool update)
308{
309 xbps_dictionary_t pkgdb_storage;
310 mode_t prev_umask;
311 static int cached_rv;
312 int rv = 0;
313
314 if (cached_rv && !flush)
315 return cached_rv;
316
317 if (xhp->pkgdb && flush) {
318 pkgdb_storage = xbps_dictionary_internalize_from_file(xhp->pkgdb_plist);
319 if (pkgdb_storage == NULL ||
320 !xbps_dictionary_equals(xhp->pkgdb, pkgdb_storage)) {
321 /* flush dictionary to storage */
322 prev_umask = umask(022);
323 if (!xbps_dictionary_externalize_to_file(xhp->pkgdb, xhp->pkgdb_plist)) {
324 umask(prev_umask);
325 return errno;
326 }
327 umask(prev_umask);
328 }
329 if (pkgdb_storage)
330 xbps_object_release(pkgdb_storage);
331
332 xbps_object_release(xhp->pkgdb);
333 xhp->pkgdb = NULL;
334 cached_rv = 0;
335 }
336 if (!update)
337 return rv;
338
339 /* update copy in memory */
340 if ((xhp->pkgdb = xbps_dictionary_internalize_from_file(xhp->pkgdb_plist)) == NULL) {
341 rv = errno;
342 if (!rv)
343 rv = EINVAL;
344
345 if (rv == ENOENT)
346 xhp->pkgdb = xbps_dictionary_create();
347 else
348 xbps_error_printf("cannot access to pkgdb: %s\n", strerror(rv));
349
350 cached_rv = rv = errno;
351 }
352
353 return rv;
354}
355
356void HIDDEN
357xbps_pkgdb_release(struct xbps_handle *xhp)
358{
359 assert(xhp);
360
362 if (xhp->pkgdb)
363 xbps_object_release(xhp->pkgdb);
364 xbps_dbg_printf("[pkgdb] released ok.\n");
365}
366
367int
369 int (*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *),
370 void *arg)
371{
372 xbps_array_t allkeys;
373 int r;
374
375 // XXX: this should be done before calling the function...
376 if ((r = xbps_pkgdb_init(xhp)) != 0)
377 return r > 0 ? -r : r;
378
379 allkeys = xbps_dictionary_all_keys(xhp->pkgdb);
380 assert(allkeys);
381 r = xbps_array_foreach_cb(xhp, allkeys, xhp->pkgdb, fn, arg);
382 xbps_object_release(allkeys);
383 return r;
384}
385
386int
388 int (*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *),
389 void *arg)
390{
391 xbps_array_t allkeys;
392 int r;
393
394 // XXX: this should be done before calling the function...
395 if ((r = xbps_pkgdb_init(xhp)) != 0)
396 return r > 0 ? -r : r;
397
398 allkeys = xbps_dictionary_all_keys(xhp->pkgdb);
399 if (!allkeys)
400 return xbps_error_oom();
401
402 r = xbps_array_foreach_cb_multi(xhp, allkeys, xhp->pkgdb, fn, arg);
403 xbps_object_release(allkeys);
404 return r;
405}
406
407xbps_dictionary_t
408xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
409{
410 xbps_dictionary_t pkgd;
411
412 if (xbps_pkgdb_init(xhp) != 0)
413 return NULL;
414
415 pkgd = xbps_find_pkg_in_dict(xhp->pkgdb, pkg);
416 if (!pkgd)
417 errno = ENOENT;
418 return pkgd;
419}
420
421xbps_dictionary_t
422xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *vpkg)
423{
424 if (xbps_pkgdb_init(xhp) != 0)
425 return NULL;
426
427 return xbps_find_virtualpkg_in_dict(xhp, xhp->pkgdb, vpkg);
428}
429
430static void
431generate_full_revdeps_tree(struct xbps_handle *xhp)
432{
433 xbps_object_t obj;
434 xbps_object_iterator_t iter;
435 xbps_dictionary_t vpkg_cache;
436
437 if (xhp->pkgdb_revdeps)
438 return;
439
440 xhp->pkgdb_revdeps = xbps_dictionary_create();
441 assert(xhp->pkgdb_revdeps);
442
443 vpkg_cache = xbps_dictionary_create();
444 assert(vpkg_cache);
445
446 iter = xbps_dictionary_iterator(xhp->pkgdb);
447 assert(iter);
448
449 while ((obj = xbps_object_iterator_next(iter))) {
450 xbps_array_t rundeps;
451 xbps_dictionary_t pkgd;
452 const char *pkgver = NULL;
453
454 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
455 rundeps = xbps_dictionary_get(pkgd, "run_depends");
456 if (!xbps_array_count(rundeps))
457 continue;
458
459 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
460 for (unsigned int i = 0; i < xbps_array_count(rundeps); i++) {
461 xbps_array_t pkg;
462 const char *pkgdep = NULL, *v;
463 char curpkgname[XBPS_NAME_SIZE];
464 bool alloc = false;
465
466 xbps_array_get_cstring_nocopy(rundeps, i, &pkgdep);
467 if ((!xbps_pkgpattern_name(curpkgname, sizeof(curpkgname), pkgdep)) &&
468 (!xbps_pkg_name(curpkgname, sizeof(curpkgname), pkgdep))) {
469 abort();
470 }
471
472 /* TODO: this is kind of a workaround, to avoid calling vpkg_user_conf
473 * over and over again for the same packages which is slow. A better
474 * solution for itself vpkg_user_conf being slow should probably be
475 * implemented at some point.
476 */
477 if (!xbps_dictionary_get_cstring_nocopy(vpkg_cache, curpkgname, &v)) {
478 const char *vpkgname = vpkg_user_conf(xhp, curpkgname);
479 if (vpkgname) {
480 v = vpkgname;
481 } else {
482 v = curpkgname;
483 }
484 errno = 0;
485 if (!xbps_dictionary_set_cstring_nocopy(vpkg_cache, curpkgname, v)) {
486 xbps_error_printf("%s\n", strerror(errno ? errno : ENOMEM));
487 abort();
488 }
489 }
490
491 pkg = xbps_dictionary_get(xhp->pkgdb_revdeps, v);
492 if (pkg == NULL) {
493 alloc = true;
494 pkg = xbps_array_create();
495 }
496 if (!xbps_match_string_in_array(pkg, pkgver)) {
497 xbps_array_add_cstring_nocopy(pkg, pkgver);
498 xbps_dictionary_set(xhp->pkgdb_revdeps, v, pkg);
499 }
500 if (alloc)
501 xbps_object_release(pkg);
502 }
503 }
504 xbps_object_iterator_release(iter);
505 xbps_object_release(vpkg_cache);
506}
507
508xbps_array_t
509xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
510{
511 xbps_dictionary_t pkgd;
512 const char *pkgver = NULL;
513 char pkgname[XBPS_NAME_SIZE];
514
515 if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkg)) == NULL)
516 return NULL;
517
518 generate_full_revdeps_tree(xhp);
519 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
520 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver))
521 return NULL;
522
523 return xbps_dictionary_get(xhp->pkgdb_revdeps, pkgname);
524}
525
526xbps_array_t
527xbps_pkgdb_get_pkg_fulldeptree(struct xbps_handle *xhp, const char *pkg)
528{
529 return xbps_get_pkg_fulldeptree(xhp, pkg, false);
530}
531
532xbps_dictionary_t
533xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
534{
535 xbps_dictionary_t pkgd;
536 const char *pkgver = NULL;
537 char pkgname[XBPS_NAME_SIZE], plist[PATH_MAX];
538
539 if (pkg == NULL)
540 return NULL;
541
542 pkgd = xbps_pkgdb_get_pkg(xhp, pkg);
543 if (pkgd == NULL)
544 return NULL;
545
546 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
547 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver))
548 return NULL;
549
550 snprintf(plist, sizeof(plist)-1, "%s/.%s-files.plist", xhp->metadir, pkgname);
552}
xbps_dictionary_t pkgdb
Definition xbps.h:590
char rootdir[XBPS_MAXPATH]
Definition xbps.h:664
char metadir[XBPS_MAXPATH]
Definition xbps.h:678
char * pkgdb_plist
Definition xbps.h:639
Generic XBPS structure handler for initialization.
Definition xbps.h:560
#define xbps_error_oom()
Log out of memory condition.
Definition xbps.h:776
int xbps_error_errno(int r, const char *fmt,...)
Prints formatted log message to stderr and returns error.
Definition log.c:113
void xbps_error_printf(const char *fmt,...)
Prints error messages to stderr.
Definition log.c:93
void xbps_dbg_printf(const char *fmt,...)
Prints debug messages to stderr.
Definition log.c:67
void xbps_warn_printf(const char *fmt,...)
Prints warning messages to stderr.
Definition log.c:103
xbps_array_t xbps_pkgdb_get_pkg_fulldeptree(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:527
int xbps_pkgdb_foreach_cb_multi(struct xbps_handle *xhp, int(*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *), void *arg)
Definition pkgdb.c:387
xbps_dictionary_t xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:533
void xbps_pkgdb_unlock(struct xbps_handle *xhp)
Definition pkgdb.c:129
xbps_dictionary_t xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *vpkg)
Definition pkgdb.c:422
int xbps_pkgdb_foreach_cb(struct xbps_handle *xhp, int(*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *), void *arg)
Definition pkgdb.c:368
xbps_array_t xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:509
int xbps_pkgdb_update(struct xbps_handle *xhp, bool flush, bool update)
Definition pkgdb.c:307
int xbps_pkgdb_lock(struct xbps_handle *xhp)
Definition pkgdb.c:67
xbps_dictionary_t xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:408
bool xbps_match_string_in_array(xbps_array_t array, const char *val)
int xbps_array_foreach_cb_multi(struct xbps_handle *xhp, xbps_array_t array, xbps_dictionary_t dict, int(*fn)(struct xbps_handle *, xbps_object_t obj, const char *, void *arg, bool *done), void *arg)
Definition plist.c:101
int xbps_array_foreach_cb(struct xbps_handle *xhp, xbps_array_t array, xbps_dictionary_t dict, int(*fn)(struct xbps_handle *, xbps_object_t obj, const char *, void *arg, bool *done), void *arg)
Definition plist.c:200
xbps_dictionary_t xbps_plist_dictionary_from_file(const char *path)
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
bool xbps_pkg_name(char *dst, size_t len, const char *pkg)
Definition util.c:249
int xbps_mkpath(const char *path, mode_t mode)
Definition mkpath.c:42
bool xbps_pkgpattern_name(char *dst, size_t len, const char *pattern)
Definition util.c:285
ssize_t xbps_path_join(char *dst, size_t len,...)
Definition util_path.c:208