views:

112

answers:

2

This is a continuation of my previous question:

In Perl, how can I check for the existence of Socket options without generating warnings?

If I run the following code I get the result I expect:

#!/usr/bin/perl -w
use strict;
use diagnostics;
use Socket qw(:all);

my %opts;

if ( defined( eval { SO_REUSEPORT } ) ) {
    $opts{'SO_REUSEPORT'}
        =  {opt_level =>SOL_SOCKET,opt_name=>SO_REUSEPORT,opt_print=>\&sock_str_flag};
} else {
    print "SO_REUSEPORT undefined\n";
    $opts{'SO_REUSEPORT'}
        =  {opt_level =>0,opt_name=>0,opt_print=>undef};
}
=head
# IPV6 options
if ( defined( eval { IPV6_DONTFRAG } ) ) {
    $opts{'IPV6_DONTFRAG'}
        =  {opt_level =>IPPROTO_IPV6,opt_name=>IPV6_DONTFRAG,opt_print=>\&sock_str_flag};
} else {
    print "IPV6_DONTFRAG undefined\n";
    $opts{'IPV6_DONTFRAG'}
        =  {opt_level =>0,opt_name=>0,opt_print=>undef};
}
=cut

It outputs:

anon@perl$ ./test.pl 
SO_REUSEPORT undefined

But if I uncomment the block for IPV6_DONTFRAG I get:

Bareword "IPV6_DONTFRAG" not allowed while "strict subs" in use at ./test.pl line 17.
Bareword "IPV6_DONTFRAG" not allowed while "strict subs" in use at ./test.pl line 17.

Why is one undefined bareword causing it to barf and the other not? And how can the error be propagating out of the eval { } block?

Edit

Apparently, SO_REUSEPORT is exported by Socket.pm in some manner as it's in the @EXPORT array. So apparently it's defined but using it throws an error which the eval catches.

That still doesn't explain what's going on with IPV6_DONTFRAG. I suppose I would need to define it myself and then just call getsockopt to check if it's supported...

+4  A: 

I recommend writing it this way:

if ( defined( &IPV6_DONTFRAG ) ) {
    $opts{'IPV6_DONTFRAG'}
        =  {opt_level =>IPPROTO_IPV6,opt_name=>&IPV6_DONTFRAG,opt_print=>\&sock_str_flag};
} else {
    print "IPV6_DONTFRAG undefined\n";
    $opts{'IPV6_DONTFRAG'}
        =  {opt_level =>0,opt_name=>0,opt_print=>undef};
}

Note the added ampersand in the value for opt_name, which evades constraints due to strict 'subs'.

The documentation for defined explains:

You may also use defined(&func) to check whether subroutine &func has ever been defined. The return value is unaffected by any forward declarations of &func. Note that a subroutine which is not defined may still be callable: its package may have an AUTOLOAD method that makes it spring into existence the first time that it is called—see perlsub.

For example, with SO_BROADCAST

if (defined &SO_BROADCAST) {
  print "SO_BROADCAST = ", SO_BROADCAST, "\n";
}

the output on my machine is

SO_BROADCAST = 6
Greg Bacon
So Socket.pm defines these as subroutines which return the correct constant value to pass to functions like `socket` and `getsockopt`?
Robert S. Barnes
@Robert Yes, that's the general idea. See http://perldoc.perl.org/perlsub.html#Constant-Functions
Greg Bacon
I tried it and it doesn't work with IPV6_DONTFRAG. I still get the bareword / strict subs error.
Robert S. Barnes
Greg Bacon
Robert S. Barnes
Greg Bacon
@gbacon: Take a look at this: http://perldoc.perl.org/constant.html#TECHNICAL-NOTES
Robert S. Barnes
Robert S. Barnes
A: 

With regards to the IPV6_DONTFRAG bareword issue, it looks like Perl checks for barewords at compile time, not run time, as documented here. Eval is a construct to swallow runtime errors, so it won't help you here. It's like trying to handle a syntax error in C++ by sticking the offending code in a try/catch block.

dsolimano