Search code examples
crakuopenbsdnativecall

Creating a new file descriptor from stdout with fcntl fails in files


I have a simple test file that looks like this:

use v6.c;
use NativeCall;

sub fcntl(int32, int32 --> int32) is native { * }
sub close(int32 --> int32) is native { * }

my $fd := fcntl($*OUT.native-descriptor, 0);
say $fd;
close($fd);

The file descriptor gets returned is -1, which is not what I want. But when I run the same code in a REPL, I get what I'm looking for:

> use NativeCall
Nil
> sub fcntl(int32, int32 --> int32) is native { * }
sub fcntl (int32 $, int32 $ --> int32) { #`(Sub+{Callable[int32]}+{NativeCall::Native[Sub+{Callable[int32]},Str]}|17126514527616) ... }
> sub close(int32 --> int32) is native { * }
sub close (int32 $ --> int32) { #`(Sub+{Callable[int32]}+{NativeCall::Native[Sub+{Callable[int32]},Str]}|17126514527904) ... }
> my $fd := fcntl($*OUT.native-descriptor, 0)
15
> say $fd
15
> close($fd)
0

Why doesn't fcntl create a new file descriptor in the file like it does in the REPL?

Edit: I'm running OpenBSD 6.2, Rakudo 2018.02


Solution

  • When using the F_DUPFD flag with fcntl (which was 0), OpenBSD requires that the status flags the original file descriptor had be passed on to the new one as well. So this will work:

    use v6.c;
    use NativeCall;
    
    constant F_DUPFD = 0;
    constant F_GETFD = 1;
    
    sub fcntl(int32, int32, int32 --> int32) is native { * }
    sub close(int32 --> int32) is native { * }
    
    sub MAIN() {
        my $out-fd := $*OUT.native-descriptor;
        my $flags  := fcntl($out-fd, F_GETFD, 0);
        my $fd     := fcntl($out-fd, F_DUPFD, $flags);
        say $fd; # 15
        close($fd);
    }