XBPS Library API  0.19
The X Binary Package System
transaction_ops.c
1 /*-
2  * Copyright (c) 2009-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 #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  */
55 enum {
56  TRANS_INSTALL = 1,
57  TRANS_UPDATE
58 };
59 
60 static int
61 trans_find_pkg(struct xbps_handle *xhp, const char *pkg, int action)
62 {
63  prop_dictionary_t pkg_pkgdb, pkg_repod;
64  prop_array_t unsorted;
65  const char *pkgname, *repoloc, *repover, *repopkgver, *instver, *reason;
66  int rv = 0;
67  pkg_state_t state = 0;
68 
69  assert(pkg != NULL);
70 
71  /*
72  * Find out if the pkg has been found in repository pool.
73  */
74  if (action == TRANS_INSTALL) {
75  reason = "install";
76  if (((pkg_repod = xbps_rpool_get_pkg(xhp, pkg)) == NULL) &&
77  ((pkg_repod = xbps_rpool_get_virtualpkg(xhp, pkg)) == NULL)) {
78  /* not found */
79  return ENOENT;
80  }
81  } else {
82  if ((pkg_pkgdb = xbps_pkgdb_get_pkg(xhp, pkg)) == NULL)
83  return ENODEV;
84 
85  reason = "update";
86  if ((pkg_repod = xbps_rpool_get_pkg(xhp, pkg)) == NULL) {
87  /* not found */
88  return ENOENT;
89  }
90  }
91  prop_dictionary_get_cstring_nocopy(pkg_repod, "pkgname", &pkgname);
92  prop_dictionary_get_cstring_nocopy(pkg_repod, "version", &repover);
93  prop_dictionary_get_cstring_nocopy(pkg_repod, "pkgver", &repopkgver);
94  prop_dictionary_get_cstring_nocopy(pkg_repod, "repository", &repoloc);
95 
96  if (action == TRANS_UPDATE) {
97  /*
98  * Compare installed version vs best pkg available in repos.
99  */
100  prop_dictionary_get_cstring_nocopy(pkg_pkgdb,
101  "version", &instver);
102  if (xbps_cmpver(repover, instver) <= 0) {
103  xbps_dbg_printf(xhp, "[rpool] Skipping `%s-%s' "
104  "(installed: %s-%s) from repository `%s'\n",
105  pkgname, repover, pkgname, instver, repoloc);
106  return EEXIST;
107  }
108  }
109  /*
110  * Prepare transaction dictionary.
111  */
112  if ((rv = xbps_transaction_init(xhp)) != 0)
113  return rv;
114 
115  unsorted = prop_dictionary_get(xhp->transd, "unsorted_deps");
116  /*
117  * Find out if package has matched conflicts.
118  */
119  xbps_pkg_find_conflicts(xhp, unsorted, pkg_repod);
120 
121  /*
122  * Find out if package being updated matches the one already
123  * in transaction, in that case ignore it.
124  */
125  if (action == TRANS_UPDATE) {
126  if (xbps_find_pkg_in_array(unsorted, repopkgver)) {
127  xbps_dbg_printf(xhp, "[update] `%s' already queued in "
128  "transaction.\n", repopkgver);
129  return EEXIST;
130  }
131  }
132 
133  if ((rv = xbps_repository_find_deps(xhp, unsorted, pkg_repod)) != 0)
134  return rv;
135  /*
136  * Set package state in dictionary with same state than the
137  * package currently uses, otherwise not-installed.
138  */
139  if ((rv = xbps_pkg_state_installed(xhp, pkgname, &state)) != 0) {
140  if (rv != ENOENT)
141  return rv;
142  /* Package not installed, don't error out */
143  state = XBPS_PKG_STATE_NOT_INSTALLED;
144  }
145  if ((rv = xbps_set_pkg_state_dictionary(pkg_repod, state)) != 0)
146  return rv;
147 
148  if (state == XBPS_PKG_STATE_UNPACKED)
149  reason = "configure";
150  else if (state == XBPS_PKG_STATE_NOT_INSTALLED)
151  reason = "install";
152 
153  /*
154  * Set transaction obj in pkg dictionary to "install", "configure"
155  * or "update".
156  */
157  if (!prop_dictionary_set_cstring_nocopy(pkg_repod,
158  "transaction", reason))
159  return EINVAL;
160 
161  /*
162  * Add the pkg dictionary from repository's index dictionary into
163  * the "unsorted" queue.
164  */
165  if (!prop_array_add(unsorted, pkg_repod))
166  return EINVAL;
167 
168  xbps_dbg_printf(xhp, "%s-%s: added into the transaction (%s).\n",
169  pkgname, repover, repoloc);
170 
171  return 0;
172 }
173 
174 int
176 {
177  prop_object_t obj;
178  prop_object_iterator_t iter;
179  const char *pkgname, *holdpkg;
180  bool foundhold = false, newpkg_found = false;
181  int rv = 0;
182  size_t x;
183 
184  if ((rv = xbps_pkgdb_init(xhp)) != 0)
185  return rv;
186 
187  iter = prop_array_iterator(xhp->pkgdb);
188  assert(iter);
189 
190  while ((obj = prop_object_iterator_next(iter))) {
191  prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
192 
193  for (x = 0; x < cfg_size(xhp->cfg, "PackagesOnHold"); x++) {
194  holdpkg = cfg_getnstr(xhp->cfg, "PackagesOnHold", x);
195  if ((strcmp(holdpkg, pkgname) == 0) ||
196  (fnmatch(holdpkg, pkgname, FNM_PERIOD) == 0)) {
197  xbps_dbg_printf(xhp, "[rpool] package `%s' "
198  "on hold, ignoring updates.\n", pkgname);
199  foundhold = true;
200  break;
201  }
202  }
203  if (foundhold) {
204  foundhold = false;
205  continue;
206  }
207  rv = trans_find_pkg(xhp, pkgname, TRANS_UPDATE);
208  if (rv == 0)
209  newpkg_found = true;
210  else if (rv == ENOENT || rv == EEXIST || rv == ENODEV) {
211  /*
212  * missing pkg or installed version is greater than or
213  * equal than pkg in repositories.
214  */
215  rv = 0;
216  }
217  }
218  prop_object_iterator_release(iter);
219 
220  return newpkg_found ? rv : EEXIST;
221 }
222 
223 int
224 xbps_transaction_update_pkg(struct xbps_handle *xhp, const char *pkg)
225 {
226  return trans_find_pkg(xhp, pkg, TRANS_UPDATE);
227 }
228 
229 int
230 xbps_transaction_install_pkg(struct xbps_handle *xhp, const char *pkg,
231  bool reinstall)
232 {
233  prop_dictionary_t pkgd = NULL;
234  pkg_state_t state;
235 
236  if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkg)) ||
237  (pkgd = xbps_pkgdb_get_virtualpkg(xhp, pkg))) {
238  if (xbps_pkg_state_dictionary(pkgd, &state) != 0)
239  return EINVAL;
240  if ((state == XBPS_PKG_STATE_INSTALLED) && !reinstall) {
241  /* error out if pkg installed and no reinstall */
242  return EEXIST;
243  }
244  }
245 
246  return trans_find_pkg(xhp, pkg, TRANS_INSTALL);
247 }
248 
249 int
251  const char *pkgname,
252  bool recursive)
253 {
254  prop_dictionary_t pkgd;
255  prop_array_t unsorted, orphans, orphans_pkg, reqby;
256  prop_object_t obj;
257  const char *pkgver;
258  size_t count;
259  int rv = 0;
260 
261  assert(pkgname != NULL);
262 
263  if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL) {
264  /* pkg not installed */
265  return ENOENT;
266  }
267  /*
268  * Prepare transaction dictionary and missing deps array.
269  */
270  if ((rv = xbps_transaction_init(xhp)) != 0)
271  return rv;
272 
273  unsorted = prop_dictionary_get(xhp->transd, "unsorted_deps");
274 
275  if (!recursive)
276  goto rmpkg;
277  /*
278  * If recursive is set, find out which packages would be orphans
279  * if the supplied package were already removed.
280  */
281  if ((orphans_pkg = prop_array_create()) == NULL)
282  return ENOMEM;
283 
284  prop_array_set_cstring_nocopy(orphans_pkg, 0, pkgname);
285  orphans = xbps_find_pkg_orphans(xhp, orphans_pkg);
286  prop_object_release(orphans_pkg);
287  if (prop_object_type(orphans) != PROP_TYPE_ARRAY)
288  return EINVAL;
289 
290  count = prop_array_count(orphans);
291  while (count--) {
292  obj = prop_array_get(orphans, count);
293  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
294  prop_dictionary_set_cstring_nocopy(obj, "transaction", "remove");
295  prop_array_add(unsorted, obj);
296  xbps_dbg_printf(xhp, "%s: added into transaction (remove).\n", pkgver);
297  }
298  prop_object_release(orphans);
299 rmpkg:
300  /*
301  * Add pkg dictionary into the transaction unsorted queue.
302  */
303  prop_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
304  prop_dictionary_set_cstring_nocopy(pkgd, "transaction", "remove");
305  prop_array_add(unsorted, pkgd);
306  xbps_dbg_printf(xhp, "%s: added into transaction (remove).\n", pkgver);
307  reqby = xbps_pkgdb_get_pkg_revdeps(xhp, pkgver);
308  /*
309  * If target pkg is required by any installed pkg, the client must be aware
310  * of this to take appropiate action.
311  */
312  if ((prop_object_type(reqby) == PROP_TYPE_ARRAY) &&
313  (prop_array_count(reqby) > 0))
314  rv = EEXIST;
315 
316  return rv;
317 }
318 
319 int
321 {
322  prop_array_t orphans, unsorted;
323  prop_object_t obj;
324  const char *pkgver;
325  size_t count;
326  int rv = 0;
327 
328  orphans = xbps_find_pkg_orphans(xhp, NULL);
329  if (prop_object_type(orphans) != PROP_TYPE_ARRAY)
330  return EINVAL;
331 
332  count = prop_array_count(orphans);
333  if (count == 0) {
334  /* no orphans? we are done */
335  rv = ENOENT;
336  goto out;
337  }
338  /*
339  * Prepare transaction dictionary and missing deps array.
340  */
341  if ((rv = xbps_transaction_init(xhp)) != 0)
342  goto out;
343 
344  unsorted = prop_dictionary_get(xhp->transd, "unsorted_deps");
345  /*
346  * Add pkg orphan dictionary into the transaction unsorted queue.
347  */
348  while (count--) {
349  obj = prop_array_get(orphans, count);
350  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
351  prop_dictionary_set_cstring_nocopy(obj,
352  "transaction", "remove");
353  prop_array_add(unsorted, obj);
354  xbps_dbg_printf(xhp, "%s: added (remove).\n", pkgver);
355  }
356 out:
357  if (orphans != NULL)
358  prop_object_release(orphans);
359 
360  return rv;
361 }