XBPS Library API  0.19
The X Binary Package System
rindex_pkgdeps.c
1 /*-
2  * Copyright (c) 2008-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 <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 
31 #include "xbps_api_impl.h"
32 
33 static int
34 store_dependency(struct xbps_handle *xhp,
35  prop_array_t unsorted,
36  prop_dictionary_t repo_pkgd,
37  pkg_state_t repo_pkg_state)
38 {
39  int rv;
40  /*
41  * Overwrite package state in dictionary with same state than the
42  * package currently uses, otherwise not-installed.
43  */
44  if ((rv = xbps_set_pkg_state_dictionary(repo_pkgd, repo_pkg_state)) != 0)
45  return rv;
46  /*
47  * Add required objects into package dep's dictionary.
48  */
49  if (!prop_dictionary_set_bool(repo_pkgd, "automatic-install", true))
50  return EINVAL;
51  /*
52  * Add the dictionary into the unsorted queue.
53  */
54  prop_array_add(unsorted, repo_pkgd);
55  xbps_dbg_printf_append(xhp, "(added)\n");
56 
57  return 0;
58 }
59 
60 static int
61 add_missing_reqdep(struct xbps_handle *xhp, const char *reqpkg)
62 {
63  prop_array_t mdeps;
64  prop_string_t reqpkg_str;
65  prop_object_iterator_t iter = NULL;
66  prop_object_t obj;
67  size_t idx = 0;
68  bool add_pkgdep, pkgfound, update_pkgdep;
69  int rv = 0;
70 
71  assert(reqpkg != NULL);
72 
73  add_pkgdep = update_pkgdep = pkgfound = false;
74  mdeps = prop_dictionary_get(xhp->transd, "missing_deps");
75 
76  reqpkg_str = prop_string_create_cstring_nocopy(reqpkg);
77  if (reqpkg_str == NULL)
78  return errno;
79 
80  iter = prop_array_iterator(mdeps);
81  if (iter == NULL)
82  goto out;
83 
84  while ((obj = prop_object_iterator_next(iter)) != NULL) {
85  const char *curdep, *curver, *pkgver;
86  char *curpkgnamedep = NULL, *pkgnamedep = NULL;
87 
88  assert(prop_object_type(obj) == PROP_TYPE_STRING);
89  curdep = prop_string_cstring_nocopy(obj);
90  curver = xbps_pkgpattern_version(curdep);
91  pkgver = xbps_pkgpattern_version(reqpkg);
92  if (curver == NULL || pkgver == NULL)
93  goto out;
94  curpkgnamedep = xbps_pkgpattern_name(curdep);
95  if (curpkgnamedep == NULL)
96  goto out;
97  pkgnamedep = xbps_pkgpattern_name(reqpkg);
98  if (pkgnamedep == NULL) {
99  free(curpkgnamedep);
100  goto out;
101  }
102  if (strcmp(pkgnamedep, curpkgnamedep) == 0) {
103  pkgfound = true;
104  if (strcmp(curver, pkgver) == 0) {
105  free(curpkgnamedep);
106  free(pkgnamedep);
107  rv = EEXIST;
108  goto out;
109  }
110  /*
111  * if new dependency version is greater than current
112  * one, store it.
113  */
114  xbps_dbg_printf(xhp, "Missing pkgdep name matched, "
115  "curver: %s newver: %s\n", curver, pkgver);
116  if (xbps_cmpver(curver, pkgver) <= 0) {
117  add_pkgdep = false;
118  free(curpkgnamedep);
119  free(pkgnamedep);
120  rv = EEXIST;
121  goto out;
122  }
123  update_pkgdep = true;
124  }
125  free(curpkgnamedep);
126  free(pkgnamedep);
127  if (pkgfound)
128  break;
129 
130  idx++;
131  }
132  add_pkgdep = true;
133 out:
134  if (iter)
135  prop_object_iterator_release(iter);
136  if (update_pkgdep)
137  prop_array_remove(mdeps, idx);
138  if (add_pkgdep && !xbps_add_obj_to_array(mdeps, reqpkg_str)) {
139  prop_object_release(reqpkg_str);
140  return errno;
141  }
142 
143  return rv;
144 }
145 
146 #define MAX_DEPTH 512
147 
148 static int
149 find_repo_deps(struct xbps_handle *xhp,
150  prop_array_t unsorted, /* array of unsorted deps */
151  prop_array_t pkg_rdeps_array, /* current pkg rundeps array */
152  const char *curpkg, /* current pkgver */
153  size_t *depth) /* max recursion depth */
154 {
155  prop_dictionary_t curpkgd, tmpd;
156  prop_object_t obj;
157  prop_object_iterator_t iter;
158  prop_array_t curpkgrdeps;
159  pkg_state_t state;
160  size_t x;
161  const char *reqpkg, *pkgver_q, *reason = NULL;
162  char *pkgname;
163  int rv = 0;
164 
165  if (*depth >= MAX_DEPTH)
166  return ELOOP;
167 
168  /*
169  * Iterate over the list of required run dependencies for
170  * current package.
171  */
172  iter = prop_array_iterator(pkg_rdeps_array);
173  assert(iter);
174 
175  while ((obj = prop_object_iterator_next(iter))) {
176  reqpkg = prop_string_cstring_nocopy(obj);
177  if (xhp->flags & XBPS_FLAG_DEBUG) {
178  xbps_dbg_printf(xhp, "");
179  for (x = 0; x < *depth; x++)
180  xbps_dbg_printf_append(xhp, " ");
181  xbps_dbg_printf_append(xhp, "%s: requires dependency '%s': ",
182  curpkg != NULL ? curpkg : " ", reqpkg);
183  }
184  if (((pkgname = xbps_pkgpattern_name(reqpkg)) == NULL) &&
185  ((pkgname = xbps_pkg_name(reqpkg)) == NULL)) {
186  xbps_dbg_printf(xhp, "can't guess pkgname for %s\n",
187  reqpkg);
188  rv = EINVAL;
189  break;
190  }
191  /*
192  * Pass 1: check if required dependency is already installed
193  * and its version is fully matched.
194  */
195  if (((tmpd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL) &&
196  ((tmpd = xbps_pkgdb_get_virtualpkg(xhp, pkgname)) == NULL)) {
197  free(pkgname);
198  if (errno && errno != ENOENT) {
199  /* error */
200  rv = errno;
201  xbps_dbg_printf(xhp, "failed to find "
202  "installed pkg for `%s': %s\n",
203  reqpkg, strerror(errno));
204  break;
205  }
206  /* Required pkgdep not installed */
207  xbps_dbg_printf_append(xhp, "not installed ");
208  reason = "install";
209  state = XBPS_PKG_STATE_NOT_INSTALLED;
210  } else {
211  free(pkgname);
212  /*
213  * Check if installed version matches the
214  * required pkgdep version.
215  */
216  prop_dictionary_get_cstring_nocopy(tmpd,
217  "pkgver", &pkgver_q);
218 
219  /* Check its state */
220  if ((rv = xbps_pkg_state_dictionary(tmpd, &state)) != 0)
221  break;
222  if (xbps_match_virtual_pkg_in_dict(tmpd,reqpkg,true)) {
223  /*
224  * Check if required dependency is a virtual
225  * package and is satisfied by an
226  * installed package.
227  */
228  xbps_dbg_printf_append(xhp,
229  "[virtual] satisfied by "
230  "`%s'.\n", pkgver_q);
231  continue;
232  }
233  rv = xbps_pkgpattern_match(pkgver_q, reqpkg);
234  if (rv == 0) {
235  /*
236  * Package is installed but does not match
237  * the dependency pattern, update pkg.
238  */
239  xbps_dbg_printf_append(xhp,
240  "installed `%s', "
241  "must be updated.\n", pkgver_q);
242  reason = "update";
243  } else if (rv == 1) {
244  rv = 0;
245  if (state == XBPS_PKG_STATE_UNPACKED) {
246  /*
247  * Package matches the dependency
248  * pattern but was only unpacked,
249  * configure pkg.
250  */
251  xbps_dbg_printf_append(xhp,
252  "installed `%s'"
253  ", must be configured.\n",
254  pkgver_q);
255  reason = "configure";
256  } else if (state == XBPS_PKG_STATE_INSTALLED) {
257  /*
258  * Package matches the dependency
259  * pattern and is fully installed,
260  * skip to next one.
261  */
262  xbps_dbg_printf_append(xhp,
263  "installed "
264  "`%s'.\n", pkgver_q);
265  continue;
266  }
267  } else {
268  /* error matching pkgpattern */
269  xbps_dbg_printf(xhp, "failed to match "
270  "pattern %s with %s\n", reqpkg, pkgver_q);
271  break;
272  }
273  }
274  /*
275  * Pass 2: check if required dependency has been already
276  * added in the transaction dictionary.
277  */
278  if ((curpkgd = xbps_find_pkg_in_array(unsorted, reqpkg)) ||
279  (curpkgd = xbps_find_virtualpkg_in_array(xhp, unsorted, reqpkg))) {
280  prop_dictionary_get_cstring_nocopy(curpkgd,
281  "pkgver", &pkgver_q);
282  xbps_dbg_printf_append(xhp, " (%s queued)\n", pkgver_q);
283  continue;
284  }
285  /*
286  * Pass 3: find required dependency in repository pool.
287  * If dependency does not match add pkg into the missing
288  * deps array and pass to next one.
289  */
290  if (((curpkgd = xbps_rpool_get_pkg(xhp, reqpkg)) == NULL) &&
291  ((curpkgd = xbps_rpool_get_virtualpkg(xhp, reqpkg)) == NULL)) {
292  /* pkg not found, there was some error */
293  if (errno && errno != ENOENT) {
294  xbps_dbg_printf(xhp, "failed to find pkg "
295  "for `%s' in rpool: %s\n",
296  reqpkg, strerror(errno));
297  rv = errno;
298  break;
299  }
300  rv = add_missing_reqdep(xhp, reqpkg);
301  if (rv != 0 && rv != EEXIST) {
302  xbps_dbg_printf_append(xhp, "`%s': "
303  "add_missing_reqdep failed %s\n",
304  reqpkg);
305  break;
306  } else if (rv == EEXIST) {
307  xbps_dbg_printf_append(xhp, "`%s' missing "
308  "dep already added.\n", reqpkg);
309  rv = 0;
310  continue;
311  } else {
312  xbps_dbg_printf_append(xhp, "`%s' added "
313  "into the missing deps array.\n",
314  reqpkg);
315  continue;
316  }
317  }
318  prop_dictionary_get_cstring_nocopy(curpkgd,
319  "pkgver", &pkgver_q);
320  /*
321  * Check if package has matched conflicts.
322  */
323  xbps_pkg_find_conflicts(xhp, unsorted, curpkgd);
324  /*
325  * Package is on repo, add it into the transaction dictionary.
326  */
327  prop_dictionary_set_cstring_nocopy(curpkgd, "transaction", reason);
328  rv = store_dependency(xhp, unsorted, curpkgd, state);
329  if (rv != 0) {
330  xbps_dbg_printf(xhp, "store_dependency failed for "
331  "`%s': %s\n", reqpkg, strerror(rv));
332  break;
333  }
334  /*
335  * If package doesn't have rundeps, pass to the next one.
336  */
337  curpkgrdeps = prop_dictionary_get(curpkgd, "run_depends");
338  if (curpkgrdeps == NULL)
339  continue;
340 
341  if (xhp->flags & XBPS_FLAG_DEBUG) {
342  xbps_dbg_printf(xhp, "");
343  for (x = 0; x < *depth; x++)
344  xbps_dbg_printf_append(xhp, " ");
345 
346  xbps_dbg_printf_append(xhp,
347  "%s: finding dependencies:\n", pkgver_q);
348  }
349  /*
350  * Recursively find rundeps for current pkg dictionary.
351  */
352  (*depth)++;
353  rv = find_repo_deps(xhp, unsorted, curpkgrdeps,
354  pkgver_q, depth);
355  if (rv != 0) {
356  xbps_dbg_printf(xhp, "Error checking %s for rundeps: %s\n",
357  reqpkg, strerror(rv));
358  break;
359  }
360  }
361  prop_object_iterator_release(iter);
362  (*depth)--;
363 
364  return rv;
365 }
366 
367 int HIDDEN
368 xbps_repository_find_deps(struct xbps_handle *xhp,
369  prop_array_t unsorted,
370  prop_dictionary_t repo_pkgd)
371 {
372  prop_array_t pkg_rdeps;
373  const char *pkgver;
374  size_t depth = 0;
375 
376  pkg_rdeps = prop_dictionary_get(repo_pkgd, "run_depends");
377  if (prop_object_type(pkg_rdeps) != PROP_TYPE_ARRAY)
378  return 0;
379  else if (prop_array_count(pkg_rdeps) == 0)
380  return 0;
381 
382  prop_dictionary_get_cstring_nocopy(repo_pkgd, "pkgver", &pkgver);
383  xbps_dbg_printf(xhp, "Finding required dependencies for '%s':\n", pkgver);
384  /*
385  * This will find direct and indirect deps, if any of them is not
386  * there it will be added into the missing_deps array.
387  */
388  return find_repo_deps(xhp, unsorted, pkg_rdeps, pkgver, &depth);
389 }