Why is Bash handling child processes different compared to Sh - bash

The tini init-process, used in Docker, mentions that process group killing is not activated by default and gives the following example:
docker run krallin/ubuntu-tini sh -c 'sleep 10'
If I run this, and press Ctrl-C immediately after, I indeed have to wait for 10 seconds till the child process exits.
However, if instead of sh I used bash:
docker run krallin/ubuntu-tini bash -c 'sleep 10'
and press Ctrl-C, the process exits immediately.
Why do sh (which is symlinked to dash) and bash behave differently towards this child process?
And how does Bash kill the child process, I thought Bash does not propagate signals by default?

Answered thanks to chepner and Charles Duffy:
bash -c has an implicit optimization where it uses exec to replace itself if possible. sh (dash) does not have this optimization. See also this observation.
To verify:
Process tree using bash:
❯ docker run --name test --rm --detach krallin/ubuntu-tini bash -c 'sleep 60'
03194d48a4dcc8225251fe1e5de2dcbb901c8a9cfd0853ae910bfe4d3735608d
❯ docker exec test ps axfo pid,ppid,args
PID PPID COMMAND
1 0 /usr/bin/tini -- bash -c sleep 60
7 1 sleep 60
Process tree using sh:
❯ docker run --name test --rm --detach krallin/ubuntu-tini sh -c 'sleep 60'
e56f207509df4b0b57f8e6b2b2760835f6784a147b200d798dffad112bb11d6a
❯ docker exec test ps axfo pid,ppid,args
PID PPID COMMAND
1 0 /usr/bin/tini -- sh -c sleep 60
7 1 sh -c sleep 60
8 7 \_ sleep 60

Related

How to execute a shell script as input on an interactive bash pod in Kubernetes?

I have a shell script my-script.sh like:
#!/bin/bash
while true; do
echo '1'
done
I can deploy a bash pod in Kubernetes like:
kubectl run my-shell --rm -it --image bash -- bash
Now, I want to execute the script on bash. How can I pass my-script.sh as input to bash? Something like
kubectl run my-shell --rm -it --image bash -- /bin/bash -c < my-script.sh
Just drop the -t to kubectl run (because you're reading from stdin, not a terminal) and the -c from bash (because you're passing the script on stdin, not as an argument):
$ kubectl run my-shell --rm -i --image docker.io/bash -- bash < my-script.sh
If you don't see a command prompt, try pressing enter.
1
1
1
1
...

Unable to Find Entrypoint For Nextcloud (Alpine-based Version) For a Cron Container

I'm using Docker with Rancher v1.6, setting up a Nextcloud stack.
I would like to use a dedicated container for running cron tasks every 15 minutes.
The "normal" Nextcloud Docker image can simply use the following:
entrypoint: |
bash -c 'bash -s <<EOF
trap "break;exit" SIGHUP SIGINT SIGTERM
while /bin/true; do
su -s "/bin/bash" -c "/usr/local/bin/php /var/www/html/cron.php" www-data
echo $$(date) - Running cron finished
sleep 900
done
EOF'
(Pulled from this GitHub post)
However, the Alpine-based image does not have bash, and so it cannot be used.
I found this script in the list of examples:
#!/bin/sh
set -eu
exec busybox crond -f -l 0 -L /dev/stdout
However, I cannot seem to get that working with my docker-compose.yml file.
I don't want to use an external file, just to have the script entirely in the docker-compose.yml file, to make preparation and changes a bit easier.
Thank you!

"bash -c" vs. "dash -c"

dash -c behaves differently from bash -c:
docker run -it ubuntu /bin/dash -c ps
PID TTY TIME CMD
1 ? 00:00:00 sh
7 ? 00:00:00 ps
docker run -it ubuntu /bin/bash -c ps
PID TTY TIME CMD
1 ? 00:00:00 ps
Is there an explanation for this difference?
bash has an optimisation where the very last command in a script implicitly gets executed with exec. dash recently gained this optimisation as well, but not yet in the version you're using. You'll see the same behaviour with bash -c 'exec ps' and dash -c 'exec ps'.

Why does docker run -t keep the bash process from exiting and stopping the container

I understand that a docker container will stop once the main process (the command) ends.
I also understand that the -t will allocate a pseudo TTY
docker run -t <image> <command>
Now, when I run bash by default the container stops immediately, which is expected
docker run fedora bash
docker -ps =>(this gives empty list)
But when I run bash with -t like this
docker run -t fedora bash
[CTRL+C]
docker ps =>(this shows one running container)
Why does -t keep the bash process running? Although the same -t will not keep, for example, echo running
docker run -t fedora echo "hello"
[CTRL+C]
docker ps =>(this shows empty list although we added -t)
When you use bash without -t it exits with status 0 immediately since you're not supplying any command to bash using -c option.
By using -t you are allocating a pseudo-tty to bash process. However you're not using another important -i option that keeps STDIN open even if not attached. Without -i you cannot type anything inside the bash hence bash keeps running waiting for the next command.
echo command exits with status 0 immediately because echo is not an interactive process like bash.
So right way to start bash is:
docker run -it fedora bash
As per official documentation:
For interactive processes (like a shell), you must use -i -t together in order to allocate a tty for the container process. -i -t is often written -it.

Why docker exec is killing nohup process on exit?

I have running docker ubuntu container with just a bash script inside. I want to start my application inside that container with docker exec like that:
docker exec -it 0b3fc9dd35f2 ./main.sh
Inside main script I want to run another application with nohup as this is a long running application:
#!/bin/bash
nohup ./java.sh &
#with this strange sleep the script is working
#sleep 1
echo `date` finish main >> /status.log
The java.sh script is as follow (for simplicity it is a dummy script):
#!/bin/bash
sleep 10
echo `date` finish java >> /status.log
The problem is that java.sh is killed immediately after docker exec returns. The question is why?
The only solution I found out is to add some dummy sleep 1 into the first script after nohup is started. Than second process is running fine. Do you have any ideas why it is like that?
[EDIT]
Second solution is to add some echo or trap command to java.sh script just before sleep. Than it works fine. Unfortunately I cannot use this workaround as instead of this script I have java process.
This is not an answer, but I still don't have the required reputation to comment.
I don't know why the nohup doesn't work. But I did a workaround that worked, using your ideas:
docker exec -ti running_container bash -c 'nohup ./main.sh &> output & sleep 1'
Okay, let's join two answers above :D
First rcmgleite say exactly right: use
-d
options to run process as 'detached' background.
And second (the most important!) if you run detached process, you don't needed nohup!
deploy_app.sh
#!/bin/bash
cd /opt/git/app
git pull
python3 setup.py install
python3 -u webui.py >> nohup.out
Execute this inside a container
docker exec -itd container_name bash -c "/opt/scripts/deploy_app.sh"
Check it
$ docker attach container_name
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 11768 1940 pts/0 Ss Aug31 0:00 /bin/bash
root 887 0.4 0.0 11632 1396 pts/1 Ss+ 02:47 0:00 /bin/bash /opt/scripts/deploy_app
root 932 31.6 0.4 235288 32332 pts/1 Sl+ 02:47 0:00 python3 -u webui.py
I know this is a late response but I will add it here for documentation reasons.
When using nohup on bash and running it with 'exec' on a docker container, you should use
$ docker exec -d 0b3fc9dd35f2 /bin/bash -c "./main.sh"
The -d option means:
-d, --detach Detached mode: run command in the
background
for more information about docker exec, see:
https://docs.docker.com/engine/reference/commandline/exec/
This should do the trick.

Resources