From 931506031bbf51051200b67f096fe02620e6fbf5 Mon Sep 17 00:00:00 2001 From: Judd Vinet Date: Mon, 18 Mar 2002 09:36:01 +0000 Subject: Imported from pacman-1.2.tar.gz --- ChangeLog | 5 + Makefile | 9 +- README | 18 +++- makepkg | 14 ++- makepkg.8.in | 110 +++++++++++++++++++++ makeworld | 4 + pacman.8.in | 4 +- pacman.c | 306 +++++++++++++++++++++++++++++++++++++---------------------- pacman.h | 8 +- pacsync | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pacsync.8.in | 53 +++++++++++ pacsync.conf | 8 ++ 12 files changed, 692 insertions(+), 128 deletions(-) create mode 100755 makepkg.8.in create mode 100755 pacsync create mode 100644 pacsync.8.in create mode 100644 pacsync.conf diff --git a/ChangeLog b/ChangeLog index 6a9a6b89..41a3edd0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ VERSION DESCRIPTION ------------------------------------------------------------------ + - +1.1 - Fixed some string-handling bugs + - Added better handling of configuration files and the like. + If "file" is about to be removed, but it is designated to + be backed up, then it will be copied to "file.save" - Changed db_find_conflicts() to ignore directories 1.0 - Initial Release diff --git a/Makefile b/Makefile index 5184a572..3e1d0b0d 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ BINDIR = /usr/bin MANDIR = /usr/man ETCDIR = /etc -VERSION = 1.1 +VERSION = 1.2 LIBTAR_VERSION = 1.2.4 CXX = gcc @@ -45,7 +45,7 @@ pacman: $(OBJECTS) pacman.o: pacman.c pacman.h $(CXX) $(CXXFLAGS) -c pacman.c -man: pacman.8 +man: pacman.8 pacsync.8 makepkg.8 %: %.in sed -e "s/#VERSION#/$(VERSION)/" $< > $@ @@ -62,11 +62,14 @@ libtar: install: all install -D -m0755 pacman $(DESTDIR)$(BINDIR)/pacman install -D -m0644 pacman.8 $(DESTDIR)$(MANDIR)/man8/pacman.8 + install -D -m0644 pacsync.8 $(DESTDIR)$(MANDIR)/man8/pacsync.8 + install -D -m0644 makepkg.8 $(DESTDIR)$(MANDIR)/man8/makepkg.8 install -D -m0755 makepkg $(DESTDIR)$(BINDIR)/makepkg install -D -m0755 makeworld $(DESTDIR)$(BINDIR)/makeworld + install -D -m0755 pacsync $(DESTDIR)$(BINDIR)/pacsync @echo "" @echo "*** If this is a first-time install, you should copy makepkg.conf" - @echo "*** to /etc now." + @echo "*** and pacsync.conf to /etc now." @echo "" clean: diff --git a/README b/README index 26cab790..b39ecedb 100644 --- a/README +++ b/README @@ -13,7 +13,17 @@ DESCRIPTION: upgrade packages in the system, and it will allow you to query the package database for installed packages, files and owners. - Plans exist for dependency checking and remote file fetching, as well. + Although the package manager itself is quite simple, the pacman tarball + also comes with scripts that help automate building and installing + packages. These are used extensively in the Arch Build System (ABS), + used in Arch Linux . See ABS.txt for more + information. + + As of version 1.2, pacsync is included as well. This performs an apt-like + function, keeping your system's packages in sync with the packages on + a remote server. + + Plans exist for dependency checking as I approach version 2.0. See TODO for more info. @@ -32,15 +42,15 @@ Note: Since pacman is compiled statically, you will need the static libraries BUGS: ----- -If you find bugs (which is quite likely at version 1.0), please submit -them to with specific information, such as your +If you find bugs (which is quite likely), please submit them to + with specific information, such as your commandline, the nature of the bug, and even the package database, if it helps. COPYRIGHT: ---------- -pacman is Copyright (c) 2002 Judd vinet and is +pacman is Copyright (c) 2002 Judd Vinet and is licensed through the GNU General Public License (see COPYING). pacman uses "libtar", a library for reading/writing tar-files. This diff --git a/makepkg b/makepkg index c286d7fc..18d5df97 100755 --- a/makepkg +++ b/makepkg @@ -1,7 +1,7 @@ #!/bin/bash me=`basename $0` -myver='1.1' +myver='1.2' startdir=`pwd` [ -f /etc/makepkg.conf ] && . /etc/makepkg.conf @@ -30,9 +30,9 @@ for netfile in ${source[@]}; do if [ -f ../$file ]; then echo "==> Found $file in build dir" >&2 cp ../$file . - elif [ -f /var/cache/pkg/$file ]; then + elif [ -f /var/cache/pacman/src/$file ]; then echo "==> Using local copy of $file" >&2 - cp /var/cache/pkg/$file . + cp /var/cache/pacman/src/$file . else echo "==> Downloading $file" >&2 wget --passive-ftp --no-directories --tries=3 --waitretry=3 $netfile 2>&1 @@ -41,7 +41,7 @@ for netfile in ${source[@]}; do echo "==> Aborting..." >&2 exit 1 fi - mkdir -p /var/cache/pkg && cp $file /var/cache/pkg + mkdir -p /var/cache/pacman/src && cp $file /var/cache/pacman/src fi case $file in *.tar.gz|*.tar.Z|*.tgz) @@ -76,6 +76,12 @@ echo "pkgver = $pkgver-$pkgrel" >>.PKGINFO for bakfile in "${backup[@]}"; do echo "backup = $bakfile" >>.PKGINFO done +if [ "$install" != "" ]; then + cat $startdir/$install | egrep '^[^$]' | sed 's/^/install = /g' >>.PKGINFO +fi +if [ "$remove" != "" ]; then + cat $startdir/$remove | egrep '^[^$]' | sed 's/^/remove = /g' >>.PKGINFO +fi # remove info/doc files cd $startdir diff --git a/makepkg.8.in b/makepkg.8.in new file mode 100755 index 00000000..a7430770 --- /dev/null +++ b/makepkg.8.in @@ -0,0 +1,110 @@ +.TH makepkg 8 "Mar 17, 2002" "makepkg #VERSION#" "" +.SH NAME +makepkg \- package build utility +.SH SYNOPSIS +\fBmakepkg\fP +.SH DESCRIPTION +\fBmakepkg\fP will build packages for you. All it needs is +a build-capable linux platform, wget, and some build scripts. The advantage +to a script-based build is that you only really do the work once. Once you +have the build script for a package, you just need to run makepkg and it +will do the rest: download source files, configure the buildtime settings, +build the package, install the package into a temporary root, make +customizations, and package the whole thing up for pacman to use. + +\fBmakeworld\fP can be used to rebuild an entire package group, or the +entire build tree. +.SH BUILD PROCESS (or How To Build Your Own Packages) +Start in an isolated directory (ie, it's not used for anything other +than building this package). The build script should be called PKGBUILD +and it should bear resemblance to the example below. + +.TP +.TP +.SH PKGBUILD Example: +.RS +.nf +pkgname=modutils +pkgver=2.4.13 +pkgrel=1 +backup=(etc/modules.conf) +source=(ftp://ftp.server.com/$pkgname-$pkgver.tar.gz modules.conf) + +build() { + cd $startdir/src/$pkgname-$pkgver + ./configure --prefix=/usr + make || return 1 + make prefix=$startdir/pkg/usr install + # copy our custom modules.conf into the package root + mkdir -p $startdir/pkg/etc + cp ../modules.conf $startdir/pkg/etc +} +.fi +.RE + +As you can see, the setup is fairly simple. The first three lines define +the package name and version info. They also define the final package name, +which will be of the form $pkgname-$pkgver-$pkgrel.pkg.tar.gz + +The sources are then decompressed (if necessary) into a directory called ./src. +Then the \fIbuild\fP function is called. This is where all package configuration, +building, and installing should be done. Any customization will likely take +place here. + +After a package is built, the \fIbuild\fP function must install the package +files into a special package root, which can be referenced by \fB$startdir/pkg\fP +in the \fIbuild\fP function. The typical way to do this is one of the following: +.RS +.nf + +make DESTDIR=$startdir/pkg install + +or + +make prefix=$startdir/pkg/usr install + +.fi +.RE +Notice that the "/usr" portion should be present with "prefix", but not "DESTDIR." + +Once the package is successfully installed into the package root, \fImakepkg\fP +will remove some directories (as per Arch Linux package guidelines; if you use +this elsewhere, feel free to change it) like /usr/doc and /usr/info. It will +then strip debugging info from libraries and binaries and compress + +.SH PKGBUILD Directives +.TP +.B backup +A space-delimited array of filenames (without a preceiding slash). The +\fIbackup\fP line will be propagated to the package meta-info file for +pacman. This will designate all files listed there to be backed up if this +package is ever removed from a system. + +.TP +.B source +The \fIsource\fP line is an array of source files required to build the +package. Source files must reside in the same directory as the PKGBUILD +file, unless they have a fully-qualified URL. Then if the source file +does not already exist in /var/cache/pacman/src, the file is downloaded +by wget. + +.TP +.B install +There is also an \fIinstall\fP directive that is not used in the example +above. If \fIinstall\fP is set to the name of a file in the package build +directory (but \fBnot\fP listed in the source line), then it will be +copied to the package meta-info file and designated as a post-install script. +This will be run by pacman whenever it installs the package. + +.SH CONFIGURATION +Configuration options are stored in /etc/makepkg.conf. This file is parsed +as a bash script, so you can export any special compiler flags you wish +to use. This is helpful for building for different architectures, or with +different optimizations. + +\fBNOTE:\fP This does not guarantee that all package Makefiles will use +your exported variables. Some of them are flaky... +.SH AUTHOR +.nf +Judd Vinet +.fi diff --git a/makeworld b/makeworld index 883e5ad5..e517321a 100755 --- a/makeworld +++ b/makeworld @@ -1,11 +1,15 @@ #!/bin/bash toplevel=`pwd` +version="1.2" if [ $# -lt 2 ]; then + echo "makepkg version $version" echo "usage: $0 [category] ..." echo " where is base, opt, etc." echo " eg: makeworld /packages base opt extra" + echo + echo " this should be run from the toplevel directory of ABS (usually /usr/abs)" exit 1 fi diff --git a/pacman.8.in b/pacman.8.in index a7e720ed..86783836 100644 --- a/pacman.8.in +++ b/pacman.8.in @@ -1,8 +1,8 @@ -.TH pacman 8 "Feb 10, 2002" "pacman #VERSION#" "" +.TH pacman 8 "Mar 17, 2002" "pacman #VERSION#" "" .SH NAME pacman \- package manager utility .SH SYNOPSIS -\fBpacman [options] \fP +\fBpacman [options] [package] ...\fP .SH DESCRIPTION \fBpacman\fP is a \fIpackage management\fP utility. Package information is maintained in a basic text format for easy diff --git a/pacman.c b/pacman.c index 0a686925..5f01e5ff 100644 --- a/pacman.c +++ b/pacman.c @@ -95,15 +95,21 @@ unsigned short pmo_q_info = 0; unsigned short pmo_q_list = 0; char* pmo_q_owns = NULL; -pkginfo_t** packages = NULL; -unsigned int pkgcount = 0; +/* list of installed packages */ +pkginfo_t** pm_packages = NULL; +unsigned int pm_pkgcount = 0; +/* list of targets specified on command line */ +fileset_t pm_targets = NULL; +unsigned int pm_targct = 0; + +/* path to post-install script, if any */ +char* pm_install = NULL; int main(int argc, char* argv[]) { pm_opfunc_t op_func; - char* funcvar = NULL; - int ret = 0; + int i, ret = 0; /* default root */ pmo_root = (char*)malloc(2); @@ -118,16 +124,16 @@ int main(int argc, char* argv[]) /* the handler function */ if(!strcmp(argv[1], "-A") || !strcmp(argv[1], "--add")) { op_func = pacman_add; - funcvar = parseargs(PM_ADD, argc, argv); + ret = parseargs(PM_ADD, argc, argv); } else if(!strcmp(argv[1], "-R") || !strcmp(argv[1], "--remove")) { op_func = pacman_remove; - funcvar = parseargs(PM_REMOVE, argc, argv); + ret = parseargs(PM_REMOVE, argc, argv); } else if(!strcmp(argv[1], "-Q") || !strcmp(argv[1], "--query")) { op_func = pacman_query; - funcvar = parseargs(PM_QUERY, argc, argv); + ret = parseargs(PM_QUERY, argc, argv); } else if(!strcmp(argv[1], "-U") || !strcmp(argv[1], "--upgrade")) { op_func = pacman_upgrade; - funcvar = parseargs(PM_UPGRADE, argc, argv); + ret = parseargs(PM_UPGRADE, argc, argv); } else if(!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { usage(PM_MAIN, (char*)basename(argv[0])); return(1); @@ -135,16 +141,21 @@ int main(int argc, char* argv[]) version(); return(1); } else { - printf("error: invalid operation\n\n"); + fprintf(stderr, "error: invalid operation\n\n"); usage(PM_MAIN, (char*)basename(argv[0])); return(1); } - if(funcvar == NULL && op_func != pacman_query) { + if(ret || (pm_targct < 1 && op_func != pacman_query)) { return(1); } vprint("Installation Root: %s\n", pmo_root); - vprint("Package Name: %s\n", funcvar); + if(pm_targct) { + vprint("Targets:\n"); + for(i = 0; i < pm_targct; i++) { + vprint(" %s\n", pm_targets[i]); + } + } /* check for db existence */ if(pmo_root != NULL) { @@ -161,27 +172,40 @@ int main(int argc, char* argv[]) ret = db_open(dbpath); if(ret == 1) { - printf("error: Could not open package database file!\n"); - printf(" Check to see that %s exists.\n", dbpath); - printf(" If not, you may simply create it by \"touch\"-ing it.\n"); + fprintf(stderr, "error: Could not open package database file!\n"); + fprintf(stderr, " Check to see that %s exists.\n", dbpath); + fprintf(stderr, " If not, you may simply create it by \"touch\"-ing it.\n"); return(1); } if(ret == 2) { - printf("error: Database is corrupt! You may need to use the backup database.\n"); - printf(" I hope you like tweaking... ;-)\n"); + fprintf(stderr, "error: Database is corrupt! You may need to use the backup database.\n"); + fprintf(stderr, " I hope you like tweaking... ;-)\n"); return(1); } /* start the requested operation */ if(!pmo_nofunc) { - ret = op_func(funcvar); - if(ret) { - printf("There were errors\n"); + for(i = 0; i < pm_targct; i++) { + if(op_func(pm_targets[i])) { + ret = 1; + } + } + if(op_func == pacman_query && pm_targct == 0) { + ret = op_func(NULL); } } + + if(op_func == pacman_remove) { + /* the remove function no longer updates the db itself. we do it here + * instead, in an effort to save some expensive file rewrites. note that we + * can't do this for pacman_add() yet, as he's gotta call db_find_conflicts + * for each package. + */ + ret = db_update(NULL, 0); + } fclose(dbfp); - return(0); + return(ret); } int pacman_add(char* pkgfile) @@ -206,13 +230,13 @@ int pacman_add(char* pkgfile) vprint("Looking for DB conflicts...\n"); if((i = db_find_conflicts(files, filecount)) == 1) { if(pmo_force) { - printf("\nInstalling package anyway...\n"); - printf(" You might have duplicate entries in your package\n"); - printf(" database now. You may want to edit it and remove\n"); - printf(" one of the copies.\n\n"); + fprintf(stderr, "\nInstalling package anyway...\n"); + fprintf(stderr, " You might have duplicate entries in your package\n"); + fprintf(stderr, " database now. You may want to edit it and remove\n"); + fprintf(stderr, " one of the copies.\n\n"); } else { - printf("Aborting...\n"); - printf(" (use -f to override)\n\n"); + fprintf(stderr, "Aborting...\n"); + fprintf(stderr, " (use -f to override)\n\n"); return(1); } } else if(i == 2) { @@ -225,7 +249,11 @@ int pacman_add(char* pkgfile) if(pmo_upgrade) { vprint("Removing old package first...\n"); if(pacman_remove(pkgname)) { - printf("\nUpgrade aborted.\n"); + fprintf(stderr, "\nUpgrade aborted.\n"); + return(1); + } + if(db_update(NULL, 0)) { + fprintf(stderr, "\nError updating database. Upgrade aborted.\n"); return(1); } } @@ -257,11 +285,11 @@ int pacman_add(char* pkgfile) strcpy(newpath, expath); strcat(newpath, ".save"); rename(expath, newpath); - printf("%s renamed to %s\n", expath, newpath); + fprintf(stderr, "%s renamed to %s\n", expath, newpath); } if(tar_extract_file(tar, expath)) { errmsg = strerror(errno); - printf("could not extract %s: %s\n", th_get_pathname(tar), errmsg); + fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), errmsg); errors = 1; } free(expath); @@ -269,16 +297,29 @@ int pacman_add(char* pkgfile) tar_close(tar); vprint("Done.\n"); if(errors) { - printf("There were errors. No database update was performed.\n"); + fprintf(stderr, "There were errors. No database update was performed.\n"); + return(1); } else { vprint("Updating database...\n"); if(db_update(files, filecount)) { - printf("error: Could not update database! The database may not\n"); - printf(" be in a sane state!\n"); + fprintf(stderr, "error: Could not update database! The database may not\n"); + fprintf(stderr, " be in a sane state!\n"); return(1); } vprint("Done.\n"); } + + /* run the script in pm_install */ + if(pm_install != NULL) { + vprint("Executing post-install script...\n"); + expath = (char*)malloc(256); + snprintf(expath, 255, "/bin/bash %s", pm_install); + system(expath); + free(expath); + unlink(pm_install); + free(pm_install); + } + return(0); } @@ -286,7 +327,7 @@ int pacman_remove(char* pkgfile) { int found = 0, done = 0; int i; - char line[255]; + char line[PATH_MAX+1]; fileset_t files = NULL; unsigned int filecount = 0, nb = 0; struct stat buf; @@ -304,17 +345,17 @@ int pacman_remove(char* pkgfile) strcpy(line, trim(line)); if(!strcmp(line, pkgfile)) { /* read the version */ - fgets(line, 255, dbfp); + fgets(line, 63, dbfp); found = 1; } } if(!found) { - printf("Cannot remove %s: Package was not found.\n", pkgfile); + fprintf(stderr, "Cannot remove %s: Package was not found.\n", pkgfile); return(1); } while(!done) { - fgets(line, 255, dbfp); + fgets(line, PATH_MAX, dbfp); strcpy(line, trim(line)); if(strlen(line)) { /* add the path to the list */ @@ -347,13 +388,13 @@ int pacman_remove(char* pkgfile) /* perror("cannot remove directory"); */ } } else { - /* if the file ends in .conf, back it up */ + /* if the file is flagged, back it up to .save */ if(!pmo_nosave && nb) { newpath = (char*)realloc(newpath, strlen(file)+6); strcpy(newpath, file); strcat(newpath, ".save"); rename(file, newpath); - printf("%s renamed to %s\n", file, newpath); + fprintf(stderr, "%s renamed to %s\n", file, newpath); } else { vprint(" unlinking %s\n", file); if(unlink(file)) { @@ -365,31 +406,31 @@ int pacman_remove(char* pkgfile) /* now splice this name out of the packages list */ found = 0; - for(i = 0; i < pkgcount-1; i++) { + for(i = 0; i < pm_pkgcount-1; i++) { if(found) { - packages[i] = packages[i+1]; + pm_packages[i] = pm_packages[i+1]; } else { - if(!strcmp(packages[i]->name, pkgfile)) { + if(!strcmp(pm_packages[i]->name, pkgfile)) { found = 1; - if(i < pkgcount-1) { - packages[i] = packages[i+1]; + if(i < pm_pkgcount-1) { + pm_packages[i] = pm_packages[i+1]; } } } } /* drop the last item */ - packages = (pkginfo_t**)realloc(packages, (--pkgcount)*sizeof(pkginfo_t*)); + pm_packages = (pkginfo_t**)realloc(pm_packages, (--pm_pkgcount)*sizeof(pkginfo_t*)); - /* and commit the db */ - return(db_update(NULL, 0)); + /* the db will be commited back up in main() */ + return(0); } int pacman_query(char* pkgfile) { char *str = NULL; - char name[255]; - char ver[255]; - char line[255]; + char name[256]; + char ver[64]; + char line[PATH_MAX+1]; int found = 0; int done = 0; int i; @@ -422,22 +463,25 @@ int pacman_query(char* pkgfile) while(!found && !feof(dbfp)) { fgets(name, 255, dbfp); strcpy(name, trim(name)); - fgets(ver, 255, dbfp); + fgets(ver, 63, dbfp); strcpy(ver, trim(ver)); strcpy(line, " "); while(strlen(line) && !feof(dbfp)) { - fgets(line, 255, dbfp); + fgets(line, PATH_MAX, dbfp); strcpy(line, trim(line)); str = line; + if(line[0] == '*') { + str++; + } str += strlen(pmo_root); - if(!strcmp(line, pmo_q_owns)) { + if(!strcmp(str, pmo_q_owns)) { printf("%s %s\n", name, ver); return(0); } } } - printf("No package owns this file.\n"); - return(0); + fprintf(stderr, "No package owns this file.\n"); + return(2); } /* find packages in the db */ @@ -449,7 +493,7 @@ int pacman_query(char* pkgfile) strcpy(name, trim(name)); if(pkgfile == NULL || (pkgfile != NULL && !strcmp(name, pkgfile))) { /* read the version */ - fgets(ver, 255, dbfp); + fgets(ver, 63, dbfp); strcpy(ver, trim(ver)); found = 1; if(pkgfile != NULL) { @@ -459,13 +503,14 @@ int pacman_query(char* pkgfile) } if(feof(dbfp)) { if(pkgfile != NULL && !found) { - printf("Package was not found in database.\n"); + fprintf(stderr, "Package \"%s\" was not found in database.\n", pkgfile); + return(2); } break; } found = 0; while(!found) { - fgets(line, 255, dbfp); + fgets(line, PATH_MAX, dbfp); strcpy(line, trim(line)); if(strlen(line)) { if(pmo_q_list) { @@ -494,9 +539,8 @@ int pacman_upgrade(char* pkgfile) { /* this is basically just a remove,add process. pacman_add() will */ /* handle it */ - pmo_upgrade = 1; - pacman_add(pkgfile); - return(0); + pmo_upgrade = 1; + return pacman_add(pkgfile); } /* @@ -559,10 +603,10 @@ int load_pkg(char* pkgfile, fileset_t* listptr, unsigned short output) } tar_close(tar); + free(descfile); + if(pkgname == NULL || pkgver == NULL) { - printf("The current version of Pacman requires a .PKGINFO file\n"); - printf("present in the .tar.gz archive. This package does not\n"); - printf("have one.\n"); + fprintf(stderr, "Error: Missing .PKGINFO file in %s\n", pkgfile); return(0); } @@ -575,26 +619,38 @@ int load_pkg(char* pkgfile, fileset_t* listptr, unsigned short output) */ int db_open(char* path) { - char line[255]; + char line[PATH_MAX+1]; + int i; pkginfo_t* info; + + /* if pm_packages already contains data, free it first */ + if(pm_pkgcount) { + for(i = 0; i < pm_pkgcount; i++) { + free(pm_packages[i]); + } + free(pm_packages); + pm_packages = NULL; + pm_pkgcount = 0; + } + dbfp = fopen(path, "r"); if(dbfp == NULL) { return(1); } while(!feof(dbfp)) { info = (pkginfo_t*)malloc(sizeof(pkginfo_t)); - fgets(line, 64, dbfp); + fgets(line, sizeof(info->name)-1, dbfp); if(feof(dbfp)) { break; } strcpy(info->name, trim(line)); - fgets(line, 32, dbfp); + fgets(line, sizeof(info->version)-1, dbfp); strcpy(info->version, trim(line)); /* add to the collective */ - packages = (pkginfo_t**)realloc(packages, (++pkgcount) * sizeof(pkginfo_t*)); - packages[pkgcount-1] = info; + pm_packages = (pkginfo_t**)realloc(pm_packages, (++pm_pkgcount) * sizeof(pkginfo_t*)); + pm_packages[pm_pkgcount-1] = info; for(;;) { - fgets(line, 255, dbfp); + fgets(line, PATH_MAX, dbfp); if(feof(dbfp)) { return(2); } @@ -617,12 +673,12 @@ int db_open(char* path) */ int db_update(fileset_t files, unsigned int filecount) { - FILE* olddb; + FILE* olddb = NULL; char* newpath = NULL; char* str = NULL; - char name[64]; - char ver[32]; - char line[255]; + char name[256]; + char ver[64]; + char line[PATH_MAX+1]; int i = 0, found = 0, done = 0; /* build the backup pathname */ @@ -643,21 +699,21 @@ int db_update(fileset_t files, unsigned int filecount) rewind(olddb); while(!feof(olddb)) { - if(!fgets(name, 64, olddb)) { + if(!fgets(name, 255, olddb)) { break; } strcpy(name, trim(name)); - fgets(ver, 32, olddb); + fgets(ver, 63, olddb); found = 0; - for(i = 0; i < pkgcount && !found; i++) { - if(!strcmp(packages[i]->name, name)) { + for(i = 0; i < pm_pkgcount && !found; i++) { + if(!strcmp(pm_packages[i]->name, name)) { /* it's there... copy the entries over */ found = 1; fputs(name, dbfp); fputc('\n', dbfp); fputs(ver, dbfp); for(done = 0; !done;) { - fgets(line, 255, olddb); + fgets(line, PATH_MAX, olddb); if(found) { fputs(line, dbfp); } @@ -670,7 +726,7 @@ int db_update(fileset_t files, unsigned int filecount) if(!found) { /* skip through filelist for this package */ for(done = 0; !done;) { - fgets(line, 255, olddb); + fgets(line, PATH_MAX, olddb); if(strlen(trim(line)) == 0 || feof(olddb)) { done = 1; } @@ -713,8 +769,8 @@ int db_update(fileset_t files, unsigned int filecount) int db_find_conflicts(fileset_t files, unsigned int filecount) { int i; - char line[255]; - char name[255]; + char line[PATH_MAX+1]; + char name[256]; char* dbstr = NULL; char* filestr = NULL; struct stat buf; @@ -726,13 +782,13 @@ int db_find_conflicts(fileset_t files, unsigned int filecount) fgets(name, 255, dbfp); strcpy(name, trim(name)); if(!pmo_upgrade && !strcmp(name, pkgname)) { - printf("error: This package is already installed.\n"); - printf(" Maybe you should be using --upgrade.\n"); + fprintf(stderr, "error: This package is already installed.\n"); + fprintf(stderr, " Maybe you should be using --upgrade.\n"); return(2); } - fgets(line, 255, dbfp); + fgets(line, 64, dbfp); while(!feof(dbfp)) { - fgets(line, 255, dbfp); + fgets(line, PATH_MAX, dbfp); strcpy(line, trim(line)); dbstr = line; if(dbstr[0] == '*') { @@ -742,6 +798,7 @@ int db_find_conflicts(fileset_t files, unsigned int filecount) break; } if(index(dbstr, '/') == dbstr && (!pmo_upgrade || strcmp(name,pkgname))) { + /* we're looking at a file in the db that belongs to a different package */ for(i = 0; i < filecount; i++) { filestr = files[i]; if(filestr[0] == '*') { @@ -752,7 +809,7 @@ int db_find_conflicts(fileset_t files, unsigned int filecount) /* this filename has a trailing '/', so it's a directory -- skip it. */ continue; } - printf("conflict: %s already exists in package \"%s\"\n", dbstr, name); + fprintf(stderr, "conflict: %s already exists in package \"%s\"\n", dbstr, name); conflicts = 1; } } @@ -768,7 +825,7 @@ int db_find_conflicts(fileset_t files, unsigned int filecount) filestr++; } if(!stat(filestr, &buf) && !S_ISDIR(buf.st_mode)) { - printf("conflict: %s already exists in filesystem\n", filestr); + fprintf(stderr, "conflict: %s already exists in filesystem\n", filestr); conflicts = 1; } } @@ -785,8 +842,9 @@ int db_find_conflicts(fileset_t files, unsigned int filecount) int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, unsigned int* bakct) { - FILE* fp; - char line[255]; + FILE* fp = NULL; + FILE* inst = NULL; + char line[PATH_MAX+1]; char* ptr = NULL; char* key = NULL; int linenum = 0; @@ -799,7 +857,7 @@ int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, } while(!feof(fp)) { - fgets(line, 255, fp); + fgets(line, PATH_MAX, fp); if(output) { printf("%s", line); } @@ -814,7 +872,7 @@ int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, ptr = line; key = strsep(&ptr, "="); if(key == NULL || ptr == NULL) { - printf("Syntax error in description file line %d\n", linenum); + fprintf(stderr, "Syntax error in description file line %d\n", linenum); } else { key = trim(key); key = strtoupper(key); @@ -827,12 +885,25 @@ int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, strcpy(pkgver, ptr); } else if(!strcmp(key, "PKGDESC")) { /* Not used yet */ + } else if(!strcmp(key, "INSTALL")) { + if(inst == NULL) { + pm_install = (char*)malloc(strlen("/tmp/pacman_XXXXXX")+1); + strcpy(pm_install, "/tmp/pacman_XXXXXX"); + mkstemp(pm_install); + inst = fopen(pm_install, "w"); + if(inst == NULL) { + perror("fopen"); + return(1); + } + } + fputs(ptr, inst); + fputc('\n', inst); } else if(!strcmp(key, "BACKUP")) { backup = (fileset_t)realloc(backup, (++count) * sizeof(char*)); backup[count-1] = (char*)malloc(strlen(ptr)+1); strcpy(backup[count-1], ptr); } else { - printf("Syntax error in description file line %d\n", linenum); + fprintf(stderr, "Syntax error in description file line %d\n", linenum); } } line[0] = '\0'; @@ -840,6 +911,11 @@ int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, fclose(fp); unlink(descfile); + if(inst != NULL) { + fputs("exit 0\n", inst); + fclose(inst); + } + if(count > 0) { (*bakptr) = backup; (*bakct) = count; @@ -853,19 +929,21 @@ int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr, * argc: argc * argv: argv * - * Returns: the functional variable for that operation - * (eg, the package file name for PM_ADD) + * Returns: 0 on success, 1 on error */ -char* parseargs(int op, int argc, char** argv) +int parseargs(int op, int argc, char** argv) { - char* pkg = NULL; int i; for(i = 2; i < argc; i++) { + if(strlen(argv[i]) > PATH_MAX) { + fprintf(stderr, "error: argument %d is too long.\n", i); + return(1); + } if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { pmo_nofunc = 1; usage(op, (char*)basename(argv[0])); - return(NULL); + return(1); } else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { pmo_verbose = 1; } else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--force")) { @@ -873,14 +951,14 @@ char* parseargs(int op, int argc, char** argv) } else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--root")) { i++; if(i >= argc) { - printf("error: missing argument for %s\n", argv[i-1]); - return(NULL); + fprintf(stderr, "error: missing argument for %s\n", argv[i-1]); + return(1); } free(pmo_root); pmo_root = (char*)malloc(PATH_MAX); if(realpath(argv[i], pmo_root) == NULL) { perror("bad root path"); - return(NULL); + return(1); } } else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--nosave")) { pmo_nosave = 1; @@ -888,15 +966,19 @@ char* parseargs(int op, int argc, char** argv) /* PM_QUERY only */ i++; if(i >= argc) { - printf("error: missing argument for %s\n", argv[i-1]); - return(NULL); + fprintf(stderr, "error: missing argument for %s\n", argv[i-1]); + return(1); + } + if(strlen(argv[i]) > PATH_MAX) { + fprintf(stderr, "error: argument %d is too long.\n", i); + return(1); } free(pmo_q_owns); pmo_q_owns = (char*)malloc(PATH_MAX); if(realpath(argv[i], pmo_q_owns) == NULL) { perror("bad path specified for --owns"); pmo_nofunc = 1; - return(NULL); + return(1); } } else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) { /* PM_QUERY only */ @@ -908,20 +990,22 @@ char* parseargs(int op, int argc, char** argv) /* PM_QUERY only */ pmo_q_info = 1; } else { - pkg = argv[i]; + /* add the target to our pseudo linked list */ + pm_targets = (fileset_t)realloc(pm_targets, (++pm_targct) * sizeof(char*)); + pm_targets[pm_targct-1] = argv[i]; } } - if(op != PM_QUERY && pkg == NULL) { - printf("error: no package specified\n\n"); + if(op != PM_QUERY && pm_targct < 1) { + fprintf(stderr, "error: no package specified\n\n"); usage(op, (char*)basename(argv[0])); - return(NULL); + return(1); } - if(op == PM_QUERY && pmo_q_isfile && pkg == NULL) { - printf("error: no package file specified\n\n"); - return(NULL); + if(op == PM_QUERY && pmo_q_isfile && pm_targct < 1) { + fprintf(stderr, "error: no package file specified\n\n"); + return(1); } - return(pkg); + return(0); } /* Display usage/syntax for the specified operation. diff --git a/pacman.h b/pacman.h index 76914964..09d471fa 100644 --- a/pacman.h +++ b/pacman.h @@ -21,7 +21,7 @@ #ifndef PACMAN_H #define PACMAN_H -#define VERSION "1.1" +#define VERSION "1.2" #define PKGEXT ".tar.gz" #define PKGDB "/var/lib/pacman/pacman.db" @@ -36,8 +36,8 @@ typedef int (*pm_opfunc_t)(char*); typedef char** fileset_t; typedef struct __pkginfo_t { - char version[32]; - char name[64]; + char version[64]; + char name[256]; } pkginfo_t; int pacman_add(char* pkgfile); @@ -50,7 +50,7 @@ int db_update(fileset_t files, unsigned int filecount); int db_find_conflicts(fileset_t files, unsigned int filecount); int load_pkg(char* pkgfile, fileset_t* listptr, unsigned short output); -char* parseargs(int op, int argc, char** argv); +int parseargs(int op, int argc, char** argv); int parse_descfile(char* descfile, unsigned short output, fileset_t* bakptr, unsigned int* bakct); diff --git a/pacsync b/pacsync new file mode 100755 index 00000000..61e9106f --- /dev/null +++ b/pacsync @@ -0,0 +1,281 @@ +#!/bin/bash + +version="1.2" +tanpath="/var/lib/pacman" +tandb="pacsync.db" +errors=0 +upgrade=0 + +message() { + echo "==> $1" >&2 +} + +usage() { + echo "pacsync version $version" + echo "usage: $0 [package]" + echo "" + echo "operations:" + echo " sync Download a fresh package list from the server" + echo " install Download and install " + echo " upgrade Download and upgrade " + echo " report Generate a report of all packages that could be upgraded" + echo " sysupgrade Same as \"report\", but actually do the upgrades" + echo " clean Removes all files from package cache to clear up diskspace" + echo "" +} + +checkdb() { + if [ ! -f $tanpath/$tandb ]; then + message "missing package list. (use \"sync\" first)" + exit 1 + fi +} + +download() { + targ=$1 + shift + cl= + for file in $*; do + cl="$cl $SYNC_SERVER/$file" + done + message "Downloading $targ" + $ftpagent $cl + if [ $? -gt 0 ]; then + message "ERROR: could not download $targ" + return 1 + fi + return 0 +} + +dosync() { + cd /tmp + download "package list" $tandb + if [ $? -gt 0 ]; then + exit 1 + fi + rm -f $tanpath/$tandb + mv /tmp/$tandb $tanpath/$tandb + message "Done." +} + +doinstall() { + pkg2dl= + pkg2inst= + for pkgname in $*; do + line=`egrep "^[a-z]+/$pkgname-[a-z0-9\.]+-[0-9]+\.pkg\.tar\.gz$" $tanpath/$tandb` + if [ $? -gt 0 ]; then + message "package $pkgname not found" + exit 1 + fi + pacman=`pacman -Q $pkgname 2>/dev/null` + if [ $? -eq 0 ]; then + message "$pkgname is already installed (try using \"upgrade\")" + exit 1 + fi + filename=`echo $line | sed 's|^[a-z]*/||g'` + pkg2inst="$pkg2inst $filename" + if [ ! -f /var/cache/pacman/pkg/$filename ]; then + pkg2dl="$pkg2dl arch/$filename" + fi + done + + # download packages that aren't already cached + if [ "$pkg2dl" != "" ]; then + download "packages" $pkg2dl + if [ $? -gt 0 ]; then + exit 1 + fi + if [ `pwd` != "/var/cache/pacman/pkg" ]; then + # move downloaded files into cache + mkdir -p /var/cache/pacman/pkg + mv `echo $pkg2dl | sed 's|arch/||g'` /var/cache/pacman/pkg/ + fi + fi + + # install packages + message "Installing packages" + cd /var/cache/pacman/pkg + pacman -A $pkg2inst + if [ $? -gt 0 ]; then + echo "ERROR: some packages failed to install" + exit 1 + fi + + message "Done" + exit 0 +} + +doupgrade() { + pkg2dl= + pkg2up= + for pkgname in $*; do + line=`egrep "^[a-z]+/$pkgname-[a-z0-9\.]+-[0-9]+\.pkg\.tar\.gz$" $tanpath/$tandb` + if [ $? -gt 0 ]; then + message "package $pkgname not found" + exit 1 + fi + pacman=`pacman -Q $pkgname 2>/dev/null` + if [ $? -gt 0 ]; then + message "$pkgname is not installed (use \"install\" first)" + exit 1 + fi + pkgver=`echo $pacman | awk '{print $2}'` + package="$pkgname-$pkgver" + filename=`echo $line | sed 's|^[a-z]*/||g'` + # compare filename and package. if they are at all different, we + # assume that the newer version is on the server and do the upgrade + if [ "$filename" = "$package.pkg.tar.gz" ]; then + message "$pkgname is already up to date (skipping)" + else + pkg2up="$pkg2up $filename" + if [ ! -f /var/cache/pacman/pkg/$filename ]; then + pkg2dl="$pkg2dl arch/$filename" + fi + fi + done + + # download packages that aren't already cached + if [ "$pkg2dl" != "" ]; then + download "packages" $pkg2dl + if [ $? -gt 0 ]; then + exit 1 + fi + if [ `pwd` != "/var/cache/pacman/pkg" ]; then + # move downloaded files into cache + mkdir -p /var/cache/pacman/pkg + mv `echo $pkg2dl | sed 's|arch/||g'` /var/cache/pacman/pkg/ + fi + fi + + # install packages + if [ "$pkg2up" != "" ]; then + message "Upgrading packages" + cd /var/cache/pacman/pkg + pacman -U $pkg2up + if [ $? -gt 0 ]; then + echo "ERROR: some packages failed to upgrade" + exit 1 + fi + message "Done" + fi + + exit 0 +} + +doreport() { + headers=0 + pkg2up= + for pkgfile in `cat $tanpath/$tandb | sed "s|^[a-z]*/||g"`; do + pkgname=`echo $pkgfile | sed 's|-[a-z0-9\.]*-[0-9]*\.pkg\.tar\.gz||g'` + pacman=`pacman -Q $pkgname 2>/dev/null` + if [ $? -gt 0 ]; then + # skip this one, it's not installed + continue + fi + pkgver=`echo $pacman | awk '{print $2}'` + locfile="$pkgname-$pkgver" + remfile=`echo $pkgfile | sed 's|^[a-z]*/||g' | sed 's|\.pkg\.tar\.gz||g'` + # compare locfile and remfile + if [ "$locfile" = "$remfile" ]; then + # this package is up to date + continue + else + if [ "$headers" = "0" ]; then + echo "+--------------------------------------+--------------------------------------+" + echo "| LOCAL | REMOTE |" + echo "+--------------------------------------+--------------------------------------+" + headers=1 + fi + echo -n "| $locfile" + awk "BEGIN { for (j=length(\"$locfile\"); j<36; j++) printf \" \" }" + echo -n " | " + awk "BEGIN { for (j=length(\"$remfile\"); j<36; j++) printf \" \" }" + echo "$remfile |" + pkg2up="$pkg2up $pkgname" + fi + done + if [ "$headers" = "1" ]; then + echo "+--------------------------------------+--------------------------------------+" + fi + + # do we upgrade? + if [ "$upgrade" = "1" -a "$pkg2up" != "" ]; then + echo "" + echo -n "Do you want to upgrade these packages? [Y/n] " + read answer + echo "" + if [ "$answer" = "y" -o "$answer" = "Y" -o "$answer" = "" ]; then + doupgrade $pkg2up + fi + fi + + exit 0 +} + +if [ $# -lt 1 ]; then + usage + exit 0 +fi + +if [ -f /etc/pacsync.conf ]; then + . /etc/pacsync.conf +else + message "error: missing configuration file!" + exit 1 +fi + +proto=`echo $SYNC_SERVER | sed 's|://.*||'` +# check for a download utility +if [ -x /usr/bin/lftpget -a "$proto" = "ftp" ]; then + ftpagent="/usr/bin/lftpget" +elif [ -x /usr/bin/wget ]; then + ftpagent="/usr/bin/wget --passive-ftp --tries=3 --waitretry=3" +else + message "error: you need an ftp client installed (lftp or wget) in /usr/bin" + exit 1 +fi + +op=$1 +shift +if [ "$1" = "-v" ]; then + verbose=1 + shift +fi +case $op in + sync) + dosync + ;; + install) + checkdb + if [ "$1" = "" ]; then + message "error: no package specified" + exit 1 + fi + doinstall $* + ;; + upgrade) + checkdb + if [ "$1" = "" ]; then + message "error: no package specified" + exit 1 + fi + doupgrade $* + ;; + report) + checkdb + doreport + ;; + sysupgrade) + checkdb + upgrade=1 + doreport + ;; + clean) + message "Removing packages from cache" + rm -f /var/cache/pacman/pkg/* + ;; + *) + message "error: invalid operation" + exit 1 + ;; +esac diff --git a/pacsync.8.in b/pacsync.8.in new file mode 100644 index 00000000..a1316637 --- /dev/null +++ b/pacsync.8.in @@ -0,0 +1,53 @@ +.TH pacsync 8 "Mar 17, 2002" "pacsync #VERSION#" "" +.SH NAME +pacsync \- package update frontend for pacman +.SH SYNOPSIS +\fBpacsync [package] [package] ...\fP +.SH DESCRIPTION +\fBpacsync\fP is a \fIpackage update\fP frontend for the +\fIpacman\fP package manager. It keeps track of the versions +of packages installed on the local system and the versions of +packages on a remote server. This allows you to issue a single +command and have you system's packages brought into sync. +.SH OPERATIONS +.TP +.B "sync" +Download a new copy of the package list from the server. This +should be done regularly, usually before an "install", "upgrade" +or "sysupgrade" command. +.TP +.B "install " +Install a new package. \fBpacsync\fP will first look for the package +in the cache directory. If it is not there, it will attempt to +download the file from the server. On success, the package will +be installed. (Note that the downloaded file is also copied to +a cache directory on your filesystem. You can delete it with +"clean"). +.TP +.B "upgrade " +Upgrade a package. This is the same as \fIinstall\fP, except a package +is upgraded (ie, it is already installed in the system). +.TP +.B "report" +Generate an upgrade report. This operation will compare all local +package versions to remote versions, and then show you a report of +the differences. +.TP +.B "sysupgrade" +Full system upgrade. This is the same as report except after confirmation, +\fBpacsync\fP will upgrade all packages that are not up to date. +.TP +.B "clean" +This will remove all downloaded packages from the cache directory. This +can/should be used regularly to free up diskspace. +.SH CONFIGURATION +Configuration options are stored in /etc/pacsync.conf. Currently there is only +one directive. +.TP +.B "SYNC_SERVER" +This should be set to the full URL of the download server closest to you. +FTP is preferred, but HTTP is supported also, provided you have wget. +.SH AUTHOR +.nf +Judd Vinet +.fi diff --git a/pacsync.conf b/pacsync.conf new file mode 100644 index 00000000..52d54c30 --- /dev/null +++ b/pacsync.conf @@ -0,0 +1,8 @@ +# +# /etc/pacsync.conf +# + +# the full URL of the server (up to the /arch package directory) +SYNC_SERVER="ftp://ftp.archlinux.org" +#SYNC_SERVER="http://www.archlinux.org/pub" + -- cgit v1.2.3-24-g4f1b