home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume16
/
deliver
/
part03
/
procs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-14
|
5KB
|
293 lines
/* $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);
}