From d04baabafa2ebbad92741d1f87e6ff32999f894a Mon Sep 17 00:00:00 2001 From: Judd Vinet Date: Tue, 15 Mar 2005 01:51:43 +0000 Subject: Initial revision --- lib/libalpm/Makefile | 39 ++ lib/libalpm/add.c | 580 ++++++++++++++++++ lib/libalpm/add.h | 34 + lib/libalpm/alpm.c | 577 +++++++++++++++++ lib/libalpm/alpm.h | 330 ++++++++++ lib/libalpm/backup.c | 63 ++ lib/libalpm/backup.h | 30 + lib/libalpm/cache.c | 203 ++++++ lib/libalpm/cache.h | 42 ++ lib/libalpm/db.c | 651 ++++++++++++++++++++ lib/libalpm/db.h | 60 ++ lib/libalpm/deps.c | 685 +++++++++++++++++++++ lib/libalpm/deps.h | 35 ++ lib/libalpm/error.c | 90 +++ lib/libalpm/error.h | 30 + lib/libalpm/group.c | 67 ++ lib/libalpm/group.h | 48 ++ lib/libalpm/handle.c | 229 +++++++ lib/libalpm/handle.h | 63 ++ lib/libalpm/list.c | 210 +++++++ lib/libalpm/list.h | 50 ++ lib/libalpm/log.c | 52 ++ lib/libalpm/log.h | 32 + lib/libalpm/md5.c | 338 ++++++++++ lib/libalpm/md5.h | 51 ++ lib/libalpm/md5driver.c | 81 +++ lib/libalpm/package.c | 342 +++++++++++ lib/libalpm/package.h | 78 +++ lib/libalpm/provide.c | 53 ++ lib/libalpm/provide.h | 33 + lib/libalpm/remove.c | 259 ++++++++ lib/libalpm/remove.h | 34 + lib/libalpm/rpmvercmp.c | 237 +++++++ lib/libalpm/rpmvercmp.h | 32 + lib/libalpm/sync.c | 248 ++++++++ lib/libalpm/sync.h | 47 ++ lib/libalpm/trans.c | 187 ++++++ lib/libalpm/trans.h | 54 ++ lib/libalpm/util.c | 401 ++++++++++++ lib/libalpm/util.h | 57 ++ lib/libftp/Makefile | 68 ++ lib/libftp/ftplib.c | 1569 +++++++++++++++++++++++++++++++++++++++++++++++ lib/libftp/ftplib.h | 131 ++++ 43 files changed, 8500 insertions(+) create mode 100644 lib/libalpm/Makefile create mode 100644 lib/libalpm/add.c create mode 100644 lib/libalpm/add.h create mode 100644 lib/libalpm/alpm.c create mode 100644 lib/libalpm/alpm.h create mode 100644 lib/libalpm/backup.c create mode 100644 lib/libalpm/backup.h create mode 100644 lib/libalpm/cache.c create mode 100644 lib/libalpm/cache.h create mode 100644 lib/libalpm/db.c create mode 100644 lib/libalpm/db.h create mode 100644 lib/libalpm/deps.c create mode 100644 lib/libalpm/deps.h create mode 100644 lib/libalpm/error.c create mode 100644 lib/libalpm/error.h create mode 100644 lib/libalpm/group.c create mode 100644 lib/libalpm/group.h create mode 100644 lib/libalpm/handle.c create mode 100644 lib/libalpm/handle.h create mode 100644 lib/libalpm/list.c create mode 100644 lib/libalpm/list.h create mode 100644 lib/libalpm/log.c create mode 100644 lib/libalpm/log.h create mode 100644 lib/libalpm/md5.c create mode 100644 lib/libalpm/md5.h create mode 100644 lib/libalpm/md5driver.c create mode 100644 lib/libalpm/package.c create mode 100644 lib/libalpm/package.h create mode 100644 lib/libalpm/provide.c create mode 100644 lib/libalpm/provide.h create mode 100644 lib/libalpm/remove.c create mode 100644 lib/libalpm/remove.h create mode 100644 lib/libalpm/rpmvercmp.c create mode 100644 lib/libalpm/rpmvercmp.h create mode 100644 lib/libalpm/sync.c create mode 100644 lib/libalpm/sync.h create mode 100644 lib/libalpm/trans.c create mode 100644 lib/libalpm/trans.h create mode 100644 lib/libalpm/util.c create mode 100644 lib/libalpm/util.h create mode 100644 lib/libftp/Makefile create mode 100644 lib/libftp/ftplib.c create mode 100644 lib/libftp/ftplib.h (limited to 'lib') diff --git a/lib/libalpm/Makefile b/lib/libalpm/Makefile new file mode 100644 index 00000000..9a517b3b --- /dev/null +++ b/lib/libalpm/Makefile @@ -0,0 +1,39 @@ + +CXX=gcc +CFLAGS=-g -Wall -pedantic -D_GNU_SOURCE -I. -I../.. +AR=ar rc +RAN=ranlib + +OBJS=md5driver.o \ + md5.o \ + util.o \ + list.o \ + log.o \ + error.o \ + package.o \ + group.o \ + db.o \ + cache.o \ + deps.o \ + provide.o \ + rpmvercmp.o \ + backup.o \ + trans.o \ + add.o \ + remove.o \ + sync.o \ + handle.o \ + alpm.o + +all: libalpm.a + +%.o: %.c %.h + $(CXX) -c $(CFLAGS) -o $@ $< + +libalpm.a: $(OBJS) alpm.h + $(AR) $@ $(OBJS) + $(RAN) $@ + +clean: + rm -f *.o *~ core + rm -f libalpm.a diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c new file mode 100644 index 00000000..2c883f10 --- /dev/null +++ b/lib/libalpm/add.c @@ -0,0 +1,580 @@ +/* + * add.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "util.h" +#include "error.h" +#include "list.h" +#include "cache.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "log.h" +#include "backup.h" +#include "package.h" +#include "db.h" +#include "provide.h" +#include "trans.h" +#include "deps.h" +#include "add.h" +#include "remove.h" +#include "handle.h" + +extern pmhandle_t *handle; + +int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name) +{ + pmpkg_t *info, *dummy; + PMList *j; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* ORE + load_pkg should be done only if pkg has to be added to the transaction */ + + _alpm_log(PM_LOG_FLOW2, "reading %s...", name); + info = pkg_load(name); + if(info == NULL) { + /* pm_errno is already set by pkg_load() */ + return(-1); + } + + /* no additional hyphens in version strings */ + if(strchr(info->version, '-') != strrchr(info->version, '-')) { + FREEPKG(info); + pm_errno = PM_ERR_INVALID_NAME; + return(-1); + } + + dummy = db_get_pkgfromcache(db, info->name); + /* only freshen this package if it is already installed and at a lesser version */ + if(trans->flags & PM_TRANS_FLAG_FRESHEN) { + if(dummy == NULL || rpmvercmp(dummy->version, info->version) >= 0) { + FREEPKG(info); + PM_RET_ERR(PM_ERR_PKG_CANT_FRESH, -1); + } + } + /* only install this package if it is not already installed */ + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + if(dummy) { + FREEPKG(info); + PM_RET_ERR(PM_ERR_PKG_INSTALLED, -1); + } + } + + /* check if an older version of said package is already in transaction packages. + * if so, replace it in the list */ + /* ORE + we'd better do it before load_pkg. */ + for(j = trans->packages; j; j = j->next) { + pmpkg_t *pkg = j->data; + + if(strcmp(pkg->name, info->name) != 0) { + if(rpmvercmp(pkg->version, info->version) < 0) { + _alpm_log(PM_LOG_WARNING, "replacing older version of %s in target list", pkg->name); + FREEPKG(j->data); + j->data = info; + } + } + } + + /* add the package to the transaction */ + trans->packages = pm_list_add(trans->packages, info); + + return(0); +} + +int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + PMList *lp; + + *data = NULL; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* ORE ??? + No need to check deps if pacman_add was called during a sync: + it is already done in pacman_sync */ + + /* Check dependencies + */ + + if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) { + PMList *j; + + TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL); + + lp = checkdeps(db, trans->type, trans->packages); + if(lp != NULL) { + int errorout = 0; + + /* look for unsatisfied dependencies */ + _alpm_log(PM_LOG_FLOW2, "looking for unsatisfied dependencies..."); + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = j->data; + + if(miss->type == PM_DEP_DEPEND || miss->type == PM_DEP_REQUIRED) { + if(!errorout) { + errorout = 1; + } + if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) { + FREELIST(lp); + /* ORE, needed or not ? + FREELIST(*data);*/ + PM_RET_ERR(PM_ERR_MEMORY, -1); + } + *miss = *(pmdepmissing_t*)j->data; + *data = pm_list_add(*data, miss); + } + } + if(errorout) { + FREELIST(lp); + PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1); + } + + /* no unsatisfied deps, so look for conflicts */ + _alpm_log(PM_LOG_FLOW2, "looking for conflicts..."); + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = (pmdepmissing_t *)j->data; + if(miss->type == PM_DEP_CONFLICT) { + if(!errorout) { + errorout = 1; + } + MALLOC(miss, sizeof(pmdepmissing_t)); + *miss = *(pmdepmissing_t*)j->data; + *data = pm_list_add(*data, miss); + } + } + if(errorout) { + FREELIST(lp); + PM_RET_ERR(PM_ERR_CONFLICTING_DEPS, -1); + } + FREELIST(lp); + } + + /* re-order w.r.t. dependencies */ + _alpm_log(PM_LOG_FLOW2, "sorting by dependencies..."); + lp = sortbydeps(trans->packages); + /* free the old alltargs */ + for(j = trans->packages; j; j = j->next) { + j->data = NULL; + } + FREELIST(trans->packages); + trans->packages = lp; + + TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL); + } + + /* Check for file conflicts + */ + if(!(trans->flags & PM_TRANS_FLAG_FORCE)) { + TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_START, NULL, NULL); + + lp = db_find_conflicts(db, trans->packages, handle->root); + if(lp != NULL) { + *data = lp; + PM_RET_ERR(PM_ERR_FILE_CONFLICTS, -1); + } + + TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_DONE, NULL, NULL); + } + + return(0); +} + +int add_commit(pmdb_t *db, pmtrans_t *trans) +{ + int i, ret = 0, errors = 0; + TAR *tar = NULL; + char expath[PATH_MAX]; + time_t t; + pmpkg_t *info = NULL; + PMList *targ, *lp; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + if(trans->packages == NULL) { + return(0); + } + + for(targ = trans->packages; targ; targ = targ->next) { + tartype_t gztype = { + (openfunc_t)_alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t)gzread, + (writefunc_t)gzwrite + }; + unsigned short pmo_upgrade = (trans->type == PM_TRANS_TYPE_UPGRADE) ? 1 : 0; + char pm_install[PATH_MAX]; + pmpkg_t *oldpkg = NULL; + info = (pmpkg_t *)targ->data; + errors = 0; + + /* see if this is an upgrade. if so, remove the old package first */ + if(pmo_upgrade) { + if(pkg_isin(info, db_get_pkgcache(db))) { + TRANS_CB(trans, PM_TRANS_CB_UPGRADE_START, info, NULL); + + /* we'll need the full record for backup checks later */ + if((oldpkg = db_scan(db, info->name, INFRQ_ALL)) != NULL) { + pmtrans_t *tr; + + _alpm_log(PM_LOG_FLOW2, "removing old package first...\n"); + /* ORE + set flags to something, but what (nodeps?) ??? */ + tr = trans_new(); + if(tr == NULL) { + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(trans_init(tr, PM_TRANS_TYPE_UPGRADE, 0, NULL) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(remove_loadtarget(db, tr, info->name) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(remove_commit(db, tr) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + FREETRANS(tr); + } + } else { + /* no previous package version is installed, so this is actually just an + * install + */ + pmo_upgrade = 0; + } + } + if(!pmo_upgrade) { + TRANS_CB(trans, PM_TRANS_CB_ADD_START, info, NULL); + } + + /* Add the package to the database */ + t = time(NULL); + + /* Update the requiredby field by scaning the whole database + * looking for packages depending on the package to add */ + for(lp = db_get_pkgcache(db); lp; lp = lp->next) { + pmpkg_t *tmpp = NULL; + PMList *tmppm = NULL; + + tmpp = db_scan(db, ((pmpkg_t *)lp->data)->name, INFRQ_DEPENDS); + if(tmpp == NULL) { + continue; + } + for(tmppm = tmpp->depends; tmppm; tmppm = tmppm->next) { + pmdepend_t depend; + splitdep(tmppm->data, &depend); + if(tmppm->data && !strcmp(depend.name, info->name)) { + info->requiredby = pm_list_add(info->requiredby, strdup(tmpp->name)); + continue; + } + } + } + + _alpm_log(PM_LOG_FLOW2, "updating database..."); + /* Figure out whether this package was installed explicitly by the user + * or installed as a dependency for another package + */ + /* ORE + info->reason = PM_PKG_REASON_EXPLICIT; + if(pm_list_is_strin(dependonly, info->data)) { + info->reason = PM_PKG_REASON_DEPEND; + }*/ + /* make an install date (in UTC) */ + strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate)); + if(db_write(db, info, INFRQ_ALL)) { + _alpm_log(PM_LOG_ERROR, "could not update database for %s", info->name); + alpm_logaction(NULL, "error updating database for %s!", info->name); + PM_RET_ERR(PM_ERR_DB_WRITE, -1); + } + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + pmpkg_t *depinfo = NULL; + pmdepend_t depend; + + splitdep(lp->data, &depend); + depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides) { + /* use the first one */ + depinfo = db_scan(db, ((pmpkg_t *)provides->data)->name, INFRQ_DEPENDS); + if(depinfo == NULL) { + PMList *lp; + /* wtf */ + for(lp = provides; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(provides); + continue; + } + } else { + continue; + } + } + depinfo->requiredby = pm_list_add(depinfo->requiredby, strdup(info->name)); + db_write(db, depinfo, INFRQ_DEPENDS); + FREEPKG(depinfo); + } + + /* Extract the .tar.gz package */ + if(tar_open(&tar, info->data, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + PM_RET_ERR(PM_ERR_PKG_OPEN, -1); + } + _alpm_log(PM_LOG_DEBUG, "extracting files..."); + for(i = 0; !th_read(tar); i++) { + int nb = 0; + int notouch = 0; + char *md5_orig = NULL; + char pathname[PATH_MAX]; + struct stat buf; + + strncpy(pathname, th_get_pathname(tar), PATH_MAX); + + if(!strcmp(pathname, ".PKGINFO") || !strcmp(pathname, ".FILELIST")) { + tar_skip_regfile(tar); + continue; + } + + if(!strcmp(pathname, "._install") || !strcmp(pathname, ".INSTALL")) { + /* the install script goes inside the db */ + snprintf(expath, PATH_MAX, "%s/%s-%s/install", db->path, info->name, info->version); + } else { + /* build the new pathname relative to handle->root */ + snprintf(expath, PATH_MAX, "%s%s", handle->root, pathname); + } + + if(!stat(expath, &buf) && !S_ISDIR(buf.st_mode)) { + /* file already exists */ + if(pm_list_is_strin(pathname, handle->noupgrade)) { + notouch = 1; + } else { + if(!pmo_upgrade || oldpkg == NULL) { + nb = pm_list_is_strin(pathname, info->backup) ? 1 : 0; + } else { + /* op == PM_UPGRADE */ + if((md5_orig = _alpm_needbackup(pathname, oldpkg->backup)) != 0) { + nb = 1; + } + } + } + } + + if(nb) { + char *temp; + char *md5_local, *md5_pkg; + + md5_local = MDFile(expath); + /* extract the package's version to a temporary file and md5 it */ + temp = strdup("/tmp/pacman_XXXXXX"); + mkstemp(temp); + if(tar_extract_file(tar, temp)) { + alpm_logaction("could not extract %s: %s", pathname, strerror(errno)); + errors++; + continue; + } + md5_pkg = MDFile(temp); + /* append the new md5 hash to it's respective entry in info->backup + * (it will be the new orginal) + */ + for(lp = info->backup; lp; lp = lp->next) { + char *fn; + + if(!lp->data) continue; + if(!strcmp((char*)lp->data, pathname)) { + /* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */ + MALLOC(fn, strlen(lp->data)+34); + sprintf(fn, "%s\t%s", (char*)lp->data, md5_pkg); + FREE(lp->data); + lp->data = fn; + } + } + + _alpm_log(PM_LOG_FLOW2, " checking md5 hashes for %s", expath); + _alpm_log(PM_LOG_FLOW2, " current: %s", md5_local); + _alpm_log(PM_LOG_FLOW2, " new: %s", md5_pkg); + if(md5_orig) { + _alpm_log(PM_LOG_FLOW2, " original: %s", md5_orig); + } + + if(!pmo_upgrade) { + /* PM_ADD */ + + /* if a file already exists with a different md5 hash, + * then we rename it to a .pacorig extension and continue */ + if(strcmp(md5_local, md5_pkg)) { + char newpath[PATH_MAX]; + snprintf(newpath, PATH_MAX, "%s.pacorig", expath); + if(rename(expath, newpath)) { + _alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno)); + alpm_logaction("error: could not rename %s: %s", expath, strerror(errno)); + } + if(_alpm_copyfile(temp, expath)) { + _alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno)); + alpm_logaction("error: could not copy %s to %s: %s", temp, expath, strerror(errno)); + errors++; + } else { + _alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath); + alpm_logaction("warning: %s saved as %s", expath, newpath); + } + } + } else if(md5_orig) { + /* PM_UPGRADE */ + int installnew = 0; + + /* the fun part */ + if(!strcmp(md5_orig, md5_local)) { + if(!strcmp(md5_local, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } else { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } + } else if(!strcmp(md5_orig, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: leaving existing file in place"); + } else if(!strcmp(md5_local, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } else { + char newpath[PATH_MAX]; + _alpm_log(PM_LOG_FLOW2, " action: saving current file and installing new one"); + installnew = 1; + snprintf(newpath, PATH_MAX, "%s.pacsave", expath); + if(rename(expath, newpath)) { + _alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno)); + alpm_logaction("error: could not rename %s: %s", expath, strerror(errno)); + } else { + _alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath); + alpm_logaction("warning: %s saved as %s", expath, newpath); + } + } + + if(installnew) { + /*_alpm_log(PM_LOG_FLOW2, " %s", expath);*/ + if(_alpm_copyfile(temp, expath)) { + _alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno)); + errors++; + } + } + } + + FREE(md5_local); + FREE(md5_pkg); + FREE(md5_orig); + unlink(temp); + FREE(temp); + } else { + if(!notouch) { + _alpm_log(PM_LOG_FLOW2, "%s", pathname); + } else { + _alpm_log(PM_LOG_FLOW2, "%s is in NoUpgrade - skipping", pathname); + strncat(expath, ".pacnew", PATH_MAX); + _alpm_log(PM_LOG_WARNING, "warning: extracting %s%s as %s", handle->root, pathname, expath); + alpm_logaction("warning: extracting %s%s as %s", handle->root, pathname, expath); + tar_skip_regfile(tar); + } + if(trans->flags & PM_TRANS_FLAG_FORCE) { + /* if FORCE was used, then unlink() each file (whether it's there + * or not) before extracting. this prevents the old "Text file busy" + * error that crops up if one tries to --force a glibc or pacman + * upgrade. + */ + unlink(expath); + } + if(tar_extract_file(tar, expath)) { + _alpm_log(PM_LOG_ERROR, "could not extract %s: %s", pathname, strerror(errno)); + alpm_logaction("could not extract %s: %s", pathname, strerror(errno)); + errors++; + } + /* calculate an md5 hash if this is in info->backup */ + for(lp = info->backup; lp; lp = lp->next) { + char *fn, *md5; + char path[PATH_MAX]; + + if(!lp->data) continue; + if(!strcmp((char*)lp->data, pathname)) { + snprintf(path, PATH_MAX, "%s%s", handle->root, (char*)lp->data); + md5 = MDFile(path); + /* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */ + MALLOC(fn, strlen(lp->data)+34); + sprintf(fn, "%s\t%s", (char*)lp->data, md5); + FREE(lp->data); + lp->data = fn; + } + } + } + } + tar_close(tar); + + if(errors) { + ret = 1; + _alpm_log(PM_LOG_ERROR, "errors occurred while %s %s", + (pmo_upgrade ? "upgrading" : "installing"), info->name); + alpm_logaction("errors occurred while %s %s", + (pmo_upgrade ? "upgrading" : "installing"), info->name); + } + + /* run the post-install script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + if(pmo_upgrade) { + _alpm_runscriptlet(handle->root, pm_install, "post_upgrade", info->version, oldpkg ? oldpkg->version : NULL); + } else { + _alpm_runscriptlet(handle->root, pm_install, "post_install", info->version, NULL); + } + + if(pmo_upgrade && oldpkg) { + TRANS_CB(trans, PM_TRANS_CB_UPGRADE_DONE, info, NULL); + alpm_logaction("upgraded %s (%s -> %s)", info->name, + oldpkg->version, info->version); + } else { + TRANS_CB(trans, PM_TRANS_CB_ADD_DONE, info, NULL); + alpm_logaction("installed %s (%s)", info->name, info->version); + } + + FREEPKG(oldpkg); + } + + /* run ldconfig if it exists */ + _alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root); + _alpm_ldconfig(handle->root); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/add.h b/lib/libalpm/add.h new file mode 100644 index 00000000..cc578537 --- /dev/null +++ b/lib/libalpm/add.h @@ -0,0 +1,34 @@ +/* + * add.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_ADD_H +#define _ALPM_ADD_H + +#include "list.h" +#include "db.h" +#include "trans.h" + +int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name); +int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int add_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_ADD_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c new file mode 100644 index 00000000..23528ae4 --- /dev/null +++ b/lib/libalpm/alpm.c @@ -0,0 +1,577 @@ +/* + * alpm.c + * + * Copyright (c) 2002 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* PATH_MAX */ +#include +/* pacman */ +#include "log.h" +#include "error.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "list.h" +#include "package.h" +#include "group.h" +#include "util.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "backup.h" +#include "add.h" +#include "remove.h" +#include "sync.h" +#include "handle.h" +#include "alpm.h" + +pmhandle_t *handle = NULL; + +enum __pmerrno_t pm_errno; + +/* + * Library + */ + +int alpm_initialize(char *root) +{ + char str[PATH_MAX]; + + ASSERT(handle == NULL, PM_RET_ERR(PM_ERR_HANDLE_NOT_NULL, -1)); + + handle = handle_new(); + + /* lock db */ + if(handle->access == PM_ACCESS_RW) { + if(_alpm_lckmk(PACLOCK) == -1) { + FREE(handle); + PM_RET_ERR(PM_ERR_HANDLE_LOCK, -1); + } + } + + strncpy(str, (root) ? root : PACROOT, PATH_MAX); + /* add a trailing '/' if there isn't one */ + if(str[strlen(str)-1] != '/') { + strcat(str, "/"); + } + handle->root = strdup(str); + + return(0); +} + +int alpm_release() +{ + PMList *i; + + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + /* unlock db */ + if(handle->access == PM_ACCESS_RW) { + if(_alpm_lckrm(PACLOCK)) { + _alpm_log(PM_LOG_WARNING, "could not remove lock file %s", PACLOCK); + alpm_logaction("warning: could not remove lock file %s", PACLOCK); + } + } + + /* close local database */ + if(handle->db_local) { + db_close(handle->db_local); + handle->db_local = NULL; + } + /* and also sync ones */ + for(i = handle->dbs_sync; i; i = i->next) { + if(i->data) { + db_close(i->data); + i->data = NULL; + } + } + + FREEHANDLE(handle); + + return(0); +} + +/* + * Options + */ + +int alpm_set_option(unsigned char parm, unsigned long data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + return(handle_set_option(handle, parm, data)); +} + +int alpm_get_option(unsigned char parm, long *data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + return(handle_get_option(handle, parm, data)); +} + +/* + * Databases + */ + +int alpm_db_register(char *treename, PM_DB **db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(treename != NULL && strlen(treename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* check if the db if already registered */ + *db = db_open(handle->root, handle->dbpath, treename); + if(*db == NULL) { + /* couldn't open the db directory - try creating it */ + if(db_create(handle->root, handle->dbpath, treename) == -1) { + PM_RET_ERR(PM_ERR_DB_CREATE, -1); + } + *db = db_open(handle->root, handle->dbpath, treename); + if(*db == NULL) { + /* couldn't open the db directory, try creating it */ + PM_RET_ERR(PM_ERR_DB_OPEN, -1); + } + } + + if(strcmp(treename, "local") == 0) { + handle->db_local = *db; + } else { + handle->dbs_sync = pm_list_add(handle->dbs_sync, *db); + } + + return(0); +} + +int alpm_db_unregister(PM_DB *db) +{ + PMList *i; + int found = 0; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(db == handle->db_local) { + db_close(handle->db_local); + handle->db_local = NULL; + found = 1; + } else { + for(i = handle->dbs_sync; i && !found; i = i->next) { + if(db == i->data) { + db_close(i->data); + i->data = NULL; + /* ORE + it should be _alpm_list_removed instead */ + found = 1; + } + } + } + + if(!found) { + PM_RET_ERR(PM_ERR_DB_NOT_FOUND, -1); + } + + return(0); +} + +PM_PKG *alpm_db_readpkg(PM_DB *db, char *name) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + ASSERT(name != NULL && strlen(name) != 0, return(NULL)); + + return(db_get_pkgfromcache(db, name)); +} + +PM_LIST *alpm_db_getpkgcache(PM_DB *db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + + return(db_get_pkgcache(db)); +} + +PM_GRP *alpm_db_readgrp(PM_DB *db, char *name) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + ASSERT(name != NULL && strlen(name) != 0, return(NULL)); + + return(db_get_grpfromcache(db, name)); +} + +PM_LIST *alpm_db_getgrpcache(PM_DB *db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + + return(db_get_grpcache(db)); +} + +PM_LIST *alpm_db_nextgrp(PM_LIST *cache) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(cache != NULL, return(NULL)); + + return(cache->next); +} + +PM_GRP *alpm_db_getgrp(PM_LIST *cache) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(cache != NULL, return(NULL)); + + return(cache->data); +} + +/* + * Packages + */ + +void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(pkg != NULL, return(NULL)); + + /* Update the cache package entry if needed */ + if(pkg->origin == PKG_FROM_CACHE && pkg->data == handle->db_local) { + switch(parm) { + case PM_PKG_FILES: + case PM_PKG_BACKUP: + if(!(pkg->infolevel & INFRQ_FILES)) { + char target[321]; /* 256+1+64 */ + + snprintf(target, 321, "%s-%s", pkg->name, pkg->version); + db_read(pkg->data, target, INFRQ_FILES, pkg); + } + break; + + case PM_PKG_SCRIPLET: + if(!(pkg->infolevel & INFRQ_SCRIPLET)) { + char target[321]; + + snprintf(target, 321, "%s-%s", pkg->name, pkg->version); + db_read(pkg->data, target, INFRQ_SCRIPLET, pkg); + } + break; + } + } + + switch(parm) { + case PM_PKG_NAME: data = pkg->name; break; + case PM_PKG_VERSION: data = pkg->version; break; + case PM_PKG_DESC: data = pkg->desc; break; + case PM_PKG_GROUPS: data = pkg->groups; break; + case PM_PKG_URL: data = pkg->url; break; + case PM_PKG_LICENSE: data = pkg->license; break; + case PM_PKG_ARCH: data = pkg->arch; break; + case PM_PKG_BUILDDATE: data = pkg->builddate; break; + case PM_PKG_INSTALLDATE: data = pkg->installdate; break; + case PM_PKG_PACKAGER: data = pkg->packager; break; + case PM_PKG_SIZE: data = (void *)pkg->size; break; + case PM_PKG_REASON: data = (void *)(int)pkg->reason; break; + case PM_PKG_REPLACES: data = pkg->replaces; break; + case PM_PKG_MD5SUM: data = pkg->md5sum; break; + case PM_PKG_DEPENDS: data = pkg->depends; break; + case PM_PKG_REQUIREDBY: data = pkg->requiredby; break; + case PM_PKG_PROVIDES: data = pkg->provides; break; + case PM_PKG_CONFLICTS: data = pkg->conflicts; break; + case PM_PKG_FILES: data = pkg->files; break; + case PM_PKG_BACKUP: data = pkg->backup; break; + case PM_PKG_SCRIPLET: data = (void *)(int)pkg->scriptlet; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_pkg_load(char *filename, PM_PKG **pkg) +{ + /* Sanity checks */ + ASSERT(filename != NULL && strlen(filename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + *pkg = pkg_load(filename); + if(*pkg == NULL) { + /* pm_errno is set by pkg_load */ + return(-1); + } + + return(0); +} + +int alpm_pkg_free(PM_PKG *pkg) +{ + /* Sanity checks */ + ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + pkg_free(pkg); + + return(0); +} + +int alpm_pkg_vercmp(const char *ver1, const char *ver2) +{ + return(rpmvercmp(ver1, ver2)); +} + +/* + * Groups + */ + +void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(grp != NULL, return(NULL)); + + switch(parm) { + case PM_GRP_NAME: data = grp->name; break; + case PM_GRP_PKGNAMES: data = grp->packages; break; + default: + data = NULL; + break; + } + + return(data); +} + +/* + * Sync operations + */ + +void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(sync != NULL, return(NULL)); + + switch(parm) { + case PM_SYNC_TYPE: data = (void *)(int)sync->type; break; + case PM_SYNC_LOCALPKG: data = sync->lpkg; break; + case PM_SYNC_SYNCPKG: data = sync->spkg; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_sync_sysupgrade(PM_LIST **data) +{ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + return(sync_sysupgrade(data)); +} + +/* + * Transactions + */ + +void *alpm_trans_getinfo(unsigned char parm) +{ + pmtrans_t *trans; + void *data; + + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + + trans = handle->trans; + + switch(parm) { + case PM_TRANS_TYPE: data = (void *)(int)trans->type; break; + case PM_TRANS_FLAGS: data = (void *)(int)trans->flags; break; + case PM_TRANS_TARGETS: data = trans->targets; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + ASSERT(handle->trans == NULL, PM_RET_ERR(PM_ERR_TRANS_NOT_NULL, -1)); + + handle->trans = trans_new(); + if(handle->trans == NULL) { + PM_RET_ERR(PM_ERR_MEMORY, -1); + } + + return(trans_init(handle->trans, type, flags, cb)); +} + +int alpm_trans_addtarget(char *target) +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(target != NULL && strlen(target) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); + + return(trans_addtarget(trans, target)); +} + +int alpm_trans_prepare(PMList **data) +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); + + return(trans_prepare(handle->trans, data)); +} + +int alpm_trans_commit() +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_PREPARED, PM_RET_ERR(PM_ERR_TRANS_NOT_PREPARED, -1)); + + /* ORE + ASSERT(handle->access != PM_ACCESS_RW, PM_RET_ERR(PM_ERR_BAD_PERMS, -1));*/ + + return(trans_commit(handle->trans)); +} + +int alpm_trans_release() +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state != STATE_IDLE, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + FREETRANS(handle->trans); + + return(0); +} + +/* + * Log facilities + */ + +int alpm_logaction(char *fmt, ...) +{ + char str[256]; + va_list args; + + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + return(_alpm_log_action(handle->usesyslog, handle->logfd, str)); +} + +/* + * Lists wrappers + */ + +PM_LIST *alpm_list_first(PM_LIST *list) +{ + ASSERT(list != NULL, return(NULL)); + + return(list); +} + +PM_LIST *alpm_list_next(PM_LIST *entry) +{ + ASSERT(entry != NULL, return(NULL)); + + return(entry->next); +} + +void *alpm_list_getdata(PM_LIST *entry) +{ + ASSERT(entry != NULL, return(NULL)); + + return(entry->data); +} + +int alpm_list_free(PM_LIST *entry) +{ + if(entry) { + /* ORE + does not free all memory for packages... */ + pm_list_free(entry); + } + + return(0); +} + +/* + * Misc wrappers + */ + +char *alpm_get_md5sum(char *name) +{ + ASSERT(name != NULL, return(NULL)); + + return(MDFile(name)); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h new file mode 100644 index 00000000..85442196 --- /dev/null +++ b/lib/libalpm/alpm.h @@ -0,0 +1,330 @@ +/* + * alpm.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_H +#define _ALPM_H + +/* + * Arch Linux Package Management library + */ + +/* + * Structures (opaque) + */ + +typedef struct __pmlist_t PM_LIST; +typedef struct __pmdb_t PM_DB; +typedef struct __pmpkg_t PM_PKG; +typedef struct __pmgrp_t PM_GRP; +typedef struct __pmsync_t PM_SYNC; +typedef struct __pmtrans_t PM_TRANS; +/* ORE +typedef struct __pmdepend_t PM_DEP; +typedef struct __pmdepmissing_t PM_DEPMISS; */ + +/* + * Library + */ + +/* Version */ +#define ALPM_VERSION "0.1.0" + +int alpm_initialize(char *root); +int alpm_release(); + +/* + * Logging facilities + */ + +/* Levels */ +#define PM_LOG_DEBUG 0x01 +#define PM_LOG_ERROR 0x02 +#define PM_LOG_WARNING 0x04 +#define PM_LOG_FLOW1 0x08 +#define PM_LOG_FLOW2 0x10 +#define PM_LOG_FUNCTION 0x20 + +int alpm_logaction(char *fmt, ...); + +/* + * Options + */ + +/* Parameters */ +enum { + PM_OPT_LOGCB = 1, + PM_OPT_LOGMASK, + PM_OPT_USESYSLOG, + PM_OPT_ROOT, + PM_OPT_DBPATH, + PM_OPT_LOGFILE, + PM_OPT_LOCALDB, + PM_OPT_SYNCDB, + PM_OPT_NOUPGRADE, + PM_OPT_IGNOREPKG, + PM_OPT_HOLDPKG +}; + +int alpm_set_option(unsigned char parm, unsigned long data); +int alpm_get_option(unsigned char parm, long *data); + +/* + * Databases + */ + +int alpm_db_register(char *treename, PM_DB **db); +int alpm_db_unregister(PM_DB *db); + +PM_PKG *alpm_db_readpkg(PM_DB *db, char *name); +PM_LIST *alpm_db_getpkgcache(PM_DB *db); + +PM_GRP *alpm_db_readgrp(PM_DB *db, char *name); +PM_LIST *alpm_db_getgrpcache(PM_DB *db); + +/* + * Packages + */ + +/* Info parameters */ +enum { + /* Desc entry */ + PM_PKG_NAME = 1, + PM_PKG_VERSION, + PM_PKG_DESC, + PM_PKG_GROUPS, + PM_PKG_URL, + PM_PKG_LICENSE, + PM_PKG_ARCH, + PM_PKG_BUILDDATE, + PM_PKG_INSTALLDATE, + PM_PKG_PACKAGER, + PM_PKG_SIZE, + PM_PKG_REASON, + PM_PKG_REPLACES, /* Sync DB only */ + PM_PKG_MD5SUM, /* Sync DB only */ + /* Depends entry */ + PM_PKG_DEPENDS, + PM_PKG_REQUIREDBY, + PM_PKG_CONFLICTS, + PM_PKG_PROVIDES, + /* Files entry */ + PM_PKG_FILES, + PM_PKG_BACKUP, + /* Sciplet */ + PM_PKG_SCRIPLET +}; + +/* reasons -- ie, why the package was installed */ +#define PM_PKG_REASON_EXPLICIT 0 /* explicitly requested by the user */ +#define PM_PKG_REASON_DEPEND 1 /* installed as a dependency for another package */ + +void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm); +int alpm_pkg_load(char *filename, PM_PKG **pkg); +int alpm_pkg_free(PM_PKG *pkg); +int alpm_pkg_vercmp(const char *ver1, const char *ver2); + +/* + * Groups + */ + +/* Info parameters */ +enum { + PM_GRP_NAME = 1, + PM_GRP_PKGNAMES +}; + +void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm); + +/* + * Sync + */ + +/* Types */ +enum { + PM_SYSUPG_REPLACE = 1, + PM_SYSUPG_UPGRADE, + PM_SYSUPG_DEPEND +}; +/* Info parameters */ +enum { + PM_SYNC_TYPE = 1, + PM_SYNC_LOCALPKG, + PM_SYNC_SYNCPKG +}; + +void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm); +int alpm_sync_sysupgrade(PM_LIST **data); + +/* + * Transactions + */ + +/* Types */ +enum { + PM_TRANS_TYPE_ADD = 1, + PM_TRANS_TYPE_REMOVE, + PM_TRANS_TYPE_UPGRADE, + PM_TRANS_TYPE_SYNC +}; + +/* Flags */ +#define PM_TRANS_FLAG_NODEPS 0x01 +#define PM_TRANS_FLAG_FORCE 0x02 +#define PM_TRANS_FLAG_NOSAVE 0x04 +#define PM_TRANS_FLAG_FRESHEN 0x08 +#define PM_TRANS_FLAG_CASCADE 0x10 +#define PM_TRANS_FLAG_RECURSE 0x20 +#define PM_TRANS_FLAG_DBONLY 0x40 + +/* Callback events */ +enum { + PM_TRANS_CB_DEPS_START = 1, + PM_TRANS_CB_DEPS_DONE, + PM_TRANS_CB_CONFLICTS_START, + PM_TRANS_CB_CONFLICTS_DONE, + PM_TRANS_CB_ADD_START, + PM_TRANS_CB_ADD_DONE, + PM_TRANS_CB_REMOVE_START, + PM_TRANS_CB_REMOVE_DONE, + PM_TRANS_CB_UPGRADE_START, + PM_TRANS_CB_UPGRADE_DONE +}; + +/* Callback */ +typedef void (*alpm_trans_cb)(unsigned short, void *, void *); + +/* Info parameters */ +enum { + PM_TRANS_TYPE = 1, + PM_TRANS_FLAGS, + PM_TRANS_TARGETS +}; + +/* Dependencies */ +enum { + PM_DEP_ANY = 1, + PM_DEP_EQ, + PM_DEP_GE, + PM_DEP_LE +}; +enum { + PM_DEP_DEPEND = 1, + PM_DEP_REQUIRED, + PM_DEP_CONFLICT +}; + +/* ORE +to be deprecated in favor of PM_DEP and PM_DEPMISS (opaque) */ +typedef struct __pmdepend_t { + unsigned short mod; + char name[256]; + char version[64]; +} pmdepend_t; + +typedef struct __pmdepmissing_t { + unsigned char type; + char target[256]; + pmdepend_t depend; +} pmdepmissing_t; + +void *alpm_trans_getinfo(unsigned char parm); +int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb); +int alpm_trans_addtarget(char *target); +int alpm_trans_prepare(PM_LIST **data); +int alpm_trans_commit(); +int alpm_trans_release(); + +/* + * PM_LIST helpers + */ +PM_LIST *alpm_list_first(PM_LIST *list); +PM_LIST *alpm_list_next(PM_LIST *entry); +void *alpm_list_getdata(PM_LIST *entry); +int alpm_list_free(PM_LIST *entry); + +/* + * Helpers + */ + +char *alpm_get_md5sum(char *name); + +/* + * Errors + */ + +extern enum __pmerrno_t { + PM_ERR_NOERROR = 1, + PM_ERR_MEMORY, + PM_ERR_SYSTEM, + PM_ERR_BADPERMS, + PM_ERR_NOT_A_FILE, + PM_ERR_WRONG_ARGS, + /* Interface */ + PM_ERR_HANDLE_NULL, + PM_ERR_HANDLE_NOT_NULL, + PM_ERR_HANDLE_LOCK, + /* Databases */ + PM_ERR_DB_OPEN, + PM_ERR_DB_CREATE, + PM_ERR_DB_NULL, + PM_ERR_DB_NOT_FOUND, + PM_ERR_DB_NOT_NULL, + PM_ERR_DB_WRITE, + /* Cache */ + PM_ERR_CACHE_NULL, + /* Configuration */ + PM_ERR_OPT_LOGFILE, + PM_ERR_OPT_DBPATH, + PM_ERR_OPT_LOCALDB, + PM_ERR_OPT_SYNCDB, + PM_ERR_OPT_USESYSLOG, + /* Transactions */ + PM_ERR_TRANS_NOT_NULL, + PM_ERR_TRANS_NULL, + PM_ERR_TRANS_DUP_TARGET, + PM_ERR_TRANS_INITIALIZED, + PM_ERR_TRANS_NOT_INITIALIZED, + PM_ERR_TRANS_NOT_PREPARED, + PM_ERR_TRANS_ABORT, + /* Packages */ + PM_ERR_PKG_NOT_FOUND, + PM_ERR_PKG_INVALID, + PM_ERR_PKG_OPEN, + PM_ERR_PKG_LOAD, + PM_ERR_PKG_INSTALLED, + PM_ERR_PKG_CANT_FRESH, + PM_ERR_INVALID_NAME, + /* Groups */ + PM_ERR_GRP_NOT_FOUND, + /* Dependencies */ + PM_ERR_UNSATISFIED_DEPS, + PM_ERR_CONFLICTING_DEPS, + PM_ERR_UNRESOLVABLE_DEPS, + PM_ERR_FILE_CONFLICTS, + /* Misc */ + PM_ERR_USER_ABORT, + PM_ERR_INTERNAL_ERROR +} pm_errno; + +char *alpm_strerror(int err); + +#endif /* _ALPM_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/backup.c b/lib/libalpm/backup.c new file mode 100644 index 00000000..f664277e --- /dev/null +++ b/lib/libalpm/backup.c @@ -0,0 +1,63 @@ +/* + * backup.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +/* pacman */ +#include "backup.h" + +/* Look for a filename in a pkginfo_t.backup list. If we find it, + * then we return the md5 hash (parsed from the same line) + */ +char *_alpm_needbackup(char* file, PMList *backup) +{ + PMList *lp; + + if(file == NULL || backup == NULL) { + return(NULL); + } + + /* run through the backup list and parse out the md5 hash for our file */ + for(lp = backup; lp; lp = lp->next) { + char *str = strdup(lp->data); + char *ptr; + + /* tab delimiter */ + ptr = strchr(str, '\t'); + if(ptr == NULL) { + free(str); + continue; + } + *ptr = '\0'; + ptr++; + /* now str points to the filename and ptr points to the md5 hash */ + if(!strcmp(file, str)) { + free(str); + return(strdup(ptr)); + } + free(str); + } + + return(NULL); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/backup.h b/lib/libalpm/backup.h new file mode 100644 index 00000000..af22e1e5 --- /dev/null +++ b/lib/libalpm/backup.h @@ -0,0 +1,30 @@ +/* + * backup.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_BACKUP_H +#define _ALPM_BACKUP_H + +#include "list.h" + +char *_alpm_needbackup(char* file, PMList *backup); + +#endif /* _ALPM_BACKUP_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.c b/lib/libalpm/cache.c new file mode 100644 index 00000000..c6b298eb --- /dev/null +++ b/lib/libalpm/cache.c @@ -0,0 +1,203 @@ +/* + * cache.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "list.h" +#include "package.h" +#include "group.h" +#include "db.h" +#include "cache.h" + +/* Returns a new package cache from db. + * It frees the cache if it already exists. + */ +int db_load_pkgcache(pmdb_t *db) +{ + pmpkg_t *info; + + if(db == NULL) { + return(-1); + } + + db_free_pkgcache(db); + + db_rewind(db); + while((info = db_scan(db, NULL, INFRQ_DESC|INFRQ_DEPENDS)) != NULL) { + info->origin = PKG_FROM_CACHE; + info->data = db; + /* add to the collective */ + db->pkgcache = pm_list_add_sorted(db->pkgcache, info, pkg_cmp); + } + + return(0); +} + +void db_free_pkgcache(pmdb_t *db) +{ + if(db == NULL || db->pkgcache == NULL) { + return; + } + + FREELISTPKGS(db->pkgcache); + + if(db->grpcache) { + db_free_grpcache(db); + } +} + +PMList *db_get_pkgcache(pmdb_t *db) +{ + if(db == NULL) { + return(NULL); + } + + if(db->pkgcache == NULL) { + db_load_pkgcache(db); + } + + return(db->pkgcache); +} + +pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target) +{ + PMList *i; + + if(db == NULL || target == NULL || strlen(target) == 0) { + return(NULL); + } + + for(i = db_get_pkgcache(db); i; i = i->next) { + pmpkg_t *info = i->data; + + if(strcmp(info->name, target) == 0) { + return(info); + } + } + + return(NULL); +} + +/* Returns a new group cache from db. + * It frees the cache if it already exists. + */ +int db_load_grpcache(pmdb_t *db) +{ + PMList *lp; + + if(db == NULL) { + return(-1); + } + + if(db->pkgcache == NULL) { + db_load_pkgcache(db); + } + + for(lp = db->pkgcache; lp; lp = lp->next) { + PMList *i; + pmpkg_t *pkg = lp->data; + + for(i = pkg->groups; i; i = i->next) { + if(!pm_list_is_strin(i->data, db->grpcache)) { + pmgrp_t *grp = grp_new(); + + strncpy(grp->name, (char *)i->data, 256); + grp->packages = pm_list_add_sorted(grp->packages, pkg->name, grp_cmp); + db->grpcache = pm_list_add_sorted(db->grpcache, grp, grp_cmp); + } else { + PMList *j; + + for(j = db->grpcache; j; j = j->next) { + pmgrp_t *grp = j->data; + + if(strcmp(grp->name, i->data) == 0) { + if(!pm_list_is_strin(pkg->name, grp->packages)) { + grp->packages = pm_list_add_sorted(grp->packages, (char *)pkg->name, grp_cmp); + } + } + } + } + } + } + + return(0); +} + +void db_free_grpcache(pmdb_t *db) +{ + PMList *lg; + + if(db == NULL || db->grpcache == NULL) { + return; + } + + for(lg = db->grpcache; lg; lg = lg->next) { + PMList *lp; + pmgrp_t *grp = lg->data; + + for(lp = grp->packages; lp; lp = lp->next) { + lp->data = NULL; + } + FREELIST(grp->packages); + FREEGRP(lg->data); + } + FREELIST(db->grpcache); +} + +PMList *db_get_grpcache(pmdb_t *db) +{ + if(db == NULL) { + return(NULL); + } + + if(db->grpcache == NULL) { + db_load_grpcache(db); + } + + return(db->grpcache); +} + +pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target) +{ + PMList *i; + + if(db == NULL || target == NULL || strlen(target) == 0) { + return(NULL); + } + + for(i = db_get_grpcache(db); i; i = i->next) { + pmgrp_t *info = i->data; + + if(strcmp(info->name, target) == 0) { + return(info); + } + } + + return(NULL); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.h b/lib/libalpm/cache.h new file mode 100644 index 00000000..b8897322 --- /dev/null +++ b/lib/libalpm/cache.h @@ -0,0 +1,42 @@ +/* + * cache.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_CACHE_H +#define _ALPM_CACHE_H + +#include "list.h" +#include "package.h" +#include "group.h" +#include "db.h" + +/* packages */ +int db_load_pkgcache(pmdb_t *db); +void db_free_pkgcache(pmdb_t *db); +PMList *db_get_pkgcache(pmdb_t *db); +pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target); +/* groups */ +int db_load_grpcache(pmdb_t *db); +void db_free_grpcache(pmdb_t *db); +PMList *db_get_grpcache(pmdb_t *db); +pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target); + +#endif /* _ALPM_CACHE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c new file mode 100644 index 00000000..3fad4bb6 --- /dev/null +++ b/lib/libalpm/db.c @@ -0,0 +1,651 @@ +/* + * db.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "util.h" +#include "group.h" +#include "cache.h" +#include "db.h" + +/* Open a database and return a pmdb_t handle */ +pmdb_t *db_open(char *root, char *dbpath, char *treename) +{ + pmdb_t *db; + + if(root == NULL || dbpath == NULL || treename == NULL) { + return(NULL); + } + + MALLOC(db, sizeof(pmdb_t)); + + MALLOC(db->path, strlen(root)+strlen(dbpath)+strlen(treename)+2); + sprintf(db->path, "%s%s/%s", root, dbpath, treename); + + db->dir = opendir(db->path); + if(db->dir == NULL) { + FREE(db->path); + FREE(db); + return(NULL); + } + + strncpy(db->treename, treename, sizeof(db->treename)-1); + + db->pkgcache = NULL; + db->grpcache = NULL; + + return(db); +} + +void db_close(pmdb_t *db) +{ + if(db == NULL) { + return; + } + + if(db->dir) { + closedir(db->dir); + db->dir = NULL; + } + FREE(db->path); + + db_free_pkgcache(db); + db_free_grpcache(db); + + free(db); + + return; +} + +int db_create(char *root, char *dbpath, char *treename) +{ + char path[PATH_MAX]; + + if(root == NULL || dbpath == NULL || treename == NULL) { + return(-1); + } + + snprintf(path, PATH_MAX, "%s%s/local", root, dbpath); + if(_alpm_makepath(path) != 0) { + return(-1); + } + + return(0); +} + +void db_rewind(pmdb_t *db) +{ + if(db == NULL || db->dir == NULL) { + return; + } + + rewinddir(db->dir); +} + +pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq) +{ + struct dirent *ent = NULL; + char name[256]; + char *ptr = NULL; + int ret, found = 0; + pmpkg_t *pkg; + + if(db == NULL) { + return(NULL); + } + + if(target != NULL) { + /* search for a specific package (by name only) */ + rewinddir(db->dir); + while(!found && (ent = readdir(db->dir)) != NULL) { + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + strncpy(name, ent->d_name, 255); + /* truncate the string at the second-to-last hyphen, */ + /* which will give us the package name */ + if((ptr = rindex(name, '-'))) { + *ptr = '\0'; + } + if((ptr = rindex(name, '-'))) { + *ptr = '\0'; + } + if(!strcmp(name, target)) { + found = 1; + } + } + if(!found) { + return(NULL); + } + } else { + /* normal iteration */ + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + if(!strcmp(ent->d_name, ".")) { + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + } + if(!strcmp(ent->d_name, "..")) { + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + } + } + + pkg = pkg_new(); + if(pkg == NULL) { + return(NULL); + } + ret = db_read(db, ent->d_name, inforeq, pkg); + if(ret == -1) { + FREEPKG(pkg); + } + + return(ret == 0 ? pkg : NULL); +} + +int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info) +{ + FILE *fp = NULL; + struct stat buf; + char path[PATH_MAX]; + char line[512]; + + if(db == NULL || name == NULL || info == NULL) { + return(-1); + } + + snprintf(path, PATH_MAX, "%s/%s", db->path, name); + if(stat(path, &buf)) { + /* directory doesn't exist or can't be opened */ + return(-1); + } + + /* DESC */ + if(inforeq & INFRQ_DESC) { + snprintf(path, PATH_MAX, "%s/%s/desc", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(!feof(fp)) { + if(fgets(line, 256, fp) == NULL) { + break; + } + _alpm_strtrim(line); + if(!strcmp(line, "%NAME%")) { + if(fgets(info->name, sizeof(info->name), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->name); + } else if(!strcmp(line, "%VERSION%")) { + if(fgets(info->version, sizeof(info->version), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->version); + } else if(!strcmp(line, "%DESC%")) { + if(fgets(info->desc, sizeof(info->desc), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->desc); + } else if(!strcmp(line, "%GROUPS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + char *s = strdup(line); + info->groups = pm_list_add(info->groups, s); + } + } else if(!strcmp(line, "%URL%")) { + if(fgets(info->url, sizeof(info->url), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->url); + } else if(!strcmp(line, "%LICENSE%")) { + if(fgets(info->license, sizeof(info->license), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->license); + } else if(!strcmp(line, "%ARCH%")) { + if(fgets(info->arch, sizeof(info->arch), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->arch); + } else if(!strcmp(line, "%BUILDDATE%")) { + if(fgets(info->builddate, sizeof(info->builddate), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->builddate); + } else if(!strcmp(line, "%INSTALLDATE%")) { + if(fgets(info->installdate, sizeof(info->installdate), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->installdate); + } else if(!strcmp(line, "%PACKAGER%")) { + if(fgets(info->packager, sizeof(info->packager), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->packager); + } else if(!strcmp(line, "%REASON%")) { + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->reason = atol(tmp); + } else if(!strcmp(line, "%SIZE%")) { + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->size = atol(tmp); + } else if(!strcmp(line, "%CSIZE%")) { + /* NOTE: the CSIZE and SIZE fields both share the "size" field + * in the pkginfo_t struct. This can be done b/c CSIZE + * is currently only used in sync databases, and SIZE is + * only used in local databases. + */ + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->size = atol(tmp); + } else if(!strcmp(line, "%REPLACES%")) { + /* the REPLACES tag is special -- it only appears in sync repositories, + * not the local one. */ + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->replaces = pm_list_add(info->replaces, strdup(line)); + } + } else if(!strcmp(line, "%MD5SUM%")) { + /* MD5SUM tag only appears in sync repositories, + * not the local one. */ + if(fgets(info->md5sum, sizeof(info->md5sum), fp) == NULL) { + return(-1); + } + } else if(!strcmp(line, "%FORCE%")) { + /* FORCE tag only appears in sync repositories, + * not the local one. */ + info->force = 1; + } + } + fclose(fp); + } + + /* FILES */ + if(inforeq & INFRQ_FILES) { + snprintf(path, PATH_MAX, "%s/%s/files", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(fgets(line, 256, fp)) { + _alpm_strtrim(line); + if(!strcmp(line, "%FILES%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->files = pm_list_add(info->files, strdup(line)); + } + } else if(!strcmp(line, "%BACKUP%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->backup = pm_list_add(info->backup, strdup(line)); + } + } + } + fclose(fp); + } + + /* DEPENDS */ + if(inforeq & INFRQ_DEPENDS) { + snprintf(path, PATH_MAX, "%s/%s/depends", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(!feof(fp)) { + fgets(line, 255, fp); + _alpm_strtrim(line); + if(!strcmp(line, "%DEPENDS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->depends = pm_list_add(info->depends, strdup(line)); + } + } else if(!strcmp(line, "%REQUIREDBY%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->requiredby = pm_list_add(info->requiredby, strdup(line)); + } + } else if(!strcmp(line, "%CONFLICTS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->conflicts = pm_list_add(info->conflicts, strdup(line)); + } + } else if(!strcmp(line, "%PROVIDES%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->provides = pm_list_add(info->provides, strdup(line)); + } + } + } + fclose(fp); + } + + /* INSTALL */ + if(inforeq & INFRQ_SCRIPLET) { + snprintf(path, PATH_MAX, "%s/%s/install", db->path, name); + if(!stat(path, &buf)) { + info->scriptlet = 1; + } + } + + /* internal */ + info->infolevel |= inforeq; + + return(0); +} + +int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq) +{ + char topdir[PATH_MAX]; + FILE *fp = NULL; + char path[PATH_MAX]; + mode_t oldmask; + PMList *lp = NULL; + + if(db == NULL || info == NULL) { + return(-1); + } + + snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path, + info->name, info->version); + oldmask = umask(0000); + mkdir(topdir, 0755); + /* make sure we have a sane umask */ + umask(0022); + + /* DESC */ + if(inforeq & INFRQ_DESC) { + snprintf(path, PATH_MAX, "%s/desc", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%NAME%\n", fp); + fprintf(fp, "%s\n\n", info->name); + fputs("%VERSION%\n", fp); + fprintf(fp, "%s\n\n", info->version); + fputs("%DESC%\n", fp); + fprintf(fp, "%s\n\n", info->desc); + fputs("%GROUPS%\n", fp); + for(lp = info->groups; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%URL%\n", fp); + fprintf(fp, "%s\n\n", info->url); + fputs("%LICENSE%\n", fp); + fprintf(fp, "%s\n\n", info->license); + fputs("%ARCH%\n", fp); + fprintf(fp, "%s\n\n", info->arch); + fputs("%BUILDDATE%\n", fp); + fprintf(fp, "%s\n\n", info->builddate); + fputs("%INSTALLDATE%\n", fp); + fprintf(fp, "%s\n\n", info->installdate); + fputs("%PACKAGER%\n", fp); + fprintf(fp, "%s\n\n", info->packager); + fputs("%SIZE%\n", fp); + fprintf(fp, "%ld\n\n", info->size); + fputs("%REASON%\n", fp); + fprintf(fp, "%ld\n\n", info->size); + fclose(fp); + } + + /* FILES */ + if(inforeq & INFRQ_FILES) { + snprintf(path, PATH_MAX, "%s/files", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%FILES%\n", fp); + for(lp = info->files; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%BACKUP%\n", fp); + for(lp = info->backup; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fclose(fp); + } + + /* DEPENDS */ + if(inforeq & INFRQ_DEPENDS) { + snprintf(path, PATH_MAX, "%s/depends", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%DEPENDS%\n", fp); + for(lp = info->depends; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%REQUIREDBY%\n", fp); + for(lp = info->requiredby; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%CONFLICTS%\n", fp); + for(lp = info->conflicts; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%PROVIDES%\n", fp); + for(lp = info->provides; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fclose(fp); + } + + /* INSTALL */ + /* nothing needed here (script is automatically extracted) */ + + umask(oldmask); + + return(0); +} + +int db_remove(pmdb_t *db, pmpkg_t *info) +{ + char topdir[PATH_MAX]; + char file[PATH_MAX]; + + if(db == NULL || info == NULL) { + return(-1); + } + + snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path, info->name, info->version); + + /* DESC */ + snprintf(file, PATH_MAX, "%s/desc", topdir); + unlink(file); + /* FILES */ + snprintf(file, PATH_MAX, "%s/files", topdir); + unlink(file); + /* DEPENDS */ + snprintf(file, PATH_MAX, "%s/depends", topdir); + unlink(file); + /* INSTALL */ + snprintf(file, PATH_MAX, "%s/install", topdir); + unlink(file); + /* Package directory */ + if(rmdir(topdir) == -1) { + return(-1); + } + + return(0); +} + +PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root) +{ + PMList *i, *j, *k; + char *filestr = NULL; + char path[PATH_MAX+1]; + char *str = NULL; + struct stat buf, buf2; + PMList *conflicts = NULL; + + if(db == NULL || targets == NULL || root == NULL) { + return(NULL); + } + + /* CHECK 1: check every db package against every target package */ + /* XXX: I've disabled the database-against-targets check for now, as the + * many many strcmp() calls slow it down heavily and most of the + * checking is redundant to the targets-against-filesystem check. + * This will be re-enabled if I can improve performance significantly. + * + pmpkg_t *info = NULL; + char *dbstr = NULL; + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL) { + for(i = info->files; i; i = i->next) { + if(i->data == NULL) continue; + dbstr = (char*)i->data; + for(j = targets; j; j = j->next) { + pmpkg_t *targ = (pmpkg_t*)j->data; + if(strcmp(info->name, targ->name)) { + for(k = targ->files; k; k = k->next) { + filestr = (char*)k->data; + if(!strcmp(dbstr, filestr)) { + if(rindex(k->data, '/') == filestr+strlen(filestr)-1) { + continue; + } + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (installed)", dbstr, + targ->name, info->name); + conflicts = pm_list_add(conflicts, str); + } + } + } + } + } + }*/ + + /* CHECK 2: check every target against every target */ + for(i = targets; i; i = i->next) { + pmpkg_t *p1 = (pmpkg_t*)i->data; + for(j = i; j; j = j->next) { + pmpkg_t *p2 = (pmpkg_t*)j->data; + if(strcmp(p1->name, p2->name)) { + for(k = p1->files; k; k = k->next) { + filestr = k->data; + if(!strcmp(filestr, "._install") || !strcmp(filestr, ".INSTALL")) { + continue; + } + if(rindex(filestr, '/') == filestr+strlen(filestr)-1) { + /* this filename has a trailing '/', so it's a directory -- skip it. */ + continue; + } + if(pm_list_is_strin(filestr, p2->files)) { + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (target)", + filestr, p1->name, p2->name); + conflicts = pm_list_add(conflicts, str); + } + } + } + } + } + + /* CHECK 3: check every target against the filesystem */ + for(i = targets; i; i = i->next) { + pmpkg_t *p = (pmpkg_t*)i->data; + pmpkg_t *dbpkg = NULL; + for(j = p->files; j; j = j->next) { + filestr = (char*)j->data; + snprintf(path, PATH_MAX, "%s%s", root, filestr); + if(!stat(path, &buf) && !S_ISDIR(buf.st_mode)) { + int ok = 0; + if(dbpkg == NULL) { + dbpkg = db_scan(db, p->name, INFRQ_DESC | INFRQ_FILES); + } + if(dbpkg && pm_list_is_strin(j->data, dbpkg->files)) { + ok = 1; + } + /* Make sure that the supposedly-conflicting file is not actually just + * a symlink that points to a path that used to exist in the package. + */ + /* Check if any part of the conflicting file's path is a symlink */ + if(dbpkg && !ok) { + char str[PATH_MAX]; + for(k = dbpkg->files; k; k = k->next) { + snprintf(str, PATH_MAX, "%s%s", root, (char*)k->data); + stat(str, &buf2); + if(buf.st_ino == buf2.st_ino) { + ok = 1; + } + } + } + /* Check if the conflicting file has been moved to another package/target */ + if(!ok) { + /* Look at all the targets */ + for(k = targets; k && !ok; k = k->next) { + pmpkg_t *p1 = (pmpkg_t *)k->data; + /* As long as they're not the current package */ + if(strcmp(p1->name, p->name)) { + pmpkg_t *dbpkg2 = NULL; + dbpkg2 = db_scan(db, p1->name, INFRQ_DESC | INFRQ_FILES); + /* If it used to exist in there, but doesn't anymore */ + if(dbpkg2 && !pm_list_is_strin(filestr, p1->files) && pm_list_is_strin(filestr, dbpkg2->files)) { + ok = 1; + } + FREEPKG(dbpkg2); + } + } + } + if(!ok) { + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in filesystem", path); + conflicts = pm_list_add(conflicts, str); + } + } + } + FREEPKG(dbpkg); + } + + return(conflicts); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h new file mode 100644 index 00000000..b4161871 --- /dev/null +++ b/lib/libalpm/db.h @@ -0,0 +1,60 @@ +/* + * db.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_DB_H +#define _ALPM_DB_H + +#include + +#include "list.h" +#include "package.h" + +/* Database entries */ +#define INFRQ_NONE 0x00 +#define INFRQ_DESC 0x01 +#define INFRQ_DEPENDS 0x02 +#define INFRQ_FILES 0x04 +#define INFRQ_SCRIPLET 0x08 +#define INFRQ_ALL 0xFF + +/* Database */ +typedef struct __pmdb_t { + char *path; + char treename[128]; + DIR *dir; + PMList *pkgcache; + PMList *grpcache; +} 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); + +void db_rewind(pmdb_t *db); +pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq); +int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info); +int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq); +int db_remove(pmdb_t *db, pmpkg_t *info); + +PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root); + +#endif /* _ALPM_DB_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c new file mode 100644 index 00000000..d8b79974 --- /dev/null +++ b/lib/libalpm/deps.c @@ -0,0 +1,685 @@ +/* + * deps.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +/* pacman */ +#include "util.h" +#include "log.h" +#include "list.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "provide.h" +#include "deps.h" +#include "rpmvercmp.h" + +/* Re-order a list of target packages with respect to their dependencies. + * + * Example: + * A depends on C + * B depends on A + * Target order is A,B,C,D + * + * Should be re-ordered to C,A,B,D + * + * This function returns the new PMList* target list. + * + */ +PMList *sortbydeps(PMList *targets) +{ + PMList *newtargs = NULL; + PMList *i, *j, *k; + int change = 1; + int numscans = 0; + int numtargs = 0; + int clean = 0; + + if(targets == NULL) { + return(NULL); + } + + /* count the number of targets */ + numtargs = pm_list_count(targets); + + while(change) { + change = 0; + if(numscans > numtargs) { + _alpm_log(PM_LOG_FLOW2, "warning: possible dependency cycle detected\n"); + change = 0; + continue; + } + newtargs = NULL; + numscans++; + /* run thru targets, moving up packages as necessary */ + for(i = targets; i; i = i->next) { + pmpkg_t *p = (pmpkg_t*)i->data; + for(j = p->depends; j; j = j->next) { + pmdepend_t dep; + int found = 0; + pmpkg_t *q = NULL; + + splitdep(j->data, &dep); + /* look for dep.name -- if it's farther down in the list, then + * move it up above p + */ + for(k = i->next; k && !found; k = k->next) { + q = (pmpkg_t*)k->data; + if(!strcmp(dep.name, q->name)) { + found = 1; + } + } + if(found) { + if(!pkg_isin(q, newtargs)) { + change = 1; + newtargs = pm_list_add(newtargs, q); + } + } + } + if(!pkg_isin(p, newtargs)) { + newtargs = pm_list_add(newtargs, p); + } + } + if(clean && change) { + /* free up targets -- it's local now */ + for(i = targets; i; i = i->next) { + i->data = NULL; + } + pm_list_free(targets); + } + targets = newtargs; + clean = 1; + } + return(targets); +} + +/* Returns a PMList* of missing_t pointers. + * + * conflicts are always name only, but dependencies can include versions + * with depmod operators. + * + */ +PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages) +{ + pmpkg_t *info = NULL; + pmdepend_t depend; + PMList *i, *j, *k; + int cmp; + int found = 0; + PMList *baddeps = NULL; + pmdepmissing_t *miss = NULL; + + if(db == NULL) { + return(NULL); + } + + if(op == PM_TRANS_TYPE_UPGRADE) { + /* PM_TRANS_TYPE_UPGRADE handles the backwards dependencies, ie, the packages + * listed in the requiredby field. + */ + for(i = packages; i; i = i->next) { + pmpkg_t *tp, *oldpkg; + if(i->data == NULL) { + continue; + } + tp = (pmpkg_t *)i->data; + + if((oldpkg = db_scan(db, tp->name, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + continue; + } + for(j = oldpkg->requiredby; j; j = j->next) { + char *ver; + pmpkg_t *p; + found = 0; + if((p = db_scan(db, j->data, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + /* hmmm... package isn't installed.. */ + continue; + } + if(pkg_isin(p, packages)) { + /* this package is also in the upgrade list, so don't worry about it */ + continue; + } + for(k = p->depends; k && !found; k = k->next) { + /* find the dependency info in p->depends */ + splitdep(k->data, &depend); + if(!strcmp(depend.name, oldpkg->name)) { + found = 1; + } + } + if(found == 0) { + PMList *lp; + /* look for packages that list depend.name as a "provide" */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides == NULL) { + /* not found */ + continue; + } + /* we found an installed package that provides depend.name */ + for(lp = provides; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(provides); + } + found = 0; + if(depend.mod == PM_DEP_ANY) { + found = 1; + } else { + /* note that we use the version from the NEW package in the check */ + ver = strdup(tp->version); + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + if(!found) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_REQUIRED; + miss->depend.mod = depend.mod; + strncpy(miss->target, p->name, 256); + strncpy(miss->depend.name, depend.name, 256); + strncpy(miss->depend.version, depend.version, 64); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + pkg_free(oldpkg); + } + } + if(op == PM_TRANS_TYPE_ADD || op == PM_TRANS_TYPE_UPGRADE) { + for(i = packages; i; i = i->next) { + pmpkg_t *tp = i->data; + if(tp == NULL) { + continue; + } + + /* CONFLICTS */ + for(j = tp->conflicts; j; j = j->next) { + /* check targets against database */ + for(k = db_get_pkgcache(db); k; k = k->next) { + pmpkg_t *dp = (pmpkg_t *)k->data; + if(!strcmp(j->data, dp->name)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, dp->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + /* check targets against targets */ + for(k = packages; k; k = k->next) { + pmpkg_t *a = (pmpkg_t *)k->data; + if(!strcmp(a->name, (char *)j->data)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, a->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + /* check database against targets */ + for(k = db_get_pkgcache(db); k; k = k->next) { + info = k->data; + for(j = info->conflicts; j; j = j->next) { + if(!strcmp((char *)j->data, tp->name)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, info->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + + /* PROVIDES -- check to see if another package already provides what + * we offer + */ + /* XXX: disabled -- we allow multiple packages to provide the same thing. + * list packages in conflicts if they really do conflict. + for(j = tp->provides; j; j = j->next) { + PMList *provs = whatprovides(db, j->data); + for(k = provs; k; k = k->next) { + if(!strcmp(tp->name, k->data->name)) { + // this is the same package -- skip it + continue; + } + // we treat this just like a conflict + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, k->data, 256); + if(!pm_list_is_in(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + k->data = NULL; + } + pm_list_free(provs); + }*/ + + /* DEPENDENCIES -- look for unsatisfied dependencies */ + for(j = tp->depends; j; j = j->next) { + /* split into name/version pairs */ + splitdep((char *)j->data, &depend); + found = 0; + /* check database for literal packages */ + for(k = db_get_pkgcache(db); k && !found; k = k->next) { + pmpkg_t *p = (pmpkg_t *)k->data; + if(!strcmp(p->name, depend.name)) { + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check other targets */ + for(k = packages; k && !found; k = k->next) { + pmpkg_t *p = (pmpkg_t *)k->data; + /* see if the package names match OR if p provides depend.name */ + if(!strcmp(p->name, depend.name) || pm_list_is_strin(depend.name, p->provides)) { + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check database for provides matches */ + if(!found){ + PMList *lp; + k = _alpm_db_whatprovides(db, depend.name); + if(k) { + /* grab the first one (there should only really be one, anyway) */ + pmpkg_t *p = db_scan(db, ((pmpkg_t *)k->data)->name, INFRQ_DESC); + if(p == NULL) { + /* wtf */ + fprintf(stderr, "data error: %s supposedly provides %s, but it was not found in db\n", + ((pmpkg_t *)k->data)->name, depend.name); + for(lp = k; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(k); + continue; + } + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + for(lp = k; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(k); + } + /* else if still not found... */ + if(!found) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_DEPEND; + miss->depend.mod = depend.mod; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, depend.name, 256); + strncpy(miss->depend.version, depend.version, 64); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + } else if(op == PM_TRANS_TYPE_REMOVE) { + /* check requiredby fields */ + for(i = packages; i; i = i->next) { + pmpkg_t *tp; + if(i->data == NULL) { + continue; + } + tp = (pmpkg_t*)i->data; + for(j = tp->requiredby; j; j = j->next) { + if(!pm_list_is_strin((char *)j->data, packages)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_REQUIRED; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, (char *)j->data, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + } + + return(baddeps); +} + +void splitdep(char *depstr, pmdepend_t *depend) +{ + char *str = NULL; + char *ptr = NULL; + + if(depstr == NULL) { + return; + } + + str = strdup(depstr); + + if((ptr = strstr(str, ">="))) { + depend->mod = PM_DEP_GE; + } else if((ptr = strstr(str, "<="))) { + depend->mod = PM_DEP_LE; + } else if((ptr = strstr(str, "="))) { + depend->mod = PM_DEP_EQ; + } else { + /* no version specified - accept any */ + depend->mod = PM_DEP_ANY; + strncpy(depend->name, str, sizeof(depend->name)); + strncpy(depend->version, "", sizeof(depend->version)); + } + + if(ptr == NULL) { + FREE(str); + return; + } + *ptr = '\0'; + strncpy(depend->name, str, sizeof(depend->name)); + ptr++; + if(depend->mod != PM_DEP_EQ) { + ptr++; + } + strncpy(depend->version, ptr, sizeof(depend->version)); + FREE(str); + + return; +} + +/* return a new PMList target list containing all packages in the original + * target list, as well as all their un-needed dependencies. By un-needed, + * I mean dependencies that are *only* required for packages in the target + * list, so they can be safely removed. This function is recursive. + */ +PMList* removedeps(pmdb_t *db, PMList *targs) +{ + PMList *i, *j, *k; + PMList *newtargs = targs; + + if(db == NULL) { + return(newtargs); + } + + for(i = targs; i; i = i->next) { + pmpkg_t *pkg = (pmpkg_t*)i->data; + for(j = pkg->depends; j; j = j->next) { + pmdepend_t depend; + pmpkg_t *dep; + int needed = 0; + splitdep(j->data, &depend); + dep = db_scan(db, depend.name, INFRQ_DESC | INFRQ_DEPENDS); + if(pkg_isin(dep, targs)) { + continue; + } + /* see if it was explicitly installed */ + if(dep->reason == PM_PKG_REASON_EXPLICIT) { + /* ORE + vprint("excluding %s -- explicitly installed\n", dep->name);*/ + needed = 1; + } + /* see if other packages need it */ + for(k = dep->requiredby; k && !needed; k = k->next) { + pmpkg_t *dummy = db_scan(db, k->data, INFRQ_DESC); + if(!pkg_isin(dummy, targs)) { + needed = 1; + } + } + if(!needed) { + /* add it to the target list */ + pkg_free(dep); + dep = db_scan(db, depend.name, INFRQ_ALL); + newtargs = pm_list_add(newtargs, dep); + newtargs = removedeps(db, newtargs); + } + } + } + + return(newtargs); +} + +/* populates *list with packages that need to be installed to satisfy all + * dependencies (recursive) for *syncpkg->pkg + * + * make sure *list and *trail are already initialized + */ +int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data) +{ + PMList *i, *j; + PMList *targ = NULL; + PMList *deps = NULL; + + targ = pm_list_add(targ, sync->spkg); + deps = checkdeps(local, PM_TRANS_TYPE_ADD, targ); + targ->data = NULL; + pm_list_free(targ); + + if(deps == NULL) { + return(0); + } + + for(i = deps; i; i = i->next) { + int found = 0; + pmdepmissing_t *miss = i->data; + + /* XXX: conflicts are now treated specially in the _add and _sync functions */ + + /*if(miss->type == CONFLICT) { + fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); + fprintf(stderr, " %s conflicts with %s\n", miss->target, miss->depend.name); + return(1); + } else*/ + + if(miss->type == PM_DEP_DEPEND) { + pmsync_t *sync = NULL; + + /* find the package in one of the repositories */ + + /* check literals */ + for(j = databases; !sync && j; j = j->next) { + PMList *k; + pmdb_t *dbs = j->data; + + for(k = db_get_pkgcache(dbs); !sync && k; k = k->next) { + pmpkg_t *pkg = k->data; + + if(!strcmp(miss->depend.name, pkg->name)) { + sync = sync_new(PM_SYSUPG_DEPEND, NULL, k->data); + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + /* ORE + sync->pkg->reason = PM_PKG_REASON_DEPEND;*/ + } + } + } + + /* check provides */ + /* ORE + for(j = databases; !s && j; j = j->next) { + PMList *provides; + + provides = _alpm_db_whatprovides(j->data, miss->depend.name); + if(provides) { + s = sync_new(PM_SYSUPG_DEPEND, NULL, !!!provides->data!!!); + if(s == NULL) { + pm_errno = PM_ERR_MEMORY; + FREELIST(deps); + return(-1); + } + sync->pkg->reason = PM_PKG_REASON_DEPEND; + } + FREELIST(provides); + }*/ + + if(sync == NULL) { + pmdepmissing_t *m = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t)); + if(m == NULL) { + /* ORE + Free memory before leaving */ + pm_errno = PM_ERR_MEMORY; + goto error; + } + *m = *(pmdepmissing_t *)i->data; + *data = pm_list_add(*data, m); + continue; + } + + if(*data) { + /* there is at least an unresolvable dep... so we only + * continue to get the whole list of unresolvable deps */ + continue; + } + + found = 0; + for(j = list; j && !found; j = j->next) { + pmsync_t *tmp = j->data; + + if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) { + found = 1; + } + } + + if(found) { + /* this dep is already in the target list */ + FREE(sync); + continue; + } + + _alpm_log(PM_LOG_FLOW2, "resolving %s", sync->spkg->name); + found = 0; + for(j = trail; j; j = j->next) { + pmsync_t *tmp = j->data; + + if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) { + found = 1; + } + } + + if(!found) { + trail = pm_list_add(trail, sync); + if(resolvedeps(local, databases, sync, list, trail, data)) { + goto error; + } + _alpm_log(PM_LOG_FLOW2, "adding %s-%s", sync->spkg->name, sync->spkg->version); + list = pm_list_add(list, sync); + } else { + /* cycle detected -- skip it */ + _alpm_log(PM_LOG_FLOW2, "dependency cycle detected: %s", sync->spkg->name); + FREE(sync); + } + } + } + + FREELIST(deps); + + if(*data) { + pm_errno = PM_ERR_UNRESOLVABLE_DEPS; + return(-1); + } + + return(0); + +error: + FREELIST(deps); + return(-1); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h new file mode 100644 index 00000000..60b3a6d8 --- /dev/null +++ b/lib/libalpm/deps.h @@ -0,0 +1,35 @@ +/* + * deps.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_DEPS_H +#define _ALPM_DEPS_H + +#include "db.h" +#include "sync.h" + +PMList *sortbydeps(PMList *targets); +PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages); +void splitdep(char *depstr, pmdepend_t *depend); +PMList *removedeps(pmdb_t *db, PMList *targs); +int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data); + +#endif /* _ALPM_DEPS_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c new file mode 100644 index 00000000..266368ef --- /dev/null +++ b/lib/libalpm/error.c @@ -0,0 +1,90 @@ +/* + * error.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "alpm.h" + +char *alpm_strerror(int err) +{ + switch(err) { + /* System */ + case PM_ERR_NOT_A_FILE: + return "could not find or read file"; + /* Interface */ + case PM_ERR_HANDLE_NULL: + return "library not initialized"; + case PM_ERR_HANDLE_NOT_NULL: + return "library already initialized"; + case PM_ERR_WRONG_ARGS: + return "wrong or NULL argument"; + /* Databases */ + case PM_ERR_DB_OPEN: + return "could not open database"; + case PM_ERR_DB_CREATE: + return "could not create database"; + case PM_ERR_DB_NULL: + return "database not initialized"; + case PM_ERR_DB_NOT_NULL: + return "database already registered"; + case PM_ERR_DB_NOT_FOUND: + return "could not find database"; + /* Configuration */ + case PM_ERR_OPT_LOGFILE: + case PM_ERR_OPT_DBPATH: + case PM_ERR_OPT_SYNCDB: + case PM_ERR_OPT_USESYSLOG: + return "could not set parameter"; + /* Transactions */ + case PM_ERR_TRANS_NULL: + return "transaction not initialized"; + case PM_ERR_TRANS_NOT_NULL: + return "transaction already initialized"; + case PM_ERR_TRANS_DUP_TARGET: + return "duplicated target"; + case PM_ERR_TRANS_INITIALIZED: + return "transaction already initialized"; + case PM_ERR_TRANS_NOT_INITIALIZED: + return "transaction not initialized"; + /* Packages */ + case PM_ERR_PKG_NOT_FOUND: + return "could not find or read package"; + case PM_ERR_PKG_INVALID: + return "invalid or corrupted package"; + case PM_ERR_PKG_INSTALLED: + return "package already installed"; + case PM_ERR_PKG_CANT_FRESH: + return "package not installed or lesser version"; + case PM_ERR_INVALID_NAME: + return "package name is not valid"; + /* Dependencies */ + case PM_ERR_UNSATISFIED_DEPS: + return "could not satisfy dependencies"; + case PM_ERR_CONFLICTING_DEPS: + return "conflicting dependencies"; + case PM_ERR_UNRESOLVABLE_DEPS: + return "could not resolve dependencies"; + case PM_ERR_FILE_CONFLICTS: + return "conflicting files"; + default: + return "unexpected error"; + } +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/error.h b/lib/libalpm/error.h new file mode 100644 index 00000000..f96f2cb4 --- /dev/null +++ b/lib/libalpm/error.h @@ -0,0 +1,30 @@ +/* + * error.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_ERROR_H +#define _ALPM_ERROR_H + +#include "alpm.h" + +#define PM_RET_ERR(err, ret) do { pm_errno = (err); return(ret); } while(0) + +#endif /* _ALPM_ERROR_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/group.c b/lib/libalpm/group.c new file mode 100644 index 00000000..295be2f3 --- /dev/null +++ b/lib/libalpm/group.c @@ -0,0 +1,67 @@ +/* + * group.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "util.h" +#include "group.h" + +pmgrp_t *grp_new() +{ + pmgrp_t* grp = NULL; + + grp = (pmgrp_t *)malloc(sizeof(pmgrp_t)); + if(grp == NULL) { + return(NULL); + } + + grp->name[0] = '\0'; + grp->packages = NULL; + + return(grp); +} + +void grp_free(pmgrp_t *grp) +{ + if(grp == NULL) { + return; + } + + FREELIST(grp->packages); + FREE(grp); + + return; +} + +/* Helper function for sorting groups + */ +int grp_cmp(const void *g1, const void *g2) +{ + pmgrp_t *grp1 = (pmgrp_t *)g1; + pmgrp_t *grp2 = (pmgrp_t *)g2; + + return(strcmp(grp1->name, grp2->name)); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/group.h b/lib/libalpm/group.h new file mode 100644 index 00000000..a2328e0f --- /dev/null +++ b/lib/libalpm/group.h @@ -0,0 +1,48 @@ +/* + * group.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_GROUP_H +#define _ALPM_GROUP_H + +#include "list.h" + +/* Groups structure */ +typedef struct __pmgrp_t { + char name[256]; + PMList *packages; /* List of strings */ +} pmgrp_t; + +#define FREEGRP(p) do { if(p) { grp_free(p); p = NULL; } } while(0) + +#define FREELISTGRPS(p) do { \ + PMList *i; \ + for(i = p; i; i = i->next) { \ + FREEGRP(i->data); \ + } \ + FREELIST(p); \ +} while(0) + +pmgrp_t *grp_new(); +void grp_free(pmgrp_t *grp); +int grp_cmp(const void *g1, const void *g2); + +#endif /* _ALPM_GROUP_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c new file mode 100644 index 00000000..281eb96c --- /dev/null +++ b/lib/libalpm/handle.c @@ -0,0 +1,229 @@ +/* + * handle.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "util.h" +#include "log.h" +#include "list.h" +#include "error.h" +#include "trans.h" +#include "alpm.h" +#include "handle.h" + +/* log */ +extern alpm_cb_log __pm_logcb; +extern unsigned char __pm_logmask; + +pmhandle_t *handle_new() +{ + pmhandle_t *handle; + + handle = (pmhandle_t *)malloc(sizeof(pmhandle_t)); + if(handle == NULL) { + PM_RET_ERR(PM_ERR_MEMORY, NULL); + } + + /* see if we're root or not */ + handle->uid = geteuid(); + if(!handle->uid && getenv("FAKEROOTKEY")) { + /* fakeroot doesn't count, we're non-root */ + handle->uid = 99; + } + + /* see if we're root or not (fakeroot does not count) */ + if(getuid() == 0 && !getenv("FAKEROOTKEY")) { + handle->access = PM_ACCESS_RW; + } else { + handle->access = PM_ACCESS_RO; + } + + handle->trans = NULL; + + handle->db_local = NULL; + handle->dbs_sync = NULL; + + handle->logfd = NULL; + + handle->root = NULL; + handle->dbpath = NULL; + handle->logfile = NULL; + handle->noupgrade = NULL; + handle->ignorepkg = NULL; + handle->usesyslog = 0; + + return(handle); +} + +int handle_free(pmhandle_t *handle) +{ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + /* close logfiles */ + if(handle->logfd) { + fclose(handle->logfd); + handle->logfd = NULL; + } + if(handle->usesyslog) { + handle->usesyslog = 0; + closelog(); + } + + /* free memory */ + FREETRANS(handle->trans); + FREE(handle->root); + FREE(handle->dbpath); + FREE(handle->logfile); + FREELIST(handle->dbs_sync); + FREELIST(handle->noupgrade); + FREELIST(handle->ignorepkg); + free(handle); + + return(0); +} + +int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data) +{ + PMList *lp; + char str[PATH_MAX]; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + switch(val) { + case PM_OPT_DBPATH: + if(handle->db_local) { + PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1); + } + for(lp = handle->dbs_sync; lp; lp = lp->next) { + if(lp->data) { + PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1); + } + } + + if(handle->trans && handle->trans->state != STATE_IDLE) { + PM_RET_ERR(PM_ERR_TRANS_INITIALIZED, -1); + } + + strncpy(str, ((char *)data) ? (char *)data : PACDBPATH, PATH_MAX); + handle->dbpath = strdup(str); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_DBPATH set to '%s'", handle->dbpath); + break; + case PM_OPT_LOGFILE: + if((char *)data == NULL || getuid() != 0) { + return(0); + } + if(handle->logfile) { + FREE(handle->logfile); + } + if(handle->logfd) { + if(fclose(handle->logfd) != 0) { + handle->logfd = NULL; + PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1); + } + handle->logfd = NULL; + } + if((handle->logfd = fopen((char *)data, "a")) == NULL) { + _alpm_log(PM_LOG_ERROR, "can't open log file %s", (char *)data); + PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1); + } + handle->logfile = strdup((char *)data); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGFILE set to '%s'", (char *)data); + break; + case PM_OPT_NOUPGRADE: + if((char *)data && strlen((char *)data) != 0) { + handle->noupgrade = pm_list_add(handle->noupgrade, strdup((char *)data)); + _alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_NOUPGRADE", (char *)data); + } else { + FREELIST(handle->noupgrade); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_NOUPGRADE flushed"); + } + break; + case PM_OPT_IGNOREPKG: + if((char *)data && strlen((char *)data) != 0) { + handle->ignorepkg = pm_list_add(handle->ignorepkg, strdup((char *)data)); + _alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_IGNOREPKG", (char *)data); + } else { + FREELIST(handle->ignorepkg); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_IGNOREPKG flushed"); + } + break; + case PM_OPT_USESYSLOG: + if(data != 0 && data != 1) { + PM_RET_ERR(PM_ERR_OPT_USESYSLOG, -1); + } + if(handle->usesyslog == data) { + return(0); + } + if(handle->usesyslog) { + closelog(); + } else { + openlog("alpm", 0, LOG_USER); + } + handle->usesyslog = (unsigned short)data; + _alpm_log(PM_LOG_FLOW2, "PM_OPT_USESYSLOG set to '%d'", handle->usesyslog); + break; + case PM_OPT_LOGCB: + __pm_logcb = (alpm_cb_log)data; + break; + case PM_OPT_LOGMASK: + __pm_logmask = (unsigned char)data; + _alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGMASK set to '%02x'", (unsigned char)data); + break; + default: + PM_RET_ERR(PM_ERR_WRONG_ARGS, -1); + } + + return(0); +} + +int handle_get_option(pmhandle_t *handle, unsigned char val, long *data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + switch(val) { + case PM_OPT_ROOT: *data = (long)handle->root; break; + case PM_OPT_DBPATH: *data = (long)handle->dbpath; break; + case PM_OPT_LOCALDB: *data = (long)handle->db_local; break; + case PM_OPT_SYNCDB: *data = (long)handle->dbs_sync; break; + case PM_OPT_LOGFILE: *data = (long)handle->logfile; break; + case PM_OPT_NOUPGRADE: *data = (long)handle->noupgrade; break; + case PM_OPT_IGNOREPKG: *data = (long)handle->ignorepkg; break; + case PM_OPT_USESYSLOG: *data = handle->usesyslog; break; + case PM_OPT_LOGCB: *data = (long)__pm_logcb; break; + case PM_OPT_LOGMASK: *data = __pm_logmask; break; + default: + PM_RET_ERR(PM_ERR_WRONG_ARGS, -1); + break; + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h new file mode 100644 index 00000000..e8f3dbbb --- /dev/null +++ b/lib/libalpm/handle.h @@ -0,0 +1,63 @@ +/* + * handle.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_HANDLE_H +#define _ALPM_HANDLE_H + +#include "list.h" +#include "db.h" +#include "trans.h" +#include "alpm.h" + +#define PACROOT "/" +#define PACDBPATH "var/lib/pacman" +#define PACLOCK "/tmp/pacman.lck" + +typedef enum __pmaccess_t { + PM_ACCESS_RO, + PM_ACCESS_RW +} pmaccess_t; + +typedef struct __pmhandle_t { + pmaccess_t access; + uid_t uid; + pmdb_t *db_local; + PMList *dbs_sync; /* List of (pmdb_t *) */ + FILE *logfd; + pmtrans_t *trans; + /* parameters */ + char *root; + char *dbpath; + char *logfile; + PMList *noupgrade; /* List of strings */ + PMList *ignorepkg; /* List of strings */ + unsigned char usesyslog; +} pmhandle_t; + +#define FREEHANDLE(p) do { if (p) { handle_free(p); p = NULL; } } while (0) + +pmhandle_t *handle_new(); +int handle_free(pmhandle_t *handle); +int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data); +int handle_get_option(pmhandle_t *handle, unsigned char val, long *data); + +#endif /* _ALPM_HANDLE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/list.c b/lib/libalpm/list.c new file mode 100644 index 00000000..286076d9 --- /dev/null +++ b/lib/libalpm/list.c @@ -0,0 +1,210 @@ +/* + * list.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "list.h" + +PMList* pm_list_new() +{ + PMList *list = NULL; + + list = (PMList *)malloc(sizeof(PMList)); + if(list == NULL) { + return(NULL); + } + list->data = NULL; + list->prev = NULL; + list->next = NULL; + return(list); +} + +void pm_list_free(PMList *list) +{ + if(list == NULL) { + return; + } + if(list->data != NULL) { + free(list->data); + list->data = NULL; + } + if(list->next != NULL) { + pm_list_free(list->next); + } + free(list); + return; +} + +PMList* pm_list_add(PMList *list, void *data) +{ + PMList *ptr, *lp; + + ptr = list; + if(ptr == NULL) { + ptr = pm_list_new(); + } + + lp = pm_list_last(ptr); + if(lp == ptr && lp->data == NULL) { + /* nada */ + } else { + lp->next = pm_list_new(); + if(lp->next == NULL) { + return(NULL); + } + lp->next->prev = lp; + lp = lp->next; + } + lp->data = data; + return(ptr); +} + +/* Add items to a list in sorted order. Use the given comparision func to + * determine order. + */ +PMList* pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn) +{ + PMList *add; + PMList *prev = NULL; + PMList *iter = list; + + add = pm_list_new(); + add->data = data; + + /* Find insertion point. */ + while(iter) { + if(fn(add->data, iter->data) <= 0) break; + prev = iter; + iter = iter->next; + } + + /* Insert node before insertion point. */ + add->prev = prev; + add->next = iter; + if(iter != NULL) { + /* Not at end. */ + iter->prev = add; + } + if(prev != NULL) { + /* In middle. */ + prev->next = add; + } else { + /* Start or empty, new list head. */ + list = add; + } + + return(list); +} + +/* Remove an item in a list. Use the given comparaison function to find the + * item. + * If found, 'ptr' is set to point to the removed element, so that the caller + * can free it. Otherwise, ptr is NULL. + * Return the new list (without the removed element). + */ +PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr) +{ + PMList *i = list; + + while(i) { + if(fn(data, i->data) == 0) { + break; + } + i = i->next; + } + + if(ptr) { + *ptr = NULL; + } + + if(i) { + /* we found a matching item */ + if(i->next) { + i->next->prev = i->prev; + } + if(i->prev) { + i->prev->next = i->next; + } + if(i == list) { + /* The item found is the first in the chain, + * so we move the header to the next element. + */ + list = list->next; + } + + if(ptr) { + *ptr = i->data; + } + + free(i); + } + + return(list); +} + +int pm_list_count(PMList *list) +{ + int i; + PMList *lp; + + for(lp = list, i = 0; lp; lp = lp->next, i++); + + return(i); +} + +int pm_list_is_ptrin(PMList *haystack, void *needle) +{ + PMList *lp; + + for(lp = haystack; lp; lp = lp->next) { + if(lp->data == needle) { + return(1); + } + } + return(0); +} + +/* Test for existence of a string in a PMList + */ +PMList *pm_list_is_strin(char *needle, PMList *haystack) +{ + PMList *lp; + + for(lp = haystack; lp; lp = lp->next) { + if(lp->data && !strcmp(lp->data, needle)) { + return(lp); + } + } + return(NULL); +} + +PMList* pm_list_last(PMList *list) +{ + PMList *ptr; + + for(ptr = list; ptr && ptr->next; ptr = ptr->next); + return(ptr); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/list.h b/lib/libalpm/list.h new file mode 100644 index 00000000..c5de46d0 --- /dev/null +++ b/lib/libalpm/list.h @@ -0,0 +1,50 @@ +/* + * list.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_LIST_H +#define _ALPM_LIST_H + +/* Chained list struct */ +typedef struct __pmlist_t { + void *data; + struct __pmlist_t *prev; + struct __pmlist_t *next; +} pmlist_t; + +typedef struct __pmlist_t PMList; + +#define FREELIST(p) do { if(p) { pm_list_free(p); p = NULL; } } while(0) + +/* Sort comparison callback function declaration */ +typedef int (*pm_fn_cmp) (const void *, const void *); + +PMList *pm_list_new(); +void pm_list_free(PMList *list); +PMList *pm_list_add(PMList *list, void *data); +PMList *pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn); +PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr); +int pm_list_count(PMList *list); +int pm_list_is_ptrin(PMList *haystack, void *needle); +PMList *pm_list_is_strin(char *needle, PMList *haystack); +PMList *pm_list_last(PMList *list); + +#endif /* _ALPM_LIST_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/log.c b/lib/libalpm/log.c new file mode 100644 index 00000000..dd4d34a0 --- /dev/null +++ b/lib/libalpm/log.c @@ -0,0 +1,52 @@ +/* + * log.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "log.h" + +/* Internal library log mechanism */ + +alpm_cb_log __pm_logcb = NULL; +unsigned char __pm_logmask = 0; + +void _alpm_log(unsigned char flag, char *fmt, ...) +{ + char str[256]; + va_list args; + + if(__pm_logcb == NULL) { + return; + } + + if(flag & __pm_logmask) { + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + __pm_logcb(flag, str); + } +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/log.h b/lib/libalpm/log.h new file mode 100644 index 00000000..852202ab --- /dev/null +++ b/lib/libalpm/log.h @@ -0,0 +1,32 @@ +/* + * log.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_LOG_H +#define _ALPM_LOG_H + +typedef void (*alpm_cb_log)(unsigned short, char *); + +void _alpm_log(unsigned char flag, char *fmt, ...); + +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...); + +#endif /* _ALPM_LOG_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5.c b/lib/libalpm/md5.c new file mode 100644 index 00000000..fcb1611e --- /dev/null +++ b/lib/libalpm/md5.c @@ -0,0 +1,338 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 [4], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy(POINTER, POINTER, unsigned int); +static void MD5_memset(POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5.h b/lib/libalpm/md5.h new file mode 100644 index 00000000..e6e4ea64 --- /dev/null +++ b/lib/libalpm/md5.h @@ -0,0 +1,51 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, unsigned char *, unsigned int); +void MD5Final(unsigned char [16], MD5_CTX *); + +char* MDFile(char *); +void MDPrint(unsigned char [16]); + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5driver.c b/lib/libalpm/md5driver.c new file mode 100644 index 00000000..30b37051 --- /dev/null +++ b/lib/libalpm/md5driver.c @@ -0,0 +1,81 @@ +/* MD5DRIVER.C - taken and modified from MDDRIVER.C (license below) */ +/* for use in pacman. */ +/*********************************************************************/ + +/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All +rights reserved. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* The following makes MD default to MD5 if it has not already been + defined with C compiler flags. + */ +#define MD MD5 + +#include +#include +#include +#include +#include "md5.h" + +/* Length of test block, number of test blocks. + */ +#define TEST_BLOCK_LEN 1000 +#define TEST_BLOCK_COUNT 1000 + +#define MD_CTX MD5_CTX +#define MDInit MD5Init +#define MDUpdate MD5Update +#define MDFinal MD5Final + +char* MDFile(char *filename) +{ + FILE *file; + MD_CTX context; + int len; + unsigned char buffer[1024], digest[16]; + + if((file = fopen(filename, "rb")) == NULL) { + printf ("%s can't be opened\n", filename); + } else { + char *ret; + int i; + + MDInit(&context); + while((len = fread(buffer, 1, 1024, file))) { + MDUpdate(&context, buffer, len); + } + MDFinal(digest, &context); + fclose(file); + /*printf("MD5 (%s) = ", filename); + MDPrint(digest); + printf("\n");*/ + + ret = (char*)malloc(33); + ret[0] = '\0'; + for(i = 0; i < 16; i++) { + sprintf(ret, "%s%02x", ret, digest[i]); + } + + return(ret); + } + return(NULL); +} + +/* Prints a message digest in hexadecimal. + */ +void MDPrint(unsigned char digest[16]) +{ + unsigned int i; + for (i = 0; i < 16; i++) + printf ("%02x", digest[i]); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c new file mode 100644 index 00000000..f20b88eb --- /dev/null +++ b/lib/libalpm/package.c @@ -0,0 +1,342 @@ +/* + * package.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "log.h" +#include "util.h" +#include "error.h" +#include "list.h" +#include "package.h" + +pmpkg_t *pkg_new() +{ + pmpkg_t* pkg = NULL; + + MALLOC(pkg, sizeof(pmpkg_t)); + + pkg->name[0] = '\0'; + pkg->version[0] = '\0'; + pkg->desc[0] = '\0'; + pkg->url[0] = '\0'; + pkg->license[0] = '\0'; + pkg->builddate[0] = '\0'; + pkg->installdate[0] = '\0'; + pkg->packager[0] = '\0'; + pkg->md5sum[0] = '\0'; + pkg->arch[0] = '\0'; + pkg->size = 0; + pkg->scriptlet = 0; + pkg->force = 0; + pkg->reason = PM_PKG_REASON_EXPLICIT; + pkg->requiredby = NULL; + pkg->conflicts = NULL; + pkg->files = NULL; + pkg->backup = NULL; + pkg->depends = NULL; + pkg->groups = NULL; + pkg->provides = NULL; + pkg->replaces = NULL; + /* internal */ + pkg->origin = 0; + pkg->data = NULL; + pkg->infolevel = 0; + + return(pkg); +} + +void pkg_free(pmpkg_t *pkg) +{ + if(pkg == NULL) { + return; + } + + FREELIST(pkg->files); + FREELIST(pkg->backup); + FREELIST(pkg->depends); + FREELIST(pkg->conflicts); + FREELIST(pkg->requiredby); + FREELIST(pkg->groups); + FREELIST(pkg->provides); + FREELIST(pkg->replaces); + if(pkg->origin == PKG_FROM_FILE) { + FREE(pkg->data); + } + free(pkg); + + return; +} + +/* Parses the package description file for the current package + * + * Returns: 0 on success, 1 on error + * + */ +static int parse_descfile(char *descfile, pmpkg_t *info, int output) +{ + FILE* fp = NULL; + char line[PATH_MAX+1]; + char* ptr = NULL; + char* key = NULL; + int linenum = 0; + + if((fp = fopen(descfile, "r")) == NULL) { + _alpm_log(PM_LOG_ERROR, "could not open file %s", descfile); + return(-1); + } + + while(!feof(fp)) { + fgets(line, PATH_MAX, fp); + linenum++; + _alpm_strtrim(line); + if(strlen(line) == 0 || line[0] == '#') { + continue; + } + if(output) { + printf("%s\n", line); + } + ptr = line; + key = strsep(&ptr, "="); + if(key == NULL || ptr == NULL) { + fprintf(stderr, "%s: syntax error in description file line %d\n", + info->name[0] != '\0' ? info->name : "error", linenum); + } else { + _alpm_strtrim(key); + key = _alpm_strtoupper(key); + _alpm_strtrim(ptr); + if(!strcmp(key, "PKGNAME")) { + strncpy(info->name, ptr, sizeof(info->name)); + } else if(!strcmp(key, "PKGVER")) { + strncpy(info->version, ptr, sizeof(info->version)); + } else if(!strcmp(key, "PKGDESC")) { + strncpy(info->desc, ptr, sizeof(info->desc)); + } else if(!strcmp(key, "GROUP")) { + info->groups = pm_list_add(info->groups, strdup(ptr)); + } else if(!strcmp(key, "URL")) { + strncpy(info->url, ptr, sizeof(info->url)); + } else if(!strcmp(key, "LICENSE")) { + strncpy(info->license, ptr, sizeof(info->license)); + } else if(!strcmp(key, "BUILDDATE")) { + strncpy(info->builddate, ptr, sizeof(info->builddate)); + } else if(!strcmp(key, "INSTALLDATE")) { + strncpy(info->installdate, ptr, sizeof(info->installdate)); + } else if(!strcmp(key, "PACKAGER")) { + strncpy(info->packager, ptr, sizeof(info->packager)); + } else if(!strcmp(key, "ARCH")) { + strncpy(info->arch, ptr, sizeof(info->arch)); + } else if(!strcmp(key, "SIZE")) { + char tmp[32]; + strncpy(tmp, ptr, sizeof(tmp)); + info->size = atol(tmp); + } else if(!strcmp(key, "DEPEND")) { + info->depends = pm_list_add(info->depends, strdup(ptr)); + } else if(!strcmp(key, "CONFLICT")) { + info->conflicts = pm_list_add(info->conflicts, strdup(ptr)); + } else if(!strcmp(key, "REPLACES")) { + info->replaces = pm_list_add(info->replaces, strdup(ptr)); + } else if(!strcmp(key, "PROVIDES")) { + info->provides = pm_list_add(info->provides, strdup(ptr)); + } else if(!strcmp(key, "BACKUP")) { + info->backup = pm_list_add(info->backup, strdup(ptr)); + } else { + fprintf(stderr, "%s: syntax error in description file line %d\n", + info->name[0] != '\0' ? info->name : "error", linenum); + } + } + line[0] = '\0'; + } + fclose(fp); + unlink(descfile); + + return(0); +} + +pmpkg_t *pkg_load(char *pkgfile) +{ + char *expath; + int i; + int config = 0; + int filelist = 0; + int scriptcheck = 0; + TAR *tar; + pmpkg_t *info = NULL; + tartype_t gztype = { + (openfunc_t)_alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t)gzread, + (writefunc_t)gzwrite + }; + + if(pkgfile == NULL) { + PM_RET_ERR(PM_ERR_WRONG_ARGS, NULL); + } + + if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + PM_RET_ERR(PM_ERR_NOT_A_FILE, NULL); + } + + info = pkg_new(); + if(info == NULL) { + tar_close(tar); + PM_RET_ERR(PM_ERR_MEMORY, NULL); + } + + for(i = 0; !th_read(tar); i++) { + if(config && filelist && scriptcheck) { + /* we have everything we need */ + break; + } + if(!strcmp(th_get_pathname(tar), ".PKGINFO")) { + char *descfile; + + /* extract this file into /tmp. it has info for us */ + descfile = strdup("/tmp/pacman_XXXXXX"); + mkstemp(descfile); + tar_extract_file(tar, descfile); + /* parse the info file */ + if(parse_descfile(descfile, info, 0) == -1) { + goto error; + } + if(!strlen(info->name)) { + _alpm_log(PM_LOG_ERROR, "missing package name in %s", pkgfile); + goto error; + } + if(!strlen(info->version)) { + _alpm_log(PM_LOG_ERROR, "missing package version in %s", pkgfile); + goto error; + } + config = 1; + FREE(descfile); + continue; + } else if(!strcmp(th_get_pathname(tar), "._install") || !strcmp(th_get_pathname(tar), ".INSTALL")) { + info->scriptlet = 1; + scriptcheck = 1; + } else if(!strcmp(th_get_pathname(tar), ".FILELIST")) { + /* Build info->files from the filelist */ + FILE *fp; + char *fn; + char *str; + + MALLOC(str, PATH_MAX); + fn = strdup("/tmp/pacman_XXXXXX"); + mkstemp(fn); + tar_extract_file(tar, fn); + fp = fopen(fn, "r"); + while(!feof(fp)) { + if(fgets(str, PATH_MAX, fp) == NULL) { + continue; + } + _alpm_strtrim(str); + info->files = pm_list_add(info->files, strdup(str)); + } + FREE(str); + fclose(fp); + if(unlink(fn)) { + _alpm_log(PM_LOG_WARNING, "could not remove tempfile %s\n", fn); + } + FREE(fn); + filelist = 1; + continue; + } else { + scriptcheck = 1; + if(!filelist) { + /* no .FILELIST present in this package.. build the filelist the */ + /* old-fashioned way, one at a time */ + expath = strdup(th_get_pathname(tar)); + info->files = pm_list_add(info->files, expath); + } + } + + if(TH_ISREG(tar) && tar_skip_regfile(tar)) { + _alpm_log(PM_LOG_ERROR, "bad package file in %s", pkgfile); + goto error; + } + expath = NULL; + } + tar_close(tar); + + if(!config) { + _alpm_log(PM_LOG_ERROR, "missing package info file in %s", pkgfile); + goto error; + } + + /* internal */ + info->origin = PKG_FROM_FILE; + info->data = strdup(pkgfile); + info->infolevel = 0xFF; + + return(info); + +error: + printf("toto\n"); + + FREEPKG(info); + tar_close(tar); + + return(NULL); +} + +/* Helper function for sorting packages + */ +int pkg_cmp(const void *p1, const void *p2) +{ + pmpkg_t *pkg1 = (pmpkg_t *)p1; + pmpkg_t *pkg2 = (pmpkg_t *)p2; + + return(strcmp(pkg1->name, pkg2->name)); +} + +/* Test for existence of a package in a PMList* + * of pmpkg_t* + * + * returns: 0 for no match + * 1 for identical match + * -1 for name-only match (version mismatch) + */ +int pkg_isin(pmpkg_t *needle, PMList *haystack) +{ + PMList *lp; + + if(needle == NULL || haystack == NULL) { + return(0); + } + + for(lp = haystack; lp; lp = lp->next) { + pmpkg_t *info = lp->data; + + if(info && !strcmp(info->name, needle->name)) { + if(!strcmp(info->version, needle->version)) { + return(1); + } + return(-1); + } + } + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h new file mode 100644 index 00000000..890bbccc --- /dev/null +++ b/lib/libalpm/package.h @@ -0,0 +1,78 @@ +/* + * package.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_PACKAGE_H +#define _ALPM_PACKAGE_H + +#include "list.h" + +#define PKG_FROM_CACHE 1 +#define PKG_FROM_FILE 2 + +typedef struct __pmpkg_t { + char name[256]; + char version[64]; + char desc[512]; + char url[256]; + char license[128]; + char builddate[32]; + char installdate[32]; + char packager[64]; + char md5sum[33]; + char arch[32]; + unsigned long size; + unsigned char scriptlet; + unsigned char force; + unsigned char reason; + PMList *replaces; + PMList *groups; + PMList *files; + PMList *backup; + PMList *depends; + PMList *requiredby; + PMList *conflicts; + PMList *provides; + /* internal */ + unsigned char origin; + void *data; + unsigned char infolevel; +} pmpkg_t; + +#define FREEPKG(p) do { if(p) { pkg_free(p); p = NULL; } } while(0) + +#define FREELISTPKGS(p) do {\ + if(p) { \ + PMList *i;\ + for(i = p; i; i = i->next) {\ + FREEPKG(i->data); \ + }\ + FREELIST(p);\ + } \ +} while(0) + +pmpkg_t* pkg_new(); +void pkg_free(pmpkg_t *pkg); +pmpkg_t *pkg_load(char *pkgfile); +int pkg_cmp(const void *p1, const void *p2); +int pkg_isin(pmpkg_t *needle, PMList *haystack); + +#endif /* _ALPM_PACKAGE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/provide.c b/lib/libalpm/provide.c new file mode 100644 index 00000000..a472c7af --- /dev/null +++ b/lib/libalpm/provide.c @@ -0,0 +1,53 @@ +/* + * provide.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +/* pacman */ +#include "cache.h" +#include "list.h" +#include "db.h" +#include "alpm.h" + +/* return a PMList of packages in "db" that provide "package" + */ +PMList *_alpm_db_whatprovides(pmdb_t *db, char *package) +{ + PMList *pkgs = NULL; + PMList *lp; + + if(db == NULL || package == NULL || strlen(package) == 0) { + return(NULL); + } + + for(lp = db_get_pkgcache(db); lp; lp = lp->next) { + pmpkg_t *info = lp->data; + + if(pm_list_is_strin(package, info->provides)) { + pkgs = pm_list_add(pkgs, info); + } + } + + return(pkgs); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/provide.h b/lib/libalpm/provide.h new file mode 100644 index 00000000..c8c84367 --- /dev/null +++ b/lib/libalpm/provide.h @@ -0,0 +1,33 @@ +/* + * provide.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_PROVIDE_H +#define _ALPM_PROVIDE_H + +#include "config.h" + +#include "list.h" +#include "db.h" + +PMList *_alpm_db_whatprovides(pmdb_t *db, char *package); + +#endif /* _ALPM_PROVIDE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c new file mode 100644 index 00000000..c53ffa4d --- /dev/null +++ b/lib/libalpm/remove.c @@ -0,0 +1,259 @@ +/* + * remove.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "util.h" +#include "error.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "log.h" +#include "backup.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "provide.h" +#include "remove.h" +#include "handle.h" +#include "alpm.h" + +extern pmhandle_t *handle; + +int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name) +{ + pmpkg_t *info; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if((info = db_scan(db, name, INFRQ_ALL)) == NULL) { + _alpm_log(PM_LOG_ERROR, "could not find %s in database", name); + PM_RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); + } + trans->packages = pm_list_add(trans->packages, info); + + return(0); +} + +int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + pmpkg_t *info; + PMList *lp; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(!(trans->flags & (PM_TRANS_FLAG_NODEPS)) && (trans->type != PM_TRANS_TYPE_UPGRADE)) { + TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL); + + if((lp = checkdeps(db, trans->type, trans->packages)) != NULL) { + if(trans->flags & PM_TRANS_FLAG_CASCADE) { + while(lp) { + PMList *j; + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = (pmdepmissing_t*)j->data; + info = db_scan(db, miss->depend.name, INFRQ_ALL); + if(!pkg_isin(info, trans->packages)) { + trans->packages = pm_list_add(trans->packages, info); + } + } + FREELIST(lp); + lp = checkdeps(db, trans->type, trans->packages); + } + } else { + *data = lp; + PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1); + } + } + + if(trans->flags & PM_TRANS_FLAG_RECURSE) { + _alpm_log(PM_LOG_FLOW1, "finding removable dependencies..."); + trans->packages = removedeps(db, trans->packages); + } + + TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL); + } + + return(0); +} + +int remove_commit(pmdb_t *db, pmtrans_t *trans) +{ + pmpkg_t *info; + struct stat buf; + PMList *targ, *lp; + char line[PATH_MAX+1]; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + for(targ = trans->packages; targ; targ = targ->next) { + char pm_install[PATH_MAX]; + info = (pmpkg_t*)targ->data; + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + TRANS_CB(trans, PM_TRANS_CB_REMOVE_START, info, NULL); + + /* run the pre-remove scriptlet if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + _alpm_runscriptlet(handle->root, pm_install, "pre_remove", info->version, NULL); + } + + if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) { + /* iterate through the list backwards, unlinking files */ + for(lp = pm_list_last(info->files); lp; lp = lp->prev) { + char *newpath = NULL; + int nb = 0; + if(_alpm_needbackup(lp->data, info->backup)) { + nb = 1; + } + if(!nb && trans->type == PM_TRANS_TYPE_UPGRADE) { + /* check noupgrade */ + if(pm_list_is_strin(lp->data, handle->noupgrade)) { + nb = 1; + } + } + snprintf(line, PATH_MAX, "%s%s", handle->root, (char*)lp->data); + if(lstat(line, &buf)) { + _alpm_log(PM_LOG_ERROR, "file %s does not exist", line); + continue; + } + if(S_ISDIR(buf.st_mode)) { + _alpm_log(PM_LOG_DEBUG, "removing directory %s", line); + if(rmdir(line)) { + /* this is okay, other packages are probably using it. */ + } + } else { + /* if the file is flagged, back it up to .pacsave */ + if(nb) { + if(trans->type == PM_TRANS_TYPE_UPGRADE) { + /* we're upgrading so just leave the file as is. pacman_add() will handle it */ + } else { + if(!(trans->flags & PM_TRANS_FLAG_NOSAVE)) { + newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1); + sprintf(newpath, "%s.pacsave", line); + rename(line, newpath); + _alpm_log(PM_LOG_WARNING, "%s saved as %s", line, newpath); + alpm_logaction("%s saved as %s", line, newpath); + } else { + _alpm_log(PM_LOG_DEBUG, "unlinking %s", line); + if(unlink(line)) { + _alpm_log(PM_LOG_ERROR, "cannot remove file %s", line); + } + } + } + } else { + _alpm_log(PM_LOG_DEBUG, "unlinking %s", line); + if(unlink(line)) { + _alpm_log(PM_LOG_ERROR, "cannot remove file %s", line); + } + } + } + } + } + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + char pm_install[PATH_MAX]; + + /* run the post-remove script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + _alpm_runscriptlet(handle->root, pm_install, "post_remove", info->version, NULL); + } + + /* remove the package from the database */ + if(db_remove(db, info) == -1) { + _alpm_log(PM_LOG_ERROR, "failed to remove database entry %s/%s-%s", db->path, info->name, info->version); + } + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + PMList *last, *j; + pmpkg_t *depinfo = NULL; + pmdepend_t depend; + + splitdep((char*)lp->data, &depend); + + depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides) { + /* TODO: should check _all_ packages listed in provides, not just + * the first one. + */ + /* use the first one */ + depinfo = db_scan(db, provides->data, INFRQ_DEPENDS); + FREELIST(provides); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + continue; + } + } + /* splice out this entry from requiredby */ + last = pm_list_last(depinfo->requiredby); + /* ORE - use list_remove here? */ + for(j = depinfo->requiredby; j; j = j->next) { + if(!strcmp((char*)j->data, info->name)) { + if(j == depinfo->requiredby) { + depinfo->requiredby = j->next; + } + if(j->prev) j->prev->next = j->next; + if(j->next) j->next->prev = j->prev; + /* free the spliced node */ + j->prev = j->next = NULL; + FREELIST(j); + break; + } + } + db_write(db, depinfo, INFRQ_DEPENDS); + FREEPKG(depinfo); + } + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + TRANS_CB(trans, PM_TRANS_CB_REMOVE_DONE, info, NULL); + alpm_logaction("removed %s (%s)", info->name, info->version); + } + } + + /* run ldconfig if it exists */ + _alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root); + _alpm_ldconfig(handle->root); + + /* cache needs to be rebuilt */ + db_free_pkgcache(db); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/remove.h b/lib/libalpm/remove.h new file mode 100644 index 00000000..2d5cec1b --- /dev/null +++ b/lib/libalpm/remove.h @@ -0,0 +1,34 @@ +/* + * remove.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_REMOVE_H +#define _ALPM_REMOVE_H + +#include "list.h" +#include "db.h" +#include "trans.h" + +int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name); +int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int remove_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_REMOVE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/rpmvercmp.c b/lib/libalpm/rpmvercmp.c new file mode 100644 index 00000000..bbabc2b4 --- /dev/null +++ b/lib/libalpm/rpmvercmp.c @@ -0,0 +1,237 @@ +/* + * rpmvercmp.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "rpmvercmp.h" + +/* this function was taken from rpm 4.0.4 and rewritten */ +int rpmvercmp(const char *a, const char *b) +{ + char *str1, *str2; + char *one, *two; + char *rel1 = NULL, *rel2 = NULL; + char oldch1, oldch2; + int is1num, is2num; + int rc; + + if(!strcmp(a,b)) { + return(0); + } + + str1 = strdup(a); + str2 = strdup(b); + + /* lose the release number */ + for(one = str1; *one && *one != '-'; one++); + if(one) { + *one = '\0'; + rel1 = ++one; + } + for(two = str2; *two && *two != '-'; two++); + if(two) { + *two = '\0'; + rel2 = ++two; + } + + one = str1; + two = str2; + + while(*one || *two) { + while(*one && !isalnum(*one)) one++; + while(*two && !isalnum(*two)) two++; + + str1 = one; + str2 = two; + + /* find the next segment for each string */ + if(isdigit(*str1)) { + is1num = 1; + while(*str1 && isdigit(*str1)) str1++; + } else { + is1num = 0; + while(*str1 && isalpha(*str1)) str1++; + } + if(isdigit(*str2)) { + is2num = 1; + while(*str2 && isdigit(*str2)) str2++; + } else { + is2num = 0; + while(*str2 && isalpha(*str2)) str2++; + } + + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + /* see if we ran out of segments on one string */ + if(one == str1 && two != str2) { + return(is2num ? -1 : 1); + } + if(one != str1 && two == str2) { + return(is1num ? 1 : -1); + } + + /* see if we have a type mismatch (ie, one is alpha and one is digits) */ + if(is1num && !is2num) return(1); + if(!is1num && is2num) return(-1); + + if(is1num) while(*one == '0') one++; + if(is2num) while(*two == '0') two++; + + rc = strverscmp(one, two); + if(rc) return(rc); + + *str1 = oldch1; + *str2 = oldch2; + one = str1; + two = str2; + } + + if((!*one) && (!*two)) { + /* compare release numbers */ + if(rel1 && rel2) return(rpmvercmp(rel1, rel2)); + return(0); + } + + return(*one ? 1 : -1); +} + +#ifndef HAVE_STRVERSCMP + +/* GNU's strverscmp() function, taken from glibc 2.3.2 sources + */ + +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jean-François Bignolles , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractionnal parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the texinfo doc). +*/ + +int strverscmp (s1, s2) + const char *s1; + const char *s2; +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = + { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = + { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP, + +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (isdigit (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (isdigit (c1) != 0); + } + + state = result_type[state << 2 | (((c2 == '0') + (isdigit (c2) != 0)))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (isdigit (*p1++)) + if (!isdigit (*p2++)) + return 1; + + return isdigit (*p2) ? -1 : diff; + + default: + return state; + } +} + +#endif + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/rpmvercmp.h b/lib/libalpm/rpmvercmp.h new file mode 100644 index 00000000..c42c0fc7 --- /dev/null +++ b/lib/libalpm/rpmvercmp.h @@ -0,0 +1,32 @@ +/* + * rpmvercmp.h + * + * Copyright (c) 2002 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_RPMVERCMP_H +#define _PM_RPMVERCMP_H + +int rpmvercmp(const char *a, const char *b); + +#ifndef HAVE_STRVERSCMP +int strverscmp(const char *s1, const char *s2); +#endif + +#endif + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c new file mode 100644 index 00000000..8a049c74 --- /dev/null +++ b/lib/libalpm/sync.c @@ -0,0 +1,248 @@ +/* + * sync.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "log.h" +#include "util.h" +#include "list.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "trans.h" +#include "sync.h" +#include "rpmvercmp.h" +#include "handle.h" + +extern pmhandle_t *handle; + +pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg) +{ + pmsync_t *sync; + + if((sync = (pmsync_t *)malloc(sizeof(pmsync_t))) == NULL) { + return(NULL); + } + + sync->type = type; + sync->lpkg = lpkg; + sync->spkg = spkg; + + return(sync); +} + +int sync_sysupgrade(PMList **data) +{ + PMList *i, *j, *k; + PMList *targets = NULL; + + *data = NULL; + + /* check for "recommended" package replacements */ + for(i = handle->dbs_sync; i; i = i->next) { + PMList *j; + + for(j = db_get_pkgcache(i->data); j; j = j->next) { + pmpkg_t *spkg = j->data; + + for(k = spkg->replaces; k; k = k->next) { + PMList *m; + + for(m = db_get_pkgcache(handle->db_local); m; m = m->next) { + pmpkg_t *lpkg = m->data; + + if(!strcmp(k->data, lpkg->name)) { + if(pm_list_is_strin(lpkg->name, handle->ignorepkg)) { + _alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (to be replaced by %s-%s)", + lpkg->name, lpkg->version, spkg->name, spkg->version); + } else { + pmsync_t *sync = sync_new(PM_SYSUPG_REPLACE, lpkg, spkg); + + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + + targets = pm_list_add(targets, sync); + } + } + } + } + } + } + + /* match installed packages with the sync dbs and compare versions */ + for(i = db_get_pkgcache(handle->db_local); i; i = i->next) { + int cmp; + pmpkg_t *local = i->data; + pmpkg_t *spkg = NULL; + pmsync_t *sync; + + for(j = handle->dbs_sync; !spkg && j; j = j->next) { + + for(k = db_get_pkgcache(j->data); !spkg && k; k = k->next) { + pmpkg_t *sp = k->data; + + if(!strcmp(local->name, sp->name)) { + spkg = sp; + } + } + } + if(spkg == NULL) { + /*fprintf(stderr, "%s: not found in sync db. skipping.", local->name);*/ + continue; + } + + /* compare versions and see if we need to upgrade */ + cmp = rpmvercmp(local->version, spkg->version); + if(cmp > 0 && !spkg->force) { + /* local version is newer */ + _alpm_log(PM_LOG_FLOW1, "%s-%s: local version is newer", + local->name, local->version); + continue; + } else if(cmp == 0) { + /* versions are identical */ + continue; + } else if(pm_list_is_strin(i->data, handle->ignorepkg)) { + /* package should be ignored (IgnorePkg) */ + _alpm_log(PM_LOG_FLOW1, "%s-%s: ignoring package upgrade (%s)", + local->name, local->version, spkg->version); + continue; + } + + sync = sync_new(PM_SYSUPG_UPGRADE, local, spkg); + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + + targets = pm_list_add(targets, sync); + } + + *data = targets; + + return(0); + +error: + FREELIST(targets); + return(-1); +} + +int sync_resolvedeps(PMList **syncs) +{ + return(0); +} + +int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + PMList *i; + PMList *trail = NULL; + + /* Resolve targets dependencies */ + for(i = trans->targets; i; i = i->next) { + if(resolvedeps(handle->db_local, handle->dbs_sync, i->data, trans->targets, trail, data) == -1) { + /* pm_errno is set by resolvedeps */ + goto error; + } + } + + /* ORE + check for inter-conflicts and whatnot */ + + /* ORE + any packages in rmtargs need to be removed from final. + rather than ripping out nodes from final, we just copy over + our "good" nodes to a new list and reassign. */ + + /* ORE + Check dependencies of packages in rmtargs and make sure + we won't be breaking anything by removing them. + If a broken dep is detected, make sure it's not from a + package that's in our final (upgrade) list. */ + + return(0); + +error: + return(-1); +} + +int sync_commit(pmdb_t *db, pmtrans_t *trans) +{ + PMList *i, *files = NULL; + PMList *final = NULL; + PMList *data; + pmtrans_t *tr; + + /* remove any conflicting packages (WITHOUT dep checks) */ + + /* remove to-be-replaced packages */ + + /* install targets */ + tr = trans_new(PM_TRANS_TYPE_UPGRADE, 0); + for(i = files; i; i = i->next) { + trans_addtarget(tr, i->data); + } + + trans_prepare(tr, &data); + + trans_commit(tr); + + trans_free(tr); + + /* propagate replaced packages' requiredby fields to their new owners */ + for(i = final; i; i = i->next) { + /*syncpkg_t *sync = (syncpkg_t*)i->data; + if(sync->replaces) { + pkginfo_t *new = db_scan(db, sync->pkg->name, INFRQ_DEPENDS); + for(j = sync->replaces; j; j = j->next) { + pkginfo_t *old = (pkginfo_t*)j->data; + // merge lists + for(k = old->requiredby; k; k = k->next) { + if(!is_in(k->data, new->requiredby)) { + // replace old's name with new's name in the requiredby's dependency list + PMList *m; + pkginfo_t *depender = db_scan(db, k->data, INFRQ_DEPENDS); + for(m = depender->depends; m; m = m->next) { + if(!strcmp(m->data, old->name)) { + FREE(m->data); + m->data = strdup(new->name); + } + } + db_write(db, depender, INFRQ_DEPENDS); + + // add the new requiredby + new->requiredby = list_add(new->requiredby, strdup(k->data)); + } + } + } + db_write(db, new, INFRQ_DEPENDS); + FREEPKG(new); + }*/ + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/sync.h b/lib/libalpm/sync.h new file mode 100644 index 00000000..00bd7c57 --- /dev/null +++ b/lib/libalpm/sync.h @@ -0,0 +1,47 @@ +/* + * sync.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_SYNC_H +#define _ALPM_SYNC_H + +#include "db.h" +#include "package.h" +#include "trans.h" +#include "alpm.h" + +typedef struct __pmsync_t { + unsigned char type; + pmpkg_t *lpkg; + pmpkg_t *spkg; +} pmsync_t; + +pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg); + +/*int sync_findpkg(char *name, PMList *dbs, pmsyncpkg_t **sync); +pmsyncpkg_t *find_pkginsync(char *needle, PMList *haystack); +PMList *rm_pkginsync(char *needle, PMList *haystack);*/ + +int sync_sysupgrade(PMList **data); +int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int sync_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_SYNC_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c new file mode 100644 index 00000000..1aaca0e4 --- /dev/null +++ b/lib/libalpm/trans.c @@ -0,0 +1,187 @@ +/* + * trans.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +/* pacman */ +#include "error.h" +#include "package.h" +#include "util.h" +#include "list.h" +#include "handle.h" +#include "add.h" +#include "remove.h" +#include "sync.h" +#include "alpm.h" + +extern pmhandle_t *handle; + +pmtrans_t *trans_new() +{ + pmtrans_t *trans; + + if((trans = (pmtrans_t *)malloc(sizeof(pmtrans_t))) == NULL) { + return(NULL); + } + + trans->targets = NULL; + trans->packages = NULL; + trans->type = 0; + trans->flags = 0; + trans->cb = NULL; + trans->state = STATE_IDLE; + + return(trans); +} + +void trans_free(pmtrans_t *trans) +{ + if(trans == NULL) { + return; + } + + FREELIST(trans->targets); + FREELISTPKGS(trans->packages); + + free(trans); +} + +int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb) +{ + /* Sanity checks */ + if(trans == NULL) { + PM_RET_ERR(PM_ERR_TRANS_NULL, -1); + } + + /* ORE + perform sanity checks on type and flags: + for instance, we can't set UPGRADE and FRESHEN at the same time */ + + trans->type = type; + trans->flags = flags; + trans->cb = cb; + trans->state = STATE_INITIALIZED; + + return(0); +} + +int trans_addtarget(pmtrans_t *trans, char *target) +{ + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(target != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(pm_list_is_strin(target, trans->targets)) { + PM_RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1); + } + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_loadtarget(handle->db_local, trans, target) == -1) { + /* pm_errno is set by add_loadtarget() */ + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_loadtarget(handle->db_local, trans, target) == -1) { + /* pm_errno is set by remove_loadtarget() */ + return(-1); + } + break; + } + trans->targets = pm_list_add(trans->targets, strdup(target)); + trans->state = STATE_INITIALIZED; + + return(0); +} + +int trans_prepare(pmtrans_t *trans, PMList **data) +{ + *data = NULL; + + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + ASSERT(trans->packages != NULL, return(0)); + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by add_prepare() */ + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by remove_prepare() */ + return(-1); + } + break; + case PM_TRANS_TYPE_SYNC: + if(sync_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by sync_prepare() */ + return(-1); + } + break; + } + + trans->state = STATE_PREPARED; + + return(0); +} + +int trans_commit(pmtrans_t *trans) +{ + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* If there's nothing to do, return without complaining */ + ASSERT(trans->packages != NULL, return(0)); + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + case PM_TRANS_TYPE_SYNC: + if(sync_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + } + + trans->state = STATE_COMMITED; + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/trans.h b/lib/libalpm/trans.h new file mode 100644 index 00000000..98732fbf --- /dev/null +++ b/lib/libalpm/trans.h @@ -0,0 +1,54 @@ +/* + * trans.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_TRANS_H +#define _ALPM_TRANS_H + +enum { + STATE_IDLE = 0, + STATE_INITIALIZED, + STATE_PREPARED, + STATE_COMMITED +}; + +#include "alpm.h" + +typedef struct __pmtrans_t { + unsigned char type; + unsigned char flags; + unsigned char state; + PMList *targets; /* PMList of (char *) */ + PMList *packages; /* PMList of (pmpkginfo_t *) */ + alpm_trans_cb cb; +} pmtrans_t; + +#define FREETRANS(p) do { if (p) { trans_free(p); p = NULL; } } while (0) +#define TRANS_CB(t, e, d1, d2) do { if((t) && (t)->cb) { (t)->cb(e, d1, d2); } } while(0) + +pmtrans_t *trans_new(); +void trans_free(pmtrans_t *trans); +int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb); +int trans_addtarget(pmtrans_t *trans, char *target); +int trans_prepare(pmtrans_t *trans, PMList **data); +int trans_commit(pmtrans_t *trans); + +#endif /* _ALPM_TRANS_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c new file mode 100644 index 00000000..9ba3e1ee --- /dev/null +++ b/lib/libalpm/util.c @@ -0,0 +1,401 @@ +/* + * util.c + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "log.h" +#include "util.h" +#include "alpm.h" + +/* borrowed and modified from Per Liden's pkgutils (http://crux.nu) */ +long _alpm_gzopen_frontend(char *pathname, int oflags, int mode) +{ + char* gzoflags; + int fd; + gzFile gzf; + + switch (oflags & O_ACCMODE) { + case O_WRONLY: + gzoflags = "w"; + break; + case O_RDONLY: + gzoflags = "r"; + break; + case O_RDWR: + default: + errno = EINVAL; + return -1; + } + + if((fd = open(pathname, oflags, mode)) == -1) { + return -1; + } + if((oflags & O_CREAT) && fchmod(fd, mode)) { + return -1; + } + if(!(gzf = gzdopen(fd, gzoflags))) { + errno = ENOMEM; + return -1; + } + + return (long)gzf; +} + +/* does the same thing as 'mkdir -p' */ +int _alpm_makepath(char *path) +{ + char *orig, *str, *ptr; + char full[PATH_MAX] = ""; + mode_t oldmask; + + oldmask = umask(0000); + + orig = strdup(path); + str = orig; + while((ptr = strsep(&str, "/"))) { + if(strlen(ptr)) { + struct stat buf; + + strcat(full, "/"); + strcat(full, ptr); + if(stat(full, &buf)) { + if(mkdir(full, 0755)) { + free(orig); + umask(oldmask); + return(1); + } + } + } + } + free(orig); + umask(oldmask); + return(0); +} + +int _alpm_copyfile(char *src, char *dest) +{ + FILE *in, *out; + size_t len; + char buf[4097]; + + in = fopen(src, "r"); + if(in == NULL) { + return(1); + } + out = fopen(dest, "w"); + if(out == NULL) { + return(1); + } + + while((len = fread(buf, 1, 4096, in))) { + fwrite(buf, 1, len, out); + } + + fclose(in); + fclose(out); + return(0); +} + +/* Convert a string to uppercase + */ +char *_alpm_strtoupper(char *str) +{ + char *ptr = str; + + while(*ptr) { + (*ptr) = toupper(*ptr); + ptr++; + } + return str; +} + +/* Trim whitespace and newlines from a string + */ +char *_alpm_strtrim(char *str) +{ + char *pch = str; + while(isspace(*pch)) { + pch++; + } + if(pch != str) { + memmove(str, pch, (strlen(pch) + 1)); + } + + pch = (char*)(str + (strlen(str) - 1)); + while(isspace(*pch)) { + pch--; + } + *++pch = '\0'; + + return str; +} + +/* A cheap grep for text files, returns 1 if a substring + * was found in the text file fn, 0 if it wasn't + */ +int _alpm_grep(const char *fn, const char *needle) +{ + FILE *fp; + + if((fp = fopen(fn, "r")) == NULL) { + return(0); + } + while(!feof(fp)) { + char line[1024]; + fgets(line, 1024, fp); + if(feof(fp)) { + continue; + } + if(strstr(line, needle)) { + fclose(fp); + return(1); + } + } + fclose(fp); + return(0); +} + +/* Create a lock file + */ +int _alpm_lckmk(char *file) +{ + int fd, count = 0; + + while((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000)) == -1 && errno == EACCES) { + if(++count < 1) { + sleep(1); + } else { + return(-1); + } + } + return(fd > 0 ? 0 : -1); + + return(0); +} + +/* Remove a lock file + */ +int _alpm_lckrm(char *file) +{ + return(unlink(file) == -1); +} + +int _alpm_unpack(char *archive, const char *prefix, const char *fn) +{ + TAR *tar = NULL; + char expath[PATH_MAX]; + tartype_t gztype = { + (openfunc_t) _alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t) gzread, + (writefunc_t)gzwrite + }; + + /* open the .tar.gz package */ + if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + perror(archive); + return(1); + } + while(!th_read(tar)) { + if(fn && strcmp(fn, th_get_pathname(tar))) { + if(TH_ISREG(tar) && tar_skip_regfile(tar)) { + char errorstr[255]; + snprintf(errorstr, 255, "bad tar archive: %s", archive); + perror(errorstr); + tar_close(tar); + return(1); + } + continue; + } + snprintf(expath, PATH_MAX, "%s/%s", prefix, th_get_pathname(tar)); + if(tar_extract_file(tar, expath)) { + fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), strerror(errno)); + } + if(fn) break; + } + tar_close(tar); + + return(0); +} + +/* does the same thing as 'rm -rf' */ +int _alpm_rmrf(char *path) +{ + int errflag = 0; + struct dirent *dp; + DIR *dirp; + char name[PATH_MAX]; + extern int errno; + + if(!unlink(path)) { + return(0); + } else { + if(errno == ENOENT) { + return(0); + } else if(errno == EPERM) { + /* fallthrough */ + } else if(errno == EISDIR) { + /* fallthrough */ + } else if(errno == ENOTDIR) { + return(1); + } else { + /* not a directory */ + return(1); + } + + if((dirp = opendir(path)) == (DIR *)-1) { + return(1); + } + for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if(dp->d_ino) { + sprintf(name, "%s/%s", path, dp->d_name); + if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { + errflag += _alpm_rmrf(name); + } + } + } + closedir(dirp); + if(rmdir(path)) { + errflag++; + } + return(errflag); + } + return(0); +} + +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...) +{ + char msg[1024]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, 1024, fmt, args); + va_end(args); + + if(usesyslog) { + syslog(LOG_WARNING, "%s", msg); + } + + if(f) { + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + + fprintf(f, "[%02d/%02d/%02d %02d:%02d] %s\n", tm->tm_mon+1, tm->tm_mday, + tm->tm_year-100, tm->tm_hour, tm->tm_min, msg); + } + + return(0); +} + +int _alpm_ldconfig(char *root) +{ + char line[PATH_MAX]; + struct stat buf; + + snprintf(line, PATH_MAX, "%setc/ld.so.conf", root); + if(!stat(line, &buf)) { + snprintf(line, PATH_MAX, "%ssbin/ldconfig", root); + if(!stat(line, &buf)) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "%s -r %s", line, root); + system(cmd); + } + } + + return(0); +} + +int _alpm_runscriptlet(char *root, char *installfn, char *script, char *ver, char *oldver) +{ + char scriptfn[PATH_MAX]; + char cmdline[PATH_MAX]; + char tmpdir[PATH_MAX] = ""; + char *scriptpath; + struct stat buf; + + return(0); + + if(stat(installfn, &buf)) { + /* not found */ + return(0); + } + + if(!strcmp(script, "pre_upgrade") || !strcmp(script, "pre_install")) { + snprintf(tmpdir, PATH_MAX, "%stmp/", root); + if(stat(tmpdir, &buf)) { + _alpm_makepath(tmpdir); + } + snprintf(tmpdir, PATH_MAX, "%stmp/pacman-XXXXXX", root); + if(mkdtemp(tmpdir) == NULL) { + perror("error creating temp directory"); + return(1); + } + _alpm_unpack(installfn, tmpdir, ".INSTALL"); + snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir); + /* chop off the root so we can find the tmpdir in the chroot */ + scriptpath = scriptfn + strlen(root) - 1; + return(0); + } else { + strncpy(scriptfn, installfn, PATH_MAX-1); + /* chop off the root so we can find the tmpdir in the chroot */ + scriptpath = scriptfn + strlen(root) - 1; + } + + if(!_alpm_grep(scriptfn, script)) { + /* script not found in scriptlet file */ + return(0); + } + + /* ORE + pm_cblog(PM_LOG_FLOW2, "Executing %s script...\n", script);*/ + if(oldver) { + snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s %s\" | chroot %s /bin/sh", + scriptpath, script, ver, oldver, root); + } else { + snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s\" | chroot %s /bin/sh", + scriptpath, script, ver, root); + } + /* ORE + pm_cblog(PM_LOG_FLOW2, "%s\n", cmdline);*/ + system(cmdline); + + if(strlen(tmpdir) && _alpm_rmrf(tmpdir)) { + fprintf(stderr, "warning: could not remove tmpdir %s\n", tmpdir); + } + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h new file mode 100644 index 00000000..3776ebbc --- /dev/null +++ b/lib/libalpm/util.h @@ -0,0 +1,57 @@ +/* + * util.h + * + * Copyright (c) 2002-2005 by Judd Vinet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_UTIL_H +#define _ALPM_UTIL_H + +#include + +#define MALLOC(p, b) { \ + if((b) > 0) { \ + p = malloc(b); \ + if (!(p)) { \ + fprintf(stderr, "malloc failure: could not allocate %d bytes\n", (b)); \ + exit(1); \ + } \ + } else { \ + p = NULL; \ + } \ +} +#define FREE(p) do { if (p) { free(p); p = NULL; } } while(0) + +#define ASSERT(cond, action) do { if(!(cond)) { action; } } while(0) + +long _alpm_gzopen_frontend(char *pathname, int oflags, int mode); +int _alpm_makepath(char *path); +int _alpm_copyfile(char *src, char *dest); +char *_alpm_strtoupper(char *str); +char *_alpm_strtrim(char *str); +int _alpm_grep(const char *fn, const char *needle); +int _alpm_lckmk(char *file); +int _alpm_lckrm(char *file); +int _alpm_unpack(char *archive, const char *prefix, const char *fn); +int _alpm_rmrf(char *path); +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...); +int _alpm_ldconfig(char *root); +int _alpm_runscriptlet(char *util, char *installfn, char *script, char *ver, char *oldver); + +#endif /* _ALPM_UTIL_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libftp/Makefile b/lib/libftp/Makefile new file mode 100644 index 00000000..c68bf2b5 --- /dev/null +++ b/lib/libftp/Makefile @@ -0,0 +1,68 @@ +# +# This makefile contains modifications submitted by Richard Braakman +# (dark@xs4all.nl) for the shared library generation. +# + +# By default, ftplib uses PASV. If you need it to use PORT +# instead, uncomment the next line +DEFINES = -DFTPLIB_DEFMODE=FTPLIB_PORT + +SONAME = 3 +SOVERSION = $(SONAME).1 + +TARGETS = qftp libftp.a libftp.so +OBJECTS = qftp.o ftplib.o +SOURCES = qftp.c ftplib.c + +CFLAGS = -Wall $(DEBUG) -I. $(INCLUDES) $(DEFINES) +LDFLAGS = -L. +DEPFLAGS = + +all : $(TARGETS) + +clean : + rm -f $(OBJECTS) core *.bak + rm -rf unshared + +clobber : clean + rm -f $(TARGETS) .depend + rm -f libftp.so.* + +install : all + install qftp /usr/local/bin + install -m 644 libftp.so.$(SOVERSION) /usr/local/lib + install -m 644 ftplib.h /usr/local/include + (cd /usr/local/lib && \ + ln -sf libftp.so.$(SOVERSION) libftp.so.$(SONAME) && \ + ln -sf libftp.so.$(SONAME) libftp.so) + -(cd /usr/local/bin && \ + for f in ftpdir ftpget ftplist ftprm ftpsend; \ + do ln -s qftp $$f; done) + +depend : + $(CC) $(CFLAGS) -M $(SOURCES) > .depend + +# build without -fPIC +unshared/ftplib.o: ftplib.c ftplib.h + -mkdir unshared + $(CC) -c $(CFLAGS) -D_REENTRANT $< -o $@ + +ftplib.o: ftplib.c ftplib.h + $(CC) -c $(CFLAGS) -fPIC -D_REENTRANT $< -o $@ + +libftp.a: unshared/ftplib.o + ar -rcs $@ $< + +libftp.so.$(SOVERSION): ftplib.o + $(CC) -shared -Wl,-soname,libftp.so.$(SONAME) -lc -o $@ $< + +libftp.so: libftp.so.$(SOVERSION) + ln -sf $< libftp.so.$(SONAME) + ln -sf $< $@ + +qftp : qftp.o libftp.so ftplib.h + $(CC) $(LDFLAGS) -o $@ $< -lftp + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/lib/libftp/ftplib.c b/lib/libftp/ftplib.c new file mode 100644 index 00000000..b69744eb --- /dev/null +++ b/lib/libftp/ftplib.c @@ -0,0 +1,1569 @@ +/***************************************************************************/ +/* */ +/* ftplib.c - callable ftp access routines */ +/* Copyright (C) 1996-2000 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +#if defined(__unix__) || defined(__VMS) +#include +#endif +#if defined(_WIN32) +#include +#endif +#include +#include +#include +#include +#include +#if defined(__unix__) +#include +#include +#include +#include +#include +#include +#elif defined(VMS) +#include +#include +#include +#include +#include +#elif defined(_WIN32) +#include +#endif + +#define BUILDING_LIBRARY +#include "ftplib.h" + +#if defined(_WIN32) +#define SETSOCKOPT_OPTVAL_TYPE (const char *) +#else +#define SETSOCKOPT_OPTVAL_TYPE (void *) +#endif + +#define FTPLIB_BUFSIZ 8192 +#define ACCEPT_TIMEOUT 30 + +#define FTPLIB_CONTROL 0 +#define FTPLIB_READ 1 +#define FTPLIB_WRITE 2 + +#if !defined FTPLIB_DEFMODE +#define FTPLIB_DEFMODE FTPLIB_PASSIVE +#endif + +struct NetBuf { + char *cput,*cget; + int handle; + int cavail,cleft; + char *buf; + int dir; + netbuf *ctrl; + netbuf *data; + int cmode; + struct timeval idletime; + FtpCallback idlecb; + void *idlearg; + int xfered; + int cbbytes; + int xfered1; + char response[256]; +}; + +static char *version = +"ftplib Release 3.1-1 9/16/00, copyright 1996-2000 Thomas Pfau"; + +GLOBALDEF int ftplib_debug = 0; + +#if defined(__unix__) || defined(VMS) +#define net_read read +#define net_write write +#define net_close close +#elif defined(_WIN32) +#define net_read(x,y,z) recv(x,y,z,0) +#define net_write(x,y,z) send(x,y,z,0) +#define net_close closesocket +#endif + +#if defined(NEED_MEMCCPY) + /* + * VAX C does not supply a memccpy routine so I provide my own + */ +void *memccpy(void *dest, const void *src, int c, size_t n) +{ + int i=0; + const unsigned char *ip=src; + unsigned char *op=dest; + + while (i < n) + { + if ((*op++ = *ip++) == c) + break; + i++; + } + if (i == n) + return NULL; + return op; +} +#endif +#if defined(NEED_STRDUP) +/* + * strdup - return a malloc'ed copy of a string + */ +char *strdup(const char *src) +{ + int l = strlen(src) + 1; + char *dst = malloc(l); + if (dst) + strcpy(dst,src); + return dst; +} +#endif + +/* + * socket_wait - wait for socket to receive or flush data + * + * return 1 if no user callback, otherwise, return value returned by + * user callback + */ +static int socket_wait(netbuf *ctl) +{ + fd_set fd,*rfd = NULL,*wfd = NULL; + struct timeval tv; + int rv = 0; + if ((ctl->dir == FTPLIB_CONTROL) || (ctl->idlecb == NULL)) + return 1; + if (ctl->dir == FTPLIB_WRITE) + wfd = &fd; + else + rfd = &fd; + FD_ZERO(&fd); + do + { + FD_SET(ctl->handle,&fd); + tv = ctl->idletime; + rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); + if (rv == -1) + { + rv = 0; + strncpy(ctl->ctrl->response, strerror(errno), + sizeof(ctl->ctrl->response)); + break; + } + else if (rv > 0) + { + rv = 1; + break; + } + } + while ((rv = ctl->idlecb(ctl, ctl->xfered, ctl->idlearg))); + return rv; +} + +/* + * read a line of text + * + * return -1 on error or bytecount + */ +static int readline(char *buf,int max,netbuf *ctl) +{ + int x,retval = 0; + char *end,*bp=buf; + int eof = 0; + + if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) + return -1; + if (max == 0) + return 0; + do + { + if (ctl->cavail > 0) + { + x = (max >= ctl->cavail) ? ctl->cavail : max-1; + end = memccpy(bp,ctl->cget,'\n',x); + if (end != NULL) + x = end - bp; + retval += x; + bp += x; + *bp = '\0'; + max -= x; + ctl->cget += x; + ctl->cavail -= x; + if (end != NULL) + { + bp -= 2; + if (strcmp(bp,"\r\n") == 0) + { + *bp++ = '\n'; + *bp++ = '\0'; + --retval; + } + break; + } + } + if (max == 1) + { + *buf = '\0'; + break; + } + if (ctl->cput == ctl->cget) + { + ctl->cput = ctl->cget = ctl->buf; + ctl->cavail = 0; + ctl->cleft = FTPLIB_BUFSIZ; + } + if (eof) + { + if (retval == 0) + retval = -1; + break; + } + if (!socket_wait(ctl)) + return retval; + if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1) + { + perror("read"); + retval = -1; + break; + } + if (x == 0) + eof = 1; + ctl->cleft -= x; + ctl->cavail += x; + ctl->cput += x; + } + while (1); + return retval; +} + +/* + * write lines of text + * + * return -1 on error or bytecount + */ +static int writeline(char *buf, int len, netbuf *nData) +{ + int x, nb=0, w; + char *ubp = buf, *nbp; + char lc=0; + + if (nData->dir != FTPLIB_WRITE) + return -1; + nbp = nData->buf; + for (x=0; x < len; x++) + { + if ((*ubp == '\n') && (lc != '\r')) + { + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(1) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = '\r'; + } + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(2) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = lc = *ubp++; + } + if (nb) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, nb); + if (w != nb) + { + printf("net_write(3) returned %d, errno = %d\n", w, errno); + return(-1); + } + } + return len; +} + +/* + * read a response from the server + * + * return 0 if first char doesn't match + * return 1 if first char matches + */ +static int readresp(char c, netbuf *nControl) +{ + char match[5]; + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + if (nControl->response[3] == '-') + { + strncpy(match,nControl->response,3); + match[3] = ' '; + match[4] = '\0'; + do + { + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + } + while (strncmp(nControl->response,match,4)); + } + if (nControl->response[0] == c) + return 1; + return 0; +} + +/* + * FtpInit for stupid operating systems that require it (Windows NT) + */ +GLOBALDEF void FtpInit(void) +{ +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsadata; + int err; + wVersionRequested = MAKEWORD(1,1); + if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0) + fprintf(stderr,"Network failed to start: %d\n",err); +#endif +} + +/* + * FtpLastResponse - return a pointer to the last response received + */ +GLOBALDEF char *FtpLastResponse(netbuf *nControl) +{ + if ((nControl) && (nControl->dir == FTPLIB_CONTROL)) + return nControl->response; + return NULL; +} + +/* + * FtpConnect - connect to remote server + * + * return 1 if connected, 0 if not + */ +GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) +{ + int sControl; + struct sockaddr_in sin; + struct hostent *phe; + struct servent *pse; + int on=1; + netbuf *ctrl; + char *lhost; + char *pnum; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + if (pnum == NULL) + { +#if defined(VMS) + sin.sin_port = htons(21); +#else + if ((pse = getservbyname("ftp","tcp")) == NULL) + { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; +#endif + } + else + { + *pnum++ = '\0'; + if (isdigit(*pnum)) + sin.sin_port = htons(atoi(pnum)); + else + { + pse = getservbyname(pnum,"tcp"); + if(pse == NULL) { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; + } + } + if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sControl); + return 0; + } + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + net_close(sControl); + return 0; + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sControl); + return 0; + } + ctrl->buf = malloc(FTPLIB_BUFSIZ); + if (ctrl->buf == NULL) + { + perror("calloc"); + net_close(sControl); + free(ctrl); + return 0; + } + ctrl->handle = sControl; + ctrl->dir = FTPLIB_CONTROL; + ctrl->ctrl = NULL; + ctrl->cmode = FTPLIB_DEFMODE; + ctrl->idlecb = NULL; + ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; + ctrl->idlearg = NULL; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = 0; + if (readresp('2', ctrl) == 0) + { + net_close(sControl); + free(ctrl->buf); + free(ctrl); + return 0; + } + *nControl = ctrl; + return 1; +} + +/* + * FtpOptions - change connection options + * + * returns 1 if successful, 0 on error + */ +GLOBALDEF int FtpOptions(int opt, long val, netbuf *nControl) +{ + int v,rv=0; + switch (opt) + { + case FTPLIB_CONNMODE: + v = (int) val; + if ((v == FTPLIB_PASSIVE) || (v == FTPLIB_PORT)) + { + nControl->cmode = v; + rv = 1; + } + break; + case FTPLIB_CALLBACK: + nControl->idlecb = (FtpCallback) val; + rv = 1; + break; + case FTPLIB_IDLETIME: + v = (int) val; + rv = 1; + nControl->idletime.tv_sec = v / 1000; + nControl->idletime.tv_usec = (v % 1000) * 1000; + break; + case FTPLIB_CALLBACKARG: + rv = 1; + nControl->idlearg = (void *) val; + break; + case FTPLIB_CALLBACKBYTES: + rv = 1; + nControl->cbbytes = (int) val; + break; + } + return rv; +} + +/* + * FtpSendCmd - send a command and wait for expected response + * + * return 1 if proper response received, 0 otherwise + */ +static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + char buf[256]; + if (nControl->dir != FTPLIB_CONTROL) + return 0; + if (ftplib_debug > 2) + fprintf(stderr,"%s\n",cmd); + if ((strlen(cmd) + 3) > sizeof(buf)) + return 0; + sprintf(buf,"%s\r\n",cmd); + if (net_write(nControl->handle,buf,strlen(buf)) <= 0) + { + perror("write"); + return 0; + } + return readresp(expresp, nControl); +} + +/* + * FtpLogin - log in to remote server + * + * return 1 if logged in, 0 otherwise + */ +GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl) +{ + char tempbuf[64]; + + if (((strlen(user) + 7) > sizeof(tempbuf)) || + ((strlen(pass) + 7) > sizeof(tempbuf))) + return 0; + sprintf(tempbuf,"USER %s",user); + if (!FtpSendCmd(tempbuf,'3',nControl)) + { + if (nControl->response[0] == '2') + return 1; + return 0; + } + sprintf(tempbuf,"PASS %s",pass); + return FtpSendCmd(tempbuf,'2',nControl); +} + +/* + * FtpOpenPort - set up data connection + * + * return 1 if successful, 0 otherwise + */ +static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir) +{ + int sData; + union { + struct sockaddr sa; + struct sockaddr_in in; + } sin; + struct linger lng = { 0, 0 }; + unsigned int l; + int on=1; + netbuf *ctrl; + char *cp; + unsigned int v[6]; + char buf[256]; + + if (nControl->dir != FTPLIB_CONTROL) + return -1; + if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + { + sprintf(nControl->response, "Invalid direction %d\n", dir); + return -1; + } + if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE)) + { + sprintf(nControl->response, "Invalid mode %c\n", mode); + return -1; + } + l = sizeof(sin); + if (nControl->cmode == FTPLIB_PASSIVE) + { + memset(&sin, 0, l); + sin.in.sin_family = AF_INET; + if (!FtpSendCmd("PASV",'2',nControl)) + return -1; + cp = strchr(nControl->response,'('); + if (cp == NULL) + return -1; + cp++; + sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sin.sa.sa_data[2] = v[2]; + sin.sa.sa_data[3] = v[3]; + sin.sa.sa_data[4] = v[4]; + sin.sa.sa_data[5] = v[5]; + sin.sa.sa_data[0] = v[0]; + sin.sa.sa_data[1] = v[1]; + } + else + { + if (getsockname(nControl->handle, &sin.sa, &l) < 0) + { + perror("getsockname"); + return 0; + } + } + sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sData == -1) + { + perror("socket"); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_LINGER, + SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (nControl->cmode == FTPLIB_PASSIVE) + { + if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) + { + perror("connect"); + net_close(sData); + return -1; + } + } + else + { + sin.in.sin_port = 0; + if (bind(sData, &sin.sa, sizeof(sin)) == -1) + { + perror("bind"); + net_close(sData); + return 0; + } + if (listen(sData, 1) < 0) + { + perror("listen"); + net_close(sData); + return 0; + } + if (getsockname(sData, &sin.sa, &l) < 0) + return 0; + sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", + (unsigned char) sin.sa.sa_data[2], + (unsigned char) sin.sa.sa_data[3], + (unsigned char) sin.sa.sa_data[4], + (unsigned char) sin.sa.sa_data[5], + (unsigned char) sin.sa.sa_data[0], + (unsigned char) sin.sa.sa_data[1]); + if (!FtpSendCmd(buf,'2',nControl)) + { + net_close(sData); + return 0; + } + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sData); + return -1; + } + if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL)) + { + perror("calloc"); + net_close(sData); + free(ctrl); + return -1; + } + ctrl->handle = sData; + ctrl->dir = dir; + ctrl->idletime = nControl->idletime; + ctrl->idlearg = nControl->idlearg; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = nControl->cbbytes; + if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec || ctrl->cbbytes) + ctrl->idlecb = nControl->idlecb; + else + ctrl->idlecb = NULL; + *nData = ctrl; + return 1; +} + +/* + * FtpAcceptConnection - accept connection from server + * + * return 1 if successful, 0 otherwise + */ +static int FtpAcceptConnection(netbuf *nData, netbuf *nControl) +{ + int sData; + struct sockaddr addr; + unsigned int l; + int i; + struct timeval tv; + fd_set mask; + int rv; + + FD_ZERO(&mask); + FD_SET(nControl->handle, &mask); + FD_SET(nData->handle, &mask); + tv.tv_usec = 0; + tv.tv_sec = ACCEPT_TIMEOUT; + i = nControl->handle; + if (i < nData->handle) + i = nData->handle; + i = select(i+1, &mask, NULL, NULL, &tv); + if (i == -1) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else if (i == 0) + { + strcpy(nControl->response, "timed out waiting for connection"); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else + { + if (FD_ISSET(nData->handle, &mask)) + { + l = sizeof(addr); + sData = accept(nData->handle, &addr, &l); + i = errno; + net_close(nData->handle); + if (sData > 0) + { + rv = 1; + nData->handle = sData; + } + else + { + strncpy(nControl->response, strerror(i), + sizeof(nControl->response)); + nData->handle = 0; + rv = 0; + } + } + else if (FD_ISSET(nControl->handle, &mask)) + { + net_close(nData->handle); + nData->handle = 0; + readresp('2', nControl); + rv = 0; + } + } + return rv; +} + +/* + * FtpAccess - return a handle for a data stream + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData) +{ + char buf[256]; + int dir; + if ((path == NULL) && + ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ))) + { + sprintf(nControl->response, + "Missing path argument for file transfer\n"); + return 0; + } + sprintf(buf, "TYPE %c", mode); + if (!FtpSendCmd(buf, '2', nControl)) + return 0; + switch (typ) + { + case FTPLIB_DIR: + strcpy(buf,"NLST"); + dir = FTPLIB_READ; + break; + case FTPLIB_DIR_VERBOSE: + strcpy(buf,"LIST"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_READ: + strcpy(buf,"RETR"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_WRITE: + strcpy(buf,"STOR"); + dir = FTPLIB_WRITE; + break; + default: + sprintf(nControl->response, "Invalid open type %d\n", typ); + return 0; + } + if (path != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(path) + i) >= sizeof(buf)) + return 0; + strcpy(&buf[i],path); + } + if (FtpOpenPort(nControl, nData, mode, dir) == -1) + return 0; + if (!FtpSendCmd(buf, '1', nControl)) + { + FtpClose(*nData); + *nData = NULL; + return 0; + } + (*nData)->ctrl = nControl; + nControl->data = *nData; + if (nControl->cmode == FTPLIB_PORT) + { + if (!FtpAcceptConnection(*nData,nControl)) + { + FtpClose(*nData); + *nData = NULL; + nControl->data = NULL; + return 0; + } + } + return 1; +} + +/* + * FtpRead - read from a data connection + */ +GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_READ) + return 0; + if (nData->buf) + i = readline(buf, max, nData); + else + { + i = socket_wait(nData); + if (i != 1) + return 0; + i = net_read(nData->handle, buf, max); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + if (nData->idlecb(nData, nData->xfered, nData->idlearg) == 0) + return 0; + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpWrite - write to a data connection + */ +GLOBALDEF int FtpWrite(void *buf, int len, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_WRITE) + return 0; + if (nData->buf) + i = writeline(buf, len, nData); + else + { + socket_wait(nData); + i = net_write(nData->handle, buf, len); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + nData->idlecb(nData, nData->xfered, nData->idlearg); + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpClose - close a data connection + */ +GLOBALDEF int FtpClose(netbuf *nData) +{ + netbuf *ctrl; + switch (nData->dir) + { + case FTPLIB_WRITE: + /* potential problem - if buffer flush fails, how to notify user? */ + if (nData->buf != NULL) + writeline(NULL, 0, nData); + case FTPLIB_READ: + if (nData->buf) + free(nData->buf); + shutdown(nData->handle,2); + net_close(nData->handle); + ctrl = nData->ctrl; + free(nData); + if (ctrl) + { + ctrl->data = NULL; + return(readresp('2', ctrl)); + } + return 1; + case FTPLIB_CONTROL: + if (nData->data) + { + nData->ctrl = NULL; + FtpClose(nData); + } + net_close(nData->handle); + free(nData); + return 0; + } + return 1; +} + +/* + * FtpSite - send a SITE command + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(cmd) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"SITE %s",cmd); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpSysType - send a SYST command + * + * Fills in the user buffer with the remote system type. If more + * information from the response is required, the user can parse + * it out of the response buffer returned by FtpLastResponse(). + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSysType(char *buf, int max, netbuf *nControl) +{ + int l = max; + char *b = buf; + char *s; + if (!FtpSendCmd("SYST",'2',nControl)) + return 0; + s = &nControl->response[4]; + while ((--l) && (*s != ' ')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpMkdir - create a directory at server + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"MKD %s",path); + if (!FtpSendCmd(buf,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpChdir - change path at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpChdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"CWD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpCDUp - move to parent directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpCDUp(netbuf *nControl) +{ + if (!FtpSendCmd("CDUP",'2',nControl)) + return 0; + return 1; +} + +/* + * FtpRmdir - remove directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"RMD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpPwd - get working directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl) +{ + int l = max; + char *b = path; + char *s; + if (!FtpSendCmd("PWD",'2',nControl)) + return 0; + s = strchr(nControl->response, '"'); + if (s == NULL) + return 0; + s++; + while ((--l) && (*s) && (*s != '"')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +static int FtpXfer(const char *localfile, const char *path, + netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + netbuf *nData; + int rv=1; + + if (localfile != NULL) + { + char ac[4] = "a"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + if (!FtpAccess(path, typ, mode, nControl, &nData)) + return 0; + dbuf = malloc(FTPLIB_BUFSIZ); + if (typ == FTPLIB_FILE_WRITE) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((c = FtpWrite(dbuf, l, nData)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + else + { + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) + if (fwrite(dbuf, 1, l, local) < l) + { + perror("\nlocalfile write"); + rv = 0; + break; + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + FtpClose(nData); + return rv; +} + +/* + * FtpNlst - issue an NLST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpNlst(const char *outputfile, const char *path, + netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR, FTPLIB_ASCII); +} + +/* + * FtpDir - issue a LIST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDir(const char *outputfile, const char *path, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII); +} + +/* + * FtpSize - determine the size of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpSize(const char *path, int *size, char mode, netbuf *nControl) +{ + char cmd[256]; + int resp,sz,rv=1; + + if ((strlen(path) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd, "TYPE %c", mode); + if (!FtpSendCmd(cmd, '2', nControl)) + return 0; + sprintf(cmd,"SIZE %s",path); + if (!FtpSendCmd(cmd,'2',nControl)) + rv = 0; + else + { + if (sscanf(nControl->response, "%d %d", &resp, &sz) == 2) + *size = sz; + else + rv = 0; + } + return rv; +} + +/* + * FtpRestart - issue a REST command + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRestart(int offset, netbuf *nControl) +{ + char cmd[256]; + int rv=1; + + sprintf(cmd,"REST %d",offset); + if (!FtpSendCmd(cmd,'3',nControl)) + rv = 0; + return rv; +} + +/* + * FtpModDate - determine the modification date of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl) +{ + char buf[256]; + int rv = 1; + + if ((strlen(path) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"MDTM %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + rv = 0; + else + strncpy(dt, &nControl->response[4], max); + return rv; +} + +/* + * FtpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpGet(const char *outputfile, const char *path, + char mode, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_FILE_READ, mode); +} + +/* + * FtpPut - issue a PUT command and send data from input + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPut(const char *inputfile, const char *path, char mode, + netbuf *nControl) +{ + return FtpXfer(inputfile, path, nControl, FTPLIB_FILE_WRITE, mode); +} + +/* + * FtpRename - rename a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl) +{ + char cmd[256]; + + if (((strlen(src) + 7) > sizeof(cmd)) || + ((strlen(dst) + 7) > sizeof(cmd))) + return 0; + sprintf(cmd,"RNFR %s",src); + if (!FtpSendCmd(cmd,'3',nControl)) + return 0; + sprintf(cmd,"RNTO %s",dst); + if (!FtpSendCmd(cmd,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpDelete - delete a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl) +{ + char cmd[256]; + + if ((strlen(fnm) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd,"DELE %s",fnm); + if (!FtpSendCmd(cmd,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF void FtpQuit(netbuf *nControl) +{ + if (nControl->dir != FTPLIB_CONTROL) + return; + FtpSendCmd("QUIT",'2',nControl); + net_close(nControl->handle); + free(nControl->buf); + free(nControl); +} + +/* + * HttpConnect - connect to remote server + * + * return 1 if connected, 0 if not + */ +GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl) +{ + int sControl; + struct sockaddr_in sin; + struct hostent *phe; + struct servent *pse; + netbuf *ctrl; + char *lhost; + char *pnum; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + if (pnum == NULL) + { +#if defined(VMS) + sin.sin_port = htons(80); +#else + /* we pass a port variable instead (for use with proxies) + * + if ((pse = getservbyname("http","tcp")) == NULL) + { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; + */ + sin.sin_port = htons(port); +#endif + } + else + { + *pnum++ = '\0'; + if (isdigit(*pnum)) + sin.sin_port = htons(atoi(pnum)); + else + { + pse = getservbyname(pnum,"tcp"); + sin.sin_port = pse->s_port; + } + } + if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + net_close(sControl); + return 0; + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sControl); + return 0; + } + ctrl->buf = NULL; + ctrl->handle = sControl; + ctrl->dir = FTPLIB_CONTROL; + ctrl->ctrl = NULL; + ctrl->cmode = FTPLIB_DEFMODE; + ctrl->idlecb = NULL; + ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; + ctrl->idlearg = NULL; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = 0; + *nControl = ctrl; + return 1; +} + +/* + * HttpSendCmd - send a command + * + * return 1 if proper response received, 0 otherwise + */ +static int HttpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + int ret = 0; + char *buf = nControl->response; + //if (nControl->dir != FTPLIB_CONTROL) + //return 0; + if (ftplib_debug > 2) + fprintf(stderr,"%s\n",cmd); + if (net_write(nControl->handle,cmd,strlen(cmd)) <= 0) + { + perror("write"); + return 0; + } + while (ret < 256) { + if (socket_wait(nControl) != 1) + return 0; + if (net_read(nControl->handle,buf,1) != 1) { + break; + } + ret++; + if (*buf == '\r') continue; + if (*buf == '\n') break; + buf++; + } + *buf = 0; + if (nControl->response[9] == expresp) + return 1; + return 0; +} + +/* + * HttpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +static int HttpXfer(const char *localfile, const char *path, int *size, + netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + int rv=1; + int bytes = 0; + + if (localfile != NULL) + { + char ac[4] = "a"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + dbuf = malloc(FTPLIB_BUFSIZ); + if (typ == FTPLIB_FILE_WRITE) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((c = FtpWrite(dbuf, l, nControl)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + else + { + nControl->dir = FTPLIB_READ; + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nControl)) > 0) { + if (fwrite(dbuf, 1, l, local) < l) + { + perror("\nlocalfile write"); + rv = 0; + break; + } + bytes += l; + if(size && bytes >= *size) { + break; + } + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + free(nControl->data); + return rv; +} + +/* + * HttpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALREF int HttpGet(const char *host, const char *outputfile, const char *path, + int *size, netbuf *nControl, unsigned int offset) +{ + char buf[256]; + + if(offset > 0) + sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%d-\r\n\r\n", path, host, offset); + else + sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", path, host); + if(!HttpSendCmd(buf,'2',nControl)) + { + if (nControl->response[9] == '3') + printf("redirection not supported\n"); + return 0; + } + + while (1) + { + int ret = 0; + char *buf = nControl->response; + while (ret < 256) { + if (socket_wait(nControl) != 1) + return 0; + if (net_read(nControl->handle,buf,1) != 1) + break; + ret++; + if (*buf == '\r') continue; + if (*buf == '\n') break; + buf++; + } + *buf = 0; + if (strstr(nControl->response,"Content-Length")) + { + sscanf(nControl->response,"Content-Length: %d",size); + if(offset > 0) + *size += offset; + } + if (strlen(nControl->response) == 0) + break; + } + + return HttpXfer(outputfile, path, size, nControl, FTPLIB_FILE_READ, FTPLIB_IMAGE); +} + +/* + * HttpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALREF void HttpQuit(netbuf *nControl) +{ + if(nControl) { + net_close(nControl->handle); + free(nControl); + } +} diff --git a/lib/libftp/ftplib.h b/lib/libftp/ftplib.h new file mode 100644 index 00000000..49e0ccbf --- /dev/null +++ b/lib/libftp/ftplib.h @@ -0,0 +1,131 @@ +/***************************************************************************/ +/* */ +/* ftplib.h - header file for callable ftp access routines */ +/* Copyright (C) 1996, 1997 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +#if !defined(__FTPLIB_H) +#define __FTPLIB_H + +#if defined(__unix__) || defined(VMS) +#define GLOBALDEF +#define GLOBALREF extern +#elif defined(_WIN32) +#if defined BUILDING_LIBRARY +#define GLOBALDEF __declspec(dllexport) +#define GLOBALREF __declspec(dllexport) +#else +#define GLOBALREF __declspec(dllimport) +#endif +#endif + +/* FtpAccess() type codes */ +#define FTPLIB_DIR 1 +#define FTPLIB_DIR_VERBOSE 2 +#define FTPLIB_FILE_READ 3 +#define FTPLIB_FILE_WRITE 4 + +/* FtpAccess() mode codes */ +#define FTPLIB_ASCII 'A' +#define FTPLIB_IMAGE 'I' +#define FTPLIB_TEXT FTPLIB_ASCII +#define FTPLIB_BINARY FTPLIB_IMAGE + +/* connection modes */ +#define FTPLIB_PASSIVE 1 +#define FTPLIB_PORT 2 + +/* connection option names */ +#define FTPLIB_CONNMODE 1 +#define FTPLIB_CALLBACK 2 +#define FTPLIB_IDLETIME 3 +#define FTPLIB_CALLBACKARG 4 +#define FTPLIB_CALLBACKBYTES 5 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NetBuf netbuf; +typedef int (*FtpCallback)(netbuf *nControl, int xfered, void *arg); + +/* v1 compatibility stuff */ +#if !defined(_FTPLIB_NO_COMPAT) +netbuf *DefaultNetbuf; + +#define ftplib_lastresp FtpLastResponse(DefaultNetbuf) +#define ftpInit FtpInit +#define ftpOpen(x) FtpConnect(x, &DefaultNetbuf) +#define ftpLogin(x,y) FtpLogin(x, y, DefaultNetbuf) +#define ftpSite(x) FtpSite(x, DefaultNetbuf) +#define ftpMkdir(x) FtpMkdir(x, DefaultNetbuf) +#define ftpChdir(x) FtpChdir(x, DefaultNetbuf) +#define ftpRmdir(x) FtpRmdir(x, DefaultNetbuf) +#define ftpNlst(x, y) FtpNlst(x, y, DefaultNetbuf) +#define ftpDir(x, y) FtpDir(x, y, DefaultNetbuf) +#define ftpGet(x, y, z) FtpGet(x, y, z, DefaultNetbuf) +#define ftpPut(x, y, z) FtpPut(x, y, z, DefaultNetbuf) +#define ftpRename(x, y) FtpRename(x, y, DefaultNetbuf) +#define ftpDelete(x) FtpDelete(x, DefaultNetbuf) +#define ftpQuit() FtpQuit(DefaultNetbuf) +#endif /* (_FTPLIB_NO_COMPAT) */ +/* end v1 compatibility stuff */ + +GLOBALREF int ftplib_debug; +GLOBALREF void FtpInit(void); +GLOBALREF char *FtpLastResponse(netbuf *nControl); +GLOBALREF int FtpConnect(const char *host, netbuf **nControl); +GLOBALREF int FtpOptions(int opt, long val, netbuf *nControl); +GLOBALREF int FtpLogin(const char *user, const char *pass, netbuf *nControl); +GLOBALREF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData); +GLOBALREF int FtpRead(void *buf, int max, netbuf *nData); +GLOBALREF int FtpWrite(void *buf, int len, netbuf *nData); +GLOBALREF int FtpClose(netbuf *nData); +GLOBALREF int FtpSite(const char *cmd, netbuf *nControl); +GLOBALREF int FtpSysType(char *buf, int max, netbuf *nControl); +GLOBALREF int FtpMkdir(const char *path, netbuf *nControl); +GLOBALREF int FtpChdir(const char *path, netbuf *nControl); +GLOBALREF int FtpCDUp(netbuf *nControl); +GLOBALREF int FtpRmdir(const char *path, netbuf *nControl); +GLOBALREF int FtpPwd(char *path, int max, netbuf *nControl); +GLOBALREF int FtpNlst(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpDir(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpSize(const char *path, int *size, char mode, netbuf *nControl); +GLOBALREF int FtpRestart(int offset, netbuf *nControl); +GLOBALREF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl); +GLOBALREF int FtpGet(const char *output, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpPut(const char *input, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpRename(const char *src, const char *dst, netbuf *nControl); +GLOBALREF int FtpDelete(const char *fnm, netbuf *nControl); +GLOBALREF void FtpQuit(netbuf *nControl); + +GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl); +GLOBALREF int HttpGet(const char *host, const char *output, const char *path, + int *size, netbuf *nControl, unsigned int offset); +GLOBALREF void HttpQuit(netbuf *nControl); + +#ifdef __cplusplus +}; +#endif + +#endif /* __FTPLIB_H */ -- cgit v1.2.3-24-g4f1b