4 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
5 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
6 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
7 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
31 #include <sys/types.h>
33 #include <sys/statvfs.h>
39 #include "alpm_list.h"
52 /** \addtogroup alpm_trans Transaction Functions
53 * @brief Functions to manipulate libalpm transactions
57 /** Initialize the transaction.
58 * @param type type of the transaction
59 * @param flags flags of the transaction (like nodeps, etc)
60 * @param event event callback function pointer
61 * @param conv question callback function pointer
62 * @param progress progress callback function pointer
63 * @return 0 on success, -1 on error (pm_errno is set accordingly)
65 int SYMEXPORT
alpm_trans_init(pmtranstype_t type
, pmtransflag_t flags
,
66 alpm_trans_cb_event event
, alpm_trans_cb_conv conv
,
67 alpm_trans_cb_progress progress
)
72 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
74 ASSERT(handle
->trans
== NULL
, RET_ERR(PM_ERR_TRANS_NOT_NULL
, -1));
77 handle
->lckfd
= _alpm_lckmk();
78 if(handle
->lckfd
== -1) {
79 RET_ERR(PM_ERR_HANDLE_LOCK
, -1);
82 handle
->trans
= _alpm_trans_new();
83 if(handle
->trans
== NULL
) {
84 RET_ERR(PM_ERR_MEMORY
, -1);
87 return(_alpm_trans_init(handle
->trans
, type
, flags
, event
, conv
, progress
));
90 /** Search for packages to upgrade and add them to the transaction.
91 * @return 0 on success, -1 on error (pm_errno is set accordingly)
93 int SYMEXPORT
alpm_trans_sysupgrade()
99 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
101 trans
= handle
->trans
;
102 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
103 ASSERT(trans
->state
== STATE_INITIALIZED
, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED
, -1));
104 ASSERT(trans
->type
== PM_TRANS_TYPE_SYNC
, RET_ERR(PM_ERR_TRANS_TYPE
, -1));
106 return(_alpm_trans_sysupgrade(trans
));
109 /** Add a target to the transaction.
110 * @param target the name of the target to add
111 * @return 0 on success, -1 on error (pm_errno is set accordingly)
113 int SYMEXPORT
alpm_trans_addtarget(char *target
)
120 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
121 ASSERT(target
!= NULL
&& strlen(target
) != 0, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
123 trans
= handle
->trans
;
124 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
125 ASSERT(trans
->state
== STATE_INITIALIZED
, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED
, -1));
127 return(_alpm_trans_addtarget(trans
, target
));
130 /** Prepare a transaction.
131 * @param data the address of an alpm_list where detailed description
132 * of an error can be dumped (ie. list of conflicting files)
133 * @return 0 on success, -1 on error (pm_errno is set accordingly)
135 int SYMEXPORT
alpm_trans_prepare(alpm_list_t
**data
)
140 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
141 ASSERT(data
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
143 ASSERT(handle
->trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
144 ASSERT(handle
->trans
->state
== STATE_INITIALIZED
, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED
, -1));
146 return(_alpm_trans_prepare(handle
->trans
, data
));
149 /** Commit a transaction.
150 * @param data the address of an alpm_list where detailed description
151 * of an error can be dumped (ie. list of conflicting files)
152 * @return 0 on success, -1 on error (pm_errno is set accordingly)
154 int SYMEXPORT
alpm_trans_commit(alpm_list_t
**data
)
159 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
161 ASSERT(handle
->trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
162 ASSERT(handle
->trans
->state
== STATE_PREPARED
, RET_ERR(PM_ERR_TRANS_NOT_PREPARED
, -1));
164 return(_alpm_trans_commit(handle
->trans
, data
));
167 /** Interrupt a transaction.
168 * @return 0 on success, -1 on error (pm_errno is set accordingly)
170 int SYMEXPORT
alpm_trans_interrupt()
177 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
179 trans
= handle
->trans
;
180 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
181 ASSERT(trans
->state
== STATE_COMMITING
|| trans
->state
== STATE_INTERRUPTED
,
182 RET_ERR(PM_ERR_TRANS_TYPE
, -1));
184 trans
->state
= STATE_INTERRUPTED
;
189 /** Release a transaction.
190 * @return 0 on success, -1 on error (pm_errno is set accordingly)
192 int SYMEXPORT
alpm_trans_release()
199 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
201 trans
= handle
->trans
;
202 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
203 ASSERT(trans
->state
!= STATE_IDLE
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
205 _alpm_trans_free(trans
);
206 handle
->trans
= NULL
;
209 if(handle
->lckfd
!= -1) {
210 close(handle
->lckfd
);
214 _alpm_log(PM_LOG_WARNING
, _("could not remove lock file %s\n"),
215 alpm_option_get_lockfile());
216 alpm_logaction("warning: could not remove lock file %s\n",
217 alpm_option_get_lockfile());
225 pmtrans_t
*_alpm_trans_new()
231 CALLOC(trans
, 1, sizeof(pmtrans_t
), RET_ERR(PM_ERR_MEMORY
, NULL
));
233 trans
->targets
= NULL
;
234 trans
->packages
= NULL
;
235 trans
->skip_add
= NULL
;
236 trans
->skip_remove
= NULL
;
239 trans
->cb_event
= NULL
;
240 trans
->cb_conv
= NULL
;
241 trans
->cb_progress
= NULL
;
242 trans
->state
= STATE_IDLE
;
247 void _alpm_trans_free(pmtrans_t
*trans
)
255 FREELIST(trans
->targets
);
256 if(trans
->type
== PM_TRANS_TYPE_SYNC
) {
257 alpm_list_free_inner(trans
->packages
, (alpm_list_fn_free
)_alpm_sync_free
);
259 alpm_list_free_inner(trans
->packages
, (alpm_list_fn_free
)_alpm_pkg_free
);
261 alpm_list_free(trans
->packages
);
263 FREELIST(trans
->skip_add
);
264 FREELIST(trans
->skip_remove
);
269 int _alpm_trans_init(pmtrans_t
*trans
, pmtranstype_t type
, pmtransflag_t flags
,
270 alpm_trans_cb_event event
, alpm_trans_cb_conv conv
,
271 alpm_trans_cb_progress progress
)
276 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
279 trans
->flags
= flags
;
280 trans
->cb_event
= event
;
281 trans
->cb_conv
= conv
;
282 trans
->cb_progress
= progress
;
283 trans
->state
= STATE_INITIALIZED
;
288 int _alpm_trans_sysupgrade(pmtrans_t
*trans
)
293 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
295 return(_alpm_sync_sysupgrade(trans
, handle
->db_local
, handle
->dbs_sync
,
296 &(trans
->packages
)));
299 /** Add a target to the transaction.
300 * @param trans the current transaction
301 * @param target the name of the target to add
302 * @return 0 on success, -1 on error (pm_errno is set accordingly)
304 int _alpm_trans_addtarget(pmtrans_t
*trans
, char *target
)
309 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
310 ASSERT(target
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
312 if(alpm_list_find_str(trans
->targets
, target
)) {
314 //RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1);
317 switch(trans
->type
) {
318 case PM_TRANS_TYPE_ADD
:
319 case PM_TRANS_TYPE_UPGRADE
:
320 if(_alpm_add_loadtarget(trans
, handle
->db_local
, target
) == -1) {
321 /* pm_errno is set by _alpm_add_loadtarget() */
325 case PM_TRANS_TYPE_REMOVE
:
326 case PM_TRANS_TYPE_REMOVEUPGRADE
:
327 if(_alpm_remove_loadtarget(trans
, handle
->db_local
, target
) == -1) {
328 /* pm_errno is set by _alpm_remove_loadtarget() */
332 case PM_TRANS_TYPE_SYNC
:
333 if(_alpm_sync_addtarget(trans
, handle
->db_local
, handle
->dbs_sync
, target
) == -1) {
334 /* pm_errno is set by _alpm_sync_loadtarget() */
340 trans
->targets
= alpm_list_add(trans
->targets
, strdup(target
));
345 int _alpm_trans_prepare(pmtrans_t
*trans
, alpm_list_t
**data
)
352 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
354 /* If there's nothing to do, return without complaining */
355 if(trans
->packages
== NULL
) {
359 switch(trans
->type
) {
360 case PM_TRANS_TYPE_ADD
:
361 case PM_TRANS_TYPE_UPGRADE
:
362 if(_alpm_add_prepare(trans
, handle
->db_local
, data
) == -1) {
363 /* pm_errno is set by _alpm_add_prepare() */
367 case PM_TRANS_TYPE_REMOVE
:
368 case PM_TRANS_TYPE_REMOVEUPGRADE
:
369 if(_alpm_remove_prepare(trans
, handle
->db_local
, data
) == -1) {
370 /* pm_errno is set by _alpm_remove_prepare() */
374 case PM_TRANS_TYPE_SYNC
:
375 if(_alpm_sync_prepare(trans
, handle
->db_local
, handle
->dbs_sync
, data
) == -1) {
376 /* pm_errno is set by _alpm_sync_prepare() */
382 trans
->state
= STATE_PREPARED
;
387 int _alpm_trans_commit(pmtrans_t
*trans
, alpm_list_t
**data
)
395 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
397 /* If there's nothing to do, return without complaining */
398 if(trans
->packages
== NULL
) {
402 trans
->state
= STATE_COMMITING
;
404 switch(trans
->type
) {
405 case PM_TRANS_TYPE_ADD
:
406 case PM_TRANS_TYPE_UPGRADE
:
407 if(_alpm_add_commit(trans
, handle
->db_local
) == -1) {
408 /* pm_errno is set by _alpm_add_commit() */
412 case PM_TRANS_TYPE_REMOVE
:
413 case PM_TRANS_TYPE_REMOVEUPGRADE
:
414 if(_alpm_remove_commit(trans
, handle
->db_local
) == -1) {
415 /* pm_errno is set by _alpm_remove_commit() */
419 case PM_TRANS_TYPE_SYNC
:
420 if(_alpm_sync_commit(trans
, handle
->db_local
, data
) == -1) {
421 /* pm_errno is set by _alpm_sync_commit() */
427 trans
->state
= STATE_COMMITED
;
432 /* A cheap grep for text files, returns 1 if a substring
433 * was found in the text file fn, 0 if it wasn't
435 static int grep(const char *fn
, const char *needle
)
439 if((fp
= fopen(fn
, "r")) == NULL
) {
444 fgets(line
, 1024, fp
);
448 if(strstr(line
, needle
)) {
457 int _alpm_runscriptlet(const char *root
, const char *installfn
,
458 const char *script
, const char *ver
,
459 const char *oldver
, pmtrans_t
*trans
)
461 char scriptfn
[PATH_MAX
];
462 char cmdline
[PATH_MAX
];
463 char tmpdir
[PATH_MAX
];
468 int clean_tmpdir
= 0;
474 if(stat(installfn
, &buf
)) {
476 _alpm_log(PM_LOG_DEBUG
, "scriptlet '%s' not found\n", installfn
);
480 snprintf(tmpdir
, PATH_MAX
, "%sbin/sh", root
);
481 if(stat(tmpdir
, &buf
)) {
483 _alpm_log(PM_LOG_ERROR
, _("No /bin/sh in root dir (%s), aborting scriptlet\n"), root
);
487 /* creates a directory in $root/tmp/ for copying/extracting the scriptlet */
488 snprintf(tmpdir
, PATH_MAX
, "%stmp/", root
);
489 if(stat(tmpdir
, &buf
)) {
490 _alpm_makepath(tmpdir
);
492 snprintf(tmpdir
, PATH_MAX
, "%stmp/alpm_XXXXXX", root
);
493 if(mkdtemp(tmpdir
) == NULL
) {
494 _alpm_log(PM_LOG_ERROR
, _("could not create temp directory\n"));
500 /* either extract or copy the scriptlet */
501 snprintf(scriptfn
, PATH_MAX
, "%s/.INSTALL", tmpdir
);
502 if(!strcmp(script
, "pre_upgrade") || !strcmp(script
, "pre_install")) {
503 if(_alpm_unpack(installfn
, tmpdir
, ".INSTALL")) {
507 if(_alpm_copyfile(installfn
, scriptfn
)) {
508 _alpm_log(PM_LOG_ERROR
, _("could not copy tempfile to %s (%s)\n"), scriptfn
, strerror(errno
));
516 /* mark the scriptlet as executable */
517 chmod(scriptfn
, 0755);
519 /* chop off the root so we can find the tmpdir in the chroot */
520 scriptpath
= scriptfn
+ strlen(root
) - 1;
522 if(!grep(scriptfn
, script
)) {
523 /* script not found in scriptlet file */
527 /* save the cwd so we can restore it later */
528 if(getcwd(cwd
, PATH_MAX
) == NULL
) {
529 _alpm_log(PM_LOG_ERROR
, _("could not get current working directory\n"));
534 /* just in case our cwd was removed in the upgrade operation */
535 if(chdir(root
) != 0) {
536 _alpm_log(PM_LOG_ERROR
, _("could not change directory to %s (%s)\n"), root
, strerror(errno
));
540 _alpm_log(PM_LOG_DEBUG
, "executing %s script...\n", script
);
543 snprintf(cmdline
, PATH_MAX
, "%s %s %s %s",
544 scriptpath
, script
, ver
, oldver
);
546 snprintf(cmdline
, PATH_MAX
, "%s %s %s",
547 scriptpath
, script
, ver
);
549 _alpm_log(PM_LOG_DEBUG
, "%s\n", cmdline
);
551 /* fork- parent and child each have seperate code blocks below */
554 _alpm_log(PM_LOG_ERROR
, _("could not fork a new process (%s)\n"), strerror(errno
));
561 /* this code runs for the child only (the actual chroot/exec) */
562 _alpm_log(PM_LOG_DEBUG
, "chrooting in %s\n", root
);
563 if(chroot(root
) != 0) {
564 _alpm_log(PM_LOG_ERROR
, _("could not change the root directory (%s)\n"),
568 if(chdir("/") != 0) {
569 _alpm_log(PM_LOG_ERROR
, _("could not change directory to / (%s)\n"),
574 _alpm_log(PM_LOG_DEBUG
, "executing \"%s\"\n", cmdline
);
575 /* execl("/bin/sh", "sh", "-c", cmdline, (char *)NULL); */
576 pipe
= popen(cmdline
, "r");
578 _alpm_log(PM_LOG_ERROR
, _("call to popen failed (%s)"),
585 if(fgets(line
, PATH_MAX
, pipe
) == NULL
)
587 alpm_logaction("%s", line
);
588 EVENT(trans
, PM_TRANS_EVT_SCRIPTLET_INFO
, line
, NULL
);
592 /* this code runs for the parent only (wait on the child) */
595 retpid
= waitpid(pid
, &status
, 0);
597 _alpm_log(PM_LOG_ERROR
, _("call to waitpid failed (%s)\n"),
602 /* check the return status, make sure it is 0 (success) */
603 if(WIFEXITED(status
)) {
604 _alpm_log(PM_LOG_DEBUG
, "call to waitpid succeeded\n");
605 if(WEXITSTATUS(status
) != 0) {
606 _alpm_log(PM_LOG_ERROR
, _("scriptlet failed to execute correctly\n"));
614 if(clean_tmpdir
&& _alpm_rmrf(tmpdir
)) {
615 _alpm_log(PM_LOG_WARNING
, _("could not remove tmpdir %s\n"), tmpdir
);
624 pmtranstype_t SYMEXPORT
alpm_trans_get_type()
627 ASSERT(handle
!= NULL
, return(-1));
628 ASSERT(handle
->trans
!= NULL
, return(-1));
630 return handle
->trans
->type
;
633 unsigned int SYMEXPORT
alpm_trans_get_flags()
636 ASSERT(handle
!= NULL
, return(-1));
637 ASSERT(handle
->trans
!= NULL
, return(-1));
639 return handle
->trans
->flags
;
642 alpm_list_t SYMEXPORT
* alpm_trans_get_targets()
645 ASSERT(handle
!= NULL
, return(NULL
));
646 ASSERT(handle
->trans
!= NULL
, return(NULL
));
648 return handle
->trans
->targets
;
651 alpm_list_t SYMEXPORT
* alpm_trans_get_pkgs()
654 ASSERT(handle
!= NULL
, return(NULL
));
655 ASSERT(handle
->trans
!= NULL
, return(NULL
));
657 return handle
->trans
->packages
;
659 /* vim: set ts=2 sw=2 noet: */