/*
* popen, pclose - open and close pipes (Plan 9 version)
*/
#include <u.h>
#include <libc.h>
enum { Stdin, Stdout, };
enum {
Rd,
Wr,
Maxfd = 200,
};
typedef struct {
long pid;
char *sts;
char stsset; /* flag: sts is valid */
} Pipe;
static Pipe pipes[Maxfd];
static int
_pipefd(int rfd, int wfd)
{
close(wfd);
return rfd;
}
int
popen(char *file, char *mode)
{
int pipedes[2];
long pid;
if (pipe(pipedes) < 0) /* cat's got the last pipe */
return -1;
if ((pid = fork()) < 0) { /* can't fork */
close(pipedes[Rd]);
close(pipedes[Wr]);
return -1;
}
/*
* The pipe was created and the fork succeeded.
* Now fiddle the file descriptors in both processes.
*/
if (pid == 0) { /* child process */
int sts;
/*
* If the mode is 'r', the child writes on stdout so the
* parent can read on its stdin from the child.
* If the mode is not 'r', the child reads on stdin so the
* parent can write on its stdout to the child.
*/
if (mode[0] == 'r') /* read from child */
sts = dup(pipedes[Wr], Stdout);
else /* write to child */
sts = dup(pipedes[Rd], Stdin);
if (sts < 0) /* couldn't fiddle fd's */
_exits("no pipe");
close(pipedes[Rd]);
close(pipedes[Wr]);
execl("/bin/rc", "rc", "-c", file, (char *)nil);
_exits("no /bin/rc"); /* no shell */
/* NOTREACHED */
return -1;
} else { /* parent process */
int fd;
/*
* If the mode is 'r', the parent reads on its stdin the child;
* otherwise the parent writes on its stdout to the child.
*/
if (mode[0] == 'r') /* read from child */
fd = _pipefd(pipedes[Rd], pipedes[Wr]);
else
fd = _pipefd(pipedes[Wr], pipedes[Rd]);
if (fd >= 0 && fd < Maxfd) {
Pipe *pp = pipes + fd;
pp->pid = pid; /* save fd's child's pid */
free(pp->sts);
pp->sts = nil;
pp->stsset = 0;
}
return fd;
}
}
static volatile int waiting;
static int
gotnote(void *, char *note)
{
if (strcmp(note, "interrupt") == 0)
if (waiting)
return 1; /* NCONT */
return 0; /* not a known note: NDFLT */
}
char *
pclose(int fd)
{
int pid; /* pid, wait status for some child */
Pipe *fpp, *app = nil, *spp;
static int registered;
if (fd < 0 || fd >= Maxfd)
return "fd out of range";
fpp = pipes + fd;
if (fpp->pid <= 0)
return "no child process for fd";
/*
* Ignore notes in case this process was catching them.
* Otherwise both this process and its child(ren) would
* catch these notes.
* Ideally I suppose popen should ignore the notes.
*/
if (!registered) {
atnotify(gotnote, 1);
registered = 1;
}
waiting = 1;
/*
* Wait for fd's child to die.
*/
close(fd);
while (!fpp->stsset) {
Waitmsg *wm = wait();
if (wm == nil)
break; /* ``can't happen'' */
pid = wm->pid;
/*
* ``Bring out your dead!''
* See if any fd is attached to this corpse;
* if so, give that fd its wait status.
*/
if (pid == fpp->pid) /* quick check */
app = fpp;
else
for (spp = pipes; spp < pipes + Maxfd; spp++)
if (pid == app->pid) {
app = spp;
break;
}
if (app != nil) {
/* record pid's status, possibly for later use */
free(app->sts);
app->sts = strdup(wm->msg);
app->stsset = 1;
}
}
waiting = 0;
return fpp->stsset? fpp->sts: "no open pipe";
}
/* Written by geoff@collyer.net for Rangboom - fst 11/11/07 */
|