/etc/entrypoint.d if script ends with `exit 0` no other scripts are executed
Steps To Reproduce
- I am running 8.3-fpm-apache with a bunch of custom entrypoint scripts
- as of 3.4.0 my later scripts are not executed because my
11-xxx.shends with aexit 0, this causes thewhileloop to terminate early
# Execute scripts from /etc/entrypoint.d/ in numeric order
find /etc/entrypoint.d/ -type f -name '*.sh' | sort -n -t- -k1 | while IFS= read -r f; do
if [ -e "$f" ]; then
if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then
echo "Executing $f"
fi
if ! . "$f"; then
echo "Error executing $f" >&2
exit 1
fi
else
echo "Warning: $f not found" >&2
fi
done
Outcome
What did you expect?
all entrypoint scripts should execute successfully
What happened instead?
only scripts until my 11-xxx.sh scripts are executed, all subsequent scripts are skipped
Affected Docker Images
serversideup/php:8.3-fpm-apache (at least)
Anything else?
This is copilots take on the problem
To be honest I have no idea why it did work with 3.3.0 and lower, the change from https://github.com/serversideup/docker-php/issues/410 does not really appear to make any difference in that regard (to me), but it seems to do it. It would be more robust if you can use your good old exit <code> in your scripts without breaking the startup sequence.
So this is the culprit:
https://github.com/serversideup/docker-php/blob/825625bfee3fa2849328ba3386a2e8cb3b63b8da/src/common/usr/local/bin/docker-php-serversideup-entrypoint#L42-L54
Line 47 is where it gets executed.
I forget the exact reason why we did it this way, but if there is a better way to execute sub-scripts -- I am all ears.
To get your script to exit properly, you need to use return 0. The exit 0 will stop since it's using the same shell.
found this https://unix.stackexchange.com/questions/730580/can-a-script-be-run-as-a-subshell which seems to match with an earlier implementation of this script.
I wonder if @szepeviktor would have any opinions on this as he is a shell wizard π§
. is the short version of source.
source does not start a subshell, $f is executed in the same shell environment. - Here exit is dangerous.
sh $f or ./f does open a subshell. - A subshell is exit-tolerant, exit will exit from the subshell but not from the current script.
Thanks! Makes sense.
I guess my question is Which method should we follow? Existing shell or subshell?
In the case of allowing people to run their own scripts, should we put them in their own subshell or keep it all within our existing shell?
We would need the environment variables to be accessible from Docker Compose files, etc, so I think that might be a reason why I went with the same shell? It's been a while since I looked at this and I forgot why I went this way -- but it was my first time building something like this so that's why I opened this up for discussion
There is no solution π£ only work-arounds.
Shell scripting is for simple tasks, there is no security in shell.
The only thought that does not make my stomach turn is: use export for every variable you want to make available.
Thanks @szepeviktor!
@rburgst: Are you okay with just using return 0 instead of exit 0?
I have changed our scripts accordingly however itβs a bit unintuitive or might be actually a footgun.
I am all ears on proposals.
I have @cappuc who submitted a PR to have it be return 0 and it seems like you're suggesting it to be exit 0.
We can't support both, so let's work together as a community and figure out what is the best DX and most secure π
So I've been looking into this deeper and trying to use AI to help guide a direction with this, it had a helpful explanation as I asked it about this section:
https://github.com/serversideup/docker-php/blob/f2250d8f6c4932db22aa161b7576c5eaa6afd54e/src/common/usr/local/bin/docker-php-serversideup-entrypoint#L41-L54
It pointed me to an example of the official Postgres entrypoint, which follows a similar method to what we're doing.
I also found this article which was really helpful: https://www.docker.com/blog/docker-best-practices-choosing-between-run-cmd-and-entrypoint/
Conclusion
- It seems like
return 0is the recommended way to go - I will get this updated in the documentation on why
If you have any more thoughts, I would love to hear them.
I made that PR to change exit 0 to return 0 because I encountered the same problem.
With the current implementation exit 0 can be used to prevent subsequent entrypoint scripts to run without exiting with an error (I don't if this can be usefull in some cases).
I don't know what is the best solution.
Thanks! @cappuc! I think the return 0 without a subshell is the way to go (at least my gut is telling me that).
I documented it here: https://github.com/serversideup/docker-php/commit/c98009b07516c3f51b77106cacc66d93be86b087
I am all ears if someone has a better solution in the meantime
I added this in the docs: https://serversideup.net/open-source/docker-php/docs/customizing-the-image/adding-your-own-start-up-scripts#dont-use-exit-0-in-your-script
I am closing for now, but if there is any further discussion on this item, please comment below π
@jaydrogers This is a surprise gift π for the permission-less shell script.
a.sh
exit()
{
return
}
export -f exit
source b.sh
echo "End of a.sh"
b.sh
echo "This is b.sh"
exit 3
Start a.sh: b.sh can not exit!
Fun! So should I document that you shouldn't source other scripts from your script?
I'm not aware of a use case where someone would want to do this.
Entrypoint scripts are just simple start up scripts to execute something.
It seems like the issue you bring up would affect the official Postgres images too?
where someone would want to do this.
You should define that function in your script where you call a user script.
(it is like immunizing against exit)
If you use exit 0 in your script, it will stop the execution of the rest of the entrypoint scripts.
ππ»
"it won't stop the execution"
Ohhhhhhh, now I get it π
I like where this is going, but will it work in /bin/sh? We need to make sure it's /bin/sh for Alpine.
To my understanding this might only work in /bin/bash.
Yes I'm a Bashist with a hard hat.
Please try it in your shell!
Trust me, I wish I could do this in /bin/bash π
export -f is not available in /bin/sh. I tried this:
###############################################
# Usage: docker-php-serversideup-entrypoint
###############################################
# This script is used to execute scripts from "/etc/entrypoint.d" and then
# execute the CMD passed in from the Dockerfile.
# Define exit function to prevent premature termination
exit() {
return
}
# Export the exit function for use in scripts
export -f exit
# Execute scripts from /etc/entrypoint.d/ in numeric order
find /etc/entrypoint.d/ -type f -name '*.sh' | sort -V | while IFS= read -r f; do
if [ -e "$f" ]; then
if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then
echo "Executing $f"
fi
if ! . "$f"; then
echo "Error executing $f" >&2
exit 1 # This still works for error cases since we're using the real exit
fi
else
echo "Warning: $f not found" >&2
fi
done
# Restore the original exit function
unset -f exit
My error
syntax error: bad function name