使用 shell 检测初始化系统

bea*_*mit 103 shell-script init portability

这可能与检测操作系统有关,但我特别需要系统上当前使用的 init 系统。

Fedora 15 和 Ubuntu 现在使用 systemd,Ubuntu 过去使用 Upstart(很长时间默认直到 15.04),而其他人使用 System V 的变体。

我有一个应用程序,我正在编写它作为跨平台守护程序。初始化脚本是根据可以在配置时传入的参数动态生成的。

我想做的只是为他们正在使用的特定初始化系统生成脚本。这样,安装脚本可以在没有参数的情况下以 root 身份合理运行,并且守护进程可以自动“安装”。

这是我想出的:

  • 在 /bin 中搜索 systemd、upstart 等
  • 将 /proc/1/comm 与 systemd、upstart 等进行比较
  • 询问用户

这样做的最佳跨/平台方式是什么?

有点相关,我可以依赖 bash 来支持大多数 *nix 还是依赖于发行版/操作系统?

目标平台:

  • 苹果系统
  • Linux(所有发行版)
  • BSD(所有版本)
  • Solaris、Minix 和其他 *nix

小智 65

我自己也遇到过这个问题,并决定做一些测试。我完全同意应该为每个发行版单独打包的答案,但有时会出现一些实际问题(尤其是人力)。

所以对于那些想要“自动检测”的人来说,这是我在一组有限的发行版中发现的(更多见下文):

这是我使用以下命令行的实验:

if [[ `/sbin/init --version` =~ upstart ]]; then echo using upstart;
elif [[ `systemctl` =~ -\.mount ]]; then echo using systemd;
elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then echo using sysv-init;
else echo cannot tell; fi
Run Code Online (Sandbox Code Playgroud)

在 ec2 实例上(我包括了 us-east AMI id):

  • ArchLinux:使用 systemd(自2012.10.06 起
  • CentOS6.4 ami-52009e3b:使用 upstart
  • CentOS7 ami-96a818fe:使用 systemd
  • Debian 6 ami-80e915e9:使用 sysv-init
  • Debian 7.5 ami-2c886c44:使用 sysv-init
  • Debian 7.6 GCE 容器虚拟机:使用 sysv-init
  • RHEL 6.5 ami-8d756fe4:使用新贵
  • SLES 11 ami-e8084981:使用 sysv-init
  • Ubuntu 10.04 ami-6b350a02:使用暴发户
  • Ubuntu 12.04 ami-b08b6cd8:使用暴发户
  • Ubuntu 14.04 ami-a427efcc:使用暴发户
  • Ubuntu 14.10 及以下:使用 systemd
  • AWS linux 2014.3.2 ami-7c807d14:使用暴发户
  • Fedora 19 ami-f525389c:使用 systemd
  • Fedora 20 ami-21362b48:使用 systemd

需要明确的是:我并不是说这是万无一失的!,几乎可以肯定不是。另请注意,为方便起见,我使用 bash regexp 匹配项,但并非随处可用。以上对我现在来说已经足够了。但是,如果您发现某个发行版出现故障,请告诉我,如果有一个 EC2 AMI 重现了该问题,我会尝试修复它...

  • 根据 systemd 文档([sd_booted(3)](http://manpages.ubuntu.com/manpages/xenial/en/man3/sd_booted.3.html)),检查 systemd 的正确方法是检查是否目录`/run/systemd/system` 存在。 (7认同)

roz*_*acz 33

对于第二个问题,答案是否定的,您应该查看可移植 shell 编程的参考资料

至于第一部分——首先,你当然要小心。我会说执行几次测试以确保- 因为有人确实安装了systemd(例如)这一事实并不意味着它实际上用作默认init. 此外,查看/proc/1/comm可能会产生误导,因为各种 init 程序的某些安装可以自动创建/sbin/init符号链接硬链接,甚至是主程序的重命名版本。

也许最有用的事情可能是查看 init 脚本类型 - 因为这些是您实际要创建的内容,无论它们运行的​​是什么。

作为旁注,您可能还会查看OpenRC,它旨在提供与 Linux 和 BSD 系统兼容的初始化脚本结构。

  • “查看初始化脚本类型”是什么意思?通常,不同的 init 系统会将它们的脚本/文件放在 `/etc/init` 之外的某个地方,就像 systemd 将它们放在 `/etc/systemd` 中一样。如果我让我的脚本理解这些,可能需要一点时间。哦,感谢您提供便携式 shell 编程的链接 BTW。 (2认同)
  • 那么,您是说没有可靠的方法来以编程方式检测 init 系统吗?让用户传入参数当然更安全,但是如果用户没有传入任何东西,那么最好的猜测方法是什么? (2认同)

slm*_*slm 20

使用流程

查看ps可以检测systemd&的各种版本的几个命令的输出,可以upstart像这样制作:

暴发户

$ ps -eaf|grep '[u]pstart'
root       492     1  0 Jan02 ?        00:00:00 upstart-udev-bridge --daemon
root      1027     1  0 Jan02 ?        00:00:00 upstart-socket-bridge --daemon
Run Code Online (Sandbox Code Playgroud)

系统

$ ps -eaf|grep '[s]ystemd'
root         1     0  0 07:27 ?        00:00:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 20
root       343     1  0 07:28 ?        00:00:03 /usr/lib/systemd/systemd-journald
root       367     1  0 07:28 ?        00:00:00 /usr/lib/systemd/systemd-udevd
root       607     1  0 07:28 ?        00:00:00 /usr/lib/systemd/systemd-logind
dbus       615     1  0 07:28 ?        00:00:13 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
Run Code Online (Sandbox Code Playgroud)

注意 PID #1 的进程的名称也可能揭示正在使用哪个 init 系统。在 Fedora 19 上(使用systemd,例如:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 07:27 ?        00:00:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 20
Run Code Online (Sandbox Code Playgroud)

注意它不是init。在带有 Upstart 的 Ubuntu 上,它仍然是/sbin/init.

$ ps -efa|grep init
root         1     0  0 Jan02 ?        00:00:03 /sbin/init
Run Code Online (Sandbox Code Playgroud)

注意:但请谨慎使用。没有一成不变的任何一套,说在给定的发行版中使用特定的init系统已经拥有systemd的PID#1。

通用的

$ (ps -eo "ppid,args" 2>/dev/null || echo "ps call error") \
    | awk 'NR==1 || $1==1' | less
 PPID   COMMAND
    1   /lib/systemd/systemd-journald
    1   /lib/systemd/systemd-udevd
    1   /lib/systemd/systemd-timesyncd
Run Code Online (Sandbox Code Playgroud)

查看 ppid 为 1 的进程(init 进程的子进程)。(一些)子进程名称可能指向正在使用的 init 系统。

文件系统

如果您询问init可执行文件,您也可以从中获取一些信息。简单地解析--version输出。例如:

暴发户

$ sudo /sbin/init --version
init (upstart 1.5)
Copyright (C) 2012 Scott James Remnant, Canonical Ltd.

This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)

系统

$ type init
init is /usr/sbin/init
Run Code Online (Sandbox Code Playgroud)

注意:init不在其标准位置的事实有点暗示/告诉。它始终位于/sbin/initsysvinit 系统中。

sysvinit

$ type init
init is /sbin/init
Run Code Online (Sandbox Code Playgroud)

还有这个:

$ sudo init --version
init: invalid option -- -
Usage: init 0123456SsQqAaBbCcUu
Run Code Online (Sandbox Code Playgroud)

结论

因此,似乎没有任何一种方法可以做到这一点,但是您可以制定一套检查方法,以相当高的信心确定您正在使用的初始化系统。

  • 如果使用 systemd,PID #1 不一定是`/usr/lib/systemd/systemd`,这是一个错误的假设。例如,在我的系统上,PID #1 是 `/sbin/init`(我使用 systemd)。它依赖于分布。 (2认同)

Mar*_* An 15

效率不高,但我似乎有效。

strings /sbin/init | grep -q "/lib/systemd" && echo SYSTEMD
strings /sbin/init | grep -q "sysvinit" && echo SYSVINIT
strings /sbin/init | grep -q "upstart" && echo UPSTART
Run Code Online (Sandbox Code Playgroud)

如果多个字符串匹配,它将打印更多行,这可以翻译为“无法猜测”。grep 中使用的字符串可以稍作修改,但在以下操作系统中进行测试时,我总是得到一行。

  • RHEL 6.4 [新贵]
  • RHEL ES 4(Nahant 更新 7)[SYSVINIT]
  • Ubuntu 16.04.1 LTS [系统]
  • Ubuntu 14.04.2 LTS [启动]
  • Fedora 23 版(在线 shell)[SYSTEMD]
  • Debian GNU/Linux 7(在线 shell)[SYSTEMD]
  • Centos 7.6 (VM) [系统]

相同解决方案的更简单方法(但在第一场比赛时停止)

strings /sbin/init |
  awk 'match($0, /(upstart|systemd|sysvinit)/) { print toupper(substr($0, RSTART, RLENGTH));exit; }'
Run Code Online (Sandbox Code Playgroud)


Ski*_*rou 12

有时它就像使用一样简单ls :

$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 juin  25 12:04 /sbin/init -> /lib/systemd/systemd
Run Code Online (Sandbox Code Playgroud)

我想如果/sbin/init不是符号链接,您将不得不进一步检查其他答案中的以下建议。


Eme*_*ado 5

我也遇到了同样的问题,在一些 RedHat/CentOS/Debian/Ubuntu/Mint 机器上做了很多测试。这就是我最终得到的结果,结果很好。

  1. 查找 PID 为 1 的可执行文件的名称:

    ps -p 1
    
    Run Code Online (Sandbox Code Playgroud)

    如果是 systemd 或 Upstart,问题就解决了。如果它是“init”,则它可能是符号链接或其他名称而不是前期名称。前进。

  2. 找到可执行文件的真实路径(仅以 root 身份工作):

    ls -l `which init`
    
    Run Code Online (Sandbox Code Playgroud)

    如果init是 Upstart 或 systemd 的符号链接,则问题已解决。否则,几乎可以肯定您有 SysV init。但它可能是一个错误命名的可执行文件。前进。

  3. 找到提供可执行文件的包。不幸的是,这取决于发行版:

    dpkg-query -S (executable real path) # Debian  
    rpm -qf (executable real path) # RedHat  
    
    Run Code Online (Sandbox Code Playgroud)

然后,如果你想编写脚本(最有趣的部分,恕我直言),这些是我的单行程序(以 root 身份运行):

ls -l $(which $(ps -p 1 o comm)) | awk '{ system("dpkg-query -S "$NF) }' # Debian  

ls -l $(which $(ps -p 1 o comm)) | awk '{ system("rpm -qf "$NF) }' # RedHat  
Run Code Online (Sandbox Code Playgroud)