Deliver 1.00 (patchlevel 8) Part 3/3
Chip Salzenberg
chip at ateng.ateng.com
Fri Feb 24 02:48:11 AEST 1989
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: main.c mbox.c procs.c subs.c sysdep.c uucp.c
# Wrapped by network at ateng on Wed Feb 15 20:36:33 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(13643 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/* $Header: main.c,v 1.11 89/02/15 19:11:25 network Exp $
X *
X * A program to deliver local mail with some flexibility.
X *
X * $Log: main.c,v $
X * Revision 1.11 89/02/15 19:11:25 network
X * Provide second system-wide delivery file, executed after user delivery
X * files but before any deliveries take place. Useful for implementing
X * system-wide aliases.
X * Also, fix bug in do_dfile() that caused infinite loops if delivery files
X * output any lines containing white space. (!)
X *
X * Revision 1.10 89/02/10 15:46:59 network
X * V7 support.
X *
X * Revision 1.9 88/11/28 18:08:03 network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X *
X * Revision 1.8 88/11/26 13:20:51 network
X * patch4: Add return type of signal handlers to config.h.
X * patch4: Provide a version of getopt() for systems that lack it.
X * patch4: Call va_end() in routines that use varargs.
X * patch4: Make consistent checks for valid address strings.
X *
X * Revision 1.7 88/11/18 12:17:17 network
X * patch2: Improved signal handling.
X * patch2: Make sure all environment variables are always provided.
X * patch2: Some users can be trusted to specify delivery files.
X *
X * Revision 1.6 88/10/13 12:19:27 network
X * patch1: add "-n" option, and general bug fixes.
X *
X * Revision 1.5 88/09/14 20:00:03 network
X * Add version string, including patchlevel.
X *
X * Revision 1.4 88/09/14 19:41:54 network
X * Portability to System V and BSD.
X * General fixup.
X *
X * Revision 1.3 88/08/30 16:13:54 network
X * Remove general subroutines to new module, subs.c.
X *
X * Revision 1.2 88/08/25 15:29:59 network
X * Implement -s and -u options and ENV_SYSDEL and ENV_USERDEL environment
X * variables. Tighten up control over effective and real uid/gid.
X * In particular, renounce setuid privileges if the system or user delivery
X * file is specified.
X *
X * Revision 1.1 88/06/06 09:38:54 chip
X * Initial revision
X *
X */
X
X#include "deliver.h"
X#include "patchlevel.h"
X#include <signal.h>
X
X/*
X * External data.
X */
X
X/* Variables set by getopt() [blech] */
X
Xextern int optind, opterr;
Xextern char *optarg;
X
X/*
X * Local data
X */
X
Xstatic char sys_dfl[] = SYS_DELIVER;
Xstatic char post_dfl[] = POST_DELIVER;
Xstatic char user_dfl[] = USER_DELIVER;
X
X/*
X * Global data
X */
X
Xint verbose = FALSE;
Xint dryrun = FALSE;
Xint rundfiles = TRUE;
Xint printaddrs = FALSE;
Xint leavetemps = FALSE;
Xint boxdelivery = FALSE;
X
Xchar *progname = "deliver";
Xchar version[32] = "1.0";
Xchar *shell = SHELL;
X
Xchar *sys_deliver = sys_dfl;
Xchar *post_deliver = post_dfl;
Xchar *user_deliver = user_dfl;
Xchar *sender = NULL;
Xchar *hostname = NULL;
X
Xint eff_uid = -1;
Xint eff_gid = -1;
Xint real_uid = -1;
Xint real_gid = -1;
X
XCONTEXT *eff_ct = NULL;
XCONTEXT *real_ct = NULL;
X
Xint tty_input = FALSE;
XSIGFLAG got_sig = FALSE;
X
Xint trust_user = FALSE;
Xint trust_delfiles = FALSE;
X
Xchar *ttype[T_MAX] = { "header", "body", "header copy", "body copy" };
Xchar *tfile[T_MAX] = { NULL, NULL, NULL, NULL };
Xchar *tenv[T_MAX] = { NULL, NULL, ENV_HEADER, ENV_BODY };
Xint tfd[T_MAX] = { -1, -1, -1, -1 };
X
X/*
X * Local functions.
X */
X
Xstatic SIGTYPE sighup(), sigint(), sigquit();
X
X/*----------------------------------------------------------------------
X * The Program.
X */
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X char *p;
X int u, c, errcount, copy;
X
X /* Make sure that stdout and stderr are interleaved correctly */
X
X Linebuf(stdout);
X Linebuf(stderr);
X
X /* Figure out the name used to invoke this program. */
X
X progname = basename(argv[0]);
X
X /* What version of the program is this? */
X
X (void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
X
X /* Figure out the name of this host */
X
X if ((hostname = gethost()) == NULL)
X {
X hostname = "unknown";
X error("unable to determine host name; using \"%s\"\n",
X hostname);
X }
X
X /* Find effective and real uids and gids. */
X
X eff_uid = geteuid();
X eff_gid = getegid();
X real_uid = getuid();
X real_gid = getgid();
X
X if (eff_uid != real_uid && eff_uid != 0)
X {
X error("if setuid, must be setuid root\n");
X leave(1);
X }
X
X /* Process environment: handle recursive invocation */
X
X if ((p = getenv(ENV_DFLAGS)) != NULL)
X {
X while (*p)
X {
X switch (*p++)
X {
X case 'v':
X verbose = TRUE;
X break;
X case 'd':
X verbose = TRUE;
X dryrun = TRUE;
X break;
X case 'A':
X printaddrs = TRUE;
X dryrun = TRUE;
X break;
X case 'n':
X rundfiles = FALSE;
X break;
X case 't':
X leavetemps = TRUE;
X break;
X }
X }
X }
X
X if ((p = getenv(ENV_SYSDEL)) != NULL && *p)
X sys_deliver = p;
X if ((p = getenv(ENV_POSTDEL)) != NULL && *p)
X post_deliver = p;
X if ((p = getenv(ENV_USERDEL)) != NULL && *p)
X user_deliver = p;
X if ((p = getenv(ENV_SENDER)) != NULL && *p)
X sender = p;
X if ((p = getenv(ENV_HOSTNAME)) != NULL && *p)
X hostname = p;
X
X /* Parse command line arguments */
X
X while ((c = getopt(argc, argv, "vdAntbs:p:u:r:h:")) != EOF)
X {
X switch (c)
X {
X case 'v':
X verbose = TRUE;
X break;
X case 'd':
X verbose = TRUE;
X dryrun = TRUE;
X break;
X case 'A':
X printaddrs = TRUE;
X dryrun = TRUE;
X break;
X case 'n':
X rundfiles = FALSE;
X break;
X case 't':
X leavetemps = TRUE;
X break;
X case 'b':
X boxdelivery = TRUE;
X break;
X case 's':
X if (*optarg)
X sys_deliver = optarg;
X break;
X case 'p':
X if (*optarg)
X post_deliver = optarg;
X break;
X case 'u':
X if (*optarg)
X user_deliver = optarg;
X break;
X case 'r':
X if (*optarg)
X sender = optarg;
X break;
X case 'h':
X if (*optarg)
X hostname = optarg;
X break;
X case '?':
X usage();
X }
X }
X
X /* If no destinations were given, forget it. */
X
X if (optind >= argc)
X {
X error("no recipients specified\n");
X usage();
X }
X
X /* Print a debugging message */
X
X if (verbose)
X {
X message("%s %s running on host %s\n",
X progname, version, hostname);
X }
X
X /* Do we trust our caller? */
X
X if (trusted_uid(real_uid))
X trust_user = TRUE;
X
X /* Do we trust our delivery files? */
X
X if (strcmp(sys_dfl, sys_deliver) == 0
X && strcmp(post_dfl, post_deliver) == 0
X && strcmp(user_dfl, user_deliver) == 0)
X trust_delfiles = TRUE;
X
X /* Renounce special privileges if something insecure was requested. */
X
X if (!trust_user && !trust_delfiles)
X {
X if (setgid(eff_gid = real_gid) == -1
X || setuid(eff_uid = real_uid) == -1)
X {
X syserr("can't renounce setuid privileges");
X leave(1);
X }
X }
X
X /* Get the contexts of our effective and real uids. */
X
X if ((eff_ct = uid_context(eff_uid)) == NULL)
X error("invalid effective uid %d!?\n", eff_uid);
X
X if ((real_ct = uid_context(real_uid)) == NULL)
X error("invalid real uid %d!?\n", real_uid);
X
X if (!eff_ct || !real_ct)
X leave(1);
X
X if (verbose)
X {
X message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
X eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid,
X real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid);
X }
X
X /* Let's be sane about the file creation mask. */
X
X u = umask(0);
X u &= ~0700; /* Let's not deprive ourselves of permissions. */
X u |= 022; /* Let's be reasonably paranoid about writing. */
X (void) umask(u);
X
X /*
X * Where is the message coming from?
X */
X
X if (isatty(0))
X tty_input = TRUE;
X
X /*
X * If we are not going to deliver, or if we are receiving the
X * message from a tty, catch signals so we can remove temp files.
X * Otherwise, ignore signals.
X */
X
X if (dryrun || tty_input)
X catch_sigs();
X else
X ignore_sigs();
X
X /*
X * Create the temporary files and write the message to them.
X */
X
X copy = copy_message();
X
X /*
X * No more signals...
X */
X
X ignore_sigs();
X
X /*
X * ... but if we had already caught a signal,
X * or if copy_msg() had a problem, leave.
X */
X
X if ((copy < 0) || got_sig)
X {
X if (got_sig)
X error("caught signal - exiting\n");
X leave(1);
X }
X
X /*
X * Set up useful environment variables.
X * Note that this must be done _after_ copy_message(),
X * since that's where the temp files are created.
X */
X
X setup_environ();
X
X /*
X * Perhaps we should consider all arguments as mailbox names...
X */
X
X if (boxdelivery)
X {
X int a;
X
X if (verbose)
X message("mailbox delivery as %s\n", real_ct->ct_name);
X
X /*
X * Consider all arguments as mailbox filenames.
X */
X
X for (a = optind; a < argc; ++a)
X (void) dest(real_ct->ct_name, argv[a]);
X
X if (verbose)
X dumpdests("(should all be mailboxes)");
X }
X
X /*
X * They're not mailbox names, so they should be mail addresses.
X */
X
X else
X {
X /* Run all destinations though the system delivery file. */
X
X if (sys_dfile(argc - optind, argv + optind) >= 0)
X {
X if (verbose)
X dumpdests("after running system delivery file");
X }
X else
X {
X int a;
X
X /*
X * System delivery file is missing or ignored.
X * Use the argument list verbatim.
X */
X
X for (a = optind; a < argc; ++a)
X (void) dest(argv[a], (char *) NULL);
X
X if (verbose)
X dumpdests("as taken from argument list");
X }
X
X /*
X * Run each user destination through his delivery file.
X */
X
X if (user_dfiles() >= 0)
X {
X if (verbose)
X dumpdests("after running user delivery files");
X }
X
X /*
X * Run each remaining destination though the post-user
X * delivery file.
X */
X
X if (post_dfile() >= 0)
X {
X if (verbose)
X dumpdests("after running post-user delivery file");
X }
X }
X
X /*
X * Drop mail in mailbox(es).
X */
X
X mbox_deliver();
X
X if (verbose)
X dumpdests("after delivery to all mailboxes");
X
X /*
X * Send mail to UUCP address(es).
X */
X
X uucp_deliver();
X
X if (verbose)
X dumpdests("after delivery to UUCP addresses");
X
X /*
X * Report any errors, and leave.
X */
X
X errcount = report_errors();
X
X /*
X * All done.
X */
X
X leave(errcount ? 1 : 0);
X /* NOTREACHED */
X}
X
X/*----------------------------------------------------------------------
X * Print a usage message and exit.
X */
X
Xusage()
X{
X message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname);
X message("-b All arguments are mailbox filenames.\n");
X message(" (Default: arguments are user names.)\n");
X message("-A Resolve addresses but do not deliver.\n");
X message("-d Be verbose but do not deliver.\n");
X message("-v Be verbose and deliver.\n");
X message("-n Do not run any delivery files.\n");
X message("-t Do not remote temp files before exiting.\n");
X message("-s file Specify the system delivery filename.\n");
X message("-p file Specify the post-user delivery filename.\n");
X message("-u file Specify the user delivery filename.\n");
X message("-r from Specify the address to appear in the \"From \" line.\n");
X message("-h host Specify the host name.\n");
X message("args Either user addresses or mailboxes (-b).\n");
X leave(1);
X}
X
X/*----------------------------------------------------------------------
X * Clean up and exit.
X */
X
Xleave(code)
Xint code;
X{
X if (! leavetemps)
X {
X int t;
X
X for (t = 0; t < T_MAX; ++t)
X {
X if (tfd[t] != -1)
X (void) close(tfd[t]);
X if (tfile[t] && unlink(tfile[t]) == -1)
X syserr("can't unlink %s", tfile[t]);
X }
X }
X
X exit(code);
X}
X
X/*----------------------------------------------------------------------
X * Catch signals.
X */
X
Xcatch_sigs()
X{
X if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X (void) signal(SIGHUP, sighup);
X if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X (void) signal(SIGINT, sigint);
X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X (void) signal(SIGQUIT, sigquit);
X}
X
X/*----------------------------------------------------------------------
X * Ignore signals.
X */
X
Xignore_sigs()
X{
X (void) signal(SIGHUP, SIG_IGN);
X (void) signal(SIGINT, SIG_IGN);
X (void) signal(SIGQUIT, SIG_IGN);
X}
X
Xstatic SIGTYPE
Xsighup()
X{
X (void) signal(SIGHUP, sighup);
X got_sig = TRUE;
X}
X
Xstatic SIGTYPE
Xsigint()
X{
X (void) signal(SIGINT, sigint);
X got_sig = TRUE;
X}
X
Xstatic SIGTYPE
Xsigquit()
X{
X (void) signal(SIGQUIT, sigquit);
X got_sig = TRUE;
X}
X
X/*----------------------------------------------------------------------
X * Report any errors to stderr.
X * Return an error count.
X */
X
Xint
Xreport_errors()
X{
X DEST *d;
X int count = 0;
X
X for (d = first_dest(); d; d = next_dest(d))
X {
X if (d->d_state != ST_ERROR)
X continue;
X
X if (++count == 1)
X {
X error(
X "delivery to the following address(es) failed on host %s\n",
X hostname);
X }
X
X message("\t\"%s\"", d->d_name);
X if (d->d_class == CL_MBOX)
X message(", mailbox \"%s\"", d->d_mailbox);
X message(": %s\n", derrmsg(d->d_error));
X }
X
X return count;
X}
X
X/*----------------------------------------------------------------------
X * Is the given uid trusted?
X */
X
Xint
Xtrusted_uid(uid)
Xint uid;
X{
X CONTEXT *ct;
X char **n;
X static char *t[] = { TRUSTED_USERS, 0 };
X
X for (n = t; *n; ++n)
X {
X if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid)
X return TRUE;
X }
X
X return FALSE;
X}
X
X/*----------------------------------------------------------------------
X * Set up useful environment variables.
X */
X
Xsetup_environ()
X{
X char flags[8];
X int f = 0;
X
X flags[f++] = '-';
X if (verbose)
X flags[f++] = (dryrun ? 'd' : 'v');
X if (printaddrs)
X flags[f++] = 'A';
X if (leavetemps)
X flags[f++] = 't';
X flags[f] = 0;
X
X alloc_env(ENV_DFLAGS, (f > 1) ? flags : "");
X if (sys_deliver && *sys_deliver)
X alloc_env(ENV_SYSDEL, sys_deliver);
X if (user_deliver && *user_deliver)
X alloc_env(ENV_USERDEL, user_deliver);
X if (hostname && *hostname)
X alloc_env(ENV_HOSTNAME, hostname);
X if (sender && *sender)
X alloc_env(ENV_SENDER, sender);
X
X alloc_env("IFS", " \t\n");
X}
END_OF_FILE
if test 13643 -ne `wc -c <'main.c'`; then
echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'mbox.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mbox.c'\"
else
echo shar: Extracting \"'mbox.c'\" \(4621 characters\)
sed "s/^X//" >'mbox.c' <<'END_OF_FILE'
X/* $Header: mbox.c,v 1.4 89/02/10 15:47:10 network Exp $
X *
X * Finally! Put the message in the specified mailbox(es).
X *
X * $Log: mbox.c,v $
X * Revision 1.4 89/02/10 15:47:10 network
X * V7 support.
X *
X * Revision 1.3 88/11/28 18:08:13 network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X *
X * Revision 1.2 88/09/14 19:42:06 network
X * Portability to System V and BSD.
X * General fixup.
X *
X * Revision 1.1 88/06/06 09:39:06 chip
X * Initial revision
X *
X */
X
X#include "deliver.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X
X/*
X * External data.
X */
X
Xextern int errno;
X
X/*
X * Local functions.
X */
X
Xstatic mbox_one();
Xstatic int mbox_write();
X
X/*----------------------------------------------------------------------
X * Deliver mail to all valid destinations.
X */
X
Xmbox_deliver()
X{
X DEST *d;
X
X for (d = first_dest(); d; d = next_dest(d))
X {
X switch (d->d_class)
X {
X case CL_USER:
X case CL_MBOX:
X if (d->d_state == ST_WORKING)
X mbox_one(d);
X break;
X }
X }
X}
X
X/*----------------------------------------------------------------------
X * Deliver mail to one destination.
X */
X
Xstatic
Xmbox_one(d)
XDEST *d;
X{
X CONTEXT *ct;
X int ret = 0;
X
X if (printaddrs)
X {
X (void) printf("%s", d->d_name);
X if (d->d_class == CL_MBOX)
X (void) printf(":%s", d->d_mailbox);
X (void) printf("\n");
X }
X
X if (dryrun)
X {
X d->d_state = ST_DONE;
X return;
X }
X
X if ((ct = name_context(d->d_name)) == NULL)
X {
X dest_err(d, E_CTLOST);
X return;
X }
X
X if (! ok_context(ct))
X {
X dest_err(d, E_CTPERM);
X return;
X }
X
X if (d->d_class == CL_MBOX)
X {
X if (sfork() == 0)
X {
X if (become(ct, !boxdelivery) < 0)
X exit(1);
X if (mbox_write(d->d_mailbox, ct, FALSE) < 0)
X exit(1);
X exit(0);
X }
X
X if (await_child() != 0)
X ret = -1;
X }
X else
X {
X char mailbox[100];
X
X (void) sprintf(mailbox, "%s/%s",
X#ifdef MBX_DIR
X MBX_DIR, d->d_name
X#else
X d->d_home, MBX_NAME
X#endif
X );
X
X if (mbox_write(mailbox, ct, TRUE) < 0)
X ret = -1;
X }
X
X if (ret >= 0)
X d->d_state = ST_DONE;
X else
X dest_err(d, E_MBOX);
X}
X
X/*----------------------------------------------------------------------
X * Write mail to the named mailbox.
X * If we have to create the mailbox, give it to the specified user.
X * If "is_sys" is true, then we're writing to a system mailbox.
X */
X
Xstatic int
Xmbox_write(mailbox, ct, is_sys)
Xchar *mailbox;
XCONTEXT *ct;
Xint is_sys;
X{
X struct stat st;
X int fd, t, mbox_uid, mbox_gid;
X int ret = 0;
X
X if (verbose)
X {
X message("As %s, delivering to %s mailbox %s\n",
X ct->ct_name, (is_sys ? "system" : "user"), mailbox);
X }
X
X if (name_lock(mailbox) < 0)
X return -1;
X
X while ((fd = open(mailbox, O_WRONLY)) == -1)
X {
X if (errno != ENOENT)
X {
X syserr("can't open %s", mailbox);
X break;
X }
X
X#ifdef O_CREAT
X fd = open(mailbox, O_WRONLY|O_CREAT|O_EXCL, MBX_MODE);
X
X /* If it exists now, try open() again. */
X if (fd == -1 && errno == EEXIST)
X continue;
X#else
X fd = creat(mailbox, MBX_MODE);
X#endif
X if (fd == -1)
X {
X syserr("can't create %s", mailbox);
X break;
X }
X
X /* Make sure the mailbox receives the correct modes */
X
X mbox_uid = ct->ct_uid;
X mbox_gid = ct->ct_gid;
X
X#ifdef MBX_GROUP
X if (is_sys)
X {
X static int mbox_sv_gid = -2;
X
X if (mbox_sv_gid == -2)
X mbox_sv_gid = group_id(MBX_GROUP);
X
X if (mbox_sv_gid < 0)
X message("%s: no such group\n", MBX_GROUP);
X else
X mbox_gid = mbox_sv_gid;
X }
X#endif /* MBX_GROUP */
X
X if (fstat(fd, &st) == -1)
X {
X syserr("can't fstat open mailbox?!");
X (void) close(fd);
X fd = -1;
X break;
X }
X
X /* Change mailbox ownership if it's not already correct. */
X
X if ((st.st_uid != mbox_uid || st.st_gid != mbox_gid)
X && chown(mailbox, mbox_uid, mbox_gid) == -1)
X {
X /* print a message, but that's all. (???) */
X syserr("can't chown %s to %d,%d",
X mailbox, mbox_uid, mbox_gid);
X }
X
X /* It's open now, so we can stop looping now. */
X
X break;
X }
X
X if (fd == -1)
X {
X (void) name_unlock(mailbox);
X return -1;
X }
X
X if (fd_lock(fd) < 0)
X {
X (void) close(fd);
X (void) name_unlock(mailbox);
X return -1;
X }
X
X (void) lseek(fd, 0L, 2); /* No error check: may be a special file */
X
X for (t = T_HDR; t <= T_BODY; ++t)
X {
X if (lseek(tfd[t], 0L, 0) == -1)
X {
X syserr("lseek in %s file %s", ttype[t], tfile[t]);
X ret = -1;
X break;
X }
X
X if (copyfd(tfd[t], fd) < 0)
X {
X ret = -1;
X break;
X }
X }
X
X if (verbose)
X {
X if (ret >= 0)
X message("wrote message to %s\n", mailbox);
X }
X
X if (fd_unlock(fd) < 0)
X ret = -1;
X (void) close(fd);
X if (name_unlock(mailbox) < 0)
X ret = -1;
X
X return ret;
X}
END_OF_FILE
if test 4621 -ne `wc -c <'mbox.c'`; then
echo shar: \"'mbox.c'\" unpacked with wrong size!
fi
# end of 'mbox.c'
fi
if test -f 'procs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procs.c'\"
else
echo shar: Extracting \"'procs.c'\" \(5411 characters\)
sed "s/^X//" >'procs.c' <<'END_OF_FILE'
X/* $Header: procs.c,v 1.4 89/02/10 15:47:31 network Exp $
X *
X * Process management and misc support.
X *
X * $Log: procs.c,v $
X * Revision 1.4 89/02/10 15:47:31 network
X * V7 support.
X *
X * Revision 1.3 88/11/26 13:21:07 network
X * patch4: Add return type of signal handlers to config.h.
X * patch4: Provide a version of getopt() for systems that lack it.
X * patch4: Call va_end() in routines that use varargs.
X * patch4: Make consistent checks for valid address strings.
X *
X * Revision 1.2 88/09/14 19:42:28 network
X * Portability to System V and BSD.
X * General fixup.
X *
X * Revision 1.1 88/06/06 09:39:15 chip
X * Initial revision
X *
X */
X
X#include "deliver.h"
X#include <errno.h>
X#include <signal.h>
X
X/*
X * External data.
X */
X
Xextern int errno;
X
X/*
X * Local data.
X */
X
Xstatic int child_pid = -1;
Xstatic SIGTYPE (*saved_sigpipe)() = SIG_DFL;
X
X/*----------------------------------------------------------------------
X * Like popen(), but execute the child in a specific context.
X * Also, the argument list is already a vector.
X */
X
XFILE *
Xct_popenv(ct, prog, av, mode)
XCONTEXT *ct;
Xchar *prog;
Xchar **av;
Xchar *mode;
X{
X char ch;
X int child, parent;
X int pfd[2];
X
X if (!ct || !prog || !av || !mode)
X return NULL;
X
X if (mode[0] == 'r' && mode[1] == 0)
X child = 1, parent = 0;
X else if (mode[0] == 'w' && mode[1] == 0)
X child = 0, parent = 1;
X else
X return NULL;
X
X /* We can't have more than one child at a time. */
X
X if (child_pid >= 0)
X {
X error("in ct_popen: a process is already open\n");
X return NULL;
X }
X
X /* Make a stab at predicting uid-related failure. */
X
X if (! ok_context(ct))
X {
X error("in ct_popen: no permissions to become %s\n",
X ct->ct_name);
X return NULL;
X }
X
X /* Pipes? Like, tubular, fer shur! */
X
X if (pipe(pfd) == -1)
X {
X syserr("can't create a pipe");
X return NULL;
X }
X
X /* Generate a debugging message. */
X
X if (verbose)
X {
X int a;
X
X message("Spawning");
X for (a = 0; av[a]; ++a)
X message(" %s", av[a]);
X message("\n");
X }
X
X /* Handle the child case */
X
X if (sfork() == 0)
X {
X if (child == 0)
X {
X (void) close(0);
X (void) dup(pfd[0]); /* ass_u_me 0 */
X }
X else
X {
X (void) close(0);
X if (open("/dev/null", O_RDONLY) != 0)
X {
X /* This should _never_ happen, but... */
X syserr("can't open /dev/null");
X (void) dup(1); /* ass_u_me 0 */
X }
X
X (void) close(1);
X (void) dup(pfd[1]); /* ass_u_me 1 */
X }
X
X if (become(ct, TRUE) < 0)
X (void) write(pfd[1], "n", 1);
X else
X {
X int t;
X
X (void) write(pfd[1], "y", 1);
X
X (void) close(pfd[child]);
X (void) close(pfd[parent]);
X for (t = 0; t < T_MAX; ++t)
X (void) close(tfd[t]);
X
X (void) execv(prog, av);
X syserr("can't execute %s", prog);
X }
X
X exit(127);
X }
X
X /* Make sure that a broken pipe won't kill us */
X
X saved_sigpipe = signal(SIGPIPE, SIG_IGN);
X
X /* The child must report "OK" before we continue. */
X
X if ((read(pfd[0], &ch, 1) < 1) || (ch != 'y'))
X {
X (void) close(pfd[0]);
X (void) close(pfd[1]);
X (void) await_child();
X return NULL;
X }
X
X (void) close(pfd[child]);
X return fdopen(pfd[parent], mode);
X}
X
X/*----------------------------------------------------------------------
X * Close the stream opened by ct_popen().
X */
X
Xct_pclose(fp)
XFILE *fp;
X{
X if (fp)
X (void) fclose(fp);
X return await_child();
X}
X
X/*----------------------------------------------------------------------
X * Assume the identity of the given user.
X */
X
Xint
Xbecome(ct, chd)
XCONTEXT *ct;
Xint chd;
X{
X char env_path[32];
X
X /*
X * Assume a new identity.
X * Note the importance of doing the setgid() before the setuid().
X */
X
X if (setgid(ct->ct_gid) == -1)
X {
X syserr("can't setgid to %d", ct->ct_gid);
X return -1;
X }
X if (setuid(ct->ct_uid) == -1)
X {
X syserr("can't setgid to %u", ct->ct_uid);
X return -1;
X }
X if (chd && chdir(ct->ct_home) == -1)
X {
X syserr("can't chdir to %s", ct->ct_home);
X return -1;
X }
X
X /* Set up the environment */
X
X (void) sprintf(env_path, "%s:/bin:/usr/bin",
X ((ct->ct_uid == 0) ? "/etc" : "."));
X alloc_env("HOME", ct->ct_home);
X alloc_env("PATH", env_path);
X
X /* I guess it worked. */
X
X return 0;
X}
X
X/*----------------------------------------------------------------------
X * Safe fork. If it doesn't work, it exits.
X */
X
Xint
Xsfork()
X{
X int tries;
X
X /*
X * A few safety measures.
X */
X
X (void) await_child();
X (void) fflush(stdout);
X (void) fflush(stderr);
X
X /*
X * Be patient in waiting for a fork().
X */
X
X for (tries = 0; tries < 10; ++tries)
X {
X if (tries)
X snooze(3);
X if ((child_pid = fork()) >= 0)
X return child_pid;
X if (errno != EAGAIN)
X break;
X }
X
X syserr("can't fork");
X leave(1);
X /* NOTREACHED */
X}
X
X/*----------------------------------------------------------------------
X * Wait for our child (if any) to exit.
X * Returns child's exit status or -1 if there is a problem.
X */
X
Xint
Xawait_child()
X{
X int wpid, st;
X
X if (child_pid < 0)
X return -1;
X
X while ((wpid = wait(&st)) >= 0)
X {
X if (wpid == child_pid)
X break;
X }
X
X child_pid = -1;
X if (wpid == -1)
X syserr("waiting for child");
X
X (void) signal(SIGPIPE, saved_sigpipe);
X saved_sigpipe = SIG_DFL;
X
X if (wpid == -1)
X return -1;
X
X if (st & 0xFF)
X {
X error("child process died%s due to signal %d.\n",
X ((st & 0x80) ? " and dumped core" : ""),
X (st & 0x7F));
X
X return -1;
X }
X
X if (verbose)
X {
X message("child process exited with status %d.\n",
X (st >> 8) & 0xFF);
X }
X
X return ((st >> 8) & 0xFF);
X}
END_OF_FILE
if test 5411 -ne `wc -c <'procs.c'`; then
echo shar: \"'procs.c'\" unpacked with wrong size!
fi
# end of 'procs.c'
fi
if test -f 'subs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'subs.c'\"
else
echo shar: Extracting \"'subs.c'\" \(2775 characters\)
sed "s/^X//" >'subs.c' <<'END_OF_FILE'
X/* $Header: subs.c,v 1.6 89/02/10 15:47:40 network Exp $
X *
X * Miscellaneous subroutines.
X *
X * $Log: subs.c,v $
X * Revision 1.6 89/02/10 15:47:40 network
X * V7 support.
X *
X * Revision 1.5 88/11/26 13:21:11 network
X * patch4: Add return type of signal handlers to config.h.
X * patch4: Provide a version of getopt() for systems that lack it.
X * patch4: Call va_end() in routines that use varargs.
X * patch4: Make consistent checks for valid address strings.
X *
X * Revision 1.4 88/10/13 12:20:34 network
X * patch1: add "-n" option, and general bug fixes.
X *
X * Revision 1.3 88/09/14 19:42:33 network
X * Portability to System V and BSD.
X * General fixup.
X *
X * Revision 1.2 88/08/30 16:14:53 network
X * New module. Includes routines from main.c.
X * Also, new routine savestr().
X *
X */
X
X#include "deliver.h"
X
X/*----------------------------------------------------------------------
X * Allocate memory for an environment variable, and putenv() it.
X */
X
Xalloc_env(name, value)
Xchar *name;
Xchar *value;
X{
X char *s;
X
X if (!name || !value)
X return;
X
X s = zalloc((unsigned) (strlen(name) + strlen(value) + 2));
X (void) sprintf(s, "%s=%s", name, value);
X if (putenv(s))
X nomem();
X}
X
X/*----------------------------------------------------------------------
X * Allocate and clear. If it fails, it takes the emergency exit.
X */
X
Xchar *
Xzalloc(size)
Xunsigned size;
X{
X char *p;
X
X if ((p = malloc(size)) == NULL)
X nomem();
X
X Zero(p, size);
X return p;
X}
X
X/*----------------------------------------------------------------------
X * Reallocate to new size. If it fails, it takes the emergency exit.
X */
X
Xchar *
Xsrealloc(ptr, size)
Xchar *ptr;
Xunsigned size;
X{
X char *p;
X
X if ((p = realloc(ptr, size)) == NULL)
X nomem();
X
X return p;
X}
X
X/*----------------------------------------------------------------------
X * Make an allocated copy of a string.
X */
X
Xchar *
Xcopystr(s)
Xchar *s;
X{
X char *p;
X
X if (s == NULL)
X return NULL;
X
X if ((p = malloc((unsigned) strlen(s) + 1)) == NULL)
X nomem();
X
X (void) strcpy(p, s);
X return p;
X}
X
X/*----------------------------------------------------------------------
X * Emergency exit for memory loss.
X */
X
Xnomem()
X{
X error("out of memory\n");
X leave(1);
X}
X
X/*----------------------------------------------------------------------
X * Return the last component of the given pathname.
X */
X
Xchar *
Xbasename(name)
Xchar *name;
X{
X char *b;
X
X if ((b = strrchr(name, '/')) != NULL)
X ++b;
X else
X b = name;
X
X return (b);
X}
X
X/*----------------------------------------------------------------------
X * Check an address for validity.
X */
X
Xvalid_address(addr)
Xchar *addr;
X{
X char *p;
X static char sanitize[] = SANITIZE;
X
X for (p = addr; *p; ++p)
X {
X if (strchr(sanitize, *p))
X return FALSE;
X }
X
X return TRUE;
X}
END_OF_FILE
if test 2775 -ne `wc -c <'subs.c'`; then
echo shar: \"'subs.c'\" unpacked with wrong size!
fi
# end of 'subs.c'
fi
if test -f 'sysdep.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sysdep.c'\"
else
echo shar: Extracting \"'sysdep.c'\" \(7306 characters\)
sed "s/^X//" >'sysdep.c' <<'END_OF_FILE'
X/* $Header: sysdep.c,v 1.7 89/02/10 15:47:44 network Exp $
X *
X * Routines which are (or might well be) system-dependant.
X * I've put the message routines here since you may need to use
X * the ANSI <stdarg.h> instead of <varargs.h>.
X *
X * $Log: sysdep.c,v $
X * Revision 1.7 89/02/10 15:47:44 network
X * V7 support.
X *
X * Revision 1.6 88/11/30 16:24:56 network
X * patch6: Separate getopt() into its own module.
X *
X * Revision 1.5 88/11/26 13:21:15 network
X * patch4: Add return type of signal handlers to config.h.
X * patch4: Provide a version of getopt() for systems that lack it.
X * patch4: Call va_end() in routines that use varargs.
X * patch4: Make consistent checks for valid address strings.
X *
X * Revision 1.4 88/10/13 12:20:39 network
X * patch1: add "-n" option, and general bug fixes.
X *
X * Revision 1.3 88/09/14 20:00:24 network
X * Fix type of gethostname() for BSD.
X *
X * Revision 1.2 88/09/14 19:42:37 network
X * Portability to System V and BSD.
X * General fixup.
X *
X * Revision 1.1 88/06/06 09:39:29 chip
X * Initial revision
X *
X */
X
X#include "deliver.h"
X#include <errno.h>
X#ifdef HAS_STDARG
X#include <stdarg.h>
X#else
X#ifdef HAS_VARARGS
X#include <varargs.h>
X#else
X/*
X * Non-portable home-grown varargs. Use at your own risk.
X * Especially note that if sizeof(int) > sizeof(short), then
X * "va_arg(..,short)" is broken.
X */
Xtypedef char *va_list;
X#define va_dcl int va_alist;
X#define va_start(ap) ap = (char *) &va_alist
X#define va_arg(ap,type) *(type *)(ap += sizeof(type), ap - sizeof(type))
X#define va_end(ap) /* nothing */
X#endif
X#endif
X
X#ifdef UNAME
X#include <sys/utsname.h>
X#endif
X
X/*
X * External functions.
X */
X
X#ifdef M_XENIX
Xextern long nap();
X#else
Xextern unsigned sleep();
X#endif
X
X/*
X * External data.
X */
X
Xextern int errno;
Xextern int sys_nerr;
Xextern char *sys_errlist[];
X
X/*----------------------------------------------------------------------
X * Print a message.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xmessage(char *fmt, ...)
X#else
Xmessage(va_alist) va_dcl
X#endif
X{
X va_list ap;
X
X#ifdef HAS_STDARG
X va_start(ap, fmt);
X#else
X char *fmt;
X va_start(ap);
X fmt = va_arg(ap, char *);
X#endif
X
X (void) vfprintf(stderr, fmt, ap);
X
X va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Print an error message.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xerror(char *fmt, ...)
X#else
Xerror(va_alist) va_dcl
X#endif
X{
X va_list ap;
X
X#ifdef HAS_STDARG
X va_start(ap, fmt);
X#else
X char *fmt;
X va_start(ap);
X fmt = va_arg(ap, char *);
X#endif
X
X (void) fprintf(stderr, "%s: ", progname);
X (void) vfprintf(stderr, fmt, ap);
X
X va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Report an error returned from a system call.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xsyserr(char *fmt, ...)
X#else
Xsyserr(va_alist) va_dcl
X#endif
X{
X int e = errno;
X va_list ap;
X
X#ifdef HAS_STDARG
X va_start(ap, fmt);
X#else
X char *fmt;
X va_start(ap);
X fmt = va_arg(ap, char *);
X#endif
X
X (void) fprintf(stderr, "%s: ", progname);
X (void) vfprintf(stderr, fmt, ap);
X if (e <= sys_nerr)
X (void) fprintf(stderr, ": %s\n", sys_errlist[e]);
X else
X (void) fprintf(stderr, ": unknown system error %d\n", e);
X
X va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Sleep for the given number of seconds.
X */
X
Xsnooze(n)
Xint n;
X{
X#ifdef M_XENIX
X (void) nap(n * 1000L);
X#else
X (void) sleep(n);
X#endif
X}
X
X/*----------------------------------------------------------------------
X * Get the host name from HOSTFILE.
X */
X
X#ifdef HOSTFILE
X
Xchar *
Xgethost()
X{
X int fd, rd;
X char *p;
X static char name[32];
X
X if ((fd = open(HOSTFILE, O_RDONLY)) == -1)
X return NULL;
X rd = read(fd, name, sizeof(name) - 1);
X (void) close(fd);
X
X if (rd < 1)
X return NULL;
X name[rd] = 0;
X if ((p = strchr(name, '\n')) != NULL)
X *p = 0;
X
X return (name[0] ? name : NULL);
X}
X
X#endif /* HOSTFILE */
X
X/*----------------------------------------------------------------------
X * Get the host name via the uname() system call.
X */
X
X#ifdef UNAME
X
Xchar *
Xgethost()
X{
X static struct utsname u;
X
X uname(&u);
X return (u.nodename[0] ? u.nodename : NULL);
X}
X
X#endif /* UNAME */
X
X/*----------------------------------------------------------------------
X * Get the host name via the gethostname() system call.
X */
X
X#ifdef GETHOSTNAME
X
Xchar *
Xgethost()
X{
X static char hostname[64];
X
X if (gethostname(hostname, sizeof(hostname)) == -1)
X return NULL;
X
X return hostname;
X}
X
X#endif /* GETHOSTNAME */
X
X/*----------------------------------------------------------------------
X * Return a pre-defined HOSTNAME.
X */
X
X#ifdef HOSTNAME
X
Xchar *
Xgethost()
X{
X return HOSTNAME;
X}
X
X#endif /* HOSTNAME */
X
X/*----------------------------------------------------------------------
X * Variable-argument-list output, System V style.
X */
X
X#ifndef HAS_VPRINTF
X
Xvprintf(fmt, ap)
Xchar *fmt;
Xva_list ap;
X{
X int a,b,c,d,e,f,g,h;
X
X a = va_arg(ap, int);
X b = va_arg(ap, int);
X c = va_arg(ap, int);
X d = va_arg(ap, int);
X e = va_arg(ap, int);
X f = va_arg(ap, int);
X g = va_arg(ap, int);
X h = va_arg(ap, int);
X
X (void) printf(fmt, a,b,c,d,e,f,g,h);
X}
X
Xvfprintf(fp, fmt, ap)
XFILE *fp;
Xchar *fmt;
Xva_list ap;
X{
X int a,b,c,d,e,f,g,h;
X
X a = va_arg(ap, int);
X b = va_arg(ap, int);
X c = va_arg(ap, int);
X d = va_arg(ap, int);
X e = va_arg(ap, int);
X f = va_arg(ap, int);
X g = va_arg(ap, int);
X h = va_arg(ap, int);
X
X (void) fprintf(fp, fmt, a,b,c,d,e,f,g,h);
X}
X
Xvsprintf(s, fmt, ap)
Xchar *s;
Xchar *fmt;
Xva_list ap;
X{
X int a,b,c,d,e,f,g,h;
X
X a = va_arg(ap, int);
X b = va_arg(ap, int);
X c = va_arg(ap, int);
X d = va_arg(ap, int);
X e = va_arg(ap, int);
X f = va_arg(ap, int);
X g = va_arg(ap, int);
X h = va_arg(ap, int);
X
X (void) sprintf(s, fmt, a,b,c,d,e,f,g,h);
X}
X
X#endif /* !HAS_VPRINTF */
X
X/*----------------------------------------------------------------------
X * Add a new environment variable.
X */
X
X#ifndef HAS_PUTENV
X
Xint
Xputenv(s)
Xchar *s;
X{
X static char **env_array;
X static int env_size;
X char *e;
X int i, j;
X
X if (env_array == NULL)
X {
X for (i = 0; environ[i]; ++i)
X {}
X env_size = i + 10; /* arbitrary */
X env_array = (char **) zalloc(env_size * sizeof(char *));
X Copy((char *)env_array, (char *)environ,
X (int) ((i + 1) * sizeof(char *)));
X environ = env_array;
X }
X else if (environ != env_array)
X message("putenv: warning: someone moved environ!\n");
X
X if ((e = strchr(s, '=')) != NULL)
X ++e;
X else
X e = s + strlen(s);
X
X j = 0;
X for (i = 0; env_array[i]; ++i)
X {
X if (strncmp(env_array[i], s, e - s) != 0)
X env_array[j++] = env_array[i];
X }
X
X if ((j + 1) >= env_size)
X {
X env_size += 10; /* arbitrary */
X env_array = (char **) srealloc((char *)env_array,
X env_size * sizeof(char **));
X }
X
X env_array[j++] = s;
X env_array[j] = NULL;
X
X environ = env_array;
X return 0;
X}
X
X#endif /* !HAS_PUTENV */
X
X/*----------------------------------------------------------------------
X * Memory copy.
X */
X
X#ifdef MEMFUNCS
X
XCopy(dest, src, len)
Xchar *dest;
Xchar *src;
Xint len;
X{
X while (len-- > 0)
X *dest++ = *src++;
X}
X
X#endif
X
X/*----------------------------------------------------------------------
X * Memory clear.
X */
X
X#ifdef MEMFUNCS
X
XZero(dest, len)
Xchar *dest;
Xint len;
X{
X while (len-- > 0)
X *dest++ = 0;
X}
X
X#endif
END_OF_FILE
if test 7306 -ne `wc -c <'sysdep.c'`; then
echo shar: \"'sysdep.c'\" unpacked with wrong size!
fi
# end of 'sysdep.c'
fi
if test -f 'uucp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'uucp.c'\"
else
echo shar: Extracting \"'uucp.c'\" \(3269 characters\)
sed "s/^X//" >'uucp.c' <<'END_OF_FILE'
X/* $Header: uucp.c,v 1.3 89/02/10 15:47:51 network Exp $
X *
X * Handle mail destined for other hosts via UUCP.
X * Deliver is intended as a very low-level program, so we don't
X * do anything fancy here. We just hand the message to uux.
X *
X * $Log: uucp.c,v $
X * Revision 1.3 89/02/10 15:47:51 network
X * V7 support.
X *
X * Revision 1.2 88/11/28 18:08:23 network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X *
X * Revision 1.1 88/06/06 09:39:42 chip
X * Initial revision
X *
X */
X
X#include "deliver.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/*
X * Local functions.
X */
X
Xstatic int uucp_copy();
X
X/*----------------------------------------------------------------------
X * Send mail to UUCP addresses (if any).
X * This is a simple implementation: invoke uux once per address.
X */
X
Xuucp_deliver()
X{
X struct stat st;
X DEST *d;
X char *uux;
X static char uux1[] = "/bin/uux";
X static char uux2[] = "/usr/bin/uux";
X
X if (stat(uux1, &st) == 0)
X uux = uux1;
X else if (stat(uux2, &st) == 0)
X uux = uux2;
X else
X {
X error("can't find uux!?\n");
X return;
X }
X
X for (d = first_dest(); d; d = next_dest(d))
X {
X FILE *uux_fp;
X char *bang;
X char *av[5];
X char rmail[40];
X char who[BUFSIZ];
X
X if (d->d_class != CL_UUCP || d->d_state != ST_WORKING)
X continue;
X
X if (printaddrs)
X (void) printf("%s\n", d->d_name);
X
X if (dryrun)
X {
X d->d_state = ST_DONE;
X continue;
X }
X
X bang = strchr(d->d_name, '!');
X *bang = 0;
X (void) sprintf(rmail, "%s!rmail", d->d_name);
X *bang++ = '!';
X (void) sprintf(who, "(%s)", bang);
X
X av[0] = "uux";
X av[1] = "-";
X av[2] = rmail;
X av[3] = who;
X av[4] = NULL;
X if ((uux_fp = ct_popenv(eff_ct, uux, av, "w")) == NULL)
X continue;
X
X if (uucp_copy(uux_fp) < 0)
X dest_err(d, E_UUX);
X
X if (ct_pclose(uux_fp))
X {
X /* "No such host" overrides piping problems. */
X dest_err(d, E_NSHOST);
X }
X else
X d->d_state = ST_DONE;
X }
X}
X
X/*----------------------------------------------------------------------
X * Write the message for UUCP transmission to the given file.
X */
X
Xstatic int
Xuucp_copy(ofp)
XFILE *ofp;
X{
X FILE *ifp;
X char *p;
X register int c;
X int fd;
X char buf[BUFSIZ];
X
X if ((fd = dup(tfd[T_HDR])) == -1)
X {
X syserr("can't dup header fd");
X return -1;
X }
X (void) lseek(fd, 0L, 0);
X if ((ifp = fdopen(fd, "r")) == NULL)
X {
X error("can't fdopen header fd");
X return -1;
X }
X
X /*
X * Copy the header, but tack "remote from" onto the end of the
X * From_ line. (If it weren't for dealing with the From_ line,
X * I'd skip stream I/O altogether and use read/write. Maybe
X * I should save the length of the From_ line when I copy it...)
X */
X
X (void) fgets(buf, GETSIZE(buf), ifp);
X if ((p = strchr(buf, '\n')) != NULL)
X *p = 0;
X (void) fprintf(ofp, "%s remote from %s\n", buf, hostname);
X
X while ((c = getc(ifp)) != EOF)
X (void) putc(c, ofp);
X
X (void) fclose(ifp);
X
X /*
X * Copy the body
X */
X
X if ((fd = dup(tfd[T_BODY])) == -1)
X {
X syserr("can't dup body fd");
X return -1;
X }
X (void) lseek(fd, 0L, 0);
X if ((ifp = fdopen(fd, "r")) == NULL)
X {
X error("can't fdopen body fd");
X (void) close(fd);
X return -1;
X }
X
X while ((c = getc(ifp)) != EOF)
X (void) putc(c, ofp);
X
X (void) fclose(ifp);
X return 0;
X}
END_OF_FILE
if test 3269 -ne `wc -c <'uucp.c'`; then
echo shar: \"'uucp.c'\" unpacked with wrong size!
fi
# end of 'uucp.c'
fi
echo shar: End of shell archive.
exit 0
--
Chip Salzenberg <chip at ateng.com> or <uunet!ateng!chip>
A T Engineering Me? Speak for my company? Surely you jest!
"It's no good. They're tapping the lines."
More information about the Comp.sources.bugs
mailing list