perl: What would cause perl to not find a file that exists? - perl

Running a perl cgi script I got the error it couldn't find the file it was trying to open. I check apache error log /var/log/apache2/error.log:
[Tue Jan 13 20:59:17 2015] [error] [client ::1] [Tue Jan 13 20:59:17 2015] submit.cgi: [Tue Jan 13 20:59:17 2015] submit.cgi:
/home/jddancks/Documents/perl/homeworks/hw13/grades4.txt: No such file or directory at /var/www/homeworks/hw13/CreateExam.pm line 48., referer: http://localhost/homeworks/hw13/test.cgi
double check:
root#debian-macbook:/var/log/apache2# ls -l /home/jddancks/Documents/perl/homeworks/hw13/grades4.txt
-rwxrwxrwx 1 jddancks jddancks 2095 Jan 7 12:25 /home/jddancks/Documents/perl/homeworks/hw13/grades4.txt
Why would this happen? This is a debian machine running apache 2.2 IDK if that helps.
There are 2 files: submit.cgi and CreateExam.pm.
submit.cgi:
use CreateExam;
...
my $path = `pwd`;
...
my $check = CreateExam->new("${path}/exam4.txt","${path}/answers4.txt","${path}/grades4.txt",$pathroot);
$check->entergrades($cookie_value,$cgi->Vars());
CreateExam.pm:
package CreateExam;
sub new {
my ($class,$file,$answers,$grades,$script) = #_;
#print "<p>in new: file: $file, grades: $grades</p>\n";
return bless {'file'=>$file,'answers'=>$answers,'gradefile'=>$grades,'script'=>$script},$class;
}
sub tooktest {
my ($self,$person) = #_;
#print "<p>in tooktest: person: $person</p>\n";
my $grades = $self->{'gradefile'};
open(ANS,"< $grades") or die "$grades: $!";
my $found = 0;
LAST: while(my $line = <ANS>) {
if($line =~ /\<test taker=$person/) { $found = 1; last LAST;}
}
return $found==1;
}
Data::Dumper:
$VAR1 = "/home/jddancks/Documents/perl/homeworks/hw13"; (in browser)
hexdump:
jddancks#debian-macbook:~/Documents/perl/homeworks/hw13$ perl -e 'print qx(pwd)' | hexdump -C
00000000 2f 68 6f 6d 65 2f 6a 64 64 61 6e 63 6b 73 2f 44 |/home/jddancks/D|
00000010 6f 63 75 6d 65 6e 74 73 2f 70 65 72 6c 2f 68 6f |ocuments/perl/ho|
00000020 6d 65 77 6f 72 6b 73 2f 68 77 31 33 0a |meworks/hw13.|
0000002d

As your hexdump shows, pwd returns the working directory followed by a newline, and that is what is you are assigning to $path. Then you try to open "/home/jddancks/Documents/perl/homeworks/hw13\n/grades4.txt" which indeed has a directory that does not exist.
Try doing:
chomp( my $path = `pwd` );

If Apache is configured to run in a chroot it does not see /home on the host system at all.

Related

Perl printf line breaks

Looking to add a line break after every 16 bytes of the 100 bytes printed from j. All 100 bytes are currently printed on one line at the moment.
Any assistance would be great full, thanks.
for ($j = 6; $j < 106; $j++)
{
printf("%02X ", hex(#bytes[$j]));
}
printf("\t");
Thanks all. I approached it another way. In the end the visual aspect was not a concern as I ended up printing to file anyway. This data then gets pasted in a hex editor so formatting was a forethought rather than a need be. The below is now outputting all the data I was looking for in the end rather than a predetermined length.
for ($j = 6; $j < #bytes; $j = $j + 1)
{
printf outfile ("%02X ", hex(#bytes[$j]));
}
printf("\n");
Use splice() to grab 16 elements at a time from your array.
I've also switched to using say(), join(), map() and sprintf().
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my #bytes = map { int(rand(255)) } 0..200;
# As splice is destructive, take a copy of the array.
# Also, let's remove those first six elements and any
# after index 106.
my #data = #bytes[6 .. 105];
while (my #row = splice #data, 0, 16) {
say join ' ', map { sprintf '%02X', $_ } #row;
}
Update: A few more Perl tips for you.
Always add use strict and use warnings to the top of your Perl programs. And fix the problems they will show you.
#bytes[$j] is better written as $bytes[$j] (as it's a single value). That's one of the things use warnings will tell you about.
for ($j = 6; $j < 106; $j++) is probably better written as for my $j (6 .. 105). Less chance of "off-by-one" errors.
Actually, as you're just using $j to get the element from the array, you probably want to just iterate across the elements of the array directly - for my $elem (#bytes[6 .. 105]) (and then using $elem in place of $bytes[$j]).
You can utilize some $counter and print \n once $counter % 16 equal 0.
use strict;
use warnings;
my #bytes = map { int(rand(255)) } 0..200;
my $counter = 1;
for ( #bytes[6..106] ) {
printf "%02X ", $_;
print "\n" unless $counter++ % 16;
}
print "\n";
Output sample
C9 CA E7 66 13 F5 56 BE 08 68 E4 22 93 77 E0 14
08 4F F3 AD CC F4 66 DE 6C BB 1B E6 CE F3 13 DD
AE 6A CD 9B 5E 98 1F D4 2E C5 80 4B 3E 8E BC BF
5B 27 F9 0D 97 AB 26 C0 11 2D 1D 95 CE 26 3C D8
3C D8 A4 06 0A 48 0D 45 53 28 7E 5D D2 AD 90 5C
03 32 95 48 F6 DB 20 90 A7 62 41 3D D7 AB 7C 3B
CF 3D 0D C2 DA
Note: index from 6 to 106 gives 101 element
Short and fast:
sub hex_dump {
my $i = 0;
my $d = uc unpack 'H*', pack 'C*', #_;
$d =~ s{ ..\K(?!\z) }{ ++$i % 16 ? " " : "\n" }xseg;
return "$d\n";
}
print hex_dump(#bytes[6..105]);
There's also the very clean splice loop approach.
sub hex_dump {
my $d = '';
$d .= join(" ", map sprintf("%02X", $_), splice(#_, 0, 16)) . "\n" while #_;
return $d;
}
print hex_dump(#bytes[6..105]);
Because this approach produces a line at a time, it's great for recreating the traditional hex dump format (with offset and printable characters shown).
$ hexdump -C a.pl
00000000 75 73 65 20 73 74 72 69 63 74 3b 0a 75 73 65 20 |use strict;.use |
00000010 77 61 72 6e 69 6e 67 73 3b 0a 0a 73 75 62 20 68 |warnings;..sub h|
00000020 65 78 5f 64 75 6d 70 20 7b 0a 20 20 20 6d 79 20 |ex_dump {. my |
...
000000a0 74 65 73 20 3d 20 30 2e 2e 32 35 35 3b 0a 0a 70 |tes = 0..255;..p|
000000b0 72 69 6e 74 20 68 65 78 5f 64 75 6d 70 28 40 62 |rint hex_dump(#b|
000000c0 79 74 65 73 5b 36 2e 2e 31 30 35 5d 29 3b 0a |ytes[6..105]);.|
000000cf
You could also use a for loop. You would iterate over the indexes, and print either a space or a line feed depending on the index of the byte you're printing.
sub hex_dump {
my $d = '';
for my $i (0..$#_) {
$d .= sprintf("%02X%s", $_[$i], $i == $#_ || $i % 16 == 15 ? "\n" : " ");
}
return $d;
}
print hex_dump(#bytes[6..105]);

Why don't I get output when I use __DATA__ in Perl?

Does anybody know if the Perl __DATA__ syntax on macOS Catalina is deprecated? I have perl v5.18.4 running, even a simple program like this gives no output (and no error either);
use strict;
use warnings;
while(<DATA>){
print $_;
}
__DATA__
line1
line2
line3
Edit:
This is weird. I said earlier that I have 2 Mac systems, both having the same problem. Not quite right, on one system the program works, on the other system the same program doesn’t.
Hexdump on both systems is the same:
Mac Mini:
Mac-mini-van-Theo:Programming theo$ hexdump -C test.pl
00000000 75 73 65 20 73 74 72 69 63 74 3b 0d 75 73 65 20 |use strict;.use |
00000010 77 61 72 6e 69 6e 67 73 3b 0d 77 68 69 6c 65 28 |warnings;.while(|
00000020 3c 44 41 54 41 3e 29 20 7b 0d 20 20 20 20 70 72 |<DATA>) {. pr|
00000030 69 6e 74 20 24 5f 3b 0d 7d 0d 5f 5f 44 41 54 41 |int $_;.}.__DATA|
00000040 5f 5f 0d 6c 69 6e 65 31 0d 6c 69 6e 65 32 0d 6c |__.line1.line2.l|
00000050 69 6e 65 33 0d |ine3.|
00000055
iMac:
Theo#iMac-van-Theo Programming % hexdump -C test.pl
00000000 75 73 65 20 73 74 72 69 63 74 3b 0a 75 73 65 20 |use strict;.use |
00000010 77 61 72 6e 69 6e 67 73 3b 0a 77 68 69 6c 65 28 |warnings;.while(|
00000020 3c 44 41 54 41 3e 29 7b 0a 20 20 20 20 70 72 69 |<DATA>){. pri|
00000030 6e 74 20 24 5f 3b 0a 7d 0a 0a 5f 5f 44 41 54 41 |nt $_;.}..__DATA|
00000040 5f 5f 0a 6c 69 6e 65 31 0a 6c 69 6e 65 32 0a 6c |__.line1.line2.l|
00000050 69 6e 65 33 0a |ine3.|
00000055
However, a 'cat’ or a ‘more’ shows differences:
Mac Mini:
Mac-mini-van-Theo:Programming theo$ more test.pl
use strict;^Muse warnings;^Mwhile(<DATA>) {^M print $_;^M}^M__DATA__^Mline1^Mline2^Mline3
iMac:
Theo#iMac-van-Theo Programming % more test.pl
use strict;
use warnings;
while(<DATA>){
print $_;
}
__DATA__
line1
line2
line3
The difference? The Mac Mini uses ‘bash’ as shell (where the program fails), the iMac uses ‘zsh’. So the problem is not really perl related but perl/shell related. With Catalina, Zsh is used as the default shell but the old Bash shell is still included with macOS and you can still switch to it. It seems to be related to how the shell handles line-endings, although I do not understand why this happens and moreover how to solve it.
__DATA__ is just fine, and there's no platform-specific issues with it (and there are lots of stupid tricks you can do with it).
However, if you want to know the state of any particular Perl thing, there's the perldeprecation docs. Sometimes perlexperiment is handy too.
How are you running your program?
Supply a hexdump of your program: hexdump -C program.pl. Maybe there are funny characters.

File::stat returns "No such file or directory"

When I run this program:
#!/usr/bin/perl -w
use File::Find;
use File::stat;
use Time::gmtime;
use Fcntl ':mode';
my %size = ();
my #directory = ('.');
find(
sub {
my $st = stat($File::Find::name) or die "stat failed for $File::Find::name : $!";
if ( defined $st )
{
my $gm = gmtime $st->mtime;
$size{$gm->year + 1900} += $st->blksize unless S_ISDIR($st->mode);
}
else
{
print "stat failed for ", $File::Find::name, ": $!\n";
}
},
#directory);
foreach my $year (keys %size)
{
print "$year ", $size{$year}, "\n";
}
I get stat failed for ./1128/00 : No such file or directory at ./size.pl line 13.. But, when I list it, it's there:
# ls ./1128/00
03 05 07 09 12 14 18 20 22 24 27 29 32 34 37 40 43 45 47 50 52 54 57 59 63 65 67 69 75 78 81 83 85 88 90 92 95
04 06 08 11 13 15 19 21 23 25 28 31 33 35 39 41 44 46 48 51 53 55 58 61 64 66 68 71 77 79 82 84 86 89 91 93
Based on diagnostics that I have removed for this question, I can see that it does successfully stat the first 4 files and the . directory and 1128 directory (parent to 1128/00). It always successfully stats the same files and directories and fails on 1128/00. Why is it failing?
By default, File::Find will chdir to each directory as it recurses.
Because of this, performing stat on the $File::Find::name value of ./1128/00 is actually looking for the file ./1128/./1128/00, which does not exist.
To get the behavior that you want, simply perform your file operations on the $_ variable.
my $st = stat($_) or die "stat failed for $_: $!";

Why does Perl MIME::Base64 insert CR characters before LF characters on decoding Base64-encoded strings when they aren't present in the original data?

Why does the Perl MIME::Base64 module on decoding Base64-encoded strings insert CR characters before LF characters when they are not present in the original data?
Input: a binary described by the following hex string,
14 15 6A 48 E4 15 6A 32 E5 48 46 13 A5 E3 88 43 18 A6 84 E3 51 3A 8A 0A 1A 3E E6 84 A6 1A 16 E8 46 84 A1 2E A3 5E 84 8A 4E 1A 35 E1 35 1E 84 A9 8E 46 54 44
This encodes to the Base64-encoded string:
FBVqSOQVajLlSEYTpeOIQximhONROooKGj7mhKYaFuhGhKEuo16Eik4aNeE1HoSpjkZURA==
My Perl script for decoding is
use MIME::Base64;
my $bin = decode_base64('FBVqSOQVajLlSEYTpeOIQximhONROooKGj7mhKYaFuhGhKEuo16Eik4aNeE1HoSpjkZURA==');
open FH, ">test.bin" or die $!;
print FH $bin;
close FH;
Output: the resulting file 'test.bin' has the following hex string representation,
14 15 6A 48 E4 15 6A 32 E5 48 46 13 A5 E3 88 43 18 A6 84 E3 51 3A 8A 0D 0A 1A 3E E6 84 A6 1A 16 E8 46 84 A1 2E A3 5E 84 8A 4E 1A 35 E1 35 1E 84 A9 8E 46 54 44
Note the hex digits in bold highlighting the additional '0D' character that has been inserted before '0A' where it was not present in the original data.
I'm using Perl v5.14.2 on Windows 7.
Since you're on Windows, you will need to open that filehandle in binary mode to prevent your line-endings from being munged.
open FH, ">test.bin" or die $!;
binmode FH;
You can do that all at once using IO layers, and also using a lexical filehandle which is better practice than a package symbol like FH:
open my $fh, '>:raw', 'test.bin' or die $!;
print { $fh } $bin;
For more, check out
perldoc perlio
perldoc perlopentut

Sending a trap with Perl's Net::SNMP

I'm trying to send a trap as part of a larger Perl script. I've copied the trapsending code to another file, and am running it by itself. The code seems to think the trap sends successfully, yet I'm not seeing the trap on either machine that I have a trap listener running on.
Here's the code:
#! /usr/local/bin/perl
use strict;
use warnings;
use Net::SNMP;
#messy hardcoding
my $snmp_target = '192.168.129.50';
#my $snmp_target = '10.200.6.105'; # Server running trap listener
my $enterprise = '1.3.6.1.4.1.27002.1';
my ($sess, $err) = Net::SNMP->session(
-hostname => $snmp_target,
-version => 1, #trap() requires v1
);
if (!defined $sess) {
print "Error connecting to target ". $snmp_target . ": ". $err;
next;
}
my #vars = qw();
my $varcounter = 1;
push (#vars, $enterprise . '.' . $varcounter);
push (#vars, OCTET_STRING);
push (#vars, "Test string");
my $result = $sess->trap(
-varbindlist => \#vars,
-enterprise => $enterprise,
-specifictrap => 1,
);
if (! $result)
{
print "An error occurred sending the trap: " . $sess->error();
}
EDIT: Added $sess->debug(255) call, here's the output:
debug: [440] Net::SNMP::Dispatcher::_event_insert(): created new head and tail [ARRAY(0x1af1fea8)]
debug: [687] Net::SNMP::Message::send(): transport address 192.168.129.50:161
debug: [2058] Net::SNMP::Message::_buffer_dump(): 70 bytes
[0000] 30 44 02 01 00 04 06 70 75 62 6C 69 63 A4 37 06 0D.....public.7.
[0016] 09 2B 06 01 04 01 81 D2 7A 01 40 04 C0 A8 81 85 .+......z.#.....
[0032] 02 01 06 02 01 01 43 01 00 30 1B 30 19 06 0A 2B ......C..0.0...+
[0048] 06 01 04 01 81 D2 7A 01 01 04 0B 54 65 73 74 20 ......z....Test
[0064] 73 74 72 69 6E 67 string
debug: [517] Net::SNMP::Dispatcher::_event_delete(): deleted [ARRAY(0x1af1fea8)], list is now empty
EDIT: Can anyone running a trap listener try this code on their machine and let me know if it works?
EDIT: Tried it from my MBP. Same result. Then noticed that the debug info says it is sending to port 161. Forced -port => 162 parameter, and it works. That leaves me with a couple of questions:
Why does the trap sender default to 161?
I get this error when I run with debug on. What does it mean?
error: [97] Net::SNMP::Transport::IPv4::UDP::agent_addr(): Failed to disconnect: Address family not supported by protocol family
Fixed by changing 'Port' setting from default 161 to 162.