[uClibc-cvs] uClibc/libc/stdio popen.c,1.8,1.9

Manuel Novoa III mjn3 at uclibc.org
Fri Jan 2 07:11:38 UTC 2004


Update of /var/cvs/uClibc/libc/stdio
In directory nail:/tmp/cvs-serv24331

Modified Files:
	popen.c 
Log Message:
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().


Index: popen.c
===================================================================
RCS file: /var/cvs/uClibc/libc/stdio/popen.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- popen.c	1 Dec 2002 23:32:17 -0000	1.8
+++ popen.c	2 Jan 2004 07:11:35 -0000	1.9
@@ -1,73 +1,197 @@
-/*
- * Modified     3/03/2001       Manuel Novoa III
+/*  Copyright (C) 2004     Manuel Novoa III
  *
- * Added check for legal mode arg.
- * Call fdopen and check return value before forking.
- * Reduced code size by using variables pr and pnr instead of array refs.
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* 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/types.h>
 #include <sys/wait.h>
-#include <errno.h>
 
 /* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
 #include <sys/syscall.h>
 #if ! defined __NR_vfork
-#define vfork fork	
+# define vfork fork	
+# define VFORK_LOCK		((void) 0)
+# define VFORK_UNLOCK	((void) 0)
 #endif
 
-FILE *popen (const char *command, const char *mode)
+#ifdef __UCLIBC_HAS_THREADS__
+#include <pthread.h>
+static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK			__pthread_mutex_lock(&mylock)
+# define UNLOCK			__pthread_mutex_unlock(&mylock);
+#else
+# define LOCK			((void) 0)
+# define UNLOCK			((void) 0)
+#endif      
+
+#ifndef VFORK_LOCK
+# define VFORK_LOCK		LOCK
+# define VFORK_UNLOCK	UNLOCK
+#endif
+
+struct 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 pid, reading;
-	int pr, pnr;
+	int parent_fd;
+	int child_fd;
+	int child_writing;			/* Doubles as the desired child fildes. */
+	pid_t pid;
 
-	reading = (mode[0] == 'r');
-	if ((!reading && (mode[0] != 'w')) || mode[1]) {
-		__set_errno(EINVAL);			/* Invalid mode arg. */
-	} else if (pipe(pipe_fd) == 0) {
-		pr = pipe_fd[reading];
-		pnr = pipe_fd[1-reading];
-		if ((fp = fdopen(pnr, mode)) != NULL) {
-			if ((pid = vfork()) == 0) {	/* vfork -- child */
-				close(pnr);
-				if (pr != reading) {
-					close(reading);
-					dup2(pr, reading);
-					close(pr);
-				}
-				execl("/bin/sh", "sh", "-c", command, (char *) 0);
-				_exit(255);		/* execl failed! */
-			} else {			/* vfork -- parent or failed */
-				close(pr);
-				if (pid > 0) {	/* vfork -- parent */
-					return fp;
-				} else {		/* vfork -- failed! */
-					fclose(fp);
-				}
-			}
-		} else {				/* fdopen failed */
-			close(pr);
-			close(pnr);
+	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;
+		LOCK;
+		pi->next = popen_list;
+		popen_list = pi;
+		UNLOCK;
+		
+		return fp;
+	}
+
+	/* If we get here, vfork failed. */
+	fclose(fp);					/* Will close parent_fd. */
+
+ FREE_PI:
+	free(pi);
+
+ RET_NULL:
 	return NULL;
 }
 
-int pclose(FILE *fd)
+int pclose(FILE *stream)
 {
-	int waitstat;
+	struct popen_list_item *p;
+	int stat;
+	pid_t pid;
 
-	if (fclose(fd) != 0) {
-		return EOF;
+	/* First, find the list entry corresponding to stream and remove it
+	 * from the list.  Set p to the list item (NULL if not found). */
+	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);
+		}
 	}
-	if (wait(&waitstat) == -1)
-		return -1;
-	return waitstat;
-}
+	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;
+}




More information about the uClibc-cvs mailing list