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/>.
30 #include <sys/types.h>
32 #include <sys/statvfs.h>
38 #include "alpm_list.h"
49 /** \addtogroup alpm_trans Transaction Functions
50 * @brief Functions to manipulate libalpm transactions
54 /** Initialize the transaction.
55 * @param flags flags of the transaction (like nodeps, etc)
56 * @param event event callback function pointer
57 * @param conv question callback function pointer
58 * @param progress progress callback function pointer
59 * @return 0 on success, -1 on error (pm_errno is set accordingly)
61 int SYMEXPORT
alpm_trans_init(pmtransflag_t flags
,
62 alpm_trans_cb_event event
, alpm_trans_cb_conv conv
,
63 alpm_trans_cb_progress progress
)
70 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
72 ASSERT(handle
->trans
== NULL
, RET_ERR(PM_ERR_TRANS_NOT_NULL
, -1));
75 if(!(flags
& PM_TRANS_FLAG_NOLOCK
)) {
76 handle
->lckfd
= _alpm_lckmk();
77 if(handle
->lckfd
== -1) {
78 RET_ERR(PM_ERR_HANDLE_LOCK
, -1);
82 trans
= _alpm_trans_new();
84 RET_ERR(PM_ERR_MEMORY
, -1);
88 trans
->cb_event
= event
;
89 trans
->cb_conv
= conv
;
90 trans
->cb_progress
= progress
;
91 trans
->state
= STATE_INITIALIZED
;
93 handle
->trans
= trans
;
98 static alpm_list_t
*check_arch(alpm_list_t
*pkgs
)
101 alpm_list_t
*invalid
= NULL
;
103 const char *arch
= alpm_option_get_arch();
107 for(i
= pkgs
; i
; i
= i
->next
) {
108 pmpkg_t
*pkg
= i
->data
;
109 const char *pkgarch
= alpm_pkg_get_arch(pkg
);
110 if(pkgarch
&& strcmp(pkgarch
, arch
) && strcmp(pkgarch
, "any")) {
112 const char *pkgname
= alpm_pkg_get_name(pkg
);
113 const char *pkgver
= alpm_pkg_get_version(pkg
);
114 size_t len
= strlen(pkgname
) + strlen(pkgver
) + strlen(pkgarch
) + 3;
115 MALLOC(string
, len
, RET_ERR(PM_ERR_MEMORY
, invalid
));
116 sprintf(string
, "%s-%s-%s", pkgname
, pkgver
, pkgarch
);
117 invalid
= alpm_list_add(invalid
, string
);
123 /** Prepare a transaction.
124 * @param data the address of an alpm_list where detailed description
125 * of an error can be dumped (ie. list of conflicting files)
126 * @return 0 on success, -1 on error (pm_errno is set accordingly)
128 int SYMEXPORT
alpm_trans_prepare(alpm_list_t
**data
)
135 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
136 ASSERT(data
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
138 trans
= handle
->trans
;
140 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
141 ASSERT(trans
->state
== STATE_INITIALIZED
, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED
, -1));
143 /* If there's nothing to do, return without complaining */
144 if(trans
->add
== NULL
&& trans
->remove
== NULL
) {
148 alpm_list_t
*invalid
= check_arch(trans
->add
);
153 RET_ERR(PM_ERR_PKG_INVALID_ARCH
, -1);
156 if(trans
->add
== NULL
) {
157 if(_alpm_remove_prepare(trans
, handle
->db_local
, data
) == -1) {
158 /* pm_errno is set by _alpm_remove_prepare() */
162 if(_alpm_sync_prepare(trans
, handle
->db_local
, handle
->dbs_sync
, data
) == -1) {
163 /* pm_errno is set by _alpm_sync_prepare() */
168 trans
->state
= STATE_PREPARED
;
173 /** Commit a transaction.
174 * @param data the address of an alpm_list where detailed description
175 * of an error can be dumped (ie. list of conflicting files)
176 * @return 0 on success, -1 on error (pm_errno is set accordingly)
178 int SYMEXPORT
alpm_trans_commit(alpm_list_t
**data
)
185 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
187 trans
= handle
->trans
;
189 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
190 ASSERT(trans
->state
== STATE_PREPARED
, RET_ERR(PM_ERR_TRANS_NOT_PREPARED
, -1));
192 ASSERT(!(trans
->flags
& PM_TRANS_FLAG_NOLOCK
), RET_ERR(PM_ERR_TRANS_NOT_LOCKED
, -1));
194 /* If there's nothing to do, return without complaining */
195 if(trans
->add
== NULL
&& trans
->remove
== NULL
) {
199 trans
->state
= STATE_COMMITING
;
201 if(trans
->add
== NULL
) {
202 if(_alpm_remove_packages(trans
, handle
->db_local
) == -1) {
203 /* pm_errno is set by _alpm_remove_commit() */
207 if(_alpm_sync_commit(trans
, handle
->db_local
, data
) == -1) {
208 /* pm_errno is set by _alpm_sync_commit() */
213 trans
->state
= STATE_COMMITED
;
218 /** Interrupt a transaction.
219 * @return 0 on success, -1 on error (pm_errno is set accordingly)
221 int SYMEXPORT
alpm_trans_interrupt()
228 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
230 trans
= handle
->trans
;
231 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
232 ASSERT(trans
->state
== STATE_COMMITING
|| trans
->state
== STATE_INTERRUPTED
,
233 RET_ERR(PM_ERR_TRANS_TYPE
, -1));
235 trans
->state
= STATE_INTERRUPTED
;
240 /** Release a transaction.
241 * @return 0 on success, -1 on error (pm_errno is set accordingly)
243 int SYMEXPORT
alpm_trans_release()
250 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
252 trans
= handle
->trans
;
253 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
254 ASSERT(trans
->state
!= STATE_IDLE
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
256 int nolock_flag
= trans
->flags
& PM_TRANS_FLAG_NOLOCK
;
258 _alpm_trans_free(trans
);
259 handle
->trans
= NULL
;
263 if(handle
->lckfd
!= -1) {
266 fd
= close(handle
->lckfd
);
267 } while(fd
== -1 && errno
== EINTR
);
271 _alpm_log(PM_LOG_WARNING
, _("could not remove lock file %s\n"),
272 alpm_option_get_lockfile());
273 alpm_logaction("warning: could not remove lock file %s\n",
274 alpm_option_get_lockfile());
283 pmtrans_t
*_alpm_trans_new()
289 CALLOC(trans
, 1, sizeof(pmtrans_t
), RET_ERR(PM_ERR_MEMORY
, NULL
));
290 trans
->state
= STATE_IDLE
;
295 void _alpm_trans_free(pmtrans_t
*trans
)
303 alpm_list_free_inner(trans
->add
, (alpm_list_fn_free
)_alpm_pkg_free_trans
);
304 alpm_list_free(trans
->add
);
305 alpm_list_free_inner(trans
->remove
, (alpm_list_fn_free
)_alpm_pkg_free
);
306 alpm_list_free(trans
->remove
);
308 FREELIST(trans
->skip_add
);
309 FREELIST(trans
->skip_remove
);
314 /* A cheap grep for text files, returns 1 if a substring
315 * was found in the text file fn, 0 if it wasn't
317 static int grep(const char *fn
, const char *needle
)
321 if((fp
= fopen(fn
, "r")) == NULL
) {
326 if(fgets(line
, sizeof(line
), fp
) == NULL
) {
329 /* TODO: this will not work if the search string
330 * ends up being split across line reads */
331 if(strstr(line
, needle
)) {
340 int _alpm_runscriptlet(const char *root
, const char *installfn
,
341 const char *script
, const char *ver
,
342 const char *oldver
, pmtrans_t
*trans
)
344 char scriptfn
[PATH_MAX
];
345 char cmdline
[PATH_MAX
];
346 char tmpdir
[PATH_MAX
];
347 char *argv
[] = { "sh", "-c", cmdline
, NULL
};
349 int clean_tmpdir
= 0;
354 if(access(installfn
, R_OK
)) {
356 _alpm_log(PM_LOG_DEBUG
, "scriptlet '%s' not found\n", installfn
);
360 /* creates a directory in $root/tmp/ for copying/extracting the scriptlet */
361 snprintf(tmpdir
, PATH_MAX
, "%stmp/", root
);
362 if(access(tmpdir
, F_OK
) != 0) {
363 _alpm_makepath_mode(tmpdir
, 01777);
365 snprintf(tmpdir
, PATH_MAX
, "%stmp/alpm_XXXXXX", root
);
366 if(mkdtemp(tmpdir
) == NULL
) {
367 _alpm_log(PM_LOG_ERROR
, _("could not create temp directory\n"));
373 /* either extract or copy the scriptlet */
374 snprintf(scriptfn
, PATH_MAX
, "%s/.INSTALL", tmpdir
);
375 if(strcmp(script
, "pre_upgrade") == 0 || strcmp(script
, "pre_install") == 0) {
376 if(_alpm_unpack_single(installfn
, tmpdir
, ".INSTALL")) {
380 if(_alpm_copyfile(installfn
, scriptfn
)) {
381 _alpm_log(PM_LOG_ERROR
, _("could not copy tempfile to %s (%s)\n"), scriptfn
, strerror(errno
));
389 /* chop off the root so we can find the tmpdir in the chroot */
390 scriptpath
= scriptfn
+ strlen(root
) - 1;
392 if(!grep(scriptfn
, script
)) {
393 /* script not found in scriptlet file */
398 snprintf(cmdline
, PATH_MAX
, ". %s; %s %s %s",
399 scriptpath
, script
, ver
, oldver
);
401 snprintf(cmdline
, PATH_MAX
, ". %s; %s %s",
402 scriptpath
, script
, ver
);
405 _alpm_log(PM_LOG_DEBUG
, "executing \"%s\"\n", cmdline
);
407 retval
= _alpm_run_chroot(root
, "/bin/sh", argv
);
410 if(clean_tmpdir
&& _alpm_rmrf(tmpdir
)) {
411 _alpm_log(PM_LOG_WARNING
, _("could not remove tmpdir %s\n"), tmpdir
);
417 int SYMEXPORT
alpm_trans_get_flags()
420 ASSERT(handle
!= NULL
, return(-1));
421 ASSERT(handle
->trans
!= NULL
, return(-1));
423 return handle
->trans
->flags
;
426 alpm_list_t SYMEXPORT
* alpm_trans_get_add()
429 ASSERT(handle
!= NULL
, return(NULL
));
430 ASSERT(handle
->trans
!= NULL
, return(NULL
));
432 return handle
->trans
->add
;
435 alpm_list_t SYMEXPORT
* alpm_trans_get_remove()
438 ASSERT(handle
!= NULL
, return(NULL
));
439 ASSERT(handle
->trans
!= NULL
, return(NULL
));
441 return handle
->trans
->remove
;
443 /* vim: set ts=2 sw=2 noet: */