Hi,
   I'm using GNU bash, version 5.1.8(1)-release (x86_64-redhat-linux-gnu),
   on a CentOS Stream 9 machine.
   I am unable to understand what I consider an unexpected behavior from a
   bash script that does the following:
   - use "set -e"
   - install an EXIT trap that takes some time to execute
   - install a TERM trap that just echoes and then re-kills the script
   with TERM
   - start a "sleep infinity" process
   Both traps unset traps for EXIT and TERM on entry.
   On reboot, the TERM signal is not caught immediately because of the
   "sleep" subprocess, which
   is killed and returns an error.  Bash then could start the EXIT trap or
   handle the TERM signal.
   What I observe is that it "starts" the EXIT trap but is then
   immediately interrupted by the TERM signal.
   It does the TERM trap, which ends in the re-kill.
   But then it continues with the EXIT trap for quite a number of seconds
   (90 in the example below).
   I would have expected the re-kill to prevent bash from doing anything
   significant after that
   kill invocation. It looks as if the TERM signal sent by the re-kill was
   not received at all, waiting
   for bash to be killed by a final KILL from reboot.
   Note: the machine executing the script does only that: no other user
   processes running, load close to 0.
   Notice that the TERM trap is preceded by 3 "+" signs, which seems to
   indicate that the trap was activated
   while at level 2 (the EXIT trap) which did not have time to do/print
   anything useful, but is resumed after
   the TERM trap.
   Looking for some insight to explain that behavior, which I cannot
   reproduce without a reboot.
   Killing with TERM on the process group outputs 'Terminated' (from sleep
   begin killed I suppose)
   and executes only the TERM trap.
   I have also reproduced the issue with version 5.3.9 (commit
   637f5c8696a6adc9b4519f1cd74aa78492266b7f),
   although with a slightly different behavior.  I could share these
   outputs if relevant.
   The script:
   --------------- 8< ------------------
   #!/usr/bin/env bash
   set -ex
   function errtrap() {
     trap - EXIT TERM
     echo "errtrap"
     i=0
     while (( ++i <= 100 )); do
       echo "cleanup $i"
       sleep 1
     done
     return 0
   }
   function termtrap() {
     trap - EXIT TERM
     echo "termtrap"
     return 0
   }
   trap 'errtrap || true' EXIT
   trap 'signal=SIGTERM; termtrap || true; kill -TERM $$' TERM
   sleep infinity
   --------------- 8< ------------------
   An excerpt of the output:
   --------------- 8< ------------------
   + trap 'errtrap || true' EXIT
   + trap 'signal=SIGTERM; termtrap || true; kill -TERM $$' TERM
   + sleep infinity
   +++ signal=SIGTERM
   +++ termtrap
   +++ trap - EXIT TERM
   +++ echo termtrap
   termtrap
   +++ return 0
   +++ kill -TERM 3129
   ++ errtrap
   ++ trap - EXIT TERM
   ++ echo errtrap
   errtrap
   ++ i=0
   ++ ((  ++i <= 100  ))
   ++ echo 'cleanup 1'
   cleanup 1
   ++ sleep 1
   ++ ((  ++i <= 100  ))
   ++ echo 'cleanup 2'
   cleanup 2
   ++ sleep 1
   ...
   ++ ((  ++i <= 100  ))
   ++ echo 'cleanup 89'
   cleanup 89
   ++ sleep 1
   ++ ((  ++i <= 100  ))
   ++ echo 'cleanup 90'
   cleanup 90
   ++ sleep 1
   --------------- 8< ------------------
   An excerpt of the strace output:
   --------------- 8< ------------------
   access("/usr/bin/sleep", R_OK)          = 0
   rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
   rt_sigprocmask(SIG_BLOCK, [INT TERM CHLD], [], 8) = 0
   lseek(255, -1, SEEK_CUR)                = 371
   clone(child_stack=NULL,
   flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
   child_tidptr=0x7fe431140a10) = 3130
   rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
   rt_sigaction(SIGINT, {sa_handler=0x55988f237fe0, sa_mask=[],
   sa_flags=SA_RESTORER, sa_restorer=0x7fe430e3fc30},
   {sa_handler=0x55988f261aa0, sa_m>
   wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0, NULL) = 3130
   --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=1, si_uid=0} ---
   --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=1, si_uid=0} ---
   rt_sigreturn({mask=[CHLD]})             = 3130
   --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=1, si_uid=0} ---
   rt_sigreturn({mask=[CHLD]})             = 3130
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, NULL, [CHLD], 8) = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, NULL, [CHLD], 8) = 0
   write(2, "+++ signal=SIGTERM\n", 19)    = 19
   write(2, "+++ termtrap\n", 13)          = 13
   write(2, "+++ trap - EXIT TERM\n", 21)  = 21
   rt_sigaction(SIGTERM, {sa_handler=0x55988f261aa0, sa_mask=[],
   sa_flags=SA_RESTORER, sa_restorer=0x7fe430e3fc30},
   {sa_handler=0x55988f25cad0, sa_>
   write(2, "+++ echo termtrap\n", 18)     = 18
   fstat(1, {st_mode=S_IFREG|0640, st_size=180, ...}) = 0
   write(1, "termtrap\n", 9)               = 9
   write(2, "+++ return 0\n", 13)          = 13
   write(2, "+++ kill -TERM 3129\n", 20)   = 20
   kill(3129, SIGTERM)                     = 0
   --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=3129,
   si_uid=1000} ---
   rt_sigreturn({mask=[CHLD]})             = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   write(2, "++ errtrap\n", 11)            = 11
   write(2, "++ trap - EXIT TERM\n", 20)   = 20
   write(2, "++ echo errtrap\n", 16)       = 16
   write(1, "errtrap\n", 8)                = 8
   write(2, "++ i=0\n", 7)                 = 7
   write(2, "++ ((  ++i <= 100  ))\n", 22) = 22
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   write(2, "++ echo 'cleanup 1'\n", 20)   = 20
   write(1, "cleanup 1\n", 10)             = 10
   write(2, "++ sleep 1\n", 11)            = 11
   rt_sigprocmask(SIG_BLOCK, NULL, [CHLD], 8) = 0
   rt_sigprocmask(SIG_BLOCK, [INT TERM CHLD], [CHLD], 8) = 0
   clone(child_stack=NULL,
   flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
   child_tidptr=0x7fe431140a10) = 3207
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
   rt_sigaction(SIGINT, {sa_handler=0x55988f237fe0, sa_mask=[],
   sa_flags=SA_RESTORER, sa_restorer=0x7fe430e3fc30},
   {sa_handler=0x55988f237fe0, sa_m>
   wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3207
   rt_sigaction(SIGINT, {sa_handler=0x55988f261aa0, sa_mask=[],
   sa_flags=SA_RESTORER, sa_restorer=0x7fe430e3fc30},
   {sa_handler=0x55988f237fe0, sa_m>
   ioctl(2, TIOCGWINSZ, 0x7fffc25fff50)    = -1 ENOTTY (Inappropriate
   ioctl for device)
   rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
   write(2, "++ ((  ++i <= 100  ))\n", 22) = 22
   ...
   --------------- 8< ------------------
   --
   Daniel Villeneuve

Reply via email to