XBPS Library API  0.19
The X Binary Package System
pkgdb.c
1 /*-
2  * Copyright (c) 2012 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 
32 #include "xbps_api_impl.h"
33 
34 /**
35  * @file lib/pkgdb.c
36  * @brief Package database handling routines
37  * @defgroup pkgdb Package database handling functions
38  *
39  * Functions to manipulate the main package database plist file (pkgdb).
40  *
41  * The following image shown below shows the proplib structure used
42  * by the main package database plist:
43  *
44  * @image html images/xbps_pkgdb_array.png
45  *
46  * Legend:
47  * - <b>Salmon filled box</b>: \a XBPS_PKGDB file internalized.
48  * - <b>White filled box</b>: mandatory objects.
49  * - <b>Grey filled box</b>: optional objects.
50  * - <b>Green filled box</b>: possible value set in the object, only one
51  * of them is set.
52  *
53  * Text inside of white boxes are the key associated with the object, its
54  * data type is specified on its edge, i.e array, bool, integer, string,
55  * dictionary.
56  */
57 int HIDDEN
58 xbps_pkgdb_init(struct xbps_handle *xhp)
59 {
60  int rv;
61 
62  assert(xhp != NULL);
63 
64  if (xhp->pkgdb != NULL)
65  return 0;
66 
67  if ((rv = xbps_pkgdb_update(xhp, false)) != 0) {
68  if (rv != ENOENT)
69  xbps_dbg_printf(xhp, "[pkgdb] cannot internalize "
70  "pkgdb array: %s\n", strerror(rv));
71 
72  return rv;
73  }
74  xbps_dbg_printf(xhp, "[pkgdb] initialized ok.\n");
75 
76  return 0;
77 }
78 
79 int
80 xbps_pkgdb_update(struct xbps_handle *xhp, bool flush)
81 {
82  prop_array_t pkgdb_storage;
83  char *plist;
84  static int cached_rv;
85  int rv = 0;
86 
87  if (cached_rv && !flush)
88  return cached_rv;
89 
90  plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_PKGDB);
91  if (xhp->pkgdb && flush) {
92  pkgdb_storage = prop_array_internalize_from_zfile(plist);
93  if (pkgdb_storage == NULL ||
94  !prop_array_equals(xhp->pkgdb, pkgdb_storage)) {
95  /* flush dictionary to storage */
96  if (!prop_array_externalize_to_file(xhp->pkgdb, plist)) {
97  free(plist);
98  return errno;
99  }
100  }
101  if (pkgdb_storage)
102  prop_object_release(pkgdb_storage);
103 
104  prop_object_release(xhp->pkgdb);
105  xhp->pkgdb = NULL;
106  cached_rv = 0;
107  }
108  /* update copy in memory */
109  if ((xhp->pkgdb = prop_array_internalize_from_zfile(plist)) == NULL) {
110  if (errno == ENOENT)
111  xhp->pkgdb = prop_array_create();
112 
113  cached_rv = rv = errno;
114  }
115  free(plist);
116 
117  return rv;
118 }
119 
120 void HIDDEN
121 xbps_pkgdb_release(struct xbps_handle *xhp)
122 {
123  assert(xhp != NULL);
124 
125  if (xhp->pkgdb == NULL)
126  return;
127 
128  if (prop_object_type(xhp->pkg_metad) == PROP_TYPE_DICTIONARY)
129  prop_object_release(xhp->pkg_metad);
130 
131  prop_object_release(xhp->pkgdb);
132  xhp->pkgdb = NULL;
133  xbps_dbg_printf(xhp, "[pkgdb] released ok.\n");
134 }
135 
136 static int
137 foreach_pkg_cb(struct xbps_handle *xhp,
138  int (*fn)(struct xbps_handle *, prop_object_t, void *, bool *),
139  void *arg,
140  bool reverse)
141 {
142  int rv;
143 
144  if ((rv = xbps_pkgdb_init(xhp)) != 0)
145  return rv;
146 
147  if (reverse)
148  rv = xbps_callback_array_iter_reverse(xhp, xhp->pkgdb, fn, arg);
149  else
150  rv = xbps_callback_array_iter(xhp, xhp->pkgdb, fn, arg);
151 
152  return rv;
153 }
154 
155 int
157  int (*fn)(struct xbps_handle *, prop_object_t, void *, bool *),
158  void *arg)
159 {
160  return foreach_pkg_cb(xhp, fn, arg, true);
161 }
162 
163 int
165  int (*fn)(struct xbps_handle *, prop_object_t, void *, bool *),
166  void *arg)
167 {
168  return foreach_pkg_cb(xhp, fn, arg, false);
169 }
170 
171 prop_dictionary_t
172 xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
173 {
174  if (xbps_pkgdb_init(xhp) != 0)
175  return NULL;
176 
177  return xbps_find_pkg_in_array(xhp->pkgdb, pkg);
178 }
179 
180 prop_dictionary_t
181 xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *vpkg)
182 {
183  if (xbps_pkgdb_init(xhp) != 0)
184  return NULL;
185 
186  return xbps_find_virtualpkg_in_array(xhp, xhp->pkgdb, vpkg);
187 }
188 
189 static prop_dictionary_t
190 get_pkg_metadata(struct xbps_handle *xhp, prop_dictionary_t pkgd)
191 {
192  prop_dictionary_t pkg_metad;
193  const char *pkgname;
194  char *plist;
195 
196  prop_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname);
197 
198  if ((pkg_metad = prop_dictionary_get(xhp->pkg_metad, pkgname)) != NULL)
199  return pkg_metad;
200 
201  plist = xbps_xasprintf("%s/.%s.plist", xhp->metadir, pkgname);
202  pkg_metad = prop_dictionary_internalize_from_zfile(plist);
203  free(plist);
204 
205  if (pkg_metad == NULL) {
206  xbps_dbg_printf(xhp, "[pkgdb] cannot read %s metadata: %s\n",
207  pkgname, strerror(errno));
208  return NULL;
209  }
210 
211  if (xhp->pkg_metad == NULL)
212  xhp->pkg_metad = prop_dictionary_create();
213 
214  prop_dictionary_set(xhp->pkg_metad, pkgname, pkg_metad);
215  prop_object_release(pkg_metad);
216 
217  return pkg_metad;
218 }
219 
220 static void
221 generate_full_revdeps_tree(struct xbps_handle *xhp)
222 {
223  prop_array_t rundeps, pkg;
224  prop_dictionary_t pkgmetad;
225  prop_object_t obj;
226  prop_object_iterator_t iter;
227  const char *pkgver, *pkgdep, *vpkgname;
228  char *curpkgname;
229  unsigned int i;
230  bool alloc;
231 
232  if (xhp->pkgdb_revdeps)
233  return;
234 
235  xhp->pkgdb_revdeps = prop_dictionary_create();
236 
237  iter = prop_array_iterator(xhp->pkgdb);
238  assert(iter);
239 
240  while ((obj = prop_object_iterator_next(iter))) {
241  /*
242  * If run_depends is in pkgdb use it, otherwise fallback to
243  * the slower pkg metadata method.
244  */
245  rundeps = prop_dictionary_get(obj, "run_depends");
246  if (rundeps == NULL) {
247  pkgmetad = get_pkg_metadata(xhp, obj);
248  assert(pkgmetad);
249  rundeps = prop_dictionary_get(pkgmetad, "run_depends");
250  }
251  if (rundeps == NULL || !prop_array_count(rundeps))
252  continue;
253 
254  for (i = 0; i < prop_array_count(rundeps); i++) {
255  alloc = false;
256  prop_array_get_cstring_nocopy(rundeps, i, &pkgdep);
257  curpkgname = xbps_pkgpattern_name(pkgdep);
258  if (curpkgname == NULL)
259  curpkgname = xbps_pkg_name(pkgdep);
260  assert(curpkgname);
261  vpkgname = vpkg_user_conf(xhp, curpkgname, false);
262  if (vpkgname == NULL)
263  vpkgname = curpkgname;
264 
265  pkg = prop_dictionary_get(xhp->pkgdb_revdeps, vpkgname);
266  if (pkg == NULL) {
267  alloc = true;
268  pkg = prop_array_create();
269  }
270  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
271  prop_array_add_cstring_nocopy(pkg, pkgver);
272  prop_dictionary_set(xhp->pkgdb_revdeps, vpkgname, pkg);
273  free(curpkgname);
274  if (alloc)
275  prop_object_release(pkg);
276  }
277  }
278  prop_object_iterator_release(iter);
279 }
280 
281 prop_array_t
282 xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
283 {
284  prop_dictionary_t pkgd;
285  const char *pkgname;
286 
287  if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkg)) == NULL)
288  return NULL;
289 
290  generate_full_revdeps_tree(xhp);
291  prop_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname);
292 
293  return prop_dictionary_get(xhp->pkgdb_revdeps, pkgname);
294 }
295 
296 prop_dictionary_t
297 xbps_pkgdb_get_pkg_metadata(struct xbps_handle *xhp, const char *pkg)
298 {
299  prop_dictionary_t pkgd;
300 
301  pkgd = xbps_pkgdb_get_pkg(xhp, pkg);
302  if (pkgd == NULL)
303  return NULL;
304 
305  return get_pkg_metadata(xhp, pkgd);
306 }
307 
308 bool
309 xbps_pkgdb_remove_pkg(struct xbps_handle *xhp, const char *pkg, bool flush)
310 {
311  bool rv = false;
312 
313  if (xbps_pkgdb_init(xhp) != 0)
314  return false;
315 
316  if (xbps_pkgpattern_version(pkg))
317  rv = xbps_remove_pkg_from_array_by_pattern(xhp->pkgdb, pkg);
318  else if (xbps_pkg_version(pkg))
319  rv = xbps_remove_pkg_from_array_by_pkgver(xhp->pkgdb, pkg);
320  else
321  rv = xbps_remove_pkg_from_array_by_name(xhp->pkgdb, pkg);
322 
323  if (!flush || !rv)
324  return rv;
325 
326  if ((xbps_pkgdb_update(xhp, true)) != 0)
327  return false;
328 
329  return true;
330 }
331 
332 bool
334  prop_dictionary_t pkgd,
335  const char *pkg,
336  bool flush)
337 {
338  int rv;
339 
340  if (xbps_pkgdb_init(xhp) != 0)
341  return false;
342 
343  if (xbps_pkgpattern_version(pkg))
344  rv = xbps_array_replace_dict_by_pattern(xhp->pkgdb, pkgd, pkg);
345  else
346  rv = xbps_array_replace_dict_by_name(xhp->pkgdb, pkgd, pkg);
347 
348  if (!flush)
349  return rv != 0 ? false : true;
350 
351  if ((xbps_pkgdb_update(xhp, true)) != 0)
352  return false;
353 
354  return true;
355 }