Automatisches Indizieren von DVDs unter Linux
Vor nun fast knapp zwei Jahren kam ein Bekannter auf mich zu, der eine größere Menge an DVDs indizieren wollte. Neben dem eigentlich Einlesen der DVDs in eine ISO Datei, sollte ebenfalls ein kurzes Inhaltsverzeichnis erstellt werden.
Der ganze Ablauf sollte, wenn möglich, voll automatisch ablaufen. Als einziger notwendiger Schritt sollte das Wechseln des Mediums notwendig sein, nachdem es vollständig eingelesen worden ist.
Nach einigem Suchen bin ich auf die Linux udev
rules aufmerksam geworden, die einem erlauben, auf gewisse Events von Devices (also auch DVD Laufwerken) zu reagieren. Kurz darauf enstand der Plan, das ganze als VM aufzusetzen, die sowohl lokale ISO Files, als auch (per paththrough vom physikalischen Laufwerk) DVDs einlesen kann.
Ich habe gerade durch Zufall die Dateien wiedergefunden und dachte, ich schreibe einen kurzen Post drüber.
Die Zutaten sind:
- VirtualBox – dort soll die VM laufen.
- Vagrant zum Aufsetzen der VM.
- Ansible um die notwendigen Script zu installieren.
- Ein paar weitere Scripte, die auf den jeweiligen Events vom Device reagieren.
- Ein Script, welches das Einbinden, Indizieren, Kopieren und anschließende Auswerfen des Mediums durchführt.
Tooling
Sowohl Vagrant und VirtualBox sollten sich ohne Problem aufsetzen lassen.
Ansible sollte man am besten per PIP installieren. Eine aktuelle Python Installation (>= 2.7) vorrausgesetzt, reicht hierzu ein pip install ansible
.
udev Scripte
Wie bereits schon erwähnt, soll auf die jeweiligen Events (z.B. DVD wurde eingelegt) des jeweiligen Devices reagiert werden. Hierzu benötigen wir zwei Scripte, welche in einer bestimmten Reihenfolge aktiviert werden müssen (weshalb sie 70-*
und 80–*
benannt werden)
cat 70-persistent-cd.rules
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:1:0", SYMLINK+="cdrom", ENV{GENERATED}="1"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:1:0", SYMLINK+="dvd", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-0:0:0:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:0:0", SYMLINK+="cdrom1", ENV{GENERATED}="1"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:0:0", SYMLINK+="dvd1", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-0:0:0:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:0:0", SYMLINK+="cdrom2", ENV{GENERATED}="1"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:0:0", SYMLINK+="dvd2", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-0:0:1:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:1:0", SYMLINK+="cdrom3", ENV{GENERATED}="1"
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:1:0", SYMLINK+="dvd3", ENV{GENERATED}="1"
Diese Script ist recht selbsterklärend. Es legt liegt in /etc/udev/rules.d/70-persistent-cd.rules
und dient dazu, dass für jedes der vier (virtuellen) Devices ein Link erstellt wird.
Unter /dev/
finden sich dann folgende Einträge:
lrwxrwxrwx 1 root root 3 Mar 11 14:22 cdrom -> sr1
lrwxrwxrwx 1 root root 3 Mar 11 14:41 cdrom1 -> sr0
lrwxrwxrwx 1 root root 3 Mar 11 14:41 cdrom2 -> sr0
lrwxrwxrwx 1 root root 3 Mar 11 14:23 cdrom3 -> sr2
…
lrwxrwxrwx 1 root root 3 Mar 11 14:22 dvd -> sr1
lrwxrwxrwx 1 root root 3 Mar 11 14:41 dvd1 -> sr0
lrwxrwxrwx 1 root root 3 Mar 11 14:41 dvd2 -> sr0
lrwxrwxrwx 1 root root 3 Mar 11 14:23 dvd3 -> sr2
Kommen wir nun zum zweiten Script.
cat 80-autodvd.rules
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-0:0:0:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:0:0", RUN+="/usr/local/bin/autodvd", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-1:0:1:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-1:0:1:0", RUN+="/usr/local/bin/autodvd", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-0:0:1:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-0:0:1:0", RUN+="/usr/local/bin/autodvd", ENV{GENERATED}="1"
# VBOX_CD-ROM (pci-0000:00:01.1-scsi-1:0:0:0)
SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:01.1-scsi-1:0:0:0", RUN+="/usr/local/bin/autodvd", ENV{GENERATED}="1"
Dieses Script sorgt dafür das alle Events der jeweiligen Devices das Script /usr/local/bin/autodvd
triggern. Wichtig zu wissen ist, dass hierdurch dem Script auch ein paar Umgebungsvariablen bekannt sind.
Diese stehen dann unter anderem beim Aufruf von env
zur Verfügung:
ID_MODEL=VBOX_CD-ROM
ID_PATH_TAG=pci-0000_00_01_1-scsi-0_0_0_0
ID_CDROM_MEDIA_TRACK_COUNT=1
ID_CDROM_MEDIA=1
ID_ATA=1
ID_MODEL_ENC=VBOX\x20CD-ROM\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_CDROM_DVD=1
ID_REVISION=1.0
DEVTYPE=disk
ID_FS_LABEL=CentOS_6.9_Final
ID_CDROM_MRW_W=1
ID_BUS=ata
SUBSYSTEM=block
ID_FS_LABEL_ENC=CentOS_6.9_Final
ID_SERIAL=VBOX_CD-ROM_VB0-01f003f6
DEVPATH=/devices/pci0000:00/0000:00:01.1/host1/target1:0:0/1:0:0:0/block/sr0
TAGS=:udev-acl:
MINOR=0
GENERATED=1
ACTION=change
ID_CDROM=1
PWD=/
UDEV_LOG=3
ID_FS_TYPE=iso9660
USEC_INITIALIZED=759847
MAJOR=11
DEVLINKS=/dev/cdrom1 /dev/cdrom2 /dev/disk/by-id/ata-VBOX_CD-ROM_VB0-01f003f6 /dev/disk/by-label/CentOS_6.9_Final /dev/disk/by-path/pci-0000:00:01.1-scsi-0:0:0:0 /dev/dvd1 /dev/dvd2
ID_CDROM_MEDIA_CD=1
DEVNAME=/dev/sr0
SHLVL=1
ID_FS_USAGE=filesystem
ID_CDROM_MEDIA_TRACK_COUNT_DATA=1
ID_TYPE=cd
ID_PART_TABLE_TYPE=dos
DISK_MEDIA_CHANGE=1
ID_SERIAL_SHORT=VB0-01f003f6
ID_CDROM_MRW=1
SEQNUM=1426
ID_PATH=pci-0000:00:01.1-scsi-0:0:0:0
ID_CDROM_MEDIA_SESSION_COUNT=1
Diese können wir im nachfolgendem Script verwenden.
Das Indizierungsscript
Das Script zum Indizieren ist das eigentlich Herzstück des gestamten Setups:
cat autodvd
#!/bin/bash
{
env
if [ $ID_CDROM_MEDIA -eq 1 ]; then
mkdir -p /vagrant/dvdindex/dev
dev=`echo $DEVLINKS | cut -d ' ' -f 1`
devlock=/vagrant/dvdindex$DEVNAME.lock
if [ ! -f $devlock ]; then
touch $devlock
mkdir -p /var/run/usbmount$DEVNAME
mount -t $ID_FS_TYPE -o ro $dev /var/run/usbmount$DEVNAME
sum=`find /var/run/usbmount$DEVNAME -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d ' ' -f 1`
logfile=/vagrant/dvdindex/$sum.nfo
isofile=/vagrant/dvdindex/$sum.iso
lockfile=/vagrant/dvdindex/$sum.lock
touch $lockfile
echo "label $ID_FS_LABEL_ENC" > $logfile
echo "" >> $logfile
echo "folder $sum" >> $logfile
echo "" >> $logfile
ls -alR /var/run/usbmount$DEVNAME >> $logfile
umount -l /var/run/usbmount$DEVNAME
dd if=$dev of=$isofile bs=4194304
sum=`sha1sum $isofile | cut -d ' ' -f 1`
echo "iso $sum" >> $logfile
rm $lockfile
rm $devlock
eject
fi
else
umount -l /var/run/usbmount$DEVNAME
rm -rf /var/run/usbmount$DEVNAME
fi
} &>> "/var/log/autodvd.log" &
Ziemlich viel Inhalt. Aber fangen wir der Reihe nach an:
- Der ganze Aufruf wird in einem Block
{…}
ausgeführt, dass Ergebnis (inkl. aller Fehler) wird dem Log File/var/log/autodvd.log
hinzugefügt. - Wir erstellen das Verzeichnis
/vagrant/dvdindex
, in dem alle ISO und Index Files gespeichert werden. Da/vagrant
eine gemountete Netzfreigabe vom VM Host sind, landen alle Dateien demnach auf dem VM Host System. - Damit wir exklusiven Zugriff auf das Device bekommen erstellen wir ein einfaches LOCK:
devlock=/vagrant/dvdindex$DEVNAME.lock if [ ! -f $devlock ]; then touch $devlock … rm $devlock fi
- Danach wird das Laufwerk gemountet und eine Checksum über den Inhalt erstellt:
sum=
find /var/run/usbmount$DEVNAME -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d ' ' -f 1
Diese Checksum wird als Dateiname für das ISO File, als auch für die Index Datei erstellt. - Wir erstellen ein Listing auf der obersten Ebene – natürlich lässt sich in diese Datei auch das komplette Medium indizieren.
- Dann wird das Medium unmountet, per
dd
kopiert und ein checksum file für das ISO Image erstellt. - Zuletzt wird per
eject
das Medium ausgeworfen:
Das Log enthält (neben obigem env Listing) folgende Einträge:
…
_=/usr/bin/env
102+0 records in
102+0 records out
427819008 bytes (428 MB) copied, 5.09119 s, 84.0 MB/s
Im Dateisystem befinden sich nach erfolgreichem Import dann folgende Dateien:
ls -al /vagrant/dvdindex
total 417824
drwxr-xr-x 1 vagrant vagrant 170 Mar 11 14:42 .
drwxr-xr-x 1 vagrant vagrant 374 Mar 11 14:50 ..
-rw-r--r-- 1 vagrant vagrant 427819008 Mar 11 14:41 3766d2f5403b3c17f5765025233736021ed12dc9.iso
-rw-r--r-- 1 vagrant vagrant 24960 Mar 11 14:42 3766d2f5403b3c17f5765025233736021ed12dc9.nfo
Im Prozess sieht der gesamte Ablauf vom Einlegen eines 400Mb Mediums bis zum Auswerfen so aus:
Alle Sourcen (Vagrant File + Ansible Script), findet ihr auf Github.