Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

System call constantly dying

by gokuraku (Monk)
on Jul 28, 2008 at 13:01 UTC ( [id://700518]=perlquestion: print w/replies, xml ) Need Help??

gokuraku has asked for the wisdom of the Perl Monks concerning the following question:

Monks,
I'm working with a script for a filter driver, what I want is to have some activity happen on each system call, by creating directories/files, modifying them or copying them. The script I have seems to be constantly dying on each system call, though it seems to successfully execute. What I have is something like the following (though the original has a Usage function for the Help command line option, but I removed it for this example:
use strict; use Cwd; use Getopt::Long; my ($help) = 0; my ($num) = 20; # Number of default loops to run my ($testdir) = ""; my ($verbose) = 0; GetOptions('help|?' => \$help, "num=i" => \$num, "testdir=s" => \$testdir, 'verbose' => \$verbose); # Header print "Beginning Directory Manipulation Testing...\n" if $verbose; $| = 1; # Get the current working directory my $home_dir = getcwd; # Change to the test directory chdir($testdir); # Set up test dirs if they do not exist if (!-d "test") { mkdir("test"); } if (!-d "test1") { mkdir("test1"); } # Change to the test area chdir("test"); # Directory Manipulation for (my $dir = 0; $dir < $num; $dir++) { # Make the directory if (!-d "directory$dir" ) { mkdir("directory$dir", 0755) or die "Can't make $testdir\\test +\\directory$dir: $!\n"; } # Change permissions of the directory if ($^O =~ /Win32/) { print "\nRunning chmod from - $home_dir\\..\\..\\bin\\chmod on + $testdir\\test\\directory$dir.\n" if ($verbose); if ($verbose) { system($home_dir . "\\..\\..\\bin\\chmod -v a+rwx $testdir +\\test\\directory$dir"); } else { system($home_dir . "\\..\\..\\bin\\chmod a+rwx $testdir\\t +est\\directory$dir"); } } else { system("/bin/chmod 777 directory$dir") or die "Could not chmod + directory$dir: $!\n"; } # Copy the directory if ($^O =~ /Win32/) { print "\nRunning $home_dir\\..\\..\\bin\\cp on $testdir\\test\\directory$dir.\n" if ($verbose); system("$home_dir\\..\\..\\bin\\cp -rv directory$dir copieddir +ectory$dir"); } else { system("/bin/cp -r directory$dir copieddirectory$dir") or die "Could not copy directory$dir to copieddirectory$dir: +$!\n"; } # Rename the directory if (!-d "copieddirectory$dir") { print "copieddirectory$dir does not exist!\n" if $verbose; } if ($^O =~ /Win32/) { system("move copieddirectory$dir ..\\test1\\moveddirectory$dir > o +ut.txt"); if (-e "out.txt") { unlink("out.txt"); } } else { system("/bin/mv copieddirectory$dir ../test1/moveddirectory$di +r") or die "Could not move directory$dir: $!\n"; } # Delete the copied directory if ($^O =~ /Win32/) { rmdir("..\\test1\\moveddirectory$dir"); } else { system("/bin/rm -r ../test1/moveddirectory$dir") or die "Could not remove moved directory$dir: $!\n"; } # Remove the directory if ($^O =~ /Win32/) { rmdir("directory$dir") or die "Can't remove directory: $!\n"; } else { system("/bin/rm -r directory$dir") or die "Could not remove di +rectory$dir: $!\n";; } }
My initial failure comes on the first copy command, it fails with - Could not copy directory0 to copieddirectory0: A file or directory in the path name does not exist. Looking at the directory I see the directories there, with the right timestamps, but somehow system seems to get a failure on the call. Is there something that might generate a problem with the call as it is, especially since I am flushing calls to disk?
Thanks!

Replies are listed 'Best First'.
Re: System call constantly dying
by moritz (Cardinal) on Jul 28, 2008 at 13:13 UTC
    The first step is to fail on failling system calls: chdir $testdir or die "Can't chdir to '$testdir': $!";

    Then you're probably confusing your paths somewhere, i.e. you're not in the directory you think you are. You can find your current working directory with Cwd.

    I'd also recommend to use File::Copy to copy and move files, and File::Path to delete files and directories.

      I would, but the preference here is to get as much on the OS and outside of perl as possible, the way the filter driver works is it processes system calls differently than perl options.
      I am in the directory I think I am, that much I know as I have debugged that previously, I thought the same thing but nope I am there. I can see the result of some of the calls, so I know they happen, but somehow system is returning a failure that's getting caught with die.
Re: System call constantly dying
by Bloodnok (Vicar) on Jul 28, 2008 at 13:39 UTC
    AFAIR, system returns a combination of the actual return code and any signal that caused the call to die (as returned by wait) where the return code is the higher and the signal is the lower 8 bits of the 16 bit status return value.

    Hence typical error detection avoids the process control signal data by shifting it out of the way before testing the return status - along the lines of...

    my $ret = system "args"; die "system($args) failed - status code: $ret" if $ret << 8;

    HTH,

    Update Thanx to pc88mer for noticing I got the wrong shift - should've read $ret >> 8 ... doh!!!

    At last, a user level that overstates my experience :-))
Re: System call constantly dying
by almut (Canon) on Jul 28, 2008 at 13:44 UTC

    system normally sets $?, not $! (except on failure to start the program, in which case system returns -1).  Also, it returns the exit code (return value of the wait call) of the called program, which is typically zero upon success...

Re: System call constantly dying
by apl (Monsignor) on Jul 28, 2008 at 13:19 UTC
    There are several system calls that don't have or die modifiers. You should consider adding them, as well as use warnings;.
Re: System call constantly dying
by graff (Chancellor) on Jul 29, 2008 at 05:45 UTC
    Apart from the issues mentioned above about your uses of "system()", I'd be worried about your use of "chdir()", especially considering your attempts at handling ms-win as well as other OS's.

    You initialize $testdir to an empty string, and that is the value that goes to chdir unless there's a command-line option to select a specific path. Have you read what perldoc -f chdir says about using chdir with an empty string? Is that really something you want your script to do? (I think the result might vary according to what the OS and/or runtime environment happens to be.)

    You might want to look at some modules like File::Spec and/or File::Basename, to make it easier for your code to handle path names in an OS-neutral way.

    (updated to fix grammar)

      Actually this script is called from another, which passes a $testDir variable so while this script has it initialized as empty it does get one on the command line. But that's a good point to check on and put in some error capturing, since that directory is consistent I can probably add something to make sure its going to get that value anyway and even if called on the command line it will be set.
      This works well cross platform, the script is run on AIX, HPUX, Linux, Solaris and Windows and its been pretty solid on those, it just means any change I make has to be checked across those platforms as well. Ah well, life goes on.
      Ok, here is the updated script. I can run it with the warnings on and get every single warning, but each system call is completing properly. Each status is being returned as 0. Very strange. If anyone can thing of something else to check on the system calls, I'd appreciate it.
      use strict; use warnings; use Cwd; use Getopt::Long; my ($help) = 0; my ($num) = 20; # Number of loops to run my ($testdir) = ""; my ($verbose) = 0; GetOptions('help|?' => \$help, "num=i" => \$num, "testdir=s" => \$testdir, 'verbose' => \$verbose) or &usage(); &usage() if $help; # Header print "Beginning Directory Manipulation Testing...\n" if $verbose; $| = 1; # Get the current working directory my $home_dir = getcwd; # Change to the test directory chdir($testdir) or die "Can't make $testdir: $!\n"; # Set up test dirs if they do not exist if (!-d "test") { mkdir("test") or die "Can't make $testdir\\test: $!\n"; } if (!-d "test1") { mkdir("test1") or die "Can't make $testdir\\test1: $!\n"; } # Change to the test area chdir("test") or die "Can't change to test directory: $!\n"; # Directory Manipulation for (my $dir = 0; $dir < $num; $dir++) { # Make the directory if (!-d "directory$dir" ) { mkdir("directory$dir", 0755) or die "Can't make $testdir\\test +\\directory$dir: $!\n"; } # Change permissions of the directory if ($^O =~ /Win32/) { print "\nRunning chmod from - $home_dir\\..\\..\\bin\\chmod on + $testdir\\test\\directory$dir.\n" if ($verbose); if ($verbose) { system($home_dir . "\\..\\..\\bin\\chmod -v a+rwx $testdir +\\test\\directory$dir"); } else { system($home_dir . "\\..\\..\\bin\\chmod a+rwx $testdir\\t +est\\directory$dir"); } } else { system("/bin/chmod 777 directory$dir"); or warn "Could not chmod directory$dir: $?\n"; } # Copy the directory if ($^O =~ /Win32/) { print "\nRunning $home_dir\\..\\..\\bin\\cp on $testdir\\test\\directory$dir.\n" if ($verbose); system("$home_dir\\..\\..\\bin\\cp -rv directory$dir copieddir +ectory$dir"); } else { system("/bin/cp -r directory$dir copieddirectory$dir"); or warn "Could not copy directory$dir to copieddirectory$dir +: $?\n"; } # Rename the directory if (!-d "copieddirectory$dir") { print "copieddirectory$dir does not exist!\n" if $verbose; } if ($^O =~ /Win32/) { system("move copieddirectory$dir ..\\test1\\moveddirectory$dir + > out.txt"); if (-e "out.txt") { unlink("out.txt") or die "Can't unlink out.txt: $!\n"; } } else { system("/bin/mv copieddirectory$dir ../test1/moveddirectory$di +r"); or warn "Could not move directory$dir to test1: $?\n"; } # Delete the copied directory if ($^O =~ /Win32/) { rmdir("..\\test1\\moveddirectory$dir"); } else { system("/bin/rm -r ../test1/moveddirectory$dir"); or warn "Could not remove test1\\directory$dir: $?\n"; } # Remove the directory if ($^O =~ /Win32/) { rmdir("directory$dir") or die "Can't remove directory: $!\n"; } else { system("/bin/rm -r directory$dir"); or warn "Could not remove directory$dir: $?\n"; } } print "complete!\n" if ($verbose); # Change back to the top directory chdir($home_dir) or die "Can't move back to $home_dir: $!\n"; sub usage { print " $0 [options] Options: -num The number of loops to run, default $num -testdir Location to run the test within -verbose Run with messaging \n"; exit(0); }
        Looking more closely at this version of your code, I'm wondering...
        • Why do you do mkdir( "directory$dir",0755 ); and then turn right around and chmod that new directory to 0777? Why not just tell mkdir to use the intended permissions in the first place? (As it turns out, 0777 is said to be the default setting for mkdir.)
        • Even if you really had to do chmod on files or directories, why use a system call for that? Is there some problem with using the perl built-in chmod function? (I would expect it to work in an OS-independent manner.)
        • Why do the if ($^O =~ /Win32/) branching so many times? I think the two points above would eliminate most of the OS-dependent branching, but if you really still have some points where you need to branch, group those things together a little more, to minimize the number of conditional blocks, and make the code easier to read/maintain.
        • It looks like you may be lacking some OS-dependent branching that should be there (near the beginning, where your "die" messages always have backslash paths, regardless of OS).

        If you were to use File::Spec (as suggested above), here's how the code would start out:

        use strict; use warnings; use Cwd; use Getopt::Long; use File::Spec; # this will automatically load OS-appropriate functi +ons # ... [snip] ... # Get the current working directory my $home_dir = getcwd; # Change to the test directory chdir($testdir) or die "$0: chdir $testdir: $!"; # Set up test dirs if they do not exist for my $t ( qw/test test1/ ) { mkdir $t or die "mkdir ". File::Spec->catfile($testdir, $t) .": $! +\n"; } # Change to the test area chdir("test") or die "chdir test: $!\n"; # Directory Manipulation for my $dir ( 0 .. $num-1 ) { my $dirname = "directory$dir"; # Make the directory if (!-d $dirname ) { mkdir $dirname or die "mkdir ". File::Spec->catfile( $testdir, "test", $dirn +ame ) .": $!\n"; } #...
        I hope that gets the general idea across. It looks like your script is simply meant to check that certain operations work as hoped for, and doesn't really accomplish anything other than testing, so I won't pursue it further.

        I'll just emphasize that you should use perl built-in functions and core modules (e.g. File::Copy whenever they are available, rather than system calls to shell commands -- perl already provides a lot of OS-independence for you, and you'll have fewer problems and less code to write that way.

        If you really must use system calls to run OS-dependent tools, modularize and/or group those things so that they hang together for each OS. Your code tests the OS type in five different places, and you shouldn't have to do that test more than once (or not at all, with proper use of existing modules).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://700518]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2024-04-19 00:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found