XBPS Library API 20250705
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 roodir is wriable: "
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) == -1) {
112 close(xhp->lock_fd);
113 xhp->lock_fd = -1;
114 return xbps_error_errno(errno, "failed to lock file: %s: %s\n",
115 path, strerror(errno));
116 }
117
118 return 0;
119}
120
121void
123{
124 if (xhp->lock_fd == -1)
125 return;
126 close(xhp->lock_fd);
127 xhp->lock_fd = -1;
128}
129
130static int
131pkgdb_map_vpkgs(struct xbps_handle *xhp)
132{
133 xbps_object_iterator_t iter;
134 xbps_object_t obj;
135 int r = 0;
136
137 if (!xbps_dictionary_count(xhp->pkgdb))
138 return 0;
139
140 if (xhp->vpkgd == NULL) {
141 xhp->vpkgd = xbps_dictionary_create();
142 if (!xhp->vpkgd) {
143 r = -errno;
144 xbps_error_printf("failed to create dictionary\n");
145 return r;
146 }
147 }
148
149 /*
150 * This maps all pkgs that have virtualpkgs in pkgdb.
151 */
152 iter = xbps_dictionary_iterator(xhp->pkgdb);
153 if (!iter) {
154 r = -errno;
155 xbps_error_printf("failed to create iterator");
156 return r;
157 }
158
159 while ((obj = xbps_object_iterator_next(iter))) {
160 xbps_array_t provides;
161 xbps_dictionary_t pkgd;
162 const char *pkgver = NULL;
163 const char *pkgname = NULL;
164 unsigned int cnt;
165
166 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
167 provides = xbps_dictionary_get(pkgd, "provides");
168 cnt = xbps_array_count(provides);
169 if (!cnt)
170 continue;
171
172 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
173 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname);
174 assert(pkgname);
175
176 for (unsigned int i = 0; i < cnt; i++) {
177 char vpkgname[XBPS_NAME_SIZE];
178 const char *vpkg = NULL;
179 xbps_dictionary_t providers;
180 bool alloc = false;
181
182 xbps_array_get_cstring_nocopy(provides, i, &vpkg);
183 if (!xbps_pkg_name(vpkgname, sizeof(vpkgname), vpkg)) {
184 xbps_warn_printf("%s: invalid provides: %s\n", pkgver, vpkg);
185 continue;
186 }
187
188 providers = xbps_dictionary_get(xhp->vpkgd, vpkgname);
189 if (!providers) {
190 providers = xbps_dictionary_create();
191 if (!providers) {
192 r = -errno;
193 xbps_error_printf("failed to create dictionary\n");
194 goto out;
195 }
196 if (!xbps_dictionary_set(xhp->vpkgd, vpkgname, providers)) {
197 r = -errno;
198 xbps_error_printf("failed to set dictionary entry\n");
199 xbps_object_release(providers);
200 goto out;
201 }
202 alloc = true;
203 }
204
205 if (!xbps_dictionary_set_cstring(providers, vpkg, pkgname)) {
206 r = -errno;
207 xbps_error_printf("failed to set dictionary entry\n");
208 if (alloc)
209 xbps_object_release(providers);
210 goto out;
211 }
212 if (alloc)
213 xbps_object_release(providers);
214 xbps_dbg_printf("[pkgdb] added vpkg %s for %s\n", vpkg, pkgname);
215 }
216 }
217out:
218 xbps_object_iterator_release(iter);
219 return r;
220}
221
222static int
223pkgdb_map_names(struct xbps_handle *xhp)
224{
225 xbps_object_iterator_t iter;
226 xbps_object_t obj;
227 int rv = 0;
228
229 if (!xbps_dictionary_count(xhp->pkgdb))
230 return 0;
231
232 /*
233 * This maps all pkgs in pkgdb to have the "pkgname" string property.
234 * This way we do it once and not multiple times.
235 */
236 iter = xbps_dictionary_iterator(xhp->pkgdb);
237 assert(iter);
238
239 while ((obj = xbps_object_iterator_next(iter))) {
240 xbps_dictionary_t pkgd;
241 const char *pkgver;
242 char pkgname[XBPS_NAME_SIZE] = {0};
243
244 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
245 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
246 continue;
247 }
248 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
249 rv = EINVAL;
250 break;
251 }
252 if (!xbps_dictionary_set_cstring(pkgd, "pkgname", pkgname)) {
253 rv = EINVAL;
254 break;
255 }
256 }
257 xbps_object_iterator_release(iter);
258 return rv;
259}
260
261int HIDDEN
262xbps_pkgdb_init(struct xbps_handle *xhp)
263{
264 int rv;
265
266 assert(xhp);
267
268 if (xhp->pkgdb)
269 return 0;
270
271 if (!xhp->pkgdb_plist)
272 xhp->pkgdb_plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_PKGDB);
273
274#if 0
275 if ((rv = xbps_pkgdb_conversion(xhp)) != 0)
276 return rv;
277#endif
278
279
280 if ((rv = xbps_pkgdb_update(xhp, false, true)) != 0) {
281 if (rv != ENOENT)
282 xbps_error_printf("failed to initialize pkgdb: %s\n", strerror(rv));
283 return rv;
284 }
285 if ((rv = pkgdb_map_names(xhp)) != 0) {
286 xbps_dbg_printf("[pkgdb] pkgdb_map_names %s\n", strerror(rv));
287 return rv;
288 }
289 if ((rv = pkgdb_map_vpkgs(xhp)) != 0) {
290 xbps_dbg_printf("[pkgdb] pkgdb_map_vpkgs %s\n", strerror(rv));
291 return rv;
292 }
293 assert(xhp->pkgdb);
294 xbps_dbg_printf("[pkgdb] initialized ok.\n");
295
296 return 0;
297}
298
299int
300xbps_pkgdb_update(struct xbps_handle *xhp, bool flush, bool update)
301{
302 xbps_dictionary_t pkgdb_storage;
303 mode_t prev_umask;
304 static int cached_rv;
305 int rv = 0;
306
307 if (cached_rv && !flush)
308 return cached_rv;
309
310 if (xhp->pkgdb && flush) {
311 pkgdb_storage = xbps_dictionary_internalize_from_file(xhp->pkgdb_plist);
312 if (pkgdb_storage == NULL ||
313 !xbps_dictionary_equals(xhp->pkgdb, pkgdb_storage)) {
314 /* flush dictionary to storage */
315 prev_umask = umask(022);
316 if (!xbps_dictionary_externalize_to_file(xhp->pkgdb, xhp->pkgdb_plist)) {
317 umask(prev_umask);
318 return errno;
319 }
320 umask(prev_umask);
321 }
322 if (pkgdb_storage)
323 xbps_object_release(pkgdb_storage);
324
325 xbps_object_release(xhp->pkgdb);
326 xhp->pkgdb = NULL;
327 cached_rv = 0;
328 }
329 if (!update)
330 return rv;
331
332 /* update copy in memory */
333 if ((xhp->pkgdb = xbps_dictionary_internalize_from_file(xhp->pkgdb_plist)) == NULL) {
334 rv = errno;
335 if (!rv)
336 rv = EINVAL;
337
338 if (rv == ENOENT)
339 xhp->pkgdb = xbps_dictionary_create();
340 else
341 xbps_error_printf("cannot access to pkgdb: %s\n", strerror(rv));
342
343 cached_rv = rv = errno;
344 }
345
346 return rv;
347}
348
349void HIDDEN
350xbps_pkgdb_release(struct xbps_handle *xhp)
351{
352 assert(xhp);
353
355 if (xhp->pkgdb)
356 xbps_object_release(xhp->pkgdb);
357 xbps_dbg_printf("[pkgdb] released ok.\n");
358}
359
360int
362 int (*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *),
363 void *arg)
364{
365 xbps_array_t allkeys;
366 int r;
367
368 // XXX: this should be done before calling the function...
369 if ((r = xbps_pkgdb_init(xhp)) != 0)
370 return r > 0 ? -r : r;
371
372 allkeys = xbps_dictionary_all_keys(xhp->pkgdb);
373 assert(allkeys);
374 r = xbps_array_foreach_cb(xhp, allkeys, xhp->pkgdb, fn, arg);
375 xbps_object_release(allkeys);
376 return r;
377}
378
379int
381 int (*fn)(struct xbps_handle *, xbps_object_t, const char *, void *, bool *),
382 void *arg)
383{
384 xbps_array_t allkeys;
385 int r;
386
387 // XXX: this should be done before calling the function...
388 if ((r = xbps_pkgdb_init(xhp)) != 0)
389 return r > 0 ? -r : r;
390
391 allkeys = xbps_dictionary_all_keys(xhp->pkgdb);
392 if (!allkeys)
393 return xbps_error_oom();
394
395 r = xbps_array_foreach_cb_multi(xhp, allkeys, xhp->pkgdb, fn, arg);
396 xbps_object_release(allkeys);
397 return r;
398}
399
400xbps_dictionary_t
401xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
402{
403 xbps_dictionary_t pkgd;
404
405 if (xbps_pkgdb_init(xhp) != 0)
406 return NULL;
407
408 pkgd = xbps_find_pkg_in_dict(xhp->pkgdb, pkg);
409 if (!pkgd)
410 errno = ENOENT;
411 return pkgd;
412}
413
414xbps_dictionary_t
415xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *vpkg)
416{
417 if (xbps_pkgdb_init(xhp) != 0)
418 return NULL;
419
420 return xbps_find_virtualpkg_in_dict(xhp, xhp->pkgdb, vpkg);
421}
422
423static void
424generate_full_revdeps_tree(struct xbps_handle *xhp)
425{
426 xbps_object_t obj;
427 xbps_object_iterator_t iter;
428 xbps_dictionary_t vpkg_cache;
429
430 if (xhp->pkgdb_revdeps)
431 return;
432
433 xhp->pkgdb_revdeps = xbps_dictionary_create();
434 assert(xhp->pkgdb_revdeps);
435
436 vpkg_cache = xbps_dictionary_create();
437 assert(vpkg_cache);
438
439 iter = xbps_dictionary_iterator(xhp->pkgdb);
440 assert(iter);
441
442 while ((obj = xbps_object_iterator_next(iter))) {
443 xbps_array_t rundeps;
444 xbps_dictionary_t pkgd;
445 const char *pkgver = NULL;
446
447 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
448 rundeps = xbps_dictionary_get(pkgd, "run_depends");
449 if (!xbps_array_count(rundeps))
450 continue;
451
452 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
453 for (unsigned int i = 0; i < xbps_array_count(rundeps); i++) {
454 xbps_array_t pkg;
455 const char *pkgdep = NULL, *v;
456 char curpkgname[XBPS_NAME_SIZE];
457 bool alloc = false;
458
459 xbps_array_get_cstring_nocopy(rundeps, i, &pkgdep);
460 if ((!xbps_pkgpattern_name(curpkgname, sizeof(curpkgname), pkgdep)) &&
461 (!xbps_pkg_name(curpkgname, sizeof(curpkgname), pkgdep))) {
462 abort();
463 }
464
465 /* TODO: this is kind of a workaround, to avoid calling vpkg_user_conf
466 * over and over again for the same packages which is slow. A better
467 * solution for itself vpkg_user_conf being slow should probably be
468 * implemented at some point.
469 */
470 if (!xbps_dictionary_get_cstring_nocopy(vpkg_cache, curpkgname, &v)) {
471 const char *vpkgname = vpkg_user_conf(xhp, curpkgname);
472 if (vpkgname) {
473 v = vpkgname;
474 } else {
475 v = curpkgname;
476 }
477 errno = 0;
478 if (!xbps_dictionary_set_cstring_nocopy(vpkg_cache, curpkgname, v)) {
479 xbps_error_printf("%s\n", strerror(errno ? errno : ENOMEM));
480 abort();
481 }
482 }
483
484 pkg = xbps_dictionary_get(xhp->pkgdb_revdeps, v);
485 if (pkg == NULL) {
486 alloc = true;
487 pkg = xbps_array_create();
488 }
489 if (!xbps_match_string_in_array(pkg, pkgver)) {
490 xbps_array_add_cstring_nocopy(pkg, pkgver);
491 xbps_dictionary_set(xhp->pkgdb_revdeps, v, pkg);
492 }
493 if (alloc)
494 xbps_object_release(pkg);
495 }
496 }
497 xbps_object_iterator_release(iter);
498 xbps_object_release(vpkg_cache);
499}
500
501xbps_array_t
502xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
503{
504 xbps_dictionary_t pkgd;
505 const char *pkgver = NULL;
506 char pkgname[XBPS_NAME_SIZE];
507
508 if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkg)) == NULL)
509 return NULL;
510
511 generate_full_revdeps_tree(xhp);
512 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
513 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver))
514 return NULL;
515
516 return xbps_dictionary_get(xhp->pkgdb_revdeps, pkgname);
517}
518
519xbps_array_t
520xbps_pkgdb_get_pkg_fulldeptree(struct xbps_handle *xhp, const char *pkg)
521{
522 return xbps_get_pkg_fulldeptree(xhp, pkg, false);
523}
524
525xbps_dictionary_t
526xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
527{
528 xbps_dictionary_t pkgd;
529 const char *pkgver = NULL;
530 char pkgname[XBPS_NAME_SIZE], plist[PATH_MAX];
531
532 if (pkg == NULL)
533 return NULL;
534
535 pkgd = xbps_pkgdb_get_pkg(xhp, pkg);
536 if (pkgd == NULL)
537 return NULL;
538
539 xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
540 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver))
541 return NULL;
542
543 snprintf(plist, sizeof(plist)-1, "%s/.%s-files.plist", xhp->metadir, pkgname);
545}
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:118
void xbps_dbg_printf(const char *fmt,...)
Prints debug messages to stderr.
Definition log.c:72
void void xbps_error_printf(const char *fmt,...)
Prints error messages to stderr.
Definition log.c:98
void xbps_warn_printf(const char *fmt,...)
Prints warning messages to stderr.
Definition log.c:108
xbps_array_t xbps_pkgdb_get_pkg_fulldeptree(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:520
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:380
xbps_dictionary_t xbps_pkgdb_get_pkg_files(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:526
void xbps_pkgdb_unlock(struct xbps_handle *xhp)
Definition pkgdb.c:122
xbps_dictionary_t xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *vpkg)
Definition pkgdb.c:415
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:361
xbps_array_t xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:502
int xbps_pkgdb_update(struct xbps_handle *xhp, bool flush, bool update)
Definition pkgdb.c:300
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:401
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:253
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:289
ssize_t xbps_path_join(char *dst, size_t len,...)
Definition util_path.c:208