summaryrefslogtreecommitdiff
path: root/usr.sbin/pkg_install/add
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pkg_install/add')
-rw-r--r--usr.sbin/pkg_install/add/Makefile17
-rw-r--r--usr.sbin/pkg_install/add/add.h44
-rw-r--r--usr.sbin/pkg_install/add/extract.c186
-rw-r--r--usr.sbin/pkg_install/add/futil.c95
-rw-r--r--usr.sbin/pkg_install/add/main.c170
-rw-r--r--usr.sbin/pkg_install/add/perform.c467
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1355
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.