From 617b904b26037fac6296eb44fb36cfadfdaf27ec Mon Sep 17 00:00:00 2001 From: Judd Vinet Date: Sat, 7 Feb 2004 20:37:00 +0000 Subject: Imported from pacman-2.7.3.tar.gz --- ChangeLog | 6 + Makefile.in | 10 +- doc/pacman.8.in | 13 +- etc/pacman.conf | 28 +- libftp/ftplib.c | 81 +- libftp/ftplib.h | 42 +- scripts/gensync | 2 +- scripts/makepkg | 40 +- scripts/makeworld | 2 +- src/pacman.c | 97 +- src/pacman.c~ | 3145 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pacman.h | 2 +- src/pacsync.c | 134 ++- 13 files changed, 3472 insertions(+), 130 deletions(-) create mode 100644 src/pacman.c~ diff --git a/ChangeLog b/ChangeLog index 2758ad6a..5ae7e954 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ VERSION DESCRIPTION ----------------------------------------------------------------------------- +2.7.3 - makepkg not longer strips files with .exe or .dll extensions + - Added Aurelien's patch: + - proxy support (no authentication yet) + - HTTP/1.1 support + - an improved progress bar with transfer rates and ETA + - cleaned up warning output a bit 2.7.2 - Supressed "No such file" messages during stripping - Removed extra newlines in /var/log/pacman.log - Added a --noextract option to makepkg to skip source extraction diff --git a/Makefile.in b/Makefile.in index 4f7b0801..13a24109 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,7 +34,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ -PACVER = 2.7.2 +PACVER = 2.7.3 TOPDIR = @srcdir@ SRCDIR = $(TOPDIR)/src/ @@ -68,9 +68,9 @@ OBJECTS = $(OBJDIR)pacman.o \ $(OBJDIR)md5driver.o \ $(OBJDIR)rpmvercmp.o -all: ftplib pacman vercmp convertdb man +all: libftp.a pacman vercmp convertdb man -pacman: $(OBJECTS) +pacman: $(OBJECTS) libftp.a $(CXX) $(OBJECTS) -o $@ $(LDFLAGS) vercmp: $(OBJDIR)vercmp.o $(OBJDIR)rpmvercmp.o @@ -91,7 +91,7 @@ man: $(MANSRC)pacman.8 \ dist: distclean (cd ..; tar czvf pacman-$(PACVER).tar.gz pacman-$(PACVER)) -ftplib: +libftp.a: (cd libftp; make libftp.a) install: pacman vercmp convertdb man @@ -112,5 +112,7 @@ clean: distclean: clean rm -f pacman convertdb vercmp + rm -f Makefile + rm -f config.h config.status config.log # End of file diff --git a/doc/pacman.8.in b/doc/pacman.8.in index 859f0b4b..491604ce 100644 --- a/doc/pacman.8.in +++ b/doc/pacman.8.in @@ -1,4 +1,4 @@ -.TH pacman 8 "September 28, 2003" "pacman #VERSION#" "" +.TH pacman 8 "February 6, 2004" "pacman #VERSION#" "" .SH NAME pacman \- package manager utility .SH SYNOPSIS @@ -100,6 +100,10 @@ diskspace, you can remove these packages by using the --clean option. Display all the members for each package group specified. If no group names are provided, all groups will be listed. .TP +.B "\-l, \-\-list" +List all files in the specified repositories. Multiple repositories can +be specified on the command line. +.TP .B "\-s, \-\-search " This will search each package in the package list for names or descriptions that contains . @@ -185,6 +189,7 @@ NoUpgrade = etc/fstab [current] Server = ftp://ftp.archlinux.org/current Server = ftp://ftp.mirror.com/archlinux/current +Server = http://www.othermirror.com/arch/current [custom] Server = file:///home/pkgs @@ -201,6 +206,12 @@ Overrides the default location of the toplevel database directory. The default Instructs pacman to ignore any upgrades for this package when performing a \fB--sysupgrade\fP. .TP +.B "ProxyServer = " +If set, pacman will use this proxy server for all ftp/http transfers. +.TP +.B "ProxyPort = " +Use this to set a different port for your proxy server (default is 80). +.TP .B "NoPassiveFtp" Disables passive ftp connections when downloading packages. (aka Active Mode) .TP diff --git a/etc/pacman.conf b/etc/pacman.conf index 188a6c94..73c9dcbf 100644 --- a/etc/pacman.conf +++ b/etc/pacman.conf @@ -1,6 +1,10 @@ # # /etc/pacman.conf # +# NOTE: If you find a mirror that is geographically close to you, please +# move it to the top of the server list, so pacman will choose it +# first. +# # See the pacman manpage for option directives @@ -8,18 +12,20 @@ # GENERAL OPTIONS # [options] -LogFile = /var/log/pacman.log -NoUpgrade = etc/passwd etc/group etc/shadow -NoUpgrade = etc/fstab etc/raidtab -NoUpgrade = etc/rc.conf etc/rc.local -NoUpgrade = etc/lilo.conf boot/grub/menu.lst -#IgnorePkg = lilo kernel +LogFile = /var/log/pacman.log +NoUpgrade = etc/passwd etc/group etc/shadow +NoUpgrade = etc/fstab etc/raidtab +NoUpgrade = etc/rc.conf etc/rc.local +NoUpgrade = etc/modprobe.conf etc/modules.conf +NoUpgrade = etc/lilo.conf boot/grub/menu.lst +#IgnorePkg = lilo kernel # # REPOSITORIES # [current] Server = ftp://ftp.archlinux.org/current +Server = ftp://ftp.archlinux.de/pub/archlinux/current Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/current Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/current Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/current @@ -27,23 +33,24 @@ Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/arc Server = ftp://ftp.parrswood.net/Mirrors/ftp.archlinux.org/current Server = ftp://ftp.kegep.tuc.gr/archlinux/current Server = http://darkstar.ist.utl.pt/archlinux/current +Server = ftp://archlinux.creativa.cl/current Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/current Server = ftp://saule.mintis.lt/pub/linux/current Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/current -Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/current # Uncomment this block to access the EXTRA package set # [extra] Server = ftp://ftp.archlinux.org/extra +Server = ftp://ftp.archlinux.de/pub/archlinux/extra Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/extra -Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/extra Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/extra Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/extra Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/extra Server = ftp://ftp.parrswood.net/Mirrors/ftp.archlinux.org/extra Server = ftp://ftp.kegep.tuc.gr/archlinux/extra Server = http://darkstar.ist.utl.pt/archlinux/extra +Server = ftp://archlinux.creativa.cl/extra Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/extra Server = ftp://saule.mintis.lt/pub/linux/extra Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/extra @@ -53,8 +60,8 @@ Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/extra # #[release] #Server = ftp://ftp.archlinux.org/release +#Server = ftp://ftp.archlinux.de/pub/archlinux/release #Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/release -#Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/release #Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/release #Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/release #Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/release @@ -69,13 +76,14 @@ Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/extra # #[unstable] #Server = ftp://ftp.archlinux.org/unstable +#Server = ftp://ftp.archlinux.de/pub/archlinux/unstable #Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/unstable -#Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/unstable #Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/unstable #Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/unstable #Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/unstable #Server = ftp://ftp.parrswood.net/Mirrors/ftp.archlinux.org/unstable #Server = http://darkstar.ist.utl.pt/archlinux/unstable +#Server = ftp://archlinux.creativa.cl/unstable #Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/unstable #Server = ftp://saule.mintis.lt/pub/linux/unstable #Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/unstable diff --git a/libftp/ftplib.c b/libftp/ftplib.c index 74fd6c5e..d9b98e9a 100644 --- a/libftp/ftplib.c +++ b/libftp/ftplib.c @@ -1,24 +1,24 @@ /***************************************************************************/ -/* */ -/* 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. */ -/* */ +/* */ +/* 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) @@ -1319,13 +1319,12 @@ GLOBALDEF void FtpQuit(netbuf *nControl) * * return 1 if connected, 0 if not */ -GLOBALREF int HttpConnect(const char *host, netbuf **nControl) +GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl) { int sControl; struct sockaddr_in sin; struct hostent *phe; struct servent *pse; - int on=1; netbuf *ctrl; char *lhost; char *pnum; @@ -1337,14 +1336,18 @@ GLOBALREF int HttpConnect(const char *host, netbuf **nControl) if (pnum == NULL) { #if defined(VMS) - sin.sin_port = htons(21); + 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 @@ -1374,13 +1377,6 @@ GLOBALREF int HttpConnect(const char *host, netbuf **nControl) 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"); @@ -1448,13 +1444,14 @@ static int HttpSendCmd(const char *cmd, char expresp, netbuf *nControl) * * return 1 if successful, 0 otherwise */ -static int HttpXfer(const char *localfile, const char *path, +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) { @@ -1487,13 +1484,18 @@ static int HttpXfer(const char *localfile, const char *path, else { nControl->dir = FTPLIB_READ; - while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nControl)) > 0) + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nControl)) > 0) { if (fwrite(dbuf, 1, l, local) <= 0) { perror("localfile write"); rv = 0; break; } + bytes += l; + if(size && bytes >= *size) { + break; + } + } } free(dbuf); fflush(local); @@ -1508,12 +1510,15 @@ static int HttpXfer(const char *localfile, const char *path, * * return 1 if successful, 0 otherwise */ -GLOBALREF int HttpGet(const char *outputfile, const char *path, int *size, - netbuf *nControl) +GLOBALREF int HttpGet(const char *host, const char *outputfile, const char *path, + int *size, netbuf *nControl, unsigned int offset) { char buf[256]; - sprintf(buf, "GET %s HTTP/1.0\r\n\r\n\r\n", path); + 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') @@ -1537,12 +1542,16 @@ GLOBALREF int HttpGet(const char *outputfile, const char *path, int *size, } *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, nControl, FTPLIB_FILE_READ, FTPLIB_IMAGE); + return HttpXfer(outputfile, path, size, nControl, FTPLIB_FILE_READ, FTPLIB_IMAGE); } /* diff --git a/libftp/ftplib.h b/libftp/ftplib.h index 737d2ebf..49e0ccbf 100644 --- a/libftp/ftplib.h +++ b/libftp/ftplib.h @@ -1,24 +1,24 @@ /***************************************************************************/ -/* */ +/* */ /* 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. */ -/* */ +/* 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) @@ -119,9 +119,9 @@ 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, netbuf **nControl); -GLOBALREF int HttpGet(const char *output, const char *path, int *size, - 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 diff --git a/scripts/gensync b/scripts/gensync index 5c563562..85774781 100755 --- a/scripts/gensync +++ b/scripts/gensync @@ -20,7 +20,7 @@ # USA. # -myver='2.7.2' +myver='2.7.3' usage() { echo "gensync $myver" diff --git a/scripts/makepkg b/scripts/makepkg index 393e00c5..df6db755 100755 --- a/scripts/makepkg +++ b/scripts/makepkg @@ -20,7 +20,7 @@ # USA. # -myver='2.7.2' +myver='2.7.3' startdir=`pwd` USE_COLOR="n" @@ -559,31 +559,33 @@ if [ -d pkg/usr/share ]; then fi # compress man pages -if [ -d pkg/usr/man ]; then - msg "Compressing man pages..." - for i in `find pkg/usr/man -type f`; do - ext=`echo $i | sed 's|.*\.||g'` - fn=`echo $i | sed 's|.*/||g'` - if [ "$ext" != "gz" ]; then - # update symlinks to this manpage - for ln in `find pkg/usr/man -lname "$fn"`; do - rm -f $ln - ln -sf ${fn}.gz ${ln}.gz - done - # compress the original - gzip -9 $i - fi - done -fi +msg "Compressing man pages..." +for i in `find pkg/{usr{,/local},opt/*}/man -type f 2>/dev/null`; do + ext=${i##*.} + fn=${i##*/} + if [ "$ext" != "gz" ]; then + # update symlinks to this manpage + for ln in `find pkg/{usr{,/local},opt/*}/man -lname "$fn" 2>/dev/null`; do + rm -f $ln + ln -sf ${fn}.gz ${ln}.gz + done + # compress the original + gzip -9 $i + fi +done cd $startdir # strip binaries if [ "$NOSTRIP" = "0" ]; then msg "Stripping debugging symbols from libraries..." - find pkg/{,usr,usr/local,opt/*}/lib -type f -exec /usr/bin/strip --strip-debug '{}' \; 2>&1 | grep -v "No such file" + find pkg/{,usr,usr/local,opt/*}/lib -type f -not -name "*.dll" -not -name "*.exe" \ + -exec /usr/bin/strip --strip-debug '{}' \; 2>&1 \ + | grep -v "No such file" | grep -v "format not recognized" msg "Stripping symbols from binaries..." - find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -exec /usr/bin/strip '{}' \; 2>&1 | grep -v "No such file" + find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -not -name "*.dll" -not -name "*.exe" \ + -exec /usr/bin/strip '{}' \; 2>&1 \ + | grep -v "No such file" | grep -v "format not recognized" fi # get some package meta info diff --git a/scripts/makeworld b/scripts/makeworld index 49f25549..20a5b8d9 100755 --- a/scripts/makeworld +++ b/scripts/makeworld @@ -21,7 +21,7 @@ # toplevel=`pwd` -version="2.7.2" +version="2.7.3" usage() { echo "makeworld version $version" diff --git a/src/pacman.c b/src/pacman.c index 14e81a12..803f39d6 100644 --- a/src/pacman.c +++ b/src/pacman.c @@ -78,6 +78,8 @@ unsigned short pmo_group = 0; /* configuration file options */ char *pmo_dbpath = NULL; char *pmo_logfile = NULL; +char *pmo_proxyhost = NULL; +unsigned short pmo_proxyport = 80; PMList *pmo_noupgrade = NULL; PMList *pmo_ignorepkg = NULL; unsigned short pmo_usesyslog = 0; @@ -96,6 +98,7 @@ char *lckfile = "/tmp/pacman.lck"; char *workfile = NULL; enum {READ_ONLY, READ_WRITE} pm_access; int maxcols = 80; +int neednl = 0; /* for cleaner message output */ int main(int argc, char *argv[]) { @@ -133,7 +136,7 @@ int main(int argc, char *argv[]) /* check for permission */ pm_access = READ_ONLY; if(pmo_op != PM_MAIN && pmo_op != PM_QUERY && pmo_op != PM_DEPTEST) { - if(pmo_op == PM_SYNC && !pmo_s_sync && (pmo_s_search || pmo_group)) { + if(pmo_op == PM_SYNC && !pmo_s_sync && (pmo_s_search || pmo_group || pmo_q_list)) { /* special case: PM_SYNC can be used w/ pmo_s_search by any user */ } else { if(geteuid() != 0) { @@ -333,7 +336,7 @@ int pacman_sync(pacdb_t *db, PMList *targets) printf("done.\n"); return(0); } - + if(pmo_s_sync) { /* grab a fresh package list */ printf(":: Synchronizing package databases... \n"); @@ -456,6 +459,37 @@ int pacman_sync(pacdb_t *db, PMList *targets) FREELIST(pkg); } FREELIST(groups); + } else if(pmo_q_list) { + PMList *reps = NULL; + int found; + if(targets) { + for(i = targets; i; i = i->next) { + reps = list_add(reps, strdup(i->data)); + } + } else { + for(i = pmc_syncs; i; i = i->next) { + reps = list_add(reps, strdup(((sync_t *)i->data)->treename)); + } + } + for(i = reps; i; i = i->next) { + found = 0; + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t *)j->data; + if(!strcmp(dbs->sync->treename, i->data)) { + for(k = dbs->pkgcache; k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + printf("%s %s %s\n", dbs->sync->treename, pkg->name, pkg->version); + } + found = 1; + } + } + if(!found) { + printf("Repository \"%s\" was not found.\n", (char *)i->data); + allgood = 0; + break; + } + } + FREELIST(reps); } else if(pmo_s_upgrade) { int newer = 0; int ignore = 0; @@ -687,7 +721,7 @@ int pacman_sync(pacdb_t *db, PMList *targets) } } - if(allgood && !pmo_s_search) { + if(allgood && !(pmo_s_search || pmo_q_list)) { /* check for inter-conflicts and whatnot */ if(!pmo_nodeps && !pmo_s_downloadonly) { int errorout = 0; @@ -1132,7 +1166,7 @@ int pacman_add(pacdb_t *db, PMList *targets) fflush(stdout); for(targ = targets; targ; targ = targ->next) { /* Populate the package struct */ - vprint("reading %s\n", (char*)targ->data); + vprint("reading %s... ", (char*)targ->data); info = load_pkg((char*)targ->data, 0); if(info == NULL) { return(1); @@ -1141,11 +1175,28 @@ int pacman_add(pacdb_t *db, PMList *targets) /* only upgrade/install this package if it is already installed and at a lesser version */ pkginfo_t *dummy = db_scan(db, info->name, INFRQ_DESC); if(dummy == NULL || rpmvercmp(dummy->version, info->version) >= 0) { + vprint("not installed or lesser version\n"); FREEPKG(info); + FREEPKG(dummy); continue; } FREEPKG(dummy); } + /* check if an older version of said package is already in alltargs. + * if so, replace it in the list */ + for(j = alltargs, k = filenames; j && j->data && k; j = j->next, k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + if(!strcmp(pkg->name, info->name)) { + if(rpmvercmp(pkg->version, info->version) < 0) { + vprint("replacing older version in target list. "); + FREEPKG(j->data); + j->data = info; + FREE(k->data); + k->data = strdup(targ->data); + } + } + } + vprint("done\n"); alltargs = list_add(alltargs, info); filenames = list_add(filenames, strdup(targ->data)); } @@ -1301,6 +1352,7 @@ int pacman_add(pacdb_t *db, PMList *targets) int retcode; printf("upgrading %s... ", info->name); + neednl = 1; /* we'll need the full record for backup checks later */ oldpkg = db_scan(db, info->name, INFRQ_ALL); @@ -1326,6 +1378,7 @@ int pacman_add(pacdb_t *db, PMList *targets) } if(!pmo_upgrade) { printf("installing %s... ", info->name); + neednl = 1; } fflush(stdout); @@ -1711,6 +1764,7 @@ int pacman_remove(pacdb_t *db, PMList *targets) if(!pmo_upgrade) { printf("removing %s... ", info->name); + neednl = 1; fflush(stdout); /* run the pre-remove script if it exists */ snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, pmo_dbpath, db->treename, info->name, info->version); @@ -1764,7 +1818,7 @@ int pacman_remove(pacdb_t *db, PMList *targets) } } } else { - vprint(" unlinking %s\n", line); + /*vprint(" unlinking %s\n", line);*/ if(unlink(line)) { perror("cannot remove file"); } @@ -2845,8 +2899,30 @@ int parseconfig(char *configfile) strncpy(pmo_dbpath, ptr, PATH_MAX); vprint("config: dbpath: %s\n", pmo_dbpath); } else if (!strcmp(key, "LOGFILE")) { + if(pmo_logfile) { + FREE(pmo_logfile); + } pmo_logfile = strndup(ptr, PATH_MAX); vprint("config: log file: %s\n", pmo_logfile); + } else if (!strcmp(key, "PROXYSERVER")) { + char *p; + if(pmo_proxyhost) { + FREE(pmo_proxyhost); + } + p = strstr(ptr, "://"); + if(p) { + p += 3; + if(p == NULL || *p == '\0') { + fprintf(stderr, "config: line %d: bad server location\n", linenum); + return(1); + } + ptr = p; + } + pmo_proxyhost = strndup(ptr, PATH_MAX); + vprint("config: proxyserver: %s\n", pmo_proxyhost); + } else if (!strcmp(key, "PROXYPORT")) { + pmo_proxyport = (unsigned short)atoi(ptr); + vprint("config: proxyport: %u\n", pmo_proxyport); } else { fprintf(stderr, "config: line %d: syntax error\n", linenum); return(1); @@ -2973,6 +3049,7 @@ void usage(int op, char *myname) printf(" -d, --nodeps skip dependency checks\n"); printf(" -f, --force force install, overwrite conflicting files\n"); printf(" -g, --groups view all members of a package group\n"); + printf(" -l, --list list all packages belonging to the specified repository\n"); printf(" -s, --search search sync database for matching strings\n"); printf(" -u, --sysupgrade upgrade all packages that are out of date\n"); printf(" -w, --downloadonly download packages, but do not install/upgrade anything\n"); @@ -3019,6 +3096,10 @@ void logaction(FILE *fp, char *fmt, ...) vsnprintf(msg, 1024, fmt, args); va_end(args); if(fp) { + if(neednl) { + fprintf(fp, "\n"); + neednl = 0; + } fprintf(fp, "%s\n", msg); fflush(fp); } @@ -3116,12 +3197,18 @@ void cleanup(int signum) FREELIST(pmo_ignorepkg); FREE(pmo_root); FREE(pmo_dbpath); + FREE(pmo_logfile); + FREE(pmo_proxyhost); FREELIST(pm_targets); /* this is segfaulting... quick fix for now FREELISTPKGS(pm_packages);*/ + if(signum) { + printf("\n"); + } + exit(signum); } diff --git a/src/pacman.c~ b/src/pacman.c~ new file mode 100644 index 00000000..e0018ba6 --- /dev/null +++ b/src/pacman.c~ @@ -0,0 +1,3145 @@ +/* + * pacman.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 "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* pacman */ +#include "rpmvercmp.h" +#include "md5.h" +#include "list.h" +#include "package.h" +#include "util.h" +#include "db.h" +#include "pacsync.h" +#include "pacman.h" + +/* + * GLOBALS + * + */ + +/* command line options */ +char *pmo_root = NULL; +unsigned short pmo_op = PM_MAIN; +unsigned short pmo_verbose = 0; +unsigned short pmo_version = 0; +unsigned short pmo_help = 0; +unsigned short pmo_force = 0; +unsigned short pmo_nodeps = 0; +unsigned short pmo_upgrade = 0; +unsigned short pmo_freshen = 0; +unsigned short pmo_nosave = 0; +unsigned short pmo_d_vertest = 0; +unsigned short pmo_d_resolve = 0; +unsigned short pmo_q_isfile = 0; +unsigned short pmo_q_info = 0; +unsigned short pmo_q_list = 0; +unsigned short pmo_q_orphans = 0; +unsigned short pmo_q_owns = 0; +unsigned short pmo_r_cascade = 0; +unsigned short pmo_s_upgrade = 0; +unsigned short pmo_s_downloadonly = 0; +unsigned short pmo_s_sync = 0; +unsigned short pmo_s_search = 0; +unsigned short pmo_s_clean = 0; +unsigned short pmo_group = 0; +/* configuration file options */ +char *pmo_dbpath = NULL; +char *pmo_logfile = NULL; +PMList *pmo_noupgrade = NULL; +PMList *pmo_ignorepkg = NULL; +unsigned short pmo_usesyslog = 0; +unsigned short pmo_nopassiveftp = 0; + + +/* list of sync_t structs for sync locations */ +PMList *pmc_syncs = NULL; +/* list of installed packages */ +PMList *pm_packages = NULL; +/* list of targets specified on command line */ +PMList *pm_targets = NULL; + +FILE *logfd = NULL; +char *lckfile = "/tmp/pacman.lck"; +char *workfile = NULL; +enum {READ_ONLY, READ_WRITE} pm_access; +int maxcols = 80; + +int main(int argc, char *argv[]) +{ + int ret = 0; + char *ptr = NULL; + char path[PATH_MAX]; + pacdb_t *db_local = NULL; + char *cenv = NULL; + + cenv = getenv("COLUMNS"); + if(cenv) { + maxcols = atoi(cenv); + } + + if(argc < 2) { + usage(PM_MAIN, (char*)basename(argv[0])); + return(0); + } + + /* default root */ + MALLOC(pmo_root, PATH_MAX); + strcpy(pmo_root, "/"); + /* default dbpath */ + MALLOC(pmo_dbpath, PATH_MAX); + strcpy(pmo_dbpath, PKGDIR); + + /* parse the command line */ + ret = parseargs(PM_ADD, argc, argv); + if(ret) { + FREE(pmo_root); + FREE(pmo_dbpath); + return(ret); + } + + /* check for permission */ + pm_access = READ_ONLY; + if(pmo_op != PM_MAIN && pmo_op != PM_QUERY && pmo_op != PM_DEPTEST) { + if(pmo_op == PM_SYNC && !pmo_s_sync && (pmo_s_search || pmo_group)) { + /* special case: PM_SYNC can be used w/ pmo_s_search by any user */ + } else { + if(geteuid() != 0) { + fprintf(stderr, "error: you cannot perform this operation unless you are root.\n"); + return(1); + } + pm_access = READ_WRITE; + /* lock */ + if(lckmk(lckfile, 1, 1) == -1) { + fprintf(stderr, "error: unable to lock pacman database.\n"); + fprintf(stderr, " if you're sure pacman is not already running, you\n"); + fprintf(stderr, " can remove %s\n", lckfile); + return(32); + } + } + } + + /* set signal handlers */ + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + + /* parse the system-wide config file */ + snprintf(path, PATH_MAX, "/%s", PACCONF); + if(parseconfig(path)) { + cleanup(1); + } + + if(pmo_usesyslog) { + openlog("pacman", 0, LOG_USER); + } + if(pmo_logfile && geteuid() == 0) { + /* open the log file */ + logfd = fopen(pmo_logfile, "a"); + if(logfd == NULL) { + perror("warning: cannot open logfile"); + } + } + + /* check for db existence */ + /* add a trailing '/' if there isn't one */ + if(pmo_root[strlen(pmo_root)-1] != '/') { + MALLOC(ptr, strlen(pmo_root)+1); + strcpy(ptr, pmo_root); + strcat(ptr, "/"); + FREE(pmo_root); + pmo_root = ptr; + } + + vprint("Installation Root: %s\n", pmo_root); + /* db location */ + vprint("Top-level DB Path: %s%s\n", pmo_root, pmo_dbpath); + if(pmo_verbose) { + list_display("Targets:", pm_targets); + } + + db_local = db_open(pmo_root, pmo_dbpath, "local"); + if(db_local == NULL) { + /* couldn't open the db directory - try creating it */ + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s%s/local", pmo_root, pmo_dbpath); + vprint("initializing database %s...\n", path); + ret = makepath(path); + + if(ret) { + fprintf(stderr, "error: could not create database.\n"); + cleanup(1); + } + if((db_local = db_open(pmo_root, pmo_dbpath, "local")) == NULL) { + fprintf(stderr, "error: could not open database.\n"); + cleanup(1); + } + } + + /* load pm_packages cache */ + pm_packages = db_loadpkgs(db_local); + + /* start the requested operation */ + switch(pmo_op) { + case PM_ADD: ret = pacman_add(db_local, pm_targets); break; + case PM_REMOVE: ret = pacman_remove(db_local, pm_targets); break; + case PM_UPGRADE: ret = pacman_upgrade(db_local, pm_targets); break; + case PM_QUERY: ret = pacman_query(db_local, pm_targets); break; + case PM_SYNC: ret = pacman_sync(db_local, pm_targets); break; + case PM_DEPTEST: ret = pacman_deptest(db_local, pm_targets); break; + case PM_MAIN: ret = 0; break; + default: fprintf(stderr, "error: no operation specified (use -h for help)\n\n"); + ret = 1; + } + db_close(db_local); + cleanup(ret); + /* not reached */ + return(0); +} + +int pacman_deptest(pacdb_t *db, PMList *targets) +{ + PMList *list = NULL; + PMList *lp, *deps; + pkginfo_t *dummy; + + if(pmo_d_vertest) { + if(targets && targets->data && targets->next && targets->next->data) { + int ret = rpmvercmp(targets->data, targets->next->data); + printf("%d\n", ret); + return(ret); + } + return(0); + } + + dummy = newpkg(); + sprintf(dummy->name, "_dummy_"); + sprintf(dummy->version, "1.0-1"); + for(lp = targets; lp; lp = lp->next) { + if(lp->data == NULL) continue; + dummy->depends = list_add(dummy->depends, lp->data); + } + list = list_add(list, dummy); + deps = checkdeps(db, PM_ADD, list); + FREELIST(list); + FREEPKG(dummy); + + if(deps) { + /* return 126 = deps were missing, but successfully resolved + * return 127 = deps were missing, and failed to resolve; OR + * = deps were missing, but no resolution was attempted; OR + * = unresolvable conflicts were found + */ + int ret = 126; + PMList *synctargs = NULL; + for(lp = deps; lp; lp = lp->next) { + depmissing_t *miss = (depmissing_t*)lp->data; + if(miss->type == CONFLICT) { + /* we can't auto-resolve conflicts */ + printf("conflict: %s\n", miss->depend.name); + ret = 127; + } else if(miss->type == DEPEND || miss->type == REQUIRED) { + if(!pmo_d_resolve) { + printf("requires: %s", miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: printf("=%s", miss->depend.version); break; + case DEP_GE: printf(">=%s", miss->depend.version); break; + case DEP_LE: printf("<=%s", miss->depend.version); break; + } + printf("\n"); + } + synctargs = list_add(synctargs, strdup(miss->depend.name)); + } + FREE(miss); + lp->data = NULL; + } + FREE(deps); + /* attempt to resolve missing dependencies */ + /* TODO: handle version comparators (eg, glibc>=2.2.5) */ + if(ret == 126 && synctargs != NULL) { + if(!pmo_d_resolve || pacman_sync(db, synctargs)) { + /* error (or -D not used) */ + ret = 127; + } + } + FREELIST(synctargs); + return(ret); + } + return(0); +} + +int pacman_sync(pacdb_t *db, PMList *targets) +{ + int allgood = 1, confirm = 0; + PMList *i, *j, *k; + PMList *rmtargs = NULL; /* conflicting packages to remove */ + PMList *final = NULL; /* packages to upgrade */ + PMList *trail = NULL; /* a breadcrumb trail to avoid running in circles */ + PMList *databases = NULL; + + if(!list_count(pmc_syncs)) { + fprintf(stderr, "error: no usable package repositories configured.\n"); + return(1); + } + + if(pmo_s_clean) { + mode_t oldmask; + + printf("removing packages from cache... "); + if(rmrf("/var/cache/pacman/pkg")) { + fprintf(stderr, "error: could not remove cache directory: %s\n", strerror(errno)); + return(1); + } + + oldmask = umask(0000); + if(makepath("/var/cache/pacman/pkg")) { + fprintf(stderr, "error: could not create new cache directory: %s\n", strerror(errno)); + return(1); + } + umask(oldmask); + + printf("done.\n"); + return(0); + } + + if(pmo_s_sync) { + /* grab a fresh package list */ + printf(":: Synchronizing package databases... \n"); + logaction(NULL, "synchronizing package lists"); + sync_synctree(); + } + + /* open the database(s) */ + for(i = pmc_syncs; i; i = i->next) { + pacdb_t *db_sync = NULL; + dbsync_t *dbs = NULL; + sync_t *sync = (sync_t*)i->data; + + db_sync = db_open(pmo_root, pmo_dbpath, sync->treename); + if(db_sync == NULL) { + fprintf(stderr, "error: could not open sync database: %s\n", sync->treename); + fprintf(stderr, " have you used --refresh yet?\n"); + return(1); + } + MALLOC(dbs, sizeof(dbsync_t)); + dbs->sync = sync; + dbs->db = db_sync; + /* cache packages */ + dbs->pkgcache = db_loadpkgs(db_sync); + databases = list_add(databases, dbs); + } + + final = list_new(); + trail = list_new(); + + if(pmo_s_search) { + /* search sync databases */ + if(targets) { + for(i = targets; i; i = i->next) { + char *targ = strdup(i->data); + strtoupper(targ); + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + char *haystack; + /* check name */ + haystack = strdup(pkg->name); + strtoupper(haystack); + if(strstr(haystack, targ)) { + printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); + indentprint(pkg->desc, 4); + printf("\n"); + } else { + /* check description */ + FREE(haystack); + haystack = strdup(pkg->desc); + strtoupper(haystack); + if(strstr(haystack, targ)) { + printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); + indentprint(pkg->desc, 4); + printf("\n"); + } + } + FREE(haystack); + } + } + FREE(targ); + } + } else { + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); + indentprint(pkg->desc, 4); + printf("\n"); + } + } + } + } else if(pmo_group) { + PMList *pm, *allgroups, *groups; + i = NULL; + /* fetch the list of existing groups */ + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + k = find_groups(dbs->db); + for(pm = k; pm; pm = pm->next) { + if(!is_in((char *)pm->data, i)) { + i = list_add(i, strdup((char *)pm->data)); + } + } + FREELIST(k); + } + allgroups = list_sort(i); + FREELIST(i); + if(targets) { + groups = NULL; + for(j = targets; j; j = j->next) { + if(is_in((char *)j->data, allgroups)) { + groups = list_add(groups, strdup((char *)j->data)); + } + } + FREELIST(allgroups); + } else { + groups = allgroups; + } + /* display the packages belonging to groups */ + for(pm = groups; pm; pm = pm->next) { + PMList *pkg; + printf("%s\n", (char *)pm->data); + if(targets == NULL) { + continue; + } + i = NULL; + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + PMList *l = pkg_ingroup(dbs->db, (char *)pm->data); + i = list_merge(i, l); + FREELIST(l); + } + pkg = list_sort(i); + FREELIST(i); + list_display(" ", pkg); + FREELIST(pkg); + } + FREELIST(groups); + } else if(pmo_s_upgrade) { + int newer = 0; + int ignore = 0; + logaction(NULL, "starting full system upgrade"); + /* check for "recommended" package replacements */ + for(i = databases; i && allgood; i = i->next) { + dbsync_t *dbs = (dbsync_t*)i->data; + for(j = dbs->pkgcache; j; j = j->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + for(k = pkg->replaces; k; k = k->next) { + PMList *m; + for(m = pm_packages; m; m = m->next) { + pkginfo_t *p = (pkginfo_t*)m->data; + if(!strcmp(k->data, p->name)) { + /* if confirmed, add this to the 'final' list, designating 'p' as + * the package to replace. + */ + if(yesno(":: replace %s with %s from \"%s\"? [Y/n] ", p->name, pkg->name, dbs->db->treename)) { + syncpkg_t *sync = NULL; + /* we save the dependency info so we can move p's requiredby stuff + * over to the replacing package + */ + pkginfo_t *q = db_scan(db, p->name, INFRQ_DESC | INFRQ_DEPENDS); + + /* check if pkg->name is already in the final list. */ + sync = find_pkginsync(pkg->name, final); + if(sync) { + /* found it -- just append to the replaces list */ + sync->replaces = list_add(sync->replaces, q); + } else { + /* none found -- enter pkg into the final sync list */ + MALLOC(sync, sizeof(syncpkg_t)); + sync->dbs = dbs; + sync->replaces = NULL; + sync->replaces = list_add(sync->replaces, q); + sync->pkg = db_scan(sync->dbs->db, pkg->name, INFRQ_DESC | INFRQ_DEPENDS); + /* add to the targets list */ + allgood = !resolvedeps(db, databases, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + if(find_pkginsync(sync->pkg->name, final) == NULL) { + final = list_add(final, sync); + } + } + } + break; + } + } + } + } + } + /* match installed packages with the sync dbs and compare versions */ + for(i = pm_packages; i && allgood; i = i->next) { + int cmp, found = 0; + pkginfo_t *local = (pkginfo_t*)i->data; + syncpkg_t *sync = NULL; + MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; + + for(j = databases; !found && j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; !found && k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + if(!strcmp(local->name, pkg->name)) { + found = 1; + sync->pkg = pkg; + sync->dbs = dbs; + } + } + } + if(!found) { + /*fprintf(stderr, "%s: not found in sync db. skipping.", local->name);*/ + FREE(sync); + continue; + } + /* compare versions and see if we need to upgrade */ + cmp = rpmvercmp(local->version, sync->pkg->version); + if(cmp > 0) { + /* local version is newer */ + fprintf(stderr, ":: %s-%s: local version is newer\n", + local->name, local->version); + newer = 1; + FREE(sync); + continue; + } else if(cmp == 0) { + /* versions are identical */ + FREE(sync); + continue; + } else if(is_in((char*)i->data, pmo_ignorepkg)) { + /* package should be ignored (IgnorePkg) */ + fprintf(stderr, ":: %s-%s: ignoring package upgrade (%s)\n", + local->name, local->version, sync->pkg->version); + ignore = 1; + FREE(sync); + continue; + } + + /* re-fetch the package record with dependency info */ + sync->pkg = db_scan(sync->dbs->db, sync->pkg->name, INFRQ_DESC | INFRQ_DEPENDS); + + /* add to the targets list */ + found = (find_pkginsync(sync->pkg->name, final) != NULL); + if(!found) { + allgood = !resolvedeps(db, databases, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + found = (find_pkginsync(sync->pkg->name, final) != NULL); + if(!found) { + final = list_add(final, sync); + } + } + } + if((newer || ignore) && allgood) { + fprintf(stderr, ":: Above packages will be skipped. To manually upgrade use 'pacman -S '\n"); + } + } else { + /* process targets */ + for(i = targets; i && allgood; i = i->next) { + if(i->data) { + int cmp, found = 0; + char *treename; + char *targ; + char *targline; + pkginfo_t *local; + syncpkg_t *sync = NULL; + MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; + + targline = strdup((char*)i->data); + targ = index(targline, '/'); + if(targ) { + *targ = '\0'; + targ++; + treename = targline; + } else { + targ = targline; + treename = NULL; + } + + for(j = databases; !found && j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; !found && k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + if(!strcmp(targ, pkg->name)) { + if(treename == NULL || + (treename && !strcmp(treename, dbs->sync->treename))) { + found = 1; + sync->dbs = dbs; + /* re-fetch the package record with dependency info */ + sync->pkg = db_scan(sync->dbs->db, pkg->name, INFRQ_DESC | INFRQ_DEPENDS); + if(sync->pkg == NULL) { + found = 0; + } + } + } + } + } + if(!found) { + if(treename == NULL) { + /* target not found: check if it's a group */ + k = NULL; + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + PMList *l = pkg_ingroup(dbs->db, targ); + k = list_merge(k, l); + FREELIST(l); + } + if(k != NULL) { + printf(":: group %s:\n", targ); + list_display(" ", k); + if(yesno(" Install whole content? [Y/n] ")) { + targets = list_merge(targets, k); + FREELIST(k); + } else { + PMList *l; + for(l = k; l; l = l->next) { + if(yesno(":: install %s from group %s? [Y/n] ", (char*)l->data, targ)) { + targets = list_add(targets, strdup((char*)l->data)); + } + } + } + FREELIST(k); + } else { + fprintf(stderr, "%s: not found in sync db\n", targ); + allgood = 0; + } + } else { + fprintf(stderr, "%s: not present in \"%s\" repository\n", targ, treename); + allgood = 0; + } + FREE(sync); + FREE(targline); + continue; + } + local = db_scan(db, targ, INFRQ_DESC); + if(local && !pmo_s_downloadonly) { + /* this is an upgrade, compare versions and determine if it is necessary */ + cmp = rpmvercmp(local->version, sync->pkg->version); + if(cmp > 0) { + /* local version is newer - get confirmation first */ + if(!yesno(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] ", local->name, local->version)) { + FREEPKG(local); + FREEPKG(sync->pkg); + FREE(sync); + FREE(targline); + continue; + } + } else if(cmp == 0) { + /* versions are identical */ + if(!yesno(":: %s-%s: is up to date. Upgrade anyway? [Y/n] ", local->name, local->version)) { + FREEPKG(local); + FREEPKG(sync->pkg); + FREE(sync); + FREE(targline); + continue; + } + } + } + FREEPKG(local); + + found = (find_pkginsync(sync->pkg->name, final) != NULL); + if(!found && !pmo_nodeps) { + allgood = !resolvedeps(db, databases, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + found = (find_pkginsync(sync->pkg->name, final) != NULL); + } + if(!found) { + final = list_add(final, sync); + } + } + } + } + + if(allgood && !pmo_s_search) { + /* check for inter-conflicts and whatnot */ + if(!pmo_nodeps && !pmo_s_downloadonly) { + int errorout = 0; + PMList *deps = NULL; + PMList *list = NULL; + + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + if(s) { + list = list_add(list, s->pkg); + } + } + deps = checkdeps(db, PM_UPGRADE, list); + if(deps) { + for(i = deps; i; i = i->next) { + depmissing_t *miss = (depmissing_t*)i->data; + if(miss->type == DEPEND || miss->type == REQUIRED) { + if(!errorout) { + fprintf(stderr, "error: unresolvable dependencies:\n"); + errorout = 1; + } + fprintf(stderr, " %s: requires %s", miss->target, miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: fprintf(stderr, "=%s", miss->depend.version); break; + case DEP_GE: fprintf(stderr, ">=%s", miss->depend.version); break; + case DEP_LE: fprintf(stderr, "<=%s", miss->depend.version); break; + } + if(miss->type == DEPEND) { + fprintf(stderr, " but it is not in the sync db\n"); + } else { + fprintf(stderr, "\n"); + } + } + } + if(!errorout) { + int found; + errorout = 0; + /* no unresolvable deps, so look for conflicts */ + for(i = deps; i && !errorout; i = i->next) { + depmissing_t *miss = (depmissing_t*)i->data; + if(miss->type == CONFLICT) { + /* check if the conflicting package is one that's about to be removed/replaced. + * if so, then just ignore it + */ + found = 0; + for(j = final; j && !found; j = j->next) { + syncpkg_t *sync = (syncpkg_t*)j->data; + for(k = sync->replaces; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + if(!strcmp(p->name, miss->depend.name)) { + found = 1; + } + } + } + /* if we didn't find it in any sync->replaces lists, then it's a conflict */ + if(!found && !is_in(miss->depend.name, rmtargs)) { + pkginfo_t p1; + /* build a "fake" pkginfo_t so we can search with is_pkgin() */ + snprintf(p1.name, sizeof(p1.name), miss->depend.name); + sprintf(p1.version, "1.0-1"); + + if(is_pkgin(&p1, pm_packages)) { + if(yesno(":: %s conflicts with %s. Remove %s? [Y/n] ", + miss->target, miss->depend.name, miss->depend.name)) { + /* remove miss->depend.name */ + rmtargs = list_add(rmtargs, strdup(miss->depend.name)); + } else { + /* abort */ + fprintf(stderr, "\nerror: package conflicts detected\n"); + errorout = 1; + } + } else { + if(!is_in(miss->depend.name, rmtargs) & !is_in(miss->target, rmtargs)) { + fprintf(stderr, "\nerror: %s conflicts with %s\n", miss->target, + miss->depend.name); + errorout = 1; + } + } + } + } + } + } + list_free(deps); + if(errorout) { + /* abort mission */ + allgood = 0; + } + } + /* cleanup */ + for(i = list; i; i = i->next) { + i->data = NULL; + } + FREELIST(list); + } + + /* 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. */ + k = NULL; + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + int keepit = 1; + for(j = rmtargs; j && keepit; j = j->next) { + if(!strcmp(j->data, s->pkg->name)) { + FREE(i->data); + keepit = 0; + } + } + if(keepit) { + k = list_add(k, s); + } + i->data = NULL; + } + FREELIST(final); + final = k; + + /* list targets */ + if(final && final->data && allgood) { + PMList *list = NULL; + char *str; + for(i = rmtargs; i; i = i->next) { + list = list_add(list, strdup(i->data)); + } + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + for(j = s->replaces; j; j = j->next) { + pkginfo_t *p = (pkginfo_t*)j->data; + list = list_add(list, strdup(p->name)); + } + } + if(list) { + printf("\nRemove: "); + str = buildstring(list); + indentprint(str, 9); + printf("\n"); + FREELIST(list); + FREE(str); + } + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + if(s && s->pkg) { + char *str = NULL; + MALLOC(str, strlen(s->pkg->name)+strlen(s->pkg->version)+2); + sprintf(str, "%s-%s", s->pkg->name, s->pkg->version); + list = list_add(list, str); + } + } + printf("\nTargets: "); + str = buildstring(list); + indentprint(str, 9); + printf("\n"); + FREELIST(list); + FREE(str); + } + + /* get confirmation */ + confirm = 0; + if(allgood && final && final->data) { + if(pmo_s_downloadonly) { + confirm = yesno("\nProceed with download? [Y/n] "); + } else { + /* don't get any confirmation if we're called from makepkg */ + if(pmo_d_resolve) { + confirm = 1; + } else { + confirm = yesno("\nProceed with upgrade? [Y/n] "); + } + } + } + } + + if(allgood && confirm && final && final->data) { + char ldir[PATH_MAX]; + int varcache = 1; + int done = 0; + int count = 0; + sync_t *current = NULL; + PMList *processed = NULL; + PMList *files = NULL; + + snprintf(ldir, PATH_MAX, "%svar/cache/pacman/pkg", pmo_root); + + /* group sync records by repository and download */ + while(!done) { + if(current) { + processed = list_add(processed, current); + current = NULL; + } + for(i = final; i; i = i->next) { + syncpkg_t *sync = (syncpkg_t*)i->data; + if(current == NULL) { + /* we're starting on a new repository */ + if(!list_isin(processed, sync->dbs->sync)) { + current = sync->dbs->sync; + } + } + if(current && !strcmp(current->treename, sync->dbs->sync->treename)) { + struct stat buf; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s/%s-%s.pkg.tar.gz", + ldir, sync->pkg->name, sync->pkg->version); + if(stat(path, &buf)) { + /* file is not in the cache dir, so add it to the list */ + snprintf(path, PATH_MAX, "%s-%s.pkg.tar.gz", sync->pkg->name, sync->pkg->version); + files = list_add(files, strdup(path)); + } else { + vprint(" %s-%s.pkg.tar.gz is already in the cache\n", sync->pkg->name, sync->pkg->version); + count++; + } + } + } + + if(files) { + struct stat buf; + + printf("\n:: Retrieving packages from %s...\n", current->treename); + fflush(stdout); + if(stat(ldir, &buf)) { + mode_t oldmask; + char parent[PATH_MAX]; + + /* no cache directory.... try creating it */ + snprintf(parent, PATH_MAX, "%svar/cache/pacman", pmo_root); + logaction(stderr, "warning: no %s cache exists. creating...", ldir); + oldmask = umask(0000); + mkdir(parent, 0755); + if(mkdir(ldir, 0755)) { + /* couldn't mkdir the cache directory, so fall back to /tmp and unlink + * the package afterwards. + */ + logaction(stderr, "warning: couldn't create package cache, using /tmp instead"); + snprintf(ldir, PATH_MAX, "/tmp"); + varcache = 0; + } + umask(oldmask); + } + if(downloadfiles(current->servers, ldir, files)) { + fprintf(stderr, "error: failed to retrieve some files from %s\n", current->treename); + allgood = 0; + } + count += list_count(files); + FREELIST(files); + } + if(count == list_count(final)) { + done = 1; + } + } + printf("\n"); + + /* double-check */ + FREELIST(files); + + if(allgood) { + /* Check integrity of files */ + printf("checking package integrity... "); + fflush(stdout); + + for(i = final; i; i = i->next) { + syncpkg_t *sync; + char str[PATH_MAX], pkgname[PATH_MAX]; + char *md5sum1, *md5sum2; + + sync = (syncpkg_t*)i->data; + snprintf(pkgname, PATH_MAX, "%s-%s.pkg.tar.gz", sync->pkg->name, sync->pkg->version); + + md5sum1 = sync->pkg->md5sum; + if(md5sum1 == NULL || md5sum1[0] == '\0') { + if(allgood) { + printf("\n"); + } + fprintf(stderr, "error: can't get md5 checksum for package %s\n", pkgname); + allgood = 0; + continue; + } + snprintf(str, PATH_MAX, "%s/%s", ldir, pkgname); + md5sum2 = MDFile(str); + if(md5sum2 == NULL || md5sum2[0] == '\0') { + if(allgood) { + printf("\n"); + } + fprintf(stderr, "error: can't get md5 checksum for archive %s\n", pkgname); + allgood = 0; + continue; + } + + if(strcmp(md5sum1, md5sum2) != 0) { + if(allgood) { + printf("\n"); + } + fprintf(stderr, "error: archive %s is corrupted\n", pkgname); + allgood = 0; + } + + FREE(md5sum2); + } + if(allgood) { + printf("done.\n"); + } else { + fprintf(stderr, "\n"); + } + } + + if(!pmo_s_downloadonly && allgood) { + /* remove any conflicting packages (WITH dep checks) */ + if(rmtargs) { + int retcode; + retcode = pacman_remove(db, rmtargs); + FREELIST(rmtargs); + if(retcode == 1) { + fprintf(stderr, "\nupgrade aborted.\n"); + allgood = 0; + } + /* reload package cache */ + FREELISTPKGS(pm_packages); + pm_packages = db_loadpkgs(db); + } + FREELIST(rmtargs); + for(i = final; allgood && i; i = i->next) { + char *str; + syncpkg_t *sync = (syncpkg_t*)i->data; + if(sync->pkg) { + MALLOC(str, PATH_MAX); + snprintf(str, PATH_MAX, "%s/%s-%s.pkg.tar.gz", ldir, sync->pkg->name, sync->pkg->version); + files = list_add(files, str); + } + for(j = sync->replaces; j; j = j->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + rmtargs = list_add(rmtargs, strdup(pkg->name)); + } + } + /* remove to-be-replaced packages */ + if(allgood && rmtargs) { + int oldval = pmo_nodeps; + /* we make pacman_remove() skip dependency checks by setting pmo_nodeps high */ + pmo_nodeps = 1; + allgood = !pacman_remove(db, rmtargs); + pmo_nodeps = oldval; + if(!allgood) { + fprintf(stderr, "package removal failed. aborting...\n"); + } + } + /* install targets */ + if(allgood) { + allgood = !pacman_upgrade(db, files); + } + /* propagate replaced packages' requiredby fields to their new owners */ + if(allgood) { + 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_ALL); + 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_ALL); + 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); + + /* add the new requiredby */ + new->requiredby = list_add(new->requiredby, strdup(k->data)); + } + } + } + db_write(db, new); + FREEPKG(new); + } + } + } + } + + if(!varcache && !pmo_s_downloadonly && allgood) { + /* delete packages */ + for(i = files; i; i = i->next) { + unlink(i->data); + } + } + } + + /* cleanup */ + for(i = final; i; i = i->next) { + syncpkg_t *sync = (syncpkg_t*)i->data; + if(sync) { + FREEPKG(sync->pkg); + FREELISTPKGS(sync->replaces); + } + FREE(sync); + i->data = NULL; + } + for(i = trail; i; i = i->next) { + /* this list used the same pointers as final, so they're already freed */ + i->data = NULL; + } + for(i = databases; i; i = i->next) { + dbsync_t *dbs = (dbsync_t*)i->data; + db_close(dbs->db); + dbs->db = NULL; + FREELISTPKGS(dbs->pkgcache); + FREE(dbs); + i->data = NULL; + } + FREELIST(databases); + FREELIST(final); + FREELIST(trail); + FREELIST(rmtargs); + return(!allgood); +} + +int pacman_add(pacdb_t *db, PMList *targets) +{ + int i, ret = 0, errors = 0; + TAR *tar = NULL; + char expath[PATH_MAX]; + char pm_install[PATH_MAX]; + pkginfo_t *info = NULL; + struct stat buf; + PMList *targ, *file, *lp, *j, *k; + PMList *alltargs = NULL; + PMList *filenames = NULL; + unsigned short real_pmo_upgrade; + tartype_t gztype = { + (openfunc_t) gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t) gzread, + (writefunc_t)gzwrite + }; + + if(targets == NULL) { + return(0); + } + + printf("loading package data... "); + fflush(stdout); + for(targ = targets; targ; targ = targ->next) { + /* Populate the package struct */ + vprint("reading %s... ", (char*)targ->data); + info = load_pkg((char*)targ->data, 0); + if(info == NULL) { + return(1); + } + if(pmo_freshen) { + /* only upgrade/install this package if it is already installed and at a lesser version */ + pkginfo_t *dummy = db_scan(db, info->name, INFRQ_DESC); + if(dummy == NULL || rpmvercmp(dummy->version, info->version) >= 0) { + vprint("not installed or lesser version\n"); + FREEPKG(info); + FREEPKG(dummy); + continue; + } + FREEPKG(dummy); + } + /* check if an older version of said package is already in alltargs. + * if so, replace it in the list */ + for(j = alltargs, k = filenames; j && j->data && k; j = j->next, k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + if(!strcmp(pkg->name, info->name)) { + if(rpmvercmp(pkg->version, info->version) < 0) { + vprint("replacing older version in target list. "); + FREEPKG(j->data); + j->data = info; + FREE(k->data); + k->data = strdup(targ->data); + } + } + } + vprint("done\n"); + alltargs = list_add(alltargs, info); + filenames = list_add(filenames, strdup(targ->data)); + } + printf("done.\n"); + + /* No need to check deps if pacman_add was called during a sync: + * it is already done in pacman_sync. */ + if(!pmo_nodeps && pmo_op != PM_SYNC) { + int errorout = 0; + vprint("checking dependencies...\n"); + lp = checkdeps(db, (pmo_upgrade ? PM_UPGRADE : PM_ADD), alltargs); + if(lp) { + /* look for unsatisfied dependencies */ + for(j = lp; j; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + if(miss->type == DEPEND || miss->type == REQUIRED) { + if(!errorout) { + fprintf(stderr, "error: unsatisfied dependencies:\n"); + errorout = 1; + } + printf(" %s: requires %s", miss->target, miss->depend.name); + switch(miss->depend.mod) { + case DEP_EQ: printf("=%s", miss->depend.version); break; + case DEP_GE: printf(">=%s", miss->depend.version); break; + case DEP_LE: printf("<=%s", miss->depend.version); break; + } + printf("\n"); + } + } + if(!errorout) { + PMList *rmtargs = NULL; + errorout = 0; + /* no unsatisfied deps, so look for conflicts */ + for(j = lp; j && !errorout; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + if(miss->type == CONFLICT && !is_in(miss->depend.name, rmtargs)) { + pkginfo_t p1; + /* build a "fake" pkginfo_t so we can search with is_pkgin() */ + snprintf(p1.name, sizeof(p1.name), miss->depend.name); + sprintf(p1.version, "1.0-1"); + + if(is_pkgin(&p1, pm_packages)) { + if(yesno(":: %s conflicts with %s. Remove %s? [Y/n] ", + miss->target, miss->depend.name, miss->depend.name)) { + /* remove miss->depend.name */ + rmtargs = list_add(rmtargs, strdup(miss->depend.name)); + } else { + /* abort */ + fprintf(stderr, "\nerror: package conflicts detected\n"); + errorout = 1; + } + } else { + if(!is_in(miss->depend.name, rmtargs) & !is_in(miss->target, rmtargs)) { + fprintf(stderr, "\nerror: %s conflicts with %s\n", miss->target, + miss->depend.name); + errorout = 1; + } + } + } + } + if(rmtargs && !errorout) { + int retcode; + int oldupg = pmo_upgrade; + /* any packages in rmtargs need to be removed from alltargs. */ + /* rather than ripping out nodes from alltargs, we just copy over */ + /* our "good" nodes to a new list and reassign. */ + k = NULL; + for(lp = alltargs; lp; lp = lp->next) { + pkginfo_t *p = (pkginfo_t*)lp->data; + int keepit = 1; + for(j = rmtargs; j && keepit; j = j->next) { + if(!strcmp(j->data, p->name)) { + /* gone! */ + FREE(lp->data); + keepit = 0; + } + } + if(keepit) { + k = list_add(k, p); + } + lp->data = NULL; + } + FREELIST(alltargs); + alltargs = k; + /* make sure pacman_remove does it's own dependency check */ + pmo_upgrade = 0; + retcode = pacman_remove(db, rmtargs); + list_free(rmtargs); + if(retcode == 1) { + fprintf(stderr, "\n%s aborted.\n", oldupg ? "upgrade" : "install"); + return(1); + } + /* reload package cache */ + FREELISTPKGS(pm_packages); + pm_packages = db_loadpkgs(db); + pmo_upgrade = oldupg; + } + } + if(errorout) { + FREELIST(lp); + return(1); + } + list_free(lp); + } + + /* re-order w.r.t. dependencies */ + vprint("sorting by dependencies\n"); + lp = sortbydeps(alltargs); + /* free the old alltargs */ + for(j = alltargs; j; j = j->next) { + j->data = NULL; + } + FREELIST(alltargs); + alltargs = lp; + } + + if(!pmo_force) { + printf("checking for file conflicts... "); + fflush(stdout); + lp = db_find_conflicts(db, alltargs, pmo_root); + if(lp) { + printf("\nerror: the following file conflicts were found:\n"); + for(j = lp; j; j = j->next) { + printf(" %s\n", (char*)j->data); + } + printf("\n"); + FREELIST(lp); + return(1); + } + printf("done.\n"); + FREELIST(lp); + } + + /* this can get modified in the next for loop, so we reset it on each iteration */ + real_pmo_upgrade = pmo_upgrade; + + for(targ = alltargs, file = filenames; targ && file; targ = targ->next, file = file->next) { + pkginfo_t* oldpkg = NULL; + info = (pkginfo_t*)targ->data; + errors = 0; + + pmo_upgrade = real_pmo_upgrade; + /* check for an already installed package */ + if(!pmo_upgrade && is_pkgin(info, pm_packages)) { + fprintf(stderr, "error: %s is already installed. (try --upgrade)\n", info->name); + continue; + } + + /* see if this is an upgrade. if so, remove the old package first */ + if(pmo_upgrade) { + if(is_pkgin(info, pm_packages)) { + PMList* tmp = list_new(); + int retcode; + + printf("upgrading %s... ", info->name); + /* we'll need the full record for backup checks later */ + oldpkg = db_scan(db, info->name, INFRQ_ALL); + + if(oldpkg) { + list_add(tmp, strdup(info->name)); + vprint("removing old package first...\n"); + retcode = pacman_remove(db, tmp); + list_free(tmp); + if(retcode == 1) { + fprintf(stderr, "\nupgrade aborted.\n"); + return(1); + } + /* reload package cache */ + FREELISTPKGS(pm_packages); + pm_packages = db_loadpkgs(db); + } + } else { + /* no previous package version is installed, so this is actually just an + * install + */ + pmo_upgrade = 0; + } + } + if(!pmo_upgrade) { + printf("installing %s... ", info->name); + } + fflush(stdout); + + /* open the .tar.gz package */ + if(tar_open(&tar, (char*)file->data, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + perror("could not open package"); + return(1); + } + vprint("extracting files...\n"); + for(i = 0; !th_read(tar); i++) { + int nb = 0; + int notouch = 0; + char *md5_orig = NULL; + char pathname[PATH_MAX]; + 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/%s-%s/install", pmo_root, + pmo_dbpath, db->treename, info->name, info->version); + } else { + /* build the new pathname relative to pmo_root */ + snprintf(expath, PATH_MAX, "%s%s", pmo_root, pathname); + } + + if(!stat(expath, &buf) && !S_ISDIR(buf.st_mode)) { + /* file already exists */ + if(is_in(pathname, pmo_noupgrade)) { + notouch = 1; + } else { + if(!pmo_upgrade || oldpkg == NULL) { + nb = is_in(pathname, info->backup); + } else { + /* op == PM_UPGRADE */ + md5_orig = needbackup(pathname, oldpkg->backup); + if(md5_orig) { + 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)) { + logaction(stderr, "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; + } + } + + vprint("checking md5 hashes for %s\n", expath); + vprint(" current: %s\n", md5_local); + vprint(" new: %s\n", md5_pkg); + if(md5_orig) { + vprint(" original: %s\n", 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)) { + logaction(stderr, "error: could not rename %s: %s", expath, strerror(errno)); + } + if(copyfile(temp, expath)) { + logaction(stderr, "error: could not copy %s to %s: %s", temp, expath, strerror(errno)); + errors++; + } else { + logaction(stderr, "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)) { + vprint(" action: installing new file\n"); + installnew = 1; + } else { + vprint(" action: installing new file\n"); + installnew = 1; + } + } else if(!strcmp(md5_orig, md5_pkg)) { + vprint(" action: leaving existing file in place\n"); + } else if(!strcmp(md5_local, md5_pkg)) { + vprint(" action: installing new file\n"); + installnew = 1; + } else { + char newpath[PATH_MAX]; + vprint(" action: saving current file and installing new one\n"); + installnew = 1; + snprintf(newpath, PATH_MAX, "%s.pacsave", expath); + if(rename(expath, newpath)) { + logaction(stderr, "error: could not rename %s: %s", expath, strerror(errno)); + } else { + logaction(stderr, "warning: %s saved as %s", expath, newpath); + } + } + + if(installnew) { + /*vprint(" %s\n", expath);*/ + if(copyfile(temp, expath)) { + fprintf(stderr, "error: could not copy %s to %s: %s\n", temp, expath, strerror(errno)); + errors++; + } + } + } + + FREE(md5_local); + FREE(md5_pkg); + FREE(md5_orig); + unlink(temp); + FREE(temp); + } else { + if(!notouch) { + /*vprint(" %s\n", expath);*/ + } else { + vprint("%s is in NoUpgrade - skipping\n", pathname); + strncat(expath, ".pacnew", PATH_MAX); + logaction(stderr, "warning: extracting %s%s as %s", pmo_root, pathname, expath); + /*tar_skip_regfile(tar);*/ + } + if(tar_extract_file(tar, expath)) { + logaction(stderr, "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", pmo_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; + logaction(stderr, "errors occurred while %s %s", + (pmo_upgrade ? "upgrading" : "installing"), info->name); + +/* XXX: this "else" is disabled so the db_write() ALWAYS occurs. If it doesn't + * packages can get lost during an upgrade. + */ + } /*else*/ { + time_t t = time(NULL); + + /* Update the requiredby field by scaning the whole database + * looking for packages depending on the package to add */ + for(lp = pm_packages; lp; lp = lp->next) { + pkginfo_t *tmpp = NULL; + PMList *tmppm = NULL; + + tmpp = db_scan(db, ((pkginfo_t*)lp->data)->name, INFRQ_DEPENDS); + if(tmpp == NULL) { + continue; + } + for(tmppm = tmpp->depends; tmppm; tmppm = tmppm->next) { + depend_t depend; + if(splitdep(tmppm->data, &depend)) { + continue; + } + if(tmppm->data && !strcmp(depend.name, info->name)) { + info->requiredby = list_add(info->requiredby, strdup(tmpp->name)); + continue; + } + } + } + + vprint("Updating database..."); + /* make an install date (in UTC) */ + strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate)); + if(db_write(db, info)) { + logaction(stderr, "error updating database for %s!", info->name); + return(1); + } + vprint("done.\n"); + if(pmo_upgrade && oldpkg) { + logaction(NULL, "upgraded %s (%s -> %s)", info->name, + oldpkg->version, info->version); + } else { + logaction(NULL, "installed %s (%s)", info->name, info->version); + } + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + pkginfo_t *depinfo = NULL; + depend_t depend; + + if(splitdep(lp->data, &depend)) { + continue; + } + depinfo = db_scan(db, depend.name, INFRQ_ALL); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = whatprovides(db, depend.name); + if(provides) { + /* use the first one */ + depinfo = db_scan(db, provides->data, INFRQ_ALL); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + continue; + } + } + depinfo->requiredby = list_add(depinfo->requiredby, strdup(info->name)); + db_write(db, depinfo); + freepkg(depinfo); + } + printf("done.\n"); fflush(stdout); + + /* run the post-install script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, pmo_dbpath, db->treename, info->name, info->version); + if(!stat(pm_install, &buf)) { + char cmdline[PATH_MAX+1]; + snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", pmo_dbpath, db->treename, info->name, info->version); + vprint("Executing post-install script...\n"); + snprintf(cmdline, PATH_MAX, "chroot %s /bin/sh %s post_%s %s %s", pmo_root, pm_install, + (pmo_upgrade ? "upgrade" : "install"), info->version, ((pmo_upgrade && oldpkg) ? oldpkg->version : "")); + system(cmdline); + } + } + + freepkg(oldpkg); + } + + /* clean up */ + for(lp = alltargs; lp; lp = lp->next) { + FREEPKG(lp->data); + } + FREELIST(alltargs); + FREELIST(filenames); + + /* run ldconfig if it exists */ + snprintf(expath, PATH_MAX, "%setc/ld.so.conf", pmo_root); + if(!stat(expath, &buf)) { + snprintf(expath, PATH_MAX, "%ssbin/ldconfig", pmo_root); + if(!stat(expath, &buf)) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "%s -r %s", expath, pmo_root); + vprint("running \"%s\"\n", cmd); + system(cmd); + } + } + + return(ret); +} + +int pacman_remove(pacdb_t *db, PMList *targets) +{ + char line[PATH_MAX+1]; + char pm_install[PATH_MAX+1]; + pkginfo_t *info = NULL; + pkginfo_t *depinfo = NULL; + struct stat buf; + char *newpath = NULL; + depend_t depend; + PMList *alltargs = NULL; + PMList *targ, *lp, *j; + + if(targets == NULL) { + return(0); + } + + /* load package info from all targets */ + for(lp = targets; lp; lp = lp->next) { + info = db_scan(db, (char*)lp->data, INFRQ_ALL); + if(info == NULL) { + PMList *groups; + /* if the target is a group, ask if its packages should be removed */ + groups = find_groups(db); + if(is_in((char *)lp->data, groups)) { + PMList *pkgs = pkg_ingroup(db, (char *)lp->data); + printf(":: group %s:\n", (char*)lp->data); + list_display(" ", pkgs); + if(yesno(" Remove whole content? [Y/n] ")) { + for(j = pkgs; j; j = j->next) { + info = db_scan(db, (char *)j->data, INFRQ_ALL); + alltargs = list_add(alltargs, info); + } + } else { + for(j = pkgs; j; j = j->next) { + if(yesno(":: remove %s from group %s? [Y/n] ", (char*)j->data, (char*)lp->data)) { + info = db_scan(db, (char *)j->data, INFRQ_ALL); + alltargs = list_add(alltargs, info); + } + } + } + FREELIST(pkgs); + FREELIST(groups); + continue; + } + FREELIST(groups); + fprintf(stderr, "error: could not find %s in database\n", (char*)lp->data); + return(1); + } + alltargs = list_add(alltargs, info); + } + if(!pmo_nodeps && !pmo_upgrade) { + vprint("checking dependencies...\n"); + lp = checkdeps(db, PM_REMOVE, alltargs); + if(lp) { + if(pmo_r_cascade) { + while(lp) { + for(j = lp; j; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + info = db_scan(db, miss->depend.name, INFRQ_ALL); + if(!is_pkgin(info, alltargs)) { + list_add(alltargs, info); + } + } + list_free(lp); + lp = checkdeps(db, PM_REMOVE, alltargs); + } + /* list targets */ + list_display("\nTargets:", alltargs); + /* get confirmation */ + if(yesno("\nDo you want to remove these packages? [Y/n] ") == 0) { + list_free(alltargs); + list_free(lp); + return(1); + } + } else { + fprintf(stderr, "error: this will break the following dependencies:\n"); + for(j = lp; j; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + printf(" %s: is required by %s\n", miss->target, miss->depend.name); + } + list_free(alltargs); + list_free(lp); + return(1); + } + } + list_free(lp); + } + + for(targ = alltargs; targ; targ = targ->next) { + info = (pkginfo_t*)targ->data; + + if(!pmo_upgrade) { + printf("removing %s... ", info->name); + fflush(stdout); + /* run the pre-remove script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, pmo_dbpath, db->treename, info->name, info->version); + if(!stat(pm_install, &buf)) { + vprint("Executing pre-remove script...\n"); + snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", pmo_dbpath, db->treename, info->name, info->version); + snprintf(line, PATH_MAX, "chroot %s /bin/sh %s pre_remove %s", pmo_root, pm_install, info->version); + + system(line); + } + } + + /* iterate through the list backwards, unlinking files */ + for(lp = list_last(info->files); lp; lp = lp->prev) { + int nb = 0; + if(needbackup((char*)lp->data, info->backup)) { + nb = 1; + } + if(!nb && pmo_upgrade) { + /* check pmo_noupgrade */ + if(is_in((char*)lp->data, pmo_noupgrade)) { + nb = 1; + } + } + snprintf(line, PATH_MAX, "%s%s", pmo_root, (char*)lp->data); + if(lstat(line, &buf)) { + vprint("file %s does not exist\n", line); + continue; + } + if(S_ISDIR(buf.st_mode)) { + /*vprint(" removing directory %s\n", 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(pmo_upgrade) { + /* we're upgrading so just leave the file as is. pacman_add() will handle it */ + } else { + if(!pmo_nosave) { + newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1); + sprintf(newpath, "%s.pacsave", line); + rename(line, newpath); + logaction(stderr, "warning: %s saved as %s", line, newpath); + } else { + /*vprint(" unlinking %s\n", line);*/ + if(unlink(line)) { + perror("cannot remove file"); + } + } + } + } else { + /*vprint(" unlinking %s\n", line);*/ + if(unlink(line)) { + perror("cannot remove file"); + } + } + } + } + + /* remove the package from the database */ + snprintf(line, PATH_MAX, "%s%s/%s/%s-%s", pmo_root, pmo_dbpath, db->treename, + info->name, info->version); + + /* DESC */ + snprintf(pm_install, PATH_MAX, "%s/desc", line); + unlink(pm_install); + /* FILES */ + snprintf(pm_install, PATH_MAX, "%s/files", line); + unlink(pm_install); + /* DEPENDS */ + snprintf(pm_install, PATH_MAX, "%s/depends", line); + unlink(pm_install); + /* INSTALL */ + snprintf(pm_install, PATH_MAX, "%s/install", line); + unlink(pm_install); + /* directory */ + rmdir(line); + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + PMList *last, *j; + + if(splitdep((char*)lp->data, &depend)) { + continue; + } + depinfo = db_scan(db, depend.name, INFRQ_ALL); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = 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_ALL); + list_free(provides); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + list_free(provides); + continue; + } + } + /* splice out this entry from requiredby */ + last = list_last(depinfo->requiredby); + 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; + list_free(j); + break; + } + } + db_write(db, depinfo); + freepkg(depinfo); + } + if(!pmo_upgrade) { + printf("done.\n"); + logaction(NULL, "removed %s (%s)", info->name, info->version); + } + } + + FREELIST(alltargs); + + /* run ldconfig if it exists */ + snprintf(line, PATH_MAX, "%setc/ld.so.conf", pmo_root); + if(!stat(line, &buf)) { + snprintf(line, PATH_MAX, "%ssbin/ldconfig", pmo_root); + if(!stat(line, &buf)) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "%s -r %s", line, pmo_root); + vprint("running \"%s\"\n", cmd); + system(cmd); + } + } + + return(0); +} + +int pacman_query(pacdb_t *db, PMList *targets) +{ + char *package = NULL; + char path[PATH_MAX+1]; + pkginfo_t *info = NULL; + PMList *targ, *lp, *q; + int done = 0; + + for(targ = targets; !done; targ = (targ ? targ->next : NULL)) { + if(targets == NULL) { + done = 1; + } else { + if(targ->next == NULL) { + done = 1; + } + package = (char*)targ->data; + } + + /* looking for groups */ + if(pmo_group) { + PMList *pkg, *groups; + groups = find_groups(db); + if(package == NULL) { + for(lp = groups; lp; lp = lp->next) { + pkg = pkg_ingroup(db, (char *)lp->data); + for(q = pkg; q; q = q->next) { + printf("%s %s\n", (char *)lp->data, (char *)q->data); + } + FREELIST(pkg); + } + } else { + if(!is_in(package, groups)) { + fprintf(stderr, "Group \"%s\" was not found.\n", package); + FREELIST(groups); + return(2); + } + pkg = pkg_ingroup(db, package); + for(q = pkg; q; q = q->next) { + printf("%s %s\n", package, (char *)q->data); + } + FREELIST(pkg); + } + FREELIST(groups); + continue; + } + + /* output info for a .tar.gz package */ + if(pmo_q_isfile) { + if(package == NULL) { + fprintf(stderr, "error: no package file was specified for --file\n"); + return(1); + } + info = load_pkg(package, pmo_q_info); + if(info == NULL) { + fprintf(stderr, "error: %s is not a package\n", package); + return(1); + } + if(pmo_q_info) { + printf("\n"); + } else if(pmo_q_list) { + for(lp = info->files; lp; lp = lp->next) { + printf("%s %s\n", info->name, (char*)lp->data); + } + } else { + printf("%s %s\n", info->name, info->version); + } + FREEPKG(info); + continue; + } + + /* determine the owner of a file */ + if(pmo_q_owns) { + char rpath[PATH_MAX]; + if(package == NULL) { + fprintf(stderr, "error: no file was specified for --owns\n"); + return(1); + } + if(realpath(package, rpath)) { + int gotcha = 0; + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL && !gotcha) { + for(lp = info->files; lp && !gotcha; lp = lp->next) { + sprintf(path, "%s%s", pmo_root, (char*)lp->data); + if(!strcmp(path, rpath)) { + printf("%s is owned by %s %s\n", package, info->name, info->version); + gotcha = 1; + } + } + FREEPKG(info); + } + if(!gotcha) { + fprintf(stderr, "No package owns %s\n", package); + } + continue; + } else { + fprintf(stderr, "error: %s is not a file.\n", package); + return(1); + } + } + + /* find packages in the db */ + if(package == NULL) { + /* no target */ + for(lp = pm_packages; lp; lp = lp->next) { + pkginfo_t *tmpp = (pkginfo_t*)lp->data; + if(pmo_q_list) { + info = db_scan(db, tmpp->name, INFRQ_DESC | INFRQ_FILES); + if(info == NULL) { + /* something weird happened */ + return(1); + } + for(q = info->files; q; q = q->next) { + printf("%s %s%s\n", info->name, pmo_root, (char*)q->data); + } + FREEPKG(info); + } else if(pmo_q_orphans) { + info = db_scan(db, tmpp->name, INFRQ_DESC | INFRQ_DEPENDS); + if(info == NULL) { + return(1); + } + if(info->requiredby == NULL) { + printf("%s %s\n", tmpp->name, tmpp->version); + } + FREEPKG(info); + } else { + printf("%s %s\n", tmpp->name, tmpp->version); + } + } + } else { + /* find a target */ + if(pmo_q_info) { + info = db_scan(db, package, INFRQ_ALL); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + dump_pkg(info); + if(pmo_q_info > 1 && info->backup) { + /* extra info */ + printf("\n"); + for(lp = info->backup; lp; lp = lp->next) { + struct stat buf; + char path[PATH_MAX]; + char *md5sum; + char *str = strdup(lp->data); + char *ptr = index(str, '\t'); + if(ptr == NULL) { + FREE(str); + continue; + } + *ptr = '\0'; + ptr++; + snprintf(path, PATH_MAX-1, "%s%s", pmo_root, str); + if(!stat(path, &buf)) { + md5sum = MDFile(path); + if(md5sum == NULL) { + fprintf(stderr, "error calculating md5sum for %s\n", path); + continue; + } + if(strcmp(md5sum, ptr)) { + printf("MODIFIED\t%s\n", path); + } else { + printf("NOT MODIFIED\t%s\n", path); + } + } else { + printf("MISSING\t\t%s\n", path); + } + FREE(str); + } + } + printf("\n"); + } else if(pmo_q_list) { + info = db_scan(db, package, INFRQ_DESC | INFRQ_FILES); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + for(lp = info->files; lp; lp = lp->next) { + printf("%s %s%s\n", info->name, pmo_root, (char*)lp->data); + } + } else if(pmo_q_orphans) { + info = db_scan(db, package, INFRQ_DESC | INFRQ_DEPENDS); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + if(info->requiredby == NULL) { + printf("%s %s\n", info->name, info->version); + } + } else { + info = db_scan(db, package, INFRQ_DESC); + if(info == NULL) { + fprintf(stderr, "Package \"%s\" was not found.\n", package); + return(2); + } + printf("%s %s\n", info->name, info->version); + } + FREEPKG(info); + } + } + + return(0); +} + +int pacman_upgrade(pacdb_t *db, PMList *targets) +{ + /* this is basically just a remove-then-add process. pacman_add() will */ + /* handle it */ + pmo_upgrade = 1; + return(pacman_add(db, targets)); +} + +/* 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; + + /* count the number of targets */ + numtargs = list_count(targets); + + while(change) { + change = 0; + if(numscans > numtargs) { + vprint("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) { + pkginfo_t *p = (pkginfo_t*)i->data; + for(j = p->depends; j; j = j->next) { + depend_t dep; + int found = 0; + pkginfo_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 = (pkginfo_t*)k->data; + if(!strcmp(dep.name, q->name)) { + found = 1; + } + } + if(found) { + if(!is_pkgin(q, newtargs)) { + change = 1; + newtargs = list_add(newtargs, q); + } + } + } + if(!is_pkgin(p, newtargs)) { + newtargs = list_add(newtargs, p); + } + } + if(clean && change) { + /* free up targets -- it's local now */ + for(i = targets; i; i = i->next) { + i->data = NULL; + } + list_free(targets); + } + targets = newtargs; + clean = 1; + } + return(targets); +} + +/* 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(pacdb_t *local, PMList *databases, syncpkg_t *syncpkg, PMList *list, PMList *trail) +{ + PMList *i, *j, *k; + PMList *targ = NULL; + PMList *deps = NULL; + + targ = list_new(); + targ = list_add(targ, syncpkg->pkg); + deps = checkdeps(local, PM_ADD, targ); + targ->data = NULL; + list_free(targ); + for(i = deps; i; i = i->next) { + int found = 0; + depmissing_t *miss = (depmissing_t*)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 == DEPEND) { + syncpkg_t *sync = NULL; + MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; + + /* find the package in one of the repositories */ + for(j = databases; !found && j; j = j->next) { + PMList *provides; + dbsync_t *dbs = (dbsync_t*)j->data; + /* check literals */ + for(k = dbs->pkgcache; !found && k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + if(!strcmp(miss->depend.name, pkg->name)) { + found = 1; + /* re-fetch the package record with dependency info */ + sync->pkg = db_scan(dbs->db, pkg->name, INFRQ_DESC | INFRQ_DEPENDS); + sync->dbs = dbs; + } + } + /* check provides */ + if(!found) { + provides = whatprovides(dbs->db, miss->depend.name); + if(provides) { + found = 1; + /* re-fetch the package record with dependency info */ + sync->pkg = db_scan(dbs->db, provides->data, INFRQ_DESC | INFRQ_DEPENDS); + sync->dbs = dbs; + } + list_free(provides); + } + } + if(!found) { + fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); + fprintf(stderr, " \"%s\" is not in the package set\n", miss->depend.name); + return(1); + } + found = 0; + for(j = list; j; j = j->next) { + syncpkg_t *tmp = (syncpkg_t*)j->data; + if(tmp && !strcmp(tmp->pkg->name, sync->pkg->name)) { + found = 1; + } + } + if(found) { + /* this dep is already in the target list */ + continue; + } + vprint("resolving %s\n", sync->pkg->name); + found = 0; + for(j = trail; j; j = j->next) { + syncpkg_t *tmp = (syncpkg_t*)j->data; + if(tmp && !strcmp(tmp->pkg->name, sync->pkg->name)) { + found = 1; + } + } + if(!found) { + list_add(trail, sync); + if(resolvedeps(local, databases, sync, list, trail)) { + return(1); + } + vprint("adding %s-%s\n", sync->pkg->name, sync->pkg->version); + list_add(list, sync); + } else { + /* cycle detected -- skip it */ + vprint("dependency cycle detected: %s\n", sync->pkg->name); + } + } + } + return(0); +} + +/* + * returns a PMList* of missing_t pointers. + * + * conflicts are always name only, but dependencies can include versions + * with depmod operators. + * + */ +PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) +{ + pkginfo_t *info = NULL; + depend_t depend; + PMList *i, *j, *k; + int cmp; + int found = 0; + PMList *baddeps = NULL; + depmissing_t *miss = NULL; + + if(op == PM_UPGRADE) { + /* PM_UPGRADE handles the backwards dependencies, ie, the packages + * listed in the requiredby field. + */ + for(i = targets; i; i = i->next) { + pkginfo_t *tp, *oldpkg; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_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; + pkginfo_t *p; + found = 0; + if((p = db_scan(db, j->data, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + /* hmmm... package isn't installed.. */ + continue; + } + if(is_pkgin(p, targets)) { + /* 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 */ + if(splitdep(k->data, &depend)) { + continue; + } + if(!strcmp(depend.name, oldpkg->name)) { + found = 1; + } + } + if(found == 0) { + /* look for packages that list depend.name as a "provide" */ + PMList *provides = whatprovides(db, depend.name); + if(provides == NULL) { + /* not found */ + continue; + } + /* we found an installed package that provides depend.name */ + } + found = 0; + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + if(!found) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = 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(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + freepkg(oldpkg); + } + } + if(op == PM_ADD || op == PM_UPGRADE) { + for(i = targets; i; i = i->next) { + pkginfo_t *tp; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_t*)i->data; + + /* CONFLICTS */ + for(j = tp->conflicts; j; j = j->next) { + /* check targets against database */ + for(k = pm_packages; k; k = k->next) { + pkginfo_t *dp = (pkginfo_t*)k->data; + if(!strcmp(j->data, dp->name)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, dp->name, 256); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + /* check targets against targets */ + for(k = targets; k; k = k->next) { + pkginfo_t *a = (pkginfo_t*)k->data; + if(!strcmp(a->name, (char*)j->data)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, a->name, 256); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + } + /* check database against targets */ + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_DEPENDS)) != NULL) { + for(j = info->conflicts; j; j = j->next) { + if(!strcmp((char*)j->data, tp->name)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, info->name, 256); + if(!list_isin(baddeps, miss)) { + baddeps = 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)) { + // this is the same package -- skip it + continue; + } + // we treat this just like a conflict + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, k->data, 256); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + }*/ + + /* DEPENDENCIES -- look for unsatisfied dependencies */ + for(j = tp->depends; j; j = j->next) { + /* split into name/version pairs */ + if(splitdep((char*)j->data, &depend)) { + logaction(stderr, "warning: invalid dependency in %s", (char*)tp->name); + continue; + } + found = 0; + /* check database for literal packages */ + for(k = pm_packages; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + if(!strcmp(p->name, depend.name)) { + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check other targets */ + for(k = targets; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + /* see if the package names match OR if p provides depend.name */ + if(!strcmp(p->name, depend.name) || is_in(depend.name, p->provides)) { + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check database for provides matches */ + if(!found){ + k = whatprovides(db, depend.name); + if(k) { + /* grab the first one (there should only really be one, anyway) */ + pkginfo_t *p = db_scan(db, k->data, INFRQ_DESC); + if(p == NULL) { + /* wtf */ + fprintf(stderr, "data error: %s supposedly provides %s, but it was not found in db\n", + (char*)k->data, depend.name); + list_free(k); + continue; + } + if(depend.mod == 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 DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + list_free(k); + } + /* else if still not found... */ + if(!found) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = 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(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + } + } else if(op == PM_REMOVE) { + /* check requiredby fields */ + for(i = targets; i; i = i->next) { + pkginfo_t* tp; + if(i->data == NULL) { + continue; + } + tp = (pkginfo_t*)i->data; + for(j = tp->requiredby; j; j = j->next) { + if(!is_in((char*)j->data, targets)) { + MALLOC(miss, sizeof(depmissing_t)); + miss->type = REQUIRED; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, (char*)j->data, 256); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + } + } + + return(baddeps); +} + +int splitdep(char *depstr, depend_t *depend) +{ + char *str = NULL; + char *ptr = NULL; + + str = strdup(depstr); + + if((ptr = strstr(str, ">="))) { + depend->mod = DEP_GE; + } else if((ptr = strstr(str, "<="))) { + depend->mod = DEP_LE; + } else if((ptr = strstr(str, "="))) { + depend->mod = DEP_EQ; + } else { + /* no version specified - accept any */ + depend->mod = DEP_ANY; + strncpy(depend->name, str, sizeof(depend->name)); + strncpy(depend->version, "", sizeof(depend->version)); + } + + if(ptr == NULL) { + FREE(str); + return(0); + } + *ptr = '\0'; + strncpy(depend->name, str, sizeof(depend->name)); + ptr++; + if(depend->mod != DEP_EQ) { + ptr++; + } + strncpy(depend->version, ptr, sizeof(depend->version)); + FREE(str); + return(0); +} + +/* 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* needbackup(char* file, PMList *backup) +{ + PMList *lp; + + /* 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 = index(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)) { + char *md5 = strdup(ptr); + FREE(str); + return(md5); + } + FREE(str); + } + return(NULL); +} + +/* Parse command-line arguments for each operation + * op: the operation code requested + * argc: argc + * argv: argv + * + * Returns: 0 on success, 1 on error + */ +int parseargs(int op, int argc, char **argv) +{ + int opt; + int option_index = 0; + static struct option opts[] = + { + {"add", no_argument, 0, 'A'}, + {"remove", no_argument, 0, 'R'}, + {"upgrade", no_argument, 0, 'U'}, + {"freshen", no_argument, 0, 'F'}, + {"query", no_argument, 0, 'Q'}, + {"sync", no_argument, 0, 'S'}, + {"deptest", no_argument, 0, 'T'}, + {"vertest", no_argument, 0, 'Y'}, + {"resolve", no_argument, 0, 'D'}, + {"root", required_argument, 0, 'r'}, + {"dbpath", required_argument, 0, 'b'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"search", no_argument, 0, 's'}, + {"clean", no_argument, 0, 'c'}, + {"force", no_argument, 0, 'f'}, + {"nodeps", no_argument, 0, 'd'}, + {"orphans", no_argument, 0, 'e'}, + {"nosave", no_argument, 0, 'n'}, + {"owns", no_argument, 0, 'o'}, + {"list", no_argument, 0, 'l'}, + {"file", no_argument, 0, 'p'}, + {"info", no_argument, 0, 'i'}, + {"sysupgrade", no_argument, 0, 'u'}, + {"downloadonly", no_argument, 0, 'w'}, + {"refresh", no_argument, 0, 'y'}, + {"cascade", no_argument, 0, 'c'}, + {"groups", no_argument, 0, 'g'}, + {0, 0, 0, 0} + }; + + while((opt = getopt_long(argc, argv, "ARUFQSTDYr:b:vhscVfnoldepiuwyg", opts, &option_index))) { + if(opt < 0) { + break; + } + switch(opt) { + case 0: break; + case 'A': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_ADD); break; + case 'R': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_REMOVE); break; + case 'U': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_UPGRADE); break; + case 'F': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_UPGRADE); pmo_freshen = 1; break; + case 'Q': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_QUERY); break; + case 'S': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_SYNC); break; + case 'T': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_DEPTEST); break; + case 'Y': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_DEPTEST); pmo_d_vertest = 1; break; + case 'D': pmo_op = (pmo_op != PM_MAIN ? 0 : PM_DEPTEST); pmo_d_resolve = 1; break; + case 'h': pmo_help = 1; break; + case 'V': pmo_version = 1; break; + case 'b': strcpy(pmo_dbpath, optarg); break; + case 'c': pmo_s_clean = 1; pmo_r_cascade = 1; break; + case 'd': pmo_nodeps = 1; break; + case 'e': pmo_q_orphans = 1; break; + case 'f': pmo_force = 1; break; + case 'g': pmo_group = 1; break; + case 'i': pmo_q_info++; break; + case 'l': pmo_q_list = 1; break; + case 'n': pmo_nosave = 1; break; + case 'p': pmo_q_isfile = 1; break; + case 'o': pmo_q_owns = 1; break; + case 'r': if(realpath(optarg, pmo_root) == NULL) { + perror("bad root path"); + return(1); + } break; + case 's': pmo_s_search = 1; break; + case 'u': pmo_s_upgrade = 1; break; + case 'v': pmo_verbose = 1; break; + case 'w': pmo_s_downloadonly = 1; break; + case 'y': pmo_s_sync = 1; break; + case '?': return(1); + default: return(1); + } + } + + if(pmo_op == 0) { + fprintf(stderr, "error: only one operation may be used at a time\n\n"); + return(1); + } + + if(pmo_help) { + usage(pmo_op, (char*)basename(argv[0])); + return(2); + } + if(pmo_version) { + version(); + return(2); + } + + while(optind < argc) { + /* add the target to our target array */ + char *s = strdup(argv[optind]); + pm_targets = list_add(pm_targets, s); + optind++; + } + + return(0); +} + +int parseconfig(char *configfile) +{ + FILE *fp = NULL; + char line[PATH_MAX+1]; + char *ptr = NULL; + char *key = NULL; + int linenum = 0; + char section[256] = ""; + sync_t *sync = NULL; + + if((fp = fopen(configfile, "r")) == NULL) { + perror(configfile); + return(1); + } + + while(fgets(line, PATH_MAX, fp)) { + linenum++; + trim(line); + if(strlen(line) == 0 || line[0] == '#') { + continue; + } + if(line[0] == '[' && line[strlen(line)-1] == ']') { + /* new config section */ + ptr = line; + ptr++; + strncpy(section, ptr, min(255, strlen(ptr)-1)); + section[min(255, strlen(ptr)-1)] = '\0'; + vprint("config: new section '%s'\n", section); + if(!strlen(section)) { + fprintf(stderr, "config: line %d: bad section name\n", linenum); + return(1); + } + if(!strcmp(section, "local")) { + fprintf(stderr, "config: line %d: %s is reserved and cannot be used as a package tree\n", + linenum, section); + return(1); + } + if(strcmp(section, "options")) { + /* start a new sync record */ + MALLOC(sync, sizeof(sync_t)); + sync->treename = strdup(section); + sync->servers = NULL; + pmc_syncs = list_add(pmc_syncs, sync); + } + } else { + /* directive */ + if(!strlen(section)) { + fprintf(stderr, "config: line %d: all directives must belong to a section\n", linenum); + return(1); + } + ptr = line; + key = strsep(&ptr, "="); + if(key == NULL) { + fprintf(stderr, "config: line %d: syntax error\n", linenum); + return(1); + } + trim(key); + key = strtoupper(key); + if(ptr == NULL) { + if(!strcmp(key, "NOPASSIVEFTP")) { + pmo_nopassiveftp = 1; + vprint("config: nopassiveftp\n"); + } else if(!strcmp(key, "USESYSLOG")) { + pmo_usesyslog = 1; + vprint("config: usesyslog\n"); + } else { + fprintf(stderr, "config: line %d: syntax error\n", linenum); + return(1); + } + } else { + trim(ptr); + if(!strcmp(section, "options")) { + if(!strcmp(key, "NOUPGRADE")) { + char *p = ptr; + char *q; + while((q = strchr(p, ' '))) { + *q = '\0'; + pmo_noupgrade = list_add(pmo_noupgrade, strdup(p)); + vprint("config: noupgrade: %s\n", p); + p = q; + p++; + } + pmo_noupgrade = list_add(pmo_noupgrade, strdup(p)); + vprint("config: noupgrade: %s\n", p); + } else if(!strcmp(key, "IGNOREPKG")) { + char *p = ptr; + char *q; + while((q = strchr(p, ' '))) { + *q = '\0'; + pmo_ignorepkg = list_add(pmo_ignorepkg, strdup(p)); + vprint("config: ignorepkg: %s\n", p); + p = q; + p++; + } + pmo_ignorepkg = list_add(pmo_ignorepkg, strdup(p)); + vprint("config: ignorepkg: %s\n", p); + } else if(!strcmp(key, "DBPATH")) { + /* shave off the leading slash, if there is one */ + if(*ptr == '/') { + ptr++; + } + strncpy(pmo_dbpath, ptr, PATH_MAX); + vprint("config: dbpath: %s\n", pmo_dbpath); + } else if (!strcmp(key, "LOGFILE")) { + pmo_logfile = strndup(ptr, PATH_MAX); + vprint("config: log file: %s\n", pmo_logfile); + } else { + fprintf(stderr, "config: line %d: syntax error\n", linenum); + return(1); + } + } else { + if(!strcmp(key, "SERVER")) { + /* parse our special url */ + server_t *server; + char *p; + + MALLOC(server, sizeof(server_t)); + server->server = server->path = NULL; + server->protocol = NULL; + + p = strstr(ptr, "://"); + if(p == NULL) { + fprintf(stderr, "config: line %d: bad server location\n", linenum); + return(1); + } + *p = '\0'; + p++; p++; p++; + if(p == NULL || *p == '\0') { + fprintf(stderr, "config: line %d: bad server location\n", linenum); + return(1); + } + server->protocol = strdup(ptr); + if(!strcmp(server->protocol, "ftp") || !strcmp(server->protocol, "http")) { + char *slash; + /* split the url into domain and path */ + slash = strchr(p, '/'); + if(slash == NULL) { + /* no path included, default to / */ + server->path = strdup("/"); + } else { + /* add a trailing slash if we need to */ + if(slash[strlen(slash)-1] == '/') { + server->path = strdup(slash); + } else { + MALLOC(server->path, strlen(slash)+2); + sprintf(server->path, "%s/", slash); + } + *slash = '\0'; + } + server->server = strdup(p); + } else if(!strcmp(server->protocol, "file")){ + /* add a trailing slash if we need to */ + if(p[strlen(p)-1] == '/') { + server->path = strdup(p); + } else { + MALLOC(server->path, strlen(p)+2); + sprintf(server->path, "%s/", p); + } + } else { + fprintf(stderr, "config: line %d: protocol %s is not supported\n", linenum, ptr); + return(1); + } + /* add to the list */ + vprint("config: %s: server: %s %s %s\n", section, server->protocol, server->server, server->path); + sync->servers = list_add(sync->servers, server); + } else { + fprintf(stderr, "config: line %d: syntax error\n", linenum); + return(1); + } + } + line[0] = '\0'; + } + } + } + fclose(fp); + + return(0); +} + +/* Display usage/syntax for the specified operation. + * op: the operation code requested + * myname: basename(argv[0]) + */ +void usage(int op, char *myname) +{ + if(op == PM_MAIN) { + printf("usage: %s {-h --help}\n", myname); + printf(" %s {-V --version}\n", myname); + printf(" %s {-A --add} [options] \n", myname); + printf(" %s {-R --remove} [options] \n", myname); + printf(" %s {-U --upgrade} [options] \n", myname); + printf(" %s {-F --freshen} [options] \n", myname); + printf(" %s {-Q --query} [options] [package]\n", myname); + printf(" %s {-S --sync} [options] [package]\n", myname); + printf("\nuse '%s --help' with other options for more syntax\n\n", myname); + } else { + if(op == PM_ADD) { + printf("usage: %s {-A --add} [options] \n", myname); + printf("options:\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + } else if(op == PM_REMOVE) { + printf("usage: %s {-R --remove} [options] \n", myname); + printf("options:\n"); + printf(" -c, --cascade remove packages and all packages that depend on them\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -n, --nosave remove configuration files as well\n"); + } else if(op == PM_UPGRADE) { + if(pmo_freshen) { + printf("usage: %s {-F --freshen} [options] \n", myname); + } else { + printf("usage: %s {-U --upgrade} [options] \n", myname); + } + printf("options:\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + } else if(op == PM_QUERY) { + printf("usage: %s {-Q --query} [options] [package]\n", myname); + printf("options:\n"); + printf(" -i, --info view package information\n"); + printf(" -g, --groups view all members of a package group\n"); + printf(" -l, --list list the contents of the queried package\n"); + printf(" -o, --owns query the package that owns \n"); + printf(" -p, --file pacman will query the package file [package] instead of\n"); + printf(" looking in the database\n"); + } else if(op == PM_SYNC) { + printf("usage: %s {-S --sync} [options] [package]\n", myname); + printf("options:\n"); + printf(" -c, --clean remove packages from cache directory to free up diskspace\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + printf(" -g, --groups view all members of a package group\n"); + printf(" -s, --search search sync database for matching strings\n"); + printf(" -u, --sysupgrade upgrade all packages that are out of date\n"); + printf(" -w, --downloadonly download packages, but do not install/upgrade anything\n"); + printf(" -y, --refresh download a fresh package sync database from the server\n"); + } + printf(" -v, --verbose be verbose\n"); + printf(" -r, --root set an alternate installation root\n"); + printf(" -b, --dbpath set an alternate database location\n"); + } +} + +/* Version + */ +void version(void) +{ + printf("\n"); + printf(" .--. Pacman v%s\n", PACVER); + printf("/ _.-' .-. .-. .-. Copyright (C) 2002-2003 Judd Vinet \n"); + printf("\\ '-. '-' '-' '-' \n"); + printf(" '--' This program may be freely redistributed under\n"); + printf(" the terms of the GNU General Public License\n\n"); +} + +/* Check verbosity option and, if set, print the + * string to stdout + */ +void vprint(char *fmt, ...) +{ + va_list args; + if(pmo_verbose) { + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + } +} + +/* Output a message to stderr, and (optionally) syslog and/or a logfile */ +void logaction(FILE *fp, char *fmt, ...) +{ + char msg[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, 1024, fmt, args); + va_end(args); + if(fp) { + fprintf(fp, "%s\n", msg); + fflush(fp); + } + if(pmo_usesyslog) { + syslog(LOG_WARNING, "%s", msg); + } + if(logfd) { + time_t t; + struct tm *tm; + t = time(NULL); + tm = localtime(&t); + + fprintf(logfd, "[%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); + } +} + +/* Condense a list of strings into one long (space-delimited) string + */ +char* buildstring(PMList *strlist) +{ + char* str; + int size = 1; + PMList *lp; + + for(lp = strlist; lp; lp = lp->next) { + size += strlen(lp->data) + 1; + } + MALLOC(str, size); + str[0] = '\0'; + for(lp = strlist; lp; lp = lp->next) { + strcat(str, lp->data); + strcat(str, " "); + } + /* shave off the last space */ + str[strlen(str)-1] = '\0'; + + return(str); +} + + +int lckmk(char *file, int retries, unsigned int sleep_secs) +{ + int fd, count = 0; + + while((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000)) == -1 && errno == EACCES) { + if(++count < retries) { + sleep(sleep_secs); + } else { + return(-1); + } + } + return(fd > 0 ? 0 : -1); +} + +int lckrm(char *file) +{ + return(unlink(file)); +} + +void cleanup(int signum) +{ + PMList *lp; + + if(pm_access == READ_WRITE && lckrm(lckfile)) { + logaction(stderr, "warning: could not remove lock file %s", lckfile); + } + if(workfile) { + /* remove the current file being downloaded (as it's not complete) */ + unlink(workfile); + FREE(workfile); + } + if(pmo_usesyslog) { + closelog(); + } + if(logfd) { + fclose(logfd); + } + + /* free memory */ + for(lp = pmc_syncs; lp; lp = lp->next) { + sync_t *sync = (sync_t *)lp->data; + PMList *i; + for(i = sync->servers; i; i = i->next) { + server_t *server = (server_t *)i->data; + FREE(server->protocol); + FREE(server->server); + FREE(server->path); + } + FREELIST(sync->servers); + FREE(sync->treename); + } + FREELIST(pmc_syncs); + FREELIST(pmo_noupgrade); + FREELIST(pmo_ignorepkg); + FREE(pmo_root); + FREE(pmo_dbpath); + + FREELIST(pm_targets); + + /* this is segfaulting... quick fix for now + FREELISTPKGS(pm_packages);*/ + + exit(signum); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman.h b/src/pacman.h index 727f0fc7..4f64519d 100644 --- a/src/pacman.h +++ b/src/pacman.h @@ -22,7 +22,7 @@ #define _PAC_PACMAN_H #ifndef PACVER -#define PACVER "2.7.2" +#define PACVER "2.7.3" #endif #ifndef PKGDIR diff --git a/src/pacsync.c b/src/pacsync.c index 6903a19f..196e1c92 100644 --- a/src/pacsync.c +++ b/src/pacsync.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /* pacman */ #include "list.h" @@ -34,13 +35,21 @@ #include "pacsync.h" #include "pacman.h" +/* progress bar */ static int log_progress(netbuf *ctl, int xfered, void *arg); static char sync_fnm[25]; static int offset; +static struct timeval t0, t; + static float rate; + static int xfered1; + static unsigned char eta_h, eta_m, eta_s; /* pacman options */ extern char *pmo_root; extern char *pmo_dbpath; +extern char *pmo_proxyhost; + +extern unsigned short pmo_proxyport; extern unsigned short pmo_nopassiveftp; /* sync servers */ @@ -112,8 +121,9 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) for(i = servers; i && !done; i = i->next) { server_t *server = (server_t*)i->data; - if(!strcmp(server->protocol, "ftp")) { + if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { FtpInit(); + vprint("Connecting to %s:21\n", server->server); if(!FtpConnect(server->server, &control)) { fprintf(stderr, "error: cannot connect to %s\n", server->server); continue; @@ -135,9 +145,15 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) } else { vprint("FTP passive mode not set\n"); } - } else if(!strcmp(server->protocol, "http")) { - if(!HttpConnect(server->server, &control)) { - fprintf(stderr, "error: cannot connect to %s\n", server->server); + /*} else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && !strcmp(server->protocol, "ftp"))) {*/ + } else if(!strcmp(server->protocol, "http") || pmo_proxyhost) { + char *host; + unsigned port; + host = (pmo_proxyhost) ? pmo_proxyhost : server->server; + port = (pmo_proxyhost) ? pmo_proxyport : 80; + vprint("Connecting to %s:%u\n", host, port); + if(!HttpConnect(host, port, &control)) { + fprintf(stderr, "error: cannot connect to %s\n", host); continue; } } @@ -155,6 +171,7 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) char output[PATH_MAX]; int j, filedone = 0; char *fn = (char*)lp->data; + char *ptr; struct stat st; if(is_in(fn, complete)) { @@ -163,13 +180,31 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) snprintf(output, PATH_MAX, "%s/%s.part", localpath, fn); strncpy(sync_fnm, fn, 24); + /* drop filename extension */ + ptr = strstr(fn, ".db.tar.gz"); + if(ptr && (ptr-fn) < 24) { + sync_fnm[ptr-fn] = '\0'; + } + ptr = strstr(fn, ".pkg.tar.gz"); + if(ptr && (ptr-fn) < 24) { + sync_fnm[ptr-fn] = '\0'; + } for(j = strlen(sync_fnm); j < 24; j++) { sync_fnm[j] = ' '; } sync_fnm[24] = '\0'; offset = 0; - if(!strcmp(server->protocol, "ftp")) { + /* ETA setup */ + gettimeofday(&t0, NULL); + t = t0; + rate = 0; + xfered1 = 0; + eta_h = 0; + eta_m = 0; + eta_s = 0; + + if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) { fprintf(stderr, "warning: failed to get filesize for %s\n", fn); } @@ -189,18 +224,21 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) } else { filedone = 1; } - } else if(!strcmp(server->protocol, "http")) { + /*} else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && !strcmp(server->protocol, "ftp"))) {*/ + } else if(!strcmp(server->protocol, "http") || pmo_proxyhost) { char src[PATH_MAX]; if(!stat(output, &st)) { - /* no resume support yet */ - unlink(output); + offset = (int)st.st_size; } - snprintf(src, PATH_MAX, "%s%s", server->path, fn); - if(!HttpGet(output, src, &fsz, control)) { + if(!pmo_proxyhost) { + snprintf(src, PATH_MAX, "%s%s", server->path, fn); + } else { + snprintf(src, PATH_MAX, "%s://%s%s%s", server->protocol, server->server, server->path, fn); + } + if(!HttpGet(server->server, output, src, &fsz, control, offset)) { fprintf(stderr, "\nfailed downloading %s from %s: %s\n", fn, server->server, FtpLastResponse(control)); - /* no resume support yet */ - unlink(output); + /* we leave the partially downloaded file in place so it can be resumed later */ } else { filedone = 1; } @@ -217,23 +255,23 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) } if(filedone) { - char completefile[PATH_MAX]; - if(!strcmp(server->protocol, "file")) { - char out[56]; - printf(" %s [", sync_fnm); - strncpy(out, server->path, 33); - printf("%s", out); - for(j = strlen(out); j < maxcols-44; j++) { - printf(" "); - } - fputs("] 100% | LOCAL\n", stdout); - } else { - log_progress(control, fsz-offset, &fsz); + char completefile[PATH_MAX]; + if(!strcmp(server->protocol, "file")) { + char out[56]; + printf(" %s [", sync_fnm); + strncpy(out, server->path, 33); + printf("%s", out); + for(j = strlen(out); j < maxcols-64; j++) { + printf(" "); } - complete = list_add(complete, fn); - /* rename "output.part" file to "output" file */ - snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); - rename(output, completefile); + fputs("] 100% | LOCAL |", stdout); + } else { + log_progress(control, fsz-offset, &fsz); + } + complete = list_add(complete, fn); + /* rename "output.part" file to "output" file */ + snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); + rename(output, completefile); } printf("\n"); fflush(stdout); @@ -257,13 +295,47 @@ static int log_progress(netbuf *ctl, int xfered, void *arg) int fsz = *(int*)arg; int pct = ((float)(xfered+offset) / fsz) * 100; int i, cur; + struct timeval t1; + float timediff; + + gettimeofday(&t1, NULL); + if(xfered+offset == fsz) { + t = t0; + } + timediff = t1.tv_sec-t.tv_sec + (float)(t1.tv_usec-t.tv_usec) / 1000000; + + if(xfered+offset == fsz) { + /* average download rate */ + rate = xfered / (timediff * 1024); + /* total download time */ + eta_s = (int)timediff; + eta_h = eta_s / 3600; + eta_s -= eta_h * 3600; + eta_m = eta_s / 60; + eta_s -= eta_m * 60; + } else if(timediff > 1) { + /* we avoid computing the rate & ETA on too small periods of time, so that + results are more significant */ + rate = (xfered-xfered1) / (timediff * 1024); + xfered1 = xfered; + gettimeofday(&t, NULL); + eta_s = (fsz-(xfered+offset)) / (rate * 1024); + eta_h = eta_s / 3600; + eta_s -= eta_h * 3600; + eta_m = eta_s / 60; + eta_s -= eta_m * 60; + } printf(" %s [", sync_fnm); - cur = (int)((maxcols-44)*pct/100); - for(i = 0; i < maxcols-44; i++) { + cur = (int)((maxcols-64)*pct/100); + for(i = 0; i < maxcols-64; i++) { (i < cur) ? printf("#") : printf(" "); } - printf("] %3d%% | %6dK\r", pct, ((xfered+offset)/1024)); + if(rate > 1000) { + printf("] %3d%%| %6dK| %6.0fK/s| %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s); + } else { + printf("] %3d%%| %6dK| %6.1fK/s| %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s); + } fflush(stdout); return(1); } -- cgit v1.2.3-24-g4f1b