[macruby-changes] [1209] MacRuby/branches/experimental/io.c

source_changes at macosforge.org source_changes at macosforge.org
Fri Mar 27 12:35:10 PDT 2009


Revision: 1209
          http://trac.macosforge.org/projects/ruby/changeset/1209
Author:   pthomson at apple.com
Date:     2009-03-27 12:35:10 -0700 (Fri, 27 Mar 2009)
Log Message:
-----------
Reimplemented IO.popen() such that it sets the #pid variable correctly.

Modified Paths:
--------------
    MacRuby/branches/experimental/io.c

Modified: MacRuby/branches/experimental/io.c
===================================================================
--- MacRuby/branches/experimental/io.c	2009-03-27 19:17:09 UTC (rev 1208)
+++ MacRuby/branches/experimental/io.c	2009-03-27 19:35:10 UTC (rev 1209)
@@ -24,6 +24,7 @@
 #include <sys/select.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <paths.h>
 #include <fcntl.h>
 
 #include <sys/stat.h>
@@ -91,6 +92,132 @@
 //     return S_ISSOCK(sbuf.st_mode);
 // }
 
+// This was copied wholesale from an implementation of unistd I found.
+// I am not sure that the usage of vfork here is correct, as on some systems
+// vfork is not at all thread-safe.
+
+static struct pid {
+	struct pid *next;
+	FILE *fp;
+	pid_t pid;
+} *pidlist;
+
+FILE *
+macruby_popen(const char *program, const char *type, pid_t *new_pid)
+{
+	struct pid * volatile cur;
+	FILE *iop;
+	int pdes[2];
+	pid_t pid;
+
+	if ((*type != 'r' && *type != 'w') || type[1] != '\0') {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	if ((cur = malloc(sizeof(struct pid))) == NULL)
+		return (NULL);
+
+	if (pipe(pdes) < 0) {
+		free(cur);
+		return (NULL);
+	}
+
+	switch (pid = vfork()) {
+		case -1:                        /* Error. */
+		(void)close(pdes[0]);
+		(void)close(pdes[1]);
+		free(cur);
+		return (NULL);
+				/* NOTREACHED */
+		case 0:                         /* Child. */
+		{
+			struct pid *pcur;
+				/*
+			* because vfork() instead of fork(), must leak FILE *,
+				* but luckily we are terminally headed for an execl()
+				*/
+				for (pcur = pidlist; pcur; pcur = pcur->next)
+				close(fileno(pcur->fp));
+
+			if (*type == 'r') {
+				int tpdes1 = pdes[1];
+
+				(void) close(pdes[0]);
+						/*
+				* We must NOT modify pdes, due to the
+					* semantics of vfork.
+						*/
+				if (tpdes1 != STDOUT_FILENO) {
+					(void)dup2(tpdes1, STDOUT_FILENO);
+					(void)close(tpdes1);
+					tpdes1 = STDOUT_FILENO;
+				}
+			} else {
+				(void)close(pdes[1]);
+				if (pdes[0] != STDIN_FILENO) {
+					(void)dup2(pdes[0], STDIN_FILENO);
+					(void)close(pdes[0]);
+				}
+			}
+			execl(_PATH_BSHELL, "sh", "-c", program, (char *)NULL);
+			_exit(127);
+				/* NOTREACHED */
+		}
+	}
+
+		/* Parent; assume fdopen can't fail. */
+	if (*type == 'r') {
+		iop = fdopen(pdes[0], type);
+		(void)close(pdes[1]);
+	} else {
+		iop = fdopen(pdes[1], type);
+		(void)close(pdes[0]);
+	}
+
+		/* Link into list of file descriptors. */
+	cur->fp = iop;
+	cur->pid =  pid;
+	cur->next = pidlist;
+	pidlist = cur;
+	
+	if (new_pid != NULL) {
+		*new_pid = pid;
+	}
+	
+	return iop;
+}
+
+int 
+macruby_pclose(FILE *iop)
+{
+	struct pid *cur, *last;
+	int pstat;
+	pid_t pid;
+
+	/* Find the appropriate file pointer. */
+	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
+		if (cur->fp == iop)
+		break;
+
+	if (cur == NULL)
+		return (-1);
+
+	(void)fclose(iop);
+
+	do {
+		pid = waitpid(cur->pid, &pstat, 0);
+	} while (pid == -1 && errno == EINTR);
+
+	/* Remove the entry from the linked list. */
+	if (last == NULL
+		pidlist = cur->next;
+	else
+		last->next = cur->next;
+	free(cur);
+	return (pid == -1 ? -1 : pstat);
+}
+
 static int
 convert_mode_string_to_fmode(VALUE rstr)
 {
@@ -358,6 +485,8 @@
     io_struct->sync = mode & FMODE_SYNC;
 }
 
+int macruby_pclose(FILE *iop);
+
 static void
 io_struct_close(rb_io_t *io_struct, bool close_read, bool close_write)
 {
@@ -368,7 +497,7 @@
 	CFWriteStreamClose(io_struct->writeStream);
     }
     if (io_struct->fp != NULL) {
-	const int status = pclose(io_struct->fp);
+	const int status = macruby_pclose(io_struct->fp);
 	io_struct->fp = NULL;
 	// TODO find out the real pid instead of passing -1
 	rb_last_status_set(status, -1);
@@ -1864,18 +1993,20 @@
 rb_io_s_popen(VALUE klass, SEL sel, int argc, VALUE *argv)
 {
     VALUE process_name, mode;
+	pid_t popen_pid;
     rb_scan_args(argc, argv, "11", &process_name, &mode);
     if (NIL_P(mode)) {
         mode = (VALUE)CFSTR("r");
     }
     
-    FILE *process = popen(StringValueCStr(process_name), RSTRING_PTR(mode));
+    FILE *process = macruby_popen(StringValueCStr(process_name), RSTRING_PTR(mode), &popen_pid);
     if (process == NULL) {
         rb_raise(rb_eIOError, "system call to popen() failed");
     }
     
     VALUE io = prep_io(fileno(process), convert_mode_string_to_fmode(mode), klass);
     ExtractIOStruct(io)->fp = process;
+	ExtractIOStruct(io)->pid = popen_pid;
     return io;
 }
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090327/04cc841d/attachment-0001.html>


More information about the macruby-changes mailing list