it-swarm.it

Rileva il sistema init usando la shell

Questo potrebbe avere più a che fare con il rilevamento di sistemi operativi, ma ho bisogno in particolare del sistema init attualmente in uso sul sistema.

Fedora 15 e Ubuntu ora usano systemd, Ubuntu usava Upstart (impostazione predefinita da molto tempo fino al 15.04), mentre altri usano varianti di System V.

Ho un'applicazione che sto scrivendo per essere un demone multipiattaforma. Gli script init vengono generati dinamicamente in base a parametri che possono essere passati su configure.

Quello che mi piacerebbe fare è solo generare lo script per il particolare sistema init che stanno usando. In questo modo lo script di installazione può essere eseguito ragionevolmente senza parametri come root e il demone può essere "installato" automagicamente.

Questo è quello che ho escogitato:

  • Cerca systemd, upstart, ecc. In/bin
  • Confronta/proc/1/comm con systemd, upstart, ecc
  • Chiedi all'utente

Quale sarebbe il miglior modo cross/platform per farlo?

Tipo di correlato, Posso dipendere da bash per essere sulla maggior parte di * nix o dipende dalla distribuzione/dal sistema operativo?

Piattaforme target:

  • Mac OS
  • Linux (tutte le distribuzioni)
  • BSD (tutte le versioni)
  • Solaris, Minix e altri * nix
96
beatgammit

Per la seconda domanda, la risposta è no e dovresti dare un'occhiata a Risorse per la programmazione Shell portatile .

Per quanto riguarda la prima parte - prima di tutto, devi certamente stare attento. Direi esegui diversi test per assicurarti - perché il fatto che qualcuno abbia systemd (per es.) Installato, non significa che sia effettivamente usato come predefinito init. Inoltre, guardando /proc/1/comm può essere fuorviante, poiché alcune installazioni di vari programmi init possono rendere automaticamente /sbin/init un collegamento simbolico o anche una versione rinominata del loro programma principale.

Forse la cosa più utile potrebbe essere guardare il tipo di script init - perché quelli sono ciò che creerai effettivamente, indipendentemente da ciò che li esegue.

Come nota a margine, potresti anche dare un'occhiata a OpenRC che mira a fornire una struttura di script init che sia compatibile con i sistemi Linux e BSD.

30
rozcietrzewiacz

Ho affrontato questo problema da solo e ho deciso di fare alcuni test. Concordo pienamente con la risposta che si dovrebbe impacchettare separatamente per ciascuna distro, ma a volte ci sono problemi pratici che lo impediscono (non ultimo il personale).

Quindi, per coloro che vogliono "rilevare automaticamente", ecco cosa ho scoperto su un numero limitato di distro (più sotto):

  • Puoi iniziare a partire da:

    [[ `/sbin/init --version` =~ upstart ]] && echo yes || echo no
    
  • Puoi dire a systemd da:

    [[ `systemctl` =~ -\.mount ]] && echo yes || echo no
    
  • Puoi dire a sys-v init da:

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

Ecco i miei esperimenti con la seguente riga di comando:

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

su istanze ec2 (includo l'id AMI us-est):

  • ArchLinux: using systemd (dal 2012.10.06 )
  • CentOS6.4 AMI-52009e3b: usando upstart
  • CentOS7 AMI-96a818fe: utilizzando systemd
  • Debian 6 AMI-80e915e9: usando sysv-init
  • Debian 7.5 AMI-2c886c44: usando sysv-init
  • Debian 7.6 GCE container-vm: usando sysv-init
  • RHEL 6.5 AMI-8d756fe4: usando upstart
  • SLES 11 AMI-e8084981: usando sysv-init
  • Ubuntu 10.04 AMI-6b350a02: usando upstart
  • Ubuntu 12.04 AMI-b08b6cd8: usando upstart
  • Ubuntu 14.04 AMI-a427efcc: usando upstart
  • Ubuntu 14.10 e successivi: usando systemd
  • AWS linux 2014.3.2 AMI-7c807d14: usando upstart
  • Fedora 19 AMI-f525389c: usando systemd
  • Fedora 20 AMI-21362b48: usando systemd

Giusto per essere chiari: Non sto affermando che questo sia infallibile! , quasi certamente non lo è. Inoltre, per comodità, utilizzo le partite di rehexp bash, che non sono disponibili ovunque. Quanto sopra è abbastanza buono per me in questo momento. Tuttavia, se trovi una distro in cui fallisce, fammi sapere e proverò a risolverlo se c'è un AMI EC2 che riproduce il problema ...

61
TvE

Utilizzando i processi

Osservando l'output di un paio di comandi ps in grado di rilevare le varie versioni di systemd & upstart, che potrebbero essere realizzate in questo modo:

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

Prestare attenzione al nome del processo PID n. 1 può anche far luce su quale sistema init viene utilizzato. Su Fedora 19 (che utilizza systemd, ad esempio:

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

Si noti che non è init. Su Ubuntu con Upstart è ancora /sbin/init.

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

NOTA: Ma usa questo con un po 'di cautela. Non c'è nulla in pietra che dica che un particolare sistema init usato su una data distro deve avere systemd come PID # 1.

generico

$ (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

Guarda i processi con ppid 1 (figli del processo init). (Alcuni dei) nomi di processi secondari potrebbero puntare al sistema init in uso.

Il filesystem

Se si interroga l'eseguibile init, è possibile ottenere anche alcune informazioni da esso. Analizzando semplicemente il --version produzione. Per esempio:

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

NOTA: Il fatto che init non si trovi nella sua posizione standard è un po 'un suggerimento. Si trova sempre in /sbin/init su sistemi sysvinit.

sysvinit

$ type init
init is /sbin/init

Anche questo:

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

Conclusioni

Quindi non sembra esserci un modo per farlo, ma potresti formulare una serie di controlli che individuerebbero quale sistema di inizializzazione stai usando con un livello abbastanza alto di sicurezza.

19
slm

Non così efficiente, ma sembra che funzioni.

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

Stamperà più righe se corrispondono più di una stringa, che potrebbe essere tradotta in "Impossibile indovinare". Le stringhe utilizzate in grep potrebbero essere leggermente modificate ma quando sono state testate nel seguente sistema operativo ho sempre ottenuto una riga.

  • RHEL 6.4 [UPSTART]
  • RHEL ES 4 (Nahant Update 7) [SYSVINIT]
  • Ubuntu 16.04.1 LTS [SYSTEMD]
  • Ubuntu 14.04.2 LTS [UPSTART]
  • Fedora versione 23 (Shell online) [SYSTEMD]
  • Debian GNU/Linux 7 (shell online) [SYSTEMD]
  • Centos 7.6 (VM) [SYSTEMD]

Un approccio più semplicistico della stessa soluzione (ma si ferma alla prima partita)

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

A volte è facile come usare ls:

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

Immagino che se /sbin/init non è un collegamento simbolico, dovrai controllare ulteriori suggerimenti seguenti in altre risposte.

Ho avuto anche lo stesso problema e ho fatto molti test su alcune macchine RedHat/CentOS/Debian/Ubuntu/Mint. Questo è quello che ho finito con buoni risultati.

  1. Trova il nome dell'eseguibile con PID 1:

    ps -p 1
    

    Se è systemd o Upstart, problema risolto. Se è "init", potrebbe essere un collegamento simbolico o qualcosa di diverso da un nome iniziale. Vai avanti.

  2. Trova il percorso reale per l'eseguibile (funziona solo come root):

    ls -l `which init`
    

    Se init è un collegamento simbolico a Upstart o systemd, problema risolto. Altrimenti, è quasi certo che hai SysV init. Ma può essere un eseguibile errato. Vai avanti.

  3. Trova il pacchetto che fornisce l'eseguibile. Sfortunatamente, questo è distro-dipendente:

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

Quindi, se vuoi copiarlo (la parte più divertente, IMHO), questi sono i miei one-liner (esegui come 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  
5
Emerson Prado
  1. Ecco a cosa servono i pacchetti specifici per la distribuzione. C'è molto di più per installare correttamente il software oltre a rilevare il sistema init. Molte distribuzioni usano SysVinit ma non tutte scrivono i loro script init allo stesso modo. Il modo corretto per risolvere questo problema è includere tutte le diverse varianti e poi raggrupparle usando file di specifiche con nomi di dipendenza specifici della distro per distribuzioni rpm, file deb per sistemi basati su apt, ecc. Quasi tutte le distro hanno una sorta di specifica del pacchetto che può scrivere che include dipendenze, script, script init, ecc. Non reinventare la ruota qui.

  2. No. Il che ci riporta a 1. Se hai bisogno di bash dovrebbe essere una dipendenza. È possibile specificare questo controllo come parte degli script di configurazione, ma dovrebbe anche essere nelle descrizioni dei pacchetti.

Modifica: Usa flag sul tuo script di configurazione come --with upstart o --without sysvinit. Scegli un valore predefinito sano, quindi gli script che impacchettano il tuo software per altre distro possono scegliere di eseguirlo con altre opzioni.

3
Caleb

Anche l'ispezione dei descrittori di file può aiutare. Ed è effettivamente eseguendo init (il tratto Debian attualmente consente di avere più sistemi init installati) :-)

$ 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

Probabilmente il modo più sicuro per verificare la presenza di busybox sarebbe check /proc/1/exe, dato che busybox usa solitamente i symlink:

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

Quindi il controllo potrebbe essere:

{ 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"
3
pevik

Semplicemente entrare nel processo con PID 1 ti dirà:

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

Non conosco altri sistemi oltre a Debian (wheezy)/o Ubuntu (14.10.), Ma collaudo tali problemi con il semplice vecchio comando file.

file /sbin/init

dare questo:

/sbin/init: symbolic link to 'upstart'

I sistemi Debian con systemd (ad es. Sid) mostrano questo:

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

Su debian/sbin/init è presente un link simbolico al tuo init predefinito

ls -l /sbin/init

ti darà le informazioni che stai cercando.

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

Su Gentoo, dai un'occhiata a 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]

Se è init, il sistema init è OpenRC. Se è systemd, il sistema init è systemd.

Puoi rilevare Gentoo con [ -f /etc/gentoo-release ].

Un altro metodo su Gentoo è usare profile-config show, che mostrerà quale profilo predefinito è in uso. Tutti i profili tranne i due che terminano in/systemd utilizzano l'init OpenRC. Tieni presente che questi sono solo rappresentativi di un valore predefinito ed è possibile che l'utente abbia adottato delle misure per sovrascrivere tale valore predefinito e potrebbe non essere indicativo del gestore di avvio effettivamente in uso.

2
casey

Questo è davvero facile per alcuni sistemi init. Per systemd:

test -d /run/systemd/system

per iniziare:

initctl --version | grep -q upstart

per qualsiasi altra cosa, puoi semplicemente supporre in base alla distro (lanciato su OS X, sysvinit su Debian, OpenRC su Gentoo).

1
CameronNemo

Per systemd:

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

La mia soluzione: controlla il comando in esecuzione come processo con 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

Al momento ho accesso solo alle macchine Init e SystemD, quindi non posso dire come verranno rilevati Upstart o macOS (OS X), ma continuerò a cercare.

1
t0r0X

Ecco uno script bash per eseguire il rilevamento. Controlla solo upstart e systemd al momento, ma dovrebbe essere facile da estendere. Ho preso questo dal codice ho contribuito allo script di installazione del driver 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
Ben McCann

Esistono molte insidie ​​sulla compatibilità durante il test di systemd vs initd. Questo funziona su OpenSuSE 42.1: ps --pid 1 | grep -q systemd && echo 'systemd' || echo 'init'

1
David Lakatos

Che ne dici di questo:

strings $(\ps -p 1 o cmd= | cut -d" " -f1) | egrep -o "upstart|sysvinit|systemd" | head -1

Testato solo sui sistemi che ho: Ubuntu e SailfishOS.

0
SebMa
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
}
0
hxysayhi