Automatisches Indizieren von DVDs unter Linux

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 1Diese 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.

Philipp Haußleiter

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert