From de1a421b68edeb2bf3660f2ed98e109db93e8d88 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 2 Oct 2019 15:39:30 +0200 Subject: [PATCH 1/7] [explorer/init] Support for Darwin and more BusyBox combinations --- cdist/conf/explorer/init | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index a8a7857e..829d6ab8 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -23,14 +23,22 @@ # for example at linux this value is "init" or "systemd" in most cases # -uname_s="$(uname -s)" - -case "$uname_s" in +case $(uname -s) in Linux) - (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true + if command -v pgrep >/dev/null + then + # BusyBox's version of ps does not support some options. + # On Linux systems, we prefer pgrep to get the name of PID1. + (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true + else + ps -o comm= -p 1 2>/dev/null || cat /proc/1/comm + fi ;; FreeBSD|OpenBSD) - ps -o comm= -p 1 || true + ps -o comm= -p 1 2>/dev/null || true + ;; + Darwin) + basename "$(ps -o comm= -p 1 2>/dev/null)" ;; *) # return a empty string as unknown value From 4fe2dcba891ae52cccbe02553976be2bb7291fd1 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 30 Jan 2020 18:35:50 +0100 Subject: [PATCH 2/7] [explorer/init] Linux is a mess... --- cdist/conf/explorer/init | 44 +++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index 829d6ab8..ceae2e9f 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -25,14 +25,48 @@ case $(uname -s) in Linux) - if command -v pgrep >/dev/null + if test -d /proc/1/ then - # BusyBox's version of ps does not support some options. - # On Linux systems, we prefer pgrep to get the name of PID1. - (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true + comm_name=$(cat /proc/1/comm) else - ps -o comm= -p 1 2>/dev/null || cat /proc/1/comm + # BusyBox's versions of ps and pgrep do not support some options + # depending on which compile-time options have been used. + # Both pgrep and ps are tried to get the command name + comm_name=$( + pgrep -P0 -l 2>/dev/null | awk '/^1[ \t]/ { print $2 }' + || ps -o comm= -p 1 2>/dev/null) fi + + case $comm_name + in + systemd) + echo systemd + ;; + init) + # It could be anything... + + if test -h /proc/1/exe + then + init_exe=/proc/1/exe + else + init_exe=$(command -v "$comm_name") + fi + + test -x "$comm_exe" || exit 1 + + case $("$comm_exe" --version | head -n 1) + in + *SysV*) + echo init + ;; + *upstart*) + echo upstart + ;; + *) + echo "" + ;; + esac + esac ;; FreeBSD|OpenBSD) ps -o comm= -p 1 2>/dev/null || true From 21c9e3db1852eebb88be5454137ceb23159f7dc0 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 3 Feb 2020 22:12:21 +0100 Subject: [PATCH 3/7] [explorer/init] Support more init systems --- cdist/conf/explorer/init | 225 ++++++++++++++++++++++++++++++--------- 1 file changed, 174 insertions(+), 51 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index ceae2e9f..bf1736cd 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -1,7 +1,8 @@ -#!/bin/sh +#!/bin/sh -e # # 2016 Daniel Heule (hda at sfs.biz) # Copyright 2017, Philippe Gregoire +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -23,59 +24,181 @@ # for example at linux this value is "init" or "systemd" in most cases # +set -e + +# Expected values: +# Linux: +# Gentoo: +# sysvinit, openrc-init + +# GNU: +# Debian: +# hurd-init, sysvinit + +# [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) + +# inspired by https://stackoverflow.com/a/33266819 +shreadlink() ( + CDPATH= + target=$1 fname= targetDir= + + # Resolve potential symlinks until the ultimate target is found. + while : + do + if ! test -e "$target" + then + printf 'ERROR: %s does not exist.\n' "'$target'" >&2 + return 1 + fi + + # Change to target dir; necessary for correct resolution of target path. + cd "$(dirname -- "$target")" + + fname=$(basename -- "$target") # Extract filename. + [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/' + + [ -L "$fname" ] || break + + # Extract [next] target path, which may be defined + # *relative* to the symlink's own directory. + # Note: We parse `ls -l` output to find the symlink target + # which is the only POSIX-compliant, albeit somewhat fragile, way. + # FIXME: Will break if one of the filenames contain ’ -> ’ + target=$(ls -l "$fname" | sed -e 's/^.* -> //') + done + + # Get canonical dir. path + targetDir=$(pwd -P) + + # Output the ultimate target's canonical path. + # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path. + if test "$fname" = '.' + then + printf '%s\n' "${targetDir%/}" + elif test "$fname" = '..' + then + # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied + # AFTER canonicalization. + printf '%s\n' "$(dirname -- "${targetDir}")" + else + printf '%s/%s\n' "${targetDir%/}" "$fname" + fi +) + + case $(uname -s) in - Linux) - if test -d /proc/1/ - then - comm_name=$(cat /proc/1/comm) - else - # BusyBox's versions of ps and pgrep do not support some options - # depending on which compile-time options have been used. - # Both pgrep and ps are tried to get the command name - comm_name=$( - pgrep -P0 -l 2>/dev/null | awk '/^1[ \t]/ { print $2 }' - || ps -o comm= -p 1 2>/dev/null) - fi + Linux|GNU) + # if test -f /proc/1/comm + # then + # comm_name=$(cat /proc/1/comm) + # else + # BusyBox's versions of ps and pgrep do not support some options + # depending on which compile-time options have been used. + # Both pgrep and ps are tried to get the command name + # comm_name=$( + # pgrep -P0 -l 2>/dev/null | awk '/^1[ \t]/ { print $2 }' + # || ps -o comm= -p 1 2>/dev/null) + # fi - case $comm_name - in - systemd) - echo systemd - ;; - init) - # It could be anything... + init_exe=$(shreadlink /proc/1/exe) - if test -h /proc/1/exe - then - init_exe=/proc/1/exe - else - init_exe=$(command -v "$comm_name") - fi + 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)" + case $init_exe + in + *' (deleted)') + init_exe=${init_exe% (deleted)} + test -x "$init_exe" || exit 1 + ;; + *) + exit 1 + ;; + esac + fi - test -x "$comm_exe" || exit 1 + if test "$init_exe" = '/hurd/init' + then + # XXX: Could maybe be removed + echo hurd-init + exit 0 + fi - case $("$comm_exe" --version | head -n 1) - in - *SysV*) - echo init - ;; - *upstart*) - echo upstart - ;; - *) - echo "" - ;; - esac - esac - ;; - FreeBSD|OpenBSD) - ps -o comm= -p 1 2>/dev/null || true - ;; - Darwin) - basename "$(ps -o comm= -p 1 2>/dev/null)" - ;; - *) - # return a empty string as unknown value - echo "" - ;; + comm_name=$(basename "$init_exe") + case $comm_name + in + init) + : # handled below + ;; + systemd) + # NOTE: sd_booted(3) + if test -d /run/systemd/system/ + then + echo systemd + exit 0 + fi + # otherwise: treat like "init" + ;; + *) + echo "$comm_name" + exit 0 + ;; + esac + + # init: it could be anything... + case $("$init_exe" --version 2>/dev/null | head -n 1) + in + SysV*) + # This is a little bit more specific than init + echo sysvinit + exit 0 + ;; + *'GNU Hurd'*) + echo hurd-init + ;; + *upstart*) + echo upstart + exit 0 + ;; + esac + case $("$init_exe" --help 2>/dev/null | head -n 1) + in + BusyBox*) + echo busybox + exit 0 + ;; + esac + + echo init + ;; + FreeBSD|OpenBSD) + ps -o comm= -p 1 2>/dev/null || true + ;; + Darwin) + basename "$(ps -o comm= -p 1 2>/dev/null)" + ;; + SunOS) + comm_name=$(ps -o comm= -p 1 2>/dev/null) + if test "$(basename "$comm_name")" != 'init' + then + echo "${comm_name}" + exit 0 + fi + + # XXX: Is this the correct way?? + if test -f /etc/svc/volatile/svc_nonpersist.db + then + echo smf + exit 0 + fi + ;; + *) + # return a empty string as unknown value + echo "" + ;; esac From d895bb0e87f524e8a64a802e6ff1922e52860ffa Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Tue, 18 Feb 2020 01:24:41 +0100 Subject: [PATCH 4/7] [explorer/init] Clean up --- cdist/conf/explorer/init | 399 ++++++++++++++++++++++++--------------- 1 file changed, 246 insertions(+), 153 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index bf1736cd..2d4f07c1 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -20,185 +20,278 @@ # along with cdist. If not, see . # # -# Returns the process name of pid 1 ( normaly the init system ) -# for example at linux this value is "init" or "systemd" in most cases +# Returns the name of the init system (PID 1) # - -set -e - # 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, ??? # Gentoo: -# sysvinit, openrc-init - +# sysvinit+openrc, openrc-init, systemd +# OpenBMC: +# systemd +# OpenWrt: +# procd, init?? +# RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...): +# systemd, upstart, sysvinit +# Slackware: +# sysvinit +# SuSE: +# systemd, sysvinit +# Ubuntu: +# systemd, upstart, sysvinit +# # GNU: -# Debian: -# hurd-init, sysvinit +# Debian: +# hurd-init, sysvinit +# +# BSD: +# {Free,Open,Net}BSD: +# init +# +# Mac OS X: +# launchd, init +# +# Solaris/Illumos: +# smf, init + # [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) -# inspired by https://stackoverflow.com/a/33266819 -shreadlink() ( - CDPATH= - target=$1 fname= targetDir= +set -e +#set -x # DEBUG - # Resolve potential symlinks until the ultimate target is found. - while : - do - if ! test -e "$target" - then - printf 'ERROR: %s does not exist.\n' "'$target'" >&2 - return 1 - fi - - # Change to target dir; necessary for correct resolution of target path. - cd "$(dirname -- "$target")" - - fname=$(basename -- "$target") # Extract filename. - [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/' - - [ -L "$fname" ] || break - - # Extract [next] target path, which may be defined - # *relative* to the symlink's own directory. - # Note: We parse `ls -l` output to find the symlink target - # which is the only POSIX-compliant, albeit somewhat fragile, way. - # FIXME: Will break if one of the filenames contain ’ -> ’ - target=$(ls -l "$fname" | sed -e 's/^.* -> //') - done - - # Get canonical dir. path - targetDir=$(pwd -P) - - # Output the ultimate target's canonical path. - # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path. - if test "$fname" = '.' +validate_busybox_init() { + # 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=$(validate_openrc) then - printf '%s\n' "${targetDir%/}" - elif test "$fname" = '..' - then - # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied - # AFTER canonicalization. - printf '%s\n' "$(dirname -- "${targetDir}")" + echo "busybox-init+${stacked}" else - printf '%s/%s\n' "${targetDir%/}" "$fname" + echo busybox-init fi -) +} +validate_hurd_init() { + # FIXME: Test me! + test -x /hurd/init || return 1 + grep -q 'GNU Hurd' /hurd/init || return 1 + echo hurd-init +} -case $(uname -s) in - Linux|GNU) - # if test -f /proc/1/comm - # then - # comm_name=$(cat /proc/1/comm) - # else - # BusyBox's versions of ps and pgrep do not support some options - # depending on which compile-time options have been used. - # Both pgrep and ps are tried to get the command name - # comm_name=$( - # pgrep -P0 -l 2>/dev/null | awk '/^1[ \t]/ { print $2 }' - # || ps -o comm= -p 1 2>/dev/null) - # fi +validate_openrc() { + test -f /run/openrc/softlevel || return 1 + echo openrc +} - init_exe=$(shreadlink /proc/1/exe) +validate_procd() { + grep -q 'procd' /sbin/procd || return 1 + echo procd +} - 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)" - case $init_exe - in - *' (deleted)') - init_exe=${init_exe% (deleted)} - test -x "$init_exe" || exit 1 - ;; - *) - exit 1 - ;; - esac - fi +validate_runit() { + test -d /run/runit || return 1 + echo runit +} - if test "$init_exe" = '/hurd/init' - then - # XXX: Could maybe be removed - echo hurd-init - exit 0 - fi +validate_smf() { + # XXX: Is this the correct way?? + test -f /etc/svc/volatile/svc_nonpersist.db || return 1 + echo smf +} - comm_name=$(basename "$init_exe") - case $comm_name +validate_systemd() { + # NOTE: sd_booted(3) + test -d /run/systemd/system/ || return 1 + # systemctl --version | sed -e '/^systemd/!d;s/^systemd //' + echo systemd +} + +validate_sysvinit() { + test -x /sbin/init \ + && grep -q 'INIT_VERSION=sysvinit-[0-9.]*' /sbin/init \ + || 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=$(validate_openrc) + then + echo "sysvinit+${stacked}" + else + echo sysvinit + fi + unset stacked +} + +validate_upstart() { + test -x "$(command -v initctl)" || return 1 + case $(initctl version) + in + *'(upstart '*')') + # if type -d /etc/init + # then + # # modern (DBus-based?) upstart >= 0.5 + # : + # elif type -d /etc/events.d + # then + # # ancient upstart + # : + # fi + echo upstart + ;; + *) + 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) + 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)" + case $init_exe in - init) - : # handled below - ;; - systemd) - # NOTE: sd_booted(3) - if test -d /run/systemd/system/ - then - echo systemd - exit 0 - fi - # otherwise: treat like "init" + *' (deleted)') + init_exe=${init_exe% (deleted)} + test -x "$init_exe" || exit 1 ;; *) - echo "$comm_name" - exit 0 + exit 1 ;; esac + fi - # init: it could be anything... - case $("$init_exe" --version 2>/dev/null | head -n 1) - in - SysV*) - # This is a little bit more specific than init - echo sysvinit - exit 0 - ;; - *'GNU Hurd'*) - echo hurd-init - ;; - *upstart*) - echo upstart - exit 0 - ;; - esac - case $("$init_exe" --help 2>/dev/null | head -n 1) - in - BusyBox*) - echo busybox - exit 0 - ;; - esac + echo "${init_exe}" +) - echo init - ;; - FreeBSD|OpenBSD) - ps -o comm= -p 1 2>/dev/null || true - ;; - Darwin) - basename "$(ps -o comm= -p 1 2>/dev/null)" - ;; - SunOS) - comm_name=$(ps -o comm= -p 1 2>/dev/null) - if test "$(basename "$comm_name")" != 'init' - then - echo "${comm_name}" - exit 0 - fi +# BusyBox's versions of ps and pgrep do not support some options +# depending on which compile-time options have been used. - # XXX: Is this the correct way?? - if test -f /etc/svc/volatile/svc_nonpersist.db - then - echo smf - exit 0 - fi - ;; - *) - # return a empty string as unknown value - echo "" - ;; -esac +find_init_pgrep() { + pgrep -P0 -fl 2>/dev/null | awk -F '[[:blank:]]' '$1 == 1 { print $2 }' +} + +find_init_ps() { + case $(uname -s) + in + Darwin|NetBSD) + ps -o ucomm= -p 1 2>/dev/null + ;; + FreeBSD) + ps -o command= -p 1 2>/dev/null | cut -d ' ' -f 1 + ;; + OpenBSD) + ps -o command -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1 + ;; + *) + ps -o comm= -p 1 2>/dev/null + ;; + esac +} + +find_init() { + case $(uname -s) + 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|FreeBSD|SunOS) + find_init_ps + ;; + *) + echo "Don't know how to determine init." >&2 + echo 'Please send a patch.' >&2 + exit 1 + esac +} + +validate_by_comm_name() { + case $1 + in + busybox) + validate_busybox_init + ;; + init) + # FIXME: Do some more magic here! + echo init + ;; + openrc-init) + validate_openrc >/dev/null && echo openrc-init + ;; + runit) + validate_runit + ;; + systemd) + validate_systemd + ;; + *) + # Run validate function by comm name if available. + # Fall back to comm name if either it does not exist or + # returns non-zero. + type "validate_$1" >/dev/null && "validate_$1" || echo $1 + esac +} + +try_all() { + # init: it could be anything... + # We try some approaches to gather more information about init without + # calling it! On some init systemd this triggers a reinitialisation of + # the system which we don't want (e.g. embedded systems). + + validate_sysvinit || \ + validate_openrc || \ + validate_runit || \ + validate_smf || \ + validate_upstart || \ + validate_hurd_init || \ + echo init # fallback +} + +init=$(find_init) + +if test -x "${init}" +then + case $init + in + /hurd/init) + # FIXME: Create validate function + echo hurd-init + ;; + */init) + try_all + ;; + *) + validate_by_comm_name "$(basename "${init}")" + ;; + esac +else + validate_by_comm_name "${init}" +fi From 364340c8d5a1bcb6492d6c8cdfbbd808b13b5024 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 20 Feb 2020 21:34:21 +0100 Subject: [PATCH 5/7] [explorer/init] Refactor and testing --- cdist/conf/explorer/init | 392 ++++++++++++++++++++++++++------------- 1 file changed, 267 insertions(+), 125 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index 2d4f07c1..db417a14 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -21,7 +21,7 @@ # # # Returns the name of the init system (PID 1) -# + # Expected values: # Linux: # Adélie Linux: @@ -35,122 +35,221 @@ # Debian: # systemd, upstart, sysvinit, openrc, ??? # Devuan: -# sysvinit, ??? +# sysvinit, sysvinit+openrc # Gentoo: # sysvinit+openrc, openrc-init, systemd # OpenBMC: # systemd # OpenWrt: -# procd, init?? +# procd, init??? # RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...): -# systemd, upstart, sysvinit +# systemd, upstart, upstart-legacy, sysvinit # Slackware: # sysvinit # SuSE: # systemd, sysvinit # Ubuntu: -# systemd, upstart, sysvinit +# systemd, upstart, upstart-legacy, sysvinit +# VoidLinux: +# runit # # GNU: # Debian: -# hurd-init, sysvinit +# sysvinit, hurd-init # # BSD: # {Free,Open,Net}BSD: # init # # Mac OS X: -# launchd, init +# launchd, init+SystemStarter # # Solaris/Illumos: -# smf, init +# 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). -# [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) set -e -#set -x # DEBUG -validate_busybox_init() { - # It is quite common to use SysVinit to stack other init systemd +KERNEL_NAME=$(uname -s) + +KNOWN_INIT_SYSTEMS=$(cat </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 } -validate_procd() { - grep -q 'procd' /sbin/procd || return 1 +check_procd() ( + procd_path=${1:-/sbin/procd} + test -x "${procd_path}" || return 1 + grep -q 'procd' "${procd_path}" || return 1 echo procd -} +) -validate_runit() { +check_runit() { test -d /run/runit || return 1 echo runit } -validate_smf() { +check_smf() { # XXX: Is this the correct way?? test -f /etc/svc/volatile/svc_nonpersist.db || return 1 echo smf } -validate_systemd() { +check_systemd() { # NOTE: sd_booted(3) test -d /run/systemd/system/ || return 1 # systemctl --version | sed -e '/^systemd/!d;s/^systemd //' echo systemd } -validate_sysvinit() { - test -x /sbin/init \ - && grep -q 'INIT_VERSION=sysvinit-[0-9.]*' /sbin/init \ - || return 1 +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} + 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=$(validate_openrc) + if stacked=$(check_openrc) then echo "sysvinit+${stacked}" else echo sysvinit fi unset stacked -} +) -validate_upstart() { +check_upstart() { test -x "$(command -v initctl)" || return 1 case $(initctl version) in *'(upstart '*')') - # if type -d /etc/init - # then - # # modern (DBus-based?) upstart >= 0.5 - # : - # elif type -d /etc/events.d - # then - # # ancient upstart - # : - # fi - echo 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 @@ -163,7 +262,7 @@ find_init_procfs() ( test -h /proc/1/exe || return 1 # Find init executable - init_exe=$(ls -l /proc/1/exe 2>/dev/null) + init_exe=$(ls -l /proc/1/exe 2>/dev/null) || return 1 init_exe=${init_exe#* -> } if ! test -x "$init_exe" @@ -171,21 +270,100 @@ find_init_procfs() ( # 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)" - case $init_exe - in - *' (deleted)') - init_exe=${init_exe% (deleted)} - test -x "$init_exe" || exit 1 - ;; - *) - exit 1 - ;; - esac + + # [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 && 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 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. @@ -194,25 +372,31 @@ find_init_pgrep() { } find_init_ps() { - case $(uname -s) + case $KERNEL_NAME in - Darwin|NetBSD) - ps -o ucomm= -p 1 2>/dev/null + Darwin) + ps -o command -p 1 2>/dev/null | tail -n +2 ;; FreeBSD) - ps -o command= -p 1 2>/dev/null | cut -d ' ' -f 1 + ps -o args= -p 1 2>/dev/null | cut -d ' ' -f 1 ;; - OpenBSD) - ps -o command -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1 - ;; - *) + Linux) ps -o comm= -p 1 2>/dev/null ;; - esac + 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 $(uname -s) + case $KERNEL_NAME in Linux|GNU|NetBSD) find_init_procfs || find_init_pgrep || find_init_ps @@ -233,65 +417,23 @@ find_init() { esac } -validate_by_comm_name() { - case $1 - in - busybox) - validate_busybox_init - ;; - init) - # FIXME: Do some more magic here! - echo init - ;; - openrc-init) - validate_openrc >/dev/null && echo openrc-init - ;; - runit) - validate_runit - ;; - systemd) - validate_systemd - ;; - *) - # Run validate function by comm name if available. - # Fall back to comm name if either it does not exist or - # returns non-zero. - type "validate_$1" >/dev/null && "validate_$1" || echo $1 - esac -} - -try_all() { - # init: it could be anything... - # We try some approaches to gather more information about init without - # calling it! On some init systemd this triggers a reinitialisation of - # the system which we don't want (e.g. embedded systems). - - validate_sysvinit || \ - validate_openrc || \ - validate_runit || \ - validate_smf || \ - validate_upstart || \ - validate_hurd_init || \ - echo init # fallback -} +# ----- init=$(find_init) -if test -x "${init}" -then - case $init - in - /hurd/init) - # FIXME: Create validate function - echo hurd-init - ;; - */init) - try_all - ;; - *) - validate_by_comm_name "$(basename "${init}")" - ;; - esac -else - validate_by_comm_name "${init}" -fi +# If we got a path, guess by the path first (fall back to file name if no match) +# else guess by file name directly. +{ + 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 From 0d84c91b4047d3da0571d0262b4b5d9a8f9796b9 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 20 Feb 2020 22:55:46 +0100 Subject: [PATCH 6/7] [explorer/init] Fix unique() for Solaris --- cdist/conf/explorer/init | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index db417a14..0f04a0ee 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -135,7 +135,8 @@ trim() { unique() { # Delete duplicate lines (keeping input order) - awk '!x[$0]++' + # NOTE: Solaris AWK breaks without if/print construct. + awk '{ if (!x[$0]++) print }' } From 0d6bc8e8f8166a3f61dd4da4a0e499499d3702c4 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 20 Feb 2020 23:29:21 +0100 Subject: [PATCH 7/7] [explorer/init] Make shellcheck happy --- cdist/conf/explorer/init | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index 0f04a0ee..1b921c68 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -310,7 +310,7 @@ guess_by_path() { fi ;; /sbin/procd) - check_procd && return + check_procd "$1" && return ;; /sbin/init|*/init) # init: it could be anything -> (explicit) no match @@ -356,7 +356,7 @@ guess_by_comm_name() { check_list() ( # List must be a multi-line input on stdin (one name per line) - while read init + while read -r init do "check_${init}" || continue return 0 @@ -408,7 +408,7 @@ find_init() { OpenBSD) find_init_pgrep || find_init_ps ;; - Darwin|FreeBSD|SunOS) + Darwin|SunOS) find_init_ps ;; *) @@ -424,6 +424,7 @@ 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}" \