diff -up foomatic-filters-3.0-20080211/foomatic-rip.in.clean-up-on-cancel foomatic-filters-3.0-20080211/foomatic-rip.in --- foomatic-filters-3.0-20080211/foomatic-rip.in.clean-up-on-cancel 2008-02-11 21:16:36.000000000 +0000 +++ foomatic-filters-3.0-20080211/foomatic-rip.in 2008-03-13 11:42:35.000000000 +0000 @@ -155,19 +155,19 @@ my $EXIT_PRNERR_NORETRY = 2; # printer my $EXIT_JOBERR = 3; # job is defective my $EXIT_SIGNAL = 4; # terminated after catching signal my $EXIT_ENGAGED = 5; # printer is otherwise engaged (connection - # refused) + # refused) my $EXIT_STARVED = 6; # starved for system resources my $EXIT_PRNERR_NORETRY_ACCESS_DENIED = 7; # bad password? bad port - # permissions? + # permissions? my $EXIT_PRNERR_NOT_RESPONDING = 8; # just doesn't answer at all - # (turned off?) + # (turned off?) my $EXIT_PRNERR_NORETRY_BAD_SETTINGS = 9; # interface settings are invalid my $EXIT_PRNERR_NO_SUCH_ADDRESS = 10; # address lookup failed, may be - # transient + # transient my $EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS = 11; # address lookup failed, not - # transient + # transient my $EXIT_INCAPABLE = 50; # printer wants (lacks) features - # or resources + # or resources # Standard Unix signal names #my SIGHUP = 1; #my SIGINT = 2; @@ -181,6 +181,21 @@ my $EXIT_INCAPABLE = 50; my $ESPIPE = 29; # the errno value when seeking a pipe or socket +# The modern_shell() function will register the PIDs of all shell calls, +# so that rip_die() can kill these processes +my %pids; + +# $kidgeneration stays 0 for the main process, child processes of the +# main process get $kidgeneration = 1, their children 2, ... +my $kidgeneration = 0; + +# Catch signals +my $retval = $EXIT_PRINTED; +use sigtrap qw(handler set_exit_canceled normal-signals + handler set_exit_error error-signals + handler set_exit_prnerr USR1 + handler set_exit_prnerr_noretry USR2 + handler set_exit_engaged TTIN); ## Some important variables @@ -2153,7 +2168,7 @@ if ($debug) { } - + ## From here on we have to repeat all the rest of the program for ## every file to print @@ -3538,13 +3553,8 @@ sub getrendererhandle { print $logh "${added_lf}Starting renderer\n"; - # Catch signals + # Reset return value of the renderer $retval = $EXIT_PRINTED; - use sigtrap qw(handler set_exit_prnerr USR1 - handler set_exit_prnerr_noretry USR2 - handler set_exit_engaged TTIN); - - # Variables for the kid processes reporting their state # Set up a pipe for the kids to pass their exit stat to the main process pipe KID_MESSAGE, KID_MESSAGE_IN; @@ -3585,6 +3595,8 @@ sub getrendererhandle { return ( *KID3, $kid3 ); } else { + $kidgeneration += 1; + close KID3; pipe KID4_IN, KID4; @@ -3774,6 +3786,8 @@ sub getrendererhandle { print $logh "KID3 finished\n"; exit $EXIT_PRINTED; } else { + $kidgeneration += 1; + # child, trailing task on the pipe; we write jcl stuff close KID4; close KID3_IN; @@ -4112,6 +4126,8 @@ sub getfileconverterhandle { return ( *KID1_IN, $kid1 ); } else { + $kidgeneration += 1; + # We go on reading the job data and stuff it into the file # converter close KID1_IN; @@ -4253,6 +4269,8 @@ sub getfileconverterhandle { print $logh "KID1 finished\n"; exit $EXIT_PRINTED; } else { + $kidgeneration += 1; + # child, first part of the pipe, reading in the data from # standard input and stuffing it into the file converter # after putting in the already read data (in $alreadyread) @@ -4580,6 +4598,8 @@ sub getdocgeneratorhandle { return ( *KID0_IN, $kid0 ); } + $kidgeneration += 1; + # we are the kid; we generate the documentation page close KID0_IN; @@ -4867,66 +4887,100 @@ sub rip_die { my $errmsg = "$!"; my $errcod = $! + 0; - # Close the documentation page generator (if it was used) - if ($docgeneratorpid) { - if ($kid0) { - print $logh "Killing process $kid0 (KID0)\n"; - kill(9, $kid0); + # Log that we are dying ... + print $logh "Process dying with \"$message\", exit stat: $exitstat\n\terror: $errmsg ($errcod)\n"; + + print $logh "Cleaning up ...\n"; + foreach my $killsignal (15, 9) { + + # Kill all registered subshells + foreach my $pid (keys %pids) { + print $logh "Killing process $pid ($pids{$pid}) and its subprocesses with signal $killsignal\n"; + # This call kills the process group with group ID $pid, the + # group which was formed from the initial process $pid which + # contains $pid and all its subprocesses + kill(-$killsignal, $pid); + # If the system does not support process groups and therefore + # the call above does not kill anything, kill at least $pid + kill($killsignal, $pid); } - $docgeneratorpid = 0; - } - # Close the file converter (if it was used) - if ($fileconverterpid) { + # Close the documentation page generator (if it was used) + if ($kid0) { + print $logh "Killing process $kid0 (KID0) with signal $killsignal\n"; + kill($killsignal, $kid0); + } + + # Close the file converter (if it was used) if ($kid2) { - print $logh "Killing process $kid2 (KID2)\n"; - kill(9, $kid2); + print $logh "Killing process $kid2 (KID2) with signal $killsignal\n"; + kill($killsignal, $kid2); } if ($kid1) { - print $logh "Killing process $kid1 (KID1)\n"; - kill(9, $kid1); + print $logh "Killing process $kid1 (KID1) with signal $killsignal\n"; + kill($killsignal, $kid1); } - $fileconverterpid = 0; - } - # Close the renderer - if ($rendererpid) { + # Close the renderer if ($kid4) { - print $logh "Killing process $kid4 (KID4)\n"; - kill(9, $kid4); + print $logh "Killing process $kid4 (KID4) with signal $killsignal\n"; + kill($killsignal, $kid4); } if ($kid3) { - print $logh "Killing process $kid3 (KID3)\n"; - kill(9, $kid3); + print $logh "Killing process $kid3 (KID3) with signal $killsignal\n"; + kill($killsignal, $kid3); } - $rendererpid = 0; + + # Wait some time for the processes to close + sleep(5 - $kidgeneration) if $killsignal != 9; } - print $logh "Process dying with \"$message\", exit stat: $exitstat\n\terror: $errmsg ($errcod)\n"; - if ($spooler eq 'ppr_int') { - # Special error handling for PPR intefaces - $message =~ s/\\/\\\\/; - $message =~ s/\"/\\\"/; - my @messagelines = split("\n", $message); - my $firstline = "TRUE"; - for my $line (@messagelines) { - modern_system("lib/alert $printer $firstline \"$line\""); - $firstline = "FALSE"; + # Do the debug dump and the PPR error handling only from the main process + if ($kidgeneration == 0) { # We are the main process + + if ($spooler eq 'ppr_int') { + # Special error handling for PPR intefaces + $message =~ s/\\/\\\\/; + $message =~ s/\"/\\\"/; + my @messagelines = split("\n", $message); + my $firstline = "TRUE"; + for my $line (@messagelines) { + modern_system("lib/alert $printer $firstline \"$line\""); + $firstline = "FALSE"; + } + } else { + print STDERR $message . "\n"; + } + if ($debug) { + use Data::Dumper; + local $Data::Dumper::Purity=1; + local $Data::Dumper::Indent=1; + print $logh Dumper($dat); } - } else { - print STDERR $message . "\n"; - } - if ($debug) { - use Data::Dumper; - local $Data::Dumper::Purity=1; - local $Data::Dumper::Indent=1; - print $logh Dumper($dat); } + + ## The End + print $logh "${added_lf}Closing foomatic-rip.\n"; + close $logh; + exit $exitstat; } # Signal handling routines +sub do_nothing { +} + +sub set_exit_canceled { + $retval = $EXIT_PRINTED; + rip_die ("Caught termination signal: Job canceled", $retval); +} + +sub set_exit_error { + $retval = $EXIT_SIGNAL; + rip_die ("Caught error signal: Error in renderer, driver, or foomatic-rip", $retval); +} + sub set_exit_prnerr { $retval = $EXIT_PRNERR; } @@ -6592,20 +6646,36 @@ sub read_attribute_file { sub modern_system { my (@list) = @_; - if (($modern_shell =~ /.+/) && ($modern_shell ne '/bin/sh')) { - # a "modern" shell other than the default shell was specified - my $pid = fork(); - - ($pid < 0) && die "failed to fork()"; - - if ($pid == 0) { # child, execute the commands under a modern shell - exec($modern_shell, "-c", @list); - die "exec($modern_shell, \"-c\", @list);"; - } else { # parent, wait for the child - waitpid($pid, 0); - } - } else { # the system shell is "modern" enough. - system(@list); + if ($modern_shell |~ /.+/) { + # No "modern" shell other than the default shell was specified + $modern_shell = '/bin/sh'; + } + + my $pid = fork(); + ($pid < 0) && die "failed to fork()"; + + if ($pid == 0) { # child, execute the commands under a modern shell + # If the system supports process groups, we create a process + # group of this subshell process. All the children of this + # process (calls of external filters, renderers, or drivers) + # will be members of this process group and so by killing this + # process group we can kill all subprocesses and so we can + # cleanly cancel print jobs + eval("setpgrp()"); + # Stop catching signals + #use sigtrap qw(die normal-signals error-signals + # handler do_nothing USR1 USR2 TTIN); + exec($modern_shell, "-c", @list); + rip_die("exec($modern_shell, \"-c\", @list);", + $EXIT_PRNERR_NORETRY_BAD_SETTINGS); + } else { # parent, register child's PID, wait for the child, and + # unregister the PID + $pids{$pid} = substr(join(" ", @list), 0, 100) . + (length(join(" ", @list)) > 100 ? "..." : ""); + print $logh "Starting process $pid: \"$pids{$pid}\"\n"; + waitpid($pid, 0); + print $logh "Process $pid ending: \"$pids{$pid}\"\n"; + delete $pids{$pid}; } }