#!/bin/sh -e # # 2016 Daniel Heule (hda at sfs.biz) # Copyright 2017, Philippe Gregoire <pg@pgregoire.xyz> # 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # # cdist is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # cdist is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with cdist. If not, see <http://www.gnu.org/licenses/>. # # # Returns the name of the init system (PID 1) # Expected values: # Linux: # Adélie Linux: # sysvinit+openrc # Alpine Linux: # busybox-init+openrc # ArchLinux: # systemd, sysvinit # CRUX: # sysvinit # Debian: # systemd, upstart, sysvinit, openrc, ??? # Devuan: # sysvinit, sysvinit+openrc # Gentoo: # sysvinit+openrc, openrc-init, systemd # OpenBMC: # systemd # OpenWrt: # procd, init??? # RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...): # systemd, upstart, upstart-legacy, sysvinit # Slackware: # sysvinit # SuSE: # systemd, sysvinit # Ubuntu: # systemd, upstart, upstart-legacy, sysvinit # VoidLinux: # runit # # GNU: # Debian: # sysvinit, hurd-init # # BSD: # {Free,Open,Net}BSD: # init # # Mac OS X: # launchd, init+SystemStarter # # Solaris/Illumos: # smf, init??? # NOTE: init systems can be stacked. This is popular to run OpenRC on top of # sysvinit (Gentoo) or busybox-init (Alpine), but can also be used to run runit # as a systemd service. This makes init system detection very complicated # (which result is expected?) This script tries to untangle some combinations, # OpenRC on top of sysv or busybox (X+openrc), but will ignore others (runit as # a systemd service) # NOTE: When we have no idea, nothing will be printed! # NOTE: # When trying to gather information about the init system make sure to do so # without calling the binary! On some systems this triggers a reinitialisation # of the system which we don't want (e.g. embedded systems). set -e KERNEL_NAME=$(uname -s) KNOWN_INIT_SYSTEMS=$(cat <<EOF systemd sysvinit upstart runit procd smf launchd init hurd_init systemstarter EOF ) common_candidates_by_kernel() { case $KERNEL_NAME in FreeBSD|NetBSD|OpenBSD) echo init ;; Linux) echo systemd echo sysvinit echo upstart ;; GNU) echo sysvinit echo hurd-init ;; Darwin) echo launchd echo systemstarter ;; SunOS) echo smf ;; esac } ## Helpers trim() { sed -e 's/^[[:blank:]]*//' -e 's/[[:blank:]]*$//' -e '/^[[:blank:]]*$/d' } unique() { # Delete duplicate lines (keeping input order) # NOTE: Solaris AWK breaks without if/print construct. awk '{ if (!x[$0]++) print }' } ## Check functions # These functions are used to verify if a guess is correct by checking some # common property of a running system (presence of a directory in /run etc.) check_busybox_init() ( busybox_path=${1:-/bin/busybox} test -x "${busybox_path}" || return 1 grep -q 'BusyBox v[0-9]' "${busybox_path}" || return 1 # It is quite common to use Busybox init to stack other init systemd # (like OpenRC) on top of it. So we check for that, too. if stacked=$(check_openrc) then echo "busybox-init+${stacked}" else echo busybox-init fi ) check_hurd_init() ( init_exe=${1:-/hurd/init} test -x "${init_exe}" || return 1 grep -q 'GNU Hurd' "${init_exe}" || return 1 echo hurd-init ) check_init() { # Checks for various BSD inits... test -x /sbin/init || return 1 if grep -q -E '(Free|Net|Open)BSD' /sbin/init then echo init return 0 fi } check_launchd() { command -v launchctl >/dev/null 2>&1 || return 1 launchctl getenv PATH >/dev/null || return 1 echo launchd } check_openrc() { test -f /run/openrc/softlevel || return 1 echo openrc } check_procd() ( procd_path=${1:-/sbin/procd} test -x "${procd_path}" || return 1 grep -q 'procd' "${procd_path}" || return 1 echo procd ) check_runit() { test -d /run/runit || return 1 echo runit } check_smf() { # XXX: Is this the correct way?? test -f /etc/svc/volatile/svc_nonpersist.db || return 1 echo smf } check_systemd() { # NOTE: sd_booted(3) test -d /run/systemd/system/ || return 1 # systemctl --version | sed -e '/^systemd/!d;s/^systemd //' echo systemd } check_systemstarter() { test -d /System/Library/StartupItems/ || return 1 test -f /System/Library/StartupItems/LoginWindow/StartupParameters.plist || return 1 echo init+SystemStarter } check_sysvinit() ( init_path=${1:-/sbin/init} test -x "${init_path}" || return 1 grep -q 'INIT_VERSION=sysvinit-[0-9.]*' "${init_path}" || return 1 # It is quite common to use SysVinit to stack other init systemd # (like OpenRC) on top of it. So we check for that, too. if stacked=$(check_openrc) then echo "sysvinit+${stacked}" else echo sysvinit fi unset stacked ) check_upstart() { test -x "$(command -v initctl)" || return 1 case $(initctl version) in *'(upstart '*')') if test -d /etc/init then # modern (DBus-based?) upstart >= 0.5 echo upstart elif test -d /etc/event.d then # ancient upstart echo upstart-legacy else # whatever... echo upstart fi ;; *) return 1 ;; esac } find_init_procfs() ( # First, check if the required file in procfs exists... test -h /proc/1/exe || return 1 # Find init executable init_exe=$(ls -l /proc/1/exe 2>/dev/null) || return 1 init_exe=${init_exe#* -> } if ! test -x "$init_exe" then # On some rare occasions it can happen that the # running init's binary has been replaced. In this # case Linux adjusts the symlink to "X (deleted)" # [root@fedora-12 ~]# readlink /proc/1/exe # /sbin/init (deleted) # [root@fedora-12 ~]# ls -l /proc/1/exe # lrwxrwxrwx. 1 root root 0 2020-01-30 23:00 /proc/1/exe -> /sbin/init (deleted) init_exe=${init_exe% (deleted)} test -x "$init_exe" || return 1 fi echo "${init_exe}" ) guess_by_path() { case $1 in /bin/busybox) check_busybox_init "$1" && return ;; /lib/systemd/systemd) check_systemd "$1" && return ;; /hurd/init) check_hurd_init "$1" && return ;; /sbin/launchd) check_launchd "$1" && return ;; /usr/bin/runit|/sbin/runit) check_runit "$1" && return ;; /sbin/openrc-init) if check_openrc "$1" >/dev/null then echo openrc-init return fi ;; /sbin/procd) check_procd "$1" && return ;; /sbin/init|*/init) # init: it could be anything -> (explicit) no match return 1 ;; esac # No match return 1 } guess_by_comm_name() { case $1 in busybox) check_busybox_init && return ;; openrc-init) if check_openrc >/dev/null then echo openrc-init return 0 fi ;; init) # init could be anything -> no match return 1 ;; *) # Run check function by comm name if available. # Fall back to comm name if either it does not exist or # returns non-zero. if type "check_$1" >/dev/null then "check_$1" && return else echo "$1" ; return 0 fi esac return 1 } check_list() ( # List must be a multi-line input on stdin (one name per line) while read -r init do "check_${init}" || continue return 0 done return 1 ) # BusyBox's versions of ps and pgrep do not support some options # depending on which compile-time options have been used. find_init_pgrep() { pgrep -P0 -fl 2>/dev/null | awk -F '[[:blank:]]' '$1 == 1 { print $2 }' } find_init_ps() { case $KERNEL_NAME in Darwin) ps -o command -p 1 2>/dev/null | tail -n +2 ;; FreeBSD) ps -o args= -p 1 2>/dev/null | cut -d ' ' -f 1 ;; Linux) ps -o comm= -p 1 2>/dev/null ;; NetBSD) ps -o comm= -p 1 2>/dev/null ;; OpenBSD) ps -o args -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1 ;; *) ps -o args= -p 1 2>/dev/null ;; esac | trim # trim trailing whitespace (some ps like Darwin add it) } find_init() { case $KERNEL_NAME in Linux|GNU|NetBSD) find_init_procfs || find_init_pgrep || find_init_ps ;; FreeBSD) find_init_procfs || find_init_ps ;; OpenBSD) find_init_pgrep || find_init_ps ;; Darwin|SunOS) find_init_ps ;; *) echo "Don't know how to determine init." >&2 echo 'Please send a patch.' >&2 exit 1 esac } # ----- init=$(find_init) # If we got a path, guess by the path first (fall back to file name if no match) # else guess by file name directly. # shellcheck disable=SC2015 { test -x "${init}" \ && guess_by_path "${init}" \ || guess_by_comm_name "$(basename "${init}")" } && exit 0 || true # Guessing based on the file path and name didn’t lead to a definitive result. # # We go through all of the checks until we find a match. To speed up the # process, common cases will be checked first based on the underlying kernel. { common_candidates_by_kernel; echo "${KNOWN_INIT_SYSTEMS}"; } \ | unique | check_list