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.