betterworld has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

as I was playing with the syscall() function, I discovered a strange behaviour when using IO handles as parameters.

#!/usr/bin/perl use strict; use warnings; require 'syscall.ph'; open my $f, '</dev/null' or die "open: $!"; syscall(&SYS_write, 2, $f, 10) != -1 or warn "write: $!"; print STDERR "\n"; defined read($f, my $waste, 1) or die "read: $!";
This script prints:
GLOB(0x814 read() on unopened filehandle at ./script line 9. read: Bad file descriptor at ./script line 9.

Huh, perl sais that $f is an unopened filehandle, even though we opened it 3 lines ealier. Further inspection shows that the error messages will disappear if you delete the line containing syscall. Sure enough, stracing the script reveals that perl closes the file descriptor.

The IO reference $f is treated as a string and perl passes the memory address of that string to the write system call. I can't understand why that should close $f.

Is there an explanation to this or is it a bug?

(I know that the script would be pretty useless even if it "worked". I don't intend to pass IO references to syscall() in any real code. My question is just out of curiosity. Actually I discovered this when I forgot to use fileno() when passing a file descriptor to the fchmod syscall.)

Replies are listed 'Best First'.
Re: syscall() closes file descriptor
by starbolin (Hermit) on Aug 22, 2006 at 21:23 UTC

    The problem is that syscall.ph doesn't understand what you are trying to do with $f. Perl is passing a GLOB to syscall but it is not getting converted to an address. That is why your output prints "GLOB(0x814".

    Note that h2ph ( and thus, syscall.ph )does not claim to handle all structures correctly.

    From man h2ph:
    " It (h2ph) doesn't handle all C constructs, but it does attempt to isolate definitions inside evals so that you can get at the definitions that it can translate. It's only intended as a rough tool. You may need to dicker with the files produced."

    If your read your data into a buffer and pass the buffer then SYS_write works correctly.

    #!/usr/bin/perl use strict; use warnings; require 'sys/syscall.ph'; my $file = 'foobar'; my $buffer; open my $f, '<', \$file or die "open: $!"; defined read($f, $buffer, 10) or die "read: $!"; syscall(&SYS_write, fileno(STDERR), $buffer, 10) != -1 or warn "write: + $!"; print STDERR "\n"; defined read($f, $buffer, 1) or die "read: $!";

    Update: Removed extraneous DATA block.

    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}

      Actually, I think I was being too easy on your code and too harsh towards syscall. I can't think of anytime that SYS_write( id, filehandle, len) would work. Write() can not be expected to fill a buffer. Which is what you are asking it to do when you call it with a filehandle as the buffer. If you want to call write() then give it buffer. If you want something in that buffer then call read().

      Sorry this post sounds harsh. It's not meant that way.:)


      s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}

        I am not complaining about the output "GLOB(0x814". This is what one should expect when treating a file handle reference as a string. I don't expect perl to read from the file handle.

        I'm only wondering why the file handle gets closed. Nobody told perl to do that.

Re: syscall() closes file descriptor
by ikegami (Patriarch) on Aug 22, 2006 at 18:39 UTC
    I think you want fileno($f). Your system wouldn't know what to do with a Perl file handle.

    Update: I got the arguments wrong. $f is being passed as the buffer. Maybe the buffer gets overwritten during the writting process? Or even more likely, maybe something along the lines of $f = "$f"; happens? Both would close the file handle because $f would become a normal string instead of a file handle.