Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

question on exitcode provided by status() method of Win32::Job

by stevehs17 (Novice)
on Nov 16, 2010 at 21:48 UTC ( #871846=perlquestion: print w/ replies, xml ) Need Help??
stevehs17 has asked for the wisdom of the Perl Monks concerning the following question:

I have a question on the Perl module in CPAN, Win32::Job. I'm using the module to impose a timeout on a script I want to run.

My question is this. In the code in the function below, I check the value of the exitcode returned by the spawned script (see the line " $state = $rs->{$pid}{exitcode};"), which the spawned script explicitly sets (when it can).

However, in come cases, I get 13 as a value for the exitcode returned by the spawned script, which isn't one of the values I return from the spawned script. In those cases, the spawned script seems to have been aborted, though I can't always tell why. (I should mention that I'm using the script for monitoring a complicated system that has a lot going on, so it's not altogether surprising that the spawned script should run into problems from time to time - though I'd like to know what they are.)

I've gone through the documentation for the Win32::Job on CPAN, and I've done some Google searches and searches on this site, and I haven't been able to figure out what a 13 returned as an exitcode means. Can you tell me what it means, or, at least, where I can look to find this out?

Thanks, Steven

use Win32::Job; sub runmons { my ($job, $pid, $ro, $timeout, $rs, $state, $w, $pfx); init(); oprn("Starting script " . basename($0) . '...', c::STARTPFX, $0); $job = Win32::Job->new(); die 'Failed to create Job object' unless ($job); oprn("Spawning process to run $CHILD...", c::NOTEPFX, $0); $pid = $job->spawn(undef, "perl $CHILD $0"); die "Failed to spawn process to execute $CHILD" unless ($pid); $ro = XMLin(c::MONOPTS, ForceArray => 1); $timeout = $ro->{timeoutsecs}[0]; die 'Timeout length not specified in ' . c::MONOPTS unless defined($timeout); oprn("Running $CHILD as PID $pid with a timeout of $timeout second +s...", c::NOTEPFX, $0); $job->run($timeout); $rs = $job->status(); $state = $rs->{$pid}{exitcode}; unless (grep { $state == $_ } @c::KNOWNERRVALS) { oprn("Error: received unknown exit code = $state from $CHILD. +Will convert to exception.", c::ERRPFX, $0); $state = c::EXCEPT; } $state == c::SUCCESS ? ($pfx = c::NOTEPFX) : ($pfx = c::ERRPFX); $w = errword($state, $0); oprn(basename($0) . " received exit code $state from $CHILD ($w)." +, $pfx, $0); return $state; };

Comment on question on exitcode provided by status() method of Win32::Job
Download Code
Re: question on exitcode provided by status() method of Win32::Job
by aquarium (Curate) on Nov 16, 2010 at 22:12 UTC
    would that be "access denied"? run filemon/procmon if you can to see why.
    the hardest line to type correctly is: stty erase ^H
Re: question on exitcode provided by status() method of Win32::Job
by ikegami (Pope) on Nov 16, 2010 at 22:46 UTC

    Sometimes programs to use system error codes as exit codes. For example, Perl uses $! as as the error code when you call die. A quick and dirty way of finding the meaning of an system error number is to assign it to $^E.

    $ perl -E'$^E=13; say $^E;' Permission denied

      That's strange?

      c:\test>perl -E"$^E=13; say $^E" The data is invalid

      Which matches with the MS definition


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I wasn't on Windows at a the time. I was showing how to get the value. Sorry, I didn't make that clear.
Re: question on exitcode provided by status() method of Win32::Job
by Anonymous Monk on Nov 16, 2010 at 23:09 UTC

    Thanks for the responses.

    My original thought was to do as you suggested, and assume that 13, the exit code returned, is a system code. I've looked it up, and so far all I've been able to find is that it means "The data is invalid."

    Now I'm trying to figure out (1) what exactly is meant by "The data is invalid", and (2) why the Win32::Job Perl module would sometimes get that error when running a spawned script.

    Any information or thoughts about (1) or (2) would be greatly appreciated.

    Thanks!

Re: question on exitcode provided by status() method of Win32::Job
by BrowserUk (Pope) on Nov 17, 2010 at 05:56 UTC

    The exitcode returned is whatever value the script returned. Which may or may not be relevant to the reason why it exited.

    For example, the code could be doing:

    ## 42 is such a crass true value exit(+1-2+3-4+5-6+7-8+9-10+11-12+13-14+15-16+17-18+19-20+21-22+23-24+2 +5);

    The point is, it is the value returned by the program or script you are running, which may or may not relate to windows system error codes.

    You need to look inside that script to understand what that number means.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Thanks for the suggestion. However, I wrote the spawned script and I know what exit codes it returns, and 13 isn't one of them. That's why I posted the question.

      I discussed the situation with my boss, and he had me check the version of the Win32::Job Perl module on CPAN. The version number is 0.01, the script was written in 2002, and it's never been updated. So it wouldn't be surprising if the mysterious exit code value were the result of a bug. Therefore, I'm getting rid of the Perl module and writing my own code to do what I need.

      Thanks for all the suggestions.

        So it wouldn't be surprising if the mysterious exit code value were the result of a bug.

        Hm. That seems most unlikely. Why would the module place an internal error code in place of the exit code of the script it is calling?

        When I look at CPAN, it looks like latest module version is 0.03, last updated during 2008. I am using it in some my programs and works fine for me. I usually check return value of run method to find if it timeed out, though.

        -- Roman

Re: question on exitcode provided by status() method of Win32::Job
by rohanpm (Novice) on Feb 29, 2012 at 04:43 UTC

    It's impossible to say exactly what the problem was without seeing the spawned script. I also use Win32::Job without problems. I think the lack of updates for Win32::Job actually means that it is stable, not that it's buggy.

    I notice that your child script is also written in perl. perl has some unintuitive exit code behavior on Windows, so I would consider the possibility that your child process is not exiting with the exit code you think it is.

    I've just been bitten myself by exit code issues on Win32, here are my discoveries which might be useful to some readers.

    Various problems all come from this conceptual difference between exit codes on Unix and Windows:

    • On a typical Unix system, the exit status is made up of 16 bits. 8 bits of that contain information such as whether or not the process exited due to a signal and whether or not it dumped core. Another 8 bits of that is the exit code. That's why you need to do $? >> 8 to get the exit code out of the status. Of course on a 32-bit system, this is all stored in a 32-bit integer - the other 16 bits are simply unused.
    • On a typical Windows system, there is no exit status, only an exit code - and that is a full 32 bits in size.

    Perl doesn't seem to handle large exit codes correctly on Windows. This code comes from win32/win32.c in v5.14.1:

    if (status < 0) { if (ckWARN(WARN_EXEC)) Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't spawn \"%s\": %s", argv[0], strerror(errno)); status = 255 * 256; } else status *= 256; PL_statusvalue = status;

    status is a signed int, casted from the unsigned int exit code of the process. PL_statusvalue is the value which eventually ends up in $?.

    The first problem is that, if the exit code was large enough that it wrapped around to a negative value while casting from unsigned to signed, perl will output a bogus warning like:

    Can't spawn "some command": No error

    ... and force the status to 255*256 (i.e. 255 << 8, an exit code of 255).

    The second problem is that status *= 256 (i.e. status << 8) overflows if status is too large ... irreversibly destroying the value.

    As well as these problems with system(), exit() silently takes only the lower 16 bits of whatever integer you pass it.

    The truncation/overflow of exit values is not at all a theoretical problem, as Windows uses large exit codes when processes exit abnormally. For example, let's say you used Win32::Job or some other native Win32 API to run an external process, and it dereferences an invalid pointer. The crashing process will exit with an exit code of 3221225477 / 0xC0000005 / STATUS_ACCESS_VIOLATION - that's how crashes work on Windows. Since you used a native Win32 API, you will get the real 32-bit exit code. Then let's say you naively try to propagate that exit code upwards to the calling process. You'll do an exit($status), where $status=3221225477. But Perl truncates the exit code to a confusingly simple value of 5 :)

    For what it's worth, this is the simplest way I found so far to get the real exit code from a system() on Windows: (note, you don't need to do anything like this with Win32::Job, it already does the right thing)

    sub win32_system { my (@command) = @_; my ($pid, $child_process, $exitcode); # this magic syntax makes system() async and return the # pid of the created process, see "perldoc perlport" $pid = system( 1, @command ); # Get the Win32 native handle to the process we just created Win32::Process::Open( $child_process, $pid, 0 ) || die; # Win32::Process equivalent of waitpid() $child_process->Wait( INFINITE ); $child_process->GetExitCode( $exitcode ); # Returns the real 32-bit exit code, no overflow return $exitcode; }

    And the simplest way I found to exit with an exit code larger than 16 bits:

    sub win32_exit { my ($exitcode) = @_; my $this_process; # get Win32 native handle to the current process Win32::Process::Open( $this_process, $$, 0 ) || die; # "kill" on Windows simply means # "force the process to exit with that exitcode" $this_process->Kill( $exitcode ); # we killed ourselves, should never get here die "Thou shalt never see this!"; }

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://871846]
Approved by eff_i_g
Front-paged by aquarium
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2014-09-23 05:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (210 votes), past polls