To může mít více společného s detekcí operačních systémů, ale potřebuji konkrétně systém init, který se v systému právě používá.
Fedora 15 a Ubuntu nyní používají systemd, Ubuntu používal Upstart (dlouhodobě výchozí do 15.04), zatímco ostatní používají varianty systému V.
Mám aplikaci, kterou píšu, jako démona napříč platformami. Inicializační skripty jsou dynamicky generovány na základě parametrů, které lze předat při konfiguraci.
Chtěl bych pouze vygenerovat skript pro konkrétní systém init, který používají. Tímto způsobem lze instalační skript spouštět přiměřeně bez parametrů jako root a démona lze automaticky „nainstalovat“.
To je to, s čím jsem přišel:
Jaký by byl nejlepší způsob napříč platformami?
Druh příbuzného, Mohu se spoléhat na to, že bash bude na většině * nix nebo je to závislé na distribuci/OS?
Cílové platformy:
Pro druhou otázku je odpověď ne a měli byste se podívat na Zdroje pro přenosné programování Shell .
Pokud jde o první část - především musíte být opatrní. Řekl bych provést několik testů, abyste se ujistili - protože skutečnost, že někdo nemá systemd (např.) Nainstalován, neznamená, že je to ve skutečnosti používá se jako výchozí init
. Při pohledu na /proc/1/comm
může být zavádějící, protože některé instalace různých init programů mohou automaticky provést /sbin/init
odkaz na symlink nebo dokonce přejmenovanou verzi jejich hlavního programu.
Možná by nejužitečnější bylo podívat se na typ init skriptů - protože to jsou to, co ve skutečnosti vytvoříte, bez ohledu na to, co je spouští.
Jako vedlejší poznámku se můžete také podívat na OpenRC , jehož cílem je poskytnout strukturu init skriptů, která je kompatibilní jak se systémy Linux, tak BSD.
Sám jsem vstoupil do tohoto problému sám a rozhodl se udělat několik testů. Plně souhlasím s odpovědí, že jeden by měl balíček pro každou distro samostatně, ale někdy existují praktické problémy, které tomu brání (v neposlední řadě pracovní síla).
Takže pro ty, kteří chtějí "auto-detekovat" tady je to, co jsem zjistil na omezeném počtu distros (více níže):
Můžete říci upstart od:
[[ `/sbin/init --version` =~ upstart ]] && echo yes || echo no
Systemd můžete říct z:
[[ `systemctl` =~ -\.mount ]] && echo yes || echo no
Můžete sys-v init říct z:
[[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]] && echo yes
Zde jsou mé experimenty s následující příkazovou řádkou:
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
v případech ec2 (zahrnuji ID AMI z východu USA):
Jednoduše řečeno: Netvrdím, že je to spolehlivé! , téměř určitě to tak není. Také si všimněte, že pro větší pohodlí používám bash regexp zápasy, které nejsou dostupné všude. Výše uvedené je pro mě právě teď dost dobré. Pokud však najdete distro, kde selže, dejte mi prosím vědět a pokusím se to opravit, pokud existuje ECI AMI, který problém reprodukuje ...
Při pohledu na výstup z několika příkazů ps
, které mohou detekovat různé verze systemd
& upstart
, které by mohly být vytvořeny takto:
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
systemd
$ 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
Věnovat pozornost názvu procesu, který je PID # 1, může také potenciálně vrhnout světlo na to, který systém init se používá. Na Fedoře 19 (která používá systemd
, například:
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
Všimněte si, že to není init
. Na Ubuntu s aktualizací je to stále /sbin/init
.
$ ps -efa|grep init
root 1 0 0 Jan02 ? 00:00:03 /sbin/init
POZNÁMKA: Ale používejte to s trochou opatrnosti. Není nic nastaveného v kameni, které říká, že konkrétní iniciační systém používaný na daném distro musí mít jako PID systemd
# 1.
obecný
$ (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
Podívejte se na procesy s ppid 1 (děti iniciačního procesu). (Některá z) podřízených názvů procesů mohou ukazovat na použitý systém init.
Pokud vyslýcháte spustitelný soubor init
, můžete z něj získat také nějaké informace. Jednoduše analyzujte --version
výstup. Například:
upstart
$ 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.
systemd
$ type init
init is /usr/sbin/init
POZNÁMKA: Skutečnost, že init
není ve svém standardním umístění, je trochu nápověda/vyprávění. Vždy se nachází v /sbin/init
na sysvinitových systémech.
sysvinit
$ type init
init is /sbin/init
Také toto:
$ Sudo init --version
init: invalid option -- -
Usage: init 0123456SsQqAaBbCcUu
Zdá se, že neexistuje žádný způsob, jak to udělat, ale mohli byste formulovat sadu kontrol, které by určily, který systém iniciátoru používáte s poměrně vysokou mírou důvěry.
Ne tak efektivní, ale zdá se, že to funguje.
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
Pokud se bude shodovat více řetězců, vytiskne více řádků, které by mohly být přeloženy do „Nelze hádat“. Řetězce použité v grepu se daly mírně modifikovat, ale při testování v následujících os jsem vždy dostal jednu řádku.
Zjednodušený přístup stejného řešení (ale zastaví se při prvním zápase)
strings /sbin/init |
awk 'match($0, /(upstart|systemd|sysvinit)/) { print toupper(substr($0, RSTART, RLENGTH));exit; }'
Někdy je to stejně snadné jako použití ls
:
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 juin 25 12:04 /sbin/init -> /lib/systemd/systemd
Myslím, že pokud /sbin/init
není symbolický odkaz, budete muset zkontrolovat další následující návrhy v jiných odpovědích.
Také jsem měl stejný problém a udělal jsem spoustu testů na některých strojích RedHat/CentOS/Debian/Ubuntu/Mint. To jsem skončil s dobrými výsledky.
Najděte název spustitelného souboru pomocí PID 1:
ps -p 1
Pokud je to systemd nebo Upstart, problém vyřešen. Pokud se jedná o „init“, může to být symbolický odkaz nebo něco jiného než jméno předem. Pokračuj.
Najděte skutečnou cestu ke spustitelnému souboru (funguje pouze jako root):
ls -l `which init`
Pokud init
je symbolický odkaz na Upstart nebo systemd, problém vyřešen. V opačném případě je téměř jisté, že máte SysV init. Ale může to být špatně pojmenovaný spustitelný soubor. Pokračuj.
Najděte balíček, který poskytuje spustitelný soubor. Bohužel je to závislé na distro:
dpkg-query -S (executable real path) # Debian
rpm -qf (executable real path) # RedHat
Pokud tedy chcete skriptovat (nejzábavnější část, IMHO), jedná se o moje jednorázové vložky (spouštěné jako 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
To je to, co distro-specifické balíčky jsou pro. Správná instalace softwaru je mnohem více, než jen zjištění systému init. Mnoho distros používá SysVinit, ale ne všichni z nich píšou své iniciační skripty stejným způsobem. Správný způsob, jak to vyřešit, je zahrnout všechny různé varianty a poté je spojit pomocí souborů spec se jmény distro-specific závislostí pro rpm distros, deb soubory pro apt systémy atd. Téměř všechna distros mají nějakou specifikaci balíčku můžete psát, který zahrnuje závislosti, skripty, inicializační skripty atd. Zde znovu nevynalézejte kolo.
Ne. Což nás přivádí zpět k 1. Pokud potřebujete bash, měla by to být závislost. Tuto kontrolu můžete zadat jako součást vašich konfiguračních skriptů, ale měla by být také v popisech balíčků.
pravit: Použijte příznaky ve vašem konfiguračním skriptu, například --with upstart
nebo --without sysvinit
. Vyberte rozumný výchozí stav, a pak si skripty, které dodávají váš software pro další distribuce, mohou zvolit spuštění s dalšími možnostmi.
Pomůže také kontrola deskriptorů souborů. A je to z aktuálně spuštěného init (Debian stretch aktuálně umožňuje mít nainstalovaných více init systémů) :-)
$ 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
Pravděpodobně bezpečnějším způsobem kontroly busyboxu by bylo check /proc/1/exe
, protože busybox obvykle používá symbolické odkazy:
$ ls -l /proc/1/exe
lrwxrwxrwx 1 root root 0 Jan 1 00:00 /proc/1/exe -> /bin/busybox
Kontrola by tedy mohla být:
{ 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"
Jednoduše se zapojíte do procesu s PID 1 vám řekne:
strings /proc/1/exe |grep -q sysvinit
strings /proc/1/exe |grep -q systemd
Nevím o jiných systémech než Debian (wheezy)/nebo Ubuntu (14.10.), Ale takové problémy testuji pomocí prostého starého příkazu file
.
file /sbin/init
dát toto:
/sbin/init: symbolic link to 'upstart'
Debianové systémy s systemd
(např. Sid) ukazují toto:
# file /sbin/init
/sbin/init: symbolic link to /lib/systemd/systemd
Na debian/sbin/init je symbolický odkaz na váš výchozí init
ls -l /sbin/init
vám poskytne informace, které hledáte.
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 nov 18 13:15 /sbin/init -> /lib/systemd/systemd
Na Gentoo se podívejte na 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]
Pokud je to init
, pak je iniciačním systémem OpenRC
. Pokud je to systemd
, pak je iniciačním systémem systemd
.
Gentoo můžete detekovat pomocí [ -f /etc/gentoo-release ]
.
Další metodou na Gentoo je použití profile-config show
, která ukáže, jaký výchozí profil se používá. Všechny profily kromě dvou konců v/systemd používají initRR. Mějte na paměti, že jsou pouze reprezentativní pro výchozí nastavení a je možné, že uživatel podnikl kroky k přepsání tohoto výchozího nastavení a nemusí naznačovat, že se spouštěcí program skutečně používá.
Pro některé init systémy je to opravdu snadné. Pro systemd:
test -d /run/systemd/system
pro začátek:
initctl --version | grep -q upstart
pro cokoli jiného, stačí předpokládat na základě distro (spuštění v OS X, sysvinit na Debianu, OpenRC na Gentoo).
Pro systemd
:
if [[ `systemctl is-system-running` =~ running ]]; then echo using systemd; fi
Moje řešení: zkontrolujte příkaz spuštěný jako proces s 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
V tuto chvíli mám přístup pouze k počítačům Init a SystemD, takže nemohu říct, jak bude detekován Upstart nebo macOS (OS X), ale budu pokračovat ve vyhledávání.
Zde je bash skript pro detekci. Momentálně kontroluje pouze upstart a systemd, ale mělo by být snadno rozšířitelné. Vzal jsem si to z kódu, který jsem přispěl do instalačního skriptu ovladače 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
}
Při testování systemd vs initd existuje mnoho nástrah kompatibility. Ve skutečnosti to funguje na OpenSuSE 42.1: ps --pid 1 | grep -q systemd && echo 'systemd' || echo 'init'
Jak na to:
strings $(\ps -p 1 o cmd= | cut -d" " -f1) | egrep -o "upstart|sysvinit|systemd" | head -1
Testováno pouze na systémech, které mám: Ubuntu a SailfishOS.
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
}