[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