libalpm
Arch Linux Package Manager Library
trans.c
Go to the documentation of this file.
00001 /*
00002  *  trans.c
00003  *
00004  *  Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
00005  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
00006  *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
00007  *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
00008  *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022  */
00023 
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <unistd.h>
00028 #include <sys/types.h>
00029 #include <errno.h>
00030 #include <limits.h>
00031 
00032 /* libalpm */
00033 #include "trans.h"
00034 #include "alpm_list.h"
00035 #include "package.h"
00036 #include "util.h"
00037 #include "log.h"
00038 #include "handle.h"
00039 #include "remove.h"
00040 #include "sync.h"
00041 #include "alpm.h"
00042 
00043 /** \addtogroup alpm_trans Transaction Functions
00044  * @brief Functions to manipulate libalpm transactions
00045  * @{
00046  */
00047 
00048 /** Initialize the transaction. */
00049 int SYMEXPORT alpm_trans_init(alpm_handle_t *handle, alpm_transflag_t flags)
00050 {
00051     alpm_trans_t *trans;
00052 
00053     /* Sanity checks */
00054     CHECK_HANDLE(handle, return -1);
00055     ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, -1));
00056 
00057     /* lock db */
00058     if(!(flags & ALPM_TRANS_FLAG_NOLOCK)) {
00059         if(_alpm_handle_lock(handle)) {
00060             RET_ERR(handle, ALPM_ERR_HANDLE_LOCK, -1);
00061         }
00062     }
00063 
00064     CALLOC(trans, 1, sizeof(alpm_trans_t), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00065     trans->flags = flags;
00066     trans->state = STATE_INITIALIZED;
00067 
00068     handle->trans = trans;
00069 
00070     return 0;
00071 }
00072 
00073 static alpm_list_t *check_arch(alpm_handle_t *handle, alpm_list_t *pkgs)
00074 {
00075     alpm_list_t *i;
00076     alpm_list_t *invalid = NULL;
00077 
00078     const char *arch = handle->arch;
00079     if(!arch) {
00080         return NULL;
00081     }
00082     for(i = pkgs; i; i = i->next) {
00083         alpm_pkg_t *pkg = i->data;
00084         const char *pkgarch = alpm_pkg_get_arch(pkg);
00085         if(pkgarch && strcmp(pkgarch, arch) && strcmp(pkgarch, "any")) {
00086             char *string;
00087             const char *pkgname = pkg->name;
00088             const char *pkgver = pkg->version;
00089             size_t len = strlen(pkgname) + strlen(pkgver) + strlen(pkgarch) + 3;
00090             MALLOC(string, len, RET_ERR(handle, ALPM_ERR_MEMORY, invalid));
00091             sprintf(string, "%s-%s-%s", pkgname, pkgver, pkgarch);
00092             invalid = alpm_list_add(invalid, string);
00093         }
00094     }
00095     return invalid;
00096 }
00097 
00098 /** Prepare a transaction. */
00099 int SYMEXPORT alpm_trans_prepare(alpm_handle_t *handle, alpm_list_t **data)
00100 {
00101     alpm_trans_t *trans;
00102 
00103     /* Sanity checks */
00104     CHECK_HANDLE(handle, return -1);
00105     ASSERT(data != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
00106 
00107     trans = handle->trans;
00108 
00109     ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00110     ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1));
00111 
00112     /* If there's nothing to do, return without complaining */
00113     if(trans->add == NULL && trans->remove == NULL) {
00114         return 0;
00115     }
00116 
00117     alpm_list_t *invalid = check_arch(handle, trans->add);
00118     if(invalid) {
00119         if(data) {
00120             *data = invalid;
00121         }
00122         RET_ERR(handle, ALPM_ERR_PKG_INVALID_ARCH, -1);
00123     }
00124 
00125     if(trans->add == NULL) {
00126         if(_alpm_remove_prepare(handle, data) == -1) {
00127             /* pm_errno is set by _alpm_remove_prepare() */
00128             return -1;
00129         }
00130     }   else {
00131         if(_alpm_sync_prepare(handle, data) == -1) {
00132             /* pm_errno is set by _alpm_sync_prepare() */
00133             return -1;
00134         }
00135     }
00136 
00137     trans->state = STATE_PREPARED;
00138 
00139     return 0;
00140 }
00141 
00142 /** Commit a transaction. */
00143 int SYMEXPORT alpm_trans_commit(alpm_handle_t *handle, alpm_list_t **data)
00144 {
00145     alpm_trans_t *trans;
00146 
00147     /* Sanity checks */
00148     CHECK_HANDLE(handle, return -1);
00149 
00150     trans = handle->trans;
00151 
00152     ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00153     ASSERT(trans->state == STATE_PREPARED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_PREPARED, -1));
00154 
00155     ASSERT(!(trans->flags & ALPM_TRANS_FLAG_NOLOCK), RET_ERR(handle, ALPM_ERR_TRANS_NOT_LOCKED, -1));
00156 
00157     /* If there's nothing to do, return without complaining */
00158     if(trans->add == NULL && trans->remove == NULL) {
00159         return 0;
00160     }
00161 
00162     trans->state = STATE_COMMITING;
00163 
00164     if(trans->add == NULL) {
00165         if(_alpm_remove_packages(handle, 1) == -1) {
00166             /* pm_errno is set by _alpm_remove_commit() */
00167             return -1;
00168         }
00169     } else {
00170         if(_alpm_sync_commit(handle, data) == -1) {
00171             /* pm_errno is set by _alpm_sync_commit() */
00172             return -1;
00173         }
00174     }
00175 
00176     trans->state = STATE_COMMITED;
00177 
00178     return 0;
00179 }
00180 
00181 /** Interrupt a transaction. */
00182 int SYMEXPORT alpm_trans_interrupt(alpm_handle_t *handle)
00183 {
00184     alpm_trans_t *trans;
00185 
00186     /* Sanity checks */
00187     CHECK_HANDLE(handle, return -1);
00188 
00189     trans = handle->trans;
00190     ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00191     ASSERT(trans->state == STATE_COMMITING || trans->state == STATE_INTERRUPTED,
00192             RET_ERR(handle, ALPM_ERR_TRANS_TYPE, -1));
00193 
00194     trans->state = STATE_INTERRUPTED;
00195 
00196     return 0;
00197 }
00198 
00199 /** Release a transaction. */
00200 int SYMEXPORT alpm_trans_release(alpm_handle_t *handle)
00201 {
00202     alpm_trans_t *trans;
00203 
00204     /* Sanity checks */
00205     CHECK_HANDLE(handle, return -1);
00206 
00207     trans = handle->trans;
00208     ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00209     ASSERT(trans->state != STATE_IDLE, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00210 
00211     int nolock_flag = trans->flags & ALPM_TRANS_FLAG_NOLOCK;
00212 
00213     _alpm_trans_free(trans);
00214     handle->trans = NULL;
00215 
00216     /* unlock db */
00217     if(!nolock_flag) {
00218         if(_alpm_handle_unlock(handle)) {
00219             _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove lock file %s\n"),
00220                     handle->lockfile);
00221             alpm_logaction(handle, "warning: could not remove lock file %s\n",
00222                     handle->lockfile);
00223         }
00224     }
00225 
00226     return 0;
00227 }
00228 
00229 /** @} */
00230 
00231 void _alpm_trans_free(alpm_trans_t *trans)
00232 {
00233     if(trans == NULL) {
00234         return;
00235     }
00236 
00237     alpm_list_free_inner(trans->unresolvable,
00238             (alpm_list_fn_free)_alpm_pkg_free_trans);
00239     alpm_list_free(trans->unresolvable);
00240     alpm_list_free_inner(trans->add, (alpm_list_fn_free)_alpm_pkg_free_trans);
00241     alpm_list_free(trans->add);
00242     alpm_list_free_inner(trans->remove, (alpm_list_fn_free)_alpm_pkg_free);
00243     alpm_list_free(trans->remove);
00244 
00245     FREELIST(trans->skip_remove);
00246 
00247     FREE(trans);
00248 }
00249 
00250 /* A cheap grep for text files, returns 1 if a substring
00251  * was found in the text file fn, 0 if it wasn't
00252  */
00253 static int grep(const char *fn, const char *needle)
00254 {
00255     FILE *fp;
00256 
00257     if((fp = fopen(fn, "r")) == NULL) {
00258         return 0;
00259     }
00260     while(!feof(fp)) {
00261         char line[1024];
00262         if(fgets(line, sizeof(line), fp) == NULL) {
00263             continue;
00264         }
00265         /* TODO: this will not work if the search string
00266          * ends up being split across line reads */
00267         if(strstr(line, needle)) {
00268             fclose(fp);
00269             return 1;
00270         }
00271     }
00272     fclose(fp);
00273     return 0;
00274 }
00275 
00276 int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath,
00277         const char *script, const char *ver, const char *oldver, int is_archive)
00278 {
00279     char cmdline[PATH_MAX];
00280     char *argv[] = { "sh", "-c", cmdline, NULL };
00281     char *tmpdir, *scriptfn = NULL, *scriptpath;
00282     int retval = 0;
00283     size_t len;
00284 
00285     if(_alpm_access(handle, NULL, filepath, R_OK) != 0) {
00286         _alpm_log(handle, ALPM_LOG_DEBUG, "scriptlet '%s' not found\n", filepath);
00287         return 0;
00288     }
00289 
00290     if(!is_archive && !grep(filepath, script)) {
00291         /* script not found in scriptlet file; we can only short-circuit this early
00292          * if it is an actual scriptlet file and not an archive.  */
00293         return 0;
00294     }
00295 
00296     /* create a directory in $root/tmp/ for copying/extracting the scriptlet */
00297     len = strlen(handle->root) + strlen("tmp/alpm_XXXXXX") + 1;
00298     MALLOC(tmpdir, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00299     snprintf(tmpdir, len, "%stmp/", handle->root);
00300     if(access(tmpdir, F_OK) != 0) {
00301         _alpm_makepath_mode(tmpdir, 01777);
00302     }
00303     snprintf(tmpdir, len, "%stmp/alpm_XXXXXX", handle->root);
00304     if(mkdtemp(tmpdir) == NULL) {
00305         _alpm_log(handle, ALPM_LOG_ERROR, _("could not create temp directory\n"));
00306         free(tmpdir);
00307         return 1;
00308     }
00309 
00310     /* either extract or copy the scriptlet */
00311     len += strlen("/.INSTALL");
00312     MALLOC(scriptfn, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00313     snprintf(scriptfn, len, "%s/.INSTALL", tmpdir);
00314     if(is_archive) {
00315         if(_alpm_unpack_single(handle, filepath, tmpdir, ".INSTALL")) {
00316             retval = 1;
00317         }
00318     } else {
00319         if(_alpm_copyfile(filepath, scriptfn)) {
00320             _alpm_log(handle, ALPM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), scriptfn, strerror(errno));
00321             retval = 1;
00322         }
00323     }
00324     if(retval == 1) {
00325         goto cleanup;
00326     }
00327 
00328     if(is_archive && !grep(scriptfn, script)) {
00329         /* script not found in extracted scriptlet file */
00330         goto cleanup;
00331     }
00332 
00333     /* chop off the root so we can find the tmpdir in the chroot */
00334     scriptpath = scriptfn + strlen(handle->root) - 1;
00335 
00336     if(oldver) {
00337         snprintf(cmdline, PATH_MAX, ". %s; %s %s %s",
00338                 scriptpath, script, ver, oldver);
00339     } else {
00340         snprintf(cmdline, PATH_MAX, ". %s; %s %s",
00341                 scriptpath, script, ver);
00342     }
00343 
00344     _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline);
00345 
00346     retval = _alpm_run_chroot(handle, "/bin/sh", argv);
00347 
00348 cleanup:
00349     if(scriptfn && unlink(scriptfn)) {
00350         _alpm_log(handle, ALPM_LOG_WARNING,
00351                 _("could not remove %s\n"), scriptfn);
00352     }
00353     if(rmdir(tmpdir)) {
00354         _alpm_log(handle, ALPM_LOG_WARNING,
00355                 _("could not remove tmpdir %s\n"), tmpdir);
00356     }
00357 
00358     free(scriptfn);
00359     free(tmpdir);
00360     return retval;
00361 }
00362 
00363 alpm_transflag_t SYMEXPORT alpm_trans_get_flags(alpm_handle_t *handle)
00364 {
00365     /* Sanity checks */
00366     CHECK_HANDLE(handle, return -1);
00367     ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00368 
00369     return handle->trans->flags;
00370 }
00371 
00372 alpm_list_t SYMEXPORT *alpm_trans_get_add(alpm_handle_t *handle)
00373 {
00374     /* Sanity checks */
00375     CHECK_HANDLE(handle, return NULL);
00376     ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, NULL));
00377 
00378     return handle->trans->add;
00379 }
00380 
00381 alpm_list_t SYMEXPORT *alpm_trans_get_remove(alpm_handle_t *handle)
00382 {
00383     /* Sanity checks */
00384     CHECK_HANDLE(handle, return NULL);
00385     ASSERT(handle->trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, NULL));
00386 
00387     return handle->trans->remove;
00388 }
00389 /* vim: set ts=2 sw=2 noet: */