XBPS Library API  0.19
The X Binary Package System
transaction_commit.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 <ctype.h>
32 #include <assert.h>
33 #include <unistd.h>
34 #include <limits.h>
35 
36 #include "xbps_api_impl.h"
37 
38 /**
39  * @file lib/transaction_commit.c
40  * @brief Transaction handling routines
41  * @defgroup transaction Transaction handling functions
42  *
43  * The following image shows off the full transaction dictionary returned
44  * by xbps_transaction_prepare().
45  *
46  * @image html images/xbps_transaction_dictionary.png
47  *
48  * Legend:
49  * - <b>Salmon bg box</b>: The transaction dictionary.
50  * - <b>White bg box</b>: mandatory objects.
51  * - <b>Grey bg box</b>: optional objects.
52  * - <b>Green bg box</b>: possible value set in the object, only one of them
53  * will be set.
54  *
55  * Text inside of white boxes are the key associated with the object, its
56  * data type is specified on its edge, i.e string, array, integer, dictionary.
57  */
58 
59 static int
60 check_binpkgs_hash(struct xbps_handle *xhp, prop_object_iterator_t iter)
61 {
62  prop_object_t obj;
63  const char *pkgver, *repoloc, *filen, *sha256, *trans;
64  const char *pkgname, *version;
65  char *binfile;
66  int rv = 0;
67 
68  while ((obj = prop_object_iterator_next(iter)) != NULL) {
69  prop_dictionary_get_cstring_nocopy(obj, "transaction", &trans);
70  if ((strcmp(trans, "remove") == 0) ||
71  (strcmp(trans, "configure") == 0))
72  continue;
73 
74  prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
75  prop_dictionary_get_cstring_nocopy(obj, "version", &version);
76  prop_dictionary_get_cstring_nocopy(obj, "repository", &repoloc);
77  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
78  prop_dictionary_get_cstring_nocopy(obj, "filename", &filen);
79  prop_dictionary_get_cstring_nocopy(obj,
80  "filename-sha256", &sha256);
81 
82  binfile = xbps_repository_pkg_path(xhp, obj);
83  if (binfile == NULL) {
84  rv = EINVAL;
85  break;
86  }
87  xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, filen,
88  "Verifying `%s' package integrity...", filen, repoloc);
89  rv = xbps_file_hash_check(binfile, sha256);
90  if (rv != 0) {
91  free(binfile);
92  xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL,
93  rv, pkgname, version,
94  "Failed to verify `%s' package integrity: %s",
95  filen, strerror(rv));
96  break;
97  }
98  free(binfile);
99  }
100  prop_object_iterator_reset(iter);
101 
102  return rv;
103 }
104 
105 static int
106 download_binpkgs(struct xbps_handle *xhp, prop_object_iterator_t iter)
107 {
108  prop_object_t obj;
109  const char *pkgver, *repoloc, *filen, *trans;
110  const char *pkgname, *version, *fetchstr;
111  char *binfile;
112  int rv = 0;
113  bool state_dload = false;
114 
115  while ((obj = prop_object_iterator_next(iter)) != NULL) {
116  prop_dictionary_get_cstring_nocopy(obj, "transaction", &trans);
117  if ((strcmp(trans, "remove") == 0) ||
118  (strcmp(trans, "configure") == 0))
119  continue;
120 
121  prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
122  prop_dictionary_get_cstring_nocopy(obj, "version", &version);
123  prop_dictionary_get_cstring_nocopy(obj, "repository", &repoloc);
124  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
125  prop_dictionary_get_cstring_nocopy(obj, "filename", &filen);
126 
127  binfile = xbps_repository_pkg_path(xhp, obj);
128  if (binfile == NULL) {
129  rv = EINVAL;
130  break;
131  }
132  /*
133  * If downloaded package is in cachedir continue.
134  */
135  if (access(binfile, R_OK) == 0) {
136  free(binfile);
137  continue;
138  }
139  /*
140  * Create cachedir.
141  */
142  if (access(xhp->cachedir, R_OK|X_OK|W_OK) == -1) {
143  if (xbps_mkpath(xhp->cachedir, 0755) == -1) {
144  xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL,
145  errno, pkgname, version,
146  "%s: [trans] cannot create cachedir `%s':"
147  "%s", pkgver, xhp->cachedir,
148  strerror(errno));
149  free(binfile);
150  rv = errno;
151  break;
152  }
153  }
154  if (state_dload == false) {
155  xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD,
156  0, NULL, NULL, NULL);
157  state_dload = true;
158  }
159  xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD,
160  0, pkgname, version,
161  "Downloading binary package `%s' (from `%s')...",
162  filen, repoloc);
163  /*
164  * Fetch binary package.
165  */
166  if (chdir(xhp->cachedir) == -1) {
167  xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL,
168  errno, pkgname, version,
169  "%s: [trans] failed to change dir to cachedir"
170  "`%s': %s", pkgver, xhp->cachedir,
171  strerror(errno));
172  rv = errno;
173  free(binfile);
174  break;
175  }
176 
177  rv = xbps_fetch_file(xhp, binfile, NULL);
178  if (rv == -1) {
179  fetchstr = xbps_fetch_error_string();
180  xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL,
181  fetchLastErrCode != 0 ? fetchLastErrCode : errno,
182  pkgname, version,
183  "%s: [trans] failed to download binary package "
184  "`%s' from `%s': %s", pkgver, filen, repoloc,
185  fetchstr ? fetchstr : strerror(errno));
186  free(binfile);
187  break;
188  }
189  rv = 0;
190  free(binfile);
191  }
192  prop_object_iterator_reset(iter);
193 
194  return rv;
195 }
196 
197 int
199 {
200  prop_object_t obj;
201  prop_object_iterator_t iter;
202  const char *pkgname, *version, *pkgver, *tract;
203  int rv = 0;
204  bool update, install, sr;
205 
206  assert(prop_object_type(xhp->transd) == PROP_TYPE_DICTIONARY);
207 
208  update = install = false;
209  iter = xbps_array_iter_from_dict(xhp->transd, "packages");
210  if (iter == NULL)
211  return EINVAL;
212  /*
213  * Download binary packages (if they come from a remote repository).
214  */
215  if ((rv = download_binpkgs(xhp, iter)) != 0)
216  goto out;
217  /*
218  * Check SHA256 hashes for binary packages in transaction.
219  */
220  xbps_set_cb_state(xhp, XBPS_STATE_TRANS_VERIFY, 0, NULL, NULL, NULL);
221  if ((rv = check_binpkgs_hash(xhp, iter)) != 0)
222  goto out;
223  /*
224  * Install, update, configure or remove packages as specified
225  * in the transaction dictionary.
226  */
227  xbps_set_cb_state(xhp, XBPS_STATE_TRANS_RUN, 0, NULL, NULL, NULL);
228 
229  while ((obj = prop_object_iterator_next(iter)) != NULL) {
230  update = false;
231  prop_dictionary_get_cstring_nocopy(obj, "transaction", &tract);
232  prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
233  prop_dictionary_get_cstring_nocopy(obj, "version", &version);
234  prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
235 
236  if (strcmp(tract, "remove") == 0) {
237  update = false;
238  sr = false;
239  /*
240  * Remove package.
241  */
242  prop_dictionary_get_bool(obj, "remove-and-update",
243  &update);
244  prop_dictionary_get_bool(obj, "softreplace", &sr);
245  rv = xbps_remove_pkg(xhp, pkgver, update, sr);
246  if (rv != 0) {
247  xbps_dbg_printf(xhp, "[trans] failed to "
248  "remove %s-%s\n", pkgname, version);
249  goto out;
250  }
251  } else if (strcmp(tract, "configure") == 0) {
252  /*
253  * Reconfigure pending package.
254  */
255  rv = xbps_configure_pkg(xhp, pkgname, false, false, false);
256  if (rv != 0)
257  goto out;
258  } else {
259  /*
260  * Install or update a package.
261  */
262  if (strcmp(tract, "update") == 0)
263  update = true;
264  else
265  install = true;
266 
267  if (update) {
268  /*
269  * Update a package: execute pre-remove
270  * action if found before unpacking.
271  */
272  xbps_set_cb_state(xhp, XBPS_STATE_UPDATE, 0,
273  pkgname, version, NULL);
274  rv = xbps_remove_pkg(xhp, pkgver, true, false);
275  if (rv != 0) {
276  xbps_set_cb_state(xhp,
277  XBPS_STATE_UPDATE_FAIL,
278  rv, pkgname, version,
279  "%s: [trans] failed to update "
280  "package to `%s': %s", pkgver,
281  version, strerror(rv));
282  goto out;
283  }
284  } else {
285  /* Install a package */
286  xbps_set_cb_state(xhp, XBPS_STATE_INSTALL,
287  0, pkgname, version, NULL);
288  }
289  /*
290  * Unpack binary package.
291  */
292  if ((rv = xbps_unpack_binary_pkg(xhp, obj)) != 0)
293  goto out;
294  /*
295  * Register package.
296  */
297  if ((rv = xbps_register_pkg(xhp, obj, true)) != 0)
298  goto out;
299  }
300  }
301  prop_object_iterator_reset(iter);
302 
303  /* if there are no packages to install or update we are done */
304  if (!update && !install)
305  goto out;
306 
307  /*
308  * Configure all unpacked packages.
309  */
310  xbps_set_cb_state(xhp, XBPS_STATE_TRANS_CONFIGURE, 0, NULL, NULL, NULL);
311 
312  while ((obj = prop_object_iterator_next(iter)) != NULL) {
313  prop_dictionary_get_cstring_nocopy(obj, "transaction", &tract);
314  if ((strcmp(tract, "remove") == 0) ||
315  (strcmp(tract, "configure") == 0))
316  continue;
317 
318  prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
319  prop_dictionary_get_cstring_nocopy(obj, "version", &version);
320  update = false;
321  if (strcmp(tract, "update") == 0)
322  update = true;
323 
324  rv = xbps_configure_pkg(xhp, pkgname, false, update, true);
325  if (rv != 0) {
326  xbps_dbg_printf(xhp, "%s: configure failed for "
327  "%s-%s: %s\n", pkgname, version, strerror(rv));
328  goto out;
329  }
330  /*
331  * Notify client callback when a package has been
332  * installed or updated.
333  */
334  if (update) {
335  xbps_set_cb_state(xhp, XBPS_STATE_UPDATE_DONE, 0,
336  pkgname, version, NULL);
337  } else {
338  xbps_set_cb_state(xhp, XBPS_STATE_INSTALL_DONE, 0,
339  pkgname, version, NULL);
340  }
341  }
342 
343 out:
344  prop_object_iterator_release(iter);
345 
346  return rv;
347 }