Cela peut avoir plus à voir avec la détection des systèmes d'exploitation, mais j'ai spécifiquement besoin du système init actuellement utilisé sur le système.
Fedora 15 et Ubuntu utilisent désormais systemd, Ubuntu utilisait Upstart (défaut de longue date jusqu'au 15.04), tandis que d'autres utilisent des variantes de System V.
J'ai une application que j'écris pour être un démon multiplateforme. Les scripts d'initialisation sont générés dynamiquement en fonction de paramètres pouvant être transmis lors de la configuration.
Ce que j'aimerais faire, c'est seulement générer le script pour le système d'initialisation particulier qu'ils utilisent. De cette façon, le script d'installation peut être exécuté raisonnablement sans paramètres en tant que root et le démon peut être "installé" de manière automatique.
Voici ce que j'ai trouvé:
Quelle serait la meilleure façon de faire cela sur plusieurs plates-formes?
Type de relation, Puis-je dépendre de bash pour être sur la majorité de * nix ou est-ce que cela dépend de la distribution/du système d'exploitation?
Plateformes cibles:
Pour la deuxième question, la réponse est non et vous devriez jeter un oeil à Ressources pour la programmation Shell portable .
En ce qui concerne la première partie - tout d'abord, vous devez certainement être prudent. Je dirais effectuez plusieurs tests pour vous assurer - parce que le fait que quelqu'un ait systemd (par exemple) installé, ne signifie pas qu'il est en fait utilisé par défaut init
. De plus, en regardant /proc/1/comm
peut être trompeur, car certaines installations de divers programmes d'initialisation peuvent automatiquement rendre /sbin/init
un lien symbolique ou même une version renommée de leur programme principal.
Peut-être que la chose la plus utile pourrait être de regarder le type de scripts d'initialisation - parce que c'est ce que vous allez réellement créer, peu importe ce qui les exécute.
En guise de remarque, vous pouvez également consulter OpenRC qui vise à fournir une structure de scripts d'initialisation compatible avec les systèmes Linux et BSD.
Je suis moi-même entré dans ce problème et j'ai décidé de faire quelques tests. Je suis entièrement d'accord avec la réponse selon laquelle on devrait emballer séparément chaque distribution, mais parfois il y a des problèmes pratiques qui empêchent cela (notamment la main-d'œuvre).
Donc, pour ceux qui veulent "détecter automatiquement", voici ce que j'ai découvert sur un ensemble limité de distributions (plus ci-dessous):
Vous pouvez dire par le biais de:
[[ `/sbin/init --version` =~ upstart ]] && echo yes || echo no
Vous pouvez indiquer à systemd:
[[ `systemctl` =~ -\.mount ]] && echo yes || echo no
Vous pouvez indiquer à sys-v init:
[[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]] && echo yes
Voici mes expériences avec la ligne de commande suivante:
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
sur les instances ec2 (j'inclus l'identifiant AMI us-east):
Juste pour être clair: Je ne prétends pas que c'est infaillible! , ce n'est certainement pas le cas. Notez également que pour plus de commodité, j'utilise des correspondances d'expression régulière bash, qui ne sont pas disponibles partout. Ce qui précède est assez bon pour moi en ce moment. Cependant, si vous trouvez une distribution où elle échoue, faites-le moi savoir et je vais essayer de la corriger s'il y a une AMI EC2 qui reproduit le problème ...
En regardant la sortie de quelques commandes ps
qui peuvent détecter les différentes versions de systemd
& upstart
, qui pourraient être conçues comme ceci:
parvenu
$ 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
Prêter attention au nom du processus qui est PID # 1 peut également éclairer le système d'initialisation utilisé. Sur Fedora 19 (qui utilise systemd
, par exemple:
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
Notez que ce n'est pas init
. Sur Ubuntu avec Upstart c'est toujours /sbin/init
.
$ ps -efa|grep init
root 1 0 0 Jan02 ? 00:00:03 /sbin/init
REMARQUE: Mais utilisez ceci avec un peu de prudence. Il n'y a rien de figé qui dit qu'un système d'initialisation particulier utilisé sur une distribution donnée a pour avoir systemd
comme PID #1.
générique
$ (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
Regardez les processus avec ppid 1 (enfants du processus init). (Certains des) noms de processus enfant peuvent pointer vers le système init utilisé.
Si vous interrogez l'exécutable init
, vous pouvez également en obtenir des informations. Analyser simplement le --version
production. Par exemple:
parvenu
$ 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
REMARQUE: Le fait que init
ne se trouve pas à son emplacement standard est un peu un indice. Il est toujours situé dans /sbin/init
sur les systèmes sysvinit.
sysvinit
$ type init
init is /sbin/init
Aussi ceci:
$ Sudo init --version
init: invalid option -- -
Usage: init 0123456SsQqAaBbCcUu
Il ne semble donc pas y avoir une seule façon de le faire, mais vous pouvez formuler une suite de vérifications qui identifierait le système d'initialisation que vous utilisez avec un degré de confiance assez élevé.
Pas si efficace que ça mais ça semble marcher.
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
Il imprimera plus de lignes si plusieurs chaînes correspondent, ce qui pourrait être traduit par "Je ne peux pas deviner". Les chaînes utilisées dans grep peuvent être légèrement modifiées, mais lorsqu'elles sont testées dans le système d'exploitation suivant, j'ai toujours une ligne.
Une approche plus simpliste de la même solution (mais elle s'arrête au premier match)
strings /sbin/init |
awk 'match($0, /(upstart|systemd|sysvinit)/) { print toupper(substr($0, RSTART, RLENGTH));exit; }'
Parfois, c'est aussi simple que d'utiliser ls
:
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 juin 25 12:04 /sbin/init -> /lib/systemd/systemd
Je suppose que si /sbin/init
n'est pas un lien symbolique, vous devrez vérifier les suggestions suivantes dans d'autres réponses.
J'ai également eu ce même problème et j'ai fait beaucoup de tests sur certaines machines RedHat/CentOS/Debian/Ubuntu/Mint. C'est ce que j'ai fini avec de bons résultats.
Trouvez le nom de l'exécutable avec PID 1:
ps -p 1
Si c'est systemd ou Upstart, le problème est résolu. Si c'est "init", il peut s'agir d'un lien symbolique ou autre chose qu'un nom initial. Aller de l'avant.
Trouvez le vrai chemin pour l'exécutable (ne fonctionne qu'en root):
ls -l `which init`
Si init
est un lien symbolique vers Upstart ou systemd, problème résolu. Sinon, il est presque certain que vous avez SysV init. Mais il peut s'agir d'un exécutable mal nommé. Aller de l'avant.
Trouvez le package qui fournit l'exécutable. Malheureusement, cela dépend de la distribution:
dpkg-query -S (executable real path) # Debian
rpm -qf (executable real path) # RedHat
Ensuite, si vous voulez écrire cela (la partie la plus drôle, à mon humble avis), ce sont mes one-liners (exécutés en tant que 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
C'est à cela que servent les packages spécifiques à la distribution. Installer un logiciel correctement ne se résume pas à détecter le système init. De nombreuses distributions utilisent SysVinit mais elles n'écrivent pas toutes leurs scripts d'initialisation de la même manière. La bonne façon de résoudre ce problème est d'inclure toutes les différentes variantes, puis de les regrouper en utilisant des fichiers de spécifications avec des noms de dépendance spécifiques aux distributions pour les distributions rpm, les fichiers deb pour les systèmes basés sur apt, etc. Presque toutes les distributions ont une sorte de spécification de package que vous peut écrire qui inclut des dépendances, des scripts, des scripts d'initialisation, etc. Ne réinventez pas la roue ici.
Non. Ce qui nous ramène à 1. Si vous avez besoin de bash, ce devrait être une dépendance. Vous pouvez spécifier cette vérification dans le cadre de vos scripts de configuration, mais elle devrait également figurer dans les descriptions des packages.
Edit: Utilisez des indicateurs sur votre script de configuration tels que --with upstart
ou --without sysvinit
. Choisissez une valeur par défaut saine, puis les scripts qui conditionnent votre logiciel pour d'autres distributions peuvent choisir de l'exécuter avec d'autres options.
L'inspection des descripteurs de fichiers peut également aider. Et c'est en fait en train de lancer init (Debian stretch permet actuellement d'installer plus de systèmes 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
Un moyen probablement plus sûr de vérifier la présence d'une boîte occupée serait de check /proc/1/exe
, comme busybox utilise généralement des liens symboliques:
$ ls -l /proc/1/exe
lrwxrwxrwx 1 root root 0 Jan 1 00:00 /proc/1/exe -> /bin/busybox
La vérification pourrait donc être:
{ 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"
Le simple fait de vous lancer dans le processus avec PID 1 vous dira:
strings /proc/1/exe |grep -q sysvinit
strings /proc/1/exe |grep -q systemd
Je ne connais pas d'autres systèmes que Debian (wheezy)/ou Ubuntu (14.10.) Mais je teste de tels problèmes avec la vieille commande file
.
file /sbin/init
donne ça:
/sbin/init: symbolic link to 'upstart'
Les systèmes Debian avec systemd
(par exemple sid) montrent ceci:
# file /sbin/init
/sbin/init: symbolic link to /lib/systemd/systemd
Sur debian/sbin/init est un lien symbolique vers votre init par défaut donc
ls -l /sbin/init
vous donnera les informations que vous recherchez.
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 nov 18 13:15 /sbin/init -> /lib/systemd/systemd
Sur Gentoo, jetez un œil au 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]
Si c'est init
, alors le système init est OpenRC
. Si c'est systemd
, alors le système init est systemd
.
Vous pouvez détecter Gentoo avec [ -f /etc/gentoo-release ]
.
Une autre méthode sur Gentoo consiste à utiliser profile-config show
, qui montrera quel profil par défaut est utilisé. Tous les profils, à l'exception des deux se terminant par/systemd, utilisent l'init OpenRC. Gardez à l'esprit que ceux-ci ne sont représentatifs que d'une valeur par défaut et il est possible que l'utilisateur ait pris des mesures pour remplacer cette valeur par défaut et peut ne pas indiquer le gestionnaire d'initialisation réellement utilisé.
C'est vraiment facile pour certains systèmes d'initialisation. Pour systemd:
test -d /run/systemd/system
parvenu:
initctl --version | grep -q upstart
pour tout le reste, vous pouvez simplement supposer basé sur la distribution (launchd sur OS X, sysvinit sur Debian, OpenRC sur Gentoo).
Pour systemd
:
if [[ `systemctl is-system-running` =~ running ]]; then echo using systemd; fi
Ma solution: vérifiez la commande exécutée en tant que processus avec l'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
Pour le moment, je n'ai accès qu'aux machines Init et SystemD, donc je ne peux pas dire comment Upstart ou macOS (OS X) seront détectés, mais je continuerai à chercher.
Voici un script bash pour effectuer la détection. Il ne vérifie pour le moment que upstart et systemd, mais devrait être facile à étendre. J'ai pris cela à partir du code que j'ai contribué au script d'installation du pilote 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
}
Il existe de nombreux pièges de compatibilité lors du test de systemd vs initd. Cela fonctionne réellement sur OpenSuSE 42.1: ps --pid 1 | grep -q systemd && echo 'systemd' || echo 'init'
How'bout celui-ci:
strings $(\ps -p 1 o cmd= | cut -d" " -f1) | egrep -o "upstart|sysvinit|systemd" | head -1
Testé uniquement sur les systèmes que j'ai: Ubuntu et 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
}