From 72c24395762c985e404f65f7ec1064efed1d44b5 Mon Sep 17 00:00:00 2001 From: Aurelien Foret Date: Sun, 20 Mar 2005 09:22:03 +0000 Subject: Added support for .lastupdate files (from pacman 2.9.1) --- lib/libalpm/alpm.c | 26 +++++++++++---- lib/libalpm/alpm.h | 3 +- lib/libalpm/db.c | 78 +++++++++++++++++++++++++++++++++++++++------ lib/libalpm/db.h | 4 ++- src/pacman/download.c | 87 +++++++++++++++++++++++++++++++++++++++++---------- src/pacman/download.h | 3 ++ src/pacman/log.c | 5 ++- src/pacman/sync.c | 59 ++++++++++++++++++++-------------- 8 files changed, 205 insertions(+), 60 deletions(-) diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c index 94c8d2dc..93ac2fad 100644 --- a/lib/libalpm/alpm.c +++ b/lib/libalpm/alpm.c @@ -205,17 +205,31 @@ int alpm_db_unregister(PM_DB *db) return(0); } -int alpm_db_update(char *treename, char *archive) +int alpm_db_getlastupdate(PM_DB *db, char *ts) { /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(treename != NULL && strlen(treename) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(db != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + return(db_getlastupdate(db, handle->root, handle->dbpath, ts)); +} + +int alpm_db_update(PM_DB *db, char *archive, char *ts) +{ + /* Sanity checks */ + ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(db != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* ORE + Does it make sense to update the 'local' database, or should we prevent it? */ + + /* ORE + check if the database is registered: if not, return an error */ /* ORE - Does it make sense to update the 'local' database, or should we prevent it? - stat(archive); */ + stat() the archive to check it exists */ - return(db_update(handle->root, handle->dbpath, treename, archive)); + return(db_update(db, handle->root, handle->dbpath, archive, ts)); } PM_PKG *alpm_db_readpkg(PM_DB *db, char *name) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 8eb1ece0..6b69b3b0 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -98,7 +98,8 @@ int alpm_get_option(unsigned char parm, long *data); int alpm_db_register(char *treename, PM_DB **db); int alpm_db_unregister(PM_DB *db); -int alpm_db_update(char *treename, char *archive); +int alpm_db_getlastupdate(PM_DB *db, char *ts); +int alpm_db_update(PM_DB *db, char *archive, char *ts); PM_PKG *alpm_db_readpkg(PM_DB *db, char *name); PM_LIST *alpm_db_getpkgcache(PM_DB *db); diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c index c555f256..7768b8a5 100644 --- a/lib/libalpm/db.c +++ b/lib/libalpm/db.c @@ -97,34 +97,92 @@ int db_create(char *root, char *dbpath, char *treename) return(0); } -int db_update(char *root, char *dbpath, char *treename, char *archive) +/* reads dbpath/.lastupdate and populates *ts with the contents. + * *ts should be malloc'ed and should be at least 15 bytes. + * + * Returns 0 on success, 1 on error + * + */ +int db_getlastupdate(pmdb_t *db, char *root, char *dbpath, char *ts) +{ + FILE *fp; + char path[PATH_MAX]; + + if(db == NULL) { + return(-1); + } + + /* get the last update time, if it's there */ + snprintf(path, PATH_MAX, "%s%s/%s/.lastupdate", root, dbpath, db->treename); + if((fp = fopen(path, "r")) == NULL) { + return(-1); + } else { + char line[256]; + if(fgets(line, sizeof(line), fp)) { + strncpy(ts, line, 15); /* YYYYMMDDHHMMSS */ + ts[14] = '\0'; + } else { + fclose(fp); + return(-1); + } + } + fclose(fp); + return(0); +} + +int db_update(pmdb_t *db, char *root, char *dbpath, char *archive, char *ts) { - char ldir[PATH_MAX]; + char path[PATH_MAX]; + + if(db == NULL) { + return(-1); + } + + snprintf(path, PATH_MAX, "%s%s/%s", root, dbpath, db->treename); + + /* ORE + if(ts && strlen(ts)) { + Should we refuse to update the db if it is already uptodate? + if(ts != db_getlastupdate(db)) { + RET_ERR(PM_ERR_DB_UPTODATE, -1); + } + }*/ - snprintf(ldir, PATH_MAX, "%s%s/%s", root, dbpath, treename); /* remove the old dir */ /* ORE - do we want to include alpm.h and use the log mechanism from db.c? - _alpm_log(PM_LOG_FLOW2, "removing %s (if it exists)\n", ldir);*/ + _alpm_log(PM_LOG_FLOW2, "removing %s (if it exists)\n", path);*/ /* ORE We should only rmrf the database content, and not the top directory, in case a (DIR *) structure is associated with it (i.e a call to db_open). */ - _alpm_rmrf(ldir); + _alpm_rmrf(path); /* make the new dir */ - if(db_create(root, dbpath, treename) != 0) { + if(db_create(root, dbpath, db->treename) != 0) { return(-1); } /* uncompress the sync database */ /* ORE _alpm_log(PM_LOG_FLOW2, "Unpacking %s...\n", archive);*/ - if(_alpm_unpack(archive, ldir, NULL)) { + if(_alpm_unpack(archive, path, NULL)) { return(-1); } - /* ORE - Should we let the the library manage updates only if needed? - Create a .lastupdate file in ldir? Ask for a timestamp as db_update argument? */ + /* writes the db->path/.lastupdate with the contents of *ts */ + if(ts && strlen(ts)) { + FILE *fp; + char file[PATH_MAX]; + + snprintf(file, PATH_MAX, "%s/.lastupdate", path); + if((fp = fopen(file, "w")) == NULL) { + return(-1); + } + if(fputs(ts, fp) <= 0) { + fclose(fp); + return(-1); + } + fclose(fp); + } return(0); } diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h index 07fa1ef2..e2cd8aa9 100644 --- a/lib/libalpm/db.h +++ b/lib/libalpm/db.h @@ -48,7 +48,9 @@ typedef struct __pmdb_t { pmdb_t *db_open(char *root, char *dbpath, char *treename); void db_close(pmdb_t *db); int db_create(char *root, char *dbpath, char *treename); -int db_update(char *root, char *dbpath, char *treename, char *archive); + +int db_getlastupdate(pmdb_t *db, char *root, char *dbpath, char *ts); +int db_update(pmdb_t *db, char *root, char *dbpath, char *archive, char *ts); void db_rewind(pmdb_t *db); pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq); diff --git a/src/pacman/download.c b/src/pacman/download.c index f994cab4..7727473b 100644 --- a/src/pacman/download.c +++ b/src/pacman/download.c @@ -31,6 +31,7 @@ #include /* pacman */ +#include "util.h" #include "log.h" #include "list.h" #include "download.h" @@ -126,7 +127,36 @@ static int copyfile(char *src, char *dest) return(0); } +/* + * Download a list of files from a list of servers + * - if one server fails, we try the next one in the list + * + * RETURN: 0 for successful download, 1 on error + */ int downloadfiles(list_t *servers, const char *localpath, list_t *files) +{ + return(!!downloadfiles_forreal(servers, localpath, files, NULL, NULL)); +} + +/* + * This is the real downloadfiles, used directly by sync_synctree() to check + * modtimes on remote (ftp only) files. + * - if *mtime1 is non-NULL, then only download files + * if they are different than *mtime1. String should be in the form + * "YYYYMMDDHHMMSS" to match the form of ftplib's FtpModDate() function. + * - if *mtime2 is non-NULL, then it will be filled with the mtime + * of the remote FTP file (from MDTM). + * + * NOTE: the *mtime option only works for FTP repositories, and won't work + * if XferCommand is used. We only use it to check mtimes on the + * repo db files. + * + * RETURN: 0 for successful download + * -1 if the mtimes are identical + * 1 on error + */ +int downloadfiles_forreal(list_t *servers, const char *localpath, + list_t *files, const char *mtime1, char *mtime2) { int fsz; netbuf *control = NULL; @@ -145,7 +175,7 @@ int downloadfiles(list_t *servers, const char *localpath, list_t *files) if(!pmo_xfercommand && strcmp(server->protocol, "file")) { if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { FtpInit(); - vprint("Connecting to %s:21\n", server->server); + vprint("connecting to %s:21\n", server->server); if(!FtpConnect(server->server, &control)) { fprintf(stderr, "error: cannot connect to %s\n", server->server); continue; @@ -174,9 +204,9 @@ int downloadfiles(list_t *servers, const char *localpath, list_t *files) host = (pmo_proxyhost) ? pmo_proxyhost : server->server; port = (pmo_proxyhost) ? pmo_proxyport : 80; if(strchr(host, ':')) { - vprint("Connecting to %s\n", host); + vprint("connecting to %s\n", host); } else { - vprint("Connecting to %s:%u\n", host, port); + vprint("connecting to %s:%u\n", host, port); } if(!HttpConnect(host, port, &control)) { fprintf(stderr, "error: cannot connect to %s\n", host); @@ -296,21 +326,42 @@ int downloadfiles(list_t *servers, const char *localpath, list_t *files) if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) { fprintf(stderr, "warning: failed to get filesize for %s\n", fn); } - if(!stat(output, &st)) { - offset = (int)st.st_size; - if(!FtpRestart(offset, control)) { - fprintf(stderr, "warning: failed to resume download -- restarting\n"); - /* can't resume: */ - /* unlink the file in order to restart download from scratch */ - unlink(output); + /* check mtimes */ + if(mtime1 || mtime2) { + char fmtime[64]; + if(!FtpModDate(fn, fmtime, sizeof(fmtime)-1, control)) { + fprintf(stderr, "warning: failed to get mtime for %s\n", fn); + } else { + strtrim(fmtime); + if(mtime1 && !strcmp(mtime1, fmtime)) { + /* mtimes are identical, skip this file */ + vprint("mtimes are identical, skipping %s\n", fn); + filedone = -1; + complete = list_add(complete, fn); + } + if(mtime2) { + strncpy(mtime2, fmtime, 15); /* YYYYMMDDHHMMSS (=14b) */ + mtime2[14] = '\0'; + } } } - if(!FtpGet(output, fn, FTPLIB_IMAGE, control)) { - fprintf(stderr, "\nfailed downloading %s from %s: %s\n", - fn, server->server, FtpLastResponse(control)); - /* we leave the partially downloaded file in place so it can be resumed later */ - } else { - filedone = 1; + if(!filedone) { + if(!stat(output, &st)) { + offset = (int)st.st_size; + if(!FtpRestart(offset, control)) { + fprintf(stderr, "warning: failed to resume download -- restarting\n"); + /* can't resume: */ + /* unlink the file in order to restart download from scratch */ + unlink(output); + } + } + if(!FtpGet(output, fn, FTPLIB_IMAGE, control)) { + fprintf(stderr, "\nfailed downloading %s from %s: %s\n", + fn, server->server, FtpLastResponse(control)); + /* we leave the partially downloaded file in place so it can be resumed later */ + } else { + filedone = 1; + } } } else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && strcmp(server->protocol, "file"))) { char src[PATH_MAX]; @@ -367,7 +418,7 @@ int downloadfiles(list_t *servers, const char *localpath, list_t *files) } } - if(filedone) { + if(filedone > 0) { char completefile[PATH_MAX]; if(!strcmp(server->protocol, "file")) { char out[56]; @@ -385,6 +436,8 @@ int downloadfiles(list_t *servers, const char *localpath, list_t *files) /* rename "output.part" file to "output" file */ snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); rename(output, completefile); + } else if(filedone < 0) { + return(-1); } printf("\n"); fflush(stdout); diff --git a/src/pacman/download.h b/src/pacman/download.h index 22ecf574..a8278475 100644 --- a/src/pacman/download.h +++ b/src/pacman/download.h @@ -31,6 +31,9 @@ typedef struct __server_t { } server_t; int downloadfiles(list_t *servers, const char *localpath, list_t *files); +int downloadfiles_forreal(list_t *servers, const char *localpath, + list_t *files, const char *mtime1, char *mtime2); + char *fetch_pkgurl(char *target); #endif /* _PM_DOWNLOAD_H */ diff --git a/src/pacman/log.c b/src/pacman/log.c index c0bb62b6..b5ab2400 100644 --- a/src/pacman/log.c +++ b/src/pacman/log.c @@ -107,7 +107,10 @@ void vprint(char *fmt, ...) neednl = 0; } va_start(args, fmt); - pm_fprintf(stdout, NL, fmt, args); + /* ORE + commented for now: it produces corruption + pm_fprintf(stdout, NL, fmt, args); */ + vprintf(fmt, args); va_end(args); } } diff --git a/src/pacman/sync.c b/src/pacman/sync.c index 86ede1cd..581d07e5 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -159,43 +159,54 @@ static int sync_cleancache(int level) static int sync_synctree(list_t *syncs) { char *root, *dbpath; + char path[PATH_MAX]; list_t *i; - int ret = 0; + int success = 0, ret; alpm_get_option(PM_OPT_ROOT, (long *)&root); alpm_get_option(PM_OPT_DBPATH, (long *)&dbpath); for(i = syncs; i; i = i->next) { - char path[PATH_MAX]; list_t *files = NULL; + char *mtime = NULL; + char newmtime[16] = ""; + char lastupdate[16] = ""; sync_t *sync = (sync_t *)i->data; + /* get the lastupdate time */ + snprintf(path, PATH_MAX, "%s/%s", path, sync->treename); + if(alpm_db_getlastupdate(sync->db, lastupdate) == -1) { + vprint("failed to get lastupdate time for %s (no big deal)\n", sync->treename); + } + mtime = lastupdate; + /* build a one-element list */ snprintf(path, PATH_MAX, "%s"PM_EXT_DB, sync->treename); files = list_add(files, strdup(path)); snprintf(path, PATH_MAX, "%s%s", root, dbpath); - if(downloadfiles(sync->servers, path, files)) { - fprintf(stderr, "failed to synchronize %s\n", sync->treename); - FREELIST(files); - ret--; - continue; - } - + ret = downloadfiles_forreal(sync->servers, path, files, mtime, newmtime); + vprint("sync: new mtime for %s: %s\n", sync->treename, newmtime); FREELIST(files); - - snprintf(path, PATH_MAX, "%s%s/%s"PM_EXT_DB, root, dbpath, sync->treename); - if(alpm_db_update(sync->treename, path) == -1) { - fprintf(stderr, "error: %s\n", alpm_strerror(pm_errno)); - ret--; + if(ret > 0) { + fprintf(stderr, "failed to synchronize %s\n", sync->treename); + success--; + } else if(ret < 0) { + printf(":: %s is up to date\n", sync->treename); + } else { + snprintf(path, PATH_MAX, "%s%s/%s"PM_EXT_DB, root, dbpath, sync->treename); + if(alpm_db_update(sync->db, path, newmtime) == -1) { + fprintf(stderr, "error: failed to set database timestamp (%s)\n", alpm_strerror(pm_errno)); + success--; + } + /* remove the .tar.gz */ + unlink(path); } - /* remove the .tar.gz */ - unlink(path); } - return(ret); + return(success); } static int sync_search(list_t *syncs, list_t *targets) @@ -375,13 +386,6 @@ int pacman_sync(list_t *targets) return(sync_cleancache(pmo_s_clean)); } - if(pmo_s_sync) { - /* grab a fresh package list */ - MSG(NL, ":: Synchronizing package databases...\n"); - alpm_logaction("synchronizing package lists"); - sync_synctree(pmc_syncs); - } - /* open the database(s) */ for(i = pmc_syncs; i; i = i->next) { sync_t *sync = i->data; @@ -391,6 +395,13 @@ int pacman_sync(list_t *targets) } } + if(pmo_s_sync) { + /* grab a fresh package list */ + MSG(NL, ":: Synchronizing package databases...\n"); + alpm_logaction("synchronizing package lists"); + sync_synctree(pmc_syncs); + } + if(pmo_s_search) { return(sync_search(pmc_syncs, targets)); } -- cgit v1.2.3-54-g00ecf