Show error if pm library doesn't exist - perl

"Interesting" dilemma this one.
To use a lib in perl, you obviously include "use lib x" command ... but what can you do if "x.pm" doesn't exist, (or more correctly per chance deleted by server admin)
In short, I 'hide' this file in with the .htaccess and other files in the root of my account, ie below the "public_html" folder - to keep it away from prying eyes. So I can set the path in the script such as "use /home/account/x" and all is well.
In the past, I think security measures mean this file sometimes gets removed. So when the script runs, the viewer sees a glorious "500 Error"
I tried:
If (-e 'use /home/account/x'){
use 'home/account/x';
...
}
else{
print "error";
}
But all that does is say it cannot find the use command on the line below "If"
Tried:
use 'home/account/x' || die ('cannot find file')
But that syntax is wrong. Is there any simple way around this?

This is to do with when the error can be 'noticed'. use happens at compile time. Thus - you can't do many of the normal program flow things that you'd do. (There are a few exceptions).
But what you can do instead, is require which loads things at run time - at a point where you can do the sort of tests you would want.
#!/usr/bin/env perl
use strict;
use warnings;
eval { require "Not_here.pm" };
warn "Couldn't load: $#" if $#;
print "fish";
Although, you may also need to import the routines you want, after requiring the module, because use does:
BEGIN { require Module; Module->import( LIST ); }

I use string eval to check if a module exists, if I can run without it.
our $have_unac = 0;
BEGIN {
eval "use Text::Unaccent::PurePerl";
$have_unac = 1 if (!$#);
}

Related

Using filehandles in Perl to alter actively running code

I've been learning about filehandles in Perl, and I was curious to see if there's a way to alter the source code of a program as it's running. For example, I created a script named "dynamic.pl" which contained the following:
use strict;
use warnings;
open(my $append, ">>", "dynamic.pl");
print $append "print \"It works!!\\n\";\n";
This program adds the line
print "It works!!\n";
to the end of it's own source file, and I hoped that once that line was added, it would then execute and output "It works!!"
Well, it does correctly append the line to the source file, but it doesn't execute it then and there.
So I assume therefore that when perl executes a program that it loads it to memory and runs it from there, but my question is, is there a way to access this loaded version of the program so you can have a program that can alter itself as you run it?
The missing piece you need is eval EXPR. This compiles, "evaluates", any string as code.
my $string = q[print "Hello, world!";];
eval $string;
This string can come from any source, including a filehandle.
It also doesn't have to be a single statement. If you want to modify how a program runs, you can replace its subroutines.
use strict;
use warnings;
use v5.10;
sub speak { return "Woof!"; }
say speak();
eval q[sub speak { return "Meow!"; }];
say speak();
You'll get a Subroutine speak redefined warning from that. It can be supressed with no warnings "redefine".
{
# The block is so this "no warnings" only affects
# the eval and not the entire program.
no warnings "redefine";
eval q[sub speak { return "Shazoo!"; }];
}
say speak();
Obviously this is a major security hole. There is many, many, many things to consider here, too long for an answer, and I strongly recommend you not do this and find a better solution to whatever problem you're trying to solve this way.
One way to mitigate the potential for damage is to use the Safe module. This is like eval but limits what built in functions are available. It is by no means a panacea for the security issues.
With a warning about all kinds of issues, you can reload modules.
There are packages for that, for example, Module::Reload. Then you can write code that you intend to change in a module, change the source at runtime, and have it reloaded.
By hand you would delete that from %INC and then require, like
# ... change source code in the module ...
delete $INC{'ModuleWithCodeThatChages.pm'};
require ModuleWithCodeThatChanges;
The only reason I can think of for doing this is experimentation and play. Otherwise, there are all kinds of concerns with doing something like this, and whatever your goal may be there are other ways to accomplish it.
Note The question does specify a filehandle. However, I don't see that to be really related to what I see to be the heart of the question, of modifying code at runtime.
The source file isn't used after it's been compiled.
You could just eval it.
use strict;
use warnings;
my $code = <<'__EOS__'
print "It works!!\n";
__EOS__
open(my $append_fh, ">>", "dynamic.pl")
or die($!);
print($append_fh $code);
eval("$code; 1")
or die($#);
There's almost definitely a better way to achieve your end goal here. BUT, you could recursively make exec() or system() calls -- latter if you need a return value. Be sure to setup some condition or the dominoes will keep falling. Again, you should rethink this, unless it's just practice of some sort, or maybe I don't get it!
Each call should execute the latest state of the file; also be sure to close the file before each call.
i.e.,
exec("dynamic.pl"); or
my retval;
retval = system("perl dynamic.pl");
Don't use eval ever.

Is it possible to delay loading BerkeleyDB?

As suggested in a previous question, one way to delay loading modules would be to use require. I would like to delay loading BerkeleyDB until it is necessary, instead of loading it every time my application runs. It would also be nice to test if BerkeleyDB is available. Here is my attempt, which seems to work with every other module I tried (calling the script 'load_bdb.pl'):
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
my %hash;
eval {
require BerkeleyDB;
BerkeleyDB->import();
1;
} or do {
my $error = $#;
die "\nERROR: Couldn't load BerkeleyDB" if $error;
};
tie %hash, 'BerkeleyDB::Btree', -Filename => 'db_file', -Flags => DB_CREATE
or die "\nERROR: Could not open DBM file: db_file: $! $BerkeleyDB::Error\n";
This generates the following error:
Bareword "DB_CREATE" not allowed while "strict subs" in use at load_bdb.pl line 18.
Execution of load_bdb.pl aborted due to compilation errors.
Suppressing the errors does not solve the problem because I then get:
Name "BerkeleyDB::Error" used only once: possible typo at load_bdb.pl line 20.
ERROR: Could not open DBM file: db_file: No such file or directory
This suggests to me that the require and import statements above are not importing BerkeleyDB correctly, whereas this works fine with use. So, why does require not work with this particular module and is there another solution?
When you run a perl script the execution goes through several phases. One of the first is compilation. During compilation it tries to resolve words such as DB_CREATE to decide what they are. In the case where BerkelyDB has not been loaded in advance (by use) then the first time it is encountered in the tie statement, perl does not know what it is.
In this case it is actually a constant sub which will be later defined and imported, when you require BerkelyDB. Note that use is a compile time operation, while require normally occurs at run time. To avoid the error you can put a & in front of the name so perl knows it is actually a sub (eg. &DB_CREATE)
The other warning if similar. You are reading a variable which would normally belong to the BerkeleyDB package, but since you did not load it, the variable is not referenced anywhere except in the one place. That leads perl to think it might be a typo in your script.
You could resolve that by setting it to something (eg. Undef) at the top of the script.

Finding files writable by everyone using perl

I'm writing a Perl script which will scan the filesystem for files that are writable by everyone. I've looked at a couple items that have helped me. Primarily this Stonehenge.com post from 2001 and a question previously asked here. Both have directed me to most of a solution. I am able to now find files that are writable by everyone, however, I have some concerns.
For starters, my script:
#!/usr/bin/perl
use warnings;
use strict;
use File::Find;
use Sys::Hostname;
my $hostname = hostname;
my $file = "/tmp/$hostname" . "_ww_files.txt";
open (WWFILE, ">>$file");
find sub {
return unless (stat)[2] & 00002; # ...and world writeable
return if "$File::Find::name" =~ m/(\/tmp)|(^\/dev)/;
print "$File::Find::name\n";
}, "/";
My first concern is that when I run the script it frequently throws an error on several files that states Use of uninitialized value in bitwise and (&) at ./ww_files.pl line 15.. This, of course, being the line that checks the mode of the file and then does a bitwise AND in order to find only those that have a "w" in the second column of each octet. Or so I'd like to believe, but I'm quite certain I'm not really looking for the right permissions with that method.
To be clear on this, all I care about is the "w". I don't care if a file is readable by everyone or even executable by everyone. So I suppose my second concern by way of the question I should be asking is what should that AND mask be in order to ignore everything but the "w"?
From the File::Find docs "if you use the warnings pragma, File::Find will report warnings for several weird situations." I wouldn't worry too much about them. To be sure about the mask you could use the constant S_IWOTH from Fcntl:
use Fcntl ':mode';
use File::Find;
no warnings 'File::Find';
find sub {
return unless (stat)[2] & S_IWOTH; # ...and world writeable
Probably the file could not be stated and so the value is undefined. You can easily trap that, though;
my #s = stat();
unless (#s) {
warn "$File::Find::name: stat failed: $!\n";
return;
}
return unless $s[2] & 0x02;
Generally speaking, you should check the return value of system calls for errors.
The mask value of 2 is precisely correct for finding world-writable files. However, perhaps you also want to check for odd group-writable files where the group is something weird?

Perl logging what scripts/modules accesses another module

We maintain a huge number of perl modules, actualy its so huge that we don't even know of all modules that we are responsible for. We would like to track what scripts and modules accesses another module in some sort of log, preferably stored by module name, so that we can evaluate whether its a risk to update a module and so that we can know what we might affect.
Is there any simple way to do this?
You could do a simple regex search:
use strict;
use warnings;
my %modules;
foreach my $perl_file (#file_list) {
open FILE, $perl_file or die "Can't open $perl_file ($!)";
while (<FILE>) {
if (/\s*(?:use|require)\s*([^;]+);/) {
$modules{$1}{$perl_file}++;
}
}
}
This is quick and dirty, but it should work pretty well. You end up with a hash of modules, each of which points to a hash of the files that use it.
Of course, it will catch things like use strict; but those will be easy enough to ignore.
If you have things like use Module qw/function/; You will grab the whole thing before the semicolon, but it shouldn't be a big deal. You can just search the keys for your known module names.
A downside is that it doesn't track dependencies. If you need that information you could add it by getting it from cpan or something.
Update: If you want to log this at runtime, you could create a wrapper script and have your perl command point to the wrapper on your system. Then make the wrapper something like this:
use strict;
use warnings;
use Module::Loaded;
my $script = shift #ARGV;
#run program
do $script;
#is_loaded() gets the path of these modules if they are loaded.
print is_loaded('Some::Module');
print is_loaded('Another::Module');
You might run the risk of funny side effects, though, since the method of calling your script has changed. It depends on what you are doing.
Maybe edit sitecustomize.pl so that each time when Perl runs, it would write some info in a log, and then analyse it? Add something like this to sitecustomize.pl:
open (LOG, '>>',"absolutepathto/logfile.txt");
print LOG $0,"\t",$$,"\t",scalar(localtime),"\n";
open SELF, $0;
while (<SELF>) {
print LOG $_ if (/use|require/);
}
close SELF;
print LOG "_" x 80,"\n";
close LOG;
EDIT:
Also, we forgot about %INC hash, so the code above may be rewritten as follows, to include more data about which modules were actually loaded + include files required by do function:
open (LOG, '>>',"absolutepathto/logfile.txt");
print LOG $0,' ',$$,' ',scalar(localtime),"\n";
open SELF, $0;
while (<SELF>) {
print LOG $_ if (/use|require/);
}
close SELF;
END {
local $" = "\n";
print LOG "Files loaded by use, eval, or do functions at the end of this program run:\n";
print LOG "#{[values %INC]}","\n";
print LOG "_" x 80,"\n";
close LOG;
}

Why am I unable to load a Perl library when using the `do` function?

I'm new to Perl, and I'm updating an old Perl website. Every .pl file seems to have this line at the top:
do "func.inc";
So I figured I could use this file to tag on a subroutine for global use.
func.inc
#!/usr/bin/perl
sub foobar
{
return "Hello world";
}
index.pl
#!/usr/bin/perl
do "func.inc";
print "Content-type: text/html\n\n";
print foobar();
However, I get this error:
Undefined subroutine &main::foobar called at /path/to/index.pl line 4.
Both files are in the same directory, and there's tones of subs in func.inc already which are used throughout the website. However, the script works in the Linux production environment, but does not work for my Windows 7 dev environment (I'm using ActivePerl).
Update:
It looks like the file is not being included; the sub works if the file is included using an absolute path...
do "C:/path/to/func.inc";
... so it looks like relative paths don't work for my local dev environment, but they work in the production environment through. But this is no good for me, because the absolute path on my dev machine will not work for the live server.
How do I get do to work using a relative path on my Windows 7 dev machine?
Update 2:
I was using the Perl -T switch. Unfortunately this removes "." from #INC, and so stops us from using relative paths for do. I removed this switch and the old code is working now. I'm aware that this is not good practice, but unfortunately I'm working with old code, so it seems that I have no choice.
The perlfunc documentation for do reads
do EXPR
Uses the value of EXPR as a filename and executes the contents of the file as a Perl script.
do 'stat.pl';
is just like
eval `cat stat.pl`;
except that it's more efficient and concise, keeps track of the current filename for error messages, searches the #INC directories, and updates %INC if the file is found.
So to see all this in action, say C:\Cygwin\tmp\mylib\func.inc looks like
sub hello {
print "Hello, world!\n";
}
1;
and we make use of it in the following program:
#!/usr/bin/perl
use warnings;
use strict;
# your code may have unshift #INC, ...
use lib "C:/Cygwin/tmp/mylib";
my $func = "func.inc";
do $func;
# Now we can just call it. Note that with strict subs enabled,
# we have to use parentheses. We could also predeclare with
# use subs qw/ hello /;
hello();
# do places func.inc's location in %INC
if ($INC{$func}) {
print "$0: $func found at $INC{$func}\n";
}
else {
die "$0: $func missing from %INC!";
}
Its output is
Hello, world!
./prog: func.inc found at C:/Cygwin/tmp/mylib/func.inc
As you've observed, do ain't always no crystal stair, which the do documentation explains:
If do cannot read the file, it returns undef and sets $! to the error. If do can read the file but cannot compile it, it returns undef and sets an error message in $#. If the file is successfully compiled, do returns the value of the last expression evaluated.
To check all these cases, we can no longer use simply do "func.inc" but
unless (defined do $func) {
my $error = $! || $#;
die "$0: do $func: $error";
}
Explanations for each case are below.
do cannot read the file
If we rename func.inc to nope.inc and rerun the program, we get
./prog: do func.inc: No such file or directory at ./prog line 12.
do can read the file but cannot compile it
Rename nope.inc back to func.inc and delete the closing curly brace in hello to make it look like
sub hello {
print "Hello, world!\n";
1;
Running the program now, we get
./prog: do func.inc: Missing right curly or square bracket at C:/Cygwin/tmp/mylib/func.inc line 4, at end of line
syntax error at C:/Cygwin/tmp/mylib/func.inc line 4, at EOF
do can read the file and compile it, but it does not return a true value.
Delete the 1; at the end of func.inc to make it
sub hello {
print "Hello, world!\n";
}
Now the output is
./prog: do func.inc: at ./prog line 13.
So without a return value, success resembles failure. We could complicate the code that checks the result of do, but the better choice is to always return a true value at the end of Perl libraries and modules.
Note that the program runs correctly even with taint checking (-T) enabled. Try it and see! Be sure to read Taint mode and #INC in perlsec.
You use the subroutine the same way that you'd use any other subroutine. It doesn't matter that you loaded it with do. However, you shouldn't use do for that. Check out the "Packages" chapter in Intermediate Perl for a detailed explanation of loading subroutines from other files. In short, use require instead.
See the documentation for do. You need to have func.inc (which you can also just call func.pl since pl is "perl library") in one of the directories where Perl will look for libraries. That might be different than the directory that has index.pl. Put func.inc in #INC somewhere, or add its directory to #INC. do also doesn't die if it can't load the file, so it doesn't tell you that it failed. That's why you shouldn't use do to load libraries. :)
Making sure the path is correct, use:
#!/usr/bin/perl
require("func.inc");
print "Content-type: text/html\n\n";
print foobar();
I would first check if the file was actually loaded, the documentation for do mentions that it updates %INC if the file was found. There is also more information in the documentation.
make sure you have func.inc in the correct path.
do "func.inc"
means you are saying func.inc is in the same path as your perl script. check the correct path and then do this
do "/path/func.inc"