v16i083: Mail delivery program, Part03/03
Rich Salz
rsalz at uunet.uu.net
Wed Nov 16 05:33:51 AEST 1988
Submitted-by: Chip Salzenberg <chip at 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 at uunet.uu.net.
More information about the Comp.sources.unix
mailing list