A few final changes for the 3.0.6 release
[pacman.git] / lib / libalpm / alpm.c
blobafb6226b7a765bfe7c729db072b62932b6421b99
1 /*
2 * alpm.c
3 *
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>
8 *
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,
22 * USA.
25 #include "config.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <time.h>
37 #include <syslog.h>
38 #include <limits.h> /* PATH_MAX */
39 #include <stdarg.h>
40 #include <libintl.h>
42 /* libalpm */
43 #include "alpm.h"
44 #include "alpm_list.h"
45 #include "log.h"
46 #include "error.h"
47 #include "versioncmp.h"
48 #include "md5.h"
49 #include "sha1.h"
50 #include "package.h"
51 #include "group.h"
52 #include "util.h"
53 #include "db.h"
54 #include "cache.h"
55 #include "deps.h"
56 #include "conflict.h"
57 #include "backup.h"
58 #include "add.h"
59 #include "remove.h"
60 #include "sync.h"
61 #include "handle.h"
62 #include "provide.h"
63 #include "server.h"
65 #define min(X, Y) ((X) < (Y) ? (X) : (Y))
67 /* Globals */
68 pmhandle_t *handle = NULL;
69 enum _pmerrno_t pm_errno SYMEXPORT;
71 /** \addtogroup alpm_interface Interface Functions
72 * @brief Functions to initialize and release libalpm
73 * @{
76 /** Initializes the library. This must be called before any other
77 * functions are called.
78 * @return 0 on success, -1 on error (pm_errno is set accordingly)
80 int SYMEXPORT alpm_initialize()
82 ASSERT(handle == NULL, RET_ERR(PM_ERR_HANDLE_NOT_NULL, -1));
84 handle = _alpm_handle_new();
85 if(handle == NULL) {
86 RET_ERR(PM_ERR_MEMORY, -1);
89 return(0);
92 /** Release the library. This should be the last alpm call you make.
93 * @return 0 on success, -1 on error (pm_errno is set accordingly)
95 int SYMEXPORT alpm_release()
97 int dbs_left = 0;
99 ALPM_LOG_FUNC;
101 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
103 /* free the transaction if there is any */
104 if(handle->trans) {
105 alpm_trans_release();
108 /* close local database */
109 if(handle->db_local) {
110 alpm_db_unregister(handle->db_local);
111 handle->db_local = NULL;
113 /* and also sync ones */
114 while((dbs_left = alpm_list_count(handle->dbs_sync)) > 0) {
115 pmdb_t *db = (pmdb_t *)handle->dbs_sync->data;
116 _alpm_log(PM_LOG_DEBUG, _("removing DB %s, %d remaining..."), db->treename, dbs_left);
117 alpm_db_unregister(db);
118 db = NULL;
121 FREEHANDLE(handle);
123 return(0);
126 /** @} */
128 /** \addtogroup alpm_databases Database Functions
129 * @brief Functions to query and manipulate the database of libalpm
130 * @{
133 /** Register a package database
134 * @param treename the name of the repository
135 * @return a pmdb_t* on success (the value), NULL on error
137 pmdb_t SYMEXPORT *alpm_db_register(char *treename)
139 ALPM_LOG_FUNC;
141 /* Sanity checks */
142 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, NULL));
143 ASSERT(treename != NULL && strlen(treename) != 0, RET_ERR(PM_ERR_WRONG_ARGS, NULL));
144 /* Do not register a database if a transaction is on-going */
145 ASSERT(handle->trans == NULL, RET_ERR(PM_ERR_TRANS_NOT_NULL, NULL));
147 return(_alpm_db_register(treename, NULL));
150 /** Unregister a package database
151 * @param db pointer to the package database to unregister
152 * @return 0 on success, -1 on error (pm_errno is set accordingly)
154 int alpm_db_unregister(pmdb_t *db)
156 int found = 0;
158 ALPM_LOG_FUNC;
160 /* Sanity checks */
161 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
162 ASSERT(db != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
163 /* Do not unregister a database if a transaction is on-going */
164 ASSERT(handle->trans == NULL, RET_ERR(PM_ERR_TRANS_NOT_NULL, -1));
166 if(db == handle->db_local) {
167 handle->db_local = NULL;
168 found = 1;
169 } else {
170 void *data;
171 handle->dbs_sync = alpm_list_remove(handle->dbs_sync, db, _alpm_db_cmp, &data);
172 if(data) {
173 found = 1;
177 if(!found) {
178 RET_ERR(PM_ERR_DB_NOT_FOUND, -1);
181 _alpm_log(PM_LOG_DEBUG, _("unregistering database '%s'"), db->treename);
183 /* Cleanup */
184 _alpm_db_free_pkgcache(db);
186 _alpm_log(PM_LOG_DEBUG, _("closing database '%s'"), db->treename);
187 _alpm_db_close(db);
189 _alpm_db_free(db);
191 return(0);
194 /** Set the serverlist of a database.
195 * @param db database pointer
196 * @param url url of the server
197 * @return 0 on success, -1 on error (pm_errno is set accordingly)
199 int alpm_db_setserver(pmdb_t *db, const char *url)
201 int found = 0;
203 ALPM_LOG_FUNC;
205 /* Sanity checks */
206 ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
208 if(strcmp(db->treename, "local") == 0) {
209 if(handle->db_local != NULL) {
210 found = 1;
212 } else {
213 alpm_list_t *i;
214 for(i = handle->dbs_sync; i && !found; i = i->next) {
215 pmdb_t *sdb = i->data;
216 if(strcmp(db->treename, sdb->treename) == 0) {
217 found = 1;
221 if(!found) {
222 RET_ERR(PM_ERR_DB_NOT_FOUND, -1);
225 if(url && strlen(url)) {
226 pmserver_t *server;
227 if((server = _alpm_server_new(url)) == NULL) {
228 /* pm_errno is set by _alpm_server_new */
229 return(-1);
231 db->servers = alpm_list_add(db->servers, server);
232 _alpm_log(PM_LOG_DEBUG, _("adding new server to database '%s': protocol '%s', server '%s', path '%s'"),
233 db->treename, server->s_url->scheme, server->s_url->host, server->s_url->doc);
234 } else {
235 FREELIST(db->servers);
236 _alpm_log(PM_LOG_DEBUG, _("serverlist flushed for '%s'"), db->treename);
239 return(0);
242 /** Update a package database
243 * @param force if true, then forces the update, otherwise update only in case
244 * the database isn't up to date
245 * @param db pointer to the package database to update
246 * @return 0 on success, > 0 on error (pm_errno is set accordingly), < 0 if up
247 * to date
249 int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
251 alpm_list_t *lp;
252 char path[PATH_MAX];
253 alpm_list_t *files = NULL;
254 char newmtime[16] = "";
255 char lastupdate[16] = "";
256 int ret;
258 ALPM_LOG_FUNC;
260 /* Sanity checks */
261 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
262 ASSERT(db != NULL && db != handle->db_local, RET_ERR(PM_ERR_WRONG_ARGS, -1));
263 /* Verify we are in a transaction. This is done _mainly_ because we need a DB
264 * lock - if we update without a db lock, we may kludge some other pacman
265 * process that _has_ a lock.
267 ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
268 ASSERT(handle->trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
269 ASSERT(handle->trans->type == PM_TRANS_TYPE_SYNC, RET_ERR(PM_ERR_TRANS_TYPE, -1));
271 if(!alpm_list_find(handle->dbs_sync, db)) {
272 RET_ERR(PM_ERR_DB_NOT_FOUND, -1);
275 if(!force) {
276 /* get the lastupdate time */
277 _alpm_db_getlastupdate(db, lastupdate);
278 if(strlen(lastupdate) == 0) {
279 _alpm_log(PM_LOG_DEBUG, _("failed to get lastupdate time for %s (no big deal)"), db->treename);
283 /* build a one-element list */
284 snprintf(path, PATH_MAX, "%s" PM_EXT_DB, db->treename);
285 files = alpm_list_add(files, strdup(path));
287 snprintf(path, PATH_MAX, "%s%s", handle->root, handle->dbpath);
289 ret = _alpm_downloadfiles_forreal(db->servers, path, files, lastupdate, newmtime);
290 FREELIST(files);
291 if(ret == 1) {
292 /* mtimes match, do nothing */
293 pm_errno = 0;
294 return(1);
295 } else if(ret == -1) {
296 /* we use downloadLastErrString and downloadLastErrCode here, error returns from
297 * libdownload */
298 _alpm_log(PM_LOG_DEBUG, _("failed to sync db: %s [%d]"), downloadLastErrString, downloadLastErrCode);
299 RET_ERR(PM_ERR_DB_SYNC, -1);
300 } else {
301 if(strlen(newmtime)) {
302 _alpm_log(PM_LOG_DEBUG, _("sync: new mtime for %s: %s"), db->treename, newmtime);
303 _alpm_db_setlastupdate(db, newmtime);
305 snprintf(path, PATH_MAX, "%s%s%s" PM_EXT_DB, handle->root, handle->dbpath, db->treename);
307 /* remove the old dir */
308 _alpm_log(PM_LOG_DEBUG, _("flushing database %s%s"), db->path);
309 for(lp = _alpm_db_get_pkgcache(db); lp; lp = lp->next) {
310 pmpkg_t *pkg = lp->data;
311 if(pkg && _alpm_db_remove(db, pkg) == -1) {
312 _alpm_log(PM_LOG_ERROR, _("could not remove database entry %s%s"), db->treename,
313 alpm_pkg_get_name(pkg));
314 RET_ERR(PM_ERR_DB_REMOVE, -1);
318 /* Cache needs to be rebuild */
319 _alpm_db_free_pkgcache(db);
321 /* uncompress the sync database */
322 if(_alpm_db_install(db, path) == -1) {
323 return -1;
327 return(0);
330 /** Get a package entry from a package database
331 * @param db pointer to the package database to get the package from
332 * @param name of the package
333 * @return the package entry on success, NULL on error
335 pmpkg_t SYMEXPORT *alpm_db_get_pkg(pmdb_t *db, const char *name)
337 ALPM_LOG_FUNC;
339 /* Sanity checks */
340 ASSERT(handle != NULL, return(NULL));
341 ASSERT(db != NULL, return(NULL));
342 ASSERT(name != NULL && strlen(name) != 0, return(NULL));
344 return(_alpm_db_get_pkgfromcache(db, name));
347 /** Get the package cache of a package database
348 * @param db pointer to the package database to get the package from
349 * @return the list of packages on success, NULL on error
351 alpm_list_t SYMEXPORT *alpm_db_getpkgcache(pmdb_t *db)
353 ALPM_LOG_FUNC;
355 /* Sanity checks */
356 ASSERT(handle != NULL, return(NULL));
357 ASSERT(db != NULL, return(NULL));
359 return(_alpm_db_get_pkgcache(db));
362 /** Get the list of packages that a package provides
363 * @param db pointer to the package database to get the package from
364 * @param name name of the package
365 * @return the list of packages on success, NULL on error
367 alpm_list_t SYMEXPORT *alpm_db_whatprovides(pmdb_t *db, const char *name)
369 ALPM_LOG_FUNC;
371 /* Sanity checks */
372 ASSERT(handle != NULL, return(NULL));
373 ASSERT(db != NULL, return(NULL));
374 ASSERT(name != NULL && strlen(name) != 0, return(NULL));
376 return(_alpm_db_whatprovides(db, name));
379 /** Get a group entry from a package database
380 * @param db pointer to the package database to get the group from
381 * @param name of the group
382 * @return the groups entry on success, NULL on error
384 pmgrp_t SYMEXPORT *alpm_db_readgrp(pmdb_t *db, const char *name)
386 ALPM_LOG_FUNC;
388 /* Sanity checks */
389 ASSERT(handle != NULL, return(NULL));
390 ASSERT(db != NULL, return(NULL));
391 ASSERT(name != NULL && strlen(name) != 0, return(NULL));
393 return(_alpm_db_get_grpfromcache(db, name));
396 /** Get the group cache of a package database
397 * @param db pointer to the package database to get the group from
398 * @return the list of groups on success, NULL on error
400 alpm_list_t SYMEXPORT *alpm_db_getgrpcache(pmdb_t *db)
402 ALPM_LOG_FUNC;
404 /* Sanity checks */
405 ASSERT(handle != NULL, return(NULL));
406 ASSERT(db != NULL, return(NULL));
408 return(_alpm_db_get_grpcache(db));
411 /** @} */
413 /** \addtogroup alpm_packages Package Functions
414 * @brief Functions to manipulate libalpm packages
415 * @{
418 /** Create a package from a file.
419 * @param filename location of the package tarball
420 * @param pkg address of the package pointer
421 * @return 0 on success, -1 on error (pm_errno is set accordingly)
423 int SYMEXPORT alpm_pkg_load(char *filename, pmpkg_t **pkg)
425 _alpm_log(PM_LOG_FUNCTION, "enter alpm_pkg_load");
427 /* Sanity checks */
428 ASSERT(filename != NULL && strlen(filename) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1));
429 ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
431 *pkg = _alpm_pkg_load(filename);
432 if(*pkg == NULL) {
433 /* pm_errno is set by pkg_load */
434 return(-1);
437 return(0);
440 /** Free a package.
441 * @param pkg package pointer to free
442 * @return 0 on success, -1 on error (pm_errno is set accordingly)
444 int SYMEXPORT alpm_pkg_free(pmpkg_t *pkg)
446 _alpm_log(PM_LOG_FUNCTION, "enter alpm_pkg_free");
448 ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
450 /* Only free packages loaded in user space */
451 if(pkg->origin != PKG_FROM_CACHE) {
452 _alpm_pkg_free(pkg);
455 return(0);
458 /** Check the integrity (with sha1) of a package from the sync cache.
459 * @param pkg package pointer
460 * @return 0 on success, -1 on error (pm_errno is set accordingly)
462 int alpm_pkg_checksha1sum(pmpkg_t *pkg)
464 char path[PATH_MAX];
465 char *sha1sum = NULL;
466 int retval = 0;
468 ALPM_LOG_FUNC;
470 ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
471 /* We only inspect packages from sync repositories */
472 ASSERT(pkg->origin == PKG_FROM_CACHE, RET_ERR(PM_ERR_PKG_INVALID, -1));
473 ASSERT(pkg->data != handle->db_local, RET_ERR(PM_ERR_PKG_INVALID, -1));
475 snprintf(path, PATH_MAX, "%s%s/%s-%s" PM_EXT_PKG,
476 handle->root, handle->cachedir,
477 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
479 sha1sum = _alpm_SHAFile(path);
480 if(sha1sum == NULL) {
481 _alpm_log(PM_LOG_ERROR, _("could not get sha1sum for package %s-%s"),
482 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
483 pm_errno = PM_ERR_NOT_A_FILE;
484 retval = -1;
485 } else {
486 if(strcmp(sha1sum, alpm_pkg_get_sha1sum(pkg)) == 0) {
487 _alpm_log(PM_LOG_DEBUG, _("sha1sums for package %s-%s match"),
488 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
489 } else {
490 _alpm_log(PM_LOG_ERROR, _("sha1sums do not match for package %s-%s"),
491 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
492 pm_errno = PM_ERR_PKG_INVALID;
493 retval = -1;
497 FREE(sha1sum);
499 return(retval);
502 /** Check the integrity (with md5) of a package from the sync cache.
503 * @param pkg package pointer
504 * @return 0 on success, -1 on error (pm_errno is set accordingly)
506 int alpm_pkg_checkmd5sum(pmpkg_t *pkg)
508 char path[PATH_MAX];
509 char *md5sum = NULL;
510 int retval = 0;
512 ALPM_LOG_FUNC;
514 ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
515 /* We only inspect packages from sync repositories */
516 ASSERT(pkg->origin == PKG_FROM_CACHE, RET_ERR(PM_ERR_PKG_INVALID, -1));
517 ASSERT(pkg->data != handle->db_local, RET_ERR(PM_ERR_PKG_INVALID, -1));
519 snprintf(path, PATH_MAX, "%s%s/%s-%s" PM_EXT_PKG,
520 handle->root, handle->cachedir,
521 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
523 md5sum = _alpm_MDFile(path);
524 if(md5sum == NULL) {
525 _alpm_log(PM_LOG_ERROR, _("could not get md5sum for package %s-%s"),
526 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
527 pm_errno = PM_ERR_NOT_A_FILE;
528 retval = -1;
529 } else {
530 if(strcmp(md5sum, alpm_pkg_get_md5sum(pkg)) == 0) {
531 _alpm_log(PM_LOG_DEBUG, _("md5sums for package %s-%s match"),
532 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
533 } else {
534 _alpm_log(PM_LOG_ERROR, _("md5sums do not match for package %s-%s"),
535 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
536 pm_errno = PM_ERR_PKG_INVALID;
537 retval = -1;
541 FREE(md5sum);
543 return(retval);
546 /** Compare versions.
547 * @param ver1 first version
548 * @param ver2 secont version
549 * @return postive, 0 or negative if ver1 is less, equal or more
550 * than ver2, respectively.
552 int SYMEXPORT alpm_pkg_vercmp(const char *ver1, const char *ver2)
554 ALPM_LOG_FUNC;
556 return(_alpm_versioncmp(ver1, ver2));
559 /* internal */
560 static char *_supported_archs[] = {
561 "i586",
562 "i686",
563 "ppc",
564 "x86_64",
567 char SYMEXPORT *alpm_pkg_name_hasarch(char *pkgname)
569 /* TODO remove this when we transfer everything over to -ARCH
571 * this parsing sucks... it's done to support
572 * two package formats for the time being:
573 * package-name-foo-1.0.0-1-i686
574 * and
575 * package-name-bar-1.2.3-1
577 size_t i = 0;
578 char *arch, *cmp, *p;
580 ALPM_LOG_FUNC;
582 if((p = strrchr(pkgname, '-'))) {
583 for(i=0; i < sizeof(_supported_archs)/sizeof(char*); ++i) {
584 cmp = p+1;
585 arch = _supported_archs[i];
587 /* whee, case insensitive compare */
588 while(*arch && *cmp && tolower(*arch++) == tolower(*cmp++)) ;
589 if(*arch || *cmp) {
590 continue;
593 return(p);
596 return(NULL);
599 /** @} */
601 /** \addtogroup alpm_sync Sync Functions
602 * @brief Functions to get informations about libalpm syncs
603 * @{
606 /** Searches a database
607 * @param db pointer to the package database to search in
608 * @param needles the list of strings to search for
609 * @return the list of packages on success, NULL on error
611 alpm_list_t SYMEXPORT *alpm_db_search(pmdb_t *db, alpm_list_t* needles)
613 ALPM_LOG_FUNC;
615 /* Sanity checks */
616 ASSERT(handle != NULL, return(NULL));
617 ASSERT(db != NULL, return(NULL));
619 return(_alpm_db_search(db, needles));
622 /** @} */
624 /** \addtogroup alpm_trans Transaction Functions
625 * @brief Functions to manipulate libalpm transactions
626 * @{
629 /** Initialize the transaction.
630 * @param type type of the transaction
631 * @param flags flags of the transaction (like nodeps, etc)
632 * @param event event callback function pointer
633 * @param conv question callback function pointer
634 * @param progress progress callback function pointer
635 * @return 0 on success, -1 on error (pm_errno is set accordingly)
637 int SYMEXPORT alpm_trans_init(pmtranstype_t type, pmtransflag_t flags,
638 alpm_trans_cb_event event, alpm_trans_cb_conv conv,
639 alpm_trans_cb_progress progress)
641 char path[PATH_MAX];
643 ALPM_LOG_FUNC;
645 /* Sanity checks */
646 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
648 ASSERT(handle->trans == NULL, RET_ERR(PM_ERR_TRANS_NOT_NULL, -1));
650 /* lock db */
651 snprintf(path, PATH_MAX, "%s%s", handle->root, PM_LOCK);
652 handle->lckfd = _alpm_lckmk(path);
653 if(handle->lckfd == -1) {
654 RET_ERR(PM_ERR_HANDLE_LOCK, -1);
657 handle->trans = _alpm_trans_new();
658 if(handle->trans == NULL) {
659 RET_ERR(PM_ERR_MEMORY, -1);
662 return(_alpm_trans_init(handle->trans, type, flags, event, conv, progress));
665 /** Search for packages to upgrade and add them to the transaction.
666 * @return 0 on success, -1 on error (pm_errno is set accordingly)
668 int SYMEXPORT alpm_trans_sysupgrade()
670 pmtrans_t *trans;
672 ALPM_LOG_FUNC;
674 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
676 trans = handle->trans;
677 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
678 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
679 ASSERT(trans->type == PM_TRANS_TYPE_SYNC, RET_ERR(PM_ERR_TRANS_TYPE, -1));
681 return(_alpm_trans_sysupgrade(trans));
684 /** Add a target to the transaction.
685 * @param target the name of the target to add
686 * @return 0 on success, -1 on error (pm_errno is set accordingly)
688 int SYMEXPORT alpm_trans_addtarget(char *target)
690 pmtrans_t *trans;
692 ALPM_LOG_FUNC;
694 /* Sanity checks */
695 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
696 ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1));
698 trans = handle->trans;
699 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
700 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
702 return(_alpm_trans_addtarget(trans, target));
705 /** Prepare a transaction.
706 * @param data the address of a PM_LIST where detailed description
707 * of an error can be dumped (ie. list of conflicting files)
708 * @return 0 on success, -1 on error (pm_errno is set accordingly)
710 int SYMEXPORT alpm_trans_prepare(alpm_list_t **data)
712 ALPM_LOG_FUNC;
714 /* Sanity checks */
715 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
716 ASSERT(data != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
718 ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
719 ASSERT(handle->trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
721 return(_alpm_trans_prepare(handle->trans, data));
724 /** Commit a transaction.
725 * @param data the address of a PM_LIST where detailed description
726 * of an error can be dumped (ie. list of conflicting files)
727 * @return 0 on success, -1 on error (pm_errno is set accordingly)
729 int SYMEXPORT alpm_trans_commit(alpm_list_t **data)
731 ALPM_LOG_FUNC;
733 /* Sanity checks */
734 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
736 ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
737 ASSERT(handle->trans->state == STATE_PREPARED, RET_ERR(PM_ERR_TRANS_NOT_PREPARED, -1));
739 /* Check for database R/W permission */
740 if(!(handle->trans->flags & PM_TRANS_FLAG_PRINTURIS)) {
741 /* The print-uris operation is a bit odd. So we explicitly check for it */
742 ASSERT(handle->access == PM_ACCESS_RW, RET_ERR(PM_ERR_BADPERMS, -1));
745 return(_alpm_trans_commit(handle->trans, data));
748 /** Release a transaction.
749 * @return 0 on success, -1 on error (pm_errno is set accordingly)
751 int SYMEXPORT alpm_trans_release()
753 pmtrans_t *trans;
754 char path[PATH_MAX];
756 ALPM_LOG_FUNC;
758 /* Sanity checks */
759 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
761 trans = handle->trans;
762 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
763 ASSERT(trans->state != STATE_IDLE, RET_ERR(PM_ERR_TRANS_NULL, -1));
765 /* during a commit do not interrupt inmediatelly, just after a target */
766 if(trans->state == STATE_COMMITING || trans->state == STATE_INTERRUPTED) {
767 if(trans->state == STATE_COMMITING) {
768 trans->state = STATE_INTERRUPTED;
770 pm_errno = PM_ERR_TRANS_COMMITING;
771 return(-1);
774 FREETRANS(handle->trans);
776 /* unlock db */
777 if(handle->lckfd != -1) {
778 close(handle->lckfd);
779 handle->lckfd = -1;
781 snprintf(path, PATH_MAX, "%s%s", handle->root, PM_LOCK);
782 if(_alpm_lckrm(path)) {
783 _alpm_log(PM_LOG_WARNING, _("could not remove lock file %s"), path);
784 alpm_logaction(_("warning: could not remove lock file %s"), path);
787 return(0);
790 /** @} */
792 /** \addtogroup alpm_log Logging Functions
793 * @brief Functions to log using libalpm
794 * @{
797 /** A printf-like function for logging.
798 * @param fmt output format
799 * @return 0 on success, -1 on error (pm_errno is set accordingly)
801 int SYMEXPORT alpm_logaction(char *fmt, ...)
803 char str[LOG_STR_LEN];
804 va_list args;
806 ALPM_LOG_FUNC;
808 /* Sanity checks */
809 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
811 va_start(args, fmt);
812 vsnprintf(str, LOG_STR_LEN, fmt, args);
813 va_end(args);
815 /* TODO We should add a prefix to log strings depending on who called us.
816 * If logaction was called by the frontend:
817 * USER: <the frontend log>
818 * and if called internally:
819 * ALPM: <the library log>
820 * Moreover, the frontend should be able to choose its prefix
821 * (USER by default?):
822 * pacman: "PACMAN"
823 * kpacman: "KPACMAN"
824 * This would allow us to share the log file between several frontends
825 * and know who does what */
826 return(_alpm_logaction(handle->usesyslog, handle->logfd, str));
828 /** @} */
830 /** \addtogroup alpm_misc Miscellaneous Functions
831 * @brief Various libalpm functions
832 * @{
835 /** Get the md5 sum of file.
836 * @param name name of the file
837 * @return the checksum on success, NULL on error
839 char SYMEXPORT *alpm_get_md5sum(char *name)
841 ALPM_LOG_FUNC;
843 ASSERT(name != NULL, return(NULL));
845 return(_alpm_MDFile(name));
848 /** Get the sha1 sum of file.
849 * @param name name of the file
850 * @return the checksum on success, NULL on error
852 char SYMEXPORT *alpm_get_sha1sum(char *name)
854 ALPM_LOG_FUNC;
856 ASSERT(name != NULL, return(NULL));
858 return(_alpm_SHAFile(name));
861 /** Fetch a remote pkg.
862 * @param url
863 * @return the downloaded filename on success, NULL on error
865 char SYMEXPORT *alpm_fetch_pkgurl(char *url)
867 ALPM_LOG_FUNC;
869 ASSERT(strstr(url, "://"), return(NULL));
871 return(_alpm_fetch_pkgurl(url));
874 /** Parses a configuration file.
875 * @param file path to the config file.
876 * @param callback a function to be called upon new database creation
877 * @param this_section the config current section being parsed
878 * @return 0 on success, -1 on error (pm_errno is set accordingly)
880 int SYMEXPORT alpm_parse_config(char *file, alpm_cb_db_register callback, const char *this_section)
882 FILE *fp = NULL;
883 char line[PATH_MAX+1];
884 char *ptr = NULL;
885 char *key = NULL;
886 int linenum = 0;
887 char origkey[256];
888 char section[256] = "";
889 pmdb_t *db = NULL;
891 ALPM_LOG_FUNC;
893 fp = fopen(file, "r");
894 if(fp == NULL) {
895 return(0);
898 if(this_section != NULL && strlen(this_section) > 0) {
899 strncpy(section, this_section, min(255, strlen(this_section)));
900 if(!strcmp(section, "local")) {
901 RET_ERR(PM_ERR_CONF_LOCAL, -1);
903 if(strcmp(section, "options")) {
904 db = _alpm_db_register(section, callback);
908 while(fgets(line, PATH_MAX, fp)) {
909 linenum++;
910 _alpm_strtrim(line);
911 if(strlen(line) == 0 || line[0] == '#') {
912 continue;
914 if(line[0] == '[' && line[strlen(line)-1] == ']') {
915 /* new config section */
916 ptr = line;
917 ptr++;
918 strncpy(section, ptr, min(255, strlen(ptr)-1));
919 section[min(255, strlen(ptr)-1)] = '\0';
920 _alpm_log(PM_LOG_DEBUG, _("config: new section '%s'"), section);
921 if(!strlen(section)) {
922 RET_ERR(PM_ERR_CONF_BAD_SECTION, -1);
924 if(!strcmp(section, "local")) {
925 RET_ERR(PM_ERR_CONF_LOCAL, -1);
927 if(strcmp(section, "options")) {
928 db = _alpm_db_register(section, callback);
929 if(db == NULL) {
930 /* pm_errno is set by alpm_db_register */
931 return(-1);
934 } else {
935 /* directive */
936 ptr = line;
937 key = strsep(&ptr, "=");
938 if(key == NULL) {
939 RET_ERR(PM_ERR_CONF_BAD_SYNTAX, -1);
941 _alpm_strtrim(key);
942 strncpy(origkey, key, min(255, strlen(key)));
943 origkey[min(255, strlen(key))] = '\0';
944 key = _alpm_strtoupper(key);
945 if(!strlen(section) && strcmp(key, "INCLUDE")) {
946 RET_ERR(PM_ERR_CONF_DIRECTIVE_OUTSIDE_SECTION, -1);
948 if(ptr == NULL) {
949 if(strcmp(origkey, "NoPassiveFTP") == 0 || strcmp(key, "NOPASSIVEFTP") == 0) {
950 alpm_option_set_nopassiveftp(1);
951 _alpm_log(PM_LOG_DEBUG, _("config: nopassiveftp"));
952 } else if(strcmp(origkey, "UseSyslog") == 0 || strcmp(key, "USESYSLOG") == 0) {
953 alpm_option_set_usesyslog(1);
954 _alpm_log(PM_LOG_DEBUG, _("config: usesyslog"));
955 } else if(strcmp(origkey, "ILoveCandy") == 0 || strcmp(key, "ILOVECANDY") == 0) {
956 alpm_option_set_chomp(1);
957 _alpm_log(PM_LOG_DEBUG, _("config: chomp"));
958 } else if(strcmp(origkey, "UseColor") == 0 || strcmp(key, "USECOLOR") == 0) {
959 alpm_option_set_usecolor(1);
960 _alpm_log(PM_LOG_DEBUG, _("config: usecolor"));
961 } else {
962 RET_ERR(PM_ERR_CONF_BAD_SYNTAX, -1);
964 } else {
965 _alpm_strtrim(ptr);
966 if(strcmp(origkey, "Include") == 0 || strcmp(key, "INCLUDE") == 0) {
967 char conf[PATH_MAX];
968 strncpy(conf, ptr, PATH_MAX);
969 _alpm_log(PM_LOG_DEBUG, _("config: including %s"), conf);
970 alpm_parse_config(conf, callback, section);
971 } else if(strcmp(section, "options") == 0) {
972 if(strcmp(origkey, "NoUpgrade") == 0 || strcmp(key, "NOUPGRADE") == 0) {
973 char *p = ptr;
974 char *q;
976 while((q = strchr(p, ' '))) {
977 *q = '\0';
978 alpm_option_add_noupgrade(p);
979 _alpm_log(PM_LOG_DEBUG, _("config: noupgrade: %s"), p);
980 p = q;
981 p++;
983 alpm_option_add_noupgrade(p);
984 _alpm_log(PM_LOG_DEBUG, _("config: noupgrade: %s"), p);
985 } else if(strcmp(origkey, "NoExtract") == 0 || strcmp(key, "NOEXTRACT") == 0) {
986 char *p = ptr;
987 char *q;
989 while((q = strchr(p, ' '))) {
990 *q = '\0';
991 alpm_option_add_noextract(p);
992 _alpm_log(PM_LOG_DEBUG, _("config: noextract: %s"), p);
993 p = q;
994 p++;
996 alpm_option_add_noextract(p);
997 _alpm_log(PM_LOG_DEBUG, _("config: noextract: %s"), p);
998 } else if(strcmp(origkey, "IgnorePkg") == 0 || strcmp(key, "IGNOREPKG") == 0) {
999 char *p = ptr;
1000 char *q;
1002 while((q = strchr(p, ' '))) {
1003 *q = '\0';
1004 alpm_option_add_ignorepkg(p);
1005 _alpm_log(PM_LOG_DEBUG, _("config: ignorepkg: %s"), p);
1006 p = q;
1007 p++;
1009 alpm_option_add_ignorepkg(p);
1010 _alpm_log(PM_LOG_DEBUG, _("config: ignorepkg: %s"), p);
1011 } else if(strcmp(origkey, "HoldPkg") == 0 || strcmp(key, "HOLDPKG") == 0) {
1012 char *p = ptr;
1013 char *q;
1015 while((q = strchr(p, ' '))) {
1016 *q = '\0';
1017 alpm_option_add_holdpkg(p);
1018 _alpm_log(PM_LOG_DEBUG, _("config: holdpkg: %s"), p);
1019 p = q;
1020 p++;
1022 alpm_option_add_holdpkg(p);
1023 _alpm_log(PM_LOG_DEBUG, _("config: holdpkg: %s"), p);
1024 } else if(strcmp(origkey, "DBPath") == 0 || strcmp(key, "DBPATH") == 0) {
1025 /* shave off the leading slash, if there is one */
1026 if(*ptr == '/') {
1027 ptr++;
1029 alpm_option_set_dbpath(ptr);
1030 _alpm_log(PM_LOG_DEBUG, _("config: dbpath: %s"), ptr);
1031 } else if(strcmp(origkey, "CacheDir") == 0 || strcmp(key, "CACHEDIR") == 0) {
1032 /* shave off the leading slash, if there is one */
1033 if(*ptr == '/') {
1034 ptr++;
1036 alpm_option_set_cachedir(ptr);
1037 _alpm_log(PM_LOG_DEBUG, _("config: cachedir: %s"), ptr);
1038 } else if(strcmp(origkey, "RootDir") == 0 || strcmp(key, "ROOTDIR") == 0) {
1039 /* shave off the leading slash, if there is one */
1040 if(*ptr == '/') {
1041 ptr++;
1043 alpm_option_set_root(ptr);
1044 _alpm_log(PM_LOG_DEBUG, _("config: rootdir: %s"), ptr);
1045 } else if (strcmp(origkey, "LogFile") == 0 || strcmp(key, "LOGFILE") == 0) {
1046 alpm_option_set_logfile(ptr);
1047 _alpm_log(PM_LOG_DEBUG, _("config: logfile: %s"), ptr);
1048 } else if (strcmp(origkey, "XferCommand") == 0 || strcmp(key, "XFERCOMMAND") == 0) {
1049 alpm_option_set_xfercommand(ptr);
1050 _alpm_log(PM_LOG_DEBUG, _("config: xfercommand: %s"), ptr);
1051 } else if (strcmp(origkey, "UpgradeDelay") == 0 || strcmp(key, "UPGRADEDELAY") == 0) {
1052 /* The config value is in days, we use seconds */
1053 time_t ud = atol(ptr) * 60 * 60 *24;
1054 alpm_option_set_upgradedelay(ud);
1055 _alpm_log(PM_LOG_DEBUG, _("config: upgradedelay: %d"), ud);
1056 } else {
1057 RET_ERR(PM_ERR_CONF_BAD_SYNTAX, -1);
1059 } else {
1060 if(strcmp(origkey, "Server") == 0 || strcmp(key, "SERVER") == 0) {
1061 /* add to the list */
1062 if(alpm_db_setserver(db, ptr) != 0) {
1063 /* pm_errno is set by alpm_db_setserver */
1064 return(-1);
1066 } else {
1067 RET_ERR(PM_ERR_CONF_BAD_SYNTAX, -1);
1070 line[0] = '\0';
1074 fclose(fp);
1076 return(0);
1079 /** @} */
1081 /* This function is mostly the same as sync.c find_replacements and sysupgrade
1082 * functions, and we should be able to combine them - this is an interim
1083 * solution made for -Qu operation */
1084 alpm_list_t SYMEXPORT *alpm_get_upgrades()
1086 alpm_list_t *syncpkgs = NULL;
1087 alpm_list_t *i, *j, *k, *m;
1089 ALPM_LOG_FUNC;
1091 /* TODO holy nested loops, Batman! */
1092 /* check for "recommended" package replacements */
1093 _alpm_log(PM_LOG_DEBUG, _("checking for package replacements"));
1094 for(i = handle->dbs_sync; i; i = i->next) {
1095 for(j = _alpm_db_get_pkgcache(i->data); j; j = j->next) {
1096 pmpkg_t *spkg = j->data;
1098 for(k = alpm_pkg_get_replaces(spkg); k; k = k->next) {
1100 for(m = _alpm_db_get_pkgcache(handle->db_local); m; m = m->next) {
1101 pmpkg_t *lpkg = m->data;
1103 if(strcmp(k->data, alpm_pkg_get_name(lpkg)) == 0) {
1104 _alpm_log(PM_LOG_DEBUG, _("checking replacement '%s' for package '%s'"), k->data,
1105 alpm_pkg_get_name(spkg));
1106 if(alpm_list_find_str(handle->ignorepkg, alpm_pkg_get_name(lpkg))) {
1107 _alpm_log(PM_LOG_WARNING, _("%s-%s: ignoring package upgrade (to be replaced by %s-%s)"),
1108 alpm_pkg_get_name(lpkg), alpm_pkg_get_version(lpkg),
1109 alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
1110 } else {
1111 /* assume all replaces=() packages are accepted */
1112 pmsyncpkg_t *sync = NULL;
1113 pmpkg_t *dummy = _alpm_pkg_new(alpm_pkg_get_name(lpkg), NULL);
1114 if(dummy == NULL) {
1115 pm_errno = PM_ERR_MEMORY;
1116 goto error;
1118 dummy->requiredby = alpm_list_strdup(alpm_pkg_get_requiredby(lpkg));
1120 pmsyncpkg_t *syncpkg;
1121 syncpkg = _alpm_sync_find(syncpkgs, alpm_pkg_get_name(spkg));
1123 if(syncpkg) {
1124 /* found it -- just append to the replaces list */
1125 sync->data = alpm_list_add(sync->data, dummy);
1126 } else {
1127 /* none found -- enter pkg into the final sync list */
1128 sync = _alpm_sync_new(PM_SYNC_TYPE_REPLACE, spkg, NULL);
1129 if(sync == NULL) {
1130 FREEPKG(dummy);
1131 pm_errno = PM_ERR_MEMORY;
1132 goto error;
1134 sync->data = alpm_list_add(NULL, dummy);
1135 syncpkgs = alpm_list_add(syncpkgs, sync);
1137 _alpm_log(PM_LOG_DEBUG, _("%s-%s elected for upgrade (to be replaced by %s-%s)"),
1138 alpm_pkg_get_name(lpkg), alpm_pkg_get_version(lpkg),
1139 alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
1141 break;
1148 /* now do normal upgrades */
1149 for(i = _alpm_db_get_pkgcache(handle->db_local); i; i = i->next) {
1150 int replace=0;
1151 pmpkg_t *local = i->data;
1152 pmpkg_t *spkg = NULL;
1153 pmsyncpkg_t *sync;
1155 for(j = handle->dbs_sync; !spkg && j; j = j->next) {
1156 spkg = _alpm_db_get_pkgfromcache(j->data, alpm_pkg_get_name(local));
1158 if(spkg == NULL) {
1159 _alpm_log(PM_LOG_DEBUG, _("'%s' not found in sync db -- skipping"), alpm_pkg_get_name(local));
1160 continue;
1163 /* we don't care about a to-be-replaced package's newer version */
1164 for(j = syncpkgs; j && !replace; j=j->next) {
1165 sync = j->data;
1166 if(sync->type == PM_SYNC_TYPE_REPLACE) {
1167 if(_alpm_pkg_find(alpm_pkg_get_name(spkg), sync->data)) {
1168 replace=1;
1172 if(replace) {
1173 _alpm_log(PM_LOG_DEBUG, _("'%s' is already elected for removal -- skipping"),
1174 alpm_pkg_get_name(local));
1175 continue;
1178 if(alpm_pkg_compare_versions(local, spkg)) {
1179 _alpm_log(PM_LOG_DEBUG, _("%s elected for upgrade (%s => %s)"),
1180 alpm_pkg_get_name(local), alpm_pkg_get_version(local),
1181 alpm_pkg_get_version(spkg));
1183 pmsyncpkg_t *syncpkg;
1184 syncpkg = _alpm_sync_find(syncpkgs, alpm_pkg_get_name(local));
1186 if(!syncpkg) {
1187 pmpkg_t *dummy = _alpm_pkg_new(alpm_pkg_get_name(local),
1188 alpm_pkg_get_version(local));
1189 if(dummy == NULL) {
1190 goto error;
1192 sync = _alpm_sync_new(PM_SYNC_TYPE_UPGRADE, spkg, dummy);
1193 if(sync == NULL) {
1194 FREEPKG(dummy);
1195 goto error;
1197 syncpkgs = alpm_list_add(syncpkgs, sync);
1202 return(syncpkgs);
1203 error:
1204 if(syncpkgs) {
1205 alpm_list_free_inner(syncpkgs, _alpm_sync_free);
1206 alpm_list_free(syncpkgs);
1208 return(NULL);
1211 /* vim: set ts=2 sw=2 noet: */