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

I am having a spot of trouble getting socket communications to work on a Solaris 5.9 system. I don't think the code is actually the problem, since I can get it to work on other servers. Any help would be greatly appreciated. I have boiled the code down for simplicity's sake:
#!/usr/bin/perl -w use IO::Socket; print "Receiver...\n"; my $sock = new IO::Socket::INET ( LocalHost => 'myhost', LocalPort => '7070', Proto => 'tcp', Listen => 1, Reuse => 1, ); die "Could not create socket: $!\n" unless $sock; my $new_sock = $sock->accept(); while(<$new_sock>) { print $_; } close($sock);
When I run it the code, I get the following response:
Receiver... Could not create socket: Bad file number
Are there Unix settings that need to be in place for Socket communications to work properly (i.e. could that port be blocked)? Several comments have revolved around the port (7070). As you can see from the listing below, 7070 is not in /etc/services, and netstat does not show any activity on port 7070.
smeau407:db1010:/export/home/oracle> -> grep 7070 /etc/services smeau407:db1010:/export/home/oracle> -> netstat -an | grep 7070 smeau407:db1010:/export/home/oracle> -> telnet localhost 7070 Trying 127.0.0.1... telnet: Unable to connect to remote host: Connection refused
I have reduced the code to essentially a null file (shebang line only):
#!/usr/bin/perl -w
Then ran a truss on it. Two interesting things appeared, which I do not understand well enough to explain. So, with your kind permission, I include excerpts from the results of truss, and ask for expert opinion:
truss ./mtb_receive.pl execve("/usr/perl5/5.6.1/bin/perl", 0xFFBFFA94, 0xFFBFFAA4) argc = 3 resolvepath("/usr/lib/ld.so.1", "/usr/lib/ld.so.1", 1023) = 16 resolvepath("/usr/perl5/5.6.1/bin/perl", "/usr/perl5/5.6.1/bin/perl", +1023) = 25 stat("/usr/perl5/5.6.1/bin/perl", 0xFFBFF868) = 0 open("/var/ld/ld.config", O_RDONLY) Err#2 ENOENT stat("/usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 0xFF +BFF370) = 0 resolvepath("/usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1 +", "/usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 1023) + = 57 : : fstat64(3, 0xFFBFED40) = 0 ioctl(3, TCGETA, 0xFFBFEE24) Err#25 ENOTTY read(3, " # ! / u s r / b i n / p".., 8192) = 1358 read(3, 0x0002E64C, 8192) = 0 llseek(3, 0, SEEK_CUR) = 1358 close(3) = 0 _exit(0)
One of our programmers discovered that LDAP may not be set up properly on this server. Notice the error from the open (Err#2 ENOENT), and the error from ioctl (Err#25 ENOTTY). The file from the open command (/var/ld/ld.config) does not exist. Could that be contributing to problems with sockets? UPDATE (8/31/2007): It turns out that LDAP was the culprit! The SA stopped the LDAP client, and voila! The original script started working like a champ. Thanks to all for your very helpful suggestions. As usual, your knowledge and willingness to share have helped me explore areas that I hadn't been exposed to previously -- I have learned a lot.

Replies are listed 'Best First'.
Re: Could not create socket: Bad file number
by dwm042 (Priest) on Aug 30, 2007 at 20:51 UTC
    This minor modification of your code worked. I set the server to 'localhost' and used a number rather than a string for the port number.

    #!/usr/bin/perl -w use IO::Socket; print "Receiver...\n"; my $sock = new IO::Socket::INET ( LocalHost => 'localhost', LocalPort => 7070, Proto => 'tcp', Listen => 1, Reuse => 1, ); die "Could not create socket: $!\n" unless $sock; my $new_sock = $sock->accept(); while(<$new_sock>) { print $_; } close($sock);
    The results were:

    C:\Code>perl socket.pl Receiver... Testing This is a test by telnetting to port 7070 C:\Code>
      Actually, the LocalHost line should be removed entirely unless there is a desire to bind to a particular interface (which is very rare). That shouldn't cause that problem, though.
        Exactly. Just to emphasize; you (usally) only need the LocalHost argument if you have multiple network connectors on your machine and you only want to listen on one of them.

        If you *do* need to do this, it may be easier to specify the IP address instead of the host name, since that may be easier than configuring the right hostname(s) to the right port(s)

      Thanks for the excellent suggestions, but no luck yet. I checked "netstat -a | grep 7070" and got no results. I tried using 7070 instead of '7070' -- no change. I switched the real name of the host with 'myhost' just in case my company has some privacy rule I'm not aware of. The actual code does in fact use the actual name of the server. I'm still trying to figure out what YMMY stands for, :).
        If port 7070 is defined in the /etc/services file on your host, you will need to use "netstat -an | grep 7070" to match or use telnet to check with the command "telnet localhost 7070". It should not make a connection. Locally port 7070 is used for arcp (don't know what that is) but there is an entry in /etc/services for port 7070.
Re: Could not create socket: Bad file number
by almut (Canon) on Aug 30, 2007 at 22:11 UTC

    To narrow down on the issue, I would run the program under strace to figure out which system call is actually failing...

    On my machine (where your (slightly adapted) code does work), the last lines I get are

    ... socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfa99308) = -1 EINVAL (Inval +id argument) _llseek(3, 0, 0xbfa99350, SEEK_CUR) = -1 ESPIPE (Illegal seek) ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfa99308) = -1 EINVAL (Inval +id argument) _llseek(3, 0, 0xbfa99350, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl64(3, F_SETFD, FD_CLOEXEC) = 0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(3, {sa_family=AF_INET, sin_port=htons(7070), sin_addr=inet_addr(" +0.0.0.0")}, 16) = 0 listen(3, 1) = 0 accept(3,

    It could be one of fcntl, setsockopt, bind or listen failing (all may potentially set EBADF). Then, depending on which one it is, look in the respective man page. If you're lucky, you might find something resembling an explanation...

      Thanks for the "strace" suggestion. I've never used it before, and there's no man-page for it, so I probably got the syntax wrong. Here's what I tried:
      -> strace perl receive.pl ERROR: unable to open /dev/log
      I also tried a 'set -x', but that returned the same results I was getting before, no detail.

        Which platform are you on? The strace I was referring to is a Linux tool — on some other unixes, the program named strace is an entirely different tool...

        [ Update: actually, that's exactly the error message you get when you try to run strace (STREAMS trace) on Solaris as a regular user (because the device that /dev/log points to is owned by root:sys). On HP-UX and AIX I'm getting different error messages — but YMMV.  So I supppose what you want is truss (usage for simple things is essentially the same as strace under Linux). ]

        A couple of weeks ago, I posted a list of corresponding tools for other platforms... (if the respective tool you want is not installed already, you should be able to download it from the net).

        (BTW, you called it correctly.)

Re: Could not create socket: Bad file number
by Joost (Canon) on Aug 30, 2007 at 20:45 UTC
    AFAIK, IO::Socket::INET->new() does not set $!, which would mean the error message you get from $! is undetermined (I get 'invalid argument', but YMMV)

    Secondly, unless the machine you're running this on really has the hostname 'myhost' it will fail.

    Removing the LocalHost parameter or setting the value to 'localhost' works for me.

      AFAIK, IO::Socket::INET->new() does not set $!,

      Quite the opposite. It's the only module I know that does set $! even for errors not originating from system calls. That could account for the error message mismatch.

        Well, if it does, I can't find any mention of that behaviour in the documentation. The only thing that I can find that even mentions $! is IO::Handle's blocking() method.

        update: however, reading the source code shows that you're right. But I can't really say if it will always produce a useful error in $! though. Sometimes it sets $! to $EINVAL, other times it just passes on $! from functions that may or may not set $! on failure. (And some build in functions that do set $! are not documented as such in the perldocs, but that's another story)

      AFAIK, IO::Socket::INET->new() does not set $!

      It's poorly documented, but IO::Socket and its INET subclass put their informative errors into $@. So, the OP wants something like:

      my $s = IO::Socket::INET->new(...) or die "new sock: $@\n";

      -pilcrow

Re: Could not create socket: Bad file number
by bruceb3 (Pilgrim) on Aug 30, 2007 at 20:48 UTC
    And to add to Joost's comments - use the netstat command to make sure that there isn't already a process listening on port 7070.