| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 | /* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org> * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> * * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. * * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details. *//* Jan 1, 2004 * * Rewrite popen for SUSv3 compliance. *   Added a list of popen()'d to store pids and use waitpid() in pclose(). *   Loop on waitpid() failure due to EINTR as required. *   Close parent's popen()'d FILEs in the {v}fork()'d child. *   Fix failure exit code for failed execve(). */#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <sys/wait.h>#include <bits/uClibc_mutex.h>#ifdef __UCLIBC_MJN3_ONLY__#warning "hmm... susv3 says Pipe streams are byte-oriented."#endif /* __UCLIBC_MJN3_ONLY__ *//* libc_hidden_proto(close) *//* libc_hidden_proto(_exit) *//* libc_hidden_proto(waitpid) *//* libc_hidden_proto(execl) *//* libc_hidden_proto(dup2) *//* libc_hidden_proto(fdopen) *//* libc_hidden_proto(pipe) *//* libc_hidden_proto(vfork) *//* libc_hidden_proto(fclose) *//* uClinux-2.0 has vfork, but Linux 2.0 doesn't */#include <sys/syscall.h>#if ! defined __NR_vfork# define vfork fork# define VFORK_LOCK		((void) 0)# define VFORK_UNLOCK		((void) 0)/* libc_hidden_proto(fork) */#endif#ifndef VFORK_LOCK__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);# define VFORK_LOCK		__UCLIBC_MUTEX_LOCK(mylock)# define VFORK_UNLOCK		__UCLIBC_MUTEX_UNLOCK(mylock)#endifstruct popen_list_item {	struct popen_list_item *next;	FILE *f;	pid_t pid;};static struct popen_list_item *popen_list /* = NULL (bss initialized) */;FILE *popen(const char *command, const char *modes){	FILE *fp;	struct popen_list_item *pi;	struct popen_list_item *po;	int pipe_fd[2];	int parent_fd;	int child_fd;	int child_writing;			/* Doubles as the desired child fildes. */	pid_t pid;	child_writing = 0;			/* Assume child is writing. */	if (modes[0] != 'w') {		/* Parent not writing... */		++child_writing;		/* so child must be writing. */		if (modes[0] != 'r') {	/* Oops!  Parent not reading either! */			__set_errno(EINVAL);			goto RET_NULL;		}	}	if (!(pi = malloc(sizeof(struct popen_list_item)))) {		goto RET_NULL;	}	if (pipe(pipe_fd)) {		goto FREE_PI;	}	child_fd = pipe_fd[child_writing];	parent_fd = pipe_fd[1-child_writing];	if (!(fp = fdopen(parent_fd, modes))) {		close(parent_fd);		close(child_fd);		goto FREE_PI;	}	VFORK_LOCK;	if ((pid = vfork()) == 0) {	/* Child of vfork... */		close(parent_fd);		if (child_fd != child_writing) {			dup2(child_fd, child_writing);			close(child_fd);		}		/* SUSv3 requires that any previously popen()'d streams in the		 * parent shall be closed in the child. */		for (po = popen_list ; po ; po = po->next) {			close(po->f->__filedes);		}		execl("/bin/sh", "sh", "-c", command, (char *)0);		/* SUSv3 mandates an exit code of 127 for the child if the		 * command interpreter can not be invoked. */		_exit(127);	}	VFORK_UNLOCK;	/* We need to close the child filedes whether vfork failed or	 * it succeeded and we're in the parent. */	close(child_fd);	if (pid > 0) {				/* Parent of vfork... */		pi->pid = pid;		pi->f = fp;		VFORK_LOCK;		pi->next = popen_list;		popen_list = pi;		VFORK_UNLOCK;		return fp;	}	/* If we get here, vfork failed. */	fclose(fp);					/* Will close parent_fd. */ FREE_PI:	free(pi); RET_NULL:	return NULL;}#warning is pclose correct wrt the new mutex semantics?int pclose(FILE *stream){	struct popen_list_item *p;	int stat;	pid_t pid;	/* First, find the list entry corresponding to stream and remove it	 * from the list.  Set p to the list item (NULL if not found). */	VFORK_LOCK;	if ((p = popen_list) != NULL) {		if (p->f == stream) {			popen_list = p->next;		} else {			struct popen_list_item *t;			do {				t = p;				if (!(p = t->next)) {					__set_errno(EINVAL); /* Not required by SUSv3. */					break;				}				if (p->f == stream) {					t->next = p->next;					break;				}			} while (1);		}	}	VFORK_UNLOCK;	if (p) {		pid = p->pid;			/* Save the pid we need */		free(p);				/* and free the list item. */		fclose(stream);	/* The SUSv3 example code ignores the return. */		/* SUSv3 specificly requires that pclose not return before the child		 * terminates, in order to disallow pclose from returning on EINTR. */		do {			if (waitpid(pid, &stat, 0) >= 0) {				return stat;			}			if (errno != EINTR) {				break;			}		} while (1);	}	return -1;}
 |