tl;dr: you’re missing the
--loginflag in your
yesterday I was trying to test some ansible roles I had written and to do so I was using a containerized version of Ubuntu that I tailored for doing the job - cirocosta/ubuntu.
The image does its job pretty well: it can run with
systemd as the PID 1, it has SSH and I can mess around with systemd services, units .. etc etc as if I had a working “VM” (given the set of boundaries of how far we can simulate a VM). Anyway … I faced an issue: my ansible roles were setting some scripts at
/etc/profile.d/ but they were not being executed! How dare they?
It turns out that something pretty simple was going on - the default shell (bash in that case) wasn’t being executed as “login shell”.
Looking at bash(1)’s man page:
--noprofile Do not read either the system-wide startup file /etc/profile or any of the personal initialization files ~/.bash_profile, ~/.bash_login, or ~/.profile. By default, bash reads these files when it is invoked as a login shell (see INVOCATION below). Invocation A login shell is one whose first character of argument zero is a -, or one started with the --login option.
i.e, when we run
bash <blabla> by default, it won’t run as a login shell and it turns out that
profile files are only read when running it as
login. Does it? We can easily check that with
# create a script to be sourced by bash on startup echo "export FOO=bar" > /etc/profile.d/my-script.sh # create a simplistic exec file echo "echo hey!" > ./exec.sh # execute it without '--login' strace -e open bash exec.sh open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 ... open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3 open("exec.sh", O_RDONLY) = 3 hey! +++ exited with 0 +++ # execute it with '--login' strace -e open bash --login exec.sh open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 .. open("/dev/tty", O_RDWR|O_NONBLOCK) = 3 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3 open("/etc/profile", O_RDONLY) = 3 open("/etc/profile.d/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 open("/etc/profile.d/my-script.sh", O_RDONLY) = 3 open("exec.sh", O_RDONLY) = 3 hey! +++ exited with 0 +++
Do you need a full ubuntu container? Should you care about
/etc/profile.d in a container? Probably not. Honestly, aside from testing
ansible roles, I never needed.
It’s pretty cool how we can super easily verify a behavior making use of a combination of
man to check the docs and
strace to check the syscalls involved. Very handy.
If you have any questions or just noticed that I wrote something that’s totally wrong or that could be made better, reach out! I’m @cirowrc on Twitter. You can also subscribe to the newsletter to keep up with some stuff that I find very useful.