使用shell检测init系统


90

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

Fedora 15和Ubuntu现在使用systemd,Ubuntu曾经使用Upstart(长期使用默认时间是15.04),而其他人则使用System V的变体。

我有一个要编写为跨平台守护程序的应用程序。初始化脚本是基于可以在configure上传递的参数动态生成的。

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

这是我想出的:

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

最好的跨平台跨平台方法是什么?

相关的一种,我可以依赖bash来依赖大多数* nix还是依赖于发行版/ OS?

目标平台:

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

1
仅加我两美分,在FreeBSD上默认未安装bash。
2011年

@tjameson,您找到更直接的方法了吗?我正在寻找相同的东西,但是这里的答案只是方向,而不是直接的答案。特别是1)要搜索的脚本位置和2)检测到有效的init系统(如果安装了多个系统的话(直接回答bash))。
n611x007

3
@naxa-简短答案,不。长话大说,您可以走得很远ps -p 1 -o command(打印当前路径init)。在Arch Linux和Fedora(IIRC)上,它是systemd二进制文件的符号链接(在所有systemd系统上可能都是相同的)。上upstartinit --help将打印使用信息,并在我的箱子,upstart被提及,它说谁的电子邮件。在FreeBSD(sysV)上,这将返回错误。在其他系统上可能也有类似的线索,但是自问这个问题以来,我决定只为所有平台创建它们,并为每个平台制作单独的软件包。
beatgammit

感谢伟大的信息!从中学到的跳跃sudo lsof -a -p 1 -d txt可能会给出更准确的结果。ps可能会打印一个任意名称,而lsof您将获得真实的可执行文件路径。(见我的问题unix.stackexchange.com/questions/102453/...
n611x007

1
链接问题中的答案或评论均与bash无关。解决方案也应适用于您的情况。
Marco Marco

Answers:


30

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

至于第一部分-首先,您一定要小心。我要说要进行几次测试以确保 -因为有人确实安装了 systemd(例如),但这并不意味着它实际上被用作default init。同样,查看/proc/1/comm可能会产生误导,因为各种初始化程序的某些安装会自动使/sbin/init符号链接变为硬链接,甚至自动为其主程序重新命名。

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

附带说明一下,您还可以看看OpenRC,它旨在提供一种与Linux和BSD系统兼容的初始化脚本结构。


2
“查看init脚本类型”是什么意思?通常,不同的init系统会将其脚本/文件放在其他位置/etc/init,就像systemd将它们放在/etc/systemd。如果要让我的脚本使用这些内容,可能需要一些时间。哦,感谢BTW进行便携式外壳编程的链接。
beatgammit,2011年

1
我的意思是说,可以对某些init实现进行调整,以利用不同的init脚本的类型和位置(如/etc/rc.d//etc/init.d/)。并且您应该在安装时适当地调整程序以利用给定系统上使用的结构。
rozcietrzewiacz 2011年

2
因此,您是说没有可靠的方法来以编程方式检测初始化系统吗?让用户传递参数当然更安全,但是如果用户不传递任何参数,什么是最好的猜测方法?
Beatgammit

我不知道什么是您程序的最佳方法-这完全取决于该程序最终将在哪些系统上运行。我并没有使用太多的分布,但是我试图分享我的观察结果以帮助您。这是您将要尝试的一段艰难的脚本,而我试图为您提供一些有关该领域的看法-您必须自己做出最终答案,或者等待更多提示。
rozcietrzewiacz 2011年

太好了,谢谢您的所有帮助。您肯定为我指明了正确的方向。
Beatgammit

58

我本人已介入此问题,并决定进行一些测试。我完全同意一个答案,即应该为每个发行版分别打包,但是有时存在一些实际问题阻止了该发行版(尤其是人力)。

因此,对于那些想要“自动检测”的人来说,这是我在有限的发行版中发现的内容(更多信息请参见下文):

  • 您可以从以下位置告诉暴发户:

    [[ `/sbin/init --version` =~ upstart ]] && echo yes || echo no
  • 您可以从以下位置告诉systemd:

    [[ `systemctl` =~ -\.mount ]] && echo yes || echo no
  • 您可以从以下位置告诉sys-v init:

    [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]] && echo yes

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

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

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

  • ArchLinux:使用systemd(自2012.10.06以来)
  • CentOS6.4 AMI-52009E3B:使用新贵
  • 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 AM6B350A02:使用新贵
  • Ubuntu 12.04 AM-B08B6CD8:使用新贵
  • Ubuntu 14.04 AM-A427EFCC:使用新贵
  • Ubuntu 14.10及更低版本:使用systemd
  • AWS Linux 2014.3.2 AMI-7C807D14:使用新贵
  • Fedora 19 AMI-F525389C:使用SystemD
  • Fedora 20 AM-21362B48:使用SystemD

只是要清楚:我并不是说这是万无一失的!,几乎可以肯定不是。另请注意,为方便起见,我使用了bash regexp匹配项,但并非在所有地方都可用。以上对我来说已经足够了。但是,如果您发现发行版失败,请告诉我,如果有EC2 AMI重现该问题,我将尝试修复它。


5
根据systemd文档(sd_booted(3)),检查systemd的正确方法是检查目录是否/run/systemd/system存在。
罗比·巴萨克

1
我将添加到此检测中launchd,即macOS [[ $(ps 1) =~ 'launchd' ]] && echo yes || echo no上的init系统:在MacBookPro 上测试,macOS Sierra版本10.12
Tony,

我还要添加busybox:if [ -h /sbin/init -a $(readlink /sbin/init | grep busybox | wc -l) -gt 0 ]; then echo yes; else echo no; fi
Astrinus

18

使用流程

查看几个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

系统的

$ 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

注意PID#1的进程名称也可能会揭示正在使用哪个初始化系统。在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

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

$ ps -efa|grep init
root         1     0  0 Jan02 ?        00:00:03 /sbin/init

注意:但是请谨慎使用。有没有什么是一成不变的,说在给定的发行版中使用特定的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

查看带有p​​pid 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.

系统的

$ type init
init is /usr/sbin/init

注意:init不在其标准位置的事实有点提示/说明。它始终位于/sbin/initsysvinit系统上。

系统

$ type init
init is /sbin/init

还有这个:

$ sudo init --version
init: invalid option -- -
Usage: init 0123456SsQqAaBbCcUu

结论

因此,似乎没有任何一种方法可以执行此操作,但是您可以制定一套检查方法,以相当高的信心确定要使用的初始化系统。


怎么样:pgrep systemd >/dev/null && echo init system: systemd
Marco Marco

谢谢,这正是我所要寻找的:尝试确定初始化系统的一些步骤。
user369450 2014年

1
PID#1不一定/usr/lib/systemd/systemd是使用systemd的情况,而是错误的假设。例如,在我的系统上,PID#1是/sbin/init(我使用systemd)。它取决于分布。
Marco Marco

在我的kubuntu 15.04安装上,我似乎同时具有systemd和sysvinit。不知道为什么,或者是什么意思,只是说.. pgrep systemd >/dev/null && echo init system: systemd -> init system: systemdtype init -> init is /sbin/init
dotnetCarpenter

12

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

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

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

  • RHEL 6.4 [UPSTART]
  • RHEL ES 4(Nahant更新7)[SYSVINIT]
  • Ubuntu 16.04.1 LTS [SYSTEMD]
  • Ubuntu 14.04.2 LTS [UPSTART]
  • Fedora版本23(在线外壳)[SYSTEMD]
  • Debian GNU / Linux 7(在线外壳)[SYSTEMD]

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

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

不错的第一个答案。欢迎来到unix.se!
奥利维尔·杜拉克

我只是在安装了OpenRC的Gentoo默认docker映像上尝试过此操作,它返回了SYSVINIT。根据wiki.gentoo.org/wiki/Comparison_of_init_systems的说法,Gentoo的默认值为OpenRC,但似乎OpenRC使用sysvinit。然后,当我尝试OpenRC命令时,我得到“您正在尝试在无法启动openrc的系统上运行OpenRC服务”。有没有办法区分sysvinit正确和使用sysvinit的OpenRC?
tudor

9

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

$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 juin  25 12:04 /sbin/init -> /lib/systemd/systemd

我想如果/sbin/init不是符号链接,则必须在其他答案中进一步检查以下建议。


3
  1. 这就是特定于发行版的软件包的用途。正确安装软件不只是检测启动系统而已。许多发行版都使用SysVinit,但并非所有人都以相同的方式编写其初始化脚本。解决此问题的正确方法是包括所有不同的变体,然后使用带有rpm发行版的发行版特定依赖项名称的spec文件,适用于apt的系统的deb文件等将其捆绑在一起。几乎所有发行版都有某种软件包规范可以编写包括依赖项,脚本,初始化脚本等在内的内容。不要在这里重新发明轮子。

  2. 否。这使我们回到1。如果您需要bash,则它应该是一个依赖项。您可以在配置脚本中指定此检查,但也应在软件包描述中。

编辑:在配置脚本上使用标志,例如 --with upstart--without sysvinit。选择一个合理的默认值,然后为其他发行版打包软件的脚本可以选择使用其他选项来运行它。


哎呀,看来我无法拥有“一个脚本来统治所有人”的解决方案。我见过很多使用autoconf或类似的程序来处理跨平台内容的程序,但似乎它不是适合我的应用程序的工具。维护每个平台的版本真的是唯一可靠的解决方案吗?
beatgammit,2011年

BadIdea是一个可以全部规矩的安装脚本。最终失败的地方超过了它的工作范围。就其本身而言,Autoconf很好。努力使软件的结尾尽可能通用,并在包中包含备用的初始化脚本。设置几个主要发行版的软件包规格。如果您的软件不错,则可以让其他人帮助您将其打包为其他系统。
卡莱布

@tjameson:我刚刚意识到我忘记了最重要的一点。这种事情通常是通过传递给配置脚本的开关来完成的。每个发行版的构建/打包例程都可以调用不同的开关,并且您的configure / make仅需知道传递了哪个开关,而无需检测所有可能的软件配置。
卡莱布

@ Caleb-是的,我已经在其中有了逻辑,但是很好。我希望找到一种方法来嗅探初始化系统,以使用智能的猜测而非合理的默认值。
Beatgammit

2

在Gentoo上,看看pid 1:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4216   340 ?        Ss    2013   0:57 init [3]

如果是init,则init系统为OpenRC。如果是systemd,则init系统为systemd

您可以使用来检测Gentoo [ -f /etc/gentoo-release ]

Gentoo的另一种方法是使用profile-config show,它将显示正在使用的默认配置文件。除以/ systemd结尾的两个以外,所有配置文件均使用OpenRC初始化。请记住,这些仅代表默认值,并且用户可能已采取步骤覆盖该默认值,并且可能未指示实际使用的初始化管理器。


愚蠢的问题,但是如何检查pid 1?
Faheem Mitha 2014年

使用p选项(与-p和相同--pid)按PID选择。上面的代码段实际上是的输出ps u --pid 1
何M.贝尼特斯

2

在debian上,/ sbin / init是一个指向默认init的符号链接,因此

ls -l /sbin/init

将为您提供所需的信息。

$ ls -l /sbin/init 
lrwxrwxrwx 1 root root 20 nov 18 13:15 /sbin/init -> /lib/systemd/systemd

1
不是这样 在具有Upstart的Ubuntu 14.04上,它不是符号链接。
muru 2014年

安装另一个初始化系统,然后再次检查。
rmorelli74 2014年

init我还可以在Ubuntu 14.04上安装哪些其他软件?
muru

...为什么不使用sysv或systemd?
rmorelli74 2014年

1
我不想拒绝投票,我只想让您指定适用于哪个版本的Debian。(也许只是将“基于Debian的”改写为“ Debian”,以便它变得正确。)
muru

2

简单地使用PID 1进入过程将告诉您:

strings /proc/1/exe |grep -q sysvinit
strings /proc/1/exe |grep -q systemd

并非所有提到的OSeS都有proc文件系统...
Toby Speight

2

检查文件描述符也可以提供帮助。它来自实际运行的init(Debian Stretch目前允许安装更多的init系统):-)

$ ls -l /proc/1/fd |grep systemd
lrwx------ 1 root root 64 srp 14 13:56 25 -> /run/systemd/initctl/fifo
lr-x------ 1 root root 64 srp 14 13:56 6 -> /sys/fs/cgroup/systemd

$ ls -l /proc/1/fd |grep /run/initctl # sysvinit
lrwx------ 1 root root 64 srp 14 14:04 10 -> /run/initctl

$ ls -l /proc/1/fd |grep upstart
l-wx------ 1 root root 64 srp 13 16:09 13 -> /var/log/upstart/mysql.log.1 (delete
l-wx------ 1 root root 64 srp 13 16:09 9 -> /var/log/upstart/dbus.log.1 (deleted)

$ ls -l /proc/1/fd # busybox
total 0
lrwx------    1 root     root          64 Jan  1 00:00 0 -> /dev/console
lrwx------    1 root     root          64 Jan  1 00:00 1 -> /dev/console
lrwx------    1 root     root          64 Jan  1 00:00 2 -> /dev/console

可能最安全的方法来检查busybox是to check /proc/1/exe,因为busybox通常使用符号链接:

$ ls -l /proc/1/exe 
lrwxrwxrwx    1 root     root          0 Jan  1 00:00 /proc/1/exe -> /bin/busybox

因此检查可能是:

{ ls -l /proc/1/fd |grep -q systemd && echo "init: systemd"; } || \
{ ls -l /proc/1/fd |grep -q /run/initctl && echo "init: sysvinit"; } || \
{ ls -l /proc/1/fd |grep -q upstart && echo "init: upstart"; } || \
{ ls -l /proc/1/exe |grep -q busybox && echo "init: busybox"; } || \
echo "unknown init"

同样,具有systemd的系统通常具有目录/run/systemd/system
pevik

2

除了Debian(wheezy)/ Ubuntu(14.10。)之外,我还不了解其他系统,但是我使用普通的旧file命令测试了此类问题。

file /sbin/init

给这个:

/sbin/init: symbolic link to 'upstart'

具有systemd(例如sid)的Debian系统显示如下:

# file /sbin/init 
/sbin/init: symbolic link to /lib/systemd/systemd

您在什么系统上进行了测试?没有Debian / Ubuntu这样的东西,这在Debian上不起作用。您在Ubuntu上尝试过吗?
terdon

抱歉,我的意思是Debian(wheezy)/或Ubuntu(14.10。)。在debian上的输出: file /sbin/init /sbin/init: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, BuildID[sha1]=0x8c68a736c6a4e6fadf22d5ac9debf11e79c6bdcd, stripped 意味着我们在这里使用SYSV。我的答案中显示了ubuntu的输出。
zzeroo 2014年

的确如此,因此,您的解决方案似乎仅在碰巧使用upstart和Ubuntu时才有效。
terdon

@terdon:在运行systemd的系统(RHEL,Fedora)上,它返回“符号链接到... systemd”;在运行upstart的系统(Ubuntu)上,它返回“到upstart的符号链接”;在运行SysV init的系统(RHEL,Ubuntu,Debian)上,它返回“可执行”。虽然这并不是一个全面的调查,而且这种方法显然不是100%可靠的,但它显然更接近“至少为主要发行版工作”,而不是“仅是暴发户和Ubuntu”!
psmears 2014年

1
@psmears如果它也适用于某些systemd系统,那确实更好(但未在此答案中提及)。我刚刚在运行sysvinit的Debian和三个Ubuntu(10.04、12.04和14.04,都在运行新贵)上进行了测试,并 file /sbin/init在所有系统上返回“可执行”。在CentOS 5.8,SLES 10,Ubuntu 8.04上,它也返回相同的结果,基本上我可以使用的每个系统都可以。因此,据我所知,它甚至不适用于暴发户和Ubuntu。
terdon

2

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

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

    ps -p 1

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

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

    ls -l `which init`

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

  3. 查找提供可执行文件的软件包。不幸的是,这是依赖发行版的:

    dpkg-query -S (executable real path) # Debian  
    rpm -qf (executable real path) # RedHat  
    

然后,如果要编写脚本(最有趣的部分,恕我直言),这些是我的一线书(以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  

1
并非所有指定的操作系统都具有dpkg或rpm(此问题并非仅针对您的答案,BTW)。
Toby Speight

@ toby-speight是的,可以。请注意,我在回答中是这样说的,只是为了明确限制。
艾默生·普拉多

至于步骤3,您可以运行init --version以获取信息。
jarno

1

对于某些初始化系统来说,这确实很容易。对于systemd:

test -d /run/systemd/system

对于新贵:

initctl --version | grep -q upstart

对于其他任何事情,您都可以基于发行版进行假设(在OS X上启动,在Debian上启动sysvinit,在Gentoo上启动OpenRC)。


“假设基于发行版”不适用于Debian,因为它至少支持三种不同的初始化方式……
Toby Speight

1

这是执行检测的bash脚本。它目前仅检查新贵和systemd,但应该易于扩展。我从我为DisplayLink驱动程序安装脚本贡献的代码中获取了此代码

detect_distro()
{
  # init process is pid 1
  INIT=`ls -l /proc/1/exe`
  if [[ $INIT == *"upstart"* ]]; then
    SYSTEMINITDAEMON=upstart
  elif [[ $INIT == *"systemd"* ]]; then
    SYSTEMINITDAEMON=systemd
  elif [[ $INIT == *"/sbin/init"* ]]; then
    INIT=`/sbin/init --version`
    if [[ $INIT == *"upstart"* ]]; then
      SYSTEMINITDAEMON=upstart
    elif [[ $INIT == *"systemd"* ]]; then
      SYSTEMINITDAEMON=systemd
    fi
  fi

  if [ -z "$SYSTEMINITDAEMON" ]; then
    echo "WARNING: Unknown distribution, assuming defaults - this may fail." >&2
  else
    echo "Init system discovered: $SYSTEMINITDAEMON"
  fi
}

1
我认为将/proc这种情况与Linux(如果配置正确)以及其他一两个相关的问题联系在一起-这肯定不是普遍的。
Toby Speight


1

对于systemd

if [[ `systemctl is-system-running` =~ running ]]; then echo using systemd; fi

之所以投票,是因为与许多现有答案相比,该答案没有提供任何新的或不同的信息。
jayhendren

@jayhendren我在其他地方看不到此片段。它本身并不完整,可能最好将其视为对TvE的答案unix.stackexchange.com/a/164092/100397的评论
roaima

哦,你说得对@roaima。它与其他不完全相同。我在页面上搜索了一个不太具体的字符串:)
jayhendren

1
这似乎是迄今为止最正确的答案:)
Jack O'Connor

1

我的解决方案:检查以ID 1作为进程运行的命令。

case `cat /proc/1/comm` in
    init)    echo Init ;;
    systemd) echo SystemD ;;
    # add here other patterns
    *)       echo "unknown: '`cat /proc/1/comm`'" ;;
esac

目前,我只能访问Init和SystemD计算机,因此我无法确定如何检测到Upstart或macOS(OS X),但我会继续搜索。


0
check(){
    if hash systemctl 2>/dev/null;
    then
        echo "there is systemd"
    fi

    if hash initctl 2>/dev/null;
    then
        echo "there is upStart"
    fi

    if [ -f "/etc/inittab"];
    then
        echo "there is systemV"
    fi
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.