home *** CD-ROM | disk | FTP | other *** search
- /* $Header: procs.c,v 1.2 88/09/14 19:42:28 network Exp $
- *
- * Process management and misc support.
- *
- * $Log: procs.c,v $
- * Revision 1.2 88/09/14 19:42:28 network
- * Portability to System V and BSD.
- * General fixup.
- *
- * Revision 1.1 88/06/06 09:39:15 chip
- * Initial revision
- *
- */
-
- #include "deliver.h"
- #include <errno.h>
- #include <signal.h>
-
- /*
- * External data.
- */
-
- extern int errno;
-
- /*
- * Local data.
- */
-
- static int child_pid = -1;
- static int (*saved_sigpipe)() = SIG_DFL;
-
- /*----------------------------------------------------------------------
- * Like popen(), but execute the child in a specific context.
- * Also, the argument list is already a vector.
- */
-
- FILE *
- ct_popenv(ct, prog, av, mode)
- CONTEXT *ct;
- char *prog;
- char **av;
- char *mode;
- {
- char ch;
- int child, parent;
- int pfd[2];
-
- if (!ct || !prog || !av || !mode)
- return NULL;
-
- if (mode[0] == 'r' && mode[1] == 0)
- child = 1, parent = 0;
- else if (mode[0] == 'w' && mode[1] == 0)
- child = 0, parent = 1;
- else
- return NULL;
-
- /* We can't have more than one child at a time. */
-
- if (child_pid >= 0)
- {
- error("in ct_popen: a process is already open\n");
- return NULL;
- }
-
- /* Make a stab at predicting uid-related failure. */
-
- if (! ok_context(ct))
- {
- error("in ct_popen: no permissions to become %s\n", ct->name);
- return NULL;
- }
-
- /* Pipes? Like, tubular, fer shur! */
-
- if (pipe(pfd) == -1)
- {
- syserr("can't create a pipe");
- return NULL;
- }
-
- /* Generate a debugging message. */
-
- if (verbose)
- {
- int a;
-
- message("Spawning");
- 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]);
-
- (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 NULL;
- }
-
- (void) close(pfd[child]);
- return fdopen(pfd[parent], mode);
- }
-
- /*----------------------------------------------------------------------
- * Close the stream opened by ct_popen().
- */
-
- ct_pclose(fp)
- FILE *fp;
- {
- if (fp)
- (void) fclose(fp);
- return await_child();
- }
-
- /*----------------------------------------------------------------------
- * Assume the identity of the given user.
- */
-
- int
- become(ct, chd)
- CONTEXT *ct;
- int chd;
- {
- char env_path[32];
-
- /*
- * Assume a new identity.
- * Note the importance of doing the setgid() before the setuid().
- */
-
- if (setgid(ct->gid) == -1)
- {
- syserr("can't setgid to %d", ct->gid);
- return -1;
- }
- if (setuid(ct->uid) == -1)
- {
- syserr("can't setgid to %u", ct->uid);
- return -1;
- }
- if (chd && chdir(ct->home) == -1)
- {
- syserr("can't chdir to %s", ct->home);
- return -1;
- }
-
- /* Set up the environment */
-
- (void) sprintf(env_path, "%s:/bin:/usr/bin",
- ((ct->uid == 0) ? "/etc" : "."));
- alloc_env("HOME", 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);
-
- /*
- * 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.\n",
- ((st & 0x80) ? " and dumped core" : ""),
- (st & 0x7F));
-
- return -1;
- }
-
- if (verbose)
- {
- message("child process exited with status %d.\n",
- (st >> 8) & 0xFF);
- }
-
- return ((st >> 8) & 0xFF);
- }
-