/* * popen, pclose - open and close pipes (Plan 9 version) */ #include #include 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 */