How can I determine, within a shell script, whether it is being called by systemd or not?
From Lucas Werkmeister's informative answer on Server Fault:
- With systemd versions 231 and later, there's a JOURNAL_STREAM variable that is set for services whose stdout or stderr is connected to the journal.
- With systemd versions 232 and later, there's an INVOCATION_ID variable that is set.
If you don't want to rely on those variables, or for systemd versions before 231, you can check if the parent PID is equal to 1:
if [[ $PPID -ne 1 ]]
then
echo "Don't call me directly; instead, call 'systemctl start/stop service-name'"
exit 1
fi >&2
The Short Answer
if ! grep -qEe '[.]service$' /proc/self/cgroup; then
echo "This script should be started with systemctl" >&2
exit 1
fi
...or, if you know the specific service name you're expected to run as, and want to be robust against misconfigurations that prevent a user session from being created:
if ! grep -qEe '/myservice[.]service$' /proc/self/cgroup; then
echo "This service should be started with systemctl start myservice" >&2
exit 1
fi
Why It Works
One way to determine which service -- if any -- started the current process is checking /proc/self/cgroup
. For a systemd
-triggered service, this will contain the service name; for example:
12:pids:/system.slice/dhcpcd.service
11:rdma:/
10:memory:/system.slice/dhcpcd.service
9:blkio:/system.slice/dhcpcd.service
8:devices:/system.slice/dhcpcd.service
7:hugetlb:/
6:cpuset:/
5:freezer:/
4:cpu,cpuacct:/system.slice/dhcpcd.service
3:net_cls,net_prio:/
2:perf_event:/
1:name=systemd:/system.slice/dhcpcd.service
0::/system.slice/dhcpcd.service
...whereas for a process associated with a user's session, the cgroup will be something more like /user.slice/user-1000.slice/session-337.scope
(assuming that this is the user with UID 1000's 337th session on the system since its last reboot).
A Fancier Implementation
If one wants to detect the specific service being run as, this too can be extracted from /proc/self/cgroup
. Consider, for example:
cgroup_full=$(awk -F: '$1 == 0 { print $3 }' /proc/self/cgroup)
cgroup_short=${cgroup_full##*/}
case $cgroup_full in
/system.slice/*.service) echo "Run from system service ${cgroup_short%.*}";;
/user.slice/*.service) echo "Run from user service ${cgroup_short%.*}";;
*.service) echo "Service ${cgroup_short%.*} type unknown";;
*) echo "Not run from a systemd service; in $cgroup_full";;
esac
Another obvious solution that comes to mind is to add something like
Environment=FROM_SYSTEMD=1
to the service file, and test on that envvar.