[comp.sources.unix] v16i083: Mail delivery program, Part03/03

rsalz@uunet.uu.net (Rich Salz) (11/16/88)

Submitted-by: Chip Salzenberg <chip@ateng.uu.net>
Posting-number: Volume 16, Issue 83
Archive-name: deliver/part03

#! /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
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'\" \(9621 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/* $Header: main.c,v 1.5 88/09/14 20:00:03 network Exp $
X *
X * A program to deliver local mail with some flexibility.
X *
X * $Log:	main.c,v $
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 * Global data
X */
X
Xint     verbose         = FALSE;
Xint     dryrun          = FALSE;
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    = NULL;
Xchar    *user_deliver   = NULL;
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
Xchar    *ttype[T_MAX]   = { "header", "body" };
Xchar    *tfile[T_MAX]   = { NULL, NULL };
Xint     tfd[T_MAX]      = { -1, -1 };
X
X/*----------------------------------------------------------------------
X * The Program.
X */
X
Xmain(argc, argv)
Xint     argc;
Xchar    **argv;
X{
X	char    *p;
X	int     u, c, errcount, insecure;
X
X	/* Make sure that stdout and stderr are interleaved correctly */
X
X	(void) Linebuf(stdout);
X	(void) 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	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	/* 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 't':
X				leavetemps = TRUE;
X				break;
X			}
X		}
X	}
X	if ((p = getenv(ENV_SYSDEL)) != NULL)
X		sys_deliver = p;
X	if ((p = getenv(ENV_USERDEL)) != NULL)
X		user_deliver = p;
X	if ((p = getenv(ENV_SENDER)) != NULL)
X		sender = p;
X	if ((p = getenv(ENV_HOSTNAME)) != NULL)
X		hostname = p;
X
X	/* Parse command line arguments */
X
X	while ((c = getopt(argc, argv, "vdAtbs: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 't':
X			leavetemps = TRUE;
X			break;
X		case 'b':
X			boxdelivery = TRUE;
X			break;
X		case 's':
X			sys_deliver = optarg;
X			break;
X		case 'u':
X			user_deliver = optarg;
X			break;
X		case 'r':
X			sender = optarg;
X			break;
X		case 'h':
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		message("%s: no recipients specified\n", progname);
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		if (sender && *sender)
X			message("Sender is %s\n", sender);
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		message("%s: if setuid, must be setuid root\n");
X		leave(1);
X	}
X
X	/* Renounce special privileges if something insecure was requested. */
X
X	if (sys_deliver || user_deliver)
X	{
X		if (setgid(eff_gid = real_gid) == -1
X		 || setuid(eff_uid = real_uid) == -1)
X		{
X			syserr("%s: 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->name, eff_ct->uid, eff_ct->gid,
X			real_ct->name, real_ct->uid, real_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	/* Turn off all intrusive signals (unless we're not delivering). */
X
X	if (! dryrun)
X	{
X		(void) signal(SIGHUP, SIG_IGN);
X		(void) signal(SIGINT, SIG_IGN);
X		(void) signal(SIGQUIT, SIG_IGN);
X	}
X
X	/*
X	 * Create the temporary files and write the message to them.
X	 */
X
X	if (copy_message() < 0)
X		leave(1);
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	 * Assign the default delivery file names.
X	 * Note that this must be after setup_environ(), or else the
X	 * environment won't reflect specified/unspecified options.
X	 */
X
X	if (!sys_deliver)
X		sys_deliver = SYS_DELIVER;
X	if (!user_deliver)
X		user_deliver = USER_DELIVER;
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->name);
X
X		/*
X		 * Consider all arguments as mailbox filenames.
X		 */
X
X		for (a = optind; a < argc; ++a)
X			(void) dest(real_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		/*
X		 * Run all destinations though the system delivery file.
X		 * If sys_dfile() doesn't find one, it will call dest()
X		 * on each address.
X		 */
X
X		sys_dfile(argc - optind, argv + optind);
X
X		if (verbose)
X			dumpdests("after running system delivery file");
X
X		/*
X		 * Run each user destination through his delivery file.
X		 */
X
X		user_dfiles();
X
X		if (verbose)
X			dumpdests("after running user delivery files");
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][-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("-t      Do not remote temp files before exiting.\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("        (This option overrides any \"From \" line in the input.)\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 (tfile[t] && unlink(tfile[t]) == -1)
X				syserr("can't unlink %s", tfile[t]);
X		}
X	}
X
X	exit(code);
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->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("\tuser \"%s\"", d->name);
X		if (d->class == CL_MBOX)
X			message(", mailbox \"%s\"", d->mailbox);
X		message(": %s\n", d->error);
X	}
X
X	return count;
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(ENV_HEADER, tfile[T_HEADER]);
X	alloc_env(ENV_BODY,   tfile[T_BODY]);
X
X	alloc_env("IFS", " \t\n");
X}
END_OF_FILE
if test 9621 -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'\" \(4767 characters\)
sed "s/^X//" >'mbox.c' <<'END_OF_FILE'
X/* $Header: mbox.c,v 1.2 88/09/14 19:42:06 network Exp $
X *
X * Finally!  Put the message in the specified mailbox(es).
X *
X * $Log:	mbox.c,v $
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_dest();
Xstatic  int     mbox_write();
Xstatic  int     mbox_copy();
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->class)
X		{
X		case CL_USER:
X		case CL_MBOX:
X			if (d->state == ST_WORKING)
X				mbox_dest(d);
X			break;
X		}
X	}
X}
X
X/*----------------------------------------------------------------------
X * Deliver mail to one destination.
X */
X
Xstatic
Xmbox_dest(d)
XDEST    *d;
X{
X	CONTEXT *ct;
X	int     ret = 0;
X
X	if (printaddrs)
X	{
X		(void) printf("%s", d->name);
X		if (d->class == CL_MBOX)
X			(void) printf(":%s", d->mailbox);
X		(void) printf("\n");
X	}
X
X	if (dryrun)
X	{
X		d->state = ST_DONE;
X		return;
X	}
X
X	if ((ct = name_context(d->name)) == NULL)
X	{
X		d->state = ST_ERROR;
X		d->error = "Lost context in mbox_dest()";
X		return;
X	}
X
X	if (! ok_context(ct))
X	{
X		d->state = ST_ERROR;
X		d->error = "No permissions for that context";
X		return;
X	}
X
X	if (d->class == CL_MBOX)
X	{
X		give_temps(ct);
X
X		if (sfork() == 0)
X		{
X			if (become(ct, !boxdelivery) < 0)
X				exit(1);
X			if (mbox_write(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 MAILBOX_DIR
X			MAILBOX_DIR, d->name
X#else
X			d->home, MAILBOX_NAME
X#endif
X			);
X
X		if (mbox_write(mailbox, ct, TRUE) < 0)
X			ret = -1;
X	}
X
X	if (ret >= 0)
X		d->state = ST_DONE;
X	else
X	{
X		d->state = ST_ERROR;
X		d->error = "Error writing to mailbox";
X	}
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	int     fd, t;
X	int     ret = 0;
X
X	if (verbose)
X	{
X		message("As %s, delivering to %s mailbox %s\n",
X			ct->name, (is_sys ? "system" : "user"), mailbox);
X	}
X
X	if (lock_name(mailbox) < 0)
X		return -1;
X
X	while ((fd = open(mailbox, O_RDWR)) == -1)
X	{
X		if (errno != ENOENT)
X		{
X			syserr("can't open %s", mailbox);
X			break;
X		}
X
X		if ((fd = open(mailbox, O_RDWR|O_CREAT|O_EXCL,
X				MAILBOX_MODE)) != -1)
X		{
X			/* Make sure the mailbox receives the correct modes */
X
X			int mbox_gid = ct->gid;
X
X#ifdef MAILBOX_GROUP
X			if (is_sys)
X			{
X				static int mbox_gid_sv = -2;
X
X				if (mbox_gid_sv == -2)
X					mbox_gid_sv = group_id(MAILBOX_GROUP);
X
X				if (mbox_gid_sv < 0)
X					message("%s: no such group\n", MAILBOX_GROUP);
X				else
X					mbox_gid = mbox_gid_sv;
X			}
X#endif /* MAILBOX_GROUP */
X
X			if (chown(mailbox, ct->uid, mbox_gid) == -1)
X			{
X				/* print a message, but that's all. (???) */
X				syserr("can't chown %s to %d,%d",
X					mailbox, ct->uid, mbox_gid);
X			}
X			break;
X		}
X
X		if (errno != EEXIST)
X		{
X			syserr("can't create %s", mailbox);
X			break;
X		}
X	}
X
X	if (fd == -1)
X	{
X		(void) unlock_name(mailbox);
X		return -1;
X	}
X
X	if (lock_fd(fd) < 0)
X	{
X		(void) close(fd);
X		(void) unlock_name(mailbox);
X		return -1;
X	}
X
X	(void) lseek(fd, 0L, 2); /* No error check: may be a special file */
X
X	for (t = 0; t < T_MAX; ++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		switch (mbox_copy(tfd[t], fd))
X		{
X		case 1:
X			syserr("can't read %s file %s", ttype[t], tfile[t]);
X			ret = -1;
X			break;
X		case 2:
X			syserr("can't write to mailbox %s as %s",
X				mailbox, ct->name);
X			ret = -1;
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X
X	if (verbose)
X	{
X		if (ret >= 0)
X			message("wrote message to %s\n", mailbox);
X	}
X
X	if (unlock_fd(fd) < 0)
X		ret = -1;
X	(void) close(fd);
X	if (unlock_name(mailbox) < 0)
X		ret = -1;
X
X	return ret;
X}
X
X/*----------------------------------------------------------------------
X * Copy the named file to the given file descriptor.
X */
X
Xstatic int
Xmbox_copy(ifd, ofd)
Xint     ifd;
Xint     ofd;
X{
X	char    buf[BUFSIZ];
X	int     rd;
X
X	while ((rd = read(ifd, buf, sizeof(buf))) > 0)
X	{
X		errno = 255;    /* to avoid bogus syserr() output */
X		if (write(ofd, buf, (unsigned) rd) != rd)
X			return 2;
X	}
X
X	if (rd == -1)
X		return 1;
X
X	return 0;
X}
END_OF_FILE
if test 4767 -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'\" \(5021 characters\)
sed "s/^X//" >'procs.c' <<'END_OF_FILE'
X/* $Header: procs.c,v 1.2 88/09/14 19:42:28 network Exp $
X *
X * Process management and misc support.
X *
X * $Log:	procs.c,v $
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  int     (*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", 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->gid) == -1)
X	{
X		syserr("can't setgid to %d", ct->gid);
X		return -1;
X	}
X	if (setuid(ct->uid) == -1)
X	{
X		syserr("can't setgid to %u", ct->uid);
X		return -1;
X	}
X	if (chd && chdir(ct->home) == -1)
X	{
X		syserr("can't chdir to %s", ct->home);
X		return -1;
X	}
X
X	/* Set up the environment */
X
X	(void) sprintf(env_path, "%s:/bin:/usr/bin",
X			((ct->uid == 0) ? "/etc" : "."));
X	alloc_env("HOME", 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 5021 -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'\" \(2050 characters\)
sed "s/^X//" >'subs.c' <<'END_OF_FILE'
X/* $Header: subs.c,v 1.3 88/09/14 19:42:33 network Exp $
X *
X * Miscellaneous subroutines.
X *
X * $Log:	subs.c,v $
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	(void) 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	message("%s: out of memory\n", progname);
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}
END_OF_FILE
if test 2050 -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'\" \(5242 characters\)
sed "s/^X//" >'sysdep.c' <<'END_OF_FILE'
X/* $Header: sysdep.c,v 1.3 88/09/14 20:00:24 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.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#include <varargs.h>
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/* VARARGS1 */
Xmessage(fmt, va_alist)
Xchar    *fmt;
Xva_dcl
X{
X	va_list args;
X	va_start(args);
X
X	(void) vfprintf(stderr, fmt, args);
X}
X
X/*----------------------------------------------------------------------
X * Print an error message.
X */
X
X/* VARARGS1 */
Xerror(fmt, va_alist)
Xchar    *fmt;
Xva_dcl
X{
X	va_list args;
X	va_start(args);
X
X	(void) fprintf(stderr, "%s: ", progname);
X	(void) vfprintf(stderr, fmt, args);
X}
X
X/*----------------------------------------------------------------------
X * Report an error returned from a system call.
X */
X
X/* VARARGS1 */
Xsyserr(fmt, va_alist)
Xchar    *fmt;
Xva_dcl
X{
X	int     e = errno;
X	va_list args;
X	va_start(args);
X
X	(void) fprintf(stderr, "%s: ", progname);
X	(void) vfprintf(stderr, fmt, args);
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
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 * 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	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	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	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		(void) memcpy((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 */
END_OF_FILE
if test 5242 -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'\" \(3132 characters\)
sed "s/^X//" >'uucp.c' <<'END_OF_FILE'
X/* $Header: uucp.c,v 1.1 88/06/06 09:39:42 chip 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.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->class != CL_UUCP || d->state != ST_WORKING)
X			continue;
X
X		if (printaddrs)
X			(void) printf("%s\n", d->name);
X
X		if (dryrun)
X		{
X			d->state = ST_DONE;
X			continue;
X		}
X
X		bang = strchr(d->name, '!');
X		*bang = 0;
X		(void) sprintf(rmail, "%s!rmail", 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		{
X			d->state = ST_ERROR;
X			d->error = "Error piping to uux";
X		}
X
X		if (ct_pclose(uux_fp))
X		{
X			/* Overrides any problems with uucp_copy() */
X
X			d->state = ST_ERROR;
X			d->error = "UUCP not available to that host";
X		}
X		else
X			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_HEADER])) == -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 3132 -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

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.