Warum unterscheidet sich der Exit-Status beim Ausführen von `systemctl start? systemctl is-active` und `systemctl is-active` getrennt?

7

Die folgende Sequenz gibt mir den Rückgabewert des ersten Befehls, nicht des zweiten, wie ich erwartet hätte (unabhängig davon, ob ich den ersten Befehl in einer Subshell ausführe):

sudo systemctl start x; sudo systemctl is-active --quiet x; echo $?;
(sudo systemctl start x); sudo systemctl is-active --quiet x; echo $?;

Der Dienst xist defekt und konnte nicht gestartet werden - er läuft also nicht. Der folgende Befehl, der eigenständig ausgeführt wird, gibt mir einen korrekten Rückgabewert, 3wie er sein sollte:

sudo systemctl is-active --quiet x; echo $?;

Warum erhalte ich 0beim Ausführen den Rückgabewert des ersten Befehls ( ) command; command; echo $?anstelle des Rückgabewerts ( 3) des zweiten mit echo $??

Ich bin dran GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu). Ich weiß, dass es funktioniert, wenn ich es in zwei Zeilen aufteile:

sudo systemctl start x;
sudo systemctl is-active --quiet x; echo $?;

Aber ich muss es als Einzeiler haben, da ich es in die PHP- shell_exec()Funktion stecke . Das zweimalige Ausführen shell_exec()hat das gleiche Ergebnis wie das Einfügen der Befehle in eine Zeile.

Manifestator
quelle

Antworten:

13

Wenn ich auf ein solches Problem stoße, folge ich eher dem Mantra von Sherlock Holmes und überlege, was übrig bleibt, wie unplausibel es auch sein mag, wenn das Unmögliche beseitigt ist. Natürlich ist mit Computern nichts unmöglich, aber einige Dinge sind so unwahrscheinlich, dass wir sie zunächst ignorieren können. (Dies ist sinnvoller mit dem Originaltitel " command; command; echo $?- Rückgabewert ist nicht korrekt, warum?")

In diesem Fall, wenn

sudo systemctl start x; sudo systemctl is-active --quiet x; echo $?;

zeigt, dass $?0 ist, was bedeutet, dass dies systemctl is-activewirklich Erfolg anzeigt. Die Tatsache, dass ein separater Dienst systemctl is-activeanzeigt, dass der Dienst nicht aktiv ist, deutet stark darauf hin, dass zwischen dem Dienst und dem menschlichen Bediener, der die Befehle eingibt, ein Wettlauf besteht. Grundsätzlich wird der Dienst in einem ausreichenden Umfang gestartet, um ihn systemctl startzu beenden und systemctl is-activeauszuführen und den Dienst aktiv zu finden. Dann schlägt der Dienst jedoch fehl, sodass ein von einem Benutzer eingegebener Dienst systemctl is-activeihn inaktiv findet.

Hinzufügen einer kurzen Verzögerung zwischen systemctl startund systemctl is-activesollte das falsche Positiv vermeiden.

Stephen Kitt
quelle
3

Systemd ruft den Dienst für kurze Zeit ( 0.1Sekunden) auf und dann stürzt der Dienst ab.

Gibt zurück, 3wie es sein sollte;

sudo systemctl start x; sleep 0.2; sudo systemctl is-active --quiet x; echo $?;

Weniger als 0.2Sekunden kehrt es zurück, 0wie es nicht sein sollte:

sudo systemctl start x; sleep 0.1; sudo systemctl is-active --quiet x; echo $?;

Auch wenn ich das tue, systemctl start x; ps -fA | grep -i xsehe ich den Service. Wenn ich danach pswieder renne, ist es weg.

Manifestator
quelle
1
Ein ExecStartPost, der prüft, ob der Dienst wirklich aktiv ist und fehlschlägt, wenn dies nicht der Fall ist, ist eine Möglichkeit, dies zu vermeiden. Wenn Sie die Quelle für Ihren Dienst verwalten und den sd_notifySocket verwenden, um den Starterfolg selbst zu melden, sogar mehr. Das heißt, es ist durchaus möglich sicherzustellen, dass systemd weiß, wann ein Dienst tatsächlich erfolgreich initialisiert wurde. Es braucht nur etwas zusätzlichen Code.
Charles Duffy
+1 @CharlesDuffy zur sd_notifyInformation - leider pflege ich die Quelldateien nicht. Deshalb ist es einfacher zu bedienen sleepund abzuwarten, was passiert :)
Manifest
1

Versuche dies:

sudo systemctl start x && sudo systemctl is-active --quiet x; echo $?;

Dadurch &&wird das System gezwungen, die Befehle der Reihe nach auszuführen, und zwar nur dann, wenn der vorherige Befehl erfolgreich ausgeführt wurde.

Jason
quelle
2
Dies behandelt nicht den Fall, den der Benutzer hier höchstwahrscheinlich erlebt, dh, dass der Dienst lange genug gestartet wird , damit auch is-activetrue zurückgegeben wird, aber dann stirbt. Wie andere vorgeschlagen haben, ist es besser, zwischen den beiden systemctlAnrufen eine kurze Verzögerung von ein oder zwei Sekunden einzufügen .
Kusalananda