File:  [ELWIX - Embedded LightWeight unIX -] / libelwix / src / pio.c
Revision 1.1.2.2: download - view: text, annotated - select for diffs - revision graph
Thu Dec 5 15:04:20 2013 UTC (10 years, 5 months ago) by misho
Branches: elwix2_6
fix support

#include "global.h"


extern char **environ;

pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist);
static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;

#define	THREAD_LOCK()	if (__isthreaded) pthread_mutex_lock(&pidlist_mutex)
#define	THREAD_UNLOCK()	if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex)


/*
 * e_popen() - ELWIX replacement of standard popen
 *
 * @command = command
 * @type = type
 * @ppid = return pid of child program
 * return: NULL error or !=NULL open program
 */
FILE *
e_popen(const char *command, const char *type, pid_t *ppid)
{
	struct tagPIOPID *cur, *p;
	FILE *iop;
	int pdes[2], pid, twoway, cloexec;
	char *argv[4];

	cloexec = strchr(type, 'e') != NULL;
	/*
	 * Lite2 introduced two-way popen() pipes using _socketpair().
	 * FreeBSD's pipe() is bidirectional, so we use that.
	 */
	if (strchr(type, '+')) {
		twoway = 1;
		type = "r+";
	} else  {
		twoway = 0;
		if ((*type != 'r' && *type != 'w') ||
		    (type[1] && (type[1] != 'e' || type[2])))
			return (NULL);
	}
	if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0)
		return (NULL);

	if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
		close(pdes[0]);
		close(pdes[1]);
		return (NULL);
	}

	argv[0] = "sh";
	argv[1] = "-c";
	argv[2] = (char *)command;
	argv[3] = NULL;

	THREAD_LOCK();
	switch (pid = vfork()) {
	case -1:			/* Error. */
		THREAD_UNLOCK();
		close(pdes[0]);
		close(pdes[1]);
		e_free(cur);
		return (NULL);
		/* NOTREACHED */
	case 0:				/* Child. */
		if (*type == 'r') {
			/*
			 * The _dup2() to STDIN_FILENO is repeated to avoid
			 * writing to pdes[1], which might corrupt the
			 * parent's copy.  This isn't good enough in
			 * general, since the _exit() is no return, so
			 * the compiler is free to corrupt all the local
			 * variables.
			 */
			if (!cloexec)
				close(pdes[0]);
			if (pdes[1] != STDOUT_FILENO) {
				dup2(pdes[1], STDOUT_FILENO);
				if (!cloexec)
					close(pdes[1]);
				if (twoway)
					dup2(STDOUT_FILENO, STDIN_FILENO);
			} else if (twoway && (pdes[1] != STDIN_FILENO)) {
				dup2(pdes[1], STDIN_FILENO);
				if (cloexec)
					fcntl(pdes[1], F_SETFD, 0);
			} else if (cloexec)
				fcntl(pdes[1], F_SETFD, 0);
		} else {
			if (pdes[0] != STDIN_FILENO) {
				dup2(pdes[0], STDIN_FILENO);
				if (!cloexec)
					close(pdes[0]);
			} else if (cloexec)
				fcntl(pdes[0], F_SETFD, 0);
			if (!cloexec)
				close(pdes[1]);
		}
		SLIST_FOREACH(p, &pio_pidlist, next)
			close(fileno(p->fp));
		execve(_PATH_BSHELL, argv, environ);
		_exit(127);
		/* NOTREACHED */
	default:
		if (ppid)
			*ppid = pid;
	}
	THREAD_UNLOCK();

	/* Parent; assume fdopen can't fail. */
	if (*type == 'r') {
		iop = fdopen(pdes[0], type);
		close(pdes[1]);
	} else {
		iop = fdopen(pdes[1], type);
		close(pdes[0]);
	}

	/* Link into list of file descriptors. */
	cur->fp = iop;
	cur->pid = pid;
	THREAD_LOCK();
	SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
	THREAD_UNLOCK();

	return (iop);
}

/*
 * e_pclose() - ELWIX replacement of standard pclose
 *
 * @iop = popen handle
 * return: -1 error or !=-1 pid status
 */
int
e_pclose(FILE *iop)
{
	struct tagPIOPID *cur, *last = NULL;
	int pstat;
	pid_t pid;

	/*
	 * Find the appropriate file pointer and remove it from the list.
	 */
	THREAD_LOCK();
	SLIST_FOREACH(cur, &pio_pidlist, next) {
		if (cur->fp == iop)
			break;
		last = cur;
	}
	if (!cur) {
		THREAD_UNLOCK();
		return (-1);
	}
	if (!last)
		SLIST_REMOVE_HEAD(&pio_pidlist, next);
	else
		SLIST_REMOVE_AFTER(last, next);
	THREAD_UNLOCK();

	fclose(iop);

	do {
		pid = wait4(cur->pid, &pstat, 0, NULL);
	} while (pid == -1 && errno == EINTR);

	e_free(cur);

	return (pid == -1 ? -1 : pstat);
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>