Plan 9 from Bell Labs’s /usr/web/sources/contrib/fst/popen.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * 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 */

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.