ubuntuusers.de

Bash-Script um externe Sicherungsplatte unter Bedingungen zu prüfen

Status: Ungelöst | Ubuntu-Version: Xubuntu 19.10 (Eoan Ermine)
Antworten |

glaskugel

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Ich habe mir ein Script mit halbstündlichen Cronjob gemacht, dass die Platten auf "No Errors Logged" prüft, sieht in etwa auszugsweise so aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if test -n "$MOUNTED" ; then
	ERRORS=`/usr/sbin/smartctl -s on -l error /dev/sd$DEV | /bin/grep -i "No Errors Logged"`
	if test -n "$ERRORS" ; then
		ERRORS=0
		#/usr/sbin/smartctl -s on -x /dev/sd$DEV > /tmp/emailBody.txt
		#mutt -s "KEINE FEHLER bei /dev/sd$DEV ($RECHNERNAME) - $FAMILY ($MODEL - $SERIAL)" $INFOANUSER@localhost < /tmp/emailBody.txt
	else
		ERRORS=`/usr/sbin/smartctl -s on -l error /dev/sd$DEV | /bin/grep -i "occurred at disk" | sort -r | head -n1 | /usr/bin/cut -f2 -d" "`
		if [ $ERRORSALT -lt $ERRORS ] ; then
			/usr/sbin/smartctl -s on -x /dev/sd$DEV > /tmp/emailBody.txt
			mutt -s "ALARM: ERRORS bei /dev/sd$DEV ($RECHNERNAME) auf $ERRORS erhöht - $MODEL" $INFOANUSER@localhost < /tmp/emailBody.txt
		fi
	fi
fi

Cronjob dazu:

25,55 * * * * /usr/local/bin/hd_errors_check.sh 2>&1 > /dev/null

Im normalen Betrieb haben die Platten also eine hohe Chance regelmäßig geprüft zu werden. Bei einer externen (LUKS-verschlüsselten) Sicherungsplatte könnte es durchaus sein, dass die Platte nie geprüft wird, weil die Sicherung in der Regel nur Minuten dauert und dann das zu einem anderen Zeitpunkt passieren kann.

Die Frage ist nun, wie löse ich die Prüfung der externen HD am besten aus?

Gesichert wird mit User-Rechten, die HD wird mit root-Rechten geprüft.

Ich kann mir vorstellen, dass die externe HD beim "umount" geprüft wird, also nicht notwendigerweise beim Runterfahren des PCs, da kann die schon längst manuell abgemeldet sein. Ich bin mir nicht sicher, ob die Luks-Verschlüsselung Auswirkungen auf smartctl hat bzw. ob die externe HD mit dem LUKS-PW gemountet sein muss, vermute aber nicht.

Eventuell kann man das Sicherungsscript nach Beendigung der Sichern für die HD-Prüfung starten. Problem ist hier eben, dass der User sichert, aber das Root-PW nicht in das Script geschrieben werden soll (oder sudo, es gibt einen User root mit PW)

Wie könnte man das automatisiert lösen? Der sichernde User kennt das Root-PW, aber das sollte nicht notwendig sein.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13207

glaskugel schrieb:

Du könntest mal von Backticks auf $(...) umstellen - das ist lesbarer und lässt sich verlässlich schachteln.

Im normalen Betrieb haben die Platten also eine hohe Chance regelmäßig geprüft zu werden. Bei einer externen (LUKS-verschlüsselten) Sicherungsplatte könnte es durchaus sein, dass die Platte nie geprüft wird, weil die Sicherung in der Regel nur Minuten dauert und dann das zu einem anderen Zeitpunkt passieren kann.

Die Frage ist nun, wie löse ich die Prüfung der externen HD am besten aus?

udev

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Du könntest mal von Backticks auf $(...) umstellen - das ist lesbarer

Eigentlich hast du recht, weil sich dann andere leichter tun, für mich sind die Backticks gewohnter, sind ja nur ein paar Zeilen und ich habe Probleme bei der Fehlersuche mich auf die 2 Varianten einzustellen.

Ich habe mittlerweile ein anderes Konzept und scheitere an einer awk-Syntax.

cat /proc/partitions 
major minor  #blocks  name

   7        0     221624 loop0
   7        1      45240 loop1
   7        2      55932 loop2
   7        3      91228 loop3
   7        4     203704 loop4
   7        5     153924 loop5
   7        6     160440 loop6
   7        7      29596 loop7
 259        0  488386584 nvme0n1
 259        1     524288 nvme0n1p1
 259        2   97655808 nvme0n1p2
 259        3  146484224 nvme0n1p3
 259        4   97655808 nvme0n1p4
 259        5  146064384 nvme0n1p5
   8        0 7814026584 sda
   8        1  976561152 sda1
   8        2 1953125376 sda2
   8        3  585937920 sda3
   8        4 4295898112 sda4
   8        5    2502656 sda5
   8       16 5860522584 sdb
   8       17 5860520960 sdb1
  11        0    1048575 sr0
   7        8     151732 loop8
   7        9     151728 loop9

So filtere ich die sdX-Geräte raus:

awk '/[ \t]sd[a-z]$/ { print "/dev/" $4; }' /proc/partitions
/dev/sda
/dev/sdb

Aber bei nvme scheitere ich:

awk '/[ \t]nvme[0-9]$/ { print "/dev/" $4; }' /proc/partitions

Da werden keine nvme-Geräte angezeigt. Zu udev frage ich später im Detail nach. Vielleicht kann ich mein Script da irgendwo dazu hängen?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13207

Das geht einfacher:

1
awk '$4 ~ /^(sd|nvme)/ {print "/dev/" $4}' /proc/partitions 

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Ein wenig weiter bin ich, aber ich verstehe die Nomenklatur von nvme noch zu wenig und habe bis jetzt nur 1 SSD, das Script soll aber gleich so gemacht werden, dass es für mehrere passt.https://wiki.hetzner.de/index.php/NVMe habe ich mir angesehen.

awk '/[ \t]nvme[0-9]n1$/ { print "/dev/" $4; }' /proc/partitions
/dev/nvme0n1

Nun gibt es das nächste Problem

eval `udevadm info -q env -n /dev/nvme0n1 | grep 'ID_SERIAL'`
SSD: Befehl nicht gefunden.

Bei "ID_SERIAL=Samsung SSD 970" wird also das Leerzeichen für einen Befehl interpretiert.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

rklm schrieb:

Das geht einfacher:

1
awk '$4 ~ /^(sd|nvme)/ {print "/dev/" $4}' /proc/partitions 

Unsere Antworten haben sich überschnitten, aber deine Variante ist mir sympathischer, nur muss das noch verfeinert werden, ich brauche nur die Geräte und nicht die Partitionen.

awk '$4 ~ /^(sd|nvme)/ {print "/dev/" $4}' /proc/partitions 
/dev/nvme0n1
/dev/nvme0n1p1
/dev/nvme0n1p2
/dev/nvme0n1p3
/dev/nvme0n1p4
/dev/nvme0n1p5
/dev/sda
/dev/sda1
/dev/sda2
/dev/sda3
/dev/sda4
/dev/sda5
/dev/sdb
/dev/sdb1

Mache ich da beim Quoting mit eval was falsch? Siehe davor.

Mich beschäftigt auch die Frage, ob man mit udev erkennen kann, ob eine USB-HD oder ein USB-Stick gemountet wurde. Alternativ könnte man nach "Unknown USB bridge" mit smart suchen.

Doc_Symbiosis

Avatar von Doc_Symbiosis

Anmeldungsdatum:
11. Oktober 2006

Beiträge: 4453

Wohnort: Göttingen

Hm, willst Du da nicht lieber ein entsprechendes Tool für nehmen? Z.B.:

lsblk -n -S --output NAME

Ich weiß nur nicht, ob USB-Devices dort dann auch auftauchen...

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Mir ist es egal, womit ich abfrage.

Es geht darum eine Liste von angeschlossenen Geräten, die aber nicht unbedingt gemountet sind, zu erhalten und für die smartctl eine sinnvolle Ausgabe erstellt. Diese Geräte sollen in einer Schleife abgearbeitet werden.

Noch experimentiere ich in etwa so:

for GERAET in `awk '/[ \t]sd[a-z]$/ { print "/dev/" $4; }' /proc/partitions`; do

Da gehen aber noch die nvme-Platten ab.

rklm hat ja schon einen Ansatz vorgeschlagen, passt aber noch nicht, es geht nur um die Geräte, nicht um Partitionen

awk '$4 ~ /^(sd|nvme)/ {print "/dev/" $4}' /proc/partitions 

Ich kenne mich mit awk zu wenig aus. Ein Schritt wäre IMHO die Ziffern am Ende zu entfernen. Denn Sinn bei "/dev/nvme0n1p1" mit n(amespace) habe ich noch nicht verstanden, dh vermutlich kann man ab n1 alles entfernen. Natürlich sollen durch das Entfernen Devices nicht doppelt gelistet sein. Keine Ahnung, ob auch n2 denkbar wäre.

Es sieht so aus, dass lsblk nicht mit USB umgehen kann, also ein USB-Stick wird nicht gelistet, aber auch nicht mit nvme und damit ist lsblk aus dem Rennen, oder?

Darf ich bitte auch noch an eine andere Frage bzgl. eval erinnern:

eval `udevadm info -q env -n /dev/nvme0n1 | grep 'ID_SERIAL'`
SSD: Befehl nicht gefunden.

Ich muss das nicht zwingend mit udevadm abfragen, denkbar wäre auch smartctl.

Vielleicht ist es besser mit blkid weiterzumachen

blkid | cut -f1 -d":"

Das ergibt dann:

/dev/nvme0n1p1
/dev/nvme0n1p2
/dev/nvme0n1p3
/dev/nvme0n1p4
/dev/nvme0n1p5
/dev/sda1
/dev/sda2
/dev/sda3
/dev/sda4
/dev/sda5
/dev/sdb1

bzw.

/dev/sda1
/dev/sdb1
/dev/sdc1
/dev/sdc2
/dev/sdc3
/dev/sdc4
/dev/sdc5
/dev/sdd1
/dev/sdd2
/dev/sdd3
/dev/sdd4
/dev/sdd5
/dev/sdd6
/dev/sdd7
/dev/sde1
/dev/sde2
/dev/sde3
/dev/loop0
/dev/loop1
/dev/loop2
/dev/loop3
/dev/loop4
/dev/loop5
/dev/loop6
/dev/loop7
/dev/loop8
/dev/loop9
/dev/loop10
/dev/loop11
/dev/loop12
/dev/loop13
/dev/loop14
/dev/loop15
/dev/sdf1
/dev/sdg1

sdf ist ein USB-Stick und sdg die externe USB-HD mit Luks. Es sieht danach aus, dass blkid USB-Sticks ohne Mount nicht erkennt, USB-HDs aber schon.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Ich habe mir in der Zwischenzeit was zusammengebastelt, aber "schön" finde ich es nicht. Mangels fehlerhafter HD konnte ich nur HDs testen, die fehlerfrei sind. Wenn smartctl die Ausgabe gegenüber vor Jahren geändert hat, dann wird es Probleme geben.

Gar nicht gefällt mir:

for geraet in `/usr/sbin/blkid | cut -f1 -d":" | sed -e 's/[0-9]$//' -e 's/[0-9]$//' -e 's/nvme0n1p/nvme0n1/' -e 's/\/dev\/loop/\/dev\/sda/' | sort -u`; do

Für meine PCs scheint es aber zu funktionieren.

1
2
3
#!/bin/bash

# Script entfernt, da Fehler mit Cronjob

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Ich habe da irgendein Syntax-Problem, wenn es als Cronjob läuft, als root gibt es keine Probleme.

mysmartctl=`which smartctl`

Damit erreiche ich, dass je nach PC festgestellt wird, wo smartctl nun liegt, das selber kompilierte ist ja woanders als das von der Distri

$mysmartctl -x $geraet > "$mailtext"

Und hier ist das Problem, sobald ich eine Option angebe, die mit - beginnt gibt es eine Fehlermeldung, wenn das Script als Cronjob läuft, nicht aber als root. Ich habe schon verschiedene Quotings probliert.

-x: Befehl nicht gefunden

Es sieht so aus, dass im cronjob $smartctl leer ist

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13207

glaskugel schrieb:

Ich habe da irgendein Syntax-Problem, wenn es als Cronjob läuft, als root gibt es keine Probleme.

Ist immer hilfreich in solchen Fällen die genaue Meldung zu posten.

mysmartctl=`which smartctl`

Lieber

1
mysmartctl=$(type -p smartctl)

Damit erreiche ich, dass je nach PC festgestellt wird, wo smartctl nun liegt, das selber kompilierte ist ja woanders als das von der Distri

Aber wozu? which und type benutzen ja denselben Suchpfad wie die Shell. Da muss man den Pfad nicht in einer Variablen speichern.

$mysmartctl -x $geraet > "$mailtext"

Und hier ist das Problem, sobald ich eine Option angebe, die mit - beginnt gibt es eine Fehlermeldung, wenn das Script als Cronjob läuft, nicht aber als root. Ich habe schon verschiedene Quotings probliert.

Die müsstest Du dann mal posten.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Lieber

wegen dem type oder der Variablensyntax? Mache ich natürlich.

/usr/local/bin/hd_errors_check.sh: Zeile 44: -x: Befehl nicht gefunden

hat aber an dem Problem nichts geändert

Da muss man den Pfad nicht in einer Variablen speichern.

Meine Erfahrungen vor Jahren ist, dass man bei einem Cronjob besser den vollen Pfad angibt.

Die müsstest Du dann mal posten.

Ich habe zwar im Posting davor ein paar Mal korrigiert, aber vom Zeitpunkt der Antwort solltest du es gesehen haben.

Es sieht so aus, dass im cronjob $smartctl leer ist

Da war ein Fehler von mir dabei, muss $mysmartctl sein

if test -z "$hd_serial" ; then
	mutt -s "ACHTUNG die Variable von smartctl < $mysmartctl > scheint für $geraet nicht definiert zu sein" $mail_an@localhost < "$mailtext"
fi

Ich prüfe da also testweise, ob die Seriennr. existiert, das kann nur sein, wenn der smartctl-Befehl funktioniert hat und im Titel steht der volle Pfad von $mysmartctl und das ist leer:

Subject: ACHTUNG die Variable von smartctl < > scheint für /dev/sde nicht definiert zu sein

Damit macht natürlich ein Befehl -x ein Problem.

Wenn ich keine Variable verwende und smartctl ohne Pfad verwende habe ich das gleich Problem.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13207

glaskugel schrieb:

Lieber

wegen dem type oder der Variablensyntax?

Wegen dem type und der Command Substitution.

Da muss man den Pfad nicht in einer Variablen speichern.

Meine Erfahrungen vor Jahren ist, dass man bei einem Cronjob besser den vollen Pfad angibt.

Ja, aber dann müsstest Du ja das Skript generieren, um den Namen fest einzubacken - davon sehe ich hier aber nichts.

Die müsstest Du dann mal posten.

Ich habe zwar im Posting davor ein paar Mal korrigiert, aber vom Zeitpunkt der Antwort solltest du es gesehen haben.

Ich kann im Moment nicht nachvollziehen, welche Version des Skriptes für die Fehlermeldung verantwortlich ist. Du solltest immer beides zusammen posten, sonst wird es schwer für die Helfer.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Mein Script ist mal außen vor, das Problem sieht man hier:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

#!/bin/bash

mailtext="/tmp/emailBody.txt"

/usr/sbin/smartctl -x /dev/sda > "$mailtext"
mutt -s "smartctl-cronjob-test mit Pfad ohne Programm-Variable" ab@localhost < "$mailtext"

smartctl -x /dev/sda > "$mailtext"
mutt -s "smartctl-cronjob-test ohne Pfad und ohne Variable" ab@localhost < "$mailtext"

mysmartctl=$(type -p smartctl)
$mysmartctl -x /dev/sda > "$mailtext"
mutt -s "smartctl-cronjob-test mit Programm-Variable" ab@localhost < "$mailtext"

Sobald smartctl als Variable oder nicht der volle Pfad verwendet wird, funktioniert es nicht.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3735

Ich mache es jetzt so mit dem Script für den Cronjob, mir fällt nichts besseres ein, wenn Variablen nicht funktionieren bzw. voller Pfad benötigt wird (Testscript siehe davor). Bis jetzt keine Probleme festgestellt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
        if test -f "/usr/local/sbin/smartctl" ; then
		/usr/local/sbin/smartctl -x $geraet > "$mailtext"
	else
		if test -f "/usr/sbin/smartctl" ; then
			/usr/sbin/smartctl -x $geraet > "$mailtext"
		else
			echo
			echo "smartctl nicht gefunden! - exit"
			rm /hd_errors_check_running
			echo
			exit
		fi
	fi
Antworten |