TODO 644 3513 4 417 5032703557 4565 finish new setuid scheme
write document
add security configuration file support
(idea: receive reads this file, which looks like:
local-user remote-system remote-user remote-file actions...)
set up two-pass install/remove scheme
make a backup package before installation
������ ape/ 755 3513 4 0 5032703607 4567 ape/Future/ 755 3513 4 0 5031447510 6036 ape/Future/conf.c 644 3513 4 4652 5016334717 7232 #include <ctype.h>
#include <regexp.h> /* FIXME: use Posix interface. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "conf.h"
#include "dist.h"
static char *
gettok(char *tok, char *src)
{
while (!isspace(*src)) {
if (!*src)
break;
*tok++ = *src++;
}
while (isspace(*src))
++src;
return src;
}
/* return the next line of the config file matching the given args.
and argument can be NULL and will be assumed to match. the return
value is in a static data area. */
struct conf *
confread(FILE *fp, char *host, char *user, char *file)
{
static char confline[MAXLINE];
static char confhost[MAXLINE], confuser[MAXLINE], conffile[MAXLINE];
char *next;
static struct conf conf = { confhost, confuser, conffile };
int len, flag;
regexp *re;
regsubexp match[1];
for (;;) {
if (!fgets(confline, MAXLINE, fp))
return NULL;
if (confline[0] == '#')
continue;
len = strlen(confline);
if (confline[len - 1] != '\n') {
if (feof(fp))
eprintf("readconf: incomplete last line!");
else
eprintf("readconf: line too long or embedded NUL!");
exit(1);
}
next = gettok(confhost, confline);
next = gettok(confuser, next);
conf.cmds = gettok(conffile, next);
if (host) {
re = regcomp(confhost);
flag = regexec(re, host, match, 1);
free(re);
if (!flag || match[0].ep - match[0].sp != strlen(host))
continue;
}
if (user) {
re = regcomp(confuser);
flag = regexec(re, user, match, 1);
free(re);
if (!flag || match[0].ep - match[0].sp != strlen(user))
continue;
}
if (file) {
re = regcomp(conffile);
flag = regexec(re, file, match, 1);
free(re);
if (!flag || match[0].ep - match[0].sp != strlen(file))
continue;
}
return &conf;
}
}
/* call functions associated with the commands in the given string
return the total of the return values */
int
confexec(char *cmdstr, struct confcmds cmdtab[])
{
char cmd[MAXLINE], arg[MAXLINE];
int i, np, ret;
ret = 0;
while (*cmdstr) {
for (i = 0; isalnum(*cmdstr); ++cmdstr)
cmd[i++] = *cmdstr;
cmd[i] = 0;
if (*cmdstr == '(')
for (i = 0, np = 1, ++cmdstr; *cmdstr && np; ++cmdstr) {
if (*cmdstr == '(')
++np;
if (*cmdstr == ')' && !--np)
continue;
arg[i++] = *cmdstr;
}
for (i = 0; cmdtab[i].name; ++i)
if (!strcmp(cmd, cmdtab[i].name)) {
ret += (*cmdtab[i].func)(arg);
break;
}
while (*cmdstr && !isalnum(*cmdstr))
++cmdstr;
}
return ret;
}
t i, np, ret;
ret = 0;
while (*cmdstr) {
for (i = 0; isalnum(*cmdstr); ++cmdstr)
ape/Future/conf.h 644 3513 4 676 5016334663 7221 /* structure corresponding to line of the config file */
struct conf {
char *host; /* regexp */
char *user; /* regexp */
char *file; /* regexp */
char *cmds; /* cmd+cmd+cmd... */
};
extern struct conf *confread(FILE *fp, char *host, char *user, char *file);
/* configuration command name and associated function to call */
struct confcmds {
char *name;
int (*func)(char *);
};
extern int confexec(char *cmdstr, struct confcmds *cmdtab);
= 0;
while (*cmdstr) {
for (i = 0; isalnum(*cmdstr); ++cmdstr)
ape/dist.sh 644 3513 4 4306 5032703266 6157 #! /bin/sh -
umask 022
LDIR=%LDIR%
SDIR=%SDIR%
QPREFIX=Q.
PATH=$LDIR:/bin:/usr/bin:/usr/ucb:/usr/bsd
export PATH
DEST=$LDIR/destinations
showq=0
recv=0
notify=0
systems=
usage='usage: dist [-q|-r] [system|-[Ff] file] [-n] [files...]'
while [ $# -gt 0 ]
do
case "$1" in
-q)
showq=1
;;
-r)
recv=1
;;
-n)
notify=1
;;
-[Ff])
if [ $# -lt 2 ]
then
echo "$usage" 1>&2
exit 1
fi
if [ x$1 = x-f -a -r "$2" ]
then
systems=`cat "$2" 2>/dev/null`
elif [ -r $DEST/"$2" ]
then
systems=`cat $DEST/"$2" 2>/dev/null`
else
echo "dist: can't open '$2'" 1>&2
exit 1
fi
shift
;;
*)
if [ "x$systems" != x ]
then
break
fi
systems="$1"
;;
esac
shift
done
if [ $recv = 1 ]
then
if [ x"$systems" = x ]
then
echo dist: no systems specified 1>&2
exit 1
else
for sys in $systems
do
echo $sys:
connect $sys transmit io receive || exit 1
done
exit 0
fi
fi
if [ $showq = 1 ]
then
if [ x"$systems" = x ]
then
showq
exit 0
else
for sys in $systems
do
echo $sys:
connect $sys showq i /bin/cat
done
exit 0
fi
fi
if [ $# = 0 ]
then
echo "$usage" 1>&2
exit 1
fi
# construct spool directory
for d in `echo $$ | awk '{for (i = 0; i < 1000; ++i) printf "%3d%d\n", i, $1}'`
do
if [ ! -f $SDIR/$QPREFIX$d ]
then
dir=$QPREFIX$d
mkdir $SDIR/$dir && break
echo "dist: can't make spool directory" 1>&2
exit 1
fi
done
data=$SDIR/$dir/data
ctl=$SDIR/$dir/tempctl
files=$SDIR/$dir/files
# use canonical system names.
canon $systems | awk '{for (i = 1; i <= NF; ++i) printf "%d %s\n", ++n, $i}' > $ctl
mkpkg "$@" > $data
pwd=`pwd`
for f in "$@"
do
case "$f" in
-*) # flag to mkpkg
;;
/*)
echo "$f"
;;
*)
echo "$pwd/$f"
;;
esac
done | sort > $files
# rename the ctl file to its real name; at this point the package
# is now available for shipment.
mv $ctl $SDIR/$dir/ctl
# supersede old jobs containing the same files
cd $SDIR
for d in $QPREFIX*
do
test $d = $dir && continue
test -w $d || continue
if [ `comm -23 $d/files $dir/files | wc -c` = 0 ]
then
sort +1 $d/ctl $dir/ctl | uniq -d -2 |
while read num sys
do
echo superseded by $dir > $d/$num.done
done
fi
done
# clean up any jobs we completely superseded.
cleanq
done | sort > $files
# rename the ctl file to its real name; at this point the package
# is now available for shipment.
mv $ctl $SDIR/$dir/ctl
# supersede old jobs containing the same files
cd $SDIR
for d in $QPREFIX*
do
test $d = $dir && continue
test -w $d || continue
if [ `comm -23 $d/files $dir/files | ape/mkfile 644 3513 4 2060 5031455043 6041 <../mkconf.$SYS
<../mkrules.$SYS
CFLAGS=-g
BPROG=dist
LPROG=receive transmit showq cleanq genmail nightly
LSUID=transmit cleanq
LIBOBJ=chat.$O chkperm.$O crc.$O eprintf.$O path.$O scanq.$O tryperm.$O
%.$O: %.c
$CC -DLDIR='"'$LDIR'"' -DSDIR='"'$SDIR'"' -c $stem.c
%: %.$SH
sed -e s@%LDIR%@$LDIR@ -e s@%SDIR%@$SDIR@ $stem.$SH > $target || rm -f $target
chmod +x $target
compile:V: $BPROG $LPROG
install:V: compile
test -d $LDIR || mkdir $LDIR
test -d $SDIR || mkdir $SDIR && chmod 777 $SDIR
cp $BPROG $BDIR
cp $LPROG $LDIR
(cd $LDIR; chown root $LSUID; chmod 4755 $LSUID)
receive: receive.$O lib.a ../$SYS/lib.a
$CC -o receive receive.$O lib.a ../$SYS/lib.a $CCLIB
transmit: transmit.$O lib.a ../$SYS/lib.a
$CC -o transmit transmit.$O lib.a ../$SYS/lib.a $CCLIB
showq: showq.$O lib.a ../$SYS/lib.a
$CC -o showq showq.$O lib.a ../$SYS/lib.a $CCLIB
cleanq: cleanq.$O lib.a ../$SYS/lib.a
$CC -o cleanq cleanq.$O lib.a ../$SYS/lib.a $CCLIB
lib.a: ${LIBOBJ:%=lib.a(%)}
$RL lib.a
lib.a(%): %
ar r lib.a $stem
clean:V:
rm -f $BPROG $LPROG *.[$OS] lib.a
UID; chmod 4755 $LSUID)
receive: receive.$O lib.a ../$SYS/lib.a
$CC -o receive receive.$O lib.a ../$SYS/lib.a $CCLIB
transmit: transmit.$O lib.a ../$SYS/lib.a
$CC -o transmit transmit.$O lib.a ../$SYS/lib.a $CCLIB
showq: showq.$O lib.a ../$SYS/lib.a
$CC -o showq showq.$O lib.a ../$SYS/lib.a $CCLIB
cleanq: cleanq.$O lib.a ../$SYS/lib.a
$CC -o cleanq cleanq.$O lib.a ../$SYS/lib.a $CCLIB
lib.a: ${LIBOBJ:%=lib.a(%)}
$RL lib.a
lib.a(%): %
ar r lib.a $stape/scanq.c 644 3513 4 3466 5032142073 6130 #include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dist.h"
static char *
copy(char *s)
{
char *r;
r = malloc(strlen(s) + 1);
if (r)
strcpy(r, s);
return r;
}
static void
removeq(char *name)
{
DIR *dp;
struct dirent *d;
char file[2 * MAXNAME + 1];
name = copy(name); /* readdir bug?!? */
if (fork() == 0) {
setuid(getuid());
execl(LDIR "/genmail", "genmail", "-f", name, (char *) 0);
exit(0);
} else
wait(0);
if (!(dp = opendir(name)))
return;
while (d = readdir(dp)) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
sprintf(file, "%s/%.*s", name, MAXNAME, d->d_name);
remove(file);
}
closedir(dp);
rmdir(name);
free(name);
}
void
scanq(char *remsys, void (*func)(char *, int, char *))
{
DIR *dp;
struct dirent *d;
FILE *fp;
char sys[MAXLINE], cansys[MAXLINE], file[MAXNAME];
int num, zapq;
if (remsys)
canon(remsys, cansys);
if (chdir(SDIR) < 0) {
eprintf("can't chdir %s", SDIR);
exit(1);
}
if (!(dp = opendir("."))) {
eprintf("can't read spool directory!");
exit(1);
}
while (d = readdir(dp))
if (strncmp(d->d_name, QPREFIX, strlen(QPREFIX)) == 0) {
zapq = 1;
if (chdir(d->d_name) < 0) {
eprintf("can't chdir %s/%s", SDIR, d->d_name);
continue;
}
if (fp = fopen(CTL, "r")) {
while (fscanf(fp, "%d%s", &num, sys) == 2) {
sprintf(file, "%d.%s", num, DONE);
if (access(file, F_OK) == 0)
continue;
if (remsys && strcmp(sys, cansys) != 0) {
zapq = 0;
continue;
}
if (func)
(*func)(d->d_name, num, sys);
if (access(file, F_OK) != 0)
zapq = 0;
}
fclose(fp);
} else
eprintf("can't read %s/%s", d->d_name, CTL);
chdir("..");
if (zapq)
removeq(d->d_name);
}
closedir(dp);
}
if (fp = fopen(CTL, "r")) {
while (fscanf(fp, "%d%s", &num, sys) == 2) {
sprintf(file, "%d.%s", num, DONE);
if (access(file, F_OK) == 0)
continue;
if (remsys && strcmp(sys, caape/eprintf.c 644 3513 4 1030 5020312715 6452 #include <stdarg.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "dist.h"
char *prog;
void
eprintf(char *fmt, ...)
{
time_t now;
char mesg[MAXLINE];
va_list ap;
static int fd = -1;
time(&now);
sprintf(mesg, "%s[%d %.24s]: ", prog, getpid(), ctime(&now));
va_start(ap, fmt);
vsprintf(mesg + strlen(mesg), fmt, ap);
strcat(mesg, "\n");
write(2, mesg, strlen(mesg));
if (fd < 0)
fd = open(SDIR "/log", 1);
lseek(fd, 0L, 2); /* we really want O_APPEND mode, but... */
write(fd, mesg, strlen(mesg));
}
lude <stdio.h>
#include <time.h>
#include <unistd.h>
#include "dist.h"
char *prog;
void
eprintf(char *fmt, ...)
{
time_t now;
char mesg[MAXLINE];
va_list ap;
static int fd = -1;
time(&now);
sprintf(mesg, "%s[%d %.24s]: ", prog, getpid(), ctime(&now));
va_start(ap, fmt);
vsprintf(mesg + strlen(mesg), fmt, ap);
strcat(mesg, "\n");
write(2, mesg, strlen(mesg));
if (fd < 0)
fd = open(SDIR "/log", 1);
lseek(fd, 0L, 2); /* we really want O_APPEND mode, but... */
write(fd,ape/receive.c 644 3513 4 3406 5032661703 6445 #include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "dist.h"
void
recvjob(void)
{
char data[MAXNAME], errs[MAXNAME], mesg[MAXLINE];
int dfd, efd, attempt, status;
if (!getline(mesg))
exit(0);
if (sscanf(mesg, "begin job %s", data) != 1) {
eprintf("job header munged: mesg = %s", mesg);
exit(1);
}
sprintf(data, "data.%d", getpid());
remove(data);
dfd = creat(data, 0600); /* ape deficiency */
close(dfd);
dfd = open(data, O_RDWR);
if (dfd < 0) {
eprintf("can't create data file %s", data);
exit(1);
}
if (!recvfile(dfd)) {
remove(data);
eprintf("recvfile failed");
exit(1);
}
/* see if file system permissions will likely allow inspkg to succeed */
lseek(dfd, 0L, 0);
attempt = tryperm(dfd);
sprintf(mesg, "client attempt=%d\n", attempt);
write(1, mesg, strlen(mesg));
if (attempt) {
sprintf(errs, "errs.%d", getpid());
efd = creat(errs, 0600); /* more ape bogosity */
close(efd);
efd = open(errs, O_RDWR);
if (efd < 0) {
eprintf("can't create errs file %s", errs);
exit(1);
}
if (fork()) {
status = 0xFF; /* evilly catch wait failures */
wait(&status);
if (status & 0xFF) {
sprintf(mesg, "inspkg: exit %d\n", status & 0xFF);
write(efd, mesg, strlen(mesg));
}
} else {
lseek(dfd, 0L, 0);
dup2(dfd, 0);
dup2(efd, 1);
dup2(efd, 2);
close(dfd);
close(efd);
execl(LDIR "/inspkg", "inspkg", 0);
eprintf("can't exec inspkg");
exit(1);
}
lseek(efd, 0L, 0);
if (!sendfile(efd)) {
remove(data);
remove(errs);
eprintf("can't send error log");
exit(1);
}
}
close(dfd);
remove(data);
close(efd);
remove(errs);
}
int
main(int argc, char *argv[])
{
prog = argv[0];
for (;;)
recvjob();
}
write(efd, mesg, strlen(mesg));
}
} else {
lseek(dfd, 0L, 0);
dup2(dfd, 0);
dup2(efd, 1);
dup2(efd, 2);
close(dfd);
close(efd);
execl(LDIR "/inspkg", "inspkg", 0);
eprintf("can't exec inspkg");
exit(1);
}
lseek(eape/tryperm.c 644 3513 4 6311 5027736536 6536 #include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "../pkg/ar.h"
#include "dist.h"
/* determine if we can create the given pathname. not a generally useful
function since it destroys the pathname in the process... */
static int
cancreate(char *path, int recurse)
{
char *slash;
for (;;) {
slash = strrchr(path, '/');
if (! slash)
return 0; /* supposed to be absolute */
*slash = '\0';
if (!*path)
break;
if (access(path, W_OK) == 0)
return 1;
if (!recurse) /* for remove we check only one directory */
return 0;
if (access(path, F_OK) == 0)
return 0;
}
return access("/", W_OK) == 0;
}
/*
* try the instructions of the package on the given fd against the
* permissions currently in the file system. return 1 if we think we
* ought to go ahead and try inspkg.
*/
int
tryperm(int fd)
{
char armag[SARMAG];
struct ar_hdr arhdr;
long size;
char temp[L_tmpnam];
int tfd;
FILE *tfp;
int cmd, c, nf, retval;
char *path;
/* return 1 so inspkg will be called and barf for us */
if (read(fd, armag, sizeof armag) != SARMAG || strncmp(armag, ARMAG, SARMAG))
return 1;
if (read(fd, (char *) &arhdr, sizeof arhdr) != sizeof arhdr)
return 1;
size = strtol(arhdr.ar_size, 0, 10);
tmpnam(temp);
close(creat(temp, 0600)); /* ape deficiency */
tfd = open(temp, O_RDWR);
if (tfd < 0) {
eprintf("can't create temp file %s for tryperm", temp);
remove(temp); /* just in case something's *real* weird */
exit(1);
}
if (fdcopy(tfd, fd, size, 0) != size) {
close(tfd);
remove(temp);
eprintf("can't extract Instructions in tryperm");
return 1; /* inspkg will (presumably) barf for us */
}
lseek(tfd, 0L, 0);
tfp = fdopen(tfd, "r");
/* now that we have the instructions file, do the real meat. */
retval = 1;
while ((cmd = getc(tfp)) != EOF) {
switch (cmd) {
case 'b': /* b <mode> <dmaj> <dmin> <uid> <gid> <path> */
case 'c': /* c <mode> <dmaj> <dmin> <uid> <gid> <path> */
nf = 6;
break;
case 'd': /* d <mode> <uid> <gid> <path> */
case 'f': /* f <component> <uid> <gid> <path> */
nf = 4;
break;
case 'l': /* l <path1> <path2> */
case 's': /* s <path1> <path2> */
nf = 2;
break;
case 'r': /* r <path> */
case 'x': /* x <command> */
case 'X': /* X <path> */
nf = 1;
break;
default:
goto reterr;
}
/* get the last field of the line */
while (nf--) {
while ((c = getc(tfp)) != EOF)
if (!isspace(c))
break;
if (c == EOF)
goto reterr;
ungetc(c, tfp);
path = getpath(tfp);
}
/* eat the newline */
if (getc(tfp) != '\n')
goto reterr;
switch (cmd) {
case 'X':
case 'x':
break;
case 'r':
if (access(path, F_OK) != 0)
break;
if (cancreate(path, 0))
break;
retval = 0;
goto out;
default:
if (cancreate(path, 1))
break;
retval = 0;
goto out;
}
}
out:
fclose(tfp);
remove(temp);
return retval;
reterr:
/* there was some kind of bug in the format of the package. return 1
and later on inspkg will generate an appropriate error message to
be returned to the remote system */
fclose(tfp);
remove(temp);
return 1;
}
ase 'x':
break;
case 'r':
if (access(path, F_OK) != 0)
break;
if (cancreate(path, 0))
break;
retval = 0;
goto out;
default:
if (cancreate(path, 1))
break;
retval = 0;
goto out;
}
}
out:
fclose(tfp);
remove(temp);
return retval;
reterr:
/* there was some kindape/dist.h 644 3513 4 1530 5030707254 5767 #define QPREFIX "Q."
#define CTL "ctl"
#define DATA "data"
#define BUSY "busy"
#define DONE "done"
#define FILES "files"
#define MAXLINE 256
#define MAXNAME 20
/* system dependent */
extern void canon(char *sys, char cansys[]);
extern int lcreat(char *name, int mode);
/* chat.c */
extern int getline(char line[]);
extern long fdcopy(int dfd, int sfd, long size, unsigned long *crc);
extern int recvfile(int fd);
extern int sendfile(int fd);
/* chkperm.c */
extern int chkperm(int fd);
/* crc.c */
extern void crcinit(int *aux);
extern unsigned long crcincr(unsigned char *, size_t, unsigned long, int *);
/* eprintf.c */
extern char *prog;
extern void eprintf(char *fmt, ...);
/* path.c */
extern char *getpath(FILE *);
/* scanq.c */
extern void scanq(char *remsys, void (*func)(char *, int, char *));
/* tryperm.c */
extern int tryperm(int fd);
, int sfd, long size, unsigned long *crc);
extern int recvfile(int fd);
extern int sendfile(int fd);
/* chkperm.c */
extern int chkperm(int fd);
/* crc.c */
extern voape/chat.c 644 3513 4 3611 5027732160 5740 #include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include "dist.h"
#define MIN(A, B) ((A) < (B) ? (A) : (B))
int
getline(char line[])
{
int i;
i = 0;
do
if (read(0, line + i, 1) != 1)
return 0;
while (line[i++] != '\n' && i < MAXLINE - 1);
if (line[i - 1] != '\n')
return 0;
line[i] = 0;
return 1;
}
long
fdcopy(int dst, int src, long size, unsigned long *crc)
{
char buf[4096];
long cc, total;
int aux;
total = 0;
if (crc) {
crcinit(&aux);
*crc = 0;
}
while (total < size) {
if ((cc = read(src, buf, MIN(sizeof buf, size - total))) <= 0)
break;
if (crc)
*crc = crcincr((unsigned char *) buf, cc, *crc, &aux);
if (write(dst, buf, cc) != cc)
break;
total += cc;
}
return total;
}
int
sendfile(int fd)
{
long len;
char mesg[MAXLINE];
unsigned long crc;
/* size header */
len = lseek(fd, 0L, 2);
lseek(fd, 0L, 0);
sprintf(mesg, "file length=%ld\n", len);
write(1, mesg, strlen(mesg));
/* body */
if (fdcopy(1, fd, len, &crc) != len) {
eprintf("sendfile fdcopy failure");
return 0;
}
/* crc footer */
sprintf(mesg, "file crc=%lu\n", crc);
write(1, mesg, strlen(mesg));
/* acknowledge */
if (!getline(mesg) || strcmp(mesg, "ok\n") != 0) {
eprintf("sendfile acknowledgement failure");
return 0;
}
return 1;
}
int
recvfile(int fd)
{
long len;
char mesg[MAXLINE];
unsigned long crc, trycrc;
/* size header */
if (!getline(mesg) || sscanf(mesg, "file length=%ld\n", &len) != 1) {
eprintf("recvfile header munged");
return 0;
}
/* body */
if (fdcopy(fd, 0, len, &crc) != len) {
eprintf("recvfile fdcopy failure");
return 0;
}
/* crc footer */
if (!getline(mesg) || sscanf(mesg, "file crc=%lu\n", &trycrc) != 1) {
eprintf("recvfile footer munged");
return 0;
}
if (crc != trycrc) {
eprintf("recvfile crc failure");
return 0;
}
/* acknowledge */
sprintf(mesg, "ok\n");
write(1, mesg, strlen(mesg));
return 1;
}
|| sscanf(mesg, "file length=%ld\n", &len) != 1) {
eprintf("recvfile header munged");
return 0;
}
/* body */
iape/chkperm.c 644 3513 4 135 5030710456 6426 #include <stddef.h>
#include <stdio.h>
#include "dist.h"
int
chkperm(int fd)
{
return 1;
}
/* crc footer */
if (!getline(mesg) || sscanf(mesg, "file crc=%lu\n", &trycrc) != 1) {
eprintf("recvfile footer munged");
return 0;
}
if (crc != trycrc) {
eprintf("recvfile crc failure");
return 0;
}
/* acknowledge */
sprintf(mesg, "ok\n");
write(1, mesg, strlen(mesg));
return 1;
}
|| sscanf(mesg, "file length=%ld\n", &len) != 1) {
eprintf("recvfile header munged");
return 0;
}
/* body */
iape/transmit.c 644 3513 4 2347 5025030057 6661 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "dist.h"
static void
sendjob(char *job, int num, char *sys)
{
char busy[MAXNAME], done[MAXNAME], mesg[MAXLINE];
int fd, attempt;
sprintf(busy, "%d.%s", num, BUSY);
sprintf(done, "%d.%s", num, DONE);
fd = lcreat(busy, 0600);
if (fd < 0)
return;
dup2(fd, 2);
close(fd);
/* avoid race condition */
if (access(done, F_OK) == 0)
goto out;
fd = open(DATA, O_RDONLY);
if (fd < 0)
goto out;
if (!chkperm(fd)) {
close(fd);
goto out;
}
sprintf(mesg, "begin job %s\n", job);
write(1, mesg, strlen(mesg));
if (!sendfile(fd))
goto err;
if (!getline(mesg) || sscanf(mesg, "client attempt=%d\n", &attempt) != 1)
goto err;
if (!attempt)
goto out;
if (!recvfile(2))
goto err;
rename(busy, done);
fd = open("/dev/null", O_WRONLY);
dup2(fd, 2);
close(fd);
return;
out:
/* continue with the next job */
remove(busy);
return;
err:
/* failure that's too hard to resynchronize */
eprintf("sendjob really barfed");
remove(busy);
exit(1);
}
int
main(int argc, char *argv[])
{
prog = argv[0];
if (argc < 2) {
eprintf("usage: transmit system");
return 1;
}
scanq(argv[1], sendjob);
return 0;
}
= 1)
goto err;
if (!attempt)
goto out;
if (!recvfile(2))
goto err;
rename(busy, done);
fd = open("/dev/null", O_WRONLY);
dup2(fd, 2);
close(fd);
return;
out:
/* continue with the next job */
remove(busy);
return;
err:
/* failure that's too hard to resynchroape/showq.c 644 3513 4 352 5030710545 6136 #include <stddef.h>
#include <stdio.h>
#include "dist.h"
static void
showq(char *job, int num, char *sys)
{
printf("%s: %d %s\n", job, num, sys);
}
int
main(int argc, char *argv[])
{
prog = argv[0];
scanq(0, showq);
return 0;
}
)
goto err;
if (!attempt)
goto out;
if (!recvfile(2))
goto err;
rename(busy, done);
fd = open("/dev/null", O_WRONLY);
dup2(fd, 2);
close(fd);
return;
out:
/* continue with the next job */
remove(busy);
return;
err:
/* failure that's too hard to resynchroape/TODO 644 3513 4 330 5023514043 5312 Pervasive error checking:
* dist.sh
check exit code of mkpkg and other programs; don't
make a valid queue entry unless everything went perfectly.
* scanq.c transmit.c:
inspect for places that need error checking
wq);
return 0;
}
)
goto err;
if (!attempt)
goto out;
if (!recvfile(2))
goto err;
rename(busy, done);
fd = open("/dev/null", O_WRONLY);
dup2(fd, 2);
close(fd);
return;
out:
/* continue with the next job */
remove(busy);
return;
err:
/* failure that's too hard to resynchroape/path.c 644 3513 4 4341 5027737356 5772 /* brutally hacked from ../pkg/path.c */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "dist.h"
#define CHUNK 64
static char *r;
static unsigned size;
static char *
ralloc(char *ptr, size_t size)
{
char *result;
if (!ptr)
result = malloc(size);
else
result = realloc(ptr, size);
if (!result) {
eprintf("out of memory in getpath");
exit(1);
}
return result;
}
char *
getpath (FILE *file)
{
register int c;
register int len = 0;
c = getc (file);
while (!isspace(c) && c != EOF) {
register int i = 0, n = 0;
/* determine the next input character */
if (c == '\\') {
c = getc (file);
switch (c) {
case '\\':
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'v':
c = '\v';
break;
case ' ':
/* c = ' '; */
break;
default:
while (c >= '0' && c <= '7' && i < 3) {
n = (n << 3) + c - '0';
i++;
c = getc (file);
}
ungetc (c, file);
c = n;
break;
}
}
/* ensure there's room in the buffer */
if (len >= size)
r = ralloc (r, size += CHUNK);
/* put the character in the buffer */
r[len++] = c;
/* read the next character */
c = getc (file);
}
/* unless we hit eof, we read one character too far. */
if (c != EOF)
ungetc (c, file);
/* put a final null into the buffer */
if (len >= size)
r = ralloc (r, size += CHUNK);
r[len] = '\0';
return r;
}
void
putpath (FILE *file, char *path)
{
register char *p = path;
register int c;
while ((c = *p++) != NULL) {
switch (c) {
case '\n':
fprintf (file, "\\n");
break;
case '\r':
fprintf (file, "\\r");
break;
case '\b':
fprintf (file, "\\b");
break;
case '\t':
fprintf (file, "\\t");
break;
case '\f':
fprintf (file, "\\f");
break;
case '\v':
fprintf (file, "\\v");
break;
case '\\':
fprintf (file, "\\\\");
break;
case ' ':
fprintf (file, "\\ ");
break;
default:
if (iscntrl (c))
fprintf (file,
*p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
c);
else
putc (c, file);
break;
}
}
}
e '\r':
fprintf (file, "\\r");
break;
case '\b':
fprintf (file, "\\b");
break;
case '\t':
fprintf (file, "\\t");
break;
case '\f':
fprintf (file, "\\f");
break;
case '\v':
fprintf (file, "\\v");
break;
case '\\':
fprintf (file, "\\\\")ape/crc.c 644 3513 4 7124 5030710503 5562 #include <stddef.h>
#include <stdio.h>
#include "dist.h"
/* CRC table for cksum from IEEE 1003.2 draft 11. */
const unsigned long crctab[] = {
0x7FFFFFFF, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4619AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
void
crcinit(int *aux)
{
*aux = 0;
}
/* incrementally compute crc for part of a block */
unsigned long
crcincr(unsigned char *addr, size_t size, unsigned long crc, int *aux)
{
int i;
while (size--) {
i = (crc >> 24) ^ *addr++;
if (i == 0) {
i = (*aux)++;
if (*aux >= sizeof crctab / sizeof crctab[0])
*aux = 0;
}
crc = (crc << 8) ^ crctab[i];
}
return crc;
}
, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4619AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
void
crcinit(int *aux)
{
*aux = 0;
}
/* incrementally compute crc for part of a block */
unsigned long
crcincr(unsigned char *addr, size_t size, unsigned long crc, int *aux)
{
int i;
while (size--) {
i = (crc >> 24) ^ *addr++;
if (i == 0) {
i = (*aux)++;
if (*aux >= sizeof crctab / siape/genmail.sh 644 3513 4 1605 5031142116 6615 #! /bin/sh -
# send mail to a user for the given job
# option -f: say this is the final report
exec 2>> %SDIR%/log
PATH=%LDIR%:/bin:/usr/bin:/usr/ucb:/usr/bsd
cd %SDIR%
if [ "x$1" = x-f ]
then
shift
final=1
else
final=0
fi
if [ $# != 1 ]
then
exit 1
fi
user=`ls -ld $1 | awk '{print $3}'`
host=`hostname`
trap 'rm -f /tmp/genmail.$$' 0 1 2 3
rm -f /tmp/genmail.$$
if [ $final = 0 ]
then
echo current status report of dist job $host!$1
else
echo final status report of dist job $host!$1
fi > /tmp/genmail.$$
while read num sys
do
if [ -f $1/$num.mail ]
then
:
else
if [ -s $1/$num.done ]
then
echo $sys completed with errors:
sed 's/^/ /' $1/$num.done
touch $1/$num.mail
elif [ -f $1/$num.done ]
then
echo $sys completed successfully.
touch $1/$num.mail
else
echo $sys not completed.
fi
fi
done < $1/ctl >> /tmp/genmail.$$
mail $user < /tmp/genmail.$$
]
then
echo current status report of dist job $host!$1
else
echo final status report of dist job $host!$1
fi > /tmp/genmape/nightly.sh 644 3513 4 173 5021521457 6646 #! /bin/sh -
# nightly shell script to send mail for unfinished jobs
cd %SDIR%
for job in Q.*
do
%LDIR%/genmail $job
done
d with errors:
sed 's/^/ /' $1/$num.done
touch $1/$num.mail
elif [ -f $1/$num.done ]
then
echo $sys completed successfully.
touch $1/$num.mail
else
echo $sys not completed.
fi
fi
done < $1/ctl >> /tmp/genmail.$$
mail $user < /tmp/genmail.$$
]
then
echo current status report of dist job $host!$1
else
echo final status report of dist job $host!$1
fi > /tmp/genmape/cleanq.c 644 3513 4 211 5031454571 6237 #include <stddef.h>
#include <stdio.h>
#include "dist.h"
int
main(int argc, char *argv[])
{
prog = argv[0];
scanq(0, 0);
return 0;
}
sed 's/^/ /' $1/$num.done
touch $1/$num.mail
elif [ -f $1/$num.done ]
then
echo $sys completed successfully.
touch $1/$num.mail
else
echo $sys not completed.
fi
fi
done < $1/ctl >> /tmp/genmail.$$
mail $user < /tmp/genmail.$$
]
then
echo current status report of dist job $host!$1
else
echo final status report of dist job $host!$1
fi > /tmp/genmdoc.ms 644 3513 4 1177 5032532571 5223 .TL
A System for Automatic Source Distribution
.AU
Mike Haertel
.AI
AT&T Bell Laboratories
Murray Hill, NJ 07974
.AB
.\"
.\" "Single Heterogeneous network seeks automatic software distribution mechanism..."
.\" \(em Greg Lindahl
.\"
With the advent of wildly heterogeneous networks, automatic software
distribution has become a nightmare. Binaries are useless, and source
code often fails to recompile across ostensibly standard systems.
Finally, security is, as always, a concern, especially when the automatic
installation of priviliged system software is desired. We describe
yet another attempt to address some of these issues.
.AE
Single Heterogeneous network seeks automatic software distribution mechanism..."
.\" \(em Greg Lindahl
.\"
With the advent of wildly heterogeneous networks, automatic software
distribution has become a nightmare. Binaries are useless, and source
code often fails to recompile across ostensibly standard systems.
Finally, security is, as always, a concern, especially when the automatigenmkfile.sh 644 3513 4 721 5031150727 6362 #! /bin/sh
echo "<mkconf.$1" > mkfile
cat << 'EOF' >> mkfile
DIRS=ape pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatiman/ 755 3513 4 0 5031450027 4567 man/dist.1 644 3513 4 3344 5031462000 5677 .TH DIST 1
.SH NAME
dist \(mi distribute files to remote systems
.SH SYNOPSIS
.\".BI "dist [ " system " | -[Ff] " file " ] [ -n ] " files
.BI "dist [ " system " | -[Ff] " file " ] " files
.PP
.B "dist -q ["
.I system
.B "| -[Ff]"
.I file
.B "]"
.PP
.B "dist -r ["
.I system
.B "| -[Ff]"
.I file
.B "]"
.SH DESCRIPTION
.I Dist
distributes files to other systems, where they are installed
under the same names.
.I Dist
operates by packaging the files with
.IR mkpkg (1),
and queuing the resulting package in a spool directory to be
picked up by the remote systems.
.PP
The remote systems' names are given as a single argument, containing
the system names (in the format of
.IR ipc (3))
separated by white space; alternatively, the
.B -f
option may be used to specify a file containing a list of system names.
The
.B -F
option is identical to
.B -f
except
.I dist
looks for the file in a standard directory.
.PP
The
.I dist
command has two forms of use. In the first form,
.I dist
packages a group of files and queues them for transmission.
.\"If the
.\".B -n
.\"option is specified, the remote systems are notified that the package
.\"is available.
The
.I file
arguments may be either file names or options for
.IR mkpkg (1).
.PP
When
.B -q
is given,
.I dist
queries the named systems and displays the contents of their transmission
queues; if no systems are given the local queue is displayed
by default.
.PP
When
.B -r
is given,
.I dist
calls the named system and installs any packages it has queued for
the local system.
.SH EXAMPLES
Distribute a new binary for grep to all vaxes:
.IP
.B "dist -F vaxes /bin/grep"
.SH FILES
.B /usr/lib/dist/*
.br
.B /usr/lib/dist/destinations/*
.br
.B /usr/spool/dist/*
.SH "SEE ALSO"
.IR mkpkg (1),
.IR dist (5),
.IR dist (8)
I dist
queries the named systems and displays the contents of their transmission
queues; if no systems are given the local queue is displayed
by default.
.PP
When
.B -r
is given,
.I dist
calls the named system and installs any packages it has queued for
the local system.
.SH EXAMPLESman/dist.5 644 3513 4 3634 5017041737 5723 .TH DIST 5
.SH NAME
dist \(mi spool directory structure for
.B dist
.SH SYNOPSIS
.ds sd /usr/spool/dist
.B \*(sd/Q.*/
.PP
.B \*(sd/Q.*/ctl
.PP
.B \*(sd/Q.*/data
.PP
.B \*(sd/Q.*/files
.PP
.B \*(sd/Q.*/[1-9]*.busy
.PP
.B \*(sd/Q.*/[1-9]*.done
.SH DESCRIPTION
The
.I dist
spool directory contains distributions queued for transmission
to remote systems, and is also used as the working directory when
unpacking distributions received from remote systems.
.PP
Each job has its own subdirectory
of the spool directory. Several files with conventional names
appear in this per-job directory, as well as files with
generated names containing status information for each remote
system in the job.
.PP
The files in a job's spool directory include:
.TP
.B ctl
The control file contains a list of identifying numbers
and remote system names. The identifying number
is used in file names containing status information for corresponding
remote system, since network names may not be legal file names.
The file contains a sequence of lines; each line contains
an identifying number, a space, and the corresponding remote system name.
.TP
.B data
The data file contains the output of
.IR mkpkg (1)
for the distribution.
.TP
.B files
A list of file names specified in the command line
that created the job. This is mainly used as a key
for superseding jobs: if a new job is created that
contains a superset of the files of some old
job, the old job is marked as completed for all remote
systems it has in common with the new job.
.TP
.B [1-9]*.busy
An empty lock file indicating that the identified system
is currently receiving the distribution.
.TP
.B [1-9]*.done
Status file indicating that the identified system is
finished with the distribution. This file is empty
if and only if the distribution was successfully installed;
otherwise it contains any error messages from the remote
system.
.SH FILES
.B \*(sd/*
.SH "SEE ALSO"
.IR dist (1),
.IR dist (8)
b is marked as completed for all remote
systems it has in common with the new job.
.TP
.B [1-9]*.busman/dist.8 644 3513 4 7340 5017051644 5722 .TH DIST 8
.ds ld /usr/lib/dist
.SH NAME
connect, dispatch \(mi generic network client and server
.PP
query, receive, notify \(mi network clients
.PP
answer, transmit, notified \(mi network servers
.SH SYNOPSIS
.B \*(ld/connect
.I remote-system remote-cmd io local-cmd local-args
.PP
.B \*(ld/query
.PP
.B \*(ld/receive
.I package-id
.PP
.B \*(ld/notify
.I package-id
.PP
.B \*(ld/dispatch
.I remote-system remote-user
.PP
.B \*(ld/answer
.I remote-system remote-user
.PP
.B \*(ld/transmit
.I remote-system remote-user
.PP
.B \*(ld/notified
.I remote-system remote-user
.SH DESCRIPTION
These programs act behind the scenes on behalf of
.IR dist (1).
.I Connect
and
.I dispatch
deal with all the details of establishing and authenticating connections
on the network. The remaining programs are independent of the underlying
network.
.PP
.I Connect
encapsulates all the details of making a call on the underlying network.
Its arguments are mostly self explanatory.
.I Remote-cmd
must be one of
.BR answer ", " transmit ", or " notified "."
If
.I io
is
.BR i ,
the standard input of the local command is connected
to the standard output of the remote command. If
.B i
is not specified, the local command inherits its local
standard input, and the remote command has its standard
output directed to a log file.
Similarly,
.B o
means the standard output of the local command is connected
to the standard input of the remote command. If
.B o
is not specified, the local command inherits its local
standard output, and the remote command has its standard
input attached to
.BR /dev/null .
Legal possibilities are
.BR i ,
.BR o ,
and
.BR io.
.PP
.I Dispatch
is intended to be called by a generic network daemon
such as
.IR svcmgr (8).
It talks to
.I connect
and takes care of establishing the appropriate standard input and
output before calling
.IR answer ,
.IR transmit ,
or
.IR notified .
.I Dispatch
expects arguments giving the remote system, remote user, and remote
address, and passes these to whatever program it calls.
.PP
.I Query
is a simple program called by
.I connect
with its standard input attached to the remote
.IR answer .
.I Query
displays a formatted version of the queue list from the remote machine.
(It may even turn out to be the same as
.BR /bin/cat .)
.PP
.I Receive
is a more complicated program that is called by
.I connect
with both its input and output attached to the remote
.IR transmit .
.I Receive
performs actual software installation and sends a transcript of any
errors back to the remote system.
.PP
.I Notify
is a simple program called by
.I connect
with its standard output attached to the remote
.IR notified .
.I Notify
sends an announcement of the availability of some specific package.
.PP
All of the servers read a configuration file,
.BR \*(ld/conf ,
to determine how to handle installation on behalf
of a given remote system and user. The file consists of lines
containing patterns and actions. Empty lines or lines beginning
with a '\c
.BR "#" "'"
are ignored. The first three fields (separated by white space)
of each line are regular
expressions to be matched the remote system name, remote user name,
and file name respectively. The first line in which all three match
will be chosen.
The remainder of the line contains
the associated actions.
Each action is an alphanumeric name, optionally followed immediately
by a parenthesized argument.
.PP
Someday the actions will be enumerated here, but I don't know what
they are yet.
.SH EXAMPLES
A simple configuration file:
.EX
.ta \w'00000000'u +\w'00000000'u +\w'00000000'u +\w'00000000'u +\w'00000000'u +\w'00000000'u
# system user pathname action
# we are willing to distribute stuff to coma and pyxis.
(coma|pyxis) .* .* accept(yes)
\&.* .* .* accept(no)
.EE
.SH "SEE ALSO"
.IR dist (1),
.IR dist (5)
ach action is an alphanumeric name, optionally followed immediately
by a parenthesized argument.
.PP
Someday the actions will be enumerated here, but I don't know what
they are yet.
.SH EXAMPLES
A simple configuration file:
.EX
.ta \w'00000000'u +\w'00000000'u +\w'00000000'u +\w'00000000mkconf.bsd 644 3513 4 106 5031147027 6030 SYS=bsd
BDIR=/usr/mjh/bin
LDIR=/usr/mjh/dist
SDIR=/usr/mjh/dist/spool
pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatimkconf.sysv 644 3513 4 73 5031452264 6251 SYS=sysv
BDIR=/v/bin
LDIR=/v/lib/dist
SDIR=/usr/spool/dist
dist/spool
pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatimkconf.v10 644 3513 4 76 5031450436 5655 SYS=v10
BDIR=/usr/bin
LDIR=/usr/lib/dist
SDIR=/usr/spool/dist
t/spool
pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatimkrules.bsd 644 3513 4 57 5031147365 6227 CC=cc -g
CCLIB=
O=o
OS=o
SH=sh
AR=ar
RL=ranlib
usr/spool/dist
t/spool
pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatimkrules.sysv 644 3513 4 64 5031457630 6460 CC=cc -g -I../sysv
CCLIB=
O=o
OS=o
SH=sh
AR=ar
RL=:
pool/dist
t/spool
pkg $SYS
%-compile:V: paths.h
cd $stem; mk compile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatimkrules.v10 644 3513 4 166 5032136552 6103 CC=/usr/ape/apebin/pcc -g -D_POSIX_SOURCE -D_RESEARCH_SOURCE
CCLIB=/usr/ape/lib/libv.a
O=o
OS=o
SH=sh
AR=ar
RL=ranlib
mpile
%-install:V: %-compile
cd $stem; mk install
%-clean:V:
cd $stem; mk clean
ape-compile pkg-compile:Pexit 1: $SYS-compile
compile:V: ${DIRS:%=%-compile}
install:V: ${DIRS:%=%-install}
clean:V: ${DIRS:%=%-clean}
rm -f paths.h
paths.h: mkconf.$SYS
echo '#define LDIR "'$LDIR'"' > paths.h
echo '#define SDIR "'$SDIR'"' >> paths.h
EOF
always, a concern, especially when the automatinotes/ 755 3513 4 0 5031450036 5144 notes/IDEAS 644 3513 4 1446 5013610625 6010 Use APE everywhere. Keep inspkg, mkpkg, seal, and unseal essential unchanged
from old ASD stuff.
Client distributee calls distributor rather than vice versa;
eliminate the need for trust.
Eliminate the crypto stuff; nobody uses it. Either that or figure out
a good way to put it in avoiding the problem Dennis mentioned about
delayed replies.
What about user ids and group ids? There seem to be some hacks
for when running setuid under Unix. Does any of that mean anything
real, or is it just feeping creaturism?
Take out the feature for shipping special files--there is no
real support of special files in APE, and we want to run under
that everywhere. If you really want to you can ship a script
to make special files on the remote machine anyway. (How calvinist
are you feeling, Andrew asks?)
the problem Dennis mentioned about
delayed replies.
What about user ids and group ids? There seem to be some hacks
for when running setuid under Unix. Does any of that mean anything
real, or is it just feeping creanotes/PLAN 644 3513 4 6110 5012057047 5710 The ship system will live in two places, the library directory $L (typically
/usr/lib/ship) and the spool directory $S (typically /usr/spool/ship).
Operations are of two classes: client functions and server functions.
A single machine may operate as both a client and a server. The whole
setup is controlled by a single configuration file, $L/conf.
The configuration file consists of a sequence of pattern lines interspersed
with definitions pertaining to clients or servers matching the patterns.
Patterns are of the form 'key: match (match ...) (key: match ...)'; the whole
pattern may be prefixed by an exclamation point to negate it. Definitions
are of the form var=value, in shell syntax, and appear one to a line prefixed
by a tab character.
Each match is a restricted globbing expression, of the form understood by the shell.
Client and server programs scan the configuration file in order, choosing
only the first configuration that applies to a given situation.
Lines beginning with a # are comments.
For example, the following excerpt might occur in the configuration file of
a sources repository machine.
# We accept connections from client1, client2, and client3.
client: client1 client2 client3
accept=yes
client: *
accept=no
# We periodically poll server1, which sends us some stuff now and then.
poll: server1
poll=hourly
# The user fred on server1 is a klutz.
server: server1 user: fred
accept=no
# We also don't accept stuff from guests.
server: server1 group: guest
accept=no
# This line applies if none of the rejections above do.
server: server1
accept=yes
For each configuration (server, client, poll, or other to be invented), some
assignments may be optional and others mandatory. For example, for client:
and server: configurations, the 'accept=' assignment is probably mandatory.
However, for clients the 'notify=' (do we notify them when we have a new package
for them, or do we wait for them to call?) is optional and defaults (probably)
to 'no'.
One uniform option understood by all configuration types is the 'transport='
option, which specifies a program to use to place the network call. On most
machines this will probably be subsumed by the networking library, but some
might require special treatment.
There should be some sort of mechanism for file inclusion. This would allow
system administrators to delegate administration of some aspects of the system
to other users without actually granting write permission for the $L/conf file.
Some specific programs include:
$L/poll - a program run periodically on clients to poll appropriate servers
$L/client - a program run on the client to accept a shipment from a particular server
$L/server - the program run on the server that actually makes shipments
$L/notify - a program run on the server to notify clients
$L/notified - notify's peer on clients
The spool directory will be organized to have one subdirectory for each
unfinished job. Each job's subdirectory will contain a data file and
control files for each client. This organization makes it easy to
atomically access and modify the status of any ongoing job.
program run on the client to accept a shipment from a particular server
$L/server - the program run on the server that actually makes shipments
$L/notify - a program run on the server to notify clients
$L/notified - notify's peer on clients
The spool directory will be organized to have one subdirectory for each
unfinished job. Each job's subdirectory will contain a data file and
control files for each client. This organization makesnotes/PLAN2 644 3513 4 6620 5015021031 5763 The nasd (pronounced "nasty") system will live in two places, the library
directory $L (typically /usr/lib/nasd) and the spool directory $S (typically
/usr/spool/nasd).
Operations are of two classes: client functions, and server functions.
A machine may act as both a client and a server.
The user interface to the system is the 'dist' command (details to be worked out,
but mostly like ship, except the destination is taken from argument(s) rather than
the environment).
Several programs work behind the scenes:
$L/connect - make a network connection and call $L/query, $L/receive, or $L/notify
$L/query - query a server for pending jobs
$L/receive - request a package from a server and optionally install it
$L/notify - notify a client that a package is a available
$L/dispatch - dispatch incoming network connections to $L/answer, $L/send, or $L/notice
$L/answer - the server for query
$L/send - the server for receive
$L/notice - the server for notify
The network programs are organized so they can work on a batch network as well
as an interactive network. $L/query, $L/receive expect only input from the
network connection; $L/connect passes any initial arguments to the remote server.
$L/notify produces only output for the network connection.
$L/dispatch, $L/answer, $L/send, and $L/notice are similarly organized.
The whole thing is driven by a configuration file $L/conf describing the
allowed operations to be requested by remote machines. The format of the
file is as follows: Lines beginning with a # are comments and are ignored.
Other lines are of the form:
service system user pathname action
Service is "server" or "client"; system, user, and pathname are regular
expressions, and action is a list of actions, separated by +. A sample
configuration file might be:
# service system user pathname action
# we are willing to distribute stuff to coma and pyxis.
server (coma|pyxis) .* .* accept(yes)
server .* .* .* accept(no)
# we don't like fred
client .* fred .* accept(no)
# we also refuse to accept stuff from root as a matter of principle
client .* root .* accept(no)
# stuff from bowell!mjh is installed in /usr/bin as root.
client bowell mjh /usr/bin/.* accept(yes)+user(root)
# we accept files in /usr/pub from anyone on alice, but we don't
# execute random commands for them
client alice .* /usr/pub/.* accept(yes)+cmds(no)
The configuration file is scanned for the first match to get actions. If no
match is found the request is rejected. Some actions, such as "accept", must
always be provided; others, such as "cmds" have defaults or are otherwise
# service system user pathname action
# we are willing to distribute stuff to coma and pyxis.
transmit (coma|pyxis) .* .* accept(yes)
transmit .* .* .* accept(no)
# we don't like fred
receive .* fred .* accept(no)
# we also refuse to accept stuff from root as a matter of principle
receive .* root .* accept(no)
# stuff from bowell!mjh is installed in /usr/bin as root.
receive bowell mjh /usr/bin/.* accept(yes)+user(root)
# we accept files in /usr/pub from anyone on alice, but we don't
# execute random commands for them
receive alice .* /usr/pub/.* accept(yes)+cmds(no)
The configuration file is scanned for the first match to get actions. If no
match is found the request is rejected. Some actions, such as "accept", must
always be provided; others, such as "cmds" have defaults or are otherwise
unnecessary.
stuff from bowell!mjh is installed in /usr/bin as root.
receive bowell mjh /usr/bin/.* accept(yes)+user(rootnotes/PLAN2+ 644 3513 4 0 5012311223 5742 notes/STRATEGY 644 3513 4 2401 5022742334 6420 The client loop:
* for each package it receives, it checks if file system permissions
will permit that package to be installed. if not, it rejects the
package and the server will retransmit it at a later date.
* it makes a backup package.
* it actually installs the package. if installation fails, it installs
the backup package and reports errors to the server.
The server loop:
* for each queued package matching the remote system name, check if
the current user has read access to each component of the package.
if not, continue with the next package.
* transmit the package.
* wait for the client to check file system permissions against the package.
the client may decide not to try installing it. if so, defer the package
until later, and continue with the next package.
* the client has decided to install the package. record the error
transcript the client returns (if any), and mark the package as done
for that system. (if it was the last outstanding system for the given
package, send mail to the owner of the package saying it was done.)
spool directory management:
* the spool directory and all files therein are owned by daemon.
the queue maker and the file server run setuid daemon so they can
make and remove queue entries respectively.
ue with the next package.
* the client has decided to install the package. record the error
transcript the client returns (if any), and mark the package as done
for that system. (if it was the last outstanding system for the given
package, send mailnotes/TODO 644 3513 4 511 5020574003 5675 Figure out which regexp library to use. POSIX mandates one.
Perhaps I should write it.
Use alarm() on network reads and writes.
Write educated mkfiles.
Replace NAME_MAX with something that might be less
pessimly large, considering that we have control over
all file names in use.
Add cleanq to the man page.
Also add canon.
ge. record the error
transcript the client returns (if any), and mark the package as done
for that system. (if it was the last outstanding system for the given
package, send mailpkg/ 755 3513 4 0 5032703620 4576 pkg/HISTORY 644 3513 4 3472 5013610562 5756 04/20/82 Initial version.
05/24/82 Added -b option for backup, created local versions
of tmpnam and ftw functions.
06/10/82 Handle strange characters in names.
08/04/82 Packaging a nonexistent file arranges to delete
the file during installation.
03/02/83 Handle character uid/gid not present on host
05/02/83 Added seal, unseal.
05/03/83 Split asd command into mkpkg, inspkg; general cleanup
05/12/83 In asdrcv, make separate keyfiles and permfiles.
05/15/83 Seal and unseal now handle lines beginning with "From"
08/26/83 Handle \ in file names properly
08/27/83 Ship is now a real command (well, a shell script...)
Asdrcv now looks in /etc/asd/keys and /etc/asd/perm.
09/10/83 It is now possible to package and install special files.
09/15/83 Asdrcv no longer passes -v to inspkg.
09/23/83 Renamed tmpnam to tmpname to avoid conflicts.
09/24/83 Change SIG_TYP to Sig_typ and declare it ("portability").
04/08/85 Change BMASK to BYTEMASK to avoid clashes with system.
04/10/85 Robustness changes in ship.sh
06/29/85 Makefile now conforms to reality again.
09/10/85 Handle symbolic links in package.c, inspkg.c, mkdir.c
09/10/85 Path.c accounts for \v
09/25/85 added -x and -X to mkpkg and inspkg for execution after installation
09/25/85 asdrcv now sends back stdout as well as stderr
01/29/86 ftw.c now closes directory before calling FTW_DP
05/29/86 numuid() and numgid() return real rather than
effective uid and gid on failure
07/04/86 adapted to UNIX PC
07/25/86 reworked system configuration stuff
09/06/86 removed duplicate free() in inspkg.c
09/11/86 arguments to struid() and strgid() should be unsigned
09/25/86 allow imbedded '.' and '-' in machine and user names
11/18/86 inspkg checks for error returns when closing files
01/20/89 more portable installation procedure
01/20/89 remove a special file before trying to install it
lling FTW_DP
05/29/86 numuid() and numgid() return real rather than
effective uid and gid on failure
07/04/86 adapted to UNIX PC
07/25/86 reworked system configuration stuff
09/06/86 removed duplipkg/TODO 644 3513 4 276 5013610563 5342 Fix up the #include files so they aren't so redundantly included.
Make schk() and nchk() more informative.
Look into Sig_type.
Declare all the externs from "asd.h" in appropriate places.
when closing files
01/20/89 more portable installation procedure
01/20/89 remove a special file before trying to install it
lling FTW_DP
05/29/86 numuid() and numgid() return real rather than
effective uid and gid on failure
07/04/86 adapted to UNIX PC
07/25/86 reworked system configuration stuff
09/06/86 removed duplipkg/alloc.c 644 3513 4 1014 5013610563 6117 /*
* storage allocator
*
* calls malloc or realloc and aborts if unsuccessful
*/
#include "asd.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *
alloc (size_t n)
{
register void *p;
p = malloc (n);
schk (p);
return p;
}
void *
ralloc (void *s, size_t n)
{
register void *p;
if (s == NULL)
return alloc (n);
p = realloc (s, n);
schk (p);
return p;
}
/* return a copy of a string */
char *
copy (char *s)
{
register char *r;
r = alloc (strlen (s) + 1);
strcpy (r, s);
return r;
}
e allocator
*
* calls malloc or realloc and aborts if unsuccessful
*/
#include "asd.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *
alloc (size_t n)
{
register void *p;
p = malloc (n);
schk (p);
return p;
}
void *
ralloc (void *s, size_t n)
{
register void *p;
if (s == NULL)
return alloc (n);
p = realloc (s, n);
schk (p);
return p;
}
/* return a copy of a string */
char *
copy (char *s)
{
register char *r;
r = alloc (strlen (s) + 1);
strcpy (r, s);
pkg/ar.h 644 3513 4 324 5013610563 5417 #define ARMAG "!<arch>\n"
#define SARMAG 8
#define ARFMAG "`\n"
struct ar_hdr {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
};
= malloc (n);
schk (p);
return p;
}
void *
ralloc (void *s, size_t n)
{
register void *p;
if (s == NULL)
return alloc (n);
p = realloc (s, n);
schk (p);
return p;
}
/* return a copy of a string */
char *
copy (char *s)
{
register char *r;
r = alloc (strlen (s) + 1);
strcpy (r, s);
pkg/args.c 644 3513 4 6462 5013610564 5776 #include "asd.h"
#include <libv.h>
#include <string.h>
int bflag; /* -b make a backup package */
int kflag; /* -k crypto key (deprecated) */
int Kflag; /* -K crypto key from file (deprecated) */
int nflag; /* -n don't actually install files */
int vflag; /* -v verbose output */
char *xstr; /* -x command to execute */
char *Xstr; /* -X file to execute */
struct replist *replist; /* -D substitutions */
int
getargs (int argc, char **argv, char *optkey, int (*func)(FILE *, char *))
{
register int c;
int rc = 0;
char *keyfile;
while ((c = getopt (argc, argv, optkey)) != EOF) {
register struct replist *rl;
register char *p, *q;
switch (c) {
case 'b':
bflag++;
break;
case 'k':
kflag++;
break;
case 'n':
nflag++;
break;
case 'v':
vflag++;
break;
case 'x':
if (xstr) {
fprintf (stderr, "duplicate -x ignored\n");
rc++;
} else if (Xstr) {
fprintf (stderr, "cannot have both -x and -X\n");
rc++;
} else
xstr = optarg;
break;
case 'X':
if (Xstr) {
fprintf (stderr, "duplicate -X ignored\n");
rc++;
} else if (xstr) {
fprintf (stderr, "cannot have both -x and -X\n");
rc++;
} else
Xstr = copy (transname (optarg));
break;
case 'D':
p = strchr (optarg, '=');
if (p == NULL) {
fprintf (stderr, "invalid option %s\n", optarg);
exit (1);
}
rl = new (struct replist);
/* copy the pathname to rl->source */
rl->source = alloc ((unsigned) (p - optarg + 1));
p = rl->source;
q = optarg;
while (*q != '=')
*p++ = *q++;
*p = '\0';
/* now expand rl->source */
p = rl->source;
rl->source = copy (fullname (p));
free (p);
/* expand rl->dest */
rl->dest = copy (fullname (q + 1));
/* link rl into the chain */
rl->link = replist;
replist = rl;
break;
case 'K':
Kflag++;
keyfile = optarg;
break;
case '?':
default:
rc++;
break;
}
}
if (rc) {
fprintf (stderr, "%s: bad argument\n", argv[0]);
exit (rc);
}
if (kflag && Kflag) {
fprintf (stderr, "%s: cannot specify both k and K\n", argv[0]);
exit (1);
}
/* read key from terminal if requested */
if (kflag) {
register char *p;
p = getpass ("Key:");
/* a null key is treated as no key at all */
if (p && *p)
setup (p);
else
kflag = 0;
}
/* read key from file if requested */
if (Kflag) {
char key[100];
register FILE *kf;
register char *p;
/* try to open the file */
kf = fopen (keyfile, "r");
if (kf == NULL) {
perror (keyfile);
exit (1);
}
/* read the first line */
p = fgets (key, sizeof (key), kf);
fclose (kf);
/* if EOF, assume no key */
if (p == NULL) {
Kflag = 0;
} else {
/* delete the trailing newline */
p = key;
while (*p != '\n' && *p != '\0')
p++;
*p = '\0';
/* if the key is empty, assume no key */
if (key[0] == '\0')
Kflag = 0;
else
setup (key);
}
}
if (func) {
/* process the arguments */
if (optind >= argc)
rc = (*func) (stdin, "standard input");
else {
register int i;
for (i = optind; i < argc; i++) {
register char *fn = argv[i];
register FILE *f = fopen (fn, "r");
if (f) {
rc += (*func) (f, argv[i]);
fclose (f);
} else {
fprintf (stderr, "%s: can't open %s\n", argv[0], fn);
rc++;
}
}
}
}
return rc;
}
ssume no key */
if (key[0] == '\0')
Kflag = 0;
else
setup (key);
}
}
if (func) {
/* process the arguments */
if (optind >= argc)
rc = (*func) (stdin, "standard input");
else {
pkg/asd.h 644 3513 4 4124 5014037413 5604 #include "ar.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#if 0
/* system-dependent stuff */
#ifdef unix
#ifndef major
#include <sys/sysmacros.h>
#endif
#endif
#endif
/* longest archive component name we will generate */
#define MAXCOMP 14
/* macro to allocate storage of a given type */
#define new(t) ((t *) alloc (sizeof (t)))
/* some systems define SIG_TYP, others don't, so we make our own */
#ifdef __STDC__
typedef void (*Sig_typ)(int);
#else
typedef int (*Sig_typ)();
#endif
struct replist {
char *source;
char *dest;
struct replist *link;
};
extern struct replist *replist;
/* structures to deal with archive headers */
struct ar_hdr ar_hdr;
struct hdr {
long size;
int mode;
long date;
};
extern struct hdr hdr;
/* alloc.c */
extern void *alloc(size_t);
extern void *ralloc(void *, size_t);
extern char *copy(char *);
/* args.c */
extern int bflag;
extern int kflag;
extern int Kflag;
extern int nflag;
extern int vflag;
extern char *xstr;
extern char *Xstr;
extern int getargs(int, char **, char *, int (*)(FILE *, char *));
/* chk.c */
extern void nchk(int);
extern void schk(void *);
/* crypt.c */
extern void resetN12(void);
extern void setup(char *);
extern void mangle(char *, char *);
/* data.c */
extern char *hextab;
extern char *instr;
/* fullname.c */
extern char *fullname(char *);
/* gid.c */
extern char *gidstr(gid_t);
extern gid_t gidnum(char *);
/* header.c */
extern long cvlong(char *, size_t, int);
extern long read_header(char *, FILE *);
extern void next_header(FILE *);
extern char *getfield(FILE *);
extern void geteol(FILE *);
/* mkdir.c */
extern int mkd(char *);
extern int rmdir(char *);
extern int rmall(char *);
/* package.c */
extern void pkgstart(void);
extern void pkgfile(char *);
extern int pkgend(void);
/* path.c */
extern char *getpath(FILE *);
extern void putpath(FILE *, char *);
/* pwd.c */
extern char *pwd(void);
/* transname.c */
extern char *transname(char *);
/* uid.c */
extern char *uidstr(uid_t);
extern uid_t uidnum(char *);
id next_header(FILE *);
extern char *getfield(FILE *);
extern void geteol(FILE *);
/* mkdir.c */
extern int mkd(char *);
extern int rmdir(char *);
extern int rmall(char *);
/* package.c */
extern void pkgstart(void);
extern void pkgfile(char *);
extern int pkgend(void);
/* path.c */
extern char *getpath(FILE *);
extern void putpath(FILE *, char *);
/* pwd.c */
extern char *pwd(void);
/* transname.c */
extern char *transpkg/chk.c 644 3513 4 472 5013610565 5563 #include "asd.h"
#include <stdio.h>
/*
* little subroutines to check return codes
*/
void
nchk (int n)
{
if (n < 0) {
fprintf (stderr, "unexpected error return -- help!\n");
exit (1);
}
}
void
schk (void *s)
{
if (s == NULL) {
fprintf (stderr, "unexpected error return -- help!\n");
exit (1);
}
}
pkgfile(char *);
extern int pkgend(void);
/* path.c */
extern char *getpath(FILE *);
extern void putpath(FILE *, char *);
/* pwd.c */
extern char *pwd(void);
/* transname.c */
extern char *transpkg/crypt.c 644 3513 4 3604 5013610566 6200 /*
* encryption service routines
*/
#include "asd.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
/* encryption parameters and tables */
#define ROTORSZ 256
#define MASK 0377
static char t1[ROTORSZ];
static char t2[ROTORSZ];
static char t3[ROTORSZ];
static char t4[ROTORSZ];
/* current rotor settings */
static int N1, N2;
void
resetN12(void)
{
N1 = N2 = 0;
}
void
setup(char *pw)
{
int ic, i, k, temp, pf[2];
unsigned random;
char buf[13];
long seed;
int pid;
resetN12();
strncpy(buf, pw, 8);
while (*pw)
*pw++ = '\0';
buf[8] = buf[0];
buf[9] = buf[1];
pipe(pf);
switch (pid = fork()) {
case -1:
fprintf (stderr, "seal: cannot fork\n");
exit (1);
case 0:
close(0);
close(1);
dup(pf[0]);
dup(pf[1]);
execl("/usr/lib/makekey", "-", 0);
execl("/lib/makekey", "-", 0);
exit(1);
default:
write(pf[1], buf, 10);
while (wait ((int *) NULL) != pid)
;
}
if (read(pf[0], buf, 13) != 13) {
fprintf(stderr, "seal: cannot generate key\n");
exit(1);
}
seed = 123;
for (i=0; i<13; i++)
seed = seed*buf[i] + i;
for(i=0;i<ROTORSZ;i++)
t1[i] = i;
for(i=0;i<ROTORSZ;i++) {
seed = 5*seed + buf[i%13];
random = seed % 65521;
k = ROTORSZ-1 - i;
ic = (random&MASK)%(k+1);
random >>= 8;
temp = t1[k];
t1[k] = t1[ic];
t1[ic] = temp;
if(t3[k]!=0) continue;
ic = (random&MASK) % k;
while(t3[ic]!=0) ic = (ic+1) % k;
t3[k] = ic;
t3[ic] = k;
}
for(i=0;i<ROTORSZ;i++){
t2[t1[i]&MASK] = i;
t4[i] = (t1[i] + t3[i]) & 0377;
}
}
void
mangle (char *buf, char *limit)
{
register int i;
register char *p;
register int n1 = N1, n2 = N2;
int n3;
p = buf;
while(p < limit) {
i = *p;
n3 = t4[n1];
i = t2[(t3[(t1[(i+n3)&MASK]+n2)&MASK]-n2)&MASK]-n3;
*p++ = i;
n1++;
if(n1==ROTORSZ) {
n1 = 0;
n2++;
if(n2==ROTORSZ) n2 = 0;
}
}
N1 = n1;
N2 = n2;
}
dom&MASK) % k;
while(t3[ic]!=0) ic = (ic+1) % k;
t3[k] = ic;
t3[ic] = k;
}
for(i=0;i<ROTORSZ;i++){
t2[t1[i]&MASK] pkg/data.c 644 3513 4 156 5013610566 5727 /*
* Initializations
*/
#include "asd.h"
char *hextab = "0123456789abcdef";
char *instr = "Instructions";
char *p;
register int n1 = N1, n2 = N2;
int n3;
p = buf;
while(p < limit) {
i = *p;
n3 = t4[n1];
i = t2[(t3[(t1[(i+n3)&MASK]+n2)&MASK]-n2)&MASK]-n3;
*p++ = i;
n1++;
if(n1==ROTORSZ) {
n1 = 0;
n2++;
if(n2==ROTORSZ) n2 = 0;
}
}
N1 = n1;
N2 = n2;
}
dom&MASK) % k;
while(t3[ic]!=0) ic = (ic+1) % k;
t3[k] = ic;
t3[ic] = k;
}
for(i=0;i<ROTORSZ;i++){
t2[t1[i]&MASK] pkg/fullname.c 644 3513 4 1461 5013610567 6642 /*
* fullname -- return the full pathname corresponding to the
* abbreviated pathname given as argument. Returned value
* is in a buffer that will stay around no longer than its
* argument or the next call to fullname, whichever is earlier.
*/
#include "asd.h"
#include <string.h>
char *
fullname (char *s)
{
register char *t;
static char *r;
static int size;
register unsigned n;
/* if first char is slash, absolute path */
if (s[0] == '/')
return s;
/* strip leading './' */
while (s[0] == '.' && s[1] == '/')
s += 2;
t = pwd();
/* null string or "." means current directory */
if (s[0] == '\0' || strcmp (s, ".") == 0)
return t;
n = strlen (s) + strlen (t) + 2;
if (n > size) {
r = ralloc (r, n);
size = n;
}
strcpy (r, t);
strcat (r, "/");
strcat (r, s);
return r;
}
(char *s)
{
register char *t;
static char *r;
static int size;
register unsigned n;
/* if first char is slash, absolute path */
if (s[0] == '/')
return s;
/* strip leading './' */
while (s[0] ==pkg/gid.c 644 3513 4 2533 5013610570 5575 #include "asd.h"
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define CHUNK 16
static struct gtab {
unsigned gid;
char *name;
} *gtab;
static int size, salloc;
char *
gidstr (gid_t gid)
{
register int i;
static char buf[12];
struct group *g;
/* search the cache for the gid */
for (i = 0; i < size; i++)
if (gtab[i].gid == gid)
return gtab[i].name;
/* try to find it in the system's database */
if (g = getgrgid(gid)) {
if (size % CHUNK == 0)
gtab = (struct gtab *) ralloc((char *) gtab, salloc += CHUNK);
gtab[size].gid = g->gr_gid;
gtab[size].name = copy(g->gr_name);
++size;
return gtab[size - 1].name;
}
/* failure, invent a string */
sprintf (buf, "#%u", gid);
return buf;
}
gid_t
gidnum (char *name)
{
register int i;
struct group *g;
/* if it starts with a #, use the number */
if (name[0] == '#')
return atoi (name + 1);
/* search the cache */
for (i = 0; i < size; i++)
if (strcmp (gtab[i].name, name) == 0)
return gtab[i].gid;
/* try to find it in the system's database */
if (g = getgrnam(name)) {
if (size % CHUNK == 0)
gtab = (struct gtab *) ralloc((char *) gtab, salloc += CHUNK);
gtab[size].gid = g->gr_gid;
gtab[size].name = copy(g->gr_name);
++size;
return gtab[size - 1].gid;
}
/* failure, invent a value */
return getgid();
}
, use the number */
if (name[0] == '#')
return atoi (name + 1);
/* search the cache */
for (i = 0; i < size; i++)
if (strcmp (gtab[i].name, name) == 0)
repkg/header.c 644 3513 4 4144 5013610570 6262 /*
* various subroutines to deal with archive headers
*/
#include "asd.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
struct hdr hdr;
/*
* convert p to a long value. maximum input length is len,
* input is to be interpreted in base b. No negative values.
*/
long
cvlong (char *p, size_t len, int base)
{
register int i;
register long r;
r = 0;
i = len;
do {
register int c = *p++;
if (isdigit (c))
r = r * base + c - '0';
} while (--i > 0);
return r;
}
long
read_header (char *name, FILE *file)
{
register int n;
register int i;
register char *p, *q;
n = fread ((char *) &ar_hdr, sizeof (ar_hdr), 1, file);
if (n != 1) {
fprintf (stderr, "can't read %s\n", name);
exit (1);
}
if (strncmp (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag)) != 0) {
fprintf (stderr, "input phase error on %s\n", name);
exit (1);
}
/* check the component name, allowing for trailing blanks */
p = name;
q = ar_hdr.ar_name;
for (i = 0; i < sizeof (ar_hdr.ar_name); i++) {
if (*q++ != (*p? *p++: ' ')) {
fprintf (stderr, "expected %s, got %.*s\n",
name, sizeof (ar_hdr.ar_name), ar_hdr.ar_name);
}
}
/* crack the archive header and put the information in "hdr" */
hdr.size = cvlong (ar_hdr.ar_size, sizeof (ar_hdr.ar_size), 10);
hdr.mode = cvlong (ar_hdr.ar_mode, sizeof (ar_hdr.ar_mode), 8);
hdr.date = cvlong (ar_hdr.ar_date, sizeof (ar_hdr.ar_date), 10);
return hdr.size;
}
/* advance to the start of the next archive header */
void
next_header(FILE *f)
{
if (hdr.size & 1)
getc(f);
}
/* skip leading white space, return a field */
char *
getfield (FILE *f)
{
register char c;
/* skip leading white space */
do c = getc (f);
while (isspace (c) && c != '\n');
/* if we hit a newline, something's wrong */
if (c == '\n') {
fprintf (stderr, "unexpected newline\n");
exit (1);
}
/* return the nonblank, read a "pathname" and return it */
ungetc (c, f);
return getpath (f);
}
/* insist on an end of line right here */
void
geteol (FILE *f)
{
register int c;
c = getc (f);
if (c != '\n') {
fprintf (stderr, "expected newline, got %c\n", c);
exit (1);
}
}
ister char c;
/* skip leading white space */
do c = getc (f);
while (isspace (c) && c != '\n');
/* if we hit a newline, something's wrong */
if (c == '\n') {
fprintf (stderr, "unexpected newline\n");
exit (1);
}
/* return the nonblank, read a "pathname" and return it */
ungetc (c, f);
return getpath (f);
}
/* insist on an end of line right here */
void
geteol (FILE *f)
{
register int c;
c pkg/inspkg.c 644 3513 4 25371 5013610572 6354 #include "asd.h"
#include <libv.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utime.h>
#define CHUNK 64
/* type codes for installation subroutine */
#define BACKUP 0
#define INSTALL 1
static void readtemp(int, FILE *);
/*
* 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 (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 (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(void)
{
register struct list *l;
while (l = dirs) {
dirs = l->next;
free (l->name);
free ((char *) l);
}
}
static char tfname[L_tmpnam];
static void delete(int);
/* process a single archive in a concatenation */
static int
doarch (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) tmpnam (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;
}
/*
* install the given file
*/
static int
process (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;
}
int
main (int argc, char **argv)
{
static char errbuf[BUFSIZ];
setbuf (stderr, errbuf);
umask (0);
return getargs (argc, argv, "nvbD:", process);
}
/*
* Make a pass through the temp file.
*/
static void
readtemp (int code, 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) {
#if 0
/* 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 = uidnum (getfield (tf));
gid = gidnum (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;
#endif
/* directory */
case 'd':
/* read the parameters */
param = getfield (tf);
mode = cvlong (param, strlen (param), 8);
uid = uidnum (getfield (tf));
gid = gidnum (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);
mkd (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(0);
exit (1);
}
strcpy (component, param);
uid = uidnum (getfield (tf));
gid = gidnum (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) {
struct utimbuf utb;
utb.actime = utb.modtime = hdr.date;
utime (path, &utb);
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_ISLNK
/* 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(0);
exit (1);
}
}
if (ferror (tf))
perror ("inspkg: internal temp file");
fclose (tf);
}
static void
delete(int sigarg)
{
unlink (tfname);
exit (3);
}
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;
pkg/mkcsum.c 644 3513 4 436 5013610572 6313 #include "seal.h"
unsigned long
mkcsum (unsigned long csum, char *start, char *limit)
{
register int c;
while (start < limit) {
c = (unsigned char) *start++;
if (csum & 1)
csum = (csum >> 1) | CSHIBIT;
else
csum >>= 1;
csum += c;
csum &= CSMASK;
}
return csum;
}
}
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;
pkg/mkdir.c 644 3513 4 4363 5031450263 6144 #include "asd.h"
#include <sys/wait.h>
#include <unistd.h>
#include "ftw.h"
#ifndef S_ISLNK
#ifdef S_IFLNK
#define S_ISLNK(M) (((M)&S_IFMT) == S_IFLNK)
#endif
#define S_ISREG(M) (((M)&S_IFMT) == S_IFREG)
#define S_ISDIR(M) (((M)&S_IFMT) == S_IFDIR)
#define S_ISCHR(M) (((M)&S_IFMT) == S_IFCHR)
#define S_ISBLK(M) (((M)&S_IFMT) == S_IFBLK)
#endif
/*
* mkd function -- tries to make a directory whose name is "d".
* returns 0 if successful or if d already exists and is a
* directory. On failure, returns mkdir's return code.
*/
int
mkd (char *d)
{
register int pid, w;
int status;
struct stat sb;
if (stat (d, &sb) >= 0)
return !S_ISDIR(sb.st_mode);
switch (pid = fork()) {
case 0:
/* we might be executed from a setuid program */
setgid (getegid());
setuid (geteuid());
execl ("/bin/mkdir", "mkdir", d, 0);
/* No break */
case -1:
return 1;
default:
do w = wait (&status);
while (w != pid && w > 0);
if (w == pid)
return status;
return w;
}
}
static int
rm (char *name, struct stat *sb, int type, struct FTW *ftw)
{
register int r;
switch (type) {
case FTW_F:
case FTW_SL:
r = unlink (name);
if (r < 0) {
perror (name);
return r;
}
break;
case FTW_D:
break;
case FTW_DNR:
fprintf (stderr, "cannot read directory %s\n", name);
exit (1);
case FTW_NS:
fprintf (stderr, "cannot stat %s\n", name);
exit (1);
case FTW_DP:
r = rmdir (name);
if (r != 0) {
fprintf (stderr,
"trouble removing directory %s\n", name);
return r;
}
}
return 0;
}
/*
* rmdir function -- tries to remove a directory whose name is "d".
* returns 0 if successful. On failure, returns rmdir's return code.
*/
int
rmdir (char *d)
{
register int pid, w;
int status;
struct stat sb;
if (stat (d, &sb) >= 0 && !S_ISDIR(sb.st_mode))
return -1;
switch (pid = fork()) {
case 0:
/* we might be executed from a setuid program */
setgid (getegid());
setuid (geteuid());
execl ("/bin/rmdir", "rmdir", d, 0);
/* No break */
case -1:
return 1;
default:
do w = wait (&status);
while (w != pid && w > 0);
if (w == pid)
return status;
return w;
}
}
/* rmall (s) recursively removes the object named s */
int
rmall (char *s)
{
if (access(s, 0) < 0)
return 0;
return ftw (s, rm, 8);
}
&sb) >= 0 && !S_ISDIR(sb.st_mode))
return -1;
switch (pid = fork()) {
case 0:
/* we might be executed from a setuid program */
setgid (getegid());
setuid (geteuid());
execl ("/bin/rmdir", "rmdir", d, 0);
/* No break */
case -1:
return 1;
default:
pkg/mkfile 644 3513 4 1607 5031450307 6061 <../mkconf.$SYS
<../mkrules.$SYS
LPROG=mkpkg inspkg seal unseal
LIB=lib-$O.a
LIBOBJ=alloc.$O args.$O chk.$O crypt.$O data.$O ftw.$O fullname.$O \
gid.$O header.$O mkcsum.$O mkdir.$O package.$O path.$O pwd.$O \
transname.$O uid.$O
compile:V: $LPROG
install:V: compile
test -d $LDIR || mkdir $LDIR
cp $LPROG $LDIR
mkpkg: mkpkg.$O $LIB
$CC -o $target $prereq $CCLIB
inspkg: inspkg.$O $LIB
$CC -o $target $prereq $CCLIB
seal: seal.$O $LIB
$CC -o $target $prereq $CCLIB
unseal: unseal.$O $LIB
$CC -o $target $prereq $CCLIB
lib-$O.a: $LIBOBJ
rm -f lib-$O.a
ar r lib-$O.a $LIBOBJ
$RL lib-$O.a
# Need to check these.
alloc.$O args.$O data.$O fullname.$O gid.$O: asd.h ar.h
header.$O inspkg.$O mkdir.$O: asd.h ar.h
mkpkg.$O package.$O path.$O pwd.$O seal.$O: asd.h ar.h
transname.$O uid.$O unseal.$O: asd.h ar.h
mkcsum.$O seal.$O unseal.$O: seal.h
clean:V:
rm -f *.[$OS] lib-[$OS].a $LPROG
$LIB
$CC -o $target $prereq $CCLIB
seal: seal.$O $LIB
$CC -o $target $prereq $CCLIB
unseal: unseal.$O $LIB
$CC -o $tpkg/mkpkg.c 644 3513 4 453 5013610574 6126 #include "asd.h"
#include <libv.h>
#include <stdio.h>
int
main (int argc, char **argv)
{
register int i;
static char errbuf[BUFSIZ];
setbuf (stderr, errbuf);
getargs (argc, argv, "vx:X:D:", 0);
pkgstart();
for (i = optind; i < argc; i++)
pkgfile (argv[i]);
i = pkgend();
return i;
}
asd.h ar.h
mkcsum.$O seal.$O unseal.$O: seal.h
clean:V:
rm -f *.[$OS] lib-[$OS].a $LPROG
$LIB
$CC -o $target $prereq $CCLIB
seal: seal.$O $LIB
$CC -o $target $prereq $CCLIB
unseal: unseal.$O $LIB
$CC -o $tpkg/package.c 644 3513 4 23235 5032702500 6443 #include "asd.h"
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "ftw.h"
FILE *tf;
static Sig_typ sigsav;
static int rc;
static char tfname[L_tmpnam];
static struct stat outsb;
static struct pack *pkhead, *pktail;
/*
* The following structure helps keep track of things being packaged.
* iname is the internal name of the component -- in other words,
* the archive element name. ename is the (short) pathname of the
* file. The structures are chained by the "link" field. All the
* other fields are copies of things returned by "stat" and are
* used mostly to make sure nothing changed while we were packaging.
* head and tail point to the first and last items in the chain.
* The first item is known to refer to the "Instructions" component.
*/
struct pack {
char *iname;
char *ename;
struct pack *link;
dev_t dev;
ino_t ino;
int uid, gid, mode;
time_t time;
off_t size;
};
/*
* Prepare to build a package. The package will appear on stdout.
* The argument is nonzero if remarks are to be read.
*/
static void
delete(int arg)
{
unlink (tfname);
exit (3);
}
void
pkgstart(void)
{
struct stat pks;
rc = 0;
nchk (fstat (fileno (stdout), &outsb));
/* establish a temporary file to hold the instruction information */
tmpnam (tfname);
tf = fopen (tfname, "w");
sigsav = signal (SIGINT, SIG_IGN);
if (sigsav != SIG_IGN)
signal (SIGINT, delete);
nchk (chmod (tfname, 0644));
schk ((char *) tf);
/* create the initial element in the component chain */
pkhead = pktail = new (struct pack);
pkhead->iname = copy (instr);
pkhead->ename = copy (tfname);
pkhead->time = 0;
pkhead->link = NULL;
pkhead->uid = getuid();
nchk (fstat (fileno(tf), &pks));
pkhead->gid = pks.st_gid;
pkhead->mode = 0100644;
}
static int consider(char *, struct stat *, int, struct FTW *);
/*
* put a file (or directory) into a package
*/
void
pkgfile (char *file)
{
register int x;
struct stat sb;
/* if the file is truly not present, generate a remove request */
#ifdef S_IFLNK
if (lstat (file, &sb) < 0 && errno == ENOENT) {
#else
if (stat (file, &sb) < 0 && errno == ENOENT) {
#endif
fprintf (tf, "r ");
putpath (tf, transname (file));
putc ('\n', tf);
} else {
x = ftw (file, consider, 8);
if (x != 0) {
rc++;
if (x == -1)
perror (file);
}
}
}
/*
* we are done building a package. This writes the actual file
* contents, so make sure the files are still around
*/
int
pkgend(void)
{
register struct pack *pack;
struct stat s;
/* write 'x' or 'X' execution item if requested */
if (xstr) {
fprintf (tf, "x ");
putpath (tf, xstr);
fprintf (tf, "\n");
}
if (Xstr) {
fprintf (tf, "X ");
putpath (tf, Xstr);
fprintf (tf, "\n");
}
/* we now know how long the first component is */
pkhead->size = ftell (tf);
/* we no longer need to write the temporary file */
fclose (tf);
/* describe the temp file correctly so it will pass later checks */
if (pkhead->time == 0)
(void) time (&pkhead->time);
nchk (stat (tfname, &s));
pkhead->dev = s.st_dev;
pkhead->ino = s.st_ino;
/*
* write the files out into an archive
*/
/* first the archive header */
printf (ARMAG);
/*
* run through the list, creating the archive components
* and deleting the list entries. One iteration per component.
* We know there is at least one component, because the
* "Instructions" component must be represented.
*/
do {
struct ar_hdr ah;
char buf[30];
register int c;
/* "pack" is the package component under consideration */
pack = pkhead;
if (pack->iname) { /* non-regular file entry, no data */
/* log it if requested */
if (vflag)
fprintf (stderr, "package %s\n",
strcmp (pack->iname, instr)? pack->ename: instr);
/* write the archive element header */
# define ent(a,x) sprintf(buf, "%-*s", sizeof(ah.a), x); \
strncpy (ah.a, buf, sizeof (ah.a))
ent (ar_name, pack->iname);
ent (ar_fmag, ARFMAG);
# undef ent
# define ent(a,x) sprintf(buf, "%*ld", sizeof(ah.a), (long) x); \
strncpy (ah.a, buf, sizeof (ah.a))
ent (ar_date, pack->time);
ent (ar_uid, pack->uid);
ent (ar_gid, pack->gid);
ent (ar_size, pack->size);
# undef ent
# define ent(a,x) sprintf(buf, "%*o", sizeof(ah.a), x); \
strncpy (ah.a, buf, sizeof (ah.a))
ent (ar_mode, pack->mode);
# undef ent
fwrite ((char *) &ah, sizeof (ah), 1, stdout);
/* write the archive element itself */
tf = fopen (pack->ename, "r");
if (tf == NULL) {
perror (pack->iname);
exit (1);
}
while ((c = getc (tf)) != EOF)
putchar (c);
/* if things now don't match, complain */
if (fstat (fileno (tf), &s) < 0 || s.st_size != pack->size ||
(s.st_mtime != pack->time && strcmp (pack->iname, instr)) ||
s.st_dev != pack->dev || s.st_ino != pack->ino ||
s.st_uid != pack->uid || s.st_gid != pack->gid ||
(s.st_mode & 07777) != (pack->mode & 07777)) {
fprintf (stderr, "phase error on %s\n",
pack->ename);
rc++;
}
fclose (tf);
if (pack->size & 1)
putchar ('\n');
}
/* delete the element, move on to the next */
if (pack->iname) free (pack->iname);
free (pack->ename);
pkhead = pack->link;
free ((char *) pack);
} while (pkhead);
/* zap the tail pointer for general cleanliness */
pktail = NULL;
nchk (unlink (tfname));
signal (SIGINT, sigsav);
return rc;
}
static void hdrsub(char *, struct stat *);
static char *iname(char *);
/* internal function for package creation */
static int
consider (char *name, struct stat *buf, int type, struct FTW *ftw)
{
register struct pack *pack;
register int mode;
char *biname;
#ifdef S_ISLNK
char *slname;
#endif
switch (type) {
case FTW_D:
fprintf (tf, "d %-*.4o ", MAXCOMP, buf->st_mode & 07777);
hdrsub (name, buf);
fprintf (tf, "\n");
break;
case FTW_SL:
if (ftw->level == 0) {
ftw->quit = FTW_FOLLOW;
return 0;
}
/* fall through */
case FTW_F:
mode = buf->st_mode;
/* Has this file already appeared? If so, it's a link */
for (pack = pkhead->link; pack; pack = pack->link) {
if (buf->st_dev == pack->dev &&
buf->st_ino == pack->ino) {
fprintf (tf, "l %s ", transname (pack->ename));
fprintf (tf, "%s\n", transname (name));
return 0;
}
}
if (S_ISREG(mode)) {
/* refuse to package the standard output */
if (buf->st_dev == outsb.st_dev &&
buf->st_ino == outsb.st_ino) {
fprintf (stderr, "skipping output file %s\n",
fullname (name));
return 0;
}
biname = iname (name);
fprintf (tf, "f ");
putpath (tf, biname);
fprintf (tf, " ");
hdrsub (name, buf);
fprintf (tf, "\n");
}
#ifdef S_ISLNK
else if (S_ISLNK(mode)) {
slname = alloc((unsigned)buf->st_size+1);
slname[buf->st_size] = '\0';
if (readlink(name, slname, buf->st_size) !=
buf->st_size) {
perror(name);
return (0);
}
fprintf (tf, "s %s ", transname (slname));
fprintf (tf, "%s\n", transname (name));
biname = NULL;
}
#endif
#if 0 /* We don't handle special files any more. */
else if (S_ISBLK(mode) || S_ISCHR(mode)) {
fprintf (tf, "%c %#o %d %d ",
mode == S_IFBLK? 'b': 'c',
buf->st_mode & 07777,
major (buf->st_rdev),
minor (buf->st_rdev));
hdrsub (name, buf);
fprintf (tf, "\n");
biname = NULL;
}
#endif
else {
fprintf (stderr, "%s: unrecognized file type\n",
fullname (name));
break;
}
/* package the file */
pack = new (struct pack);
pack->ename = copy (name);
pack->dev = buf->st_dev;
pack->ino = buf->st_ino;
pack->uid = buf->st_uid;
pack->gid = buf->st_gid;
pack->time = buf->st_mtime;
if (pack->time > pkhead->time)
pkhead->time = pack->time;
pack->size = buf->st_size;
pack->mode = buf->st_mode;
pack->link = NULL;
pack->iname = biname;
pktail->link = pack;
pktail = pack;
break;
case FTW_DNR:
fprintf (stderr, "cannot read directory %s\n", name);
return 1;
case FTW_NS:
fprintf (stderr, "cannot stat %s\n", name);
return 1;
case FTW_DP:
break;
default:
fprintf (stderr, "impossible code %d from ftw\n", type);
exit (1);
}
return 0;
}
static void
hdrsub (char *name, struct stat *buf)
{
putpath (tf, uidstr (buf->st_uid));
putc ('\t', tf);
putpath (tf, gidstr (buf->st_gid));
putc ('\t', tf);
putpath (tf, transname (name));
}
/*
* generate a unique internal name for a file
*/
static char *
iname (char *s)
{
register char *p;
register char *lastcomp;
register struct pack *pack;
char trial[MAXCOMP+1];
/* point lastcomp at the last pathname component */
lastcomp = s;
for (p = s; *p; p++) {
if (*p == '/')
lastcomp = p + 1;
}
/* if the name is acceptably short, modify it slightly */
if (strlen (lastcomp) <= MAXCOMP) {
char prefix[MAXCOMP+1], suffix[MAXCOMP+1], num[30];
register int n;
/* split the name, remove unprintables */
strcpy (prefix, lastcomp);
for (p = prefix; *p; p++)
if (*p == ' ' || !isprint (*p))
*p = '$';
suffix[0] = '\0';
p = strrchr (prefix, '.');
if (p != NULL) {
strcpy (suffix, p);
*p = '\0';
}
/* generate trial names until we run out of space */
for (n = 0, num[0] = '\0';
strlen(prefix) + strlen(suffix) + strlen(num) <= MAXCOMP;
n++, sprintf (num, "%.0d", n)) {
/* generate the trial name */
strcpy (trial, prefix);
strcat (trial, num);
strcat (trial, suffix);
/* if the name is unique, we're done */
pack = pkhead;
while (pack != NULL && strcmp (pack->iname, trial) != 0)
pack = pack->link;
if (pack == NULL)
return copy(trial);
}
}
/* punt -- generate a completely new name */
do {
static int tempno;
tempno++;
sprintf (trial, "Temp%d", tempno);
pack = pkhead;
while (pack != NULL && strcmp (trial, pack->iname) != 0)
pack = pack->link;
} while (pack != NULL);
return copy(trial);
}
at (trial, num);
strcat (trial, suffix);
/* if the name is unique, we're done */
pack = pkhead;
while (pack != NULL && strcmp (pack->iname, trial) != 0)
pack = pack->link;
if (pack == NULL)
return copy(trial);
}
}
/* punt -- generate a completely new name */
do {
static int tempno;
tempno++;
sprintf (trial, "Temp%d"pkg/path.c 644 3513 4 6137 5013610576 6000 /*
* getpath (file) - read a path name
* putpath (file, path) - write a path name
*
* These subroutines cater to the possibility of unprintable
* characters in the path names being read or written, by
* using the same sort of \ conventions commonly found in
* C character constants. The result of getpath is a pointer
* to a static buffer whose contents will stay around no longer
* than the next call to getpath. When getpath is called, the
* character about to be read from the input file must be the
* first character of the path name.
*
* There are a few problems, mostly relating to bugs and language
* changes, to watch out for in these routines. First of all,
* we assume that \v is known by the C compiler, even though
* it is not mentioned in Kernighan and Ritchie. The reason for
* this is that if we do not make this assumption, we run into
* a common bug in the handling of iscntrl(). Although the
* manuals all say that if c is a white-space character that is
* not a blank, then iscntrl(c) is true, several versions of the
* C library disagree with the documentation. Thus we try to
* list all the white-space characters explicitly.
*/
#include "asd.h"
#include <ctype.h>
#include <stdio.h>
#define CHUNK 64
static char *r;
static unsigned size;
char *
getpath (FILE *file)
{
register int c;
register int len = 0;
c = getc (file);
while (!isspace(c) && c != EOF) {
register int i = 0, n = 0;
/* determine the next input character */
if (c == '\\') {
c = getc (file);
switch (c) {
case '\\':
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'v':
c = '\v';
break;
case ' ':
/* c = ' '; */
break;
default:
while (c >= '0' && c <= '7' && i < 3) {
n = (n << 3) + c - '0';
i++;
c = getc (file);
}
ungetc (c, file);
c = n;
break;
}
}
/* ensure there's room in the buffer */
if (len >= size)
r = ralloc (r, size += CHUNK);
/* put the character in the buffer */
r[len++] = c;
/* read the next character */
c = getc (file);
}
/* unless we hit eof, we read one character too far. */
if (c != EOF)
ungetc (c, file);
/* put a final null into the buffer */
if (len >= size)
r = ralloc (r, size += CHUNK);
r[len] = '\0';
return r;
}
void
putpath (FILE *file, char *path)
{
register char *p = path;
register int c;
while ((c = *p++) != NULL) {
switch (c) {
case '\n':
fprintf (file, "\\n");
break;
case '\r':
fprintf (file, "\\r");
break;
case '\b':
fprintf (file, "\\b");
break;
case '\t':
fprintf (file, "\\t");
break;
case '\f':
fprintf (file, "\\f");
break;
case '\v':
fprintf (file, "\\v");
break;
case '\\':
fprintf (file, "\\\\");
break;
case ' ':
fprintf (file, "\\ ");
break;
default:
if (iscntrl (c))
fprintf (file,
*p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
c);
else
putc (c, file);
break;
}
}
}
e '\r':
fprintf (file, "\\r");
break;
case '\b':
fprintf (file, "\\b");
break;
case '\t':
fprintf (file, "\\t");
break;
case '\f':
fprintf (file, "\\f");
break;
case '\v':
fprintf (file, "\\v");
break;
case '\\':
fprintf (file, "\\\\");
break;
case ' ':
fprintf (file, "\\ ");
break;
default:
if (iscntrl (c))
fprintf (file,
*p >= pkg/pwd.c 644 3513 4 1113 5013610576 5623 /*
* pwd() - return the name of the current directory.
* We assume that the current directory never changes.
*/
#include "asd.h"
#include <libv.h>
#include <stdio.h>
#define CHUNK 64
char *
pwd(void)
{
register FILE *f;
static char *r;
static unsigned size;
register int n, c;
if (r == NULL) {
f = popen ("pwd", "r");
schk ((char *) f);
n = 0;
while ((c = getc (f)) != EOF) {
if (n >= size) {
size += CHUNK;
r = ralloc (r, size);
}
r[n++] = c;
}
/* replace the trailing newline by a null */
r[n - 1] = '\0';
pclose (f);
}
return r;
}
he current directory never changes.
*/
#include "asd.h"
#include <libv.h>
#include <stdio.h>
#define CHUNK 64
char *
pwd(void)
{
register FILE *f;
static char *r;
static unsigned size;
register int n, c;
if (r == NULL) {
f = popen ("pwd", "r");
schk ((char *) f);
n = 0;
while ((c = getc (f)) != EOF) {
if (n >= size) {
size += CHUNK;
r = ralloc (r, size);
}
r[n++] = c;
}
/* replace the trailipkg/seal.c 644 3513 4 5522 5013610577 5766 /*
* seal - prepare a package for shipment
*/
#include "asd.h"
#include "seal.h"
#include <ctype.h>
#include <string.h>
static long length;
static unsigned long checksum;
/*
* convert the characters in the range (in,inend] to
* storage starting at "out" -- return the number of
* output characters.
*/
static int
hrform (char *in, char *inend, char *out)
{
register char *r = out;
/* special handling for the first character(s) */
if (in < inend) {
if (*in == '.' || *in == '!' ||
(*in == 'F' && strncmp (in, "From", 4) == 0))
*r++ = '\\';
do {
register int ch = (unsigned char) *in++;
if (isprint (ch)) {
if (ch == '\\')
*r++ = '\\';
*r++ = ch;
} else {
if (ch == ' ' || ch == '\t' || ch == '\n')
*r++ = ch;
else {
*r++ = '\\';
*r++ = hextab[(ch >> 4) & 0xf];
*r++ = hextab[ch & 0xf];
}
}
} while (in < inend);
if (r[-1] != '\n') {
*r++ = '\\';
*r++ = '\n';
}
}
return r - out;
}
static int
mrform (char *in, char *inend, char *out)
{
char *r;
register int len;
len = inend - in;
if (len <= 0)
return 0;
r = out;
*r++ = '.';
do {
register unsigned long bits;
register int n, outn;
register char *rr;
n = len;
if (n > INCPW)
n = INCPW;
len -= n;
outn = n + OUTCPW - INCPW;
bits = 0;
do bits = (bits << BPC) | (*in++ & BYTEMASK);
while (--n > 0);
rr = r = r + outn;
do {
*--rr = RADBASE + bits % RADIX;
bits /= RADIX;
} while (--outn > 0);
} while (len > 0);
*r++ = '\n';
return r - out;
}
static int
seal (FILE *f, char *fname)
{
static int first = 1;
if (first) {
first = 0;
printf ("!<seal>\n");
}
do {
char line[MAXLINE];
char outline[MAXLINE*3+10];
register char *p, *endl;
register int ch;
/* read a line, possibly short */
p = line;
endl = line + MAXLINE;
do {
ch = getc (f);
if (ch == EOF)
break;
*p++ = ch;
} while (ch != '\n' && p < endl);
endl = p;
/*
* endl now points one past the last char
* in the line we have read. If we didn't
* get any characters at all, we're done.
*/
if (line == endl)
return ferror (f) != 0;
length += endl - line;
/* now convert the line to external form */
ch = hrform (line, endl, outline);
if (ch > (endl - line + INCPW - 1) / INCPW * OUTCPW + 2)
ch = mrform (line, endl, outline);
p = outline;
/* if encrypting, do so */
if (kflag || Kflag)
mangle (line, endl);
/* accumulate the (possibly encrypted) checksum */
checksum = mkcsum (checksum, line, endl);
while (--ch >= 0)
putchar (*p++);
} while (!feof (f) && !ferror (f));
return ferror (f) != 0;
}
int
main (int argc, char **argv)
{
int rc = 0;
static char stdbuf[BUFSIZ];
setbuf (stdout, stdbuf);
length = 0;
checksum = 0;
rc = getargs (argc, argv, "kK:", seal);
printf ("!end %ld %lu\n", length, checksum);
return rc;
}
if encrypting, do so */
if (kflag || Kflag)
mangle (line, endl);
/* accumulate the (possibly encrypted) checksum */
checksum = mkcsum (checksum, line, endl);
whpkg/seal.h 644 3513 4 515 5013610577 5750 /*
* common parameters for seal and unseal
*/
#define MAXLINE 100
#define MAXULINE 200
#define RADBASE (' ' + 1)
#define INCPW 4
#define OUTCPW 5
#define RADIX 94
#define BPC 8
#define BYTEMASK 0xff
#define CSMASK 0xffffffffL
#define CSHIBIT 0x80000000L
/* mkcsum.c */
extern unsigned long mkcsum(unsigned long, char *, char *);
c;
}
if encrypting, do so */
if (kflag || Kflag)
mangle (line, endl);
/* accumulate the (possibly encrypted) checksum */
checksum = mkcsum (checksum, line, endl);
whpkg/transname.c 644 3513 4 1701 5013610600 7010 /*
* transname - take a pathname, expand it, and translate it
* according to the replist. The result is in a buffer
* that will stay around no longer than the next call
* to transname. If no translation, transname will
* just return fullname applied to its argument.
*/
#include "asd.h"
#include <string.h>
char *
transname (char *s)
{
register struct replist *rl;
static char *res;
static int size;
register int n;
s = fullname (s);
/* look for substitution */
for (rl = replist; rl; rl = rl->link) {
register char *p = rl->source;
register char *q = s;
/* comparison loop */
while (*p != '\0' && *p == *q) {
p++;
q++;
}
if (*p == '\0') {
/* comparison successful */
n = strlen (rl->dest) + strlen (q) + 1;
if (n > size) {
size = n;
res = ralloc (res, (unsigned) n);
}
strcpy (res, rl->dest);
strcat (res, q);
return res;
}
}
/* search loop failed, return expanded input */
return s;
}
/* look for substitution */
for (rl = replist; rl; rl = rl-pkg/uid.c 644 3513 4 2535 5013610601 5610 #include "asd.h"
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define CHUNK 16
static struct utab {
unsigned uid;
char *name;
} *utab;
static int size, salloc;
char *
uidstr (uid_t uid)
{
register int i;
static char buf[12];
struct passwd *p;
/* search the cache for the uid */
for (i = 0; i < size; i++)
if (utab[i].uid == uid)
return utab[i].name;
/* try to find it in the system's database */
if (p = getpwuid(uid)) {
if (size % CHUNK == 0)
utab = (struct utab *) ralloc((char *) utab, salloc += CHUNK);
utab[size].uid = p->pw_uid;
utab[size].name = copy(p->pw_name);
++size;
return utab[size - 1].name;
}
/* failure, invent a string */
sprintf (buf, "#%u", uid);
return buf;
}
uid_t
uidnum (char *name)
{
register int i;
struct passwd *p;
/* if it starts with a #, use the number */
if (name[0] == '#')
return atoi (name + 1);
/* try to find it in the system's database */
if (p = getpwnam(name)) {
if (size % CHUNK == 0)
utab = (struct utab *) ralloc((char *) utab, salloc += CHUNK);
utab[size].uid = p->pw_uid;
utab[size].name = copy(p->pw_name);
++size;
return utab[size - 1].uid;
}
/* search the cache */
for (i = 0; i < size; i++)
if (strcmp (utab[i].name, name) == 0)
return utab[i].uid;
/* failure, invent a value */
return getuid();
}
#, use the number */
if (name[0] == '#')
return atoi (name + 1);
/* try to find it in the system's database */
if (p = getpwnam(name)) {
if (size % CHUNK =pkg/unseal.c 644 3513 4 10352 5013610601 6332 /*
* unseal - disclose and verify the contents of a sealed package
*/
#include "asd.h"
#include "seal.h"
#include <ctype.h>
#include <string.h>
#include <unistd.h>
static FILE *outfd;
static char outf[L_tmpnam];
static char line[MAXULINE];
static unsigned long length, checksum;
static char *
csverify (char *p, unsigned long value)
{
register unsigned long n = 0;
while (isspace (*p))
p++;
if (!isdigit (*p))
return NULL;
do n = n * 10 + *p++ - '0';
while (isdigit (*p));
if (!isspace (*p))
return NULL;
if (n != value)
return NULL;
return p;
}
int
docksum (char *line, unsigned long length, unsigned long checksum)
{
register char *p = line + 5;
p = csverify (p, length);
if (p == NULL) {
fprintf (stderr, "unseal: invalid length\n");
return 1;
}
p = csverify (p, checksum);
if (p == NULL) {
fprintf (stderr, "unseal: invalid checksum\n");
return 1;
}
return 0;
}
static int
unhex (int c)
{
register char *p;
p = hextab;
while (*p && *p != c)
p++;
if (*p)
return p - hextab;
return -1;
}
static int
hrout (char *line, char *out)
{
register char *p, *q;
register int ch;
p = line;
q = out;
while ((ch = *p++) != '\0') {
if (ch != '\\') {
*q++ = ch;
} else {
ch = *p++;
if (ch == '\\' || ch == '.' || ch == '!' || ch == 'F') {
*q++ = ch;
} else if (ch != '\n') {
register int n = 0, i = 1;
do {
ch = unhex (ch);
if (ch < 0) {
fprintf (stderr,
"bad hex char: %s", line);
return -1;
}
n = (n << 4) | ch;
if (i)
ch = *p++;
} while (--i >= 0);
*q++ = n;
}
}
}
return q - out;
}
static int
mrout (char *line, char *out)
{
register char *p = line + 1;
char *q = out;
while (*p != '\n' && *p != '\0') {
register unsigned long bits;
register int n;
register char *rq;
n = 0;
bits = 0;
do {
register int ch = *p++;
if (ch == '\n')
break;
if (ch < RADBASE || ch >= RADBASE + RADIX) {
fprintf (stderr, "bad input char: %s", line);
return -1;
}
bits = bits * RADIX + ch - RADBASE;
n++;
} while (n < OUTCPW);
n -= OUTCPW - INCPW;
if (n <= 0) {
fprintf (stderr, "non-positive output count: %s", line);
return -1;
}
rq = q = q + n;
do {
*--rq = bits;
bits >>= BPC;
} while (--n > 0);
}
return q - out;
}
static int
dodata (char *line)
{
char out[MAXULINE * 2];
int r;
r = (*(line[0] == '.'? mrout : hrout)) (line, out);
if (r < 0)
return 1;
fwrite (out, sizeof(*out), r, outfd);
length += r;
if (kflag || Kflag)
mangle (out, out + r);
checksum = mkcsum (checksum, out, out + r);
return 0;
}
static int
unseal (FILE *f, char *fname)
{
int intext = 0, rc = 0, nulltext = 1;
length = checksum = 0;
resetN12();
while (fgets (line, MAXULINE, f) != NULL) {
/* check for a control line, which starts with "!" */
if (line[0] == '!') {
if (intext) {
/* only thing allowed is !end */
if (strncmp (line + 1, "end ", 4) == 0) {
rc += docksum (line, length, checksum);
intext = 0;
} else {
fprintf (stderr, "invalid control line %s", line);
rc++;
}
} else {
/* look for header, else quietly ignore */
if (strcmp (line + 1, "<seal>\n") == 0) {
intext++;
nulltext = 0;
}
}
/* data lines are quietly ignored if not in text */
} else if (intext) {
rc += dodata (line);
}
}
if (nulltext) {
fprintf (stderr, "unseal: no contents\n");
rc++;
}
if (intext) {
fprintf (stderr, "unseal: no checksum\n");
rc++;
}
return rc;
}
int
main (int argc, char **argv)
{
int rc = 0;
int c;
static char stdbuf[BUFSIZ];
setbuf (stdout, stdbuf);
umask (077);
tmpnam (outf);
outfd = fopen (outf, "w+");
if (outfd == NULL) {
fprintf (stderr, "unseal: can't create temp file\n");
exit (1);
}
/* don't leave dregs */
unlink (outf);
rc = getargs (argc, argv, "kK:", unseal);
/* if successful, copy the temp file to the output */
if (rc == 0) {
register FILE *in = outfd, *out = stdout;
register int ch;
rewind (in);
while ((ch = getc (in)) != EOF)
putc (ch, out);
fflush (out);
if (ferror (in)) {
fprintf (stderr, "unseal: error reading temp file\n");
rc++;
}
if (ferror (out)) {
fprintf (stderr, "unseal: error writing stdout\n");
rc++;
}
}
return rc;
}
* don't leave dregs */
unlink (outf);
rc = getargs (argc, argv, "kK:", unseal);
/* if successful, copy the temp file to the output */
if (rc == 0) {
register FILE *in = outfd, *out = stdout;
register int ch;
rewind (in);
while ((ch = getc (in)) != EOF)
putc pkg/libx.a 644 3513 4 44 5013644572 5732 674187646 f (ferror (in)) {
fprintf (stderr, "unseal: error reading temp file\n");
rc++;
}
if (ferror (out)) {
fprintf (stderr, "unseal: error writing stdout\n");
rc++;
}
}
return rc;
}
* don't leave dregs */
unlink (outf);
rc = getargs (argc, argv, "kK:", unseal);
/* if successful, copy the temp file to the output */
if (rc == 0) {
register FILE *in = outfd, *out = stdout;
register int ch;
rewind (in);
while ((ch = getc (in)) != EOF)
putc pkg/INSTRUCTIONS 644 3513 4 2022 5027730714 6556 The format of the Instructions file in a package archive
is as follows.
Each instruction is a line by itself. Each instruction consists of a
single character instruction code followed by a number of fields. Fields
do not contain spaces.
Various standard kinds of fields are:
<command> shell command (via putpath())
<component> archive component name (via putpath())
<mode> octal permissions
<dmajor>, <dminor> major and minor decimal device numbers
<uid>, <gid> user and group names (via putpath())
<path> path name (via putpath())
b <mode> <dmajor> <dminor> <uid> <gid> <path>
block device
c <mode> <dmajor> <dminor> <uid> <gid> <path>
character device
d <mode> <uid> <gid> <path>
directory
f <component> <uid> <gid> <path>
ordinary file (rest of the information is in the archive component)
l <path1> <path2>
hard link -- <path2> is the link to be created.
s <path1> <path2>
symbolic link -- <path2> is the link to be created.
r <path>
file to be removed
x <command>
command to be executed
X <path>
file to be executed
a putpath())
b <mode> <dmajor> <dminor> <uid> <gid> <path>
block device
c <mode> <dmajor> <dminor> <uid> <gid> <path>
character device
d <mode> <uid> <gid> <path>
directory
f <component> <uid> <gid> <path>
ordinary file (rest of the information is in the archive component)
l <path1> <path2>
hard link -- <path2> is the link to be created.
s <path1> <path2>
symbolic link -- <path2> is the link to be created.
r <path>
file to be removed
x <command>
command to be executed
X <path>
fipkg/ftw.c 644 3513 4 24531 5031452625 5661 /*
* ftw - file tree walk
*
* int ftw (path, fn, depth) char *path; int (*fn)(); int depth;
*
* Given a path name, ftw starts from the file given by that path
* name and visits each file and directory in the tree beneath
* that file. If a single file has multiple links within the
* structure, it will be visited once for each such link.
* For each object visited, fn is called with four arguments.
* The fourth can often be ignored; it is a pointer, say S,
* declared "struct FTW *S", discussed in more detail below.
* The first contains the path name of the object, the second
* contains a pointer to a stat buffer which will usually hold
* appropriate information for the object and the third contains
* an integer value giving additional information about the
* object, as follows:
*
* FTW_F The object is a file for which stat was
* successful. It does not guarantee that the
* file can actually be read.
*
* FTW_D The object is a directory for which stat and
* open for read were both successful. This is
* a preorder visit -- objects in the directory
* are yet to be visited.
*
* FTW_DNR The object is a directory for which stat
* succeeded, but which cannot be read. Because
* the directory cannot be read, fn will not be
* called for any descendants of this directory.
*
* FTW_DP The object is a directory for which stat and
* open for read were both successful. This is
* a postorder visit -- everything in the directory
* has already been visited.
*
* FTW_NS Lstat failed on the object. If errno is EACCES,
* then the failure stems from lack of
* appropriate permission. This indication will
* be given, for example, for each file in a directory
* with read but no execute permission. Whenever
* stat fails, it is not possible to determine
* whether this object is a file or a directory.
* The stat buffer passed to fn will contain garbage.
*
* FTW_SL The object is a symbolic link. Set S->quit
* (a component of the structure pointed to by
* the fourth parameter to fn) to FTW_FOLLOW to
* have the link followed and the object to which
* it points visited.
*
* FTW_NSL Lstat succeeded, but stat failed on the object.
* This is only possible when following a symbolic
* link.
*
* Among the components of the structure to which the fourth
* parameter, S, to fn points is S->quit. If the caller sets
* S->quit to FTW_SKR, then no more files in the current directory
* will be visited. (The current directory is the one containing
* the object being visited.) If the third parameter to fn is
* FTW_D and the caller sets S->quit to FTW_SKD, then this directory
* (the one named in the first parameter to fn) will be skipped.
*
* Other components pointed to by the fourth parameter S are
* the current recursion level S->level (top level = 0) and
* the offset S->base in the pathname of the current object
* (the first parameter to fn) of the object's base name.
* By expanding the definition of struct FTW given below and
* including the files included below, one can arrange for
* S to point to a larger structure, components of which can
* be initialized (for example) on calls to fn with third
* parameter FTW_D.
*
* If fn returns nonzero, ftw stops and returns the same value
* to its caller. Ftw only initiates a nonzero return if malloc
* fails; in this case ftw sets errno to ENOMEM and returns -1.
*
* The third argument to ftw does not limit the depth to which
* ftw will go. Rather, it limits the depth to which ftw will
* go before it starts recycling file descriptors. In general,
* it is necessary to use a file descriptor for each level of the
* tree, but they can be recycled for deep trees by saving the position,
* closing, re-opening, and seeking. It is possible to start
* recycling file descriptors by sensing when we have run out, but
* in general this will not be terribly useful if fn expects to be
* able to open files. We could also figure out how many file descriptors
* are available and guarantee a certain number to fn, but we would not
* know how many to guarantee, and we do not want to impose the extra
* overhead on a caller who knows how many are available without
* having to figure it out.
*
* It is possible for ftw to die with a memory fault in the event
* of a file system so deeply nested that the stack overflows.
*/
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include "ftw.h"
/*
* Struct FTW (whose definition starts at the end of ftw.h) must
* must include at least the integers quit, base, and level.
*/
#define FTW_PATHLEN0 1000
#define FTW_PATHINC 1000
#ifndef S_ISLNK
#define lstat stat
#endif
#ifndef ENOMEM
#include <errno.h>
#endif
extern int errno;
/*
* Each generation of ftw1 (the real ftw) allocates one copy, R, of the
* following structure; it passes a pointer to this structure when it
* recursively invokes itself. These structures are chained together,
* so that if it becomes necessary to recycle file descriptors, then
* the oldest descriptor (the one at the shallowest depth still open)
* can be recycled.
*/
struct FTW_rec {
struct FTW_rec *prev;
long here; /* seek to here when reopening at this level */
DIR *fd; /* file descriptor at this level */
};
/*
* One instance, T, of the following structure is allocated by ftw; a
* pointer to it is passed to all generations of ftw1 (the real ftw).
* T could often be a global variable, but this way the parameter fn
* can invoke ftw for an independent tree walk.
* Component T->path points to storage for the object path-names;
* this storage may be relocated by realloc if T->path needs to be
* more than T->pathlast characters long.
* T->path[T->pathnext] is the next free character in the pathnames.
* T->depth = parameter depth to ftw. T->lastout is the deepest level at
* which a file descriptor has been recycled.
*/
struct FTW_top {
int (*fn)();
char *path;
unsigned pathlast, pathnext;
int lastout;
int depth;
};
static ftw_1_();
int
ftw (path, fn, depth)
char *path;
int (*fn)();
int depth;
{
struct FTW_top T;
struct FTW_rec R;
struct FTW S;
int rc;
char *malloc(), *strcpy();
T.depth = depth;
T.lastout = -1;
T.fn = fn;
S.quit = 0;
S.level = -1;
/* initialize S.base, T.pathnext... */
{
register char c, *p, *q;
for (p = q = path; c = *p; p++) if (c == '/') q = p + 1;
S.base = q - path;
T.pathnext = p - path;
}
T.pathlast = T.pathnext + FTW_PATHLEN0;
T.path = malloc(T.pathlast);
if (!T.path) { errno = ENOMEM; return -1; }
strcpy(T.path, path);
rc = ftw_1_(&R, &T, 0, &S);
free(T.path);
return rc;
}
int
static
ftw_1_ (R, T, level, S1)
register struct FTW_rec *R;
register struct FTW_top *T;
int level;
struct FTW *S1;
{
int rc, n;
DIR *fd;
struct dirent *dirp;
char *component, *path;
struct stat sb;
struct FTW_rec mr;
unsigned nextsave;
struct FTW S;
char *realloc();
long lseek();
mr.prev = R;
path = T->path;
S.level = level;
S.quit = 0;
S.base = S1->base;
/* Try to get file status. If unsuccessful, errno will say why. */
if (lstat(path, &sb) < 0) {
rc = (*T->fn) (path, &sb, FTW_NS, &S);
S1->quit = S.quit;
return rc;
};
/*
* The stat succeeded, so we know the object exists.
* If not a directory, call the user function and return.
*/
#ifdef S_ISLNK
if (S_ISLNK(sb.st_mode )) {
rc = (*T->fn) (path, &sb, FTW_SL, &S);
S1->quit = S.quit;
if (rc || S.quit == FTW_SKR) return rc;
if (S.quit != FTW_FOLLOW) return 0;
S1->quit = S.quit = 0;
if (stat(path, &sb) < 0) {
rc = (*T->fn) (path, &sb, FTW_NSL, &S);
S1->quit = S.quit;
return rc;
};
}
#endif
if (!S_ISDIR(sb.st_mode)) {
rc = (*T->fn) (path, &sb, FTW_F, &S);
S1->quit = S.quit;
return rc;
}
/*
* The object was a directory.
*
* Open a file to read the directory
*/
mr.fd = fd = opendir(path);
/*
* Call the user function, telling it whether
* the directory can be read. If it can't be read
* call the user function or indicate an error,
* depending on the reason it couldn't be read.
*/
if (!fd) {
rc = (*T->fn) (path, &sb, FTW_DNR, &S);
S1->quit = S.quit;
return rc;
}
/* We could read the directory. Call user function. */
rc = (*T->fn) (path, &sb, FTW_D, &S);
if (rc != 0)
goto rtrn;
if (S.quit == FTW_SKD) goto rtrn;
if (S.quit == FTW_SKR) {S1->quit = FTW_SKR; goto rtrn;}
/* Make sure path is big enough to hold generated pathnames. */
n = nextsave = T->pathnext;
if (n + MAXNAMLEN + 1 >= T->pathlast) {
T->pathlast += FTW_PATHINC;
path = T->path = realloc(T->path, T->pathlast);
if (!path) {
errno = ENOMEM;
rc = -1;
goto rtrn;
}
}
/* Create a prefix to which we will append component names */
if (n > 0 && path[n-1] != '/') path[n++] = '/';
component = path + n;
/*
* Read the directory one component at a time.
* We must ignore "." and "..", but other than that,
* just create a path name and call self to check it out.
*/
while (dirp = readdir(fd)) {
if (dirp->d_ino != 0
&& strcmp (dirp->d_name, ".") != 0
&& strcmp (dirp->d_name, "..") != 0) {
int i;
struct FTW_rec *pr;
/* Append the component name to the working path */
strcpy(component, dirp->d_name);
T->pathnext = n + strlen(dirp->d_name);
/*
* If we are about to exceed our depth,
* remember where we are and close the file.
*/
if (level - T->lastout >= T->depth) {
pr = &mr;
i = T->lastout++;
while (++i < level) pr = pr->prev;
pr->here = telldir(pr->fd);
closedir(pr->fd);
}
/*
* Do a recursive call to process the file.
*/
S.quit = 0;
S.base = n;
rc = ftw_1_(&mr, T, level+1, &S);
if (rc != 0 || S.quit == FTW_SKR) {
if (level > T->lastout) closedir(fd);
T->pathnext = nextsave;
return rc;
}
/*
* If we closed the file, try to reopen it.
*/
if (level <= T->lastout) {
char c = path[nextsave];
path[nextsave] = 0;
T->lastout = level - 1;
mr.fd = fd = opendir(path);
if (!fd) {
rc = (*T->fn) (path, &sb, FTW_DNR, &S);
S1->quit = S.quit;
T->pathnext = nextsave;
return rc;
}
path[nextsave] = c;
seekdir(fd, mr.here);
}
}
}
T->pathnext = nextsave;
path[nextsave] = 0;
/*
* We got out of the subdirectory loop. Call the user
* function again at the end and clean up.
*/
rc = (*T->fn) (path, &sb, FTW_DP, &S);
S1->quit = S.quit;
rtrn:
closedir(fd);
return rc;
}
T->lastout = level - 1;
mr.fd = fd = opendir(path);
if (!fd) {
rc = (*T->fn) (path, &sb, FTW_DNR, &S);
S1->quit = S.quit;
T->pathnext = nextspkg/ftw.h 644 3513 4 1462 5031452570 5643 #ifndef __FTW
#define __FTW
/*
* Codes for the third argument to the user-supplied function
* which is passed as the second argument to ftw...
*/
#define FTW_F 0 /* file */
#define FTW_D 1 /* directory */
#define FTW_DNR 2 /* directory without read permission */
#define FTW_NS 3 /* unknown type, stat failed */
#define FTW_DP 4 /* directory, postorder visit */
#define FTW_SL 5 /* symbolic link */
#define FTW_NSL 6 /* stat failed (errno = ENOENT) on symbolic link */
/* Values the user-supplied function may wish to assign to
component quit of struct FTW...
*/
#define FTW_SKD 1 /* skip this directory (2nd par = FTW_D) */
#define FTW_SKR 2 /* skip rest of current directory */
#define FTW_FOLLOW 3 /* follow symbolic link */
struct FTW { int quit, base, level;
#ifndef FTW_more_to_come
};
#endif
#endif
failed */
#define FTW_DP 4 /* directory, postorder visit */
#define FTW_SL 5 /* symbolic link */
#define FTW_NSL 6 /* stat failed (errno = ENOENT) on symbolic link */
/* Values the user-supplied function plan9/ 755 3513 4 0 5031450043 5035 sysv/ 755 3513 4 0 5031457617 5034 sysv/lcreat.c 644 3513 4 172 5031151403 6477 #include <fcntl.h>
int
lcreat(char *name, int mode)
{
return open(name, O_EXCL | O_CREAT | O_TRUNC | O_WRONLY, mode);
}
#define �<