V10/cmd/asd/inspkg.c
#include "asd.h"
#define CHUNK 64
/* type codes for installation subroutine */
#define BACKUP 0
#define INSTALL 1
void readtemp();
/*
* The following declarations and functions manipulate
* a list of directory names. This list is used to decide
* which files should be backed up and which have already been.
*/
struct list {
char *name;
struct list *next;
};
static struct list *dirs;
/* is the name given a subdirectory of the name on the list? */
static int
subsumed (name)
register char *name;
{
register struct list *item;
register char *p, *q;
for (item = dirs; item; item = item->next) {
p = item->name;
q = name;
while (*p && *p == *q)
p++, q++;
if (*p == '\0' && (*q == '/' || *q == '\0'))
return 1;
}
return 0;
}
/* add the name given to the list */
static void
addlist (name)
register char *name;
{
register struct list *l;
l = new (struct list);
l->next = dirs;
l->name = copy (name);
dirs = l;
}
/* clear the entire list */
static void
clearlist()
{
register struct list *l;
while (l = dirs) {
dirs = l->next;
free (l->name);
free ((char *) l);
}
}
/*
* install the given file
*/
process (file, fname)
FILE *file;
char *fname;
{
register int c, rc = 0;
if (vflag)
fprintf (stderr, "%s:\n", fname);
while ((c = getc (file)) != EOF) {
ungetc (c, file);
rc += doarch (file);
}
return rc;
}
main (argc, argv)
int argc;
char **argv;
{
static char errbuf[BUFSIZ];
setbuf (stderr, errbuf);
umask (0);
return getargs (argc, argv, "nvbD:", process);
}
static char tfname[TMPNAML];
void delete();
/* process a single archive in a concatenation */
doarch (file)
register FILE *file;
{
register FILE *tf;
Sig_typ sigsav;
register long size;
register int c;
char armag[SARMAG];
/* Make sure the file is an archive */
if (fread (armag, sizeof (*armag), SARMAG, file) != SARMAG) {
fprintf (stderr, "inspkg: unexpected EOF\n");
exit (1);
}
if (strncmp (armag, ARMAG, SARMAG) != 0) {
fprintf (stderr, "inspkg: input not a package\n");
exit (1);
}
/* establish a temporary file */
(void) tmpname (tfname);
tf = fopen (tfname, "w");
sigsav = signal (SIGINT, SIG_IGN);
if (sigsav != SIG_IGN)
signal (SIGINT, delete);
chmod (tfname, 0600);
/* copy the installation instructions to the temp file */
size = read_header (instr, file);
while (--size >= 0) {
c = getc (file);
if (c == EOF) {
fprintf (stderr, "inspkg: premature EOF\n");
exit (1);
}
if (putc (c, tf) == EOF) {
perror ("inspkg: Instructions");
exit (1);
}
}
if (fclose (tf) == EOF) {
perror ("inspkg: Instructions fclose");
exit (1);
}
next_header (file);
/* create the optional backup package */
if (bflag) {
pkgstart();
readtemp (BACKUP, file);
pkgend();
clearlist();
}
/* do the actual work */
readtemp (INSTALL, file);
/* delete the temporary file */
nchk (unlink (tfname));
signal (SIGINT, sigsav);
return 0;
}
/*
* Make a pass through the temp file.
*/
static void
readtemp (code, file)
register int code;
register FILE *file;
{
register FILE *tf;
register int c;
/* we're done writing the temp file, time to read it */
tf = fopen (tfname, "r");
schk ((char *) tf);
/*
* The main loop -- one iteration per line
* We are careful in use and reuse of storage here;
* if you change this code make sure you understand
* the times at which getfield and transname
* recycle storage or strange things will happen.
*/
while ((c = getc (tf)) != EOF) {
char *param, *path, *path2;
register FILE *outfd;
int uid, gid, mode, dmajor, dminor, dev;
register long size;
char component[MAXCOMP+1];
switch (c) {
/* special files */
case 'b':
case 'c':
/* read the parameters */
param = getfield (tf);
mode = cvlong (param, strlen (param), 8) |
(c == 'c'? S_IFCHR: S_IFBLK);
param = getfield (tf);
dmajor = cvlong (param, strlen (param), 10);
param = getfield (tf);
dminor = cvlong (param, strlen (param), 10);
dev = makedev (dmajor, dminor);
uid = numuid (getfield (tf));
gid = numgid (getfield (tf));
path = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path)) {
pkgfile (path);
addlist (path);
}
break;
case INSTALL:
if (vflag) {
fprintf (stderr, "special file ");
putpath (stderr, path);
fprintf (stderr, "\n");
}
if (!nflag) {
rmall (path);
if (mknod (path, mode, dev) >= 0)
chown (path, uid, gid);
else
perror (path);
}
break;
}
break;
/* directory */
case 'd':
/* read the parameters */
param = getfield (tf);
mode = cvlong (param, strlen (param), 8);
uid = numuid (getfield (tf));
gid = numgid (getfield (tf));
path = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path)) {
pkgfile (path);
addlist (path);
}
break;
case INSTALL:
/* make the directory */
if (vflag) {
fprintf (stderr, "directory ");
putpath (stderr, path);
putc ('\n', stderr);
}
if (!nflag) {
rmall (path);
mkdir (path);
chmod (path, mode);
chown (path, uid, gid);
chmod (path, mode);
}
break;
}
break;
/* file */
case 'f':
/* read parameters */
param = getfield (tf);
if (strlen (param) > MAXCOMP) {
fprintf (stderr,
"inspkg: impossibly long component name\n");
delete();
exit (1);
}
strcpy (component, param);
uid = numuid (getfield (tf));
gid = numgid (getfield (tf));
path = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path))
pkgfile (path);
break;
case INSTALL:
/* read the corresponding archive header */
size = read_header (component, file);
if (vflag) {
fprintf (stderr, "file ");
putpath (stderr, path);
putc ('\n', stderr);
}
/* create and open the file */
if (!nflag) {
rmall (path);
umask (077);
outfd = fopen (path, "w");
umask (0);
if (outfd == NULL) {
fprintf (stderr,
"inspkg: cannot create ");
putpath (stderr, path);
putc ('\n', stderr);
}
}
if (nflag || outfd == NULL) {
outfd = fopen ("/dev/null", "w");
schk ((char *) outfd);
}
/* copy the file, advance input */
while (--size >= 0)
putc (getc (file), outfd);
next_header (file);
/* check successful completion, close output */
fflush (outfd);
if (feof (file) || ferror (file) ||
ferror (outfd)) {
fprintf (stderr, "inspkg: can't write ");
putpath (stderr, path);
putc ('\n', stderr);
}
if (fclose (outfd) == EOF) {
fprintf (stderr, "inspkg: can't close ");
putpath (stderr, path);
fprintf (stderr, ": ");
perror ("");
}
/*
* Update output modification times
* and change mode and owner.
* This is done here to cater to systems
* that allow people to give files away.
* The chmod is done twice because
* systems that let you give files away
* won't let you change the mode after
* you've done so, and some other systems
* turn off the setuid bit as a side
* effect of chown, so the second chmod
* will restore that bit.
*/
if (!nflag) {
time_t timep[2];
timep[0] = timep[1] = hdr.date;
utime (path, timep);
chmod (path, hdr.mode & 07777);
chown (path, uid, gid);
chmod (path, hdr.mode & 07777);
}
break;
}
break;
/* symbolic link */
case 's':
/* read parameters */
path = copy (transname (getfield (tf)));
path2 = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path2))
pkgfile (path2);
break;
case INSTALL:
/* log it if requested */
if (vflag) {
fprintf (stderr, "symlink ");
putpath (stderr, path2);
fprintf (stderr, " to ");
putpath (stderr, path);
putc ('\n', stderr);
}
#ifdef S_IFLNK
/* make the link */
if (!nflag) {
rmall (path2);
if (symlink (path, path2) < 0)
perror (path2);
}
#else
fprintf(stderr, "This system does not support symbolic links\n");
#endif
break;
}
free (path);
break;
/* link */
case 'l':
/* read parameters */
path = copy (transname (getfield (tf)));
path2 = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path2))
pkgfile (path2);
break;
case INSTALL:
/* log it if requested */
if (vflag) {
fprintf (stderr, "link ");
putpath (stderr, path);
fprintf (stderr, " to ");
putpath (stderr, path2);
putc ('\n', stderr);
}
/* make the link */
if (!nflag) {
struct stat sb, sb2;
/* are we about to link x to x? */
if (stat (path, &sb) < 0
|| stat (path2, &sb2) < 0
|| sb.st_dev != sb2.st_dev
|| sb.st_ino != sb2.st_ino) {
rmall (path2);
if (link (path, path2) < 0)
perror (path2);
}
}
break;
}
free (path);
break;
/* remove */
case 'r':
/* get parameters */
path = transname (getfield (tf));
geteol (tf);
switch (code) {
case BACKUP:
if (!subsumed (path))
pkgfile (path);
break;
case INSTALL:
if (vflag) {
fprintf (stderr, "remove file ");
putpath (stderr, path);
putc ('\n', stderr);
}
if (!nflag)
rmall (path);
break;
}
break;
case 'x':
xstr = getfield (tf);
geteol (tf);
if (code == INSTALL) {
if (vflag) {
fprintf (stderr, "execute: ");
putpath (stderr, xstr);
fprintf (stderr, "\n");
}
if (!nflag) {
fflush (stderr);
system (xstr);
}
}
xstr = NULL;
break;
case 'X':
Xstr = transname (getfield (tf));
geteol (tf);
if (code == INSTALL) {
if (vflag) {
fprintf (stderr, "exec file: ");
putpath (stderr, Xstr);
fprintf (stderr, "\n");
}
if (!nflag) {
int status, pid, w;
Sig_typ istat, qstat;
fflush (stderr);
if ((pid = fork()) == 0) {
execl (Xstr, Xstr, (char *)0);
execl ("/bin/sh", "sh", Xstr, (char *) 0);
putpath (stderr, Xstr);
fprintf (stderr, ": ");
fflush (stderr);
perror ("");
_exit(127);
}
istat = signal (SIGINT, SIG_IGN);
qstat = signal (SIGQUIT, SIG_IGN);
while ((w=wait(&status)) != pid && w != -1)
;
signal(SIGINT, istat);
signal(SIGQUIT, qstat);
}
}
Xstr = NULL;
break;
default:
fprintf (stderr, "inspkg: invalid package\n");
delete();
exit (1);
}
}
if (ferror (tf))
perror ("inspkg: internal temp file");
fclose (tf);
}
static void
delete()
{
unlink (tfname);
exit (3);
}