www.dev-thomynet.de



Disketten für User und von ihnen über das Netz mounten


Inhalt


Die Aufgabe

Es geht darum, daß Usern von einem Rechner ohne Diskettenlaufwerk die Möglichkeit gegeben wird, an einem anderen Rechner im LAN das Diskettenlaufwerk so zu nutzen, als wäre es lokal vorhanden. Der User soll die Diskette in den Rechner einlegen der ein Laufwerk hat,  dann zu seinem Rechner gehen und nach dem Aufruf eines Befehls sollen ihm die Dateien auf der Diskette zur Verfüging stehen. Ist der User mit seiner Arbeit fertig, soll er nach Eingabe eines Kommandos die Diskette am entfernten Rechner wieder entnehmen können.

Die Realisierung

Das Beispielnetzwerk

Netzwerkabbildung (Vergrößert - Bitte klicken!)Der Aufbau des Netzwerkes ist in der Abbildung dargestellt.
Um eine größere Abbildung des Beispielnetzwerkes zu erhalten, bitte auf das Bild klicken.

Damit ergibt sich die folgende Konstellation:

Kombination aus mount und nfs

Um die Aufgabe zu lösen, wird eine Kombination aus lokalem Mounten (an foo) und aus Mounten per NFS (von foo an bar) benutzt. Dazu sind die folgenden schritte notwendig:
  1. Diskette lokal an foo mounten
  2. Mountpoint der Diskette von foo exportieren
  3. bar muß Mountpoint der Diskette an foo über das Netz per NFS mounten
Beim Abmounten ist die umgekehrte Reihenfolge einzuhalten. Um dieses erfolgreich durchführen zu können, benötigt man einen Kernel mit Unterstützung für vfat/msdos und nfs.

Die Einstellungen am Rechner mit Diskettenlaufwerk (foo)

Zuerst muß die Datei /etc/fstab angepaßt werden, damit der defaultmäßige Mountpoint der Diskette bekannt ist und jeder User die Diskette mounten kann.
Dazu muß die Datei /etc/fstab die folgende Zeile enthalten:
/dev/fd0        /floppy                   vfat            noauto,sync,user 0   0
Diese Zeile soll nur kurz erläutert werden: Anschließend muß man dafür sorgen, daß das Verzeichnis floppy über das Netzwerk an andere Rechner exportiert werden kann. Dazu ist der folgende Eintrag in der Datei /etc/exports notwendig:
/floppy         bar(rw)
Die hier gezeigte Einstellung ist die Einfachste der möglichen Kombinationen. Weitere Einstellungen sind der Manpage von exports zu entnehmen. Der Eintrag bedeutet das Folgende: Damit das Ganze dann wirklich funktioniert, muß noch der NFS-Daemon gestartet werden. Dafür gibt es ab Kernel 2.1. mehrere Möglichkeiten. Hier wird der einfachste Weg an Hand einer SuSE-Distribution gezeigt. Wir gehen davon aus, daß man den hier beschriebenen Diskettenservice immer anbieten will. Ist nur eine einmalige Aktion geplant, reicht es, wenn man den NFS-Daemon mit
/sbin/init.d/nfsserver start
startet. Was man jetzt in jedem Fall erstmal tun sollte. Dabei wird der Portmapper automatisch mitgestartet. Das Ganze läuft jetzt bis zum nächsten reboot, init s, init 1 oder man mit
/sbin/init.d/nfsserver stop
den Daemon wieder anhält. Um den Daemon bei jedem Bootvorgang automatisch mit zu starten, ist der folgende Eintrag in der Datei /etc/rc.config notwendig:
NFS_SERVER="yes"
Defaultmäßig steht dieser auf "no".
So das war es nun aber wirklich schon - bis auf eine Ausnahme die weiter unten bei den Skripten beschrieben wird.

Die Einstellungen am Rechner ohne Diskettenlaufwerk (bar)

Das ist die einfachere Geschichte, da nur die Datei /etc/fstab angepaßt werden muß. Es muß folgender Eintrag in /etc/fstab stehen:
foo:/floppy   /mnt/remote_floppy     nfs     noauto,sync,user,rsize=8192,wsize=8192,timeo=14,intr
Der Großteil der Einträge ist bereits beim Mounten der Floppy an foo erklärt worden. Der Rest bezieht sich auf reine NFS-Einstellungen ist in der man-Page zu mount und fstab erläutert.

Damit sind an beiden Rechnern nun alle notwendigen Vorarbeiten geleistet.

Der erste Test

Zuerst muß an foo die Diskette gemountet werden. Dazu reicht das Kommando:
mount /floppy
Danach sollte mit mount überprüft werden, ob die Diskette auch gemountet ist.
/dev/fd0 on /floppy type vfat (rw,noexec,nosuid,nodev,sync,user=paul)
Hier hat also der User "paul" die Diskette erfolgreich gemountet. Ab diesem Moment sollte nicht mehr versucht werden die Diskette aus dem Laufwerk entfernt zu werden, da sonst das System etwas widerwillig reagiert. Anschließend begibt man sich zum Rechner foo. Hier gibt man das entsprechende Mountkommando
mount /mnt/remote_floppy
ein. Sollte auch das erfolgreich sein, ergibt sich bei der Ausgabe von mount folgende Zeile:
foo:/floppy on /mnt/remote_floppy type nfs (rw,noexec,nosuid,nodev,sync,rsize=8192,wsize=8192,timeo=14,intr,addr=172.16.2.1,user=gustav)
Der User "gustav" hat die Diskette erfolgreich gemountet. Ab jetzt kann er unter /mnt/remote_floppy auf die Daten der Diskette lesend und schreibend zugreifen.

Was passiert wenn ...?

Generelles

Ein Verzeichnis - egal ob lokal gemountet oder per NFS - welches gerade in Benutzung ist kann man nicht abmounten. Unter Benutzung versteht sich hier jegliche Art von Nutzung - es reicht bereits aus, wenn sich jemand per cd in diesem Verzeichnis befindet. Damit ist aber wiederum jeweils nur die lokale Benutzung gemeint. Wenn gustav an bar folgendes macht:
cd /mnt/remote_floppy
Dann kann an foo der NFS-mount nicht aufgehoben werden - d.h.:
gustav@bar> umount /mnt/remote_floppy
ergibt:
umount: /mnt/remote_floppy: device is busy
Auf der anderen Seite kann aber der User paul an bar in dem Fall - vorausgesetzt niemand befindet sich lokal an bar in dem Directory /floppy die Diskette abmounten. Dies kann unabhängig davon geschehen, wie die Lage an bar ist:
paul@foo> umount /floppy
führt also zum Erfolg. Damit sieht aber gustav an bar nicht mehr was auf der Diskette ist! Aber paul an foo kann nun die Diskette wechseln und erneut mounten. Nach diesem Mount - und einer evtl. geringen Wartezeit - stehen gustav an bar die Daten der neuen Diskette unter /mnt/remote_floppy zur Verfügung - ohne das er weiter etwas machen muß. Das Ganze ist natürlich auch anders herum mach/denkbar:

An foo die Diskette abgemountet wird

Wenn gustav nun in sein Diskettendirectory (/mnt/remote_floppy) schreibt, werden die Daten lokal auf der Platte von foo hinterlegt. Macht gustav ein
ls /mnt/remote_floppy
bekommt er ein leeres Verzeichnis - oder Fehlermeldungen, daß die bis dahin vorhandenen Dateien nicht mehr vorhanden sind. Hat paul die alte - oder eine andere Diskette - wieder an foo /floppy gemountet, stehen nach kurzer Zeit die Daten gustav wieder zur Verfügung. Sollte gustav aber Daten auf der Platte von foo hinterlegt haben, so werden diese jetzt von der gemounteten Diskette überdeckt. Um wieder an die Daten heranzukommen, muß die Diskette also wieder (an foo) abgemountet werden.

So weit zur Verwirrung der Leserin/des Lesers ;-)

Nützliche Kommandos

Die jeweils lokalen mounts sieht man am Besten mit Um zu erfahren, wie welchen Status man so als Client (wie bar in unserem Fall) hat, benutzt man: Schwieriger wird es, wenn man herausbekommen will wer gerade was von einem per NFS gemountet hat. Hier gibt es: Aber: Die beide Möglichkeiten funktionieren nicht - oder nur in Abhängigkeit von der jeweiligen Implementation!
Es kann aber sein, daß bei der Verwendung des Kernel-NFS-Daemons die Sache anders aussieht - zumal dann auch noch der Weg über das /proc-Filesystem besteht.

Die Automatisierung

Ziele

Eigentlich ist es weder gewünscht noch will der User so viel von den Hintergründen wissen. Daher ist es das Ziel, daß der User eine einfache Funktionalität bekommt, die darin besteht die Diskette per Mausklick z.B. an bar zugänglich zu machen. Mit einem weiteren Klick soll eine gefahrlose Entnahme der Diskette an foo ebenfalls möglich sein. Vereinfacht wird ab jetzt davon ausgegangen, daß es sich an bar um den User gustav handelt, der ebenfalls einen Account auf foo (mit der gleichen USER-ID wie auf bar) hat.

Zusätzliches an foo

Wie bereits oben geschildert, muß zuerst die Diskette lokal an foo gemountet werden. Dazu dient ein lokales Script an foo, was sich in dem HOME-Directory des Users - oder aber im Pfad - befindet. Dieses Script heißt floppy_local_mount.sh.  Das Script ist nur wenige Zeilen lang und hat folgenden Inhalt:
#!/bin/sh

MOUNT_POINT=/floppy

mount $MOUNT_POINT &>/dev/null
EXIT_CODE=$?

echo $EXIT_CODE
exit $EXIT_CODE
 Nichts besonderes - bis auf die Tatsache daß der Exitcode des mount-Kommandos mit echo angezeigt wird. Genau das ist aber wiederum doch eine Besonderheit, wie wir später noch sehen werden.
Natürlich gibt es noch ein Script zum Abmounten -  floppy_local_umount.sh. Es ist ebenfalls nur wenige Zeilen lang:
#!/bin/sh

MOUNT_POINT=/floppy

mount | grep -i $MOUNT_POINT &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE == 0 ] ; then
    # floppy ist gemountet
    umount $MOUNT_POINT &>/dev/null
    EXIT_CODE=$?
fi

echo $EXIT_CODE
exit $EXIT_CODE
 Nach einem kurzen Check, ob die Diskette überhaupt gemountet ist wird versucht diese unzumounten. Auf die Besonderheit mit der Anzeige des Exitcodes kommen wir später zurück.
Und zum Schluß noch etwas, was der geneigte Admin nicht so gerne sieht - wir aber hier benötigen - ein Eintrag in $HOME/.rhosts von gustav.
# this is the file .rhosts in $HOME from gustav on foo
# ATTENTION! Set the permissions to 600 for this file!
bar gustav
bar.mynet.org gustav
Damit ermöglichen wir gustav ohne weitere Paßworteingabe das rlogin (und andere r-Kommandos) von bar auf foo.
Das war schon Alles bei foo.

Zusätzliches an bar

Für den Ort der Scripte gilt das unter "Zusätzliches an foo" gesagte. Auch auf bar gibt es jeweils ein Script zum Mounten und eins zum Abmounten. Wenn man sich die Scripte ansieht, findet man immer wieder ein Konstrukt mit read var - dieses sorgt dafür, daß das Script auf eine Usereingabe wartet. Damit wird verhindert, daß das Script einfach durchläuft und der User somit keine Möglichkeit zum Lesen der Ausgabe hätte. Das ist besonders dann ärgerlich, wenn das Script über ein Icon gestartet wird - im Fehlerfall würde das XTerm einfach zugehen, da das Script beendet ist/wird.
Zuerst das Script zum Mounten -  floppy_remote_mount.sh.
#!/bin/sh

FLOPPY_HOST=foo
REMOTE_MOUNT_CMD="~/bin/floppy_local_mount.sh"
LOCAL_MOUNT_POINT=/mnt/remote_floppy

ping -qc1 -w1 $FLOPPY_HOST &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE != 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tVermutl. ist der andere Rechner ($FLOPPY_HOST) nicht eingeschaltet!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        exit $EXIT_CODE
fi 

mount -t nfs | grep -i $LOCAL_MOUNT_POINT &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE == 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tDie Diskette ist bereits über das Netz gemountet!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        exit $EXIT_CODE
fi

R_EXIT_CODE=`rsh $FLOPPY_HOST "$REMOTE_MOUNT_CMD"`

if [ $R_EXIT_CODE != 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tVermutl. ist keine Diskette im anderen Rechner ($FLOPPY_HOST) vorhanden!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        exit $R_EXIT_CODE
fi

mount $LOCAL_MOUNT_POINT &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE != 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tKonnte Floppy nicht übers Netz von ($FLOPPY_HOST) mounten!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        exit $EXIT_CODE
else
        printf "\n\tAlles i.O.!\n\tViel Spaß beim Arbeiten!\n\n"
        printf "\tDie Diskette steht unter $LOCAL_MOUNT_POINT zur Verfügung.\n"
        printf "\n\tEs befinden sich die folgenden Daten auf der Diskette:\n\n"
        ls $LOCAL_MOUNT_POINT
fi

printf "\nBeenden mit <ENTER> ...\n"
read var
exit $EXIT_CODE
Was das Script im Einzelnen tut ist eigentlich ersichtlich:
  1. ist foo per ping erreichbar
  2. versuche an foo die Diskette lokal zu mounten (per rsh wird das Script floppy_local_mount.sh auf foo aufgerufen)
  3. wenn das funktioniert hat wird versucht das Ganze per NFS zu mounten
  4. Fertig!
Und dann noch das 2. Script - zum Abmounten: floppy_remote_umount.sh.
#!/bin/sh

FLOPPY_HOST=foo
REMOTE_UMOUNT_CMD="~/bin/floppy_local_umount.sh"
LOCAL_MOUNT_POINT=/mnt/remote_floppy

mount -t nfs | grep -i $LOCAL_MOUNT_POINT &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE != 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tDie Diskette ist nicht über das Netz gemountet!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
else
        umount $LOCAL_MOUNT_POINT &>/dev/null
        EXIT_CODE=$?
        if [ $EXIT_CODE != 0 ] ; then
                printf "\n\tFehler!"
                printf "\n\tDie Diskette konnte übers Netz nicht ungemountet werden!\n\n"
                printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        fi
fi 

ping -qc1 -w1 $FLOPPY_HOST &>/dev/null
EXIT_CODE=$?

if [ $EXIT_CODE != 0 ] ; then
        printf "\n\tFehler!"
        printf "\n\tVermutl. ist der andere Rechner ($FLOPPY_HOST) nicht eingeschaltet!\n\n"
        printf "\n\tWeiter mit <ENTER>!\n\n"
        read var
        exit $EXIT_CODE
fi 

R_EXIT_CODE=`rsh $FLOPPY_HOST "$REMOTE_UMOUNT_CMD"`

case "$R_EXIT_CODE" in
        0) printf "\n\tAlles i.O.!"
           printf "\n\tDie Diskette kann an $FLOPPY_HOST entnommen werden!\n\n"
           ;;
        1) printf "\n\tDiskette war nicht gemountet!"
           printf "\n\tDie Diskette kann an $FLOPPY_HOST entnommen werden!\n\n"
           ;;
        *) printf "\n\tFehler!\n\tUnmount der Diskette fehlgeschlagen!\n\n"
           ;;
esac

printf "\n\tWeiter mit <ENTER>!\n\n"
read var
        
exit $R_EXIT_CODE
Auch hier ist eigentlich ersichtlich was passiert:
  1. ist die Diskette per NFS gemountet
    1. wenn ja: dann abmounten
    2. wenn nicht: Meldung anzeigen und auf Usereingabe warten - aber das Script nicht beenden!
  2. ist foo per ping erreichbar
  3. wenn foo erreichbar führe dort per rsh das Kommando zum Abmounten der Diskette aus
  4. Fertig

Die Besonderheit an den Scripten

Jedes Script startet von bar eine Remoteshell an foo per rsh. Jetzt möchte man aber an bar den Exitstatus des entfernt ausgeführten Kommandos haben. Der Exitstatus von rsh-Aufrufen ist aber ebend nur der Exitstatus des rsh und nicht des entfernt ausgeführten Kommandos. Z.B.: Die rsh von bar zu foo ist i.O. - aber das mitgegebenen Kommando ist nicht erfolgreich ausführbar gewesen - dann ist der Exitcode von rsh aber trotzdem 0 (was ja auch logisch ist, denn die Remoteshell konnte ja gestartet werden). Um nun aber das Ergebnis des Kommandos rauszubekommen, gibt das entfernt ausgeführte Kommando seinen Exitstatus per Echo einfach auf STDOUT aus. Auf unserem Rechner wiederum speichern wir diese Ausgabe in einer Variablen. Und schon haben wir den Exitcode des entfernten Kommandos. Und an dieser Stelle habe ich an den Scripten etwas geschlumpert - normalerweise sollte man erst den Exitcode der rsh auswerten und dann den des entfernt auszuführenden Kommandos. Warum hab´ ich das nicht gemacht? Ganz einfach: Ich war zu faul! Aber es kommt hinzu, daß das Problem dann auch wieder nicht so einfach zu lösen ist, da man nicht einfach alles mit &> umleiten kann. Aber die Lösung des Problems steht unten.
Das oben Beschriebene kann man einfach ausprobieren, wenn man an seinem localhost dafür gesorgt hat, daß man auf sich selbst wieder mit r-Kommandos zugreifen kann (per  ~/.rhost).  Dann macht man als normaler User (nicht root) Folgendes:
paul@foo> rsh localhost "/sbin/reboot"
reboot: must be superuser.
paul@foo> echo $?
0
paul@foo> /sbin/reboot
reboot: must be superuser.
paul@foo> echo $?
1
paul@ foo>
Wie unschwer zu erkennen ist, wurde der reboot jedesmal aus dem selben Grund verweigert - aber der Exitcode ist unterschiedlich! Beim ersten Mal ist er 0, da ja die rsh erfolgreich ausgeführt werden konnte. Man bekommt also gar nichts davon mit, daß das Kommando am anderen Rechner fehlgeschlagen ist. Und wie das umgangen wird, hast Du ebend gerade erfahren.
Und zum Schluß noch der Weg, den man beschreiten muß, wenn man die Fehlerausgaben von rsh (z.B. no route to host) umleiten kann und trotzdem die Ausgabe des Exitcodes der rsh und des entfernten Kommandos erhält:
paul@foo> R_EXIT_CODE=`rsh localhost '/sbin/reboot &> /dev/null; echo $?' 2>/dev/null`
paul@foo> echo $?
0
paul@foo> echo $R_EXIT_CODE
1
paul@foo> R_EXIT_CODE=`rsh einhostwoichnichtsdarf '/sbin/reboot &> /dev/null; echo $?' 2>/dev/null`
paul@foo> echo $?
1
paul@foo> echo $R_EXIT_CODE

paul@foo>
Wie man sieht bekommt man jetzt den Exitcode für rsh mit dem üblichen $? heraus und für das entfernte Kommando in $R_EXIT_CODE. Die Variable R_EXIT_CODE braucht man aber schon garnicht mehr auswerten, wenn $? einen Wert <> 0 hat (zumal sie so oder so dann leer ist!).

Download der Scripte

Wie es sich für eine ordentliche Webseite gehört, sind die Scripte natürlich alle per Download als tgz zu erhalten. Nach dem Auspacken mit
tar xvzf nfs_diskette.tgz
stehen sie in einem separatem Directory namens nfs_diskette zur Verfügung.

So das war Alles! Viel Spaß!



Questions, ideas, comments please mail me at: linux@dev-thomynet.de
Fragen, Ideen, Kommentare bitte per Email an:  linux@dev-thomynet.de