41
41
import jnr .enxio .channels .NativeSelectableChannel ;
42
42
import jnr .posix .POSIX ;
43
43
import org .jcodings .transcode .EConvFlags ;
44
+ import org .jruby .api .API ;
44
45
import org .jruby .runtime .Helpers ;
45
46
import org .jruby .runtime .JavaSites .IOSites ;
46
47
import org .jruby .runtime .callsite .RespondToCallSite ;
@@ -2185,16 +2186,79 @@ public IRubyObject close_read(ThreadContext context) {
2185
2186
return rbIoClose (context );
2186
2187
}
2187
2188
2188
- @ JRubyMethod (name = "close_on_exec=" , notImplemented = true )
2189
+ public static final int FD_CLOEXEC = 1 ;
2190
+
2191
+ @ JRubyMethod (name = "close_on_exec=" )
2189
2192
public IRubyObject close_on_exec_set (ThreadContext context , IRubyObject arg ) {
2190
- // TODO: rb_io_set_close_on_exec
2191
- throw context .runtime .newNotImplementedError ("close_on_exec=" );
2193
+ Ruby runtime = context .runtime ;
2194
+ POSIX posix = runtime .getPosix ();
2195
+ OpenFile fptr = getOpenFileChecked ();
2196
+ RubyIO write_io ;
2197
+
2198
+ if (fptr .fd ().chNative == null || !posix .isNative ()) {
2199
+ runtime .getWarnings ().warning ("close_on_exec is not implemented for this stream type: " + fptr .fd ().ch .getClass ().getSimpleName ());
2200
+ return context .nil ;
2201
+ }
2202
+
2203
+ int flag = arg .isTrue () ? FD_CLOEXEC : 0 ;
2204
+ int fd , ret ;
2205
+
2206
+ write_io = GetWriteIO ();
2207
+ if (this != write_io ) {
2208
+ fptr = write_io .getOpenFileChecked ();
2209
+ if (fptr != null && 0 <= (fd = fptr .fd ().chNative .getFD ())) {
2210
+ if ((ret = posix .fcntl (fd , Fcntl .F_GETFD )) == -1 ) return API .rb_sys_fail_path (runtime , fptr .getPath ());
2211
+ if ((ret & FD_CLOEXEC ) != flag ) {
2212
+ ret = (ret & ~FD_CLOEXEC ) | flag ;
2213
+ ret = posix .fcntlInt (fd , Fcntl .F_SETFD , ret );
2214
+ if (ret == -1 ) API .rb_sys_fail_path (runtime , fptr .getPath ());
2215
+ }
2216
+ }
2217
+
2218
+ }
2219
+
2220
+ fptr = getOpenFileChecked ();
2221
+ if (fptr != null && 0 <= (fd = fptr .fd ().chNative .getFD ())) {
2222
+ if ((ret = posix .fcntl (fd , Fcntl .F_GETFD )) == -1 ) API .rb_sys_fail_path (runtime , fptr .getPath ());
2223
+ if ((ret & FD_CLOEXEC ) != flag ) {
2224
+ ret = (ret & ~FD_CLOEXEC ) | flag ;
2225
+ ret = posix .fcntlInt (fd , Fcntl .F_SETFD , ret );
2226
+ if (ret == -1 ) API .rb_sys_fail_path (runtime , fptr .getPath ());
2227
+ }
2228
+ }
2229
+
2230
+ return context .nil ;
2192
2231
}
2193
2232
2194
- @ JRubyMethod (name = "close_on_exec?" , notImplemented = true )
2233
+ @ JRubyMethod (name = { "close_on_exec?" , "close_on_exec" } )
2195
2234
public IRubyObject close_on_exec_p (ThreadContext context ) {
2196
- // TODO: rb_io_close_on_exec_p
2197
- throw context .runtime .newNotImplementedError ("close_on_exec=" );
2235
+ Ruby runtime = context .runtime ;
2236
+ POSIX posix = runtime .getPosix ();
2237
+ OpenFile fptr = getOpenFileChecked ();
2238
+
2239
+ if (fptr == null || fptr .fd ().chNative == null
2240
+ || !posix .isNative ()) {
2241
+ return context .fals ;
2242
+ }
2243
+
2244
+ RubyIO write_io ;
2245
+ int fd , ret ;
2246
+
2247
+ write_io = GetWriteIO ();
2248
+ if (this != write_io ) {
2249
+ fptr = write_io .getOpenFileChecked ();
2250
+ if (fptr != null && 0 <= (fd = fptr .fd ().chNative .getFD ())) {
2251
+ if ((ret = posix .fcntl (fd , Fcntl .F_GETFD )) == -1 ) API .rb_sys_fail_path (runtime , fptr .getPath ());
2252
+ if ((ret & FD_CLOEXEC ) == 0 ) return context .fals ;
2253
+ }
2254
+ }
2255
+
2256
+ fptr = getOpenFileChecked ();
2257
+ if (fptr != null && 0 <= (fd = fptr .fd ().chNative .getFD ())) {
2258
+ if ((ret = posix .fcntl (fd , Fcntl .F_GETFD )) == -1 ) API .rb_sys_fail_path (runtime , fptr .getPath ());
2259
+ if ((ret & FD_CLOEXEC ) == 0 ) return context .fals ;
2260
+ }
2261
+ return context .tru ;
2198
2262
}
2199
2263
2200
2264
/** Flushes the IO output stream.
@@ -2334,15 +2398,11 @@ public void setBlocking(boolean blocking) {
2334
2398
2335
2399
@ JRubyMethod (name = "fcntl" )
2336
2400
public IRubyObject fcntl (ThreadContext context , IRubyObject cmd ) {
2337
- // TODO: This version differs from ioctl by checking whether fcntl exists
2338
- // and raising notimplemented if it doesn't; perhaps no difference for us?
2339
2401
return ctl (context .runtime , cmd , null );
2340
2402
}
2341
2403
2342
2404
@ JRubyMethod (name = "fcntl" )
2343
2405
public IRubyObject fcntl (ThreadContext context , IRubyObject cmd , IRubyObject arg ) {
2344
- // TODO: This version differs from ioctl by checking whether fcntl exists
2345
- // and raising notimplemented if it doesn't; perhaps no difference for us?
2346
2406
return ctl (context .runtime , cmd , arg );
2347
2407
}
2348
2408
@@ -2360,7 +2420,7 @@ public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
2360
2420
return ctl (context .runtime , cmd , arg );
2361
2421
}
2362
2422
2363
- public IRubyObject ctl (Ruby runtime , IRubyObject cmd , IRubyObject arg ) {
2423
+ private IRubyObject ctl (Ruby runtime , IRubyObject cmd , IRubyObject arg ) {
2364
2424
long realCmd = cmd .convertToInteger ().getLongValue ();
2365
2425
long nArg = 0 ;
2366
2426
@@ -2383,23 +2443,32 @@ public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
2383
2443
2384
2444
OpenFile fptr = getOpenFileChecked ();
2385
2445
2386
- // Fixme: Only F_SETFL and F_GETFL is current supported
2387
- // FIXME: Only NONBLOCK flag is supported
2446
+ // This currently only supports setting two flags:
2447
+ // FD_CLOEXEC on platforms where it is supported, and
2448
+ // O_NONBLOCK when the stream can be set to non-blocking.
2449
+
2388
2450
// FIXME: F_SETFL and F_SETFD are treated as the same thing here. For the case of dup(fd) we
2389
2451
// should actually have F_SETFL only affect one (it is unclear how well we do, but this TODO
2390
2452
// is here to at least document that we might need to do more work here. Mostly SETFL is
2391
2453
// for mode changes which should persist across fork() boundaries. Since JVM has no fork
2392
2454
// this is not a problem for us.
2393
2455
if (realCmd == FcntlLibrary .FD_CLOEXEC ) {
2394
- // Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
2395
- // And why the hell does webrick pass this in as a first argument!!!!!
2396
- } else if (realCmd == Fcntl .F_SETFL .intValue () || realCmd == Fcntl .F_SETFD .intValue ()) {
2397
- if ((nArg & FcntlLibrary .FD_CLOEXEC ) == FcntlLibrary .FD_CLOEXEC ) {
2398
- // Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
2456
+ close_on_exec_set (runtime .getCurrentContext (), runtime .getTrue ());
2457
+ } else if (realCmd == Fcntl .F_SETFD .intValue ()) {
2458
+ if (arg != null && (nArg & FcntlLibrary .FD_CLOEXEC ) == FcntlLibrary .FD_CLOEXEC ) {
2459
+ close_on_exec_set (runtime .getCurrentContext (), arg );
2399
2460
} else {
2461
+ throw runtime .newNotImplementedError ("F_SETFD only supports FD_CLOEXEC" );
2462
+ }
2463
+ } else if (realCmd == Fcntl .F_GETFD .intValue ()) {
2464
+ return runtime .newFixnum (close_on_exec_p (runtime .getCurrentContext ()).isTrue () ? FD_CLOEXEC : 0 );
2465
+ } else if (realCmd == Fcntl .F_SETFL .intValue ()) {
2466
+ if ((nArg & OpenFlags .O_NONBLOCK .intValue ()) != 0 ) {
2400
2467
boolean block = (nArg & ModeFlags .NONBLOCK ) != ModeFlags .NONBLOCK ;
2401
2468
2402
2469
fptr .setBlocking (runtime , block );
2470
+ } else {
2471
+ throw runtime .newNotImplementedError ("F_SETFL only supports O_NONBLOCK" );
2403
2472
}
2404
2473
} else if (realCmd == Fcntl .F_GETFL .intValue ()) {
2405
2474
return fptr .isBlocking () ? RubyFixnum .zero (runtime ) : RubyFixnum .newFixnum (runtime , ModeFlags .NONBLOCK );
0 commit comments