libalpm
Arch Linux Package Manager Library
|
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: */