replace access() calls for debug info where applicable
[pacman-ng.git] / lib / libalpm / trans.c
blob1bab830db5446238d0f3ddba60a44b012130cd10
1 /*
2 * trans.c
4 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
8 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <errno.h>
32 #include <limits.h>
34 /* libalpm */
35 #include "trans.h"
36 #include "alpm_list.h"
37 #include "package.h"
38 #include "util.h"
39 #include "log.h"
40 #include "handle.h"
41 #include "remove.h"
42 #include "sync.h"
43 #include "alpm.h"
45 /** \addtogroup alpm_trans Transaction Functions
46 * @brief Functions to manipulate libalpm transactions
47 * @{
50 /** Initialize the transaction. */
51 int SYMEXPORT alpm_trans_init(alpm_handle_t *handle, alpm_transflag_t flags,
52 alpm_trans_cb_event event, alpm_trans_cb_conv conv,
53 alpm_trans_cb_progress progress)
55 alpm_trans_t *trans;
57 /* Sanity checks */
58 CHECK_HANDLE(handle, return -1);
59 ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, -1));
61 /* lock db */
62 if(!(flags & ALPM_TRANS_FLAG_NOLOCK)) {
63 if(_alpm_handle_lock(handle)) {
64 RET_ERR(handle, ALPM_ERR_HANDLE_LOCK, -1);
68 CALLOC(trans, 1, sizeof(alpm_trans_t), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
69 trans->flags = flags;
70 trans->cb_event = event;
71 trans->cb_conv = conv;
72 trans->cb_progress = progress;
73 trans->state = STATE_INITIALIZED;
75 handle->trans = trans;
77 return 0;
80 static alpm_list_t *check_arch(alpm_handle_t *handle, alpm_list_t *pkgs)
82 alpm_list_t *i;
83 alpm_list_t *invalid = NULL;
85 const char *arch = alpm_option_get_arch(handle);
86 if(!arch) {
87 return NULL;
89 for(i = pkgs; i; i = i->next) {
90 alpm_pkg_t *pkg = i->data;
91 const char *pkgarch = alpm_pkg_get_arch(pkg);
92 if(pkgarch && strcmp(pkgarch, arch) && strcmp(pkgarch, "any")) {
93 char *string;
94 const char *pkgname = alpm_pkg_get_name(pkg);
95 const char *pkgver = alpm_pkg_get_version(pkg);
96 size_t len = strlen(pkgname) + strlen(pkgver) + strlen(pkgarch) + 3;
97 MALLOC(string, len, RET_ERR(handle, ALPM_ERR_MEMORY, invalid));
98 sprintf(string, "%s-%s-%s", pkgname, pkgver, pkgarch);
99 invalid = alpm_list_add(invalid, string);
102 return invalid;
105 /** Prepare a transaction. */
106 int SYMEXPORT alpm_trans_prepare(alpm_handle_t *handle, alpm_list_t **data)
108 alpm_trans_t *trans;
110 /* Sanity checks */
111 CHECK_HANDLE(handle, return -1);
112 ASSERT(data != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
114 trans = handle->trans;
116 ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
117 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1));
119 /* If there's nothing to do, return without complaining */
120 if(trans->add == NULL && trans->remove == NULL) {
121 return 0;
124 alpm_list_t *invalid = check_arch(handle, trans->add);
125 if(invalid) {
126 if(data) {
127 *data = invalid;
129 RET_ERR(handle, ALPM_ERR_PKG_INVALID_ARCH, -1);
132 if(trans->add == NULL) {
133 if(_alpm_remove_prepare(handle, data) == -1) {
134 /* pm_errno is set by _alpm_remove_prepare() */
135 return -1;
137 } else {
138 if(_alpm_sync_prepare(handle, data) == -1) {
139 /* pm_errno is set by _alpm_sync_prepare() */
140 return -1;
144 trans->state = STATE_PREPARED;
146 return 0;
149 /** Commit a transaction. */
150 int SYMEXPORT alpm_trans_commit(alpm_handle_t *handle, alpm_list_t **data)
152 alpm_trans_t *trans;
154 /* Sanity checks */
155 CHECK_HANDLE(handle, return -1);
157 trans = handle->trans;
159 ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
160 ASSERT(trans->state == STATE_PREPARED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_PREPARED, -1));
162 ASSERT(!(trans->flags & ALPM_TRANS_FLAG_NOLOCK), RET_ERR(handle, ALPM_ERR_TRANS_NOT_LOCKED, -1));
164 /* If there's nothing to do, return without complaining */
165 if(trans->add == NULL && trans->remove == NULL) {
166 return 0;
169 trans->state = STATE_COMMITING;
171 if(trans->add == NULL) {
172 if(_alpm_remove_packages(handle) == -1) {
173 /* pm_errno is set by _alpm_remove_commit() */
174 return -1;
176 } else {
177 if(_alpm_sync_commit(handle, data) == -1) {
178 /* pm_errno is set by _alpm_sync_commit() */
179 return -1;
183 trans->state = STATE_COMMITED;
185 return 0;
188 /** Interrupt a transaction. */
189 int SYMEXPORT alpm_trans_interrupt(alpm_handle_t *handle)
191 alpm_trans_t *trans;
193 /* Sanity checks */
194 CHECK_HANDLE(handle, return -1);
196 trans = handle->trans;
197 ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
198 ASSERT(trans->state == STATE_COMMITING || trans->state == STATE_INTERRUPTED,
199 RET_ERR(handle, ALPM_ERR_TRANS_TYPE, -1));
201 trans->state = STATE_INTERRUPTED;
203 return 0;
206 /** Release a transaction. */
207 int SYMEXPORT alpm_trans_release(alpm_handle_t *handle)
209 alpm_trans_t *trans;
211 /* Sanity checks */
212 CHECK_HANDLE(handle, return -1);
214 trans = handle->trans;
215 ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
216 ASSERT(trans->state != STATE_IDLE, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
218 int nolock_flag = trans->flags & ALPM_TRANS_FLAG_NOLOCK;
220 _alpm_trans_free(trans);
221 handle->trans = NULL;
223 /* unlock db */
224 if(!nolock_flag) {
225 if(_alpm_handle_unlock(handle)) {
226 _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove lock file %s\n"),
227 alpm_option_get_lockfile(handle));
228 alpm_logaction(handle, "warning: could not remove lock file %s\n",
229 alpm_option_get_lockfile(handle));
233 return 0;
236 /** @} */
238 void _alpm_trans_free(alpm_trans_t *trans)
240 if(trans == NULL) {
241 return;
244 alpm_list_free_inner(trans->add, (alpm_list_fn_free)_alpm_pkg_free_trans);
245 alpm_list_free(trans->add);
246 alpm_list_free_inner(trans->remove, (alpm_list_fn_free)_alpm_pkg_free);
247 alpm_list_free(trans->remove);
249 FREELIST(trans->skip_remove);
251 FREE(trans);
254 /* A cheap grep for text files, returns 1 if a substring
255 * was found in the text file fn, 0 if it wasn't
257 static int grep(const char *fn, const char *needle)
259 FILE *fp;
261 if((fp = fopen(fn, "r")) == NULL) {
262 return 0;
264 while(!feof(fp)) {
265 char line[1024];
266 if(fgets(line, sizeof(line), fp) == NULL) {
267 continue;
269 /* TODO: this will not work if the search string
270 * ends up being split across line reads */
271 if(strstr(line, needle)) {
272 fclose(fp);
273 return 1;
276 fclose(fp);
277 return 0;
280 int _alpm_runscriptlet(alpm_handle_t *handle, const char *installfn,
281 const char *script, const char *ver, const char *oldver)
283 char scriptfn[PATH_MAX];
284 char cmdline[PATH_MAX];
285 char tmpdir[PATH_MAX];
286 char *argv[] = { "sh", "-c", cmdline, NULL };
287 char *scriptpath;
288 int clean_tmpdir = 0;
289 int retval = 0;
291 if(_alpm_access(handle, NULL, installfn, R_OK) != 0) {
292 _alpm_log(handle, ALPM_LOG_DEBUG, "scriptlet '%s' not found\n", installfn);
293 return 0;
296 /* creates a directory in $root/tmp/ for copying/extracting the scriptlet */
297 snprintf(tmpdir, PATH_MAX, "%stmp/", handle->root);
298 if(access(tmpdir, F_OK) != 0) {
299 _alpm_makepath_mode(tmpdir, 01777);
301 snprintf(tmpdir, PATH_MAX, "%stmp/alpm_XXXXXX", handle->root);
302 if(mkdtemp(tmpdir) == NULL) {
303 _alpm_log(handle, ALPM_LOG_ERROR, _("could not create temp directory\n"));
304 return 1;
305 } else {
306 clean_tmpdir = 1;
309 /* either extract or copy the scriptlet */
310 snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir);
311 if(strcmp(script, "pre_upgrade") == 0 || strcmp(script, "pre_install") == 0) {
312 if(_alpm_unpack_single(handle, installfn, tmpdir, ".INSTALL")) {
313 retval = 1;
315 } else {
316 if(_alpm_copyfile(installfn, scriptfn)) {
317 _alpm_log(handle, ALPM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), scriptfn, strerror(errno));
318 retval = 1;
321 if(retval == 1) {
322 goto cleanup;
325 /* chop off the root so we can find the tmpdir in the chroot */
326 scriptpath = scriptfn + strlen(handle->root) - 1;
328 if(!grep(scriptfn, script)) {
329 /* script not found in scriptlet file */
330 goto cleanup;
333 if(oldver) {
334 snprintf(cmdline, PATH_MAX, ". %s; %s %s %s",
335 scriptpath, script, ver, oldver);
336 } else {
337 snprintf(cmdline, PATH_MAX, ". %s; %s %s",
338 scriptpath, script, ver);
341 _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline);
343 retval = _alpm_run_chroot(handle, "/bin/sh", argv);
345 cleanup:
346 if(clean_tmpdir && _alpm_rmrf(tmpdir)) {
347 _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove tmpdir %s\n"), tmpdir);
350 return retval;
353 alpm_transflag_t SYMEXPORT alpm_trans_get_flags(alpm_handle_t *handle)
355 /* Sanity checks */
356 CHECK_HANDLE(handle, return -1);
357 ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
359 return handle->trans->flags;
362 alpm_list_t SYMEXPORT *alpm_trans_get_add(alpm_handle_t *handle)
364 /* Sanity checks */
365 CHECK_HANDLE(handle, return NULL);
366 ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, NULL));
368 return handle->trans->add;
371 alpm_list_t SYMEXPORT *alpm_trans_get_remove(alpm_handle_t *handle)
373 /* Sanity checks */
374 CHECK_HANDLE(handle, return NULL);
375 ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, NULL));
377 return handle->trans->remove;
379 /* vim: set ts=2 sw=2 noet: */