home *** CD-ROM | disk | FTP | other *** search
- /* $Header: procs.c,v 2.6 90/02/23 16:35:59 chip Exp $
- *
- * Process management and misc support.
- *
- * $Log: procs.c,v $
- * Revision 2.6 90/02/23 16:35:59 chip
- * \Fix problems determining legality of user references.
- *
- * Revision 2.5 90/02/23 14:16:51 chip
- * Support "#!" in delivery files.
- * Support "user|program" and "user?error" from delivery files.
- * Improve debugging and error message formatting.
- * Rearrange code for clarity.
- *
- * Revision 2.4 89/11/01 12:19:05 network
- * Delintify.
- *
- * Revision 2.3 89/11/01 11:51:50 network
- * Add logging.
- *
- * Revision 2.2 89/09/29 18:18:03 network
- * Save message when delivery file produces no output,
- * unless delivery file output the "DROP" string.
- * Don't recopy temp files for sys and post-user delfiles.
- *
- * Revision 2.1 89/06/09 12:25:37 network
- * Update RCS revisions.
- *
- * Revision 1.5 89/06/09 12:23:57 network
- * Baseline for 2.0 release.
- *
- */
-
- #include "deliver.h"
- #include <errno.h>
- #include <signal.h>
-
- /*
- * External data.
- */
-
- extern int errno;
-
- /*
- * Local data.
- */
-
- static int child_pid = -1;
- static SIGTYPE (*saved_sigpipe)() = SIG_DFL;
-
- /*----------------------------------------------------------------------
- * Like popen(), but execute the child in a specific context.
- * Also, the argument list is already a vector.
- */
-
- FILE *
- ct_fopenv(ct, prog, av, mode)
- CONTEXT *ct;
- char *prog;
- char **av;
- char *mode;
- {
- FILE *fp;
- int fd, m;
-
- if (mode && mode[0] == 'r' && mode[1] == 0)
- m = O_RDONLY;
- else if (mode && mode[0] == 'w' && mode[1] == 0)
- m = O_WRONLY;
- else
- return NULL;
-
- if ((fd = ct_openv(ct, prog, av, m)) == -1)
- return NULL;
-
- if ((fp = fdopen(fd, mode)) == NULL)
- (void) ct_close(fd);
-
- return fp;
- }
-
- /*----------------------------------------------------------------------
- * Close the stream opened by ct_fopen().
- */
-
- ct_fclose(fp)
- FILE *fp;
- {
- int fd;
-
- if (fp)
- {
- fd = dup(fileno(fp));
- (void) fclose(fp);
- }
- else
- fd = -1;
-
- return ct_close(fd);
- }
-
- /*----------------------------------------------------------------------
- * Like popen(), but execute the child in a specific context.
- * Also, the argument list is already a vector.
- * And return a file descriptor instead of a FILE *.
- */
-
- int
- ct_openv(ct, prog, av, mode)
- CONTEXT *ct;
- char *prog;
- char **av;
- int mode;
- {
- char ch;
- int child, parent;
- int pfd[2];
-
- if (!ct || !prog || !av)
- return NULL;
-
- if (mode == O_RDONLY)
- child = 1, parent = 0;
- else if (mode == O_WRONLY)
- child = 0, parent = 1;
- else
- {
- error("in ct_open: invalid mode");
- return -1;
- }
-
- /* We can't have more than one child at a time. */
-
- if (child_pid >= 0)
- {
- error("in ct_open: a process is already open");
- return -1;
- }
-
- /* Make a stab at predicting uid-related failure. */
-
- if (! ok_context(eff_uid, real_uid, real_gid, ct))
- {
- error("in ct_open: no permissions to become %s", ct->ct_name);
- return -1;
- }
-
- /* Pipes? Like, tubular, fer shur! */
-
- if (pipe(pfd) == -1)
- {
- syserr("can't create a pipe");
- return -1;
- }
-
- /* Generate a debugging message. */
-
- if (verbose)
- {
- int a;
-
- message("%s: spawning", progname);
- for (a = 0; av[a]; ++a)
- message(" %s", av[a]);
- message("\n");
- }
-
- /* Handle the child case */
-
- if (sfork() == 0)
- {
- if (child == 0)
- {
- (void) close(0);
- (void) dup(pfd[0]); /* ass_u_me 0 */
- }
- else
- {
- (void) close(0);
- if (open("/dev/null", O_RDONLY) != 0)
- {
- /* This should _never_ happen, but... */
- syserr("can't open /dev/null");
- (void) dup(1); /* ass_u_me 0 */
- }
-
- (void) close(1);
- (void) dup(pfd[1]); /* ass_u_me 1 */
- }
-
- if (become(ct, TRUE) < 0)
- (void) write(pfd[1], "n", 1);
- else
- {
- int t;
-
- (void) write(pfd[1], "y", 1);
-
- (void) close(pfd[child]);
- (void) close(pfd[parent]);
- for (t = 0; t < T_MAX; ++t)
- (void) close(tfd[t]);
- if (log)
- (void) fclose(log);
- if (errlog)
- (void) fclose(errlog);
-
- (void) execv(prog, av);
- syserr("can't execute %s", prog);
- }
-
- exit(127);
- }
-
- /* Make sure that a broken pipe won't kill us */
-
- saved_sigpipe = signal(SIGPIPE, SIG_IGN);
-
- /* The child must report "OK" before we continue. */
-
- if ((read(pfd[0], &ch, 1) < 1) || (ch != 'y'))
- {
- (void) close(pfd[0]);
- (void) close(pfd[1]);
- (void) await_child();
- return -1;
- }
-
- (void) close(pfd[child]);
- return pfd[parent];
- }
-
- /*----------------------------------------------------------------------
- * Close the file descriptor opened by ct_open().
- */
-
- ct_close(fd)
- int fd;
- {
- if (fd != -1)
- (void) close(fd);
- return await_child();
- }
-
- /*----------------------------------------------------------------------
- * Assume the identity of the given user.
- */
-
- int
- become(ct, chd)
- CONTEXT *ct;
- int chd;
- {
- char env_path[sizeof(SAFEPATH) + 8];
-
- /*
- * Assume a new identity.
- * Note the importance of doing the setgid() before the setuid().
- */
-
- if (setgid(ct->ct_gid) == -1)
- {
- syserr("can't setgid to %d", ct->ct_gid);
- return -1;
- }
- if (setuid(ct->ct_uid) == -1)
- {
- syserr("can't setgid to %u", ct->ct_uid);
- return -1;
- }
- if (chd && chdir(ct->ct_home) == -1)
- {
- syserr("can't chdir to %s", ct->ct_home);
- return -1;
- }
-
- /* Set up the environment */
-
- env_path[0] = '\0';
- if (ct->ct_uid == 0)
- (void) strcat(env_path, "/etc:");
- (void) strcat(env_path, SAFEPATH);
-
- alloc_env("HOME", ct->ct_home);
- alloc_env("PATH", env_path);
-
- /* I guess it worked. */
-
- return 0;
- }
-
- /*----------------------------------------------------------------------
- * Safe fork. If it doesn't work, it exits.
- */
-
- int
- sfork()
- {
- int tries;
-
- /*
- * A few safety measures.
- */
-
- (void) await_child();
- (void) fflush(stdout);
- (void) fflush(stderr);
- if (log)
- (void) fflush(log);
- if (errlog)
- (void) fflush(errlog);
-
- /*
- * Be patient in waiting for a fork().
- */
-
- for (tries = 0; tries < 10; ++tries)
- {
- if (tries)
- snooze(3);
- if ((child_pid = fork()) >= 0)
- return child_pid;
- if (errno != EAGAIN)
- break;
- }
-
- syserr("can't fork");
- leave(1);
- /* NOTREACHED */
- }
-
- /*----------------------------------------------------------------------
- * Wait for our child (if any) to exit.
- * Returns child's exit status or -1 if there is a problem.
- */
-
- int
- await_child()
- {
- int wpid, st;
-
- if (child_pid < 0)
- return -1;
-
- while ((wpid = wait(&st)) >= 0)
- {
- if (wpid == child_pid)
- break;
- }
-
- child_pid = -1;
- if (wpid == -1)
- syserr("waiting for child");
-
- (void) signal(SIGPIPE, saved_sigpipe);
- saved_sigpipe = SIG_DFL;
-
- if (wpid == -1)
- return -1;
-
- if (st & 0xFF)
- {
- error("child process died%s due to signal %d.",
- ((st & 0x80) ? " and dumped core" : ""),
- (st & 0x7F));
- return -1;
- }
-
- if (verbose)
- {
- message("%s: child process exited with status %d.\n",
- progname, (st >> 8) & 0xFF);
- }
-
- return ((st >> 8) & 0xFF);
- }
-