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

I found this in Perl 5.8.8 and 5.10, but not in 5.8.7:

> perl -lwe "use strict; print qq($!)" Bad file descriptor > perl -lwe "print qq($!)" >
It's not a real problem, but I wonder why $! is set here. I had a look at strict.pm, but didn't find anything which might possibly set $! in this case.

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: "use strict" sets $!
by ig (Vicar) on Aug 19, 2009 at 11:35 UTC
    I had a look at strict.pm, but didn't find anything which might possibly set $! in this case.

    strict.pm is a file. When use strict; is compiled perl searches the directories in @INC until it finds the strict.pm file. Several system calls may be executed as it traverses @INC. Even if strict.pm is in the first directory in @INC, at least two system calls will be required to open the file and read its contents. These system calls could set errno as they succeed.

    strict.pm sets several variables. Doing so may cause perl to acquire more memory from the system. The system call (perhaps malloc) may set errno when it succeeds.

    So there are at least a few system calls involved in loading the strict module, any one of which might have set errno, causing $! to be set.

      Several system calls may be executed as it traverses @INC.

      Good point. This explains why I didn't find anything conspicuous inside strict.pm itself. Thanks for the explanation!

      -- 
      Ronald Fischer <ynnor@mm.st>
      But with 'use warnings', perl will search the same set of directories. Yet a use warnings; doesn't seem to set $!. As for the second suggestion, use warnings; sets far more variables, and $! isn't set.

        True enough. I was only pointing out that there were system calls and didn't mean to suggest that any of those I mentioned were responsible for setting errno in this case or that it was an exhaustive list.

        Investigating a little further, I find that several different versions of perl on my system (CentOS 5.3) produce the same result. Looking more closely at 5.10.1-RC1, it appears errno is being set to EBADF in PerlIO_fast_gets, with the following stack trace at the time:

        #0 PerlIO_fast_gets (f=0x826cf88) at perlio.c:1790 #1 0x08120b86 in PerlIOBuf_fill (f=0x8264724) at perlio.c:3868 #2 0x0811f42e in PerlIOBase_read (f=0x8264724, vbuf=0xbfadde57, count +=1) at perlio.c:2143 #3 0x0811f550 in PerlIO_getc (f=0x8264724) at perlio.c:5066 #4 0x080d693a in Perl_sv_gets (sv=0x8269e10, fp=0x8264724, append=0) at sv.c:6852 #5 0x0807ff22 in Perl_filter_read (idx=0, buf_sv=0x8269e10, maxlen=0) at toke.c:2955 #6 0x08080124 in S_filter_gets (sv=0x8269e10, fp=0x8264724, append=13 +6761200) at toke.c:2997 #7 0x08084d7a in Perl_yylex () at toke.c:3757 #8 0x08092368 in Perl_yyparse () at perly.c:409 #9 0x080e9918 in S_doeval (gimme=0, startop=0x0, outside=0x0, seq=0) at pp_ctl.c:2981 #10 0x080eafeb in Perl_pp_require () at pp_ctl.c:3573 #11 0x080c053e in Perl_runops_standard () at run.c:40 #12 0x0806fede in Perl_call_sv (sv=0x8269d10, flags=6) at perl.c:2717 #13 0x0807023b in Perl_call_list (oldscope=2, paramList=0x8269db0) at perl.c:5264 #14 0x0805f3e8 in S_process_special_blocks (fullname=<value optimized +out>, gv=0x8269d90, cv=0x8269d10) at op.c:5860 #15 0x08068ec9 in Perl_newATTRSUB (floor=27, o=0x826cda0, proto=0x0, attrs=0x0, block=0x826cd60) at op.c:5831 #16 0x08067e00 in Perl_utilize (aver=1, floor=27, version=0x0, idop=0x +826cb70, arg=0x0) at op.c:3878 #17 0x080944dd in Perl_yyparse () at perly.y:659 #18 0x08071e0a in S_parse_body (env=0x0, xsinit=0x805e580 <xs_init>) at perl.c:2274 #19 0x08072ad7 in perl_parse (my_perl=0x8256008, xsinit=0x805e580 <xs_ +init>, argc=3, argv=0xbfae0c64, env=0x0) at perl.c:1687 #20 0x0805e52b in main (argc=3, argv=0xbfae0c64, env=0xbfae0c74) at perlmain.c:115

        The function is:

        int PerlIO_fast_gets(PerlIO *f) { if (PerlIOValid(f) && (PerlIOBase(f)->flags & PERLIO_F_FASTGETS)) +{ const PerlIO_funcs * const tab = PerlIOBase(f)->tab; if (tab) return (tab->Set_ptrcnt != NULL); SETERRNO(EINVAL, LIB_INVARG); } else SETERRNO(EBADF, SS_IVCHAN); return 0; }

        So, system calls are not may not be the issue. It has may have something to do with Perl IO during require. It appears perl sets errno while reading strict.pm - based on observation of a successful read of the data a short time later. And I have no idea why it doesn't do the same when reading warnings.pm.

        I note in passing that there are at least about 115 (give or take a few cpp conditionals and macros) places in the perl source where errno is set and about 54 places where errno is set to EBADF.

        While I have found one place where errno is set to EBADF it is possibly not the last place where this value is set before the perl reads the value according to the print statement. Additional study would be required to confirm that this is the cause of $! being set when printed in the OP program.

Re: "use strict" sets $!
by ig (Vicar) on Aug 19, 2009 at 10:12 UTC

    IIRC $! is only in a defined state after a failed system call, though there might be a few places where perl or some module sets it to zero before a system call. If the last system call didn't fail, $! is not guaranteed to be zero - it may have any value and its value is without defined meaning.

    man 3 errno on my CentOS system says:

    The <errno.h> header file defines the integer variable errno, which is set by system calls and some library functions in the event of an error to indicate what went wrong. Its value is significant only when the call returned an error (usually -1), and a function that does succeed is allowed to change errno.

    Thus one of the possibilities you should consider is that the last system call succeeded but left errno with a non-zero value. Another possibility is that the last system call (or the last N system calls) all succeeded without changing the value of errno and it still has the value set from either the immediately preceding failed system call (which should have set it) or some subsequent successful system call. In other words, it might be quite difficult to determine which system call set it.

      If the last system call didn't fail, $! is not guaranteed to be zero
      I know that! That's why I wondered what in "use strict" might have caused a failed system call. I didn't find anything in the code of strict.pm which might have caused errno to be set. Just out of curiosity...

      -- 
      Ronald Fischer <ynnor@mm.st>
        I wondered what in "use strict" might have caused a failed system call

        Maybe you know this also but your quest for a failed system call suggests that maybe you don't or that there might be some confusion...

        The fact that $! is non-zero is not an indication that any system call failed. If you know that the last system call failed then the value of $! has meaning, otherwise its value doesn't mean anything at all.

Re: "use strict" sets $!
by cdarke (Prior) on Aug 19, 2009 at 11:33 UTC
    As perlvar says "A successful system or library call does not set the variable to zero.". Using $! without a system error is not meaningful.

    For example, the perl interpreter could try to load a .pm file (like strict.pm) from different locations in @INC. It might not find it in the first directory it searches. A failure would set errno but a success would not reset it. I don't know for a fact if that is the exact cause of this particular error but my own guess. That would explain why not using strict does not set $! - no modules have been searched for.

    Update: The difference in releases could just mean that the @INC is different and it finds the module in the first directory searched.
Re: "use strict" sets $!
by Anonymous Monk on Aug 19, 2009 at 09:21 UTC
    You might find out if you use strace, a lot of what perl does happens in C

      I'm sitting here on a Windoze platform, so I first need to look for some free strace. Maybe I'll try StraceNT...

      -- 
      Ronald Fischer <ynnor@mm.st>
      I ran a couple strace commands:
      strace -o strace.out -ff perl -lwe 'use strict; print qq($!)' strace -o strace.out.2 -ff perl -lwe 'print qq($!)'
      I ran grep ' E' on each output file to check for system calls that returned an error. (This may not find all errors.) Here is the diff. The lines starting with < are in the strace from running with use strict;.
      [chad@chad-laptop ~/x]$ diff 1 2 21,53c21,27 < stat64("/usr/local/lib/site_perl/5.10.0/i486-linux-gnu-thread-multi" +, 0xbfe70cf0) = -1 ENOENT (No such file or directory) < stat64("/usr/local/lib/site_perl/5.10.0", 0xbfe70cf0) = -1 ENOENT (N +o such file or directory) < stat64("/usr/local/lib/site_perl/i486-linux-gnu-thread-multi", 0xbfe +70cf0) = -1 ENOENT (No such file or directory) < _llseek(0, 0, 0xbfe70b60, SEEK_CUR) = -1 ESPIPE (Illegal seek) < _llseek(1, 0, 0xbfe70b60, SEEK_CUR) = -1 ESPIPE (Illegal seek) < _llseek(2, 0, 0xbfe70b60, SEEK_CUR) = -1 ESPIPE (Illegal seek) < ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfe70be8) = -1 ENOTTY (Ina +ppropriate ioctl for device) < stat64("/etc/perl/strict.pmc", 0xbfe7090c) = -1 ENOENT (No such file + or directory) < stat64("/etc/perl/strict.pm", 0xbfe70884) = -1 ENOENT (No such file +or directory) < stat64("/usr/local/lib/perl/5.10.0/strict.pmc", 0xbfe7090c) = -1 ENO +ENT (No such file or directory) < stat64("/usr/local/lib/perl/5.10.0/strict.pm", 0xbfe70884) = -1 ENOE +NT (No such file or directory) < stat64("/usr/local/share/perl/5.10.0/strict.pmc", 0xbfe7090c) = -1 E +NOENT (No such file or directory) < stat64("/usr/local/share/perl/5.10.0/strict.pm", 0xbfe70884) = -1 EN +OENT (No such file or directory) < stat64("/usr/lib/perl5/strict.pmc", 0xbfe7090c) = -1 ENOENT (No such + file or directory) < stat64("/usr/lib/perl5/strict.pm", 0xbfe70884) = -1 ENOENT (No such +file or directory) < stat64("/usr/share/perl5/strict.pmc", 0xbfe7090c) = -1 ENOENT (No su +ch file or directory) < stat64("/usr/share/perl5/strict.pm", 0xbfe70884) = -1 ENOENT (No suc +h file or directory) < stat64("/usr/lib/perl/5.10/strict.pmc", 0xbfe7090c) = -1 ENOENT (No +such file or directory) < stat64("/usr/lib/perl/5.10/strict.pm", 0xbfe70884) = -1 ENOENT (No s +uch file or directory) < stat64("/usr/share/perl/5.10/strict.pmc", 0xbfe7090c) = -1 ENOENT (N +o such file or directory) < ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfe70698) = -1 ENOTTY (Ina +ppropriate ioctl for device) < open("/usr/share/locale/en_CA.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) += -1 ENOENT (No such file or directory) < open("/usr/share/locale/en_CA.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = + -1 ENOENT (No such file or directory) < open("/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 E +NOENT (No such file or directory) < open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = - +1 ENOENT (No such file or directory) < open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 + ENOENT (No such file or directory) < open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOE +NT (No such file or directory) < open("/usr/share/locale-langpack/en_CA.UTF-8/LC_MESSAGES/libc.mo", O +_RDONLY) = -1 ENOENT (No such file or directory) < open("/usr/share/locale-langpack/en_CA.utf8/LC_MESSAGES/libc.mo", O_ +RDONLY) = -1 ENOENT (No such file or directory) < open("/usr/share/locale-langpack/en_CA/LC_MESSAGES/libc.mo", O_RDONL +Y) = -1 ENOENT (No such file or directory) < open("/usr/share/locale-langpack/en.UTF-8/LC_MESSAGES/libc.mo", O_RD +ONLY) = -1 ENOENT (No such file or directory) < open("/usr/share/locale-langpack/en.utf8/LC_MESSAGES/libc.mo", O_RDO +NLY) = -1 ENOENT (No such file or directory) < open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) += -1 ENOENT (No such file or directory) --- > stat64("/usr/local/lib/site_perl/5.10.0/i486-linux-gnu-thread-multi" +, 0xbfb1b1a0) = -1 ENOENT (No such file or directory) > stat64("/usr/local/lib/site_perl/5.10.0", 0xbfb1b1a0) = -1 ENOENT (N +o such file or directory) > stat64("/usr/local/lib/site_perl/i486-linux-gnu-thread-multi", 0xbfb +1b1a0) = -1 ENOENT (No such file or directory) > _llseek(0, 0, 0xbfb1b010, SEEK_CUR) = -1 ESPIPE (Illegal seek) > _llseek(1, 0, 0xbfb1b010, SEEK_CUR) = -1 ESPIPE (Illegal seek) > _llseek(2, 0, 0xbfb1b010, SEEK_CUR) = -1 ESPIPE (Illegal seek) > ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfb1b098) = -1 ENOTTY (Ina +ppropriate ioctl for device)
      Maybe the difference is related to the language settings or the ioctl on file descriptor 4. The strace output shows:
      open("/usr/share/perl/5.10/strict.pm", O_RDONLY|O_LARGEFILE) = 4 ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfe70698) = -1 ENOTTY (Inapp +ropriate ioctl for device) _llseek(4, 0, [0], SEEK_CUR) = 0
      Update: I'm running perl 5.10.0 on Ubuntu 9.04.