Script restart per Cronjob verursacht <defunct>

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 22.06.2011 09:53:05

Hallo zusammen,

ich habe ein Script geschrieben welches ein cat auf eine serielle Schnittstelle ausführt und die ankommenden Daten in eine Datei schreibt.
Dieses Script soll jede Nacht um 0:00 Uhr neu gestartet werden.
http://nopaste.debianforum.de/35681

Neu starten per Hand funktioniert wunderbar, aber per Cronjob klappt der Neustart nicht. Ich finde den Prozess meines Scripts morgens dann immer mit dem Zusatz <defunct> in der Prozessliste vor.
Ich habe bereits versucht die PIDs auf verschiedene Arten zu ermitteln und dem Eintrag in der crontab ein /bin/bash voranzustellen. Leider alles ohne Erfolg.

Kann mir jemand weiterhelfen?

Gruß
artificial

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 22.06.2011 10:49:26

Eine Idee mit dem rsyslogd,
/dev/ttyS0 als Log-Quelle, der "Neustart" wäre dann zur Zeit des logrotate / cron.daily.
Zuletzt geändert von rendegast am 22.06.2011 11:29:56, insgesamt 1-mal geändert.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 22.06.2011 11:14:26

Hallo rendegast,

danke für deine schnelle Antwort.
Ich verstehe nicht ganz was du meinst.
rendegast hat geschrieben:/dev/ttyS0 als Quelle
??

Wo kann ich sehen wann cron.daily ausgeführt wird? Mein Script muss zwingend um 0:00 Uhr gestartet werden. Auch das tail nach dem cat ist notwendig.

Gruß
artificial

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 22.06.2011 15:24:43

War nur so eine lustige Idee,
Problem wären aber zBsp. die vom syslog eingefügten Zeitstempel.
--------------------------------------------------


Was mit so auffällt:

Code: Alles auswählen

                        mail -s "$SUBJECT" "$ADDRESS" < script.`date '+%Y-%m-%d'`.log
                fi; done
esac
muß sein

Code: Alles auswählen

                        mail -s "$SUBJECT" "$ADDRESS" < script.`date '+%Y-%m-%d'`.log
                fi; done
                ;;
esac
Die Reihenfolge beim Killen:

Code: Alles auswählen

        stop)
                if [ -e PIDtail ] ; then
                        kill -15 `cat PIDtail` >/dev/null 2>&1
                fi
                if [ -e PIDscript ] ; then
                        kill -15 `cat PIDscript` >/dev/null 2>&1
                fi
                if [ -e PIDcat ] ; then
                        kill -15 `cat PIDcat` >/dev/null 2>&1
                fi
                ;;

        restart)
               if [ -e PIDscript ] ; then
                        kill -15 `cat PIDscript` >/dev/null 2>&1
                fi
                if [ -e PIDtail ] ; then
                        kill -15 `cat PIDtail` >/dev/null 2>&1
                fi
                if [ -e PIDcat ] ; then
                        kill -15 `cat PIDcat` >/dev/null 2>&1
                fi
...
sollte nach kurzem Versuch
PIDtail
PIDcat
PIDscript
sein, wobei script da schon beendet sein würde.
Etwas dynamischer ginge es vielleicht damit (PID-Dateien traue ich nie so richtig):

Code: Alles auswählen

        stop)
                kill -15 $(lsof | grep -e "tail.*$DIR/script.*log" | awk '{print $2}')
                kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
                ;;

        restart)
                kill -15 $(lsof | grep -e "tail.*$DIR/script.*log" | awk '{print $2}')
                kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
...





Viele greps:

Code: Alles auswählen

                ALARM=`echo "$line" | \
                grep -v '^../../.. ..:..:.. Code ' | \
                grep -v '[-]$' | \
                grep -v '^../../.. ..:..:.. Akku-Test' | \
                grep -v '^../../.. ..:..:.. Deakt. ' | \
                grep -v 'A1' | \
                grep -v '^../../.. ..:..:.. Start ' | \
                grep -v '^../../.. ..:..:.. Akt. '`
könnte auch

Code: Alles auswählen

                ALARM=`echo "$line" | \
                grep -v | \
                -e '^../../.. ..:..:.. Code ' | \
                -e '[-]$' | \
                -e '^../../.. ..:..:.. Akku-Test' | \
                -e '^../../.. ..:..:.. Deakt. ' | \
                -e 'A1' | \
                -e '^../../.. ..:..:.. Start ' | \
                -e '^../../.. ..:..:.. Akt. '`
denn dann nur ein grep-Prozeß.

Variablen benutzen:

Code: Alles auswählen

#!/bin/bash -l

case "$1" in
        start)
                # speichern der Prozess-ID des Scripts
                cd /home/user/logs
...
                cat /dev/ttyS0 >> /home/user/logs/script.`date '+%Y-%m-%d'`.log &
...
        stop)
                cd /home/user/logs
...
        restart)
                cd /home/user/logs
...
esac
umgeformt

Code: Alles auswählen

#!/bin/bash -l

DIR=/home/user/logs
SRC=/dev/ttyS0
#DATE=$(date '+%Y-%m-%d')
DATE=$(date '+%F')

cd $DIR

case "$1" in
        start)
...
                cat $SRC >> $DIR/script.$DATE.log &
...
        stop)
...
        restart)
...
esac
Noch ein paar Zeilen einsparen, und Funktionen sehen elegant aus:

Code: Alles auswählen

case "$1" in
        start)
...
        stop)
...
        restart)
...
esac

Code: Alles auswählen

do_start() {
}
do_stop() {
}

case "$1" in
        start)
                do_start
                ;;
        stop)
                do_stop
                ;;
        restart)
                do_stop
                do_start
                ;;
esac
Zuletzt geändert von rendegast am 22.06.2011 15:55:45, insgesamt 6-mal geändert.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 22.06.2011 15:44:36

Wow vielen Dank.

Dass am Ende des Restart Blocks das doppelte Semikolon fehlte war mir noch nicht aufgefallen. Habe ich korrigiert.
Deinen Vorschlag den kill-Befehl mittels

Code: Alles auswählen

kill -15 $(lsof | grep -e "tail.*$DIR/script.*log" | awk '{print $2}')
zu realisieren werde ich auch testen.

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 22.06.2011 15:58:07

Habe nicht gemerkt, daß Du das schon angesehen hast.
Mittlerweile nach 6! weiteren Änderungen hat mein Post aber die endgültige Form.


Tip,
schreibe Skripte mit einem Editor mit Syntax-Highlighting,
dann fallen solche kleinen Patzer eher auf.
Beim mcedit sind die ";;" knallrot.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 24.06.2011 10:31:56

Hallo rendegast,

habe nun deine Änderungen bisauf die Funktionsdefinitionen übernommen. Danke.
Leider wurde das Script auch letzte Nacht nicht neu gestartet. Im syslog steht auch nichts dazu.
Es funktioniert wie gesagt per Hand alles wunderbar, nur per Cron nicht. Gibt es außer der verwendeten Shell noch andere Unterschiede zwischen Cron und dem manuellen Aufruf?

Gruß
artificial

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 25.06.2011 07:14:08

Es funktioniert wie gesagt per Hand alles wunderbar, nur per Cron nicht.
Yepp. wohl weil das Skript eigentlich nicht beendet wird.
"per Hand" kommt das 'stop/kill' ja von einer anderen Instanz.
Denn Beobachtung:
Bearbeitungs-Änderungen im Skript, zBsp. 'echo'-Marker,
wurden erst ausgeführt nach Änderungen des (f)cron-Jobs,
was wohl eine neue Ausführung / Instanz des Jobs bedingte, damit verbunden ein Neueinlesen des Skripts.


Vorschlag mit 2 Skripten,
eines führt nur den "start"-Teil in einer while-Schleife aus, vor 00:00 zBsp. um 23:59 gestartet,
beachte den zusätzlichen Test, den Hilfsskript-Kill und die sleep-Pause:

Code: Alles auswählen

#!/bin/sh -l

[ "$(pgrep ^script.sh | wc -l)" -gt 2 ] && exit 0
  # Test, um Doppelstarts des Skripts zu verhindern.
  # Weitere Verfeinerung nötig, da sonst jeder Prozeß mit Namen "script.sh" hier Probleme macht, siehe Anmerkung 2.

while true; do

pgrep ^manuelles_Hilfsskript && { kill $(pgrep ^manuelles_Hilfsskript) ; }       # und dessen Prozesse, siehe auch Anmerkung 1

DIR=/home/user/logs
DIR=/tmp/gubash
SRC=/dev/ttyS0
#DATE=`date '+%Y-%m-%d'`
DATE=$(date '+%F')

cd $DIR

#case "$1" in
case start in
        start)
                # speichern der Prozess-ID des Scripts
#                cd $DIR
#                echo $(pgrep ^script) > PIDscript

                # schreiben der Logmeldungen
                cat $SRC >> $DIR/script.$DATE.log &

                # speichern der Prozess-ID des cat
                echo $! > PIDcat

                # filtern bekannter Logeintraege
                tail -fn0 script.$DATE.log | echo $(pgrep tail) > PIDtail | while read line ; do
                ALARM=`echo "$line" | \
                grep -v -e '^../../.. ..:..:.. Code ' | \
                -e '[-]$' | \
                -e '^../../.. ..:..:.. Akku-Test' | \
                -e '^../../.. ..:..:.. Deakt. ' | \
                -e 'A1' | \
                -e '^../../.. ..:..:.. Start ' | \
                -e '^../../.. ..:..:.. Akt. '`

                # wenn Logeintrag nicht bekannt, separates speichern der Meldung und Mail senden
                if [ "$ALARM" != "" ]
                then
                        echo $ALARM >> ALARM.$DATE.log
                        SUBJECT="SCRIPT: $line"
                        ADDRESS="root"
                        mail -s "$SUBJECT" "$ADDRESS" < script.$DATE.log
                fi; done
                ;;

esac

sleep 2        # Dem kill-Prozeß ein wenig Zeit geben, verhindert auch eine superschnelle Schleife im Problemfall
done

#exit 0
:
und das andere sorgt mit dem stop/kill-Teil für den "Restart" um 00:00:

Code: Alles auswählen

#!/bin/sh -l

DIR=/home/user/logs
DIR=/tmp/gubash
SRC=/dev/ttyS0
#DATE=`date '+%Y-%m-%d'`
DATE=$(date '+%F')

cd $DIR

#case "$1" in
case stop in
        stop)
#                cd $DIR
                # lesen der alten Prozess-IDs und beenden des Prozesses
#                [ -e PIDtail ]	&& { kill -15 `cat PIDtail` >/dev/null 2>&1 ; }
#                [ -e PIDcat ]	&& { kill -15 `cat PIDcat` >/dev/null 2>&1 ; }
                kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
                kill -15 $(lsof | grep -e "tail.*$DIR/script.*log" | awk '{print $2}')
                kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
#                if [ -e PIDscript ] ; then
#                        kill -15 `cat PIDscript` >/dev/null 2>&1
#                fi
                ;;

esac

#exit 0
:
das funktioniert hier.
(Ich mußte dem kill-tail ein kill-cat jedoch wieder voranstellen, sonst vermehrten sich gelegentlich die cat-Prozesse
-> Verhalten austesten)


Anmerkung 1:
Als Idee für die Praxis noch ein Hilfsskript zum manuellen Ausführen,
falls das Skript des cron-Jobs doch mal einen Totalausfall hat.
Dieses hätte einen anderen Namen, zBsp. script3.sh , damit das Skript des cron-Jobs es im Bedarfsfall killen kann,
bevor es dann wieder die eigentliche Aufgabe übernimmt.
Das Manuelle Hilfsskript hätte einen veränderten Anfang:

Code: Alles auswählen

#!/bin/sh -l

[ "$(pgrep ^script.sh | wc -l)" -gt 0 ] && exit 0
[ "$(pgrep ^manuelles_Hilfsskript | wc -l)" -gt 1 ] && exit 0

while true; do

DIR=/home/user/logs
...
d.h. es startet nicht. wenn der cron-Job läuft oder es selbst schon läuft.
Die '-gt 2', '-gt 0', '-gt 1' sind der jeweiligen beabsichtigten Startart geschuldet.
(Jedenfalls ist es so gedacht.
Kommt es doch zu mehrfacher Ausführung eines der Skripte resp. das Hauptskript killt nicht das Hilfsskript
-> Abstimmungsarbeit, genauere Testbedingungen.)

Weitere beobachtete "Spezialität":
'pgrep ^manuelles_Hilfsskript' verhält sich nicht so wie erwartet
wenn der String "manuelles_Hilfsskript" zu lang wird, bei mir war hier Schluß: "1234567890ABCDE", 15 Stellen.

Das Hilfsskript könnte alternativ auch in das Hauptskript eingebettet werden,
als zusätzliche (default-)Startoption, die sich nur außerhalb von (f)cron starten läßt,
während die anderen nur innerhalb (f)cron laufen dürfen.
Geht alles mit verfeinerten Tests.





Anmerkung 2:
Sicherheitsaspekte in einer gefährlichen Umgebung:
Das Skript ist mit der obigen EigenKill-Bedingung und dem festen Namen angreifbar.
Verfeinerung der Tests und Verwendung von 'mktemp' für Skript- und Verzeichnisnamen böten sich da an.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 27.06.2011 10:39:41

Hallo rendegast,

danke nochmals für deine Hilfe.
Ich versuche die Lösung mit 2 Skripten einmal umzusetzen.

Muss das "Kill-Script" nicht zuerst gestartet werden und anschließend das "Start-Script"? Es wir doch sonst, so wie du es beschrieben hast, das "Start-Script" um 23:59 Uhr gestartet und direkt um 00:00 Uhr wieder beendet, oder nicht?

Gruß
artificial

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 27.06.2011 14:05:30

Nein, denn das kill-Skript wirkt nur noch auf die 'cat' und 'tail'.
Das eigentliche Skript läuft durch die 'while true;'-Schleife endlos weiter.

Du hast oben geschrieben
"Mein Script muss zwingend um 0:00 Uhr gestartet werden"
Da ging es noch um 'script restart', also daß beim neuen Datum in eine neue Datei geschrieben wird.
Das ist mit dem Unterbrechen der 'cat / tail' erreicht.

Wann das Skript gestartet wird, ist eigentlich egal, es sollte vor der 'cat / tail'-Unterbrechung geschehen.
Und das ist zu jeder Tageszeit der Fall, aber 23:59 paßt halt so schön zu 00:00 ;)


Eins nochmal:
Da "Haupt"-Skript funktioniert so auch nur unter der cron-Ausführung,
für ein manuelles Starten vor 23:59 ist nur das manuelle_Hilfsskript vorgesehen,
das um 23:59 durch das unter (f)cron laufende "Haupt"script abgelöst wird.
(Natürlich läßt sich das ganze auch in ein einziges Skript gießen,
daß über Parameter gesteuert wird.
Aber dann werden die pgrep-Tests aufwändiger.)
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 27.06.2011 15:07:43

Sorry ich blicke grad nicht mehr durch.
Ich habe es nun folgendermaßen eingestellt:

Um 23:59 Uhr wird folgendes Skript gestartet:

Code: Alles auswählen

!#/bin/bash -l

DIR=/home/user/logs
SRC=/dev/ttyS0
DATE=$(date '+%F')

cd $DIR

case "$1" in
        start)
                # schreiben der Logmeldungen
                cat $SRC >> $DIR/script.$DATE.log &

                # speichern der Prozess-ID des cat
                echo $! > PIDcat

                # filtern bekannter Logeintraege
                tail -fn0 script.$DATE.log | echo $(pgrep tail) > PIDtail | while read line ; do
                ALARM=`echo "$line" | \
                grep -v -e '^../../.. ..:..:.. Code ' | \
                -e '[-]$' | \
                -e '^../../.. ..:..:.. Akku-Test' | \
                -e '^../../.. ..:..:.. Deakt. ' | \
                -e 'A1' | \
                -e '^../../.. ..:..:.. Start ' | \
                -e '^../../.. ..:..:.. Akt. '`

                # wenn Logeintrag nicht bekannt, separates speichern der Meldung und Mail senden
                if [ "$ALARM" != "" ]
                then
                        echo $ALARM >> ALARM.$DATE.log
                        SUBJECT="SCRIPT: $line"
                        ADDRESS="root"
                        mail -s "$SUBJECT" "$ADDRESS" < script.$DATE.log
                fi; done
                ;;
esac
Um 00:00 Uhr kommt folgendes Script zur Ausführung

Code: Alles auswählen

#!/bin/bash -l

DIR=/home/user/logs
DATE=$(date '+%F')

cd $DIR

kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
kill -15 $(lsof | grep -e "tail.*$DIR/script.*log" | awk '{print $2}')
kill -15 $(lsof | grep -e "cat.*$DIR/script.*log" | awk '{print $2}')
Ich bin noch ziemlich unwissend was bash-Scripting angeht.
Der Grund warum zuerst gestartet werden soll und anschließend wieder gekillt ist mir nach wie vor nicht klar. Deine Lösung mit dem while habe ich nicht verstanden.

Wie wird es denn in anderen Daemons von beispielsweise Serverdiensten geregelt? Da gibt es doch auch immer nur ein Skript, oder?

Gruß
artificial

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 27.06.2011 17:21:57

Code: Alles auswählen

while Bedingung; do
   bla
done
Die Schleife wird so lange durchlaufen, wie die Bedingung "wahr" ausgibt.
Im Falle die Bedingung ist der Immer-wahre Befehl 'true' ergibt das eine endlose Schleife.

Bei Deinem Skript fehlt die Schleife,
also führt das killen von 'cat / tail' um 00:00 zum Abbrechen des ganzen Skriptes.

Die Schleife muß das Setzen von DATE enthalten,
sonst würde bei jedem Schleifendurchlauf der immer gleiche Wert vom ersten DATE gelten.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 28.06.2011 09:55:21

Habe die while-Schleife eingebaut.
Jetzt klappt es. Vielen Dank :)

artificial
Beiträge: 145
Registriert: 09.10.2008 20:14:48

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von artificial » 01.07.2011 10:06:56

Code: Alles auswählen

[ "$(pgrep ^script.sh | wc -l)" -gt 2 ] && exit 0
Mir ist aufgefallen dass bei diesem Aufruf immer nur eine Prozess-ID zurückgeliefert wird. Auch wenn der Prozess mehrfach läuft. Wie lässt sich das sonst noch lösen?

rendegast
Beiträge: 15041
Registriert: 27.02.2006 16:50:33
Lizenz eigener Beiträge: MIT Lizenz

Re: Script restart per Cronjob verursacht <defunct>

Beitrag von rendegast » 01.07.2011 11:13:41

Du rufst das Skript von der sh auf, das gibt nur eine Prozeß-ID.
Im cron aber aufgerufen werden 2 ID ausgegeben.
Mach einfach mal eine Pipe in eine Log-Datei,
auch gut in der hierarchischen Baum-Ansicht von htop zu sehen.

Das habe ich oben schon geschrieben, das Skript ist mit diesem Test nur für die Anwendung als cron-Job gedacht.
mfg rendegast
-----------------------
Viel Eifer, viel Irrtum; weniger Eifer, weniger Irrtum; kein Eifer, kein Irrtum.
(Lin Yutang "Moment in Peking")

Antworten