Perl: Checking for the Existence of socket options - perl

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...

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

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.

Related

Conditional Compilation in Perl [duplicate]

This question already has answers here:
Is it possible to conditionally "use bigint" with Perl?
(3 answers)
Closed 5 years ago.
How do I get the following code to work?
use strict;
use warnings;
if ($^O eq 'MSWin32' || $^O eq 'MSWin64') {
use Win32;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
else {
print "Do not know how to do msgbox under UNIX!\n";
}
The above runs under Windows. But under UNIX, there is a compilation error as Win32 cannot be found. Replacing "use" with "require" makes things worse -- the code would fail to compile under both Windows and UNIX because the line containing MB_ICONINFORMATION is always compiled and "MB_ICONINFORMATION" would be an undeclared bare-word.
So how do I get around this problem?
Perl compiles code first to an intermediate representation, then executes it. Since the if is evaluated at runtime but the use is handled during compilation, you are not importing the module conditionally.
To fix this, there are a number of possible strategies:
conditional import with the use if pragma
conditional import with a BEGIN block
require the module
defer compilation with eval
To import a module only when a certain condition is met, you can use the if pragma:
use if $^O eq 'MSWin32', 'Win32';
You can also run code during compilation by putting it into a BEGIN block:
BEGIN {
if ($^O eq 'MSWin32') {
require Win32;
Win32->import; # probably not necessary
}
}
That BEGIN block behaves exactly the same like the above use if.
Note that we have to use require here. With a use Win32, the module would have been loaded during the compile time of the begin block, which bypasses the if. With require the module is loaded during runtime of the begin block, which is during compile time of the surrounding code.
In both these cases, the Win32 module will only be imported under Windows. That leaves the MB_ICONINFORMATION constant undefined on non-Windows systems. In this kind of code, it is better to not import any symbols. Instead, use the fully qualified name for everything and use parentheses for a function call (here: Win32::MB_ICONINFORMATION()). With that change, just using a require instead of an use if may also work.
If you need code to be run later, you can use a string-eval. However, this potentially leads to security issues, is more difficult to debug, and is often slower. For example, you could do:
if ($^O eq 'MSWin32') {
eval q{
use Win32;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
1;
} or die $#; # forward any errors
}
Because eval silences any errors by default, you must check success and possibly rethrow the exception. The 1 statement makes sure that the eval'ed code returns a true value if successful. eval returns undef if an error occurs. The $# variable holds the last error.
q{...} is alternative quoting construct. Aside from the curly braces as string delimiters it is exactly the same as '...' (single quotes).
If you have a lot of code that only works on a certain platform, using the above strategies for each snippet is tedious. Instead, create a module for each platform. E.g.:
Local/MyWindowsStuff.pm:
package Local::MyWindowsStuff;
use strict;
use warnings;
use Win32;
sub show_message {
my ($class, $title, $contents) = #_;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
1;
Local/MyPosixStuff.pm:
package Local::MyPosixStuff;
use strict;
use warnings;
sub show_message {
warn "messagebox only supported on Windows";
}
1;
Here I've written them to be usable as classes. We can then conditionally load one of these classes:
sub load_stuff {
if ($^O eq 'MSWin32') {
require Local::MyWindowsStuff;
return 'Local::MyWindowsStuff';
}
require Local::MyPosixStuff;
return 'Local::MyPosixStuff';
}
my $stuff = load_stuff();
Finally, instead of putting a conditional into your code, we invoke the method on the loaded class:
$stuff->show_message('Aloha!', 'Win32 Msgox');
If you don't want to create extra packages, one strategy is to eval a code ref:
sub _eval_or_throw { my ($code) = #_; return eval "$code; 1" or die $# }
my $show_message =
($^O eq 'MSWin32') ? _eval_or_throw q{
use Win32;
sub {
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
} : _eval_or_throw q{
sub {
warn "messagebox only supported on Windows";
}
};
Then: $show_message->() to invoke this code. This avoids repeatedly compiling the same code with eval. Of course that only matters when this code is run more than once per script, e.g. inside a loop or in a subroutine.

perl text::iconv unsupported conversion

In perl I read html pages and I make conversion to utf8 by text::iconv. But when some page has defined wrong code set for example: charset="blabla", then perl program died an printout "unsupported conversion".
I tried to set Text::Iconv->raise_error to 0 or 1 but without success, the program always died.
How to avoid program crash ?
OR how to check supported code set before conversion?
(I know read it in OS by "iconv --list", but must exist better solution (hope))
How to avoid program crash ?
perl uses eval for trapping errors:
use strict;
use warnings;
use 5.016;
use Text::Iconv;
my $source_encoding = 'blabla';
my $result_encoding = 'utf-8';
my $converter = eval {
Text::Iconv->new(
$source_encoding,
$result_encoding
);
}; #Error message gets inserted into $#
if (not $converter and $# =~ /invalid argument/i) {
say "Either the '$source_encoding' encoding or the ",
"'$result_encoding' encoding\nis not available on this system.";
}
if ($converter) { #Can new() fail in other ways?
my $result = $converter->convert('€');
if (not $result) {
say "Some characters in '$source_encoding'\n",
"are invalid in '$result_encoding'.";
}
else {
say $result;
}
}
In the [block] form, the code within the BLOCK is parsed only once--at the same time the code surrounding the eval itself was parsed--and executed within the context of the current Perl program. This form is typically used to trap exceptions more efficiently than the first (see below), while also providing the benefit of checking the code within BLOCK at compile time.
http://perldoc.perl.org/functions/eval.html
OR how to check supported code set before conversion? (I know read it
in OS by "iconv --list", but must exist better solution (hope))
What's so bad about iconv --list?
use strict;
use warnings;
use 5.016;
use Text::Iconv;
my $source_encoding = 'blabla';
my $result_encoding = 'utf-8';
my $available_encodings = `iconv --list`; #Backticks return a string.
my #encodings_arr = split /\s+/, $available_encodings;
my %encodings_set = map {lc $_ => undef} #encodings_arr;
my $source_encoding_available = exists $encodings_set{$source_encoding};
my $result_encoding_available = exists $encodings_set{$result_encoding};
if($source_encoding_available
and $result_encoding_available) {
say "Ready to convert";
}
else {
if (not $source_encoding_available) {
say "'$source_encoding' encoding not available.";
}
if (not $result_encoding_available) {
say "'$result_encoding' encoding not available.";
}
}

Why does Perl short circuit evaluation result in checking rvalue?

I don't understand why the following:
my $err = undef;
while(1){
if($err && ($err->code == 502)) {
[...]
}
[...]
eval {...}
$err = $#;
}
results in Can't call method "code" on an undefined value at ./filename.pl line 74. in perl v5.8.8.
In other words: why does perl execute the second boolean expresion, even if the OP1 && OP2 construct cannot become true because the first operand is false (undef).
even the more verbose version if( (defined $err) && ($err->code == 502)) produces the same result.
Thanks in advance :)
For information, when I run your code as follows on a Perl 5.10.1 interpreter, I get no error message at all. (This is really more a comment than an answer, only it does not fit in the comment column. It needs no upvote.)
#!/usr/bin/perl
use warnings;
use strict;
use integer;
my $err = undef;
while(1){
if($err && ($err->code == 502)) {
warn;
}
eval {1};
$err = $#;
}
If it is undef as you say, that version of Perl or your build of perl is buggy if you get that behaviour from that code. Neither is likely.
But it might not be undef. Keep in mind that the error might not come from the first pass of the loop. $err might contain an object with buggy overloaded operators (e.g. inconsistent boolean and stringification overloads). What does the following show?
use Devel::Peek;
Dump($err);
The reason why $err ($#) was obviously not undef after the eval{} was that a spawned function within the eval returned an error beacause of its disability to read from a file which wasn't caught by the sourrounding code :(
Thanks #ikegami for the suggestion, I got the trace using Dump after all.
The solution for the problem therefore is outside of the democode, sorry for that. I just had to create the file...

Perl: $SIG{__DIE__}, eval { } and stack trace

I have a piece of Perl code somewhat like the following (strongly simplified): There are some levels of nested subroutine calls (actually, methods), and some of the inner ones do their own exception handling:
sub outer { middle() }
sub middle {
eval { inner() };
if ( my $x = $# ) { # caught exception
if (ref $x eq 'ARRAY') {
print "we can handle this ...";
}
else {
die $x; # rethrow
}
}
}
sub inner { die "OH NOES!" }
Now I want to change that code so that it does the following:
print a full stack trace for every exception that "bubbles up" all the way to the outermost level (sub outer). Specifically, the stack trace should not stop at the first level of "eval { }".
Not having to change the the implementation of any of the inner levels.
Right now, the way I do this is to install a localized __DIE__ handler inside the outer sub:
use Devel::StackTrace;
sub outer {
local $SIG{__DIE__} = sub {
my $error = shift;
my $trace = Devel::StackTrace->new;
print "Error: $error\n",
"Stack Trace:\n",
$trace->as_string;
};
middle();
}
[EDIT: I made a mistake, the code above actually doesn't work the way I want, it actually bypasses the exception handling of the middle sub. So I guess the question should really be: Is the behaviour I want even possible?]
This works perfectly, the only problem is that, if I understand the docs correctly, it relies on behaviour that is explicitly deprecated, namely the fact that __DIE__ handlers are triggered even for "die"s inside of "eval { }"s, which they really shouldn't. Both perlvar and perlsub state that this behaviour might be removed in future versions of Perl.
Is there another way I can achieve this without relying on deprecated behaviour, or is it save to rely on even if the docs say otherwise?
UPDATE: I changed the code to override die globally so that exceptions from other packages can be caught as well.
Does the following do what you want?
#!/usr/bin/perl
use strict;
use warnings;
use Devel::StackTrace;
use ex::override GLOBAL_die => sub {
local *__ANON__ = "custom_die";
warn (
'Error: ', #_, "\n",
"Stack trace:\n",
Devel::StackTrace->new(no_refs => 1)->as_string, "\n",
);
exit 1;
};
use M; # dummy module to functions dying in other modules
outer();
sub outer {
middle( #_ );
M::n(); # M::n dies
}
sub middle {
eval { inner(#_) };
if ( my $x = $# ) { # caught exception
if (ref $x eq 'ARRAY') {
print "we can handle this ...";
}
else {
die $x; # rethrow
}
}
}
sub inner { die "OH NOES!" }
It is not safe to rely on anything that the documentation says is deprecated. The behavior could (and likely will) change in a future release. Relying on deprecated behavior locks you into the version of Perl you're running today.
Unfortunately, I don't see a way around this that meets your criteria. The "right" solution is to modify the inner methods to call Carp::confess instead of die and drop the custom $SIG{__DIE__} handler.
use strict;
use warnings;
use Carp qw'confess';
outer();
sub outer { middle(#_) }
sub middle { eval { inner() }; die $# if $# }
sub inner { confess("OH NOES!") }
__END__
OH NOES! at c:\temp\foo.pl line 11
main::inner() called at c:\temp\foo.pl line 9
eval {...} called at c:\temp\foo.pl line 9
main::middle() called at c:\temp\foo.pl line 7
main::outer() called at c:\temp\foo.pl line 5
Since you're dieing anyway, you may not need to trap the call to inner(). (You don't in your example, your actual code may differ.)
In your example you're trying to return data via $#. You can't do that. Use
my $x = eval { inner(#_) };
instead. (I'm assuming this is just an error in simplifying the code enough to post it here.)
Note that overriding die will only catch actual calls to die, not Perl errors like dereferencing undef.
I don't think the general case is possible; the entire point of eval is to consume errors. You MIGHT be able to rely on the deprecated behavior for exactly this reason: there's no other way to do this at the moment. But I can't find any reasonable way to get a stack trace in every case without potentially breaking whatever error-handling code already exists however far down the stack.

How can I determine if a Perl function exists at runtime?

I'm working on a test framework in Perl. As part of the tests, I may need to add precondition or postcondition checks for any given test, but not necessarily for all of them. What I've got so far is something like:
eval "&verify_precondition_TEST$n";
print $# if $#;
Unfortunately, this outputs "Undefined subroutine &verify_precondition_TEST1 called at ..." if the function does not exist.
How can I determine ahead of time whether the function exists, before trying to call it?
Package::Name->can('function')
or
*Package::Name::function{CODE}
# or no strict; *{ "Package::Name::$function" }{CODE}
or just live with the exception. If you call the function in an eval and $# is set, then you can't call the function.
Finally, it sounds like you may want Test::Class instead of writing this yourself.
Edit: defined &function_name (or the no strict; defined &{ $function_name } variant), as mentioned in the other answers, looks to be the best way. UNIVERSAL::can is best for something you're going to call as a method (stylistically), and why bother messing around with the symbol table when Perl gives you syntax to do what you want.
Learning++ :)
sub function_exists {
no strict 'refs';
my $funcname = shift;
return \&{$funcname} if defined &{$funcname};
return;
}
if (my $subref = function_exists("verify_precondition_TEST$n") {
...
}
With defined:
if (eval "defined(&verify_precondition_TEST$n)") {
eval "&verify_precondition_TEST$n";
print $# if $#;
}
else {
print "verify_precondition_TEST$n does not exist\n";
}
EDIT: hmm, I only thought of eval as it was in the question but with symbolic references brought up with Leon Timmermans, couldn't you do
if (defined(&{"verify_precondition_TEST$n"}) {
&{"verify_precondition_TEST$n"};
print $# if $#;
}
else {
print "verify_precondition_TEST$n does not exist\n";
}
even with strict?
I had used Leon's approach, but when I had multiple packages, it failed. I'm not sure precisely why; I think it relates to the propagation of scope between namespaces. This is the solution I came up with.
my %symbols = ();
my $package = __PACKAGE__; # bring it in at run-time
{
no strict;
%symbols = %{$package . "::"}; #S ee Symbol Tables on perlmod
}
print "$funcname not defined\n" if (!defined($symbols{$funcname});
References:
__PACKAGE__ reference on the perlmod page.
Packages/__PACKAGE__reference on Perl Training Australia.