Help with a Perl script

Hey guys, I am getting an annoying warning when running this piece of code on my FreeBSD machine with Perl 5.8.9. It doesn't have the errors under Linux with Perl 5.10

Anyhow, this is the code:

Code:
#!/usr/bin/perl
use strict;
use warnings;
use vars qw($reinit_sysintegrity);

# this value will be read from a config file placed on the monitoring server
$reinit_sysintegrity = 0;

my ($test, $error) = &get_sys_integrity;
print ("Any Changes?: $test\n$error\n");

#=- Check System File Integrity
sub get_sys_integrity {

	# work on improving the place this file is stored
	# $BASEDIRECTORY keeps the fileinit list of hashes
	our $BASEDIRECTORY = "/tmp";
	our $MASTERCOUNT = 0;
	our @FILES = ("/etc/passwd", "/etc/shadow", "/etc/group", "/etc/gshadow");
	our @DIRECTORIES = ("/etc/rc.d", "/bin", "/usr/bin", "/sbin", "/usr/sbin");
	
	if($reinit_sysintegrity == 1){
	   my $findresults = `find $BASEDIRECTORY 2>/dev/null`;
	   my @results = split(/\n/,$findresults);
	   foreach my $result (@results){
		  $_ = $result;
		  if(/CHECK\.\d\d/){
			 unlink($result);
		  }
	   }

	   &init_files("fileinit");
	   &init_directories("fileinit");

	   print "Initialization Of $MASTERCOUNT Files Successful\n";
	   return (0, "none");	# 0 for changes, and "none" for diff string
	}
	else{
	   my $CHECKCOUNT = 0;

	   my $findresults = `find $BASEDIRECTORY 2>/dev/null`;
	   my @results = split(/\n/,$findresults);
	   foreach my $result (@results){
		  $_ = $result;
		  if(/CHECK\.\d\d/){
			 $CHECKCOUNT++;		# maybe reinitialize the file after n checks
		  }
	   }

	   my $checkfilename = "CHECK." . time;

	   &init_files($checkfilename);
	   &init_directories($checkfilename);

	   my $diff_command = "diff $BASEDIRECTORY/fileinit $BASEDIRECTORY/$checkfilename";
	   my $diff_results = `$diff_command`;

	   if(length($diff_results) > 0){
		  my $difference = "diff fileinit $checkfilename\n" . $diff_results;
		  print($difference);
		  return (1, $difference);	# 1 for changes to sys files, and the full diff string
	   }
	   else{
		  unlink("$BASEDIRECTORY/$checkfilename");
		  print "Checked $MASTERCOUNT Files, Found No Changes\n";
		  return (0, "none");	# 0 for changes, and "none" for diff string
	   } 
	}

	sub init_files(){
	   my $INITNAME = shift;

	   open(FILEINIT,">$BASEDIRECTORY/$INITNAME") ||
		  die ("couldn't open $BASEDIRECTORY/$INITNAME");

	   foreach my $file (@FILES){
		  my $hash = &generate_file_hash($file);
		  print FILEINIT ($hash . "\n");
	   }

	   close(FILEINIT);
	}

	sub init_directories(){
	   my $INITNAME = shift;

	   open(FILEINIT,">>$BASEDIRECTORY/$INITNAME") ||
		  die ("couldn't open $BASEDIRECTORY/$INITNAME");

	   foreach my $directory (@DIRECTORIES){
		  my $findresults = `find $directory`;

		  my @results = split(/\n/,$findresults);

		  foreach my $result (@results){
			 my $hash = &generate_file_hash($result);
			 print FILEINIT ($hash . "\n");
		  }
	   }

	   close(FILEINIT);
	}

	sub generate_file_hash(){
	   my $filename = shift;

	   $MASTERCOUNT++;

	   my ($devs, $ino, $mode, $nlink, $uid, $gid, $rdev,
		$size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($filename);

	   return($filename . ",PERMISSIONS=" . $devs  . ",INODE=" . $ino  . 
			  ",MODE=" . $mode  . ",HARD LINKS=" . 
			  $nlink . ",UID=" . $uid  . ",GID=" . $gid  . 
			  ",DEVICE=" . $rdev  . ",SIZE=" .
			  $size . ",LAST MODIFIED=" .  $mtime  . 
			  ",INODE CREATED=" . $ctime  . ",BLOCK SIZE=" .
			  $blksize . ",BLOCKS=" . $blocks);
	}	
}

and the output when run:

Code:
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
Use of uninitialized value in concatenation (.) or string at ./sys2.pl line 112.
diff fileinit CHECK.1269153366
1c1
< /etc/passwd,PERMISSIONS=79,INODE=49775,MODE=33188,HARD LINKS=1,UID=0,GID=0,DEVICE=196641,SIZE=1616,LAST MODIFIED=1269136234,INODE CREATED=1269136234,BLOCK SIZE=4096,BLOCKS=4
---
> /etc/passwd,PERMISSIONS=79,INODE=49775,MODE=33188,HARD LINKS=1,UID=0,GID=0,DEVICE=196641,SIZE=1614,LAST MODIFIED=1269139597,INODE CREATED=1269139597,BLOCK SIZE=4096,BLOCKS=4
Any Changes?: 1
diff fileinit CHECK.1269153366
1c1
< /etc/passwd,PERMISSIONS=79,INODE=49775,MODE=33188,HARD LINKS=1,UID=0,GID=0,DEVICE=196641,SIZE=1616,LAST MODIFIED=1269136234,INODE CREATED=1269136234,BLOCK SIZE=4096,BLOCKS=4
---
> /etc/passwd,PERMISSIONS=79,INODE=49775,MODE=33188,HARD LINKS=1,UID=0,GID=0,DEVICE=196641,SIZE=1614,LAST MODIFIED=1269139597,INODE CREATED=1269139597,BLOCK SIZE=4096,BLOCKS=4

Any ideas? The line in question is the return statement in generate_file_hash()..
 
Put
Code:
no warnings;
in start of script, or replace
Code:
return($filename . ",PERMISSIONS=" . $devs  . ",INODE=" . $ino  . 
			  ",MODE=" . $mode  . ",HARD LINKS=" . 
			  $nlink . ",UID=" . $uid  . ",GID=" . $gid  . 
			  ",DEVICE=" . $rdev  . ",SIZE=" .
			  $size . ",LAST MODIFIED=" .  $mtime  . 
			  ",INODE CREATED=" . $ctime  . ",BLOCK SIZE=" .
			  $blksize . ",BLOCKS=" . $blocks);
With
Code:
return sprintf("%s,PERMISSIONS=%s,INODE=%s,MODE=%s,HARD LINKS=%s,UID=%s,GID=%s,".
"DEVICE=%s,SIZE=%s,LAST MODIFIED=%s,NODE CREATED=%s,BLOCK SIZE=%s,BLOCKS=%s", 
$filename, $devs, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $mtime, $ctime, $blksize, $blocks);
Plus, there is a way to block only this warning, but i dont remember, just google it =)
 
Alt said:
Put
Code:
no warnings;
in start of script, or replace
Code:
return($filename . ",PERMISSIONS=" . $devs  . ",INODE=" . $ino  . 
			  ",MODE=" . $mode  . ",HARD LINKS=" . 
			  $nlink . ",UID=" . $uid  . ",GID=" . $gid  . 
			  ",DEVICE=" . $rdev  . ",SIZE=" .
			  $size . ",LAST MODIFIED=" .  $mtime  . 
			  ",INODE CREATED=" . $ctime  . ",BLOCK SIZE=" .
			  $blksize . ",BLOCKS=" . $blocks);
With
Code:
return sprintf("%s,PERMISSIONS=%s,INODE=%s,MODE=%s,HARD LINKS=%s,UID=%s,GID=%s,".
"DEVICE=%s,SIZE=%s,LAST MODIFIED=%s,NODE CREATED=%s,BLOCK SIZE=%s,BLOCKS=%s", 
$filename, $devs, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $mtime, $ctime, $blksize, $blocks);
Plus, there is a way to block only this warning, but i dont remember, just google it =)

Why not determine what value is undefined and fix it? undef'ed values make for bad juju in Perl code if you don't handle them correctly...

Also, http://perldoc.perl.org/perllexwarn.html is the resource you want to refer to (or perldoc perllexwarn ;)...).
 
Ico said:
Hey guys, I am getting an annoying warning when running this piece of code on my FreeBSD machine with Perl 5.8.9. It doesn't have the errors under Linux with Perl 5.10

Anyhow, this is the code:

...

Any ideas? The line in question is the return statement in generate_file_hash()..

This is probably your problem:

Code:
       stat FILEHANDLE
       stat EXPR
       stat DIRHANDLE
       stat    Returns a 13-element list giving the status info for a file,
               either the file opened via FILEHANDLE or DIRHANDLE, or named by
               EXPR.  If EXPR is omitted, it stats $_.  Returns a null list if
                                                        ^^^^^^^^^^^^^^^^^^^^^^
               the stat fails.  Typically used as follows:
               ^^^^^^^^^^^^^^^

Are you sure the stat(2) call worked :)?
 
@Alt: I think I'd rather stick to warnings and strict, than turn them off.. as gcooper@ noted, it could cause other problems.
@gcooper@: stat() call is working, the values are assigned and the diff returns what it should as you can see toward the end of the output in my initial post.
@SirDice: ideas/examples welcome :)
 
Hint: Wrapping in an eval block and looking for a non-sane value returned from $@ would also be an interesting thing to try, or maybe looking at the value returned in $SIG{__WARN__}...

From perldoc perlvar:
Code:
       $@      The Perl syntax error message from the last eval() operator.
               If $@ is the null string, the last eval() parsed and executed
               correctly (although the operations you invoked may have failed
               in the normal fashion).  (Mnemonic: Where was the syntax error
               "at"?)

               Warning messages are not collected in this variable.  You can,
               however, set up a routine to process warnings by setting
               $SIG{__WARN__} as described below.

               Also see "Error Indicators".

Cheers!
 
Yeah, I've been trying to debug it for some time, I even rewrote the code without the extra subs cause I thought it was a scope issue with the vars, it's going on my top priority list for tomorrow. :)
 
Ico said:
@Alt: I think I'd rather stick to warnings and strict, than turn them off.. as gcooper@ noted, it could cause other problems.
@gcooper@: stat() call is working, the values are assigned and the diff returns what it should as you can see toward the end of the output in my initial post.
@SirDice: ideas/examples welcome :)

1. find output should be chomped, like so:

Code:
chomp (my $findresults = `find $BASEDIRECTORY 2>/dev/null`);

This gets rid of the stray newline.

2. Quoting paths is a good idea, like "$BASEDIRECTORY" above.
3. stat can fail (check for undef, like I quoted earlier).
4. unlink can fail (does it matter? that's up to you to decide).
5. You aren't checking to make sure that the variable being passed into generate_file_hash is actually a file, and hence the hash is failing (most likely related to style issues by not chomping each entry before it's passed in and the like).

HTH.
 
  • Thanks
Reactions: Ico
Got it! Thanks for the pointers :)

Code:
Code:
 sub generate_file_hash(){
	my $filename = shift;

	$MASTERCOUNT++;

	my @list = stat($filename);

    if (@list) {
        my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
			$size, $atime, $mtime, $ctime, $blksize, $blocks) = @list;

        return($filename . " dev_num=" . $dev . ", inode=" . $ino . 
		", mode=" . $mode . ", hard_links=" . $nlink . ", uid=" . $uid . 
		", gid=" . $gid . ", dev=" . $rdev  . ", size=" . $size . 
		", modified=" . $mtime . ", inode_created=" . $ctime  . 
		", blk_size=" . $blksize . ", blocks=" . $blocks);
    }
 }
 
Back
Top