Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Signal handler - correct way to hijack TSTP

by hippo (Bishop)
on Apr 02, 2019 at 20:57 UTC ( [id://1232049]=note: print w/replies, xml ) Need Help??


in reply to Signal handler - correct way to hijack TSTP

I've tried your code but it required a small change to run for me under perl v5.20.3. As it stands, the -t causes it grief:

[11541] START [02@21:54:27] Warning: Use of "-t" without parentheses i +s ambiguous at ./sigs.pl line 61. [11541] START [02@21:54:27] Use of ?PATTERN? without explicit operator + is deprecated at ./sigs.pl line 61. [11541] START [02@21:54:27] Search pattern not terminated or ternary o +perator parsed as search pattern at ./sigs.pl line 61. Leaving...

So changing line 61 to be this:

logger(INFO, sprintf("We %s connected to a TTY", -t () ? 'ARE' : 'ARE +NOT'));

It then runs and appears to handle the TSTP and subsequent CONT perfectly well. Which version of perl are you running and on which OS?

Replies are listed 'Best First'.
Re^2: Signal handler - correct way to hijack TSTP
by seki (Monk) on Jan 11, 2021 at 11:07 UTC

    Hi and thank you for your suggestion that I did missed.

    By testing the same code on 5.22.0 I encountered the same ambiguity warning as you. The code was initially written for a Perl 5.16.3 that runs on a Linux RHEL 6. By doing some more tests today, it seems that my problem was probably related to a mix of logs from different processes -a forgotten previous instance and a new one- in the same nohup.out i see different pids

    258135 START 11@11:54:00 Setting up signal handlers
    258135 RUN 11@11:54:00 Program PID 258135 has started
    258135 RUN 11@11:54:00 We ARE NOT connected to a TTY
    258135 In loop; sleeping...
    257458 In loop; sleeping...
    258135 RUN 11@11:54:02 SIGTSTP / CTRL-Z received. Suspending...
    257458 In loop; sleeping...
    257458 In loop; sleeping...
    

    Anyway i have improved my sscce by adding a proper propagation of the CONT signal to the childs. And I of course noticed that in case of a TERM to a child, it remains as a 'defunct' process until the parent terminates. I know about waitpid() that should fix that but am unsure of its correct use. Where to put it?

    here is my upgraded example:

    #!/usr/bin/perl # test_signal.pl - Testing signals handlers # to test without TTY, you can use 'nohup perl test_signal.pl' # to test the suspend/continue either # in foreground: C-z then 'bg' or 'fg' # in background: kill -tstp <pid> / kill -cont <pid> use strict; use warnings; use feature 'say'; use constant { FATAL => 0, ERROR => 1, INFO => 2, DEBUG => 3, TRACE => 4 }; $|++; my $stop_flag = 0; BEGIN { my $LOG_LVL = TRACE; logger(INFO, "Setting up signal handlers"); sub suspend_trap { logger(INFO, "SIGTSTP / CTRL-Z received. Suspen +ding..."); # $SIG{TSTP} = 'IGNORE'; $SIG{TSTP} = 'DEFAULT'; kill 'TSTP', -(getpgrp $$); # $SIG{TSTP} = \&suspend_trap; # Paranoid - for + unreliable signals - see Perl cookbook ch16.17.3 } # A handler can be # code ref (sub) # name of sub # 'IGNORE' # 'DEFAULT' $SIG{__WARN__} = sub { my $m = shift; chomp $m; logger(ERROR, $m); + }; $SIG{__DIE__} = sub { my $m = shift; chomp $m; logger(FATAL, $m . + " Leaving..."); exit; }; my @childs = (); $SIG{HUP} = sub { logger(INFO, "SIGHUP received. Forking."); my $pid = fork(); if ($pid){ logger(INFO, "Child PID $pid has been f +orked."); push @childs, $pid; } }; $SIG{INT} = sub { logger(INFO, "SIGINT / CTRL-C received (Int +errupt from keyboard). Leaving..."); $stop_flag++; }; $SIG{QUIT} = sub { logger(INFO, "SIGQUIT / CTRL-\\ received (Q +uit from keyboard). Leaving..."); $stop_flag++; }; $SIG{ABRT} = sub { logger(INFO, "SIGABRT received (Probable ab +normal process termination requested by a library). Leaving..."); $st +op_flag++; }; $SIG{TERM} = sub { logger(INFO, "SIGTERM - External terminatio +n request. Leaving..."); $stop_flag++; }; $SIG{TSTP} = \&suspend_trap; $SIG{CONT} = sub { $SIG{TSTP} = \&suspend_trap; logger(INFO, "SIGCONT received - continue a +fter suspension."); kill 'CONT', @childs; }; # Log some text, depending on the current log level sub logger { my ($lvl, $msg) = @_; say __stamp($msg) if ($LOG_LVL >= $lvl); } # This sub Copyright (c) 1996,97,98,99,2000,01 by Randal L. Schwar +tz sub __stamp { my ($message) = @_; my $stamp = sprintf "[%d] %s [%02d@%02d:%02d:%02d] ", $$, ${^G +LOBAL_PHASE}, (localtime)[3,2,1,0]; $message =~ s/^/$stamp/gm; $message; } } logger(INFO, "Program PID $$ has started"); logger(INFO, sprintf("We %s connected to a TTY", -t () ? 'ARE' : 'ARE +NOT')); while (!$stop_flag){ say "[$$] In loop; sleeping..."; sleep 3; }; logger(INFO, "Reached normal end. Ciao."); END { logger(INFO, "That's all folks!") };

    The best programs are the ones written when the programmer is supposed to be working on something else. - Melinda Varian
      I know about waitpid() that should fix that but am unsure of its correct use. Where to put it?

      Put that into the signal handler for $SIG{CHLD} in the parent. This is the signal a process gets if a subprocess goes away. See perlipc.

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

        Thank you, it is becoming actually satisfactory!

        htop helped much to test. My signal experimentation is now

        #!/usr/bin/perl # test_signal.pl - Testing signals handlers # to test without TTY, you can use 'nohup perl test_signal.pl' # to test the suspend/continue either # in foreground: C-z then 'bg' or 'fg' # in background: kill -tstp <pid> / kill -cont <pid> use strict; use warnings; use feature 'say'; use POSIX qw(:signal_h :errno_h :sys_wait_h); use constant { FATAL => 0, ERROR => 1, INFO => 2, DEBUG => 3, TRACE => 4 }; $|++; my $stop_flag = 0; BEGIN { my $LOG_LVL = TRACE; logger(INFO, "Setting up signal handlers"); my $childs = {}; sub suspend_trap { logger(INFO, "SIGTSTP / CTRL-Z (Terminal Stop) +received. Suspending..."); # there is also SIGSTOP that is untrappable STO +P # $SIG{TSTP} = 'IGNORE'; $SIG{TSTP} = 'DEFAULT'; kill 'TSTP', -(getpgrp $$); # propagate the si +gnal to the whole pgroup # $SIG{TSTP} = \&suspend_trap; # DISABLED due t +o endless notifications - Paranoid - for unreliable signals - see Per +l cookbook ch16.17.3 } sub child_reaper { logger(INFO, "SIGCHLD received."); my $pid; while (($pid = waitpid(-1, &WNOHANG)) > 0){ # note # $exit_value = $? >> 8; -> WEXITSTATUS # $signal_num = $? & 127; -> WTERMSIG # $dumped_core = $? & 128; if(WIFEXITED($?)){ logger(INFO, sprintf("Child %d exited n +ormally with value %d", $pid, WEXITSTATUS($?))); delete $childs->{$pid}; } elsif (WIFSIGNALED($?)){ logger(INFO, sprintf("Child %d was kill +ed by a signal %d", $pid, WTERMSIG($?))); delete $childs->{$pid}; } else { logger(INFO, "False alarm for child $pi +d"); # "false alarm" WTF?!! - Perl cookbook ch16.20 } logger(INFO, "Child $pid dumped core") if $ +? & 128; } $SIG{CHLD} = \&child_reaper; # Paranoid - for u +nreliable signals - see Perl cookbook ch16.20 } # A handler can be # code ref (sub) # name of sub # 'IGNORE' # 'DEFAULT' $SIG{__WARN__} = sub { my $m = shift; chomp $m; logger(ERROR, $m); + }; $SIG{__DIE__} = sub { my $m = shift; chomp $m; logger(FATAL, $m . + " Leaving..."); exit; }; $SIG{HUP} = sub { logger(INFO, "SIGHUP received. Forking."); my $pid = fork(); if ($pid){ logger(INFO, "Child PID $pid has been f +orked."); $childs->{$pid}++;; } }; $SIG{INT} = sub { logger(INFO, "SIGINT / CTRL-C received (Int +errupt from keyboard). Leaving..."); $stop_flag++; kill 'INT', keys % +$childs; }; $SIG{QUIT} = sub { logger(INFO, "SIGQUIT / CTRL-\\ received (Q +uit from keyboard). Leaving..."); $stop_flag++; kill 'QUIT', keys %$c +hilds; }; $SIG{ABRT} = sub { logger(INFO, "SIGABRT received (Probable ab +normal process termination requested by a library). Leaving..."); $st +op_flag++; kill 'ABRT', keys %$childs; }; $SIG{TERM} = sub { logger(INFO, "SIGTERM - External terminatio +n request. Leaving..."); $stop_flag++; kill 'TERM', keys %$childs;}; $SIG{TSTP} = \&suspend_trap; $SIG{CONT} = sub { $SIG{TSTP} = \&suspend_trap; logger(INFO, "SIGCONT received - continue a +fter suspension."); kill 'CONT', keys %$childs; # we could also + send to the whole pgroup like for TSTP }; $SIG{CHLD} = \&child_reaper; # Log some text, depending on the current log level sub logger { my ($lvl, $msg) = @_; say __stamp($msg) if ($LOG_LVL >= $lvl); } # This sub Copyright (c) 1996,97,98,99,2000,01 by Randal L. Schwar +tz sub __stamp { my ($message) = @_; my $stamp = sprintf "[%d] %s [%02d@%02d:%02d:%02d] ", $$, ${^G +LOBAL_PHASE}, (localtime)[3,2,1,0]; $message =~ s/^/$stamp/gm; $message; } logger(INFO, "End of BEGIN (...or beginning of end? ;o)"); } logger(INFO, "Program PID $$ has started"); logger(INFO, sprintf("We %s connected to a TTY", -t () ? 'ARE' : 'ARE +NOT')); while (!$stop_flag){ logger(INFO, "In loop; sleeping..."); sleep 3; }; logger(INFO, "Reached normal end. Ciao."); END { logger(INFO, "That's all folks!") };

        The best programs are the ones written when the programmer is supposed to be working on something else. - Melinda Varian

Log In?
Username:
Password:

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

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

    No recent polls found