diff options
Diffstat (limited to 'usr.sbin/pkg_install/add')
-rw-r--r-- | usr.sbin/pkg_install/add/Makefile | 17 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/add.h | 44 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/extract.c | 186 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/futil.c | 95 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/main.c | 170 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/perform.c | 467 | ||||
-rw-r--r-- | usr.sbin/pkg_install/add/pkg_add.1 | 355 |
7 files changed, 1334 insertions, 0 deletions
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. |