2

Consider the following script:

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP $$
echo 'after stop' > ${OFILE}

In an interactive shell the script is stopped and the output is ok. However, if it is started as

screen -d -m ./test.sh

the output is after stop. Does screen handle the signal? How to suspend a process in a screen session?

Andrey
  • 820
  • Have you used echo $$? – Braiam Nov 05 '15 at 04:01
  • Yes, I checked that $$ is a PID of bash. Another interesting thing is that if I run the script without dettaching the session then screen informs that "child has been stopped, restarting". – Andrey Nov 05 '15 at 04:11

2 Answers2

1

Probably the screen process is restarting the stopped bash process. I tried following (sending SIGSTOP signal to both of the processes):

test.sh

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
echo 'screen pid ' $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g') >> ${OFILE}
echo 'test.sh pid ' $$ >> ${OFILE}
kill -SIGSTOP $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g')
kill -SIGSTOP $$
echo 'after stop' >> ${OFILE}

screen command

screen -dmS sc_test ./test.sh

log file

ok
screen pid  4453
test.sh pid  4454

listing screens

screen -list
There is a screen on:
        4453.sc_test    (11/05/2015 10:45:20 AM)        (Detached)
1 Socket in /var/run/screen/S-root.
Kadir
  • 264
  • 1
  • 6
1

Examination of the source code of GNU screen-4.0.2 showed that screen checks if its child processes are stopped and then tries to resume them with SIGCONT. Here is a relevant part of the screen.c:

1561 if (WIFSTOPPED(wstat))
1562   {   
1563     debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, p->w_pid, WSTOPSIG(wstat));
....
1578     /* Try to restart process */
1579     Msg(0, "Child has been stopped, restarting.");
1580     if (killpg(p->w_pid, SIGCONT))
1581       kill(p->w_pid, SIGCONT);
1582   }   

It seems this behaviour cannot be changed with an option or via a config file. The suggested solution to stop screen process might have undesirable side effects. A better approach is to hide stopped process from the screen. For a bash script this can be done with a subshell:

#!/bin/bash
(   
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP ${BASHPID}
echo 'after stop' > ${OFILE}
)   

For other shells it might be not so straightforward, e.g. for tcsh:

#!/bin/tcsh
(\  
set OFILE='log' ;\
echo 'ok' > ${OFILE} ;\
kill -STOP `exec sh -c 'echo ${PPID}'` ;\
echo 'after stop' > ${OFILE} \
)   

The key difference is the method to get PID of the subshell, more info here.

Alternatively, the script can be started without modification with a wrapper script:

#!/bin/bash
SCRIPTDIR="$(dirname "$(readlink -f -n "$0")")"
${SCRIPTDIR}/test.sh

To run:

screen -d -m ./wrapper.sh
Andrey
  • 820