Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1bcf17174e00
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: efbf2af718a2
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 19, 2017

  1. Verified

    This commit was signed with the committer’s verified signature.
    headius Charles Oliver Nutter
    Copy the full SHA
    5594fd4 View commit details
  2. Untag passing close_on_exec specs.

    Note that the main spec, which tests that the flag is actually set
    still does not pass because it uses a temporary file, and our
    implementation of Tempfile does not use a native channel.
    headius committed Nov 19, 2017

    Verified

    This commit was signed with the committer’s verified signature.
    headius Charles Oliver Nutter
    Copy the full SHA
    efbf2af View commit details
Showing with 97 additions and 27 deletions.
  1. +87 −18 core/src/main/java/org/jruby/RubyIO.java
  2. +10 −0 core/src/main/java/org/jruby/api/API.java
  3. +0 −9 spec/tags/ruby/core/io/close_on_exec_tags.txt
105 changes: 87 additions & 18 deletions core/src/main/java/org/jruby/RubyIO.java
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
import jnr.enxio.channels.NativeSelectableChannel;
import jnr.posix.POSIX;
import org.jcodings.transcode.EConvFlags;
import org.jruby.api.API;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.JavaSites.IOSites;
import org.jruby.runtime.callsite.RespondToCallSite;
@@ -2167,16 +2168,79 @@ public IRubyObject close_read(ThreadContext context) {
return rbIoClose(runtime);
}

@JRubyMethod(name = "close_on_exec=", notImplemented = true)
public static final int FD_CLOEXEC = 1;

@JRubyMethod(name = "close_on_exec=")
public IRubyObject close_on_exec_set(ThreadContext context, IRubyObject arg) {
// TODO: rb_io_set_close_on_exec
throw context.runtime.newNotImplementedError("close_on_exec=");
Ruby runtime = context.runtime;
POSIX posix = runtime.getPosix();
OpenFile fptr = getOpenFileChecked();
RubyIO write_io;

if (fptr.fd().chNative == null || !posix.isNative()) {
runtime.getWarnings().warning("close_on_exec is not implemented for this stream type: " + fptr.fd().ch.getClass().getSimpleName());
return context.nil;
}

int flag = arg.isTrue() ? FD_CLOEXEC : 0;
int fd, ret;

write_io = GetWriteIO();
if (this != write_io) {
fptr = write_io.getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) return API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
}
}

}

fptr = getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
}
}

return context.nil;
}

@JRubyMethod(name = "close_on_exec?", notImplemented = true)
@JRubyMethod(name = {"close_on_exec?", "close_on_exec"})
public IRubyObject close_on_exec_p(ThreadContext context) {
// TODO: rb_io_close_on_exec_p
throw context.runtime.newNotImplementedError("close_on_exec=");
Ruby runtime = context.runtime;
POSIX posix = runtime.getPosix();
OpenFile fptr = getOpenFileChecked();

if (fptr == null || fptr.fd().chNative == null
|| !posix.isNative()) {
return context.fals;
}

RubyIO write_io;
int fd, ret;

write_io = GetWriteIO();
if (this != write_io) {
fptr = write_io.getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) == 0) return context.fals;
}
}

fptr = getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) == 0) return context.fals;
}
return context.tru;
}

/** Flushes the IO output stream.
@@ -2366,15 +2430,11 @@ public void setBlocking(boolean blocking) {

@JRubyMethod(name = "fcntl")
public IRubyObject fcntl(ThreadContext context, IRubyObject cmd) {
// TODO: This version differs from ioctl by checking whether fcntl exists
// and raising notimplemented if it doesn't; perhaps no difference for us?
return ctl(context.runtime, cmd, null);
}

@JRubyMethod(name = "fcntl")
public IRubyObject fcntl(ThreadContext context, IRubyObject cmd, IRubyObject arg) {
// TODO: This version differs from ioctl by checking whether fcntl exists
// and raising notimplemented if it doesn't; perhaps no difference for us?
return ctl(context.runtime, cmd, arg);
}

@@ -2392,7 +2452,7 @@ public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
return ctl(context.runtime, cmd, arg);
}

public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
private IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
long realCmd = cmd.convertToInteger().getLongValue();
long nArg = 0;

@@ -2415,23 +2475,32 @@ public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {

OpenFile fptr = getOpenFileChecked();

// Fixme: Only F_SETFL and F_GETFL is current supported
// FIXME: Only NONBLOCK flag is supported
// This currently only supports setting two flags:
// FD_CLOEXEC on platforms where it is supported, and
// O_NONBLOCK when the stream can be set to non-blocking.

// FIXME: F_SETFL and F_SETFD are treated as the same thing here. For the case of dup(fd) we
// should actually have F_SETFL only affect one (it is unclear how well we do, but this TODO
// is here to at least document that we might need to do more work here. Mostly SETFL is
// for mode changes which should persist across fork() boundaries. Since JVM has no fork
// this is not a problem for us.
if (realCmd == FcntlLibrary.FD_CLOEXEC) {
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
// And why the hell does webrick pass this in as a first argument!!!!!
} else if (realCmd == Fcntl.F_SETFL.intValue() || realCmd == Fcntl.F_SETFD.intValue()) {
if ((nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
close_on_exec_set(runtime.getCurrentContext(), runtime.getTrue());
} else if (realCmd == Fcntl.F_SETFD.intValue()) {
if (arg != null && (nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
close_on_exec_set(runtime.getCurrentContext(), arg);
} else {
throw runtime.newNotImplementedError("F_SETFD only supports FD_CLOEXEC");
}
} else if (realCmd == Fcntl.F_GETFD.intValue()) {
return runtime.newFixnum(close_on_exec_p(runtime.getCurrentContext()).isTrue() ? FD_CLOEXEC : 0);
} else if (realCmd == Fcntl.F_SETFL.intValue()) {
if ((nArg & OpenFlags.O_NONBLOCK.intValue()) != 0) {
boolean block = (nArg & ModeFlags.NONBLOCK) != ModeFlags.NONBLOCK;

fptr.setBlocking(runtime, block);
} else {
throw runtime.newNotImplementedError("F_SETFL only supports O_NONBLOCK");
}
} else if (realCmd == Fcntl.F_GETFL.intValue()) {
return fptr.isBlocking() ? RubyFixnum.zero(runtime) : RubyFixnum.newFixnum(runtime, ModeFlags.NONBLOCK);
10 changes: 10 additions & 0 deletions core/src/main/java/org/jruby/api/API.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jruby.api;

import org.jruby.Ruby;
import org.jruby.runtime.builtin.IRubyObject;

public class API {
public static IRubyObject rb_sys_fail_path(Ruby runtime, String path) {
throw runtime.newSystemCallError("bad path for cloexec: " + path);
}
}
9 changes: 0 additions & 9 deletions spec/tags/ruby/core/io/close_on_exec_tags.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
fails:IO#close_on_exec= sets the close-on-exec flag if true
fails:IO#close_on_exec= sets the close-on-exec flag if non-false
fails:IO#close_on_exec= unsets the close-on-exec flag if false
fails:IO#close_on_exec= unsets the close-on-exec flag if nil
fails:IO#close_on_exec= ensures the IO's file descriptor is closed in exec'ed processes
fails:IO#close_on_exec= raises IOError if called on a closed IO
fails:IO#close_on_exec= returns nil
fails:IO#close_on_exec? returns true if set
fails:IO#close_on_exec? raises IOError if called on a closed IO
fails:IO#close_on_exec? returns true by default