Bash Bash/Linux - Übersehener Syntaxfehler in Script?

_anonymous0815_

Lt. Commander
Registriert
Aug. 2020
Beiträge
1.314
Hallo Forum,

ich arbeite in letzter Zeit an einem kleineren Projekt, um meine Discs automatisch beim einlegen ins Laufwerk einzulesen.

Ich arbeite dabei mit folgender, eigener udev-Regel:
Code:
root@editing-vm:/home/edit/scripts# cat /etc/udev/rules.d/90-srX-changing.rules
ACTION=="change", KERNEL=="sr[0-9]*", ENV{ID_CDROM_DVD}=="1", ENV{ID_CDROM_MEDIA_STATE}=="complete", ENV{ID_FS_TYPE}=="udf", RUN+="/home/edit/scripts/read_discs.sh %k"
ACTION=="change", KERNEL=="sr[0-9]*", ENV{ID_CDROM_DVD}=="1", ENV{ID_CDROM_MEDIA_STATE}=="complete", ENV{ID_FS_TYPE}=="iso9660", RUN+="/home/edit/scripts/read_discs.sh %k"

#ACTION=="change", KERNEL=="sr[0-9]*", ENV{ID_CDROM_BD}=="1", ENV{ID_CDROM_MEDIA_STATE}=="complete", ENV{ID_FS_TYPE}=="udf", RUN+="/home/edit/scripts/read_discs.sh %k"
#ACTION=="change", KERNEL=="sr[0-9]*", ENV{ID_CDROM_BD}=="1", ENV{ID_CDROM_MEDIA_STATE}=="complete", ENV{ID_FS_TYPE}=="iso9660", RUN+="/home/edit/scripts/read_discs.sh %k"

Dabei wird das Skript, read_discs.sh angestoßen, das sieht folgendermaßen aus:

Bash:
#!/bin/bash

if [[ $1 =~ sr[0-9]+ ]]
  then
    srX="/dev/$1"
    srXlog="/home/edit/scripts/srXlog"
    blkid_label=$(blkid ${srX} | awk '{print $3}')
    uidlog="/home/edit/scripts/uid.log"

    MKVUser="edit"
    MakeMKVHome="/home/edit/WORK/MakeMKV/RAW"
    ProgressFile="/home/edit/scripts/${1}.progress"
    TargetDirectory="$(date '+%d-%m-%Y')-${blkid_label}/"
    min_length="4800"
    msg=""
    timestamp=$(date '+%d.%m.%Y %H:%M:%S')

    function autorip {
      start_time=$(date +%s)
      log_message 1
      su ${MKVUser} -c "mkdir -p ${MakeMKVHome}/${TargetDirectory} && makemkvcon --messages=-null --progress=${ProgressFile} --noscan --cache=1024 --minlength=${1} mkv dev:${srX} all ${MakeMKVHome}/${TargetDirectory}"
      RESULT=$?
      if [ $RESULT -eq 0 ] #funktioniert wenn manuell angestartet - NICHT wenn über udev gestartet
        then
          end_time=$(date +%s)
          log_message 0 $(expr $end_time - $start_time)
          eject ${srX}
          exit 0
        else
          end_time=$(date +%s)
          log_message 2 $(expr $end_time - $start_time)
          exit 1
      fi
    }

    function log_message {
      if [[ $1 == 0 ]]
        then
          export msg="0 SUCCESSFUL"
        elif [[ $1 == 2 ]]
          then
            export msg="2 ERROR"
        else
          export msg="1 RUNNING"
      fi

      if [[ $1 == 1 ]]
        then
          printf "UID: ${uid} Time: ${timestamp} Device: ${srX} ${blkid_label} Message: ${msg} Duration: 0 Seconds. \n" >> ${srXlog}
        else
          sed -i "s/UID: $uid Time: $timestamp Device: $srX $blkid_label Message: $msg Duration: 0 Seconds./UID: $uid Time: $timestamp Device: $srX $blkid_label Message: $msg Duration: $2 Seconds./" "$srXlog"
      fi
    }

    if ! [ -z ${blkid_label} ]
      then
        if ! [[ $(su ${MKVUser} -c "makemkvcon --noscan --minlength ${min_length} info dev:${srX} | awk 'END{print}'") == 'Total 0 titles' ]]
          then
            source ${uidlog} && printf "uid=$(expr $uid + 1)" > ${uidlog} && autorip ${min_length}
          else
            min_length="300"
            source ${uidlog} && printf "uid=$(expr $uid + 1)" > ${uidlog} && autorip ${min_length}
        fi
      else
        exit 0
    fi
  else
    exit 1
fi

Und jetzt habe ich folgendes Problem.

Wenn die Disc im Laufwerk drinnen liegt und ich mit ./read_discs.sh sr0

das Skript anstarte, funktioniert es perfekt. So wie es sein soll.
Wenn allerdings eine Disc neu eingelegt wird und das Laufwerk einrastet und udev das Skript startet, funktioniert das Skript zwar auch, ABER der letzte eject-Befehl, sowie der letzte Aufruf der Logfunktion bleibt aus.

Gerade der letzte Aufruf der Logfunktion ist entscheidend, da ich später den Log in ein Webinterface überführen möchte, was ungefähr so aussehen soll:
1710947113536.png

Seht Ihr eventuell einen groben Schnitzer, den ich übersehe?

Vielen Dank schon mal.
 
Zuletzt bearbeitet:
Grob sieht es gut aus.
Wenn du in Zeile 23 eine neue Zeile einfügst, hinter "RESULT=$?", mit dem
Inhalt "log_message 1 $RESULT", taucht dann im Log welcher Wert auf?

Ansonsten kannst du auch das Skript im Debugmodus laufen lassen:
#!/bin/bash

exec 5> /tmp/mydebugoutput.txt
BASH_XTRACEFD="5"
set -x
.....Rest vom Skript......

So wird die Ausgabe von einem Durchlauf in /tmp/mydebugoutput.txt abgelegt.
 
  • Gefällt mir
Reaktionen: _anonymous0815_
Könnte es mit Rechten zu tun haben? Schau mal als was udev das Skript ausführt und probier das dann mal manuell.
 
GTrash81 schrieb:
exec 5> /tmp/mydebugoutput.txt
BASH_XTRACEFD="5"
set -x
Ich probiere das mal aus!

Mit set -x, bzw. bash -x script.sh bringt ja nichts, weil es in einer Shellumgebung sauber durchläuft.

Aber wenn das auch den Output von udev aus loggt, sollte man ja herausfinden, wo der Fehler sitzt! Hatte anfänglich gedacht, dass die Pathvariablen beim udev-Aufruf leer ist und ein source /etc/profile eingefügt, das hat erst mal nichts geändert.


GTrash81 schrieb:
log_message 1 $RESULT"
Kann ich auch mal schauen, mache aber erst mal das komplette Logging.
Ergänzung ()

nullPtr schrieb:
udev das Skript ausführt und probier das dann mal
whoami sagt in beiden Fällen erst mal root und das ist erst mal korrekt. Bei su edit -c "" kommt die finale File auch mit den richtigen Rechten heraus.
 
Verflixt, der Output endet einfach abrupt, wenn das eigentliche einlesen fertig ist.

Code:
+ [[ sr1 =~ sr[0-9]+ ]]
+ srX=/dev/sr1
+ srXlog=/home/edit/scripts/srXlog
++ blkid /dev/sr1
++ awk '{print $3}'
+ blkid_label='LABEL="AVATAR_CE_D1"'
+ uidlog=/home/edit/scripts/uid.log
+ MKVUser=edit
+ MakeMKVHome=/home/edit/WORK/MakeMKV/RAW
+ ProgressFile=/home/edit/scripts/sr1.progress
++ date +%d-%m-%Y
+ TargetDirectory='20-03-2024-LABEL="AVATAR_CE_D1"/'
+ min_length=4800
+ msg=
++ date '+%d.%m.%Y %H:%M:%S'
+ timestamp='20.03.2024 18:01:42'
+ '[' -z 'LABEL="AVATAR_CE_D1"' ']'
++ su edit -c 'makemkvcon --noscan --minlength 4800 info dev:/dev/sr1 | awk '\''END{print}'\'''
+ [[ '' == \T\o\t\a\l\ \0\ \t\i\t\l\e\s ]]
+ source /home/edit/scripts/uid.log
++ uid=7                                                                                                                                                                         ++ expr 7 + 1
+ printf uid=8
+ autorip 4800
++ date +%s
+ start_time=1710954133
+ log_message 1
+ [[ 1 == 0 ]]
+ [[ 1 == 2 ]]
+ export 'msg=1 RUNNING'                                                                                                                                                         + msg='1 RUNNING'
+ [[ 1 == 1 ]]
+ printf 'UID: 7 Time: 20.03.2024 18:01:42 Device: /dev/sr1 LABEL="AVATAR_CE_D1" Message: 1 RUNNING Duration: 0 Seconds. \n'
+ su edit -c 'mkdir -p /home/edit/WORK/MakeMKV/RAW/20-03-2024-LABEL="AVATAR_CE_D1"/ && makemkvcon --messages=-null --progress=/home/edit/scripts/sr1.progress --noscan --cache=10
24 --minlength=4800 mkv dev:/dev/sr1 all /home/edit/WORK/MakeMKV/RAW/20-03-2024-LABEL="AVATAR_CE_D1"/'
 
Der Abbruch an der Stelle ergibt Sinn. Du schreibst ja in deinem Skript auch, dass es ab dort nicht mehr tut.

Du kannst mal probieren, stdout und stderr von der betreffenden Zeile in ein File umzuleiten. (Oder den kompletten Output des Skripts.)
 
nullPtr schrieb:
Der Abbruch an der Stelle ergibt Sinn.
Überhaupt nicht.

Erst mal kommt dann eine weitere If-Anweisung und der sagt entweder WENN erfolgreich, calle die log-Funktion und ändere die Zeile auf successful SONST calle die log-Funktion und ändere die Zeile auf error...

Egal wie... etwas muss er tun... dass er gar nichts tut ist extrem seltsam.

Aber er macht gar nichts.
 
Zuletzt bearbeitet:
Mir fällt auf, dass end_time nur im Else-Teil gesetzt wird und somit bei Erfolg expr eigentlich immer scheitern müsste. Ist end_time vielleicht aus irgendeinem Grund im Environment Deiner Shell gesetzt, so dass es da funktioniert, aber im Environment von Udev nicht gesetzt ist und deswegen ein ungültiger Ausdruck berechnet wird?
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: GTrash81 und _anonymous0815_
H4110 schrieb:
Mir fällt auf, dass end_time nur im Else-Teil gesetzt wird
Das muss mir beim editieren passiert sein, ich ändere das mal und lasse es durchlaufen. Glaube aber auch, dass es vorher auch nicht ging.
Ergänzung ()

Anmerkung: Ist bereits im Skript richtig hinterlegt, keine Ahnung warum das im Eingangspost gefehlt hat.
 
Hast Du schon im System Journal nach einer Fehlermeldung von Udev geschaut?

Ich könnte mir vorstellen, dass es so prinzipbedingt nicht funktionieren kann:
https://www.freedesktop.org/software/systemd/man/latest/udev.html#RUN{type}

This can only be used for very short-running foreground tasks. Running an event process for a long period of time may block all further events for this or a dependent device.

Note that running programs that access the network or mount/unmount filesystems is not allowed inside of udev rules, due to the default sandbox that is enforced on systemd-udevd.service.

Starting daemons or other long-running processes is not allowed; the forked processes, detached or not, will be unconditionally killed after the event handling has finished. In order to activate long-running processes from udev rules, provide a service unit and pull it in from a udev device using the SYSTEMD_WANTS device property. See systemd.device(5) for details.
Vor allem der zweite Absatz bezüglich Mount/Unmount ist relevant. Und vielleicht killt Udev den Prozess auch einfach, wenn es zulange dauert.

Versuch mal, es als Service zu starten, dort ist ein Beispiel:
https://www.baeldung.com/linux/shell-run-script-usb-plugged#long-running-tasks

Der Device-Name kann übrigens mit @ als Instanz-Name übergeben werden, also z.B. systemctl --user start read-discs@sr0.service. Er ist dann in der Unit als %i verfügbar.
 
  • Gefällt mir
Reaktionen: GTrash81 und _anonymous0815_
Also vorweg: Das reine ejecten funktioniert auch mit udev, das habe ich bereits getestet. Aber es funktioniert aus irgendeinem Grund nicht im vollständigen Skript.

H4110 schrieb:
Hast Du schon im System Journal nach einer Fehlermeldung von Udev geschaut?
Guter Einwand!

Code:
Mär 20 18:02:13 editing-vm su[6248]: pam_unix(su:session): session opened for user edit(uid=1000) by (uid=0)
Mär 20 18:02:42 editing-vm (udev-worker)[6212]: sr1: Spawned process '/home/edit/scripts/read_discs.sh sr1' [6214] is taking longer than 59s to complete
Mär 20 18:02:42 editing-vm systemd-udevd[421]: sr1: Worker [6212] processing SEQNUM=3193 is taking a long time
Mär 20 18:04:42 editing-vm (udev-worker)[6212]: sr1: Spawned process '/home/edit/scripts/read_discs.sh sr1' [6214] timed out after 2min 59s, killing
Mär 20 18:04:42 editing-vm systemd-udevd[421]: sr1: Worker [6212] processing SEQNUM=3193 killed
Mär 20 18:04:42 editing-vm systemd-udevd[421]: sr1: Worker [6212] terminated by signal 9 (KILL).

H4110 schrieb:
Vielleicht killt Udev den Prozess einfach, wenn es zulange dauert.
Sieht beinahe danach aus. :/
Ergänzung ()

_anonymous0815_ schrieb:
Sieht beinahe danach aus. :/
Habe mal in diesem Kontext nach udev und Timeout gesucht, anscheinend ist es wirklich so, dass udev das Skript dann einfach killt, weil das einlesen auch mal gut und gern eine Stunde dauern kann.

Dabei fand ich jetzt diese Lösung, die ich zunächst umsetzen wollen würde, da die Nutzung als systemd Service zwar auch reizvoll ist, aber ich mich da noch mal einlesen müsste, bezüglich Unitfile, etc... falls die Lösung scheitert, greife ich darauf zurück.

https://unix.stackexchange.com/a/28711
Ergänzung ()

_anonymous0815_ schrieb:
Verstehe das mal einer... ich gehe auf "share" und "copy link" und der Link endet in 404... :confused_alt:
Ergänzung ()

1000050184.jpg
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: H4110
Das an at zu delegieren sollte funktionieren.

Die Service-Variante ist übrigens auch relativ einfach. Aber stimmt, um das zu wissen muss man erstmal einiges lesen.

/etc/systemd/system/read-disc@.service
Code:
[Unit]
Description=Read disc

[Service]
User=edit
ExecStart=/home/edit/scripts/read_discs.sh %i

Und in den Udev-Regeln dann RUN+="systemctl start read-disc@%k.service".
 
  • Gefällt mir
Reaktionen: _anonymous0815_
H4110 schrieb:
Das an at zu delegieren sollte funktionieren.
Prinzipiell schon, kann aber nur Shell und eben nicht Bash. Habs auf die Schnelle getestet, es passiert nichts, ich mache jetzt die Servicevariante!

Hab auf dem System eh schon TigerVNC als systemd-Service angelegt
Ergänzung ()

Also als Service ist das viel eleganter, ich lasse es jetzt einmal durchlaufen und schaue, ob es dieses Mal problemlos läuft.

Aktuell musste ich in der Unitfile den Nutzer auf root festlegen, perspektivisch schaue ich, ob es überhaupt notwendig ist, das Skript anfänglich als root auszuführen. Denn eigentlich spricht nichts dafür.
 
Zuletzt bearbeitet:
H4110 schrieb:
Und in den Udev-Regeln dann RUN+="systemctl start read-disc@%k.service".
Da das bei mir nie funktioniert hat, habe ich nach langem recherchieren rausgefunden, dass man das ganze auch durch systemd alleine bewerkstelligen kann. Dafür bietet Systemd eine Reihe an Targets an, auf die man mit Services reagieren kann. Dadurch muss man sich nicht mit udev rumschlagen.

Durch den folgenden Aufruf bekommt man eine Liste von verfügbaren Geräten, an die man Services binden kann:
Code:
systemctl list-units --type device --all
Der hier interessante Eintrag ist "dev-sr0.device", den man in der Service-Datei als "WantedBy" eintragen kann.

Hier der Inhalt meines Test-Services, welcher bei Einlegen einer Disk eine Notifikation "Test" absetzt:
Service-Datei im Home-Verzeichnis: ~/.config/systemd/user/sr.service
Code:
[Unit]
Description=Service for displaying test notification when disk is inserted

[Service]
Type=oneshot
ExecStart=/usr/bin/notify-send "Test"

[Install]
WantedBy=dev-sr0.device

Bei "ExecStart" kann man dann einen beliebigen Aufruf angeben.

Nach Änderungen muss man systemd neuladen lassen:
Code:
systemctl --user daemon-reload

Mittels folgendem Aufruf kann man testen, ob das Skript aufgerufen werden kann:
Code:
systemctl --user start br.service

Und mit folgendem Aufruf installiert man den Dienst, damit er automatisch bei Einlegen einer Disk gestartet wird:
Code:
systemctl --user enable br.service

Keine root-Rechte erforderlich.

Wenn das ganze für einen Benutzer ohne Sitzung erfolgen soll, muss man das noch erlauben (braucht aber einmalig root-Rechte):
Code:
loginctl enable-linger $USER

Ich habe auf diese Weise ein Backup-Skript geschrieben (allerdings dann unter root als sytem-service), welches automatisch startet, sobald ich meine externe Festplatte (hier ist das Target das Partitions-Label der Platte) an mein NAS anschließe. Per Mail erhalte ich dann eine Benachrichtigung, damit ich weiß, wann das Skript fertig ist und ich die Platte wieder abziehen kann.

Mit einem user-service betreibe ich einen Jellyfin-Container auf meinem NAS.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: _anonymous0815_ und H4110
Bisher kriege ich es leider auch nicht zum laufen, obwohl udev in einer root-Umgebung den Service starten können sollte, schlägt es jedes Mal mit Exit Code 1 fehl.
 
Eine Anmerkung dazu: Type=oneshot bewirkt, dass der Service erst als gestartet gilt, wenn der Befehl von ExecStart terminiert. systemctl start blockt bis der terminiert. Als Befehl einer Udev-Regel hätte man wieder das Problem, dass es zu lange dauert.
 
Ich glaube ich habe den Fehler entdeckt %i escaped /, %I escaped / nicht.

Mit %I startet er schon mal an!
Ergänzung ()

Hmh, jetzt läuft pro Prozess der RAM ordentlich voll (> 3,5 GiB)...

Wüsste jetzt aber nicht, was diese Speichernutzung befeuert, MakeMKVCon Cache sind pro Prozess 1 GiB.
 
ENV{SYSTEMD_WANTS}+="read-disc@%k.service" statt RUN+=systemctl ... müsste übrigens auch gehen, vielleicht funktioniert das aus irgendeinem Grund besser.

Wenn der Service User=edit hat, musst Du su rausnehmen, denn dann würde es nach dem Passwort fragen, was ohne stdin nicht geht.
 
  • Gefällt mir
Reaktionen: _anonymous0815_
H4110 schrieb:
Wenn der Service User=edit hat, musst Du su rausnehmen, denn dann würde es nach dem Passwort fragen, was ohne stdin nicht geht.
Schon geschehen, root ist ausnahmsweise mal überhaupt nicht vonnöten. :)
Ergänzung ()

Wow, klappt jetzt tatsächlich, musste den sed-Befehl und die blkid_label-Variable anpassen, damit / nicht als Seperator verwendet wird und " nicht den String vorher beenden.

Jetzt kann es endlich ans Eingemachte gehen, Stichwort: Darstellung im Webinterface.

Vielen Dank an alle für den regen Austausch!
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: H4110
Zurück
Oben