diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-06-04 07:56:15 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-06-04 07:56:15 +0000 |
commit | 167be6f259825b0e88a4cfc7cd9228c9603fc603 (patch) | |
tree | c173305ec5afd2f63886e210fa18f7b9c69695b8 | |
parent | c8e3d32207db0af93ff3f660ded6b01fac993998 (diff) |
add package tools from FreeBSD
40 files changed, 6077 insertions, 3 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 81e5ad42ed2..8eca7953fa4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,5 +1,4 @@ -# from: @(#)Makefile 5.6.1.2 (Berkeley) 5/8/91 -# $Id: Makefile,v 1.11 1996/05/27 08:00:21 deraadt Exp $ +# $OpenBSD: Makefile,v 1.12 1996/06/04 07:56:00 niklas Exp $ # not yet done: catman @@ -8,7 +7,7 @@ SUBDIR= ac accton arp bootpd bootpgw bootpef bootptest \ diskpart edquota gettable gspa htable inetd iostat \ ipftest ipmon ipsend kgmon \ kvm_mkdb lpr map-mbone mrinfo mrouted mtrace mtree named \ - netgroup_mkdb portmap pppd pstat pwd_mkdb quot quotaon \ + netgroup_mkdb pkg_install portmap pppd pstat pwd_mkdb quot quotaon \ rarpd rbootd rdconfig rdate repquota rmt \ rpc.bootparamd rpc.pcnfsd rwhod \ sa sendmail sliplogin slstats spray sysctl \ diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile new file mode 100644 index 00000000000..c8f4eab0dea --- /dev/null +++ b/usr.sbin/pkg_install/Makefile @@ -0,0 +1,4 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:01 niklas Exp $ +SUBDIR=lib add create delete info + +.include <bsd.subdir.mk> diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc new file mode 100644 index 00000000000..a67fb5c22af --- /dev/null +++ b/usr.sbin/pkg_install/Makefile.inc @@ -0,0 +1,3 @@ +# $OpenBSD: Makefile.inc,v 1.1 1996/06/04 07:56:01 niklas Exp $ +# Inherit BINDIR from one level up. +.include "../Makefile.inc" diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README new file mode 100644 index 00000000000..a5a517d7752 --- /dev/null +++ b/usr.sbin/pkg_install/README @@ -0,0 +1,8 @@ +This is the pkg_install suite of tools for doing maintainance of +software "packages". More documentation is available in the man pages +for each individual command. + +This code was written by Jordan Hubbard for FreeBSD, snatched and +mildly reshaped by John Kohl in NetBSD and the changes taken back into +FreeBSD again by Jordan, who then proceeded to add another couple +of dozen features on top. Whee! :-) diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile new file mode 100644 index 00000000000..6817ca75341 --- /dev/null +++ b/usr.sbin/pkg_install/add/Makefile @@ -0,0 +1,17 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:02 niklas Exp $ +PROG= pkg_add + +CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib + +.if exists(${.CURDIR}/../lib/obj) +LDADD+= -L${.CURDIR}/../lib/obj -linstall +DPADD+= ${.CURDIR}/../lib/obj/libinstall.a +.else +LDADD+= -L${.CURDIR}/../lib -linstall +DPADD+= ${.CURDIR}/../lib/libinstall.a +.endif + + +SRCS= main.c perform.c futil.c extract.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h new file mode 100644 index 00000000000..1475e474de9 --- /dev/null +++ b/usr.sbin/pkg_install/add/add.h @@ -0,0 +1,44 @@ +/* $OpenBSD: add.h,v 1.1 1996/06/04 07:56:02 niklas Exp $ */ + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Include and define various things wanted by the add command. + * + */ + +#ifndef _INST_ADD_H_INCLUDE +#define _INST_ADD_H_INCLUDE + +typedef enum { NORMAL, MASTER, SLAVE } add_mode_t; + +extern char *Prefix; +extern Boolean NoInstall; +extern Boolean NoRecord; +extern Boolean Force; +extern char *Mode; +extern char *Owner; +extern char *Group; +extern char *Directory; +extern char *PkgName; +extern char FirstPen[]; +extern add_mode_t AddMode; + +int make_hierarchy(char *); +void extract_plist(char *, Package *); +void apply_perms(char *, char *); + +#endif /* _INST_ADD_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c new file mode 100644 index 00000000000..605bfafbbd0 --- /dev/null +++ b/usr.sbin/pkg_install/add/extract.c @@ -0,0 +1,186 @@ +# $OpenBSD: extract.c,v 1.1 1996/06/04 07:56:03 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: extract.c,v 1.1 1996/06/04 07:56:03 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the package extraction code for the add module. + * + */ + +#include "lib.h" +#include "add.h" + + +#define STARTSTRING "tar cf -" +#define TOOBIG(str) ((strlen(str) + 6 + strlen(home) + where_count > maxargs) \ + || (strlen(str) + 6 + strlen(home) + perm_count > maxargs)) + +#define PUSHOUT(todir) /* push out string */ \ + if (strlen(where_args) > sizeof(STARTSTRING)-1) { \ + strcat(where_args, "|tar xf - -C "); \ + strcat(where_args, todir); \ + if (system(where_args)) \ + barf("can't invoke tar pipeline"); \ + strcpy(where_args, STARTSTRING); \ + where_count = sizeof(STARTSTRING)-1; \ + } \ + if (perm_count) { \ + if (!isdir(todir)) apply_perms(todir, perm_args); \ + perm_args[0] = 0;\ + perm_count = 0; \ + } + +void +extract_plist(char *home, Package *pkg) +{ + PackingList p = pkg->head; + char *last_file; + char *where_args, *perm_args, *last_chdir; + int maxargs, where_count = 0, perm_count = 0, add_count; + + maxargs = sysconf(_SC_ARG_MAX); + maxargs -= 64; /* some slop for the tar cmd text, + and sh -c */ + where_args = malloc(maxargs); + if (!where_args) + barf("can't get argument list space"); + perm_args = malloc(maxargs); + if (!perm_args) + barf("can't get argument list space"); + + strcpy(where_args, STARTSTRING); + where_count = sizeof(STARTSTRING)-1; + perm_args[0] = 0; + + last_chdir = 0; + + /* Reset the world */ + Owner = NULL; + Group = NULL; + Mode = NULL; + last_file = NULL; + Directory = home; + + /* Do it */ + while (p) { + char cmd[FILENAME_MAX]; + + switch(p->type) { + case PLIST_NAME: + PkgName = p->name; + if (Verbose) + printf("extract: Package name is %s\n", p->name); + break; + + case PLIST_FILE: + last_file = p->name; + if (Verbose) + printf("extract: %s/%s\n", Directory, p->name); + if (!Fake) { + char try[FILENAME_MAX]; + + /* first try to rename it into place */ + sprintf(try, "%s/%s", Directory, p->name); + if (rename(p->name, try) == 0) { + /* try to add to list of perms to be changed, + and run in bulk. */ + add_count = snprintf(&perm_args[perm_count], + maxargs - perm_count, + "%s ", p->name); + if (add_count > maxargs - perm_count) + barf("oops, miscounted strings!"); + perm_count += add_count; + if (p->name[0] == '/') { + PUSHOUT(Directory); + } + } else { + /* rename failed, try copying with a big tar command */ + if (p->name[0] == '/' || + TOOBIG(p->name) || + last_chdir != Directory) { + PUSHOUT(last_chdir); + last_chdir = Directory; + } + add_count = snprintf(&where_args[where_count], + maxargs - where_count, + " %s", p->name); + if (add_count > maxargs - where_count) + barf("oops, miscounted strings!"); + where_count += add_count; + add_count = snprintf(&perm_args[perm_count], + maxargs - perm_count, + "%s ", p->name); + if (add_count > maxargs - perm_count) + barf("oops, miscounted strings!"); + perm_count += add_count; + if (p->name[0] == '/') { + PUSHOUT(Directory); + } + } + } + break; + + case PLIST_CWD: + if (Verbose) + printf("extract: CWD to %s\n", p->name); + PUSHOUT(Directory); + if (strcmp(p->name, ".")) { + if (!Fake && make_hierarchy(p->name) == FAIL) + barf("Unable make directory '%s'.", p->name); + Directory = p->name; + } + else + Directory = home; + break; + + case PLIST_CMD: + format_cmd(cmd, p->name, Directory, last_file); + PUSHOUT(Directory); + if (Verbose) + printf("extract: execute '%s'\n", cmd); + if (!Fake && system(cmd)) + whinge("Command '%s' failed.", cmd); + break; + + case PLIST_CHMOD: + PUSHOUT(Directory); + Mode = p->name; + break; + + case PLIST_CHOWN: + PUSHOUT(Directory); + Owner = p->name; + break; + + case PLIST_CHGRP: + PUSHOUT(Directory); + Group = p->name; + break; + + case PLIST_COMMENT: + break; + + case PLIST_IGNORE: + p = p->next; + break; + } + p = p->next; + } + PUSHOUT(Directory); +} diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c new file mode 100644 index 00000000000..d78817679d5 --- /dev/null +++ b/usr.sbin/pkg_install/add/futil.c @@ -0,0 +1,95 @@ +# $OpenBSD: futil.c,v 1.1 1996/06/04 07:56:03 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: futil.c,v 1.1 1996/06/04 07:56:03 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Miscellaneous file access utilities. + * + */ + +#include "lib.h" +#include "add.h" + +/* + * Assuming dir is a desired directory name, make it and all intervening + * directories necessary. + */ + +int +make_hierarchy(char *dir) +{ + char *cp1, *cp2; + + if (dir[0] == '/') + cp1 = cp2 = dir + 1; + else + cp1 = cp2 = dir; + while (cp2) { + if ((cp2 = index(cp1, '/')) !=NULL ) + *cp2 = '\0'; + if (fexists(dir)) { + if (!isdir(dir)) + return FAIL; + } + else { + if (vsystem("mkdir %s", dir)) + return FAIL; + apply_perms(NULL, dir); + } + /* Put it back */ + if (cp2) { + *cp2 = '/'; + cp1 = cp2 + 1; + } + } + return SUCCESS; +} + +/* Using permission defaults, apply them as necessary */ +void +apply_perms(char *dir, char *arg) +{ + char *cd_to; + + if (!dir || *arg == '/') /* absolute path? */ + cd_to = "/"; + else + cd_to = dir; + + if (Mode) + if (vsystem("cd %s && chmod -R %s %s", cd_to, Mode, arg)) + whinge("Couldn't change modes of '%s' to '%s'.", + arg, Mode); + if (Owner && Group) { + if (vsystem("cd %s && chown -R %s.%s %s", cd_to, Owner, Group, arg)) + whinge("Couldn't change owner/group of '%s' to '%s.%s'.", + arg, Owner, Group); + return; + } + if (Owner) { + if (vsystem("cd %s && chown -R %s %s", cd_to, Owner, arg)) + whinge("Couldn't change owner of '%s' to '%s'.", + arg, Owner); + return; + } else if (Group) + if (vsystem("cd %s && chgrp -R %s %s", cd_to, Group, arg)) + whinge("Couldn't change group of '%s' to '%s'.", + arg, Group); +} + diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c new file mode 100644 index 00000000000..0e067103a12 --- /dev/null +++ b/usr.sbin/pkg_install/add/main.c @@ -0,0 +1,170 @@ +# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:03 niklas Exp $ +#ifndef lint +static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:03 niklas Exp $"; +#endif + +/* + * + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the add module. + * + */ + +#include <sys/param.h> +#include "lib.h" +#include "add.h" + +static char Options[] = "hvIRfnp:SMt:"; + +char *Prefix = NULL; +Boolean NoInstall = FALSE; +Boolean NoRecord = FALSE; +Boolean Force = FALSE; + +char *Mode = NULL; +char *Owner = NULL; +char *Group = NULL; +char *PkgName = NULL; +char *Directory = NULL; +char FirstPen[FILENAME_MAX]; +add_mode_t AddMode = NORMAL; + +#define MAX_PKGS 20 +char pkgnames[MAX_PKGS][MAXPATHLEN]; +char *pkgs[MAX_PKGS]; + +int +main(int argc, char **argv) +{ + int ch, err; + char **start; + char *prog_name = argv[0], *cp; + + start = argv; + while ((ch = getopt(argc, argv, Options)) != EOF) { + switch(ch) { + case 'v': + Verbose = TRUE; + break; + + case 'p': + Prefix = optarg; + break; + + case 'I': + NoInstall = TRUE; + break; + + case 'R': + NoRecord = TRUE; + break; + + case 'f': + Force = TRUE; + break; + + case 'n': + Fake = TRUE; + Verbose = TRUE; + break; + + case 't': + strcpy(FirstPen, optarg); + break; + + case 'S': + AddMode = SLAVE; + break; + + case 'M': + AddMode = MASTER; + break; + + case 'h': + case '?': + default: + usage(prog_name, NULL); + break; + } + } + argc -= optind; + argv += optind; + + if (argc > MAX_PKGS) { + whinge("Too many packages (max %d).", MAX_PKGS); + return(1); + } + + if (AddMode != SLAVE) { + for (ch = 0; ch < MAX_PKGS; pkgs[ch++] = NULL) ; + + /* Get all the remaining package names, if any */ + for (ch = 0; *argv; ch++, argv++) { + if (!strcmp(*argv, "-")) /* stdin? */ + pkgs[ch] = "-"; + else if (isURL(*argv)) /* preserve URLs */ + pkgs[ch] = strcpy(pkgnames[ch], *argv); + else { /* expand all pathnames to fullnames */ + if (fexists(*argv)) /* refers to a file directly */ + pkgs[ch] = realpath(*argv, pkgnames[ch]); + else { /* look for the file in the expected places */ + if (!(cp = fileFindByPath(NULL, *argv))) + whinge("Can't find package '%s'.", *argv); + else + pkgs[ch] = strcpy(pkgnames[ch], cp); + } + } + } + } + /* If no packages, yelp */ + else if (!ch) + usage(prog_name, "Missing package name(s)"); + else if (ch > 1 && AddMode == MASTER) + usage(prog_name, "Only one package name may be specified with master mode"); + if ((err = pkg_perform(pkgs)) != NULL) { + if (Verbose) + fprintf(stderr, "%d package addition(s) failed.\n", err); + return err; + } + else + return 0; +} + +void +usage(const char *name, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (fmt) { + fprintf(stderr, "%s: ", name); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n\n"); + } + va_end(args); + fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name); + fprintf(stderr, "Where args are one or more of:\n\n"); + fprintf(stderr, "-v verbose\n"); + fprintf(stderr, "-p arg override prefix with arg\n"); + fprintf(stderr, "-I don't execute pkg install script, if any\n"); + fprintf(stderr, "-R don't record installation (can't delete!)\n"); + fprintf(stderr, "-n don't actually install, just show steps\n"); + fprintf(stderr, "-t temp use temp as template for mktemp()\n"); + fprintf(stderr, "-S run in SLAVE mode\n"); + fprintf(stderr, "-M run in MASTER mode\n"); + exit(1); +} diff --git a/usr.sbin/pkg_install/add/perform.c b/usr.sbin/pkg_install/add/perform.c new file mode 100644 index 00000000000..cb9e8270419 --- /dev/null +++ b/usr.sbin/pkg_install/add/perform.c @@ -0,0 +1,467 @@ +# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:04 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:04 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the main body of the add module. + * + */ + +#include "lib.h" +#include "add.h" + +#include <signal.h> +#include <sys/wait.h> + +static int pkg_do(char *); +static int sanity_check(char *); +static char LogDir[FILENAME_MAX]; + +int +pkg_perform(char **pkgs) +{ + int i, err_cnt = 0; + + signal(SIGINT, cleanup); + signal(SIGHUP, cleanup); + + if (AddMode == SLAVE) + err_cnt = pkg_do(NULL); + else { + for (i = 0; pkgs[i]; i++) + err_cnt += pkg_do(pkgs[i]); + } + return err_cnt; +} + +static Package Plist; +static char *Home; + +/* + * This is seriously ugly code following. Written very fast! + * [And subsequently made even worse.. Sigh! This code was just born + * to be hacked, I guess.. :) -jkh] + */ +static int +pkg_do(char *pkg) +{ + char pkg_fullname[FILENAME_MAX]; + char playpen[FILENAME_MAX]; + char extract_contents[FILENAME_MAX]; + char *where_to, *tmp, *extract; + FILE *cfile; + int code; + PackingList p; + struct stat sb; + char *isTMP = NULL; + char *cp; + int inPlace; + + code = 0; + LogDir[0] = '\0'; + strcpy(playpen, FirstPen); + inPlace = 0; + + /* Are we coming in for a second pass, everything already extracted? */ + if (!pkg) { + fgets(playpen, FILENAME_MAX, stdin); + playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */ + if (chdir(playpen) == FAIL) { + whinge("pkg_add in SLAVE mode can't chdir to %s.", playpen); + return 1; + } + read_plist(&Plist, stdin); + where_to = playpen; + } + /* Nope - do it now */ + else { + /* Is it an ftp://foo.bar.baz/file.tgz specification? */ + if (isURL(pkg)) { + if (!(Home = fileGetURL(NULL, pkg))) { + whinge("Unable to fetch `%s' by URL.", pkg); + return 1; + } + where_to = Home; + strcpy(pkg_fullname, pkg); + cfile = fopen(CONTENTS_FNAME, "r"); + if (!cfile) { + whinge("Unable to open table of contents file `%s' - not a package?", CONTENTS_FNAME); + goto bomb; + } + read_plist(&Plist, cfile); + fclose(cfile); + } + else { + strcpy(pkg_fullname, pkg); /* copy for sanity's sake, could remove pkg_fullname */ + if (strcmp(pkg, "-")) { + if (stat(pkg_fullname, &sb) == FAIL) { + whinge("Can't stat package file '%s'.", pkg_fullname); + goto bomb; + } + sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME); + extract = extract_contents; + } + else + extract = NULL; + Home = make_playpen(playpen, sb.st_size * 4); + if (!Home) + whinge("Unable to make playpen for %d bytes.\n", sb.st_size * 4); + where_to = Home; + if (unpack(pkg_fullname, extract)) { + whinge("Unable to extract table of contents file from `%s' - not a package?.", pkg_fullname); + goto bomb; + } + cfile = fopen(CONTENTS_FNAME, "r"); + if (!cfile) { + whinge("Unable to open table of contents file `%s' - not a package?", CONTENTS_FNAME); + goto bomb; + } + read_plist(&Plist, cfile); + fclose(cfile); + + /* Extract directly rather than moving? Oh goodie! */ + if (find_plist_option(&Plist, "extract-in-place")) { + if (Verbose) + printf("Doing in-place extraction for %s\n", pkg_fullname); + p = find_plist(&Plist, PLIST_CWD); + if (p) { + if (!isdir(p->name) && !Fake) { + if (Verbose) + printf("Desired prefix of %s does not exist, creating..\n", p->name); + vsystem("mkdir -p %s", p->name); + if (chdir(p->name) == -1) { + whinge("Unable to change directory to `%s' - no permission?", p->name); + perror("chdir"); + goto bomb; + } + } + where_to = p->name; + inPlace = 1; + } + else { + whinge("No prefix specified in `%s' - this is a bad package!", pkg_fullname); + goto bomb; + } + } + + /* + * Apply a crude heuristic to see how much space the package will + * take up once it's unpacked. I've noticed that most packages + * compress an average of 75%, so multiply by 4 for good measure. + */ + + if (!inPlace && min_free(playpen) < sb.st_size * 4) { + whinge("Projected size of %d exceeds available free space.\n" + "Please set your PKG_TMPDIR variable to point to a location with more\n" + "free space and try again.", sb.st_size * 4); + whinge("Not extracting %s\ninto %s, sorry!", pkg_fullname, where_to); + goto bomb; + } + + /* If this is a direct extract and we didn't want it, stop now */ + if (inPlace && Fake) + goto success; + + /* Finally unpack the whole mess */ + if (unpack(pkg_fullname, NULL)) { + whinge("Unable to extract `%s'!", pkg_fullname); + goto bomb; + } + } + + /* Check for sanity and dependencies */ + if (sanity_check(pkg)) + goto bomb; + + /* If we're running in MASTER mode, just output the plist and return */ + if (AddMode == MASTER) { + printf("%s\n", where_playpen()); + write_plist(&Plist, stdout); + return 0; + } + } + + /* + * If we have a prefix, delete the first one we see and add this + * one in place of it. + */ + if (Prefix) { + delete_plist(&Plist, FALSE, PLIST_CWD, NULL); + add_plist_top(&Plist, PLIST_CWD, Prefix); + } + + setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1); + /* Protect against old packages with bogus @name fields */ + PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous"; + + /* See if we're already registered */ + sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName); + if (isdir(LogDir)) { + char tmp[FILENAME_MAX]; + + whinge("Package `%s' already recorded as installed.\n", PkgName); + code = 1; + goto success; /* close enough for government work */ + } + + /* Now check the packing list for dependencies */ + for (p = Plist.head; p ; p = p->next) { + if (p->type != PLIST_PKGDEP) + continue; + if (Verbose) + printf("Package `%s' depends on `%s'.\n", PkgName, p->name); + if (!Fake && vsystem("pkg_info -e %s", p->name)) { + char path[FILENAME_MAX], *cp = NULL; + + if (!Fake && !isURL(pkg) && !getenv("PKG_ADD_BASE")) { + snprintf(path, FILENAME_MAX, "%s/%s.tgz", Home, p->name); + if (fexists(path)) + cp = path; + else + cp = fileFindByPath(pkg, p->name); + if (cp) { + if (Verbose) + printf("Loading it from %s.\n", cp); + if (vsystem("pkg_add %s", cp)) { + whinge("Autoload of dependency `%s' failed%s", cp, Force ? " (proceeding anyway)" : "!"); + if (!Force) + ++code; + } + } + } + else if (!Fake && (cp = fileGetURL(pkg, p->name)) != NULL) { + if (Verbose) + printf("Finished loading %s over FTP.\n", p->name); + if (!Fake) { + if (!fexists("+CONTENTS")) + whinge("Autoloaded package %s has no +CONTENTS file?", p->name); + else + if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) { + whinge("pkg_add of dependency `%s' failed%s", + p->name, Force ? " (proceeding anyway)" : "!"); + if (!Force) + ++code; + } + else if (Verbose) + printf("\t`%s' loaded successfully.\n", p->name); + } + /* Nuke the temporary playpen */ + leave_playpen(cp); + } + else { + if (Verbose) + printf("and was not found%s.\n", Force ? " (proceeding anyway)" : ""); + else + printf("Package dependency %s for %s not found%s\n", p->name, pkg, + Force ? " (proceeding anyway)" : "!"); + if (!Force) + ++code; + } + } + else if (Verbose) + printf(" - already installed.\n"); + } + + /* Look for the requirements file */ + if (fexists(REQUIRE_FNAME)) { + vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ + if (Verbose) + printf("Running requirements file first for %s..\n", PkgName); + if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) { + whinge("Package %s fails requirements %s", pkg_fullname, + Force ? "installing anyway" : "- not installed."); + if (!Force) { + code = 1; + goto success; /* close enough for government work */ + } + } + } + + /* If we're really installing, and have an installation file, run it */ + if (!NoInstall && fexists(INSTALL_FNAME)) { + vsystem("chmod +x %s", INSTALL_FNAME); /* make sure */ + if (Verbose) + printf("Running install with PRE-INSTALL for %s..\n", PkgName); + if (!Fake && vsystem("./%s %s PRE-INSTALL", INSTALL_FNAME, PkgName)) { + whinge("Install script returned error status."); + unlink(INSTALL_FNAME); + code = 1; + goto success; /* nothing to uninstall yet */ + } + } + + /* Now finally extract the entire show if we're not going direct */ + if (!inPlace && !Fake) + extract_plist(".", &Plist); + + if (!Fake && fexists(MTREE_FNAME)) { + if (Verbose) + printf("Running mtree for %s..\n", PkgName); + p = find_plist(&Plist, PLIST_CWD); + if (Verbose) + printf("mtree -U -f %s -d -e -p %s\n", MTREE_FNAME, p ? p->name : "/"); + if (!Fake) { + if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s", MTREE_FNAME, p ? p->name : "/")) + whinge("mtree returned a non-zero status - continuing."); + } + unlink(MTREE_FNAME); + } + + /* Run the installation script one last time? */ + if (!NoInstall && fexists(INSTALL_FNAME)) { + if (Verbose) + printf("Running install with POST-INSTALL for %s..\n", PkgName); + if (!Fake && vsystem("./%s %s POST-INSTALL", INSTALL_FNAME, PkgName)) { + whinge("Install script returned error status."); + unlink(INSTALL_FNAME); + code = 1; + goto fail; + } + unlink(INSTALL_FNAME); + } + + /* Time to record the deed? */ + if (!NoRecord && !Fake) { + char contents[FILENAME_MAX]; + FILE *cfile; + + umask(022); + if (getuid() != 0) + whinge("Not running as root - trying to record install anyway."); + if (!PkgName) { + whinge("No package name! Can't record package, sorry."); + code = 1; + goto success; /* well, partial anyway */ + } + sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName); + if (Verbose) + printf("Attempting to record package into %s..\n", LogDir); + if (make_hierarchy(LogDir)) { + whinge("Can't record package into '%s', you're on your own!", + LogDir); + bzero(LogDir, FILENAME_MAX); + code = 1; + goto success; /* close enough for government work */ + } + /* Make sure pkg_info can read the entry */ + vsystem("chmod a+rx %s", LogDir); + if (fexists(DEINSTALL_FNAME)) + move_file(".", DEINSTALL_FNAME, LogDir); + if (fexists(REQUIRE_FNAME)) + move_file(".", REQUIRE_FNAME, LogDir); + sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME); + cfile = fopen(contents, "w"); + if (!cfile) { + whinge("Can't open new contents file '%s'! Can't register pkg.", contents); + goto success; /* can't log, but still keep pkg */ + } + write_plist(&Plist, cfile); + fclose(cfile); + move_file(".", DESC_FNAME, LogDir); + move_file(".", COMMENT_FNAME, LogDir); + if (fexists(DISPLAY_FNAME)) + move_file(".", DISPLAY_FNAME, LogDir); + for (p = Plist.head; p ; p = p->next) { + if (p->type != PLIST_PKGDEP) + continue; + if (Verbose) + printf("Attempting to record dependency on package `%s'\n", p->name); + sprintf(contents, "%s/%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + basename_of(p->name), REQUIRED_BY_FNAME); + cfile = fopen(contents, "a"); + if (!cfile) + whinge("Warning: Can't open dependency file '%s'!\n" + "\tDependency registration is incomplete.", contents); + else { + fprintf(cfile, "%s\n", PkgName); + if (fclose(cfile) == EOF) + warn("Cannot properly close file %s", contents); + } + } + if (Verbose) + printf("Package %s registered in %s\n", PkgName, LogDir); + } + + if (p = find_plist(&Plist, PLIST_DISPLAY)) { + FILE *fp; + char buf[BUFSIZ]; + fp = fopen(p->name, "r"); + if (fp) { + putc('\n', stdout); + while (fgets(buf, sizeof(buf), fp)) + fputs(buf, stdout); + putc('\n', stdout); + (void) fclose(fp); + } else + warn("Cannot open display file `%s'.", p->name); + } + + goto success; + + bomb: + code = 1; + goto success; + + fail: + /* Nuke the whole (installed) show, XXX but don't clean directories */ + if (!Fake) + delete_package(FALSE, FALSE, &Plist); + + success: + /* delete the packing list contents */ + free_plist(&Plist); + leave_playpen(Home); + return code; +} + +static int +sanity_check(char *pkg) +{ + PackingList p; + int code = 0; + + if (!fexists(CONTENTS_FNAME)) { + whinge("Package %s has no CONTENTS file!", pkg); + code = 1; + } + else if (!fexists(COMMENT_FNAME)) { + whinge("Package %s has no COMMENT file!", pkg); + code = 1; + } + else if (!fexists(DESC_FNAME)) { + whinge("Package %s has no DESC file!", pkg); + code = 1; + } + return code; +} + +void +cleanup(int signo) +{ + if (signo) + printf("Signal %d received, cleaning up..\n", signo); + if (Plist.head) { + if (!Fake) + delete_package(FALSE, FALSE, &Plist); + free_plist(&Plist); + } + if (!Fake && LogDir[0]) + vsystem("%s -rf %s", REMOVE_CMD, LogDir); + leave_playpen(Home); +} diff --git a/usr.sbin/pkg_install/add/pkg_add.1 b/usr.sbin/pkg_install/add/pkg_add.1 new file mode 100644 index 00000000000..20c6b3e4af7 --- /dev/null +++ b/usr.sbin/pkg_install/add/pkg_add.1 @@ -0,0 +1,355 @@ +.\" $OpenBSD: pkg_add.1,v 1.1 1996/06/04 07:56:04 niklas Exp $ +.\" +.\" FreeBSD install - a package for the installation and maintainance +.\" of non-core utilities. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" Jordan K. Hubbard +.\" +.\" +.\" @(#)pkg_add.1 +.\" +.Dd November 25, 1994 +.Dt pkg_add 1 +.Os FreeBSD 2.0 +.Sh NAME +.Nm pkg_add +.Nd a utility for installing software package distributions. +.Sh SYNOPSIS +.Nm +.Op Fl vInfRMS +.Op Fl t Ar template +.Op Fl p Ar prefix +.Ar pkg-name [pkg-name ...] +.Sh DESCRIPTION +The +.Nm +command is used to extract packages that have been previously created +with the +.Xr pkg_create 1 +command. + +.Sh WARNING +.Bf -emphasis +Since the +.Nm +command may execute scripts or programs contained within a package file, +your system may be susceptible to ``trojan horses'' or other subtle +attacks from miscreants who create dangerous package files. +.Pp +You are advised to verify the competence and identity of those who +provide installable package files. For extra protection, use the +.Fl M +flag to extract the package file, and inspect its contents and scripts +to insure it poses no danger to your system's integrity. Pay particular +attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files, +and inspect the +CONTENTS file for +.Cm @cwd , +.Cm @mode +(check for setuid), +.Cm @dirrm , +.Cm @exec , +and +.Cm @unexec +directives, and/or use the +.Xr pkg_info 1 +command to examine the package file. +.Ef + +.Sh OPTIONS +The following command line arguments are supported. +.Bl -tag -width indent +.It Ar pkg-name [... pkg-name] +The named packages are installed. A package name of - will cause +.Nm +to read from stdin. +.It Fl v +Turns on verbose output. +.Em "Optional." +.It Fl I +If an installation script exists for a given package, do not execute it. +.Em "Optional." +.It Fl n +Don't actually install a package, just report the steps that +would be taken if it was. +.Em "Optional." +.It Fl R +Do not record the installation of a package. This means +that you cannot deinstall it later, so only use this option if +you know what you are doing! +.Em "Optional." +.It Fl f +Forces installation to proceed even if prerequisite packages are not +installed or the requirements script fails. +.Em "Optional." +.It Fl p Ar prefix +Sets +.Ar prefix +as the directory in which to extract files from a package. +If a package has set its default directory, it will be overridden +by this flag. Note that only the first +.Cm @cwd +directive will be replaced, since +.Nm +has no way of knowing which directory settings are relative and +which are absolute. It is rare, in any case, to see more than one +directory transition made, but when such is the case you +may then wish to look into the use of the +.Cm MASTER +and +.Cm SLAVE +modes (see the +.Fl M +and +.Fl S +options). +.Em "Optional." +.It Fl t Ar template +Use +.Ar template +as the input to +.Xr mktemp 3 +when creating a ``staging area.'' +By default, this is the string +.Pa /var/tmp/instmp.XXXXXX , +but it may be necessary to override it in the situation where +space in your +.Pa /tmp +directory is limited. Be sure to leave some number of `X' characters +for +.Xr mktemp 3 +to fill in with a unique ID. +.Pp +You can get a performance boost by setting the staging area +.Ar template +to reside on the same disk partition as target directories for package +file installation; often this is +.Pa /usr . +.Em "Optional." +.It Fl M +Run in +.Cm MASTER +mode. This is a very specialized mode for running +.Nm +and is meant to be run in conjunction with +.Cm SLAVE +mode. When run in this mode, +.Nm +does no work beyond extracting the package into a temporary staging +area (see the +.Fl t +option), reading in the packing list, and then dumping it (prefaced by +the current staging area) to stdout where it may be filtered by a +program such as +.Xr sed 1 . +When used in conjunction with +.Cm SLAVE +mode, it allows you to make radical changes to the package structure +before acting on its contents. +.It Fl S +Run in +.Cm SLAVE +mode. This is a very specialized mode for running +.Nm +and is meant to be run in conjunction with +.Cm MASTER +mode. When run in this mode, +.Nm +expects the release contents to be already extracted and waiting +in the staging area, the location of which is read as a string +from stdin. The complete packing list is also read from stdin, +and the contents then acted on as normal. +.El +On or more +.Ar pkg-name +arguments may be specified, each being either a file containing the +package (these usually ending with the ``.tgz'' suffix) or a +URL pointing at a file available on an ftp site. Thus you may +extract files directly from their anonymous ftp locations (e.g. +.Nm +ftp://ftp.freebsd.org/pub/FreeBSD/packages/shells/bash-1.14.4.tgz). +Note: If you wish to use +.Bf -emphasis +passive mode +.Ef +ftp in such transfers, set +the variable +.Bf -emphasis +FTP_PASSIVE_MODE +.Ef +to some value in your environment. Otherwise, the more standard +ACTIVE mode may be used. If +.Nm +consistently fails to fetch a package from a site known to work, +it may be because you have a firewall that demands the usage of +.Bf -emphasis +passive mode +.Ef +ftp. +.Sh TECHNICAL DETAILS +.Nm +is fairly simple. It extracts each packages' "packing list" +into a special staging directory in /tmp (or $PKG_TMPDIR), parses it, +then runs through the following sequence to fully extract the contents: +.Bl -enum -indent indent +.It +Check if the package is already recorded as installed. If so, +terminate installation. +.It +Scan all the package dependencies (from +.Cm @pkgdep +directives, see +.Xr pkg_create 1 ) +and make sure each one is met. If not, print the missing dependencies and +terminate the installation. +.It +Search for any +.Cm @option +directives which control how the package is added to the system. +At the time of this writing, the only currently implemented option is +.Cm @option extract-in-place +which will cause the package to be extracted direcly into its +prefix directory without moving through a staging area in /tmp. +.It +If +.Cm @option extract-in-place +is enabled, the package is now extracted directly into its +final location, otherwise it is extracted into the staging area. +.It +If the package contains a +.Ar require +file (see +.Xr pkg_create 1 ), +then execute it with the following arguments: +.Bd -filled -offset indent -compact +.Ar <pkg-name> +.Ar INSTALL +.Ed +where +.Ar <pkg-name> +is the name of the package in question and +.Ar INSTALL +is simply a keyword denoting that this is an installation requirements check. +.It +If an +.Ar install +script exists for the package, it is then executed with the following arguments: +.Bd -filled -offset indent -compact +.Ar <pkg-name> +.Ar PRE-INSTALL +.Ed +where +.Ar <pkg-name> +is the name of the package in question and +.Ar PRE-INSTALL +is a keyword denoting that this is the preinstallation phase. +.It +If +.Cm @option extract-in-place +is not used, then the packing list (this is the +.Pa +CONTENTS +file) is now used as a guide for moving (or copying, as necessary) files from +the staging area into their final locations. +.It +If the package contains an +.Ar mtreefile +file (see the +.Fl m +option to +.Xr pkg_create 1 ), +then mtree is invoked as +.Bd -filled -offset indent -compact +.Cm mtree +.Fl u +.Fl f +.Ar mtreefile +.Fl d +.Fl e +.Fl p +.Pa prefix +.Ed +where +.Pa prefix +is either the prefix specified with the +.Fl p +flag or, if no +.Fl p +flag was specified, the name of the first directory named by a +.Cm @cwd +directive within this package. +.It +If an +.Ar install +script exists for the package, it is then executed as +.Bd -filled -offset indent -compact +.Cm <script> +.Ar <pkg-name> +.Ar POST-INSTALL +.Ed +This all allows you to write a single +.Ar install +script that does both ``before and after'' actions. +.It +After installation is complete, a copy of the packing list, +.Ar deinstall +script, description, and display files are copied into +.Pa /var/db/pkg/<pkg-name> +for subsequent possible use by +.Xr pkg_delete 1 . +Any package dependencies are recorded in the other packages' +.Pa /var/db/pkg/<other-pkg>/+REQUIRED_BY +file +(if the environment variable PKG_DBDIR is set, this overrides the +.Pa /var/db/pkg/ +path shown above). +.It +Finally, the staging area is deleted and the program terminates. +.El +.Pp +All the scripts are called with the environment variable +.Ev PKG_PREFIX +set to the installation prefix (see the +.Fl p +option above). This allows a package author to write a script +that reliably performs some action on the directory where the package +is installed, even if the user might change it with the +.Fl p +flag to +.Cm pkg_add . +.Sh SEE ALSO +.Xr pkg_info 1 , +.Xr mktemp 3 , +.Xr sysconf 3 , +.Xr mtree 8 , +.Xr pkg_create 1 , +.Xr pkg_delete 1 . +.Sh AUTHORS +.Bl -tag -width indent -compact +.It "Jordan Hubbard" +most of the work +.It "John Kohl" +refined it for NetBSD +.El +.Sh BUGS +Hard links between files in a distribution are only preserved if either +(1) the staging area is on the same file system as the target directory of +all the links to the file, or (2) all the links to the file are bracketed by +.Cm @cwd +directives in the contents file, +.Em and +and the link names are extracted with a single +.Cm tar +command (not split between +invocations due to exec argument-space limitations--this depends on the +value returned by +.Fn sysconf _SC_ARG_MAX ) . +.Pp +Sure to be others. diff --git a/usr.sbin/pkg_install/create/Makefile b/usr.sbin/pkg_install/create/Makefile new file mode 100644 index 00000000000..c5029619028 --- /dev/null +++ b/usr.sbin/pkg_install/create/Makefile @@ -0,0 +1,17 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:05 niklas Exp $ +PROG= pkg_create + +CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib + +.if exists(${.CURDIR}/../lib/obj) +LDADD+= -L${.CURDIR}/../lib/obj -linstall +DPADD+= ${.CURDIR}/../lib/obj/libinstall.a +.else +LDADD+= -L${.CURDIR}/../lib -linstall +DPADD+= ${.CURDIR}/../lib/libinstall.a +.endif + + +SRCS= main.c perform.c pl.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/pkg_install/create/create.h b/usr.sbin/pkg_install/create/create.h new file mode 100644 index 00000000000..71c616c673a --- /dev/null +++ b/usr.sbin/pkg_install/create/create.h @@ -0,0 +1,46 @@ +/* $OpenBSD: create.h,v 1.1 1996/06/04 07:56:05 niklas Exp $ */ + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Include and define various things wanted by the create command. + * + */ + +#ifndef _INST_CREATE_H_INCLUDE +#define _INST_CREATE_H_INCLUDE + +extern char *Prefix; +extern char *Comment; +extern char *Desc; +extern char *Display; +extern char *Install; +extern char *DeInstall; +extern char *Contents; +extern char *Require; +extern char PlayPen[]; +extern char *ExcludeFrom; +extern char *Mtree; +extern char *Pkgdeps; +extern int Dereference; +extern int PlistOnly; + +void check_list(char *, Package *); +void usage(const char *, const char *, ...); +int pkg_perform(char **); +void copy_plist(char *, Package *); + +#endif /* _INST_CREATE_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c new file mode 100644 index 00000000000..19498a8e66c --- /dev/null +++ b/usr.sbin/pkg_install/create/main.c @@ -0,0 +1,177 @@ +# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:05 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:05 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the create module. + * + */ + +#include "lib.h" +#include "create.h" + +static char Options[] = "YNOhvf:p:P:c:d:i:k:r:t:X:D:m:"; + +char *Prefix = NULL; +char *Comment = NULL; +char *Desc = NULL; +char *Display = NULL; +char *Install = NULL; +char *DeInstall = NULL; +char *Contents = NULL; +char *Require = NULL; +char PlayPen[FILENAME_MAX]; +char *ExcludeFrom = NULL; +char *Mtree = NULL; +char *Pkgdeps = NULL; +int Dereference = 0; +int PlistOnly = 0; + +int +main(int argc, char **argv) +{ + int ch; + char **pkgs, **start; + char *prog_name = argv[0]; + + pkgs = start = argv; + while ((ch = getopt(argc, argv, Options)) != EOF) + switch(ch) { + case 'v': + Verbose = TRUE; + break; + + case 'N': + AutoAnswer = NO; + break; + + case 'Y': + AutoAnswer = YES; + break; + + case 'O': + PlistOnly = YES; + break; + + case 'p': + Prefix = optarg; + break; + + case 'f': + Contents = optarg; + break; + + case 'c': + Comment = optarg; + break; + + case 'd': + Desc = optarg; + break; + + case 'i': + Install = optarg; + break; + + case 'k': + DeInstall = optarg; + break; + + case 'r': + Require = optarg; + break; + + case 't': + strcpy(PlayPen, optarg); + break; + + case 'X': + ExcludeFrom = optarg; + break; + + case 'h': + Dereference = 1; + break; + + case 'D': + Display = optarg; + break; + + case 'm': + Mtree = optarg; + break; + + case 'P': + Pkgdeps = optarg; + break; + + case '?': + default: + usage(prog_name, NULL); + break; + } + + argc -= optind; + argv += optind; + + /* Get all the remaining package names, if any */ + while (*argv) + *pkgs++ = *argv++; + + /* If no packages, yelp */ + if (pkgs == start) + usage(prog_name, "Missing package name"); + *pkgs = NULL; + if (start[1]) + usage(prog_name, "Only one package name allowed\n\t('%s' extraneous)", + start[1]); + if (!pkg_perform(start)) { + if (Verbose) + fprintf(stderr, "Package creation failed.\n"); + return 1; + } + else + return 0; +} + +void +usage(const char *name, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (fmt) { + fprintf(stderr, "%s: ", name); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n\n"); + } + va_end(args); + fprintf(stderr, "Usage: %s [args] pkg\n\n", name); + fprintf(stderr, "Where args are one or more of:\n\n"); + + fprintf(stderr, "-c [-]file Get one-line comment from file (-or arg)\n"); + fprintf(stderr, "-d [-]file Get description from file (-or arg)\n"); + fprintf(stderr, "-f file get list of files from file (- for stdin)\n"); + fprintf(stderr, "-h follow symbolic links\n"); + fprintf(stderr, "-i script install script\n"); + fprintf(stderr, "-k script de-install script\n"); + fprintf(stderr, "-D file install notice\n"); + fprintf(stderr, "-m file mtree spec for directories\n"); + fprintf(stderr, "-P pkgs set package dependency list to pkgs\n"); + fprintf(stderr, "-p prefix install prefix will be arg\n"); + fprintf(stderr, "-r script pre/post requirements script\n"); + fprintf(stderr, "-t temp use temp as template for mktemp()\n"); + fprintf(stderr, "-X file exclude files listed in file\n"); + fprintf(stderr, "-v verbose\n"); + fprintf(stderr, "-Y assume `yes' answer to all questions\n"); + fprintf(stderr, "-N assume `no' answer to all questions\n"); + fprintf(stderr, "-O print a revised packing list and exit\n"); + exit(1); +} diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c new file mode 100644 index 00000000000..f1b703299a2 --- /dev/null +++ b/usr.sbin/pkg_install/create/perform.c @@ -0,0 +1,286 @@ +# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:05 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:05 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the main body of the create module. + * + */ + +#include "lib.h" +#include "create.h" + +#include <errno.h> +#include <signal.h> +#include <sys/syslimits.h> +#include <unistd.h> + +static void sanity_check(void); +static void make_dist(char *, char *, char *, Package *); + +static char *home; + +int +pkg_perform(char **pkgs) +{ + char *pkg = *pkgs; /* Only one arg to create */ + char *cp; + FILE *pkg_in, *fp; + Package plist; + char *suffix; /* What we tack on to the end of the finished package */ + + /* Preliminary setup */ + sanity_check(); + if (Verbose && !PlistOnly) + printf("Creating package %s\n", pkg); + get_dash_string(&Comment); + get_dash_string(&Desc); + if (!strcmp(Contents, "-")) + pkg_in = stdin; + else { + pkg_in = fopen(Contents, "r"); + if (!pkg_in) + barf("Unable to open contents file '%s' for input.", Contents); + } + plist.head = plist.tail = NULL; + + /* Break the package name into base and desired suffix (if any) */ + if ((cp = rindex(pkg, '.')) != NULL) { + suffix = cp + 1; + *cp = '\0'; + } + else + suffix = "tgz"; + + /* Stick the dependencies, if any, at the top */ + if (Pkgdeps) { + if (Verbose && !PlistOnly) + printf("Registering depends:"); + while (Pkgdeps) { + cp = strsep(&Pkgdeps, " \t\n"); + if (*cp) { + add_plist(&plist, PLIST_PKGDEP, cp); + if (Verbose && !PlistOnly) + printf(" %s", cp); + } + } + if (Verbose && !PlistOnly) + printf(".\n"); + } + /* Slurp in the packing list */ + read_plist(&plist, pkg_in); + + /* Prefix should override the packing list */ + if (Prefix) { + delete_plist(&plist, FALSE, PLIST_CWD, NULL); + add_plist_top(&plist, PLIST_CWD, Prefix); + } + /* + * Run down the list and see if we've named it, if not stick in a name + * at the top. + */ + if (find_plist(&plist, PLIST_NAME) == NULL) + add_plist_top(&plist, PLIST_NAME, basename_of(pkg)); + + /* + * We're just here for to dump out a revised plist for the FreeBSD ports + * hack. It's not a real create in progress. + */ + if (PlistOnly) { + write_plist(&plist, stdout); + exit(0); + } + + /* Make a directory to stomp around in */ + home = make_playpen(PlayPen, 0); + signal(SIGINT, cleanup); + signal(SIGHUP, cleanup); + + /* Make first "real contents" pass over it */ + check_list(home, &plist); + (void) umask(022); /* make sure gen'ed directories, files don't have + group or other write bits. */ + /* copy_plist(home, &plist); */ + /* mark_plist(&plist); */ + + /* Now put the release specific items in */ + add_plist(&plist, PLIST_CWD, "."); + write_file(COMMENT_FNAME, Comment); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, COMMENT_FNAME); + write_file(DESC_FNAME, Desc); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, DESC_FNAME); + + if (Install) { + copy_file(home, Install, INSTALL_FNAME); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, INSTALL_FNAME); + } + if (DeInstall) { + copy_file(home, DeInstall, DEINSTALL_FNAME); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME); + } + if (Require) { + copy_file(home, Require, REQUIRE_FNAME); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, REQUIRE_FNAME); + } + if (Display) { + copy_file(home, Display, DISPLAY_FNAME); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, DISPLAY_FNAME); + add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME); + } + if (Mtree) { + copy_file(home, Mtree, MTREE_FNAME); + add_plist(&plist, PLIST_IGNORE, NULL); + add_plist(&plist, PLIST_FILE, MTREE_FNAME); + add_plist(&plist, PLIST_MTREE, MTREE_FNAME); + } + + /* Run through the list again, picking up extra "local" items */ + /* check_list(".", &plist); */ + /* copy_plist(".", &plist); */ + /* mark_plist(&plist); */ + + /* Finally, write out the packing list */ + fp = fopen(CONTENTS_FNAME, "w"); + if (!fp) + barf("Can't open file %s for writing.", CONTENTS_FNAME); + write_plist(&plist, fp); + if (fclose(fp)) + barf("Error while closing %s.", CONTENTS_FNAME); + + /* And stick it into a tar ball */ + make_dist(home, pkg, suffix, &plist); + + /* Cleanup */ + free(Comment); + free(Desc); + free_plist(&plist); + cleanup(0); + return TRUE; /* Success */ +} + +static void +make_dist(char *home, char *pkg, char *suffix, Package *plist) +{ + char tball[FILENAME_MAX]; + PackingList p; + int ret, max, len; + char *args[50]; /* Much more than enough. */ + int nargs = 0; + int pipefds[2]; + FILE *totar; + pid_t pid; + + args[nargs++] = "tar"; /* argv[0] */ + + if (*pkg == '/') + snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suffix); + else + snprintf(tball, FILENAME_MAX, "%s/%s.%s", home, pkg, suffix); + + args[nargs++] = "-c"; + args[nargs++] = "-f"; + args[nargs++] = tball; + if (index(suffix, 'z')) /* Compress/gzip? */ + args[nargs++] = "-z"; + if (Dereference) + args[nargs++] = "-h"; + if (ExcludeFrom) { + args[nargs++] = "-X"; + args[nargs++] = ExcludeFrom; + } + args[nargs++] = "-T"; /* Take filenames from file instead of args. */ + args[nargs++] = "-"; /* Use stdin for the file. */ + args[nargs] = NULL; + + if (Verbose) + printf("Creating gzip'd tar ball in '%s'\n", tball); + + /* Set up a pipe for passing the filenames, and fork off a tar process. */ + if (pipe(pipefds) == -1) + barf("Cannot create pipe: %s", strerror(errno)); + if ((pid = fork()) == -1) + barf("Cannot fork process for tar: %s", strerror(errno)); + if (pid == 0) { /* The child */ + dup2(pipefds[0], 0); + close(pipefds[0]); + close(pipefds[1]); + execv("/usr/bin/tar", args); + barf("Failed to execute tar command: %s", strerror(errno)); + } + + /* Meanwhile, back in the parent process ... */ + close(pipefds[0]); + if ((totar = fdopen(pipefds[1], "w")) == NULL) + barf("fdopen failed: %s", strerror(errno)); + + fprintf(totar, "%s\n", CONTENTS_FNAME); + fprintf(totar, "%s\n", COMMENT_FNAME); + fprintf(totar, "%s\n", DESC_FNAME); + + if (Install) + fprintf(totar, "%s\n", INSTALL_FNAME); + if (DeInstall) + fprintf(totar, "%s\n", DEINSTALL_FNAME); + if (Require) + fprintf(totar, "%s\n", REQUIRE_FNAME); + if (Display) + fprintf(totar, "%s\n", DISPLAY_FNAME); + if (Mtree) + fprintf(totar, "%s\n", MTREE_FNAME); + + for (p = plist->head; p; p = p->next) { + if (p->type == PLIST_FILE) + fprintf(totar, "%s\n", p->name); + else if (p->type == PLIST_CWD || p->type == PLIST_SRC) + fprintf(totar, "-C\n%s\n", p->name); + else if (p->type == PLIST_IGNORE) + p = p->next; + } + + fclose(totar); + wait(&ret); + /* assume either signal or bad exit is enough for us */ + if (ret) + barf("tar command failed with code %d", ret); +} + +static void +sanity_check() +{ + if (!Comment) + barf("Required package comment string is missing (-c comment)."); + if (!Desc) + barf("Required package description string is missing (-d desc)."); + if (!Contents) + barf("Required package contents list is missing (-f [-]file)."); +} + + +/* Clean up those things that would otherwise hang around */ +void +cleanup(int sig) +{ + leave_playpen(home); +} diff --git a/usr.sbin/pkg_install/create/pkg_create.1 b/usr.sbin/pkg_install/create/pkg_create.1 new file mode 100644 index 00000000000..3715efdf0ad --- /dev/null +++ b/usr.sbin/pkg_install/create/pkg_create.1 @@ -0,0 +1,381 @@ +.\" $OpenBSD: pkg_create.1,v 1.1 1996/06/04 07:56:06 niklas Exp $ +.\" +.\" FreeBSD install - a package for the installation and maintainance +.\" of non-core utilities. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" Jordan K. Hubbard +.\" +.\" +.\" @(#)pkg_create.1 +.\" +.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords, +.\" added dependency tracking, etc. +.\" +.\" [jkh] Took John's changes back and made some additional extensions for +.\" better integration with FreeBSD's new ports collection. +.\" +.Dd April 21, 1995 +.Dt pkg_create 1 +.Os FreeBSD 2.0 +.Sh NAME +.Nm pkg_create +.Nd a utility for creating software package distributions. +.Sh SYNOPSIS +.Nm +.Op Fl YNOhv +.Op Fl P Ar pkgs +.Op Fl p Ar prefix +.Op Fl f Ar contents +.Op Fl i Ar iscript +.Op Fl k Ar dscript +.Op Fl r Ar rscript +.Op Fl t Ar template +.Op Fl X Ar excludefile +.Op Fl D Ar displayfile +.Op Fl m Ar mtreefile +.Fl d Ar description +.Fl f Ar packlist +.Ar pkg-name +.Sh DESCRIPTION +The +.Nm +command is used to create packages that will subsequently be fed to +one of the package extraction/info utilities. The input description +and command line arguments for the creation of a package are not +really meant to be human-generated, though it is easy enough to +do so. It is more expected that you will use a front-end tool for +the job rather than muddling through it yourself. Nonetheless, a short +description of the input syntax is included in this document. +.Sh OPTIONS +The following command line options are supported. +.Bl -tag -width indent +.It Fl f Ar packinglist +Fetch ``packing list'' for package from the file +.Ar packinglist +or +.Cm stdin +if +.Ar packinglist +is a +.Cm - +(dash). +.Em "Mandatory." +.It Fl c Ar [-]desc +Fetch package ``one line description'' from file +.Ar desc +or, if preceded by +.Cm - , +the argument itself. This string should also +give some idea of which version of the product (if any) the package +represents. +.Em "Mandatory." +.It Fl d Ar [-]desc +Fetch long description for package from file +.Ar desc +or, if preceded by +.Cm - , +the argument itself. +.Em "Mandatory." +.It Fl Y +Assume a default answer of `Yes' for any questions asked. +.Em "Optional." +.It Fl N +Assume a default answer of `No' for any questions asked. +.It Fl O +Go into a `packing list Only' mode. This is a custom hack for the +.Em "FreeBSD Ports Collection" +and is used to do `fake pkg_add' operations when a port is installed. +In such cases, it is necessary to know what the final, adjusted packing +list will look like. +.Em "Optional." +.It Fl v +Turns on verbose output. +.Em "Optional." +.It Fl h +Forces tar to follow symbolic links, so that the files they point to +are dumped, rather than the links themselves. +.It Fl i Ar iscript +Sets +.Ar iscript +to be the install procedure for the package. This can be any +executable program (or shell script). It will be invoked automatically +when the package is later installed. +.Em "Optional." +.It Fl P Ar pkgs +Sets the initial package dependency list to +.Ar pkgs. +This is assumed to be a whitespace separated list of package names +and is meant as a convenient shorthand for specifying multiple +.Cm @pkgdep +directives in the packing list (see PACKING LIST DETAILS section below). +.Em "Optional." +.It Fl p Ar prefix +Sets +.Ar prefix +as the initial directory ``base'' to start from in selecting files for +the package. +.Em "Optional." +.It Fl k Ar dscript +Sets +.Ar dscript +to be the de-install procedure for the package. This can be any +executable program (or shell script). It will be invoked automatically +when the package is later (if ever) de-installed. +.Em "Optional." +.It Fl r Ar rscript +Sets +.Ar rscript +to be the ``requirements'' procedure for the package. This can be any +executable program (or shell script). It will be invoked automatically +at installation/deinstallation time to determine whether or not +installation/deinstallation should proceed. +.Em "Optional." +.It Fl t Ar template +Use +.Ar template +as the input to +.Xr mktemp 3 . +By default, this is the string +.Pa /tmp/instmp.XXXXXX , +but it may be necessary to override it in the situation where +space in your +.Pa /tmp +directory is limited. Be sure to leave some number of `X' characters +for +.Xr mktemp 3 + to fill in with a unique ID. +.Em "Optional." +.It Fl X Ar excludefile +Pass +.Ar excludefile +as a +.Fl exclude-from +argument to +.Cm tar +when creating final package. See +.Cm tar +man page (or run +.Cm tar +with +.Fl -help +flag) for further information on using this flag. +.It Fl D Ar displayfile +Display the file (using +.Xr more 1 ) +after installing the package. Useful for things like +legal notices on almost-free software, etc. +.It Fl m Ar mtreefile +Run +.Xr mtree 8 +with input from mtreefile before the package is installed. +Mtree is invoked as +.Cm mtree +.Fl u +.Fl f +.Ar mtreefile +.Fl d +.Fl e +.Fl p +.Pa prefix , +where +.Pa prefix +is the name of the first directory named by a +.Cm @cwd +directive. +.El +.Pp +.Sh PACKING LIST DETAILS +The ``packing list'' format (see +.Fl f ) +is fairly simple, being +nothing more than a single column of filenames to include in the +package. However, since absolute pathnames are generally a bad idea +for a package that could be installed potentially anywhere, there is +another method of specifying where things are supposed to go +and, optionally, what ownership and mode information they should be +installed with. This is done by imbeding specialized command sequences +in the packing list. Briefly described, these sequences are: +.Bl -tag -width indent -compact +.It Cm @cwd Ar directory +Sets the internal directory pointer to point to +.Ar directory . +All subsequent filenames will be assumed relative to this directory. +Note: +.Cm @cd +is also an alias for this command. +.It Cm @srcdir Ar directory +Sets the internal directory pointer for _creation only_ to +.Ar directory . +That is to say that it overrides +.Cm @cwd +for package creation but not extraction. +.It Cm @exec Ar command +Execute +.Ar command +as part of the unpacking process. If +.Ar command +contains a any of the following sequences somewhere in it, they will +be expanded inline. For the following examples, assume that +.Cm @cwd +is set to +.Pa /usr/local +and the last extracted file was +.Pa bin/emacs . +.Bl -tag -width indent -compact +.It Cm "%F" +Expands to the last filename extracted (as specified), in the example case +.Pa bin/emacs +.It Cm "%D" +Expands to the current directory prefix, as set with +.Cm @cwd , +in the example case +.Pa /usr/local . +.It Cm "%B" +Expands to the ``basename'' of the fully qualified filename, that +is the current directory prefix, plus the last filespec, minus +the trailing filename. In the example case, that would be +.Pa /usr/local/bin . +.It Cm "%f" +Expands to the ``filename'' part of the fully qualified name, or +the converse of +.Cm %B , +being in the example case, +.Pa emacs . +.El +.It Cm @unexec Ar command +Execute +.Ar command +as part of the deinstallation process. Expansion of special +.Cm % +sequences is the same as for +.Cm @exec . +This command is not executed during the package add, as +.Cm @exec +is, but rather when the package is deleted. This is useful +for deleting links and other ancillary files that were created +as a result of adding the package, but not directly known to +the package's table of contents (and hence not automatically +removable). The advantage of using +.Cm @unexec +over a deinstallation script is that you can use the ``special +sequence expansion'' to get at files regardless of where they've +been potentially redirected (see +.Fl p ) +.It Cm @mode Ar mode +Sets default permission for all subsequently extracted files to +.Ar mode . +Format is the same as that used by the +.Cm chmod +command (well, considering that it's later handed off to it, that's +no surprise). Use without an arg to set back to default (extraction) +permissions. +.It Cm @owner Ar user +Sets default ownership for all subsequently extracted files to +.Ar user . +Use without an arg to set back to default (extraction) +ownership. +.It Cm @group Ar group +Sets default group ownership for all subsequently extracted files to +.Ar group . +Use without an arg to set back to default (extraction) +group ownership. +.It Cm @comment Ar string +Imbed a comment in the packing list. Useful in +trying to document some particularly hairy sequence that +may trip someone up later. +.It Cm @ignore +Used internally to tell extraction to ignore the next file (don't +copy it anywhere), as it's used for some special purpose. +.It Cm @ignore_inst +Similar to +.Cm @ignore , +but the ignoring of the next file is delayed one evaluation cycle. This +makes it possible to use this directive in the +.Ar packinglist +file, so you can pack a +specialized datafile in with a distribution for your install script (or +something) yet have the installer ignore it. +.It Cm @name Ar name +Sets the name of the package. This is mandatory and is usually +put at the top. This name is potentially different than the name of +the file it came in, and is used when keeping track of the package +for later deinstallation. Note that +.Nm +will derive this field from the package name and add it automatically +if none is given. +.It Cm @dirrm Ar name +Declare directory +.Pa name +to be deleted at deinstall time. By default, directories created by a +package installation are not deleted when the package is deinstalled; +this provides an explicit directory cleanup method. This directive +should appear at the end of the package list. If more than one +.Cm @dirrm +directives are used, the directories are removed in the order specified. +The +.Pa name +directory will not be removed unless it is empty. +.It Cm @mtree Ar name +Declare +.Pa name +as an +.Xr mtree 8 +input file to be used at install time (see +.Fl m +above). Only the first +.Cm @mtree +directive is honored. +.It Cm @display Ar name +Declare +.Pa name +as the file to be displayed at install time (see +.Fl D +above). +.It Cm @pkgdep Ar pkgname +Declares a dependency on the +.Ar pkgname +package. The +.Ar pkgname +package must be installed before this package may be +installed, and this package must be deinstalled before the +.Ar pkgname +package is deinstalled. Multiple +.Cm @pkgdep +directives may be used if hte package depends on multiple other packages. +.El +.Sh SEE ALSO +.Xr pkg_add 1 , +.Xr pkg_delete 1 , +.Xr pkg_info 1 , +.Xr sysconf 3 . +.Sh HISTORY +The +.Nm +command first appeared in FreeBSD. +.Sh AUTHORS +.Bl -tag -width indent -compact +.It "Jordan Hubbard" +most of the work +.It "John Kohl" +refined it for NetBSD +.El +.Sh BUGS +Hard links between files in a distribution must be bracketed by +.Cm @cwd +directives in order to be preserved as hard links when the package is +extracted. They additionally must not end up being split between +.Cm tar +invocations due to exec argument-space limitations (this depends on the +value returned by +.Fn sysconf _SC_ARG_MAX ) . +.Pp +Sure to be others. diff --git a/usr.sbin/pkg_install/create/pl.c b/usr.sbin/pkg_install/create/pl.c new file mode 100644 index 00000000000..39101035274 --- /dev/null +++ b/usr.sbin/pkg_install/create/pl.c @@ -0,0 +1,217 @@ +# $OpenBSD: pl.c,v 1.1 1996/06/04 07:56:06 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: pl.c,v 1.1 1996/06/04 07:56:06 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Routines for dealing with the packing list. + * + */ + +#include "lib.h" +#include "create.h" +#include <errno.h> + +/* Check a list for files that require preconversion */ +void +check_list(char *home, Package *pkg) +{ + char cmd[FILENAME_MAX]; + char name[FILENAME_MAX]; + char *where = home; + char *there = NULL; + PackingList p = pkg->head; + + while (p) { + if (p->type == PLIST_CWD) + where = p->name; + else if (p->type == PLIST_IGNORE) + p = p->next; + else if (p->type == PLIST_SRC) { + there = p->name; + } + else if (p->type == PLIST_FILE) { + cmd[0] = '\0'; + sprintf(name, "%s/%s", there ? there : where, p->name); + + if (*cmd) { + if (Verbose) + printf("Uncompressing-> %s\n", cmd); + if (system(cmd)) + barf("%s failed!", cmd); + nuke_suffix(p->name); + } + } + p = p->next; + } +} + +static int +trylink(const char *from, const char *to) +{ + if (link(from, to) == 0) + return 0; + if (errno == ENOENT) { + /* try making the container directory */ + char *cp = strrchr(to, '/'); + if (cp) + vsystem("mkdir -p %.*s", cp - to, + to); + return link(from, to); + } + return -1; +} + +#define STARTSTRING "tar cf -" +#define TOOBIG(str) strlen(str) + 6 + strlen(home) + where_count > maxargs +#define PUSHOUT() /* push out string */ \ + if (where_count > sizeof(STARTSTRING)-1) { \ + strcat(where_args, "|tar xpf -"); \ + if (system(where_args)) \ + barf("can't invoke tar pipeline"); \ + memset(where_args, 0, maxargs); \ + last_chdir = NULL; \ + strcpy(where_args, STARTSTRING); \ + where_count = sizeof(STARTSTRING)-1; \ + } + +/* + * Copy unmarked files in packing list to playpen - marked files + * have already been copied in an earlier pass through the list. + */ +void +copy_plist(char *home, Package *plist) +{ + PackingList p = plist->head; + char *where = home; + char *there = NULL, *mythere; + char *where_args, *last_chdir, *root = "/"; + int maxargs, where_count = 0, add_count; + struct stat stb; + dev_t curdir; + + maxargs = sysconf(_SC_ARG_MAX); + maxargs -= 64; /* some slop for the tar cmd text, + and sh -c */ + where_args = malloc(maxargs); + if (!where_args) + barf("can't get argument list space"); + + memset(where_args, 0, maxargs); + strcpy(where_args, STARTSTRING); + where_count = sizeof(STARTSTRING)-1; + last_chdir = 0; + + if (stat(".", &stb) == 0) + curdir = stb.st_dev; + else + curdir = (dev_t) -1; /* It's ok if this is a valid dev_t; + this is just a hint for an + optimization. */ + + while (p) { + if (p->type == PLIST_CWD) + where = p->name; + else if (p->type == PLIST_SRC) + there = p->name; + else if (p->type == PLIST_IGNORE) + p = p->next; + else if (p->type == PLIST_FILE && !p->marked) { + char fn[FILENAME_MAX]; + + + /* First, look for it in the "home" dir */ + sprintf(fn, "%s/%s", home, p->name); + if (fexists(fn)) { + if (lstat(fn, &stb) == 0 && stb.st_dev == curdir && + S_ISREG(stb.st_mode)) { + /* if we can link it to the playpen, that avoids a copy + and saves time. */ + if (p->name[0] != '/') { + /* don't link abspn stuff--it doesn't come from + local dir! */ + if (trylink(fn, p->name) == 0) { + p = p->next; + continue; + } + } + } + if (TOOBIG(fn)) { + PUSHOUT(); + } + if (p->name[0] == '/') { + add_count = snprintf(&where_args[where_count], + maxargs - where_count, + " %s %s", + last_chdir == root ? "" : "-C /", + p->name); + last_chdir = root; + } else { + add_count = snprintf(&where_args[where_count], + maxargs - where_count, + " %s%s %s", + last_chdir == home ? "" : "-C ", + last_chdir == home ? "" : home, + p->name); + last_chdir = home; + } + if (add_count > maxargs - where_count) + barf("oops, miscounted strings!"); + where_count += add_count; + } + /* + * Otherwise, try along the actual extraction path.. + */ + else { + if (p->name[0] == '/') + mythere = root; + else mythere = there; + sprintf(fn, "%s/%s", mythere ? mythere : where, p->name); + if (lstat(fn, &stb) == 0 && stb.st_dev == curdir && + S_ISREG(stb.st_mode)) { + /* if we can link it to the playpen, that avoids a copy + and saves time. */ + if (trylink(fn, p->name) == 0) { + p = p->next; + continue; + } + } + if (TOOBIG(p->name)) { + PUSHOUT(); + } + if (last_chdir == (mythere ? mythere : where)) + add_count = snprintf(&where_args[where_count], + maxargs - where_count, + " %s", p->name); + else + add_count = snprintf(&where_args[where_count], + maxargs - where_count, + " -C %s %s", + mythere ? mythere : where, + p->name); + if (add_count > maxargs - where_count) + barf("oops, miscounted strings!"); + where_count += add_count; + last_chdir = (mythere ? mythere : where); + } + } + p = p->next; + } + PUSHOUT(); + free(where_args); +} diff --git a/usr.sbin/pkg_install/delete/Makefile b/usr.sbin/pkg_install/delete/Makefile new file mode 100644 index 00000000000..b612621f964 --- /dev/null +++ b/usr.sbin/pkg_install/delete/Makefile @@ -0,0 +1,15 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:07 niklas Exp $ +PROG= pkg_delete +CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib + +.if exists(${.CURDIR}/../lib/obj) +LDADD+= -L${.CURDIR}/../lib/obj -linstall +DPADD+= ${.CURDIR}/../lib/obj/libinstall.a +.else +LDADD+= -L${.CURDIR}/../lib -linstall +DPADD+= ${.CURDIR}/../lib/libinstall.a +.endif + +SRCS= main.c perform.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h new file mode 100644 index 00000000000..e952ad1b453 --- /dev/null +++ b/usr.sbin/pkg_install/delete/delete.h @@ -0,0 +1,33 @@ +/* $OpenBSD: delete.h,v 1.1 1996/06/04 07:56:07 niklas Exp $ */ + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Include and define various things wanted by the delete command. + * + */ + +#ifndef _INST_DELETE_H_INCLUDE +#define _INST_DELETE_H_INCLUDE + +extern char *Prefix; +extern Boolean NoDeInstall; +extern Boolean CleanDirs; +extern Boolean Force; +extern char *Directory; +extern char *PkgName; + +#endif /* _INST_DELETE_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c new file mode 100644 index 00000000000..ef64951d899 --- /dev/null +++ b/usr.sbin/pkg_install/delete/main.c @@ -0,0 +1,124 @@ +# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:07 niklas Exp $ +#ifndef lint +static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:07 niklas Exp $"; +#endif + +/* + * + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the delete module. + * + */ + +#include "lib.h" +#include "delete.h" + +static char Options[] = "hvDdnfp:"; + +char *Prefix = NULL; +Boolean NoDeInstall = FALSE; +Boolean CleanDirs = FALSE; +Boolean Force = FALSE; + +int +main(int argc, char **argv) +{ + int ch, error; + char **pkgs, **start; + char *prog_name = argv[0]; + + pkgs = start = argv; + while ((ch = getopt(argc, argv, Options)) != EOF) + switch(ch) { + case 'v': + Verbose = TRUE; + break; + + case 'f': + Force = TRUE; + break; + + case 'p': + Prefix = optarg; + break; + + case 'D': + NoDeInstall = TRUE; + break; + + case 'd': + CleanDirs = TRUE; + break; + + case 'n': + Fake = TRUE; + Verbose = TRUE; + break; + + case 'h': + case '?': + default: + usage(prog_name, NULL); + break; + } + + argc -= optind; + argv += optind; + + /* Get all the remaining package names, if any */ + /* Get all the remaining package names, if any */ + while (*argv) + *pkgs++ = *argv++; + + /* If no packages, yelp */ + if (pkgs == start) + usage(prog_name, "Missing package name(s)"); + *pkgs = NULL; + if (!Fake && getuid() != 0) + errx(1, "You must be root to delete packages."); + if ((error = pkg_perform(start)) != NULL) { + if (Verbose) + fprintf(stderr, "%d package deletion(s) failed.\n", error); + return error; + } + else + return 0; +} + +void +usage(const char *name, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (fmt) { + fprintf(stderr, "%s: ", name); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n\n"); + } + va_end(args); + fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name); + fprintf(stderr, "Where args are one or more of:\n\n"); + fprintf(stderr, "-v verbose\n"); + fprintf(stderr, "-p arg override prefix with arg\n"); + fprintf(stderr, "-d delete empty directories when deinstalling\n"); + fprintf(stderr, "-f force delete even if dependencies exist\n"); + fprintf(stderr, " or deinstall/requirement checks fail\n"); + fprintf(stderr, "-D don't execute pkg de-install script, if any\n"); + fprintf(stderr, "-n don't actually de-install, just show steps\n"); + exit(1); +} diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c new file mode 100644 index 00000000000..299291ddd14 --- /dev/null +++ b/usr.sbin/pkg_install/delete/perform.c @@ -0,0 +1,216 @@ +# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:08 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:08 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the main body of the delete module. + * + */ + +#include "lib.h" +#include "delete.h" + +static int pkg_do(char *); +static void sanity_check(char *); +static void undepend(PackingList, char *); +static char LogDir[FILENAME_MAX]; + + +int +pkg_perform(char **pkgs) +{ + int i, err_cnt = 0; + + for (i = 0; pkgs[i]; i++) + err_cnt += pkg_do(pkgs[i]); + return err_cnt; +} + +static Package Plist; + +/* This is seriously ugly code following. Written very fast! */ +static int +pkg_do(char *pkg) +{ + FILE *cfile; + char home[FILENAME_MAX]; + PackingList p; + char *tmp; + + /* Reset some state */ + if (Plist.head) + free_plist(&Plist); + + sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + pkg); + if (!fexists(LogDir)) { + whinge("No such package '%s' installed.", pkg); + return 1; + } + if (!getcwd(home, FILENAME_MAX)) + barf("Unable to get current working directory!"); + if (chdir(LogDir) == FAIL) { + whinge("Unable to change directory to %s! Deinstall failed.", LogDir); + return 1; + } + if (!isemptyfile(REQUIRED_BY_FNAME)) { + char buf[512]; + whinge("Package `%s' is required by these other packages", pkg); + whinge("and may not be deinstalled%s:", Force ? " (but I'll delete it anyway)" : "" ); + cfile = fopen(REQUIRED_BY_FNAME, "r"); + if (cfile) { + while (fgets(buf, sizeof(buf), cfile)) + fprintf(stderr, "%s", buf); + fclose(cfile); + } else + whinge("cannot open requirements file `%s'", REQUIRED_BY_FNAME); + if (!Force) + return 1; + } + sanity_check(LogDir); + cfile = fopen(CONTENTS_FNAME, "r"); + if (!cfile) { + whinge("Unable to open '%s' file.", CONTENTS_FNAME); + return 1; + } + /* If we have a prefix, add it now */ + if (Prefix) + add_plist(&Plist, PLIST_CWD, Prefix); + read_plist(&Plist, cfile); + fclose(cfile); + p = find_plist(&Plist, PLIST_CWD); + if (p) + setenv(PKG_PREFIX_VNAME, p->name, 1); + else + unsetenv(PKG_PREFIX_VNAME); + if (fexists(REQUIRE_FNAME)) { + if (Verbose) + printf("Executing 'require' script.\n"); + vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ + if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { + whinge("Package %s fails requirements %s", pkg, + Force ? "." : "- not deleted."); + if (!Force) + return 1; + } + } + if (!NoDeInstall && fexists(DEINSTALL_FNAME)) { + if (Fake) + printf("Would execute de-install script at this point.\n"); + else { + vsystem("chmod +x %s", DEINSTALL_FNAME); /* make sure */ + if (vsystem("./%s %s DEINSTALL", DEINSTALL_FNAME, pkg)) { + whinge("De-Install script returned error status."); + if (!Force) + return 1; + } + } + } + if (chdir(home) == FAIL) + barf("Toto! This doesn't look like Kansas anymore!"); + if (!Fake) { + /* Some packages aren't packed right, so we need to just ignore delete_package()'s status. Ugh! :-( */ + if (delete_package(FALSE, CleanDirs, &Plist) == FAIL) + warn("Couldn't entirely delete package (perhaps the packing list is\n" + "incorrectly specified?)\n"); + if (vsystem("%s -r %s", REMOVE_CMD, LogDir)) { + whinge("Couldn't remove log entry in %s, de-install failed.", LogDir); + return 1; + } + } + for (p = Plist.head; p ; p = p->next) { + if (p->type != PLIST_PKGDEP) + continue; + if (Verbose) + printf("Attempting to remove dependency on package `%s'\n", + p->name); + if (!Fake) + undepend(p, pkg); + } + return 0; +} + +static void +sanity_check(char *pkg) +{ + if (!fexists(CONTENTS_FNAME)) + barf("Installed package %s has no %s file!", pkg, CONTENTS_FNAME); +} + +void +cleanup(int sig) +{ + /* Nothing to do */ +} + +static void +undepend(PackingList p, char *pkgname) +{ + char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; + char fbuf[FILENAME_MAX]; + FILE *fp, *fpwr; + char *tmp; + int s; + + sprintf(fname, "%s/%s/%s", + (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + p->name, REQUIRED_BY_FNAME); + fp = fopen(fname, "r"); + if (fp == NULL) { + whinge("Couldn't open dependency file `%s'", fname); + return; + } + sprintf(ftmp, "%s.XXXXXX", fname); + s = mkstemp(ftmp); + if (s == -1) { + fclose(fp); + whinge("Couldn't open temp file `%s'", ftmp); + return; + } + fpwr = fdopen(s, "w"); + if (fpwr == NULL) { + close(s); + fclose(fp); + whinge("Couldn't fdopen temp file `%s'", ftmp); + remove(ftmp); + return; + } + while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { + if (fbuf[strlen(fbuf)-1] == '\n') + fbuf[strlen(fbuf)-1] = '\0'; + if (strcmp(fbuf, pkgname)) /* no match */ + fputs(fbuf, fpwr), putc('\n', fpwr); + } + (void) fclose(fp); + if (fchmod(s, 0644) == FAIL) { + whinge("Error changing permission of temp file `%s'", ftmp); + fclose(fpwr); + remove(ftmp); + return; + } + if (fclose(fpwr) == EOF) { + whinge("Error closing temp file `%s'", ftmp); + remove(ftmp); + return; + } + if (rename(ftmp, fname) == -1) + warn("Error renaming `%s' to `%s'", ftmp, fname); + remove(ftmp); /* just in case */ + return; +} diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1 new file mode 100644 index 00000000000..3583f114769 --- /dev/null +++ b/usr.sbin/pkg_install/delete/pkg_delete.1 @@ -0,0 +1,185 @@ +.\" $OpenBSD: pkg_delete.1,v 1.1 1996/06/04 07:56:08 niklas Exp $ +.\" +.\" FreeBSD install - a package for the installation and maintainance +.\" of non-core utilities. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" Jordan K. Hubbard +.\" +.\" +.\" @(#)pkg_delete.1 +.\" +.Dd November 25, 1994 +.Dt pkg_delete 1 +.Os FreeBSD 2.0 +.Sh NAME +.Nm pkg_delete +.Nd a utility for deleting previously installed software package distributions. +.Sh SYNOPSIS +.Nm +.Op Fl vDdnf +.Op Fl p Ar prefix +.Ar pkg-name ... +.Sh DESCRIPTION +The +.Nm +command is used to delete packages that have been previously installed +with the +.Xr pkg_add 1 +command. + +.Sh WARNING +.Bf -emphasis +Since the +.Nm +command may execute scripts or programs provided by a package file, +your system may be susceptible to ``trojan horses'' or other subtle +attacks from miscreants who create dangerous package files. +.Pp +You are advised to verify the competence and identity of those who +provide installable package files. For extra protection, examine all +the package control files in the package record directory ( +.Pa /var/db/pkg/<pkg-name>/ ). +Pay particular +attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files, +and inspect the +CONTENTS file for +.Cm @cwd , +.Cm @mode +(check for setuid), +.Cm @dirrm , +.Cm @exec , +and +.Cm @unexec +directives, and/or use the +.Xr pkg_info 1 +command to examine the installed package control files. +.Ef + +.Sh OPTIONS +The following command line options are supported. +.Bl -tag -width indent +.It Ar pkg-name ... +The named packages are deinstalled. +.It Fl v +Turns on verbose output. +.Em "Optional." +.It Fl D +If a deinstallation script exists for a given package, do not execute it. +.Em "Optional." +.It Fl n +Don't actually deinstall a package, just report the steps that +would be taken if it were. +.Em "Optional." +.It Fl p Ar prefix +Sets +.Ar prefix +as the directory in which to delete files from any installed packages +which do not explicitly set theirs. For most packages, the prefix will +be set automatically to the installed location by +.Xr pkg_add 1 . +.Em "Optional." +.It Fl d +Remove empty directories created by file cleanup. By default, only +files/directories explicitly listed in a package's contents (either as +normal files/directories or with the +.Cm @dirrm +directive) will be removed at deinstallation time. This option tells +.Nm +to also remove any directories that were emptied as a result of removing +the package. +.Em "Optional." +.It Fl f +Force removal of the package, even if a dependency is recorded or the +deinstall or require script fails. +.Em "Optional." +.El + +.Pp +.Sh TECHNICAL DETAILS +.Nm +does pretty much what it says. It examines installed package records in +.Pa /var/db/pkg/<pkg-name> , +deletes the package contents, and finally removes the package records. +.Pp +If a package is required by other installed packages, +.Nm +will list those dependent packages and refuse to delete the package +(unless the +.Fl f +option is given). +.Pp +If the package contains a +.Ar require +file (see +.Xr pkg_create 1 ), +then this is executed first as +.Bd -filled -offset indent -compact +.Cm require +.Ar <pkg-name> +.Ar DEINSTALL +.Ed +(where +.Ar pkg-name +is the name of the package in question and +.I DEINSTALL +is a keyword denoting that this is a deinstallation) +to see whether or not deinstallation should continue. A non-zero exit +status means no, unless the +.Fl f +option is specified. +.Pp +If a +.Cm deinstall +script exists for the package, it is executed before any files are removed. +It is this script's responsibility to clean up any additional messy details +around the package's installation, since all +.Nm +knows how to do is delete the files created in the original distribution. +The +.Nm deinstall +script is called as: +.Bd -filled -offset indent -compact +.Cm deinstall +.Ar <pkg-name> +.Ar DEINSTALL +.Ed +Passing the keyword +.Ar DEINSTALL +lets you potentially write only one program/script that handles all +aspects of installation and deletion. +.Pp +All scripts are called with the environment variable +.Ev PKG_PREFIX +set to the installation prefix (see the +.Fl p +option above). This allows a package author to write a script +that reliably performs some action on the directory where the package +is installed, even if the user might have changed it by specifying the +.Fl p +option when running +.Nm +or +.Cm pkg_add . +.Sh SEE ALSO +.Xr pkg_add 1 , +.Xr pkg_create 1 , +.Xr pkg_info 1 , +.Xr mktemp 3 , +.Xr mtree 8 . +.Sh AUTHORS +.Bl -tag -width indent -compact +.It "Jordan Hubbard" +most of the work +.It "John Kohl" +refined it for NetBSD +.El +.Sh BUGS +Sure to be some. diff --git a/usr.sbin/pkg_install/info/Makefile b/usr.sbin/pkg_install/info/Makefile new file mode 100644 index 00000000000..854b0e4a9d3 --- /dev/null +++ b/usr.sbin/pkg_install/info/Makefile @@ -0,0 +1,15 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:09 niklas Exp $ +PROG= pkg_info +CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib + +.if exists(${.CURDIR}/../lib/obj) +LDADD+= -L${.CURDIR}/../lib/obj -linstall +DPADD+= ${.CURDIR}/../lib/obj/libinstall.a +.else +LDADD+= -L${.CURDIR}/../lib -linstall +DPADD+= ${.CURDIR}/../lib/libinstall.a +.endif + +SRCS= main.c perform.c show.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h new file mode 100644 index 00000000000..b8ad23bc0e1 --- /dev/null +++ b/usr.sbin/pkg_install/info/info.h @@ -0,0 +1,59 @@ +/* $OpenBSD: info.h,v 1.1 1996/06/04 07:56:09 niklas Exp $ */ + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 23 August 1993 + * + * Include and define various things wanted by the info command. + * + */ + +#ifndef _INST_INFO_H_INCLUDE +#define _INST_INFO_H_INCLUDE + +#ifndef MAXINDEXSIZE +#define MAXINDEXSIZE 60 +#endif + +#ifndef MAXNAMESIZE +#define MAXNAMESIZE 20 +#endif + +#define SHOW_COMMENT 0x0001 +#define SHOW_DESC 0x0002 +#define SHOW_PLIST 0x0004 +#define SHOW_INSTALL 0x0008 +#define SHOW_DEINSTALL 0x0010 +#define SHOW_REQUIRE 0x0020 +#define SHOW_PREFIX 0x0040 +#define SHOW_INDEX 0x0080 +#define SHOW_FILES 0x0100 +#define SHOW_DISPLAY 0x0200 +#define SHOW_REQBY 0x0400 +#define SHOW_MTREE 0x0800 + +extern int Flags; +extern Boolean AllInstalled; +extern Boolean Quiet; +extern char *InfoPrefix; +extern char PlayPen[]; +extern char *CheckPkg; + +extern void show_file(char *, char *); +extern void show_plist(char *, Package *, plist_t); +extern void show_files(char *, Package *); +extern void show_index(char *, char *); + +#endif /* _INST_INFO_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c new file mode 100644 index 00000000000..0564276ef86 --- /dev/null +++ b/usr.sbin/pkg_install/info/main.c @@ -0,0 +1,182 @@ +# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:09 niklas Exp $ +#ifndef lint +static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:09 niklas Exp $"; +#endif + +/* + * + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the add module. + * + */ + +#include "lib.h" +#include "info.h" + +static char Options[] = "acdDe:fikrRpLqImvhl:"; + +int Flags = 0; +Boolean AllInstalled = FALSE; +Boolean Quiet = FALSE; +char *InfoPrefix = ""; +char PlayPen[FILENAME_MAX]; +char *CheckPkg = NULL; + +int +main(int argc, char **argv) +{ + int ch; + char **pkgs, **start; + char *prog_name = argv[0]; + + pkgs = start = argv; + while ((ch = getopt(argc, argv, Options)) != EOF) + switch(ch) { + case 'a': + AllInstalled = TRUE; + break; + + case 'v': + Verbose = TRUE; + /* Reasonable definition of 'everything' */ + Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL | + SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE; + break; + + case 'I': + Flags |= SHOW_INDEX; + break; + + case 'p': + Flags |= SHOW_PREFIX; + break; + + case 'c': + Flags |= SHOW_COMMENT; + break; + + case 'd': + Flags |= SHOW_DESC; + break; + + case 'D': + Flags |= SHOW_DISPLAY; + break; + + case 'f': + Flags |= SHOW_PLIST; + break; + + case 'i': + Flags |= SHOW_INSTALL; + break; + + case 'k': + Flags |= SHOW_DEINSTALL; + break; + + case 'r': + Flags |= SHOW_REQUIRE; + break; + + case 'R': + Flags |= SHOW_REQBY; + break; + + case 'L': + Flags |= SHOW_FILES; + break; + + case 'm': + Flags |= SHOW_MTREE; + break; + + case 'l': + InfoPrefix = optarg; + break; + + case 'q': + Quiet = TRUE; + break; + + case 't': + strcpy(PlayPen, optarg); + break; + + case 'e': + CheckPkg = optarg; + break; + + case 'h': + case '?': + default: + usage(prog_name, NULL); + break; + } + + argc -= optind; + argv += optind; + + /* Set some reasonable defaults */ + if (!Flags) + Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY; + + /* Get all the remaining package names, if any */ + while (*argv) + *pkgs++ = *argv++; + + /* If no packages, yelp */ + if (pkgs == start && !AllInstalled && !CheckPkg) + usage(prog_name, "Missing package name(s)"); + *pkgs = NULL; + return pkg_perform(start); +} + +void +usage(const char *name, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (fmt) { + fprintf(stderr, "%s: ", name); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n\n"); + } + va_end(args); + fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name); + fprintf(stderr, "Where args are one or more of:\n\n"); + fprintf(stderr, "-a show all installed packages (if any)\n"); + fprintf(stderr, "-I print 'index' of packages\n"); + fprintf(stderr, "-c print `one line comment'\n"); + fprintf(stderr, "-d print description\n"); + fprintf(stderr, "-D print install notice\n"); + fprintf(stderr, "-f show packing list\n"); + fprintf(stderr, "-i show install script\n"); + fprintf(stderr, "-k show deinstall script\n"); + fprintf(stderr, "-r show requirements script\n"); + fprintf(stderr, "-R show packages depending on this package\n"); + fprintf(stderr, "-p show prefix\n"); + fprintf(stderr, "-l <str> Prefix each info catagory with <str>\n"); + fprintf(stderr, "-L show intalled files\n"); + fprintf(stderr, "-q minimal output (``quiet'' mode)\n"); + fprintf(stderr, "-v show all information\n"); + fprintf(stderr, "-t temp use temp as template for mktemp()\n"); + fprintf(stderr, "-e pkg returns 0 if pkg is installed, 1 otherwise\n"); + fprintf(stderr, "\n[no args = -c -d -R]\n"); + exit(1); +} diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c new file mode 100644 index 00000000000..84900147a91 --- /dev/null +++ b/usr.sbin/pkg_install/info/perform.c @@ -0,0 +1,211 @@ +# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:10 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:10 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 23 Aug 1993 + * + * This is the main body of the info module. + * + */ + +#include "lib.h" +#include "info.h" + +#include <signal.h> + +static int pkg_do(char *); + +int +pkg_perform(char **pkgs) +{ + int i, err_cnt = 0; + char *tmp; + + signal(SIGINT, cleanup); + + tmp = getenv(PKG_DBDIR); + if (!tmp) + tmp = DEF_LOG_DIR; + /* Overriding action? */ + if (AllInstalled || CheckPkg) { + if (isdir(tmp)) { + DIR *dirp; + struct dirent *dp; + + dirp = opendir(tmp); + if (dirp) { + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { + if (CheckPkg) { + if (!strcmp(dp->d_name, CheckPkg)) + return 0; + } + else + err_cnt += pkg_do(dp->d_name); + } + } + (void)closedir(dirp); + if (CheckPkg) + return 1; + } + else + ++err_cnt; + } else if (CheckPkg) + return 1; /* no dir -> not installed! */ + + } + for (i = 0; pkgs[i]; i++) + err_cnt += pkg_do(pkgs[i]); + return err_cnt; +} + +static char *Home; + +static int +pkg_do(char *pkg) +{ + Boolean installed = FALSE, isTMP = FALSE; + char log_dir[FILENAME_MAX]; + char fname[FILENAME_MAX]; + Package plist; + FILE *fp; + struct stat sb; + char *cp = NULL; + int code = 0; + + if (isURL(pkg)) { + if ((cp = fileGetURL(NULL, pkg)) != NULL) { + strcpy(fname, cp); + isTMP = TRUE; + } + } + else if (fexists(pkg) && isfile(pkg)) { + int len; + + if (*pkg != '/') { + if (!getcwd(fname, FILENAME_MAX)) + upchuck("getcwd"); + len = strlen(fname); + snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg); + } + else + strcpy(fname, pkg); + cp = fname; + } + else { + if ((cp = fileFindByPath(NULL, pkg)) != NULL) + strncpy(fname, cp, FILENAME_MAX); + } + if (cp) { + /* + * Apply a crude heuristic to see how much space the package will + * take up once it's unpacked. I've noticed that most packages + * compress an average of 75%, but we're only unpacking the + files so + * be very optimistic. + */ + if (stat(fname, &sb) == FAIL) { + whinge("Can't stat package file '%s'.", fname); + code = 1; + goto bail; + } + Home = make_playpen(PlayPen, sb.st_size / 2); + if (unpack(fname, "+*")) { + whinge("Error during unpacking, no info for '%s' available.", pkg); + code = 1; + goto bail; + } + } + /* It's not an ininstalled package, try and find it among the installed */ + else { + char *tmp; + + sprintf(log_dir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + pkg); + if (!fexists(log_dir)) { + whinge("Can't find package `%s' installed or in a file!", pkg); + return 1; + } + if (chdir(log_dir) == FAIL) { + whinge("Can't change directory to '%s'!", log_dir); + return 1; + } + installed = TRUE; + } + + /* Suck in the contents list */ + plist.head = plist.tail = NULL; + fp = fopen(CONTENTS_FNAME, "r"); + if (!fp) { + whinge("Unable to open %s file.", CONTENTS_FNAME); + code = 1; + goto bail; + } + /* If we have a prefix, add it now */ + read_plist(&plist, fp); + fclose(fp); + + /* + * Index is special info type that has to override all others to make + * any sense. + */ + if (Flags & SHOW_INDEX) { + char tmp[FILENAME_MAX]; + + snprintf(tmp, FILENAME_MAX, "%-19s ", pkg); + show_index(tmp, COMMENT_FNAME); + } + else { + /* Start showing the package contents */ + if (!Quiet) + printf("%sInformation for %s:\n\n", InfoPrefix, pkg); + if (Flags & SHOW_COMMENT) + show_file("Comment:\n", COMMENT_FNAME); + if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME)) + show_file("Required by:\n", REQUIRED_BY_FNAME); + if (Flags & SHOW_DESC) + show_file("Description:\n", DESC_FNAME); + if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME)) + show_file("Install notice:\n", DISPLAY_FNAME); + if (Flags & SHOW_PLIST) + show_plist("Packing list:\n", &plist, (plist_t)-1); + if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME)) + show_file("Install script:\n", INSTALL_FNAME); + if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME)) + show_file("De-Install script:\n", DEINSTALL_FNAME); + if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME)) + show_file("mtree file:\n", MTREE_FNAME); + if (Flags & SHOW_PREFIX) + show_plist("Prefix(s):\n", &plist, PLIST_CWD); + if (Flags & SHOW_FILES) + show_files("Files:\n", &plist); + if (!Quiet) + puts(InfoPrefix); + } + free_plist(&plist); + bail: + leave_playpen(Home); + if (isTMP) + unlink(fname); + return code; +} + +void +cleanup(int sig) +{ + leave_playpen(Home); +} diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1 new file mode 100644 index 00000000000..62c619db526 --- /dev/null +++ b/usr.sbin/pkg_install/info/pkg_info.1 @@ -0,0 +1,136 @@ +.\" $OpenBSD: pkg_info.1,v 1.1 1996/06/04 07:56:10 niklas Exp $ +.\" +.\" FreeBSD install - a package for the installation and maintainance +.\" of non-core utilities. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" Jordan K. Hubbard +.\" +.\" +.\" @(#)pkg_info.1 +.\" +.Dd November 25, 1994 +.Dt pkg_info 1 +.Os FreeBSD 2.0 +.Sh NAME +.Nm pkg_info +.Nd a utility for displaying information on software packages. +.Sh SYNOPSIS +.Nm pkg_info +.Op Fl cdDikrRpLqImv +.Op Fl e Ar package +.Op Fl l Ar prefix +.Ar pkg-name [pkg-name ...] +.Nm pkg_info +.Fl a +.Op Ar flags +.Sh DESCRIPTION +The +.Nm +command is used to dump out information for packages, either packed up in +files or already installed on the system +with the +.Xr pkg_create 1 +command. +.Sh OPTIONS +The following command line options are supported. +.Bl -tag -width indent +.It Ar pkg-name ... +The named packages are described. A package name may either be the name of +an installed package, the pathname to a package distribution file or a +URL to an ftp available package. +.It Fl a +Show all currently installed packages. +.It Fl v +Turns on verbose output. +.It Fl p +Show the installation prefix for each package. +.It Fl q +Be ``quiet'' in emitting report headers and such, just dump the +raw info (basically, assume a non-human reading). +.It Fl c +Show the comment (one liner) field for each package. +.It Fl d +Show the long description field for each package. +.It Fl D +Show the install-message file for each package. +.It Fl f +Show the packing list instructions for each package. +.It Fl i +Show the install script (if any) for each package. +.It Fl k +Show the de-install script (if any) for each package. +.It Fl r +Show the requirements script (if any) for each package. +.It Fl m +Show the mtree file (if any) for each package. +.It Fl L +Show the files within each package. This is different from just +viewing the packing list, since full pathnames for everything +are generated. +.It Fl e Ar pkg-name +If the package identified by +.Ar pkg-name +is currently installed, return 0, otherwise return 1. This option +allows you to easily test for the presence of another (perhaps +prerequisite) package from a script. +.It Fl l Ar str +Prefix each information catagory header (see +.Fl q ) +shown with +.Ar str . +This is primarily of use to front-end programs who want to request a +lot of different information fields at once for a package, but don't +necessary want the output intermingled in such a way that they can't +organize it. This lets you add a special token to the start of +each field. +.It Fl t Ar template +Use +.Ar template +as the input to +.Xr mktemp 3 +when creating a ``staging area.'' +By default, this is the string +.Pa /tmp/instmp.XXXXXX , +but it may be necessary to override it in the situation where +space in your +.Pa /tmp +directory is limited. Be sure to leave some number of `X' characters +for +.Xr mktemp 3 +to fill in with a unique ID. +.Bd -filled -offset indent -compact +Note: This should really not be necessary with pkg_info, +since very little information is extracted from each package +and one would have to have a very small +.Pa /tmp +indeed to overflow it. +.Ed +.Sh TECHNICAL DETAILS +Package info is either extracted from package files named on the +command line, or from already installed package information +in +.Pa /var/db/pkg/<pkg-name> . +.Sh SEE ALSO +.Xr pkg_add 1 , +.Xr pkg_create 1 , +.Xr pkg_delete 1 , +.Xr mktemp 3 , +.Xr mtree 8 . +.Sh AUTHORS +.Bl -tag -width indent -compact +.It "Jordan Hubbard" +most of the work +.It "John Kohl" +refined it for NetBSD +.El +.Sh BUGS +Sure to be some. diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c new file mode 100644 index 00000000000..3d38df6e20f --- /dev/null +++ b/usr.sbin/pkg_install/info/show.c @@ -0,0 +1,195 @@ +# $OpenBSD: show.c,v 1.1 1996/06/04 07:56:10 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: show.c,v 1.1 1996/06/04 07:56:10 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 23 Aug 1993 + * + * Various display routines for the info module. + * + */ + +#include "lib.h" +#include "info.h" + +void +show_file(char *title, char *fname) +{ + FILE *fp; + char line[1024]; + int n; + + if (!Quiet) + printf("%s%s", InfoPrefix, title); + fp = fopen(fname, "r"); + if (!fp) + printf("ERROR: show_file: Can't open '%s' for reading!\n", fname); + else { + while (n = fread(line, 1, 1024, fp)) + fwrite(line, 1, n, stdout); + fclose(fp); + } + printf("\n"); /* just in case */ +} + +void +show_index(char *title, char *fname) +{ + FILE *fp; + char line[MAXINDEXSIZE+2]; + int i,n; + + if (!Quiet) + printf("%s%s", InfoPrefix, title); + fp = fopen(fname, "r"); + if (!fp) { + whinge("show_file: Can't open '%s' for reading.", fname); + return; + } + if(fgets(line, MAXINDEXSIZE+1, fp)) { + if(line[MAXINDEXSIZE-1] != '\n') + line[MAXINDEXSIZE] = '\n'; + line[MAXINDEXSIZE+1] = 0; + fputs(line, stdout); + } + fclose(fp); +} + +/* Show a packing list item type. If type is -1, show all */ +void +show_plist(char *title, Package *plist, plist_t type) +{ + PackingList p; + Boolean ign = FALSE; + + if (!Quiet) + printf("%s%s", InfoPrefix, title); + p = plist->head; + while (p) { + if (p->type != type && type != -1) { + p = p->next; + continue; + } + switch(p->type) { + case PLIST_FILE: + if (ign) { + printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name); + ign = FALSE; + } + else + printf(Quiet ? "%s\n" : "File: %s\n", p->name); + break; + + case PLIST_CWD: + printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", p->name); + break; + + case PLIST_SRC: + printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name); + break; + + case PLIST_CMD: + printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name); + break; + + case PLIST_CHMOD: + printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n", + p->name ? p->name : "(clear default)"); + break; + + case PLIST_CHOWN: + printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n", + p->name ? p->name : "(clear default)"); + break; + + case PLIST_CHGRP: + printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n", + p->name ? p->name : "(clear default)"); + break; + + case PLIST_COMMENT: + printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name); + break; + + case PLIST_IGNORE: + ign = TRUE; + break; + + case PLIST_IGNORE_INST: + printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" : + "\tIgnore next file installation directive (doesn't belong)\n"); + ign = TRUE; + break; + + case PLIST_NAME: + printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name); + break; + + case PLIST_DISPLAY: + printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name); + break; + + case PLIST_PKGDEP: + printf(Quiet ? "@pkgdep %s\n" : "\tPackage depends on: %s\n", p->name); + break; + + case PLIST_MTREE: + printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name); + break; + + case PLIST_DIR_RM: + printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name); + break; + + default: + barf("Unknown command type %d (%s)\n", p->type, p->name); + break; + } + p = p->next; + } +} + +/* Show all files in the packing list (except ignored ones) */ +void +show_files(char *title, Package *plist) +{ + PackingList p; + Boolean ign = FALSE; + char *dir = "."; + + if (!Quiet) + printf("%s%s", InfoPrefix, title); + p = plist->head; + while (p) { + switch(p->type) { + case PLIST_FILE: + if (!ign) + printf("%s/%s\n", dir, p->name); + ign = FALSE; + break; + + case PLIST_CWD: + dir = p->name; + break; + + case PLIST_IGNORE: + ign = TRUE; + break; + } + p = p->next; + } +} diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile new file mode 100644 index 00000000000..ff1e3e686e9 --- /dev/null +++ b/usr.sbin/pkg_install/lib/Makefile @@ -0,0 +1,11 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:11 niklas Exp $ +LIB= install +SRCS= file.c ftp.c msg.c plist.c str.c exec.c global.c pen.c +CFLAGS+= ${DEBUG} +NOPROFILE= yes +NOPIC= yes + +install: + @echo -n + +.include <bsd.lib.mk> diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c new file mode 100644 index 00000000000..6f10c3ce944 --- /dev/null +++ b/usr.sbin/pkg_install/lib/exec.c @@ -0,0 +1,61 @@ +# $OpenBSD: exec.c,v 1.1 1996/06/04 07:56:11 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: exec.c,v 1.1 1996/06/04 07:56:11 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Miscellaneous system routines. + * + */ + +#include "lib.h" + +/* + * Unusual system() substitute. Accepts format string and args, + * builds and executes command. Returns exit code. + */ + +int +vsystem(const char *fmt, ...) +{ + va_list args; + char *cmd; + int ret, maxargs; + + maxargs = sysconf(_SC_ARG_MAX); + maxargs -= 32; /* some slop for the sh -c */ + cmd = malloc(maxargs); + if (!cmd) { + whinge("vsystem can't alloc arg space"); + return 1; + } + + va_start(args, fmt); + if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) { + whinge("vsystem args are too long"); + return 1; + } +#ifdef DEBUG +printf("Executing %s\n", cmd); +#endif + ret = system(cmd); + va_end(args); + free(cmd); + return ret; +} + diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c new file mode 100644 index 00000000000..d37af1e8a07 --- /dev/null +++ b/usr.sbin/pkg_install/lib/file.c @@ -0,0 +1,516 @@ +# $OpenBSD: file.c,v 1.1 1996/06/04 07:56:11 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: file.c,v 1.1 1996/06/04 07:56:11 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Miscellaneous file access utilities. + * + */ + +#include "lib.h" +#include "ftp.h" +#include <pwd.h> +#include <time.h> + +/* Quick check to see if a file exists */ +Boolean +fexists(char *fname) +{ + struct stat dummy; + if (!lstat(fname, &dummy)) + return TRUE; + return FALSE; +} + +/* Quick check to see if something is a directory */ +Boolean +isdir(char *fname) +{ + struct stat sb; + + if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) + return TRUE; + else + return FALSE; +} + +/* Check to see if file is a dir, and is empty */ +Boolean +isemptydir(char *fname) +{ + if (isdir(fname)) { + DIR *dirp; + struct dirent *dp; + + dirp = opendir(fname); + if (!dirp) + return FALSE; /* no perms, leave it alone */ + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { + closedir(dirp); + return FALSE; + } + } + (void)closedir(dirp); + return TRUE; + } + return FALSE; +} + +Boolean +isfile(char *fname) +{ + struct stat sb; + if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) + return TRUE; + return FALSE; +} + +/* Check to see if file is a file and is empty. If nonexistent or not + a file, say "it's empty", otherwise return TRUE if zero sized. */ +Boolean +isemptyfile(char *fname) +{ + struct stat sb; + if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { + if (sb.st_size != 0) + return FALSE; + } + return TRUE; +} + +/* Returns TRUE if file is a URL specification */ +Boolean +isURL(char *fname) +{ + /* + * I'm sure there are other types of URL specifications that I could + * also be looking for here, but for now I'll just be happy to get ftp + * working. + */ + if (!fname) + return FALSE; + while (isspace(*fname)) + ++fname; + if (!strncmp(fname, "ftp://", 6)) + return TRUE; + return FALSE; +} + +/* Returns the host part of a URL */ +char * +fileURLHost(char *fname, char *where, int max) +{ + char *ret; + + while (isspace(*fname)) + ++fname; + /* Don't ever call this on a bad URL! */ + fname += strlen("ftp://"); + /* Do we have a place to stick our work? */ + if (ret = where) { + while (*fname && *fname != '/' && max--) + *where++ = *fname++; + *where = '\0'; + return ret; + } + /* If not, they must really want us to stomp the original string */ + ret = fname; + while (*fname && *fname != '/') + ++fname; + *fname = '\0'; + return ret; +} + +/* Returns the filename part of a URL */ +char * +fileURLFilename(char *fname, char *where, int max) +{ + char *ret; + + while (isspace(*fname)) + ++fname; + /* Don't ever call this on a bad URL! */ + fname += strlen("ftp://"); + /* Do we have a place to stick our work? */ + if (ret = where) { + while (*fname && *fname != '/') + ++fname; + if (*fname == '/') { + while (*fname && max--) + *where++ = *fname++; + } + *where = '\0'; + return ret; + } + /* If not, they must really want us to stomp the original string */ + while (*fname && *fname != '/') + ++fname; + return fname; +} + +#define HOSTNAME_MAX 64 +/* + * Try and fetch a file by URL, returning the directory name for where + * it's unpacked, if successful. + */ +char * +fileGetURL(char *base, char *spec) +{ + char host[HOSTNAME_MAX], file[FILENAME_MAX], dir[FILENAME_MAX]; + char pword[HOSTNAME_MAX + 40], *uname, *cp, *rp, *tmp; + char fname[FILENAME_MAX]; + char pen[FILENAME_MAX]; + struct passwd *pw; + FTP_t ftp; + pid_t tpid; + int fd, fd2, i, len = 0; + char ch; + time_t start, stop; + char *hint; + + rp = NULL; + /* Special tip that sysinstall left for us */ + hint = getenv("PKG_ADD_BASE"); + if (!isURL(spec)) { + int len; + + if (!base && !hint) + return NULL; + /* We've been given an existing URL (that's known-good) and now we need + to construct a composite one out of that and the basename we were + handed as a dependency. */ + if (base) { + strcpy(fname, base); + /* Advance back two slashes to get to the root of the package hierarchy */ + cp = strrchr(fname, '/'); + if (cp) { + *cp = '\0'; /* chop name */ + cp = strrchr(fname, '/'); + } + if (cp) { + *(cp + 1) = '\0'; + strcat(cp, "All/"); + strcat(cp, spec); + } + else + return NULL; + } + else { + /* Otherwise, we've been given an environment variable hinting at the right location from sysinstall */ + strcpy(fname, hint); + strcat(fname, spec); + } + } + else + strcpy(fname, spec); + ftp = FtpInit(); + cp = fileURLHost(fname, host, HOSTNAME_MAX); + if (!*cp) { + whinge("URL `%s' has bad host part!", fname); + return NULL; + } + + cp = fileURLFilename(fname, file, FILENAME_MAX); + if (!*cp) { + whinge("URL `%s' has bad filename part!", fname); + return NULL; + } + + /* Maybe change to ftp if this doesn't work */ + uname = "anonymous"; + + /* Make up a convincing "password" */ + pw = getpwuid(getuid()); + if (!pw) { + whinge("Can't get user name for ID %d\n.", getuid()); + strcpy(pword, "joe@"); + } + else + snprintf(pword, HOSTNAME_MAX + 40, "%s@%s", pw->pw_name, host); + + if (Verbose) + printf("Trying to log into %s as %s.\n", host, uname); + FtpOpen(ftp, host, uname, pword); + if (getenv("FTP_PASSIVE_MODE")) + FtpPassive(ftp, TRUE); + + strcpy(dir, file); + for (i = strlen(dir); i && dir[i] != '/'; i--); + dir[i] = '\0'; + + if (dir[0]) { + if (Verbose) printf("FTP: chdir to %s\n", dir); + FtpChdir(ftp, dir); + } + FtpBinary(ftp, TRUE); + if (Verbose) printf("FTP: trying to get %s\n", basename_of(file)); + tmp = basename_of(file); + if (!strstr(tmp, ".tgz")) + tmp = strconcat(tmp, ".tgz"); + fd = FtpGet(ftp, tmp); + if (fd >= 0) { + pen[0] = '\0'; + if (rp = make_playpen(pen, 0)) { + if (Verbose) + printf("Extracting from FTP connection into %s\n", pen); + tpid = fork(); + if (!tpid) { + dup2(fd, 0); + i = execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-", 0); + if (Verbose) + printf("tar command returns %d status\n", i); + exit(i); + } + else { + int pstat; + + close(fd); + tpid = waitpid(tpid, &pstat, 0); + } + } + else + printf("Error: Unable to construct a new playpen for FTP!\n"); + } + else + printf("Error: FTP Unable to get %s\n", basename_of(file)); + FtpEOF(ftp); + FtpClose(ftp); + return rp; +} + +char * +fileFindByPath(char *base, char *fname) +{ + static char tmp[FILENAME_MAX]; + char *cp; + + if (fexists(fname) && isfile(fname)) { + strcpy(tmp, fname); + return tmp; + } + if (base) { + strcpy(tmp, base); + + cp = strrchr(fname, '/'); + if (cp) { + *cp = '\0'; /* chop name */ + cp = strrchr(fname, '/'); + } + if (cp) { + *(cp + 1) = '\0'; + strcat(cp, "All/"); + strcat(cp, fname); + if (fexists(tmp)) + return tmp; + } + } + + cp = getenv("PKG_PATH"); + while (cp) { + char *cp2 = strsep(&cp, ":"); + + snprintf(tmp, FILENAME_MAX, "%s/%s.tgz", cp2 ? cp2 : cp, fname); + if (fexists(tmp) && isfile(tmp)) + return tmp; + } + return NULL; +} + +char * +fileGetContents(char *fname) +{ + char *contents; + struct stat sb; + int fd; + + if (stat(fname, &sb) == FAIL) + barf("Can't stat '%s'.", fname); + + contents = (char *)malloc(sb.st_size + 1); + fd = open(fname, O_RDONLY, 0); + if (fd == FAIL) + barf("Unable to open '%s' for reading.", fname); + if (read(fd, contents, sb.st_size) != sb.st_size) + barf("Short read on '%s' - did not get %qd bytes.", fname, sb.st_size); + close(fd); + contents[sb.st_size] = '\0'; + return contents; +} + +/* Write the contents of "str" to a file */ +void +write_file(char *name, char *str) +{ + FILE *fp; + int len; + + fp = fopen(name, "w"); + if (!fp) + barf("Can't fopen '%s' for writing.", name); + len = strlen(str); + if (fwrite(str, 1, len, fp) != len) + barf("Short fwrite on '%s', tried to write %d bytes.", name, len); + if (fclose(fp)) + barf("failure to fclose '%s'.", name); +} + +void +copy_file(char *dir, char *fname, char *to) +{ + char cmd[FILENAME_MAX]; + + if (fname[0] == '/') + snprintf(cmd, FILENAME_MAX, "cp -p -r %s %s", fname, to); + else + snprintf(cmd, FILENAME_MAX, "cp -p -r %s/%s %s", dir, fname, to); + if (vsystem(cmd)) + barf("Couldn't perform '%s'", cmd); +} + +void +move_file(char *dir, char *fname, char *to) +{ + char cmd[FILENAME_MAX]; + + if (fname[0] == '/') + snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to); + else + snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to); + if (vsystem(cmd)) + barf("Couldn't perform '%s'", cmd); +} + +/* + * Copy a hierarchy (possibly from dir) to the current directory, or + * if "to" is TRUE, from the current directory to a location someplace + * else. + * + * Though slower, using tar to copy preserves symlinks and everything + * without me having to write some big hairy routine to do it. + */ +void +copy_hierarchy(char *dir, char *fname, Boolean to) +{ + char cmd[FILENAME_MAX * 3]; + + if (!to) { + /* If absolute path, use it */ + if (*fname == '/') + dir = "/"; + snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -", + dir, fname); + } + else + snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s", + fname, dir); +#ifdef DEBUG + printf("Using '%s' to copy trees.\n", cmd); +#endif + if (system(cmd)) + barf("copy_file: Couldn't perform '%s'", cmd); +} + +/* Unpack a tar file */ +int +unpack(char *pkg, char *flist) +{ + char args[10], suffix[80], *cp; + + args[0] = '\0'; + /* + * Figure out by a crude heuristic whether this or not this is probably + * compressed. + */ + if (strcmp(pkg, "-")) { + cp = rindex(pkg, '.'); + if (cp) { + strcpy(suffix, cp + 1); + if (index(suffix, 'z') || index(suffix, 'Z')) + strcpy(args, "-z"); + } + } + else + strcpy(args, "z"); + strcat(args, "xpf"); + if (vsystem("tar %s %s %s", args, pkg, flist ? flist : "")) { + whinge("Tar extract of %s failed!", pkg); + return 1; + } + return 0; +} + +/* Using fmt, replace all instances of: + * + * %F With the parameter "name" + * %D With the parameter "dir" + * %B Return the directory part ("base") of %D/%F + * %f Return the filename part of %D/%F + * + * Does not check for overflow - caution! + * + */ +void +format_cmd(char *buf, char *fmt, char *dir, char *name) +{ + char *cp, scratch[FILENAME_MAX * 2]; + + while (*fmt) { + if (*fmt == '%') { + switch (*++fmt) { + case 'F': + strcpy(buf, name); + buf += strlen(name); + break; + + case 'D': + strcpy(buf, dir); + buf += strlen(dir); + break; + + case 'B': + sprintf(scratch, "%s/%s", dir, name); + cp = &scratch[strlen(scratch) - 1]; + while (cp != scratch && *cp != '/') + --cp; + *cp = '\0'; + strcpy(buf, scratch); + buf += strlen(scratch); + break; + + case 'f': + sprintf(scratch, "%s/%s", dir, name); + cp = &scratch[strlen(scratch) - 1]; + while (cp != scratch && *(cp - 1) != '/') + --cp; + strcpy(buf, cp); + buf += strlen(cp); + break; + + default: + *buf++ = *fmt; + break; + } + ++fmt; + } + else + *buf++ = *fmt++; + } + *buf = '\0'; +} diff --git a/usr.sbin/pkg_install/lib/ftp.c b/usr.sbin/pkg_install/lib/ftp.c new file mode 100644 index 00000000000..ac507b6d6bd --- /dev/null +++ b/usr.sbin/pkg_install/lib/ftp.c @@ -0,0 +1,424 @@ + * $OpenBSD: ftp.c,v 1.1 1996/06/04 07:56:12 niklas Exp $ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * + * Return values have been sanitized: + * -1 error, but you (still) have a session. + * -2 error, your session is dead. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <netdb.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include "ftp.h" +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/* Handy global for us to stick the port # */ +int FtpPort; + +static void +debug(FTP_t ftp, const char *fmt, ...) +{ + char p[BUFSIZ]; + va_list ap; + va_start(ap, fmt); + strcpy(p,"LIBFTP: "); + (void) vsnprintf(p+strlen(p), sizeof p - strlen(p), fmt, ap); + va_end(ap); + write(ftp->fd_debug,p,strlen(p)); +} + +static int +writes(int fd, char *s) +{ + int i = strlen(s); + if (i != write(fd,s,i)) + return -2; + return 0; +} + +static __inline char* +get_a_line(FTP_t ftp) +{ + static char buf[BUFSIZ]; + int i,j; + + for(i=0;i<BUFSIZ;) { + j = read(ftp->fd_ctrl,buf+i,1); + if (j != 1) + return 0; + if (buf[i] == '\r' || buf[i] == '\n') { + if (!i) + continue; + buf[i] = '\0'; + debug(ftp, "received <%s>\n",buf); + return buf; + } + i++; + } + return buf; +} + +static int +get_a_number(FTP_t ftp, char **q) +{ + char *p; + int i = -1,j; + + while(1) { + p = get_a_line(ftp); + if (!p) + return -2; + if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2]))) + continue; + if (i == -1 && p[3] == '-') { + i = strtol(p, 0, 0); + continue; + } + if (p[3] != ' ' && p[3] != '\t') + continue; + j = strtol(p, 0, 0); + if (i == -1) { + if (q) *q = p+4; + return j; + } else if (j == i) { + if (q) *q = p+4; + return j; + } + } +} + +static int +zap(FTP_t ftp) +{ + int i; + + i = writes(ftp->fd_ctrl,"QUIT\r\n"); + debug(ftp, "Zapping ftp connection on %d returns %d\n", ftp->fd_ctrl, i); + close(ftp->fd_ctrl); ftp->fd_ctrl = -1; + close(ftp->fd_xfer); ftp->fd_xfer = -1; + ftp->state = init; + return -2; +} + +static int +botch(FTP_t ftp, char *func, char *state) +{ + debug(ftp, "Botch: %s called outside state %s\n",func,state); + return -2; +} + +static int +cmd(FTP_t ftp, const char *fmt, ...) +{ + char p[BUFSIZ]; + int i; + + va_list ap; + va_start(ap, fmt); + (void) vsnprintf(p, sizeof p, fmt, ap); + va_end(ap); + + debug(ftp, "send <%s>\n",p); + strcat(p,"\r\n"); + if (writes(ftp->fd_ctrl,p)) + return -2; + i = get_a_number(ftp,0); + return i; +} + +FTP_t +FtpInit() +{ + FTP_t ftp; + + ftp = malloc(sizeof *ftp); + if (!ftp) + return ftp; + memset(ftp, 0, sizeof *ftp); + ftp->fd_ctrl = -1; + ftp->fd_xfer = -1; + ftp->fd_debug = -1; + ftp->state = init; + return ftp; +} + +void +FtpDebug(FTP_t ftp, int i) +{ + ftp->fd_debug = i; +} + +int +FtpOpen(FTP_t ftp, char *host, char *user, char *passwd) +{ + struct hostent *he = NULL; + struct sockaddr_in sin; + int s; + unsigned long temp; + int i; + + if (ftp->state != init) + return botch(ftp,"FtpOpen","init"); + + if (!user) + user = "ftp"; + + if (!passwd) + passwd = "??@??(FreeBSD:libftp)"; /* XXX */ + + debug(ftp, "FtpOpen(ftp, %s, %s, %s)\n", host, user, passwd); + + temp = inet_addr(host); + if (temp != INADDR_NONE) { + debug(ftp, "Using dotted IP address `%s'\n", host); + ftp->addrtype = sin.sin_family = AF_INET; + sin.sin_addr.s_addr = temp; + } + else { + debug(ftp, "Trying to resolve `%s'\n", host); + he = gethostbyname(host); + if (!he) { + debug(ftp, "Lookup of `%s' failed!\n", host); + return zap(ftp); + } + ftp->addrtype = sin.sin_family = he->h_addrtype; + bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length); + } + + sin.sin_port = htons(FtpPort ? FtpPort : 21); + + if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) + { + debug(ftp, "Socket open failed: %s (%i)\n", strerror(errno), errno); + return zap(ftp); + } + + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + debug(ftp,"Connection failed: %s (%i)\n", strerror(errno), errno); + (void)close(s); + return zap(ftp); + } + + ftp->fd_ctrl = s; + + debug(ftp, "open (%d)\n",get_a_number(ftp,0)); + + i = cmd(ftp,"USER %s",user); + if (i >= 300 && i < 400) + i = cmd(ftp,"PASS %s",passwd); + if (i >= 299 || i < 0) { + close(ftp->fd_ctrl); ftp->fd_ctrl = -1; + return zap(ftp); + } + ftp->state = isopen; + return 0; +} + +void +FtpClose(FTP_t ftp) +{ + if (ftp->state != init) + return; + + if (ftp->state != isopen) + botch(ftp,"FtpClose","open or init"); + + debug(ftp, "FtpClose(ftp)\n"); + zap(ftp); +} + +int +FtpChdir(FTP_t ftp, char *dir) +{ + int i; + if (ftp->state != isopen) + return botch(ftp,"FtpChdir","open"); + i = cmd(ftp,"CWD %s",dir); + if (i < 0) + return i; + else if (i != 250) + return -1; + return 0; +} + +int +FtpGet(FTP_t ftp, char *file) +{ + int i,s; + char *q; + unsigned char addr[64]; + struct sockaddr_in sin; + u_long a; + + debug(ftp, "FtpGet(ftp,%s)\n",file); + if (ftp->state != isopen) + return botch(ftp,"FtpGet","open"); + if(ftp->binary) { + i = cmd(ftp,"TYPE I"); + if (i < 0) + return zap(ftp); + if (i > 299) + return -1; + } else { + return -1; + } + + if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) + return zap(ftp); + + if (ftp->passive) { + debug(ftp, "send <%s>\n","PASV"); + if (writes(ftp->fd_ctrl,"PASV\r\n")) + return zap(ftp); + i = get_a_number(ftp,&q); + if (i < 0) + return zap(ftp); + if (i != 227) + return zap(ftp); + while (*q && !isdigit(*q)) + q++; + if (!*q) + return zap(ftp); + q--; + for(i=0;i<6;i++) { + q++; + addr[i] = strtol(q,&q,10); + } + + sin.sin_family = ftp->addrtype; + bcopy(addr, (char *)&sin.sin_addr, 4); + bcopy(addr+4, (char *)&sin.sin_port, 2); + debug(ftp, "Opening active socket to %s : %u\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); + + debug(ftp, "Connecting to %s:%u\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + (void)close(s); + debug(ftp, "connect: %s (%d)\n", strerror(errno), errno); + return -1; + } + ftp->fd_xfer = s; + i = cmd(ftp,"RETR %s",file); + if (i < 0) { + close(s); + return zap(ftp); + } + else if (i > 299) { + debug(ftp, "FTP: No such file %s, moving on.\n", file); + close(s); + return -1; + } + ftp->state = xfer; + return s; + } else { + i = sizeof sin; + getsockname(ftp->fd_ctrl,(struct sockaddr *)&sin,&i); + sin.sin_port = 0; + i = sizeof sin; + if (bind(s,(struct sockaddr *)&sin, i) < 0) { + close (s); + debug(ftp,"bind failed %d\n",errno); + return zap(ftp); + } + getsockname(s,(struct sockaddr *)&sin,&i); + if (listen(s,1) < 0) { + close (s); + debug(ftp,"listen failed %d\n",errno); + return zap(ftp); + } + a = ntohl(sin.sin_addr.s_addr); + i = cmd(ftp,"PORT %d,%d,%d,%d,%d,%d", + (a >> 24) & 0xff, + (a >> 16) & 0xff, + (a >> 8) & 0xff, + a & 0xff, + (ntohs(sin.sin_port) >> 8) & 0xff, + ntohs(sin.sin_port) & 0xff); + if (i != 200) + return -1; + i = cmd(ftp,"RETR %s",file); + if (i < 0) { + close(s); + return zap(ftp); + } + else if (i > 299) { + debug(ftp, "FTP: No such file %s, moving on.\n", file); + close(s); + return -1; + } + ftp->fd_xfer = accept(s, 0, 0); + if (ftp->fd_xfer < 0) { + close(s); + return zap(ftp); + } + ftp->state = xfer; + close(s); + return(ftp->fd_xfer); + } +} + +int +FtpEOF(FTP_t ftp) +{ + int i; + + if (ftp->state != xfer) + return botch(ftp,"FtpEOF","xfer"); + debug(ftp, "FtpEOF(ftp)\n"); + close(ftp->fd_xfer); ftp->fd_xfer = -1; + ftp->state = isopen; + i = get_a_number(ftp,0); + if (i < 0) + return zap(ftp); + else if (i != 250 && i != 226) + return -1; + else + return 0; +} + +#ifdef STANDALONE_FTP + +/* main.c */ +int +main(int argc, char **argv) +{ + FTP_t ftp; + int i; + char c; + + ftp = FtpInit(); + if (!ftp) + err(1, "FtpInit()"); + + FtpDebug(ftp, 1); + i = FtpOpen(ftp, "freefall.cdrom.com", "ftp", "phk-libftp@"); + FtpBinary(ftp, 1); + FtpPassive(ftp, 0); + FtpChdir(ftp, "/pub"); + FtpChdir(ftp, "FreeBSD"); + i = FtpGet(ftp, "README"); + while (1 == read(i, &c, 1)) + putchar(c); + FtpEOF(ftp); + return 0; +} + +#endif /*STANDALONE_FTP*/ diff --git a/usr.sbin/pkg_install/lib/ftp.h b/usr.sbin/pkg_install/lib/ftp.h new file mode 100644 index 00000000000..b272d1c62de --- /dev/null +++ b/usr.sbin/pkg_install/lib/ftp.h @@ -0,0 +1,27 @@ +# $OpenBSD: ftp.h,v 1.1 1996/06/04 07:56:12 niklas Exp $ +#ifndef _FTP_H_INCLUDE +#define _FTP_H_INCLUDE + +typedef struct { + enum {init, isopen, xfer} state; + int fd_ctrl; + int fd_xfer; + int fd_debug; + int binary; + int passive; + int addrtype; + char *host; + char *file; +} *FTP_t; + +FTP_t FtpInit(); +int FtpOpen(FTP_t, char *host, char *user, char *passwd); +#define FtpBinary(ftp,bool) { (ftp)->binary = (bool); } +#define FtpPassive(ftp,bool) { (ftp)->passive = (bool); } +int FtpChdir(FTP_t, char *); +int FtpGet(FTP_t, char *); +int FtpEOF(FTP_t); +void FtpClose(FTP_t); + +#endif +/* _FTP_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/lib/global.c b/usr.sbin/pkg_install/lib/global.c new file mode 100644 index 00000000000..36d1a433b7b --- /dev/null +++ b/usr.sbin/pkg_install/lib/global.c @@ -0,0 +1,34 @@ +# $OpenBSD: global.c,v 1.1 1996/06/04 07:56:12 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: global.c,v 1.1 1996/06/04 07:56:12 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + + * 18 July 1993 + * + * Semi-convenient place to stick some needed globals. + * + */ + +#include "lib.h" + +/* These are global for all utils */ +Boolean Verbose = FALSE; +Boolean Fake = FALSE; +int AutoAnswer = FALSE; + + diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h new file mode 100644 index 00000000000..f09d8228033 --- /dev/null +++ b/usr.sbin/pkg_install/lib/lib.h @@ -0,0 +1,173 @@ +/* $OpenBSD: lib.h,v 1.1 1996/06/04 07:56:13 niklas Exp $ */ + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Include and define various things wanted by the library routines. + * + */ + +#ifndef _INST_LIB_LIB_H_ +#define _INST_LIB_LIB_H_ + +/* Includes */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/file.h> + +/* Macros */ +#define SUCCESS (0) +#define FAIL (-1) + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#define YES 2 +#define NO 1 + +/* Usually "rm", but often "echo" during debugging! */ +#define REMOVE_CMD "rm" + +/* Usually "rm", but often "echo" during debugging! */ +#define RMDIR_CMD "rmdir" + +/* Where we put logging information by default, else ${PKG_DBDIR} if set */ +#define DEF_LOG_DIR "/var/db/pkg" +/* just in case we change the environment variable name */ +#define PKG_DBDIR "PKG_DBDIR" + +/* The names of our "special" files */ +#define CONTENTS_FNAME "+CONTENTS" +#define COMMENT_FNAME "+COMMENT" +#define DESC_FNAME "+DESC" +#define INSTALL_FNAME "+INSTALL" +#define DEINSTALL_FNAME "+DEINSTALL" +#define REQUIRE_FNAME "+REQUIRE" +#define REQUIRED_BY_FNAME "+REQUIRED_BY" +#define DISPLAY_FNAME "+DISPLAY" +#define MTREE_FNAME "+MTREE_DIRS" + +#define CMD_CHAR '@' /* prefix for extended PLIST cmd */ + +/* The name of the "prefix" environment variable given to scripts */ +#define PKG_PREFIX_VNAME "PKG_PREFIX" + +enum _plist_t { + PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD, + PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, + PLIST_IGNORE, PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY, + PLIST_PKGDEP, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST, + PLIST_OPTION +}; +typedef enum _plist_t plist_t; + +/* Types */ +typedef unsigned int Boolean; + +struct _plist { + struct _plist *prev, *next; + char *name; + Boolean marked; + plist_t type; +}; +typedef struct _plist *PackingList; + +struct _pack { + struct _plist *head, *tail; +}; +typedef struct _pack Package; + +/* Prototypes */ +/* Misc */ +int vsystem(const char *, ...); +void cleanup(int); +char *make_playpen(char *, size_t); +char *where_playpen(void); +void leave_playpen(char *); +size_t min_free(char *); + +/* String */ +char *get_dash_string(char **); +char *copy_string(char *); +Boolean suffix(char *, char *); +void nuke_suffix(char *); +void str_lowercase(char *); +char *basename_of(char *); +char *strconcat(char *, char *); + +/* File */ +Boolean fexists(char *); +Boolean isdir(char *); +Boolean isfile(char *); +Boolean isempty(char *); +Boolean isURL(char *); +char *fileGetURL(char *, char *); +char *fileURLFilename(char *, char *, int); +char *fileURLHost(char *, char *, int); +char *fileFindByPath(char *, char *); +char *fileGetContents(char *); +void write_file(char *, char *); +void copy_file(char *, char *, char *); +void move_file(char *, char *, char *); +void copy_hierarchy(char *, char *, Boolean); +int delete_hierarchy(char *, Boolean, Boolean); +int unpack(char *, char *); +void format_cmd(char *, char *, char *, char *); + +/* Msg */ +void upchuck(const char *); +void barf(const char *, ...); +void whinge(const char *, ...); +Boolean y_or_n(Boolean, const char *, ...); + +/* Packing list */ +PackingList new_plist_entry(void); +PackingList last_plist(Package *); +PackingList find_plist(Package *, plist_t); +char *find_plist_option(Package *, char *name); +void plist_delete(Package *, Boolean, plist_t, char *); +void free_plist(Package *); +void mark_plist(Package *); +void csum_plist_entry(char *, PackingList); +void add_plist(Package *, plist_t, char *); +void add_plist_top(Package *, plist_t, char *); +void write_plist(Package *, FILE *); +void read_plist(Package *, FILE *); +int plist_cmd(char *, char **); +int delete_package(Boolean, Boolean, Package *); + +/* For all */ +void usage(const char *, const char *, ...); +int pkg_perform(char **); + +/* Externs */ +extern Boolean Verbose; +extern Boolean Fake; +extern int AutoAnswer; + +#endif /* _INST_LIB_LIB_H_ */ diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c new file mode 100644 index 00000000000..0ee3a57dbea --- /dev/null +++ b/usr.sbin/pkg_install/lib/msg.c @@ -0,0 +1,102 @@ +# $OpenBSD: msg.c,v 1.1 1996/06/04 07:56:13 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: msg.c,v 1.1 1996/06/04 07:56:13 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + + * 18 July 1993 + * + * Miscellaneous message routines. + * + */ + +#include "lib.h" + +/* Die a relatively simple death */ +void +upchuck(const char *err) +{ + fprintf(stderr, "Fatal error during execution: "); + perror(err); + cleanup(0); + exit(1); +} + +/* Die a more complex death */ +void +barf(const char *err, ...) +{ + va_list args; + + va_start(args, err); + vfprintf(stderr, err, args); + fputc('\n', stderr); + va_end(args); + cleanup(0); + exit(2); +} + +/* Get annoyed about something but don't go to pieces over it */ +void +whinge(const char *err, ...) +{ + va_list args; + + va_start(args, err); + vfprintf(stderr, err, args); + fputc('\n', stderr); + va_end(args); +} + +/* + * As a yes/no question, prompting from the varargs string and using + * default if user just hits return. + */ +Boolean +y_or_n(Boolean def, const char *msg, ...) +{ + va_list args; + int ch = 0; + FILE *tty; + + va_start(args, msg); + /* + * Need to open /dev/tty because file collection may have been + * collected on stdin + */ + tty = fopen("/dev/tty", "r"); + if (!tty) + barf("Can't open /dev/tty!\n"); + while (ch != 'Y' && ch != 'N') { + vfprintf(stderr, msg, args); + if (def) + fprintf(stderr, " [yes]? "); + else + fprintf(stderr, " [no]? "); + fflush(stderr); + if (AutoAnswer) { + ch = (AutoAnswer == YES) ? 'Y' : 'N'; + fprintf(stderr, "%c\n", ch); + } + else + ch = toupper(fgetc(tty)); + if (ch == '\n') + ch = (def) ? 'Y' : 'N'; + } + fclose(tty) ; + return (ch == 'Y') ? TRUE : FALSE; +} diff --git a/usr.sbin/pkg_install/lib/pen.c b/usr.sbin/pkg_install/lib/pen.c new file mode 100644 index 00000000000..e26ad037617 --- /dev/null +++ b/usr.sbin/pkg_install/lib/pen.c @@ -0,0 +1,144 @@ +# $OpenBSD: pen.c,v 1.1 1996/06/04 07:56:13 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: pen.c,v 1.1 1996/06/04 07:56:13 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Routines for managing the "play pen". + * + */ + +#include "lib.h" +#include <sys/signal.h> +#include <sys/param.h> +#include <sys/mount.h> + +/* For keeping track of where we are */ +static char Current[FILENAME_MAX]; +static char Previous[FILENAME_MAX]; + +char * +where_playpen(void) +{ + return Current; +} + +/* Find a good place to play. */ +static char * +find_play_pen(char *pen, size_t sz) +{ + char *cp; + struct stat sb; + + if (pen[0] && stat(pen, &sb) != FAIL && (min_free(pen) >= sz)) + return pen; + else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz)) + sprintf(pen, "%s/instmp.XXXXXX", cp); + else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz)) + sprintf(pen, "%s/instmp.XXXXXX", cp); + else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz) + strcpy(pen, "/var/tmp/instmp.XXXXXX"); + else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz) + strcpy(pen, "/tmp/instmp.XXXXXX"); + else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz) + strcpy(pen, "/usr/tmp/instmp.XXXXXX"); + else { + barf("Can't find enough temporary space to extract the files, please set\n" + "your PKG_TMPDIR environment variable to a location with at least %d bytes\n" + "free.", sz); + return NULL; + } + return pen; +} + +/* + * Make a temporary directory to play in and chdir() to it, returning + * pathname of previous working directory. + */ +char * +make_playpen(char *pen, size_t sz) +{ + char *tmp; + + if (!find_play_pen(pen, sz)) + return NULL; + + if (!mktemp(pen)) { + barf("Can't mktemp '%s'.", pen); + return NULL; + } + if (mkdir(pen, 0755) == FAIL) { + barf("Can't mkdir '%s'.", pen); + return NULL; + } + if (Verbose) { + if (sz) + fprintf(stderr, "Requested space: %d bytes, free space: %d bytes in %s\n", (int)sz, min_free(pen), pen); + } + if (min_free(pen) < sz) { + rmdir(pen); + barf("Not enough free space to create: `%s'\n" + "Please set your PKG_TMPDIR environment variable to a location\n" + "with more space and\ntry the command again.", pen); + return NULL; + } + if (Current[0]) + strcpy(Previous, Current); + else if (!getcwd(Previous, FILENAME_MAX)) { + upchuck("getcwd"); + return NULL; + } + if (chdir(pen) == FAIL) + barf("Can't chdir to '%s'.", pen); + strcpy(Current, pen); + return Previous; +} + +/* Convenience routine for getting out of playpen */ +void +leave_playpen(char *save) +{ + void (*oldsig)(int); + + /* Don't interrupt while we're cleaning up */ + oldsig = signal(SIGINT, SIG_IGN); + if (Previous[0] && chdir(Previous) == FAIL) + barf("Can't chdir back to '%s'.", Previous); + else if (Current[0] && strcmp(Current, Previous)) { + if (vsystem("rm -rf %s", Current)) + whinge("Couldn't remove temporary dir '%s'", Current); + strcpy(Current, Previous); + } + if (save) + strcpy(Previous, save); + else + Previous[0] = '\0'; + signal(SIGINT, oldsig); +} + +size_t +min_free(char *tmpdir) +{ + struct statfs buf; + + if (statfs(tmpdir, &buf) != 0) { + perror("Error in statfs"); + return -1; + } + return buf.f_bavail * buf.f_bsize; +} diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c new file mode 100644 index 00000000000..a24e63176d9 --- /dev/null +++ b/usr.sbin/pkg_install/lib/plist.c @@ -0,0 +1,448 @@ +# $OpenBSD: plist.c,v 1.1 1996/06/04 07:56:14 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD: plist.c,v 1.1 1996/06/04 07:56:14 niklas Exp $"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * General packing list routines. + * + */ + +#include "lib.h" + +/* Add an item to a packing list */ +void +add_plist(Package *p, plist_t type, char *arg) +{ + PackingList tmp; + + tmp = new_plist_entry(); + tmp->name = copy_string(arg); + tmp->type = type; + + if (!p->head) + p->head = p->tail = tmp; + else { + tmp->prev = p->tail; + p->tail->next = tmp; + p->tail = tmp; + } +} + +void +add_plist_top(Package *p, plist_t type, char *arg) +{ + PackingList tmp; + + tmp = new_plist_entry(); + tmp->name = copy_string(arg); + tmp->type = type; + + if (!p->head) + p->head = p->tail = tmp; + else { + tmp->next = p->head; + p->head->prev = tmp; + p->head = tmp; + } +} + +/* Return the last (most recent) entry in a packing list */ +PackingList +last_plist(Package *p) +{ + return p->tail; +} + +/* Mark all items in a packing list to prevent iteration over them */ +void +mark_plist(Package *pkg) +{ + PackingList p = pkg->head; + + while (p) { + p->marked = TRUE; + p = p->next; + } +} + +/* Find a given item in a packing list and, if so, return it (else NULL) */ +PackingList +find_plist(Package *pkg, plist_t type) +{ + PackingList p = pkg->head; + + while (p) { + if (p->type == type) + return p; + p = p->next; + } + return NULL; +} + +/* Look for a specific boolean option argument in the list */ +char * +find_plist_option(Package *pkg, char *name) +{ + PackingList p = pkg->head; + + while (p) { + if (p->type == PLIST_OPTION && !strcmp(p->name, name)) + return p->name; + p = p->next; + } + return NULL; +} + +/* + * Delete plist item 'type' in the list (if 'name' is non-null, match it + * too.) If 'all' is set, delete all items, not just the first occurance. + */ +void +delete_plist(Package *pkg, Boolean all, plist_t type, char *name) +{ + PackingList p = pkg->head; + + while (p) { + PackingList pnext = p->next; + + if (p->type == type && (!name || !strcmp(name, p->name))) { + free(p->name); + if (p->prev) + p->prev->next = pnext; + else + pkg->head = pnext; + if (pnext) + pnext->prev = p->prev; + else + pkg->tail = p->prev; + free(p); + if (!all) + return; + p = pnext; + } + else + p = p->next; + } +} + +/* Allocate a new packing list entry */ +PackingList +new_plist_entry(void) +{ + PackingList ret; + + ret = (PackingList)malloc(sizeof(struct _plist)); + bzero(ret, sizeof(struct _plist)); + return ret; +} + +/* Free an entire packing list */ +void +free_plist(Package *pkg) +{ + PackingList p = pkg->head; + + while (p) { + PackingList p1 = p->next; + + free(p->name); + free(p); + p = p1; + } + pkg->head = pkg->tail = NULL; +} + +/* + * For an ascii string denoting a plist command, return its code and + * optionally its argument(s) + */ +int +plist_cmd(char *s, char **arg) +{ + char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */ + char *cp, *sp; + + strcpy(cmd, s); + str_lowercase(cmd); + cp = cmd; + sp = s; + while (*cp) { + if (isspace(*cp)) { + *cp = '\0'; + while (isspace(*sp)) /* Never sure if macro, increment later */ + ++sp; + break; + } + ++cp, ++sp; + } + if (arg) + *arg = sp; + if (!strcmp(cmd, "cwd")) + return PLIST_CWD; + else if (!strcmp(cmd, "srcdir")) + return PLIST_SRC; + else if (!strcmp(cmd, "cd")) + return PLIST_CWD; + else if (!strcmp(cmd, "exec")) + return PLIST_CMD; + else if (!strcmp(cmd, "unexec")) + return PLIST_UNEXEC; + else if (!strcmp(cmd, "mode")) + return PLIST_CHMOD; + else if (!strcmp(cmd, "owner")) + return PLIST_CHOWN; + else if (!strcmp(cmd, "group")) + return PLIST_CHGRP; + else if (!strcmp(cmd, "comment")) + return PLIST_COMMENT; + else if (!strcmp(cmd, "ignore")) + return PLIST_IGNORE; + else if (!strcmp(cmd, "ignore_inst")) + return PLIST_IGNORE_INST; + else if (!strcmp(cmd, "name")) + return PLIST_NAME; + else if (!strcmp(cmd, "display")) + return PLIST_DISPLAY; + else if (!strcmp(cmd, "pkgdep")) + return PLIST_PKGDEP; + else if (!strcmp(cmd, "mtree")) + return PLIST_MTREE; + else if (!strcmp(cmd, "dirrm")) + return PLIST_DIR_RM; + else if (!strcmp(cmd, "option")) + return PLIST_OPTION; + else + return FAIL; +} + +/* Read a packing list from a file */ +void +read_plist(Package *pkg, FILE *fp) +{ + char *cp, pline[FILENAME_MAX]; + int cmd; + + while (fgets(pline, FILENAME_MAX, fp)) { + int len = strlen(pline) - 1; + + while (isspace(pline[len])) + pline[len--] = '\0'; + if (len <= 0) + continue; + cp = pline; + if (pline[0] == CMD_CHAR) { + cmd = plist_cmd(pline + 1, &cp); + if (cmd == FAIL) + barf("Bad command '%s'", pline); + if (*cp == '\0') + cp = NULL; + } + else + cmd = PLIST_FILE; + add_plist(pkg, cmd, cp); + } +} + +/* Write a packing list to a file, converting commands to ascii equivs */ +void +write_plist(Package *pkg, FILE *fp) +{ + PackingList plist = pkg->head; + + while (plist) { + switch(plist->type) { + case PLIST_FILE: + fprintf(fp, "%s\n", plist->name); + break; + + case PLIST_CWD: + fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_SRC: + fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_CMD: + fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_UNEXEC: + fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_CHMOD: + fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : ""); + break; + + case PLIST_CHOWN: + fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : ""); + break; + + case PLIST_CHGRP: + fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : ""); + break; + + case PLIST_COMMENT: + fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_IGNORE: + case PLIST_IGNORE_INST: /* a one-time non-ignored file */ + fprintf(fp, "%cignore\n", CMD_CHAR); + break; + + case PLIST_NAME: + fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_DISPLAY: + fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_PKGDEP: + fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_MTREE: + fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_DIR_RM: + fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name); + break; + + case PLIST_OPTION: + fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name); + break; + + default: + barf("Unknown command type %d (%s)\n", plist->type, plist->name); + break; + } + plist = plist->next; + } +} + +/* + * Delete the results of a package installation. + * + * This is here rather than in the pkg_delete code because pkg_add needs to + * run it too in cases of failure. + */ +int +delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg) +{ + PackingList p = pkg->head; + char *Where = ".", *last_file = ""; + Boolean fail = SUCCESS; + + if (!p) + return FAIL; + while (p) { + if (p->type == PLIST_CWD) { + Where = p->name; + if (Verbose) + printf("Change working directory to %s\n", Where); + } + else if (p->type == PLIST_UNEXEC) { + char cmd[FILENAME_MAX]; + + format_cmd(cmd, p->name, Where, last_file); + if (Verbose) + printf("Execute `%s'\n", cmd); + if (!Fake && system(cmd)) { + whinge("unexec command for `%s' failed.", cmd); + fail = FAIL; + } + } + else if (p->type == PLIST_IGNORE) + p = p->next; + else if (p->type == PLIST_FILE || p->type == PLIST_DIR_RM) { + char full_name[FILENAME_MAX]; + + sprintf(full_name, "%s/%s", Where, p->name); + if (isdir(full_name) && p->type == PLIST_FILE) { + warn("Attempting to delete directory `%s' as a file\n" + "This packing list is incorrect - ignoring delete request.\n", full_name); + } + else { + if (Verbose) + printf("Delete %s %s\n", !isdir(full_name) ? "file" : " directory", full_name); + + if (!Fake && delete_hierarchy(full_name, ign_err, p->type == PLIST_DIR_RM ? FALSE : nukedirs)) { + whinge("Unable to completely remove file '%s'", full_name); + fail = FAIL; + } + } + last_file = p->name; + } + p = p->next; + } + return fail; +} + +#ifdef DEBUG +#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir) +#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir) +#else +#define RMDIR rmdir +#define REMOVE(file,ie) (remove(file) && !(ie)) +#endif + +/* Selectively delete a hierarchy */ +int +delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs) +{ + char *cp1, *cp2; + + cp1 = cp2 = dir; + if (!fexists(dir)) { + if (!ign_err) + whinge("%s `%s' doesn't really exist.", isdir(dir) ? "Directory" : "File", dir); + } else if (nukedirs) { + if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir)) + return 1; + } else if (isdir(dir)) { + if (RMDIR(dir) && !ign_err) + return 1; + } else { + if (REMOVE(dir, ign_err)) + return 1; + } + + if (!nukedirs) + return 0; + while (cp2) { + if ((cp2 = rindex(cp1, '/')) != NULL) + *cp2 = '\0'; + if (!isemptydir(dir)) + return 0; + if (RMDIR(dir) && !ign_err) + if (!fexists(dir)) + whinge("Directory `%s' doesn't really exist.", dir); + else + return 1; + /* back up the pathname one component */ + if (cp2) { + cp1 = dir; + } + } + return 0; +} diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c new file mode 100644 index 00000000000..1d8ec1c5c99 --- /dev/null +++ b/usr.sbin/pkg_install/lib/str.c @@ -0,0 +1,111 @@ +# $OpenBSD: str.c,v 1.1 1996/06/04 07:56:14 niklas Exp $ +#ifndef lint +static const char *rcsid = "$OpenBSD"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * Miscellaneous string utilities. + * + */ + +#include "lib.h" + +/* Return the filename portion of a path */ +char * +basename_of(char *str) +{ + char *basename = str + strlen(str) - 1; + + while (basename != str && basename[-1] != '/') + --basename; + return basename; +} + +char * +strconcat(char *s1, char *s2) +{ + static char tmp[FILENAME_MAX]; + + tmp[0] = '\0'; + strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX); + if (s1 && s2) + strncat(tmp, s2, FILENAME_MAX - strlen(tmp)); + return tmp; +} + +/* Get a string parameter as a file spec or as a "contents follow -" spec */ +char * +get_dash_string(char **str) +{ + char *s = *str; + + if (*s == '-') + *str = copy_string(s + 1); + else + *str = fileGetContents(s); + return *str; +} + +/* Rather Obvious */ +char * +copy_string(char *str) +{ + char *ret; + + if (!str) + ret = NULL; + else { + ret = (char *)malloc(strlen(str) + 1); + strcpy(ret, str); + } + return ret; +} + +/* Return TRUE if 'str' ends in suffix 'suff' */ +Boolean +suffix(char *str, char *suff) +{ + char *idx; + Boolean ret = FALSE; + + idx = rindex(str, '.'); + if (idx && !strcmp(idx + 1, suff)) + ret = TRUE; + return ret; +} + +/* Assuming str has a suffix, brutally murder it! */ +void +nuke_suffix(char *str) +{ + char *idx; + + idx = rindex(str, '.'); + if (idx) + *idx = '\0'; /* Yow! Don't try this on a const! */ +} + +/* Lowercase a whole string */ +void +str_lowercase(char *str) +{ + while (*str) { + *str = tolower(*str); + ++str; + } +} diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg new file mode 100644 index 00000000000..7e11719df70 --- /dev/null +++ b/usr.sbin/pkg_install/tkpkg @@ -0,0 +1,180 @@ +#!/usr/local/bin/wish -f +#$OpenBSD: tkpkg,v 1.1 1996/06/04 07:56:02 niklas Exp $ +# +#$Log: tkpkg,v $ +#Revision 1.1 1996/06/04 07:56:02 niklas +#add package tools from FreeBSD +# +#Revision 1.2 1994/12/06 00:51:21 jkh +#Many of John T. Kohl's patches from NetBSD. Thanks, John! +#Submitted by: jkohl +# +# Revision 1.1 1994/01/06 08:16:20 jkh +# Cleaning house. +# +# Revision 1.1 1993/09/04 17:06:09 jkh +# Added Rich's wish front-end. +# +# Revision 1.6 1993/09/03 23:37:22 rich +# warn user if no tar archives are found in the current directory. +# removed the revision string from the lower text frame. +# +# Revision 1.5 1993/09/03 15:48:04 rich +# glob for .tar.gz, .tar.z and .tar.Z looking for archives +# +# Revision 1.4 1993/08/28 15:53:59 rich +# added version and date info to lower text window. +# +# Revision 1.3 1993/08/28 15:47:12 rich +# filtered out ^Ls in pkg_* output. +# +# +set pkgname "" +wm title . "Package Installation" +#-------------------------------------------------------------- +# The top level main window, consisting of a bar of buttons and a list +# of packages and a description of the current package. +#-------------------------------------------------------------- +frame .menu -relief raised -borderwidth 1 +frame .frame -borderwidth 4 + +scrollbar .frame.scroll -relief sunken -command ".frame.list yview" +listbox .frame.list -yscroll ".frame.scroll set" -relief sunken -setgrid 1 +pack append .frame .frame.scroll {right filly} \ + .frame.list {left expand fill} + +# build the lower window shoing the complete description of a pacage +frame .f -borderwidth 4 +text .f.t -width 80 -height 20 -yscrollcommand ".f.s set" -relief sunken + +# Initially display instructions in this window. Erase the +# instructions and show the package description when the user clicks +# on a package. +# +.f.t insert end "Double click on a package above to see its +complete description here." +scrollbar .f.s -relief sunken -command ".f.t yview" +pack append .f .f.s {right filly} .f.t {left expand fill} + +bind .frame.list <Double-Button-1> \ + {foreach i [selection get] {do_description $i}} +pack append . .menu {top fill} \ + .f {bottom expand fill} \ + .frame {bottom expand fill} + +#---------------------------------------------------------------- +# Make menu bar: +#---------------------------------------------------------------- +button .menu.inst -text "Install" \ + -command "apply_to_pkg \"pkg_add -v\"" +button .menu.dein -text "Deinstall" \ + -command "apply_to_pkg \"pkg_delete -v\"" +button .menu.installed -text "What is Installed?" \ + -command "list_pkgs \"pkg_info -I -a |tr ' ' ' '\"" +button .menu.available -text "What can I install?" \ + -command "list_pkgs \"pkg_info -I -c [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] |tr ' ' ' '\"" +button .menu.cont -text "Contents?" \ + -command "apply_to_pkg \"pkg_info -d -v\"" +button .menu.quit -text "Quit" -command "destroy ." +button .menu.help -text "Help" -command "do_help" + +pack append .menu \ + .menu.inst left \ + .menu.dein left \ + .menu.installed left \ + .menu.available left \ + .menu.cont left \ + .menu.quit left \ + .menu.help right +#------------------------------------------------------- +# Display the package description. +#------------------------------------------------------- +proc list_pkgs {s} { + set line "" + set f [eval "open {| sh -c \"$s\" } r"] + .frame.list delete 0 end + while {[gets $f line] > 0} { + .frame.list insert end $line + } + close $f +} + +# display the list of available packages +set archives [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] +if {$archives == ""} { + .frame.list delete 0 end + .frame.list insert end "Warning: no compressed tar archives files found." +} else { + list_pkgs "pkg_info -I -c $archives |tr ' ' ' '" +} + +#------------------------------------------------------- +# Display the package description. +#------------------------------------------------------- +proc do_description {s} { + global pkgname + regexp {[^ ]*} $s filename + set pkgname $filename + .f.t delete 0.0 end + set cmd "pkg_info -d $filename |tr -d ''" + set f [eval "open {| csh -c \"$cmd\" } r"] + while {![eof $f]} { + .f.t insert end [read $f] + } +} +#------------------------------------------------------- +# package install window. +#------------------------------------------------------- +proc do_help {{w .help}} { + catch {destroy $w} + toplevel $w + wm title $w "Help" + wm iconname $w "Help" + button $w.ok -text OK -command "destroy $w" + message $w.t -relief raised -bd 2 \ + -text "You can install, deinstall and list info on the available packages. To select a package and see its complete description, press mouse button 1 over the package name. To install a selected package, press the Install button. To exit, press the \"Quit\" button." + pack append $w $w.ok {bottom fillx} $w.t {expand fill} +} +#------------------------------------------------------- +# Apply a command to a package. +#------------------------------------------------------- +proc apply_to_pkg {s} { + apply_to_pkg_err $s "" +} +#------------------------------------------------------- +# Apply a command to a package, with error stream redirection instructions. +#------------------------------------------------------- +proc apply_to_pkg_err {s errredir} { + global pkgname + .f.t delete 0.0 end + if {$pkgname == ""} { + .f.t insert end "You must double click on a package name first!" + } else { + apply_to_pkg_int "$s $pkgname" "2>&1" + } +} +proc apply_to_pkg_int {s errredir} { + .f.t delete 0.0 end + .f.t insert end "Running: $s\n" + set f [eval "open {| sh -c \"$s $errredir\" } r"] + while {![eof $f]} { + .f.t insert end [read $f 64] + } +} +#------------------------------------------------------- +# Invoke an arbitrary command. +#------------------------------------------------------- +proc do_command {s} { + .f.t delete 0.0 end + .f.t insert end "Running: $s\n" + set f [eval "open {| $s} r"] + while {![eof $f]} { + .f.t insert end [read $f 64] + } +} +# local variables: +# mode: csh +# compile-command: "" +# comment-start: "# " +# comment-start-skip: "# " +# end: |