4 * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #include <archive_entry.h>
36 #include "alpm_list.h"
43 /* create list of directories in db */
44 static int dirlist_from_tar(const char *archive
, alpm_list_t
**dirlist
)
46 struct archive
*_archive
;
47 struct archive_entry
*entry
;
49 if((_archive
= archive_read_new()) == NULL
)
50 RET_ERR(PM_ERR_LIBARCHIVE
, -1);
52 archive_read_support_compression_all(_archive
);
53 archive_read_support_format_all(_archive
);
55 if(archive_read_open_filename(_archive
, archive
,
56 ARCHIVE_DEFAULT_BYTES_PER_BLOCK
) != ARCHIVE_OK
) {
57 _alpm_log(PM_LOG_ERROR
, _("could not open %s: %s\n"), archive
,
58 archive_error_string(_archive
));
59 RET_ERR(PM_ERR_PKG_OPEN
, -1);
62 while(archive_read_next_header(_archive
, &entry
) == ARCHIVE_OK
) {
63 const struct stat
*st
;
64 const char *entryname
; /* the name of the file in the archive */
66 st
= archive_entry_stat(entry
);
67 entryname
= archive_entry_pathname(entry
);
69 if(S_ISDIR(st
->st_mode
)) {
70 char *name
= strdup(entryname
);
71 *dirlist
= alpm_list_add(*dirlist
, name
);
74 archive_read_finish(_archive
);
76 *dirlist
= alpm_list_msort(*dirlist
, alpm_list_count(*dirlist
), _alpm_str_cmp
);
80 static int is_dir(const char *path
, struct dirent
*entry
)
83 return(entry
->d_type
== DT_DIR
);
85 char buffer
[PATH_MAX
];
86 snprintf(buffer
, PATH_MAX
, "%s/%s", path
, entry
->d_name
);
89 if (!stat(buffer
, &sbuf
)) {
90 return(S_ISDIR(sbuf
.st_mode
));
97 /* create list of directories in db */
98 static int dirlist_from_fs(const char *syncdbpath
, alpm_list_t
**dirlist
)
101 struct dirent
*ent
= NULL
;
103 dbdir
= opendir(syncdbpath
);
105 while((ent
= readdir(dbdir
)) != NULL
) {
106 char *name
= ent
->d_name
;
110 if(strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0) {
114 if(!is_dir(syncdbpath
, ent
)) {
119 MALLOC(entry
, len
+ 2, RET_ERR(PM_ERR_MEMORY
, -1));
123 *dirlist
= alpm_list_add(*dirlist
, entry
);
128 *dirlist
= alpm_list_msort(*dirlist
, alpm_list_count(*dirlist
), _alpm_str_cmp
);
132 /* remove old directories from dbdir */
133 static int remove_olddir(const char *syncdbpath
, alpm_list_t
*dirlist
)
136 for (i
= dirlist
; i
; i
= i
->next
) {
137 const char *name
= i
->data
;
139 size_t len
= strlen(syncdbpath
) + strlen(name
) + 2;
140 MALLOC(dbdir
, len
, RET_ERR(PM_ERR_MEMORY
, -1));
141 snprintf(dbdir
, len
, "%s%s", syncdbpath
, name
);
142 _alpm_log(PM_LOG_DEBUG
, "removing: %s\n", dbdir
);
143 if(_alpm_rmrf(dbdir
) != 0) {
144 _alpm_log(PM_LOG_ERROR
, _("could not remove database directory %s\n"), dbdir
);
146 RET_ERR(PM_ERR_DB_REMOVE
, -1);
153 /** Update a package database
155 * An update of the package database \a db will be attempted. Unless
156 * \a force is true, the update will only be performed if the remote
157 * database was modified since the last update.
159 * A transaction is necessary for this operation, in order to obtain a
160 * database lock. During this transaction the front-end will be informed
161 * of the download progress of the database via the download callback.
167 * db = alpm_list_getdata(alpm_option_get_syncdbs());
168 * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) {
169 * result = alpm_db_update(0, db);
170 * alpm_trans_release();
173 * printf("Unable to update database: %s\n", alpm_strerrorlast());
174 * } else if(result < 0) {
175 * printf("Database already up to date\n");
177 * printf("Database updated\n");
182 * @ingroup alpm_databases
183 * @note After a successful update, the \link alpm_db_get_pkgcache()
184 * package cache \endlink will be invalidated
185 * @param force if true, then forces the update, otherwise update only in case
186 * the database isn't up to date
187 * @param db pointer to the package database to update
188 * @return 0 on success, > 0 on error (pm_errno is set accordingly), < 0 if up
191 int SYMEXPORT
alpm_db_update(int force
, pmdb_t
*db
)
193 char *dbfile
, *dbfilepath
, *syncpath
;
194 const char *dbpath
, *syncdbpath
;
195 alpm_list_t
*newdirlist
= NULL
, *olddirlist
= NULL
;
196 alpm_list_t
*onlynew
= NULL
, *onlyold
= NULL
;
203 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
204 ASSERT(db
!= NULL
&& db
!= handle
->db_local
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
205 /* Verify we are in a transaction. This is done _mainly_ because we need a DB
206 * lock - if we update without a db lock, we may kludge some other pacman
207 * process that _has_ a lock.
209 ASSERT(handle
->trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
210 ASSERT(handle
->trans
->state
== STATE_INITIALIZED
, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED
, -1));
212 if(!alpm_list_find_ptr(handle
->dbs_sync
, db
)) {
213 RET_ERR(PM_ERR_DB_NOT_FOUND
, -1);
216 len
= strlen(db
->treename
) + 4;
217 MALLOC(dbfile
, len
, RET_ERR(PM_ERR_MEMORY
, -1));
218 sprintf(dbfile
, "%s.db", db
->treename
);
220 dbpath
= alpm_option_get_dbpath();
221 len
= strlen(dbpath
) + 6;
222 MALLOC(syncpath
, len
, RET_ERR(PM_ERR_MEMORY
, -1));
223 sprintf(syncpath
, "%s%s", dbpath
, "sync/");
225 ret
= _alpm_download_single_file(dbfile
, db
->servers
, syncpath
, force
);
230 /* files match, do nothing */
233 } else if(ret
== -1) {
234 /* pm_errno was set by the download code */
235 _alpm_log(PM_LOG_DEBUG
, "failed to sync db: %s\n", alpm_strerrorlast());
239 syncdbpath
= _alpm_db_path(db
);
241 /* form the path to the db location */
242 len
= strlen(dbpath
) + strlen(db
->treename
) + 9;
243 MALLOC(dbfilepath
, len
, RET_ERR(PM_ERR_MEMORY
, -1));
244 sprintf(dbfilepath
, "%ssync/%s.db", dbpath
, db
->treename
);
247 /* if forcing update, remove the old dir and extract the db */
248 if(_alpm_rmrf(syncdbpath
) != 0) {
249 _alpm_log(PM_LOG_ERROR
, _("could not remove database %s\n"), db
->treename
);
250 RET_ERR(PM_ERR_DB_REMOVE
, -1);
252 _alpm_log(PM_LOG_DEBUG
, "database dir %s removed\n", _alpm_db_path(db
));
255 /* if not forcing, only remove and extract what is necessary */
256 ret
= dirlist_from_tar(dbfilepath
, &newdirlist
);
260 ret
= dirlist_from_fs(syncdbpath
, &olddirlist
);
265 alpm_list_diff_sorted(olddirlist
, newdirlist
, _alpm_str_cmp
, &onlyold
, &onlynew
);
267 ret
= remove_olddir(syncdbpath
, onlyold
);
273 /* Cache needs to be rebuilt */
274 _alpm_db_free_pkgcache(db
);
277 ret
= _alpm_unpack(dbfilepath
, syncdbpath
, onlynew
, 0);
280 FREELIST(newdirlist
);
281 FREELIST(olddirlist
);
282 alpm_list_free(onlynew
);
283 alpm_list_free(onlyold
);
288 RET_ERR(PM_ERR_SYSTEM
, -1);
294 int _alpm_sync_db_populate(pmdb_t
*db
)
297 struct archive
*archive
;
298 struct archive_entry
*entry
;
299 const char * archive_path
;
303 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
305 if((archive
= archive_read_new()) == NULL
)
306 RET_ERR(PM_ERR_LIBARCHIVE
, 1);
308 archive_read_support_compression_all(archive
);
309 archive_read_support_format_all(archive
);
311 if(archive_read_open_filename(archive
, _alpm_db_path(db
),
312 ARCHIVE_DEFAULT_BYTES_PER_BLOCK
) != ARCHIVE_OK
) {
313 _alpm_log(PM_LOG_ERROR
, _("could not open %s: %s\n"), _alpm_db_path(db
),
314 archive_error_string(archive
));
315 RET_ERR(PM_ERR_PKG_OPEN
, 1);
318 while(archive_read_next_header(archive
, &entry
) == ARCHIVE_OK
) {
319 const struct stat
*st
;
323 st
= archive_entry_stat(entry
);
325 if(S_ISDIR(st
->st_mode
)) {
326 archive_path
= archive_entry_pathname(entry
);
328 pkg
= _alpm_pkg_new();
330 archive_read_finish(archive
);
334 name
= archive_entry_pathname(entry
);
336 if(splitname(name
, pkg
) != 0) {
337 _alpm_log(PM_LOG_ERROR
, _("invalid name for database entry '%s'\n"),
343 /* duplicated database entries are not allowed */
344 if(_alpm_pkg_find(db
->pkgcache
, pkg
->name
)) {
345 _alpm_log(PM_LOG_ERROR
, _("duplicated database entry '%s'\n"), pkg
->name
);
350 pkg
->origin
= PKG_FROM_SYNCDB
;
351 pkg
->ops
= &default_pkg_ops
;
352 pkg
->origin_data
.db
= db
;
354 /* add to the collection */
355 _alpm_log(PM_LOG_FUNCTION
, "adding '%s' to package cache for db '%s'\n",
356 pkg
->name
, db
->treename
);
357 db
->pkgcache
= alpm_list_add(db
->pkgcache
, pkg
);
360 /* we have desc, depends or deltas - parse it */
361 _alpm_sync_db_read(db
, archive
, entry
);
365 db
->pkgcache
= alpm_list_msort(db
->pkgcache
, count
, _alpm_pkg_cmp
);
366 archive_read_finish(archive
);
371 int _alpm_sync_db_read(pmdb_t
*db
, struct archive
*archive
, struct archive_entry
*entry
)
374 const char *entryname
;
375 char *filename
, *pkgname
, *p
, *q
;
381 RET_ERR(PM_ERR_DB_NULL
, -1);
385 _alpm_log(PM_LOG_DEBUG
, "invalid archive entry provided to _alpm_sync_db_read, skipping\n");
389 entryname
= archive_entry_pathname(entry
);
391 _alpm_log(PM_LOG_FUNCTION
, "loading package data from archive entry %s\n",
394 /* get package and db file names */
395 STRDUP(pkgname
, entryname
, RET_ERR(PM_ERR_MEMORY
, -1));
396 p
= pkgname
+ strlen(pkgname
);
397 for(q
= --p
; *q
&& *q
!= '/'; q
--);
398 STRDUP(filename
, q
+1, RET_ERR(PM_ERR_MEMORY
, -1));
399 for(p
= --q
; *p
&& *p
!= '-'; p
--);
400 for(q
= --p
; *q
&& *q
!= '-'; q
--);
403 /* package is already in db due to parsing of directory name */
404 pkg
= _alpm_pkg_find(db
->pkgcache
, pkgname
);
406 _alpm_log(PM_LOG_DEBUG
, "package %s not found in %s sync database",
407 pkgname
, db
->treename
);
411 if(strcmp(filename
, "desc") == 0) {
412 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) != NULL
) {
414 if(strcmp(line
, "%NAME%") == 0) {
415 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
418 if(strcmp(_alpm_strtrim(line
), pkg
->name
) != 0) {
419 _alpm_log(PM_LOG_ERROR
, _("%s database is inconsistent: name "
420 "mismatch on package %s\n"), db
->treename
, pkg
->name
);
422 } else if(strcmp(line
, "%VERSION%") == 0) {
423 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
426 if(strcmp(_alpm_strtrim(line
), pkg
->version
) != 0) {
427 _alpm_log(PM_LOG_ERROR
, _("%s database is inconsistent: version "
428 "mismatch on package %s\n"), db
->treename
, pkg
->name
);
430 } else if(strcmp(line
, "%FILENAME%") == 0) {
431 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
434 STRDUP(pkg
->filename
, _alpm_strtrim(line
), goto error
);
435 } else if(strcmp(line
, "%DESC%") == 0) {
436 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
439 STRDUP(pkg
->desc
, _alpm_strtrim(line
), goto error
);
440 } else if(strcmp(line
, "%GROUPS%") == 0) {
441 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) && strlen(_alpm_strtrim(line
))) {
443 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
444 pkg
->groups
= alpm_list_add(pkg
->groups
, linedup
);
446 } else if(strcmp(line
, "%URL%") == 0) {
447 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
450 STRDUP(pkg
->url
, _alpm_strtrim(line
), goto error
);
451 } else if(strcmp(line
, "%LICENSE%") == 0) {
452 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
453 strlen(_alpm_strtrim(line
))) {
455 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
456 pkg
->licenses
= alpm_list_add(pkg
->licenses
, linedup
);
458 } else if(strcmp(line
, "%ARCH%") == 0) {
459 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
462 STRDUP(pkg
->arch
, _alpm_strtrim(line
), goto error
);
463 } else if(strcmp(line
, "%BUILDDATE%") == 0) {
464 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
469 char first
= tolower((unsigned char)line
[0]);
470 if(first
> 'a' && first
< 'z') {
471 struct tm tmp_tm
= {0}; /* initialize to null in case of failure */
472 setlocale(LC_TIME
, "C");
473 strptime(line
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
474 pkg
->builddate
= mktime(&tmp_tm
);
475 setlocale(LC_TIME
, "");
477 pkg
->builddate
= atol(line
);
479 } else if(strcmp(line
, "%INSTALLDATE%") == 0) {
480 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
485 char first
= tolower((unsigned char)line
[0]);
486 if(first
> 'a' && first
< 'z') {
487 struct tm tmp_tm
= {0}; /* initialize to null in case of failure */
488 setlocale(LC_TIME
, "C");
489 strptime(line
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
490 pkg
->installdate
= mktime(&tmp_tm
);
491 setlocale(LC_TIME
, "");
493 pkg
->installdate
= atol(line
);
495 } else if(strcmp(line
, "%PACKAGER%") == 0) {
496 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
499 STRDUP(pkg
->packager
, _alpm_strtrim(line
), goto error
);
500 } else if(strcmp(line
, "%REASON%") == 0) {
501 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
504 pkg
->reason
= (pmpkgreason_t
)atol(_alpm_strtrim(line
));
505 } else if(strcmp(line
, "%SIZE%") == 0 || strcmp(line
, "%CSIZE%") == 0) {
506 /* NOTE: the CSIZE and SIZE fields both share the "size" field
507 * in the pkginfo_t struct. This can be done b/c CSIZE
508 * is currently only used in sync databases, and SIZE is
509 * only used in local databases.
511 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
514 pkg
->size
= atol(_alpm_strtrim(line
));
515 /* also store this value to isize if isize is unset */
516 if(pkg
->isize
== 0) {
517 pkg
->isize
= pkg
->size
;
519 } else if(strcmp(line
, "%ISIZE%") == 0) {
520 /* ISIZE (installed size) tag only appears in sync repositories,
521 * not the local one. */
522 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
525 pkg
->isize
= atol(_alpm_strtrim(line
));
526 } else if(strcmp(line
, "%MD5SUM%") == 0) {
527 /* MD5SUM tag only appears in sync repositories,
528 * not the local one. */
529 if(_alpm_archive_fgets(line
, sizeof(line
), archive
) == NULL
) {
532 STRDUP(pkg
->md5sum
, _alpm_strtrim(line
), goto error
);
533 } else if(strcmp(line
, "%REPLACES%") == 0) {
534 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
535 strlen(_alpm_strtrim(line
))) {
537 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
538 pkg
->replaces
= alpm_list_add(pkg
->replaces
, linedup
);
540 } else if(strcmp(line
, "%FORCE%") == 0) {
544 } else if(strcmp(filename
, "depends") == 0) {
545 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) != NULL
) {
547 if(strcmp(line
, "%DEPENDS%") == 0) {
548 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
549 strlen(_alpm_strtrim(line
))) {
550 pmdepend_t
*dep
= _alpm_splitdep(_alpm_strtrim(line
));
551 pkg
->depends
= alpm_list_add(pkg
->depends
, dep
);
553 } else if(strcmp(line
, "%OPTDEPENDS%") == 0) {
554 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
555 strlen(_alpm_strtrim(line
))) {
557 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
558 pkg
->optdepends
= alpm_list_add(pkg
->optdepends
, linedup
);
560 } else if(strcmp(line
, "%CONFLICTS%") == 0) {
561 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
562 strlen(_alpm_strtrim(line
))) {
564 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
565 pkg
->conflicts
= alpm_list_add(pkg
->conflicts
, linedup
);
567 } else if(strcmp(line
, "%PROVIDES%") == 0) {
568 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) &&
569 strlen(_alpm_strtrim(line
))) {
571 STRDUP(linedup
, _alpm_strtrim(line
), goto error
);
572 pkg
->provides
= alpm_list_add(pkg
->provides
, linedup
);
576 } else if(strcmp(filename
, "deltas") == 0) {
577 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) != NULL
) {
579 if(strcmp(line
, "%DELTAS%") == 0) {
580 while(_alpm_archive_fgets(line
, sizeof(line
), archive
) && strlen(_alpm_strtrim(line
))) {
581 pmdelta_t
*delta
= _alpm_delta_parse(line
);
583 pkg
->deltas
= alpm_list_add(pkg
->deltas
, delta
);
589 /* unknown database file */
590 _alpm_log(PM_LOG_DEBUG
, "unknown database file: %s", filename
);
599 struct db_operations sync_db_ops
= {
600 .populate
= _alpm_sync_db_populate
,
601 .unregister
= _alpm_db_unregister
,
604 pmdb_t
*_alpm_db_register_sync(const char *treename
)
611 for(i
= handle
->dbs_sync
; i
; i
= i
->next
) {
612 pmdb_t
*sdb
= i
->data
;
613 if(strcmp(treename
, sdb
->treename
) == 0) {
614 _alpm_log(PM_LOG_DEBUG
, "attempt to re-register the '%s' database, using existing\n", sdb
->treename
);
619 _alpm_log(PM_LOG_DEBUG
, "registering sync database '%s'\n", treename
);
621 db
= _alpm_db_new(treename
, 0);
622 db
->ops
= &sync_db_ops
;
624 RET_ERR(PM_ERR_DB_CREATE
, NULL
);
627 handle
->dbs_sync
= alpm_list_add(handle
->dbs_sync
, db
);
632 /* vim: set ts=2 sw=2 noet: */