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