Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
Shell-Programmierung von Jürgen Wolf
Einführung, Praxis, Referenz
Buch: Shell-Programmierung

Shell-Programmierung
782 S., mit CD, 44,90 Euro
Rheinwerk Computing
ISBN 3-89842-683-1
gp Kapitel 15 Die Praxis
  gp 15.1 Alltägliche Lösungen
    gp 15.1.1 Auf alphabetische und numerische Zeichen prüfen
    gp 15.1.2 Auf Integer überprüfen
    gp 15.1.3 echo mit oder ohne -n
  gp 15.2 Datei-Utilities
    gp 15.2.1 Leerzeichen im Dateinamen ersetzen
    gp 15.2.2 Dateiendungen verändern
    gp 15.2.3 Nach veränderten Dateien in zwei Verzeichnissen vergleichen
  gp 15.3 Systemadministration
    gp 15.3.1 Benutzerverwaltung
    gp 15.3.2 Systemüberwachung
  gp 15.4 Backup-Strategien
    gp 15.4.1 Warum ein Backup?
    gp 15.4.2 Sicherungsmedien
    gp 15.4.3 Varianten der Sicherungen
    gp 15.4.4 Bestimmte Bereiche sichern
    gp 15.4.5 Backup über ssh mit tar
    gp 15.4.6 Daten mit rsync synchronisieren
    gp 15.4.7 Dateien und Verzeichnisse per E-Mail versenden
    gp 15.4.8 Startup-Scripts
  gp 15.5 World Wide Web und HTML
    gp 15.5.1 Analysieren von access_log (Apache)
    gp 15.5.2 Analysieren von error_log (Apache)
  gp 15.6 CGI (Common Gateway Interface)
    gp 15.6.1 CGI-Scripts ausführen
    gp 15.6.2 CGI-Environment ausgeben
    gp 15.6.3 Einfache Ausgabe als Text
    gp 15.6.4 Ausgabe als HTML formatieren
    gp 15.6.5 Systeminformationen ausgeben
    gp 15.6.6 Kontaktformular
    gp 15.6.7 Noch ein Tipp


Rheinwerk Computing

15.3 Systemadministratiodowntop

Die Systemadministration dürfte wohl eines der Hauptgründe sein, weshalb Sie sich entschieden haben, die Shellscript-Programmierung zu erlernen. Zur Systemadministration gehören u. a. zentrale Themen wie die Benutzer- und Prozessverwaltung, Systemüberwachung, Backup-Strategien und das Auswerten bzw. Analysieren von Log-Dateien. Zu jedem dieser Themen werden Sie ein Beispiel für die Praxis kennen lernen und, falls das Thema recht speziell ist, auch eine Einführung.


Rheinwerk Computing

15.3.1 Benutzerverwaltung  downtop

Plattenplatzbenutzung einzelner Benutzer auf dem Rechner

Wenn Sie einen Rechner mit vielen Benutzern verwalten müssen, so sollte man dem einzelnen Benutzer auch eine gewisse Grenze setzen, was den Plattenverbrauch betrifft. Die einfachste Möglichkeit ist es, die Heimverzeichnisse der einzelnen User zu überprüfen. Natürlich schließt dies nicht nur das /home-Verzeichnis ein (auch wenn es im Beispiel so verwendet wird). Am einfachsten sucht man in entsprechenden Verzeichnissen nach Dateien, die einen bestimmten Benutzer als Eigentümer haben, und addiert die Größe einer jeden gefundenen Datei.

Damit auch alle Benutzer erfasst werden, deren User-ID größer als 99 ist, werden sie einfach alle aus /etc/passwd extrahiert. Die Werte zwischen 1 und 99 sind gewöhnlich den System-Daemons bzw. dem root vorbehalten. Ein guter Grund übrigens, dass Sie, wenn Sie einen neuen User anlegen, die UID immer über 100 wählen.


Hinweis   Die meisten Systeme nutzen inzwischen auch UIDs für die User, die größer als 1000 sind. Diese Angabe kann von System zu System variieren. Viele Systeme verwenden als »uid_start« auch den Wert 1000. Es könnte also sein, dass der Wert 100 – wie hier verwendet – zu niedrig ist. Allerdings sollte es für Sie wohl kein Problem darstellen, diesen Wert im Script zu ändern.


Das folgende Shellscript analysiert also den Plattenplatzverbrauch einzelner Benutzer (im Beispiel beschränken wir uns auf das /home-Verzeichnis). Gewöhnlich benötigt man root-Rechte, um dieses Script auszuführen. Ohne diese Rechte können Sie nur den eigenen Account überprüfen. Hat ein Benutzer den Plattenplatz, der mit »maxUsage« MB begrenzt ist, überschritten, wird eine Mail mit entsprechender Nachricht gesendet. Im Beispiel wird das Kommando mail verwendet und kann jederzeit auch durch sendmail ausgetauscht werden (abhängig vom System).


Hinweis   An dieser Stelle sollte auch auf das Quota-System hingewiesen werden. Das Quota-System wird verwendet, wenn Benutzer/Gruppen, die an einem System arbeiten und dort ein eigenes Verzeichnis besitzen, zu viele Daten in diesem Verzeichnis speichern/sammeln.

Mit Quota können Sie als Systemadministrator den verfügbaren Plattenplatz für jede/n Benutzer/Gruppe einschränken. Hierbei existieren zwei Grenzen, das Softlimit und das Hardlimit. Beim Softlimit darf der Benutzer die Grenze für eine kurze Zeit überschreiten. Dieser Zeitraum wird durch die »Grace Period« festgelegt. Beim Hardlimit darf der Benutzer (oder die Gruppe) diese Grenze keinesfalls überschreiten. Es gibt also keine Möglichkeit, dieses Limit zu umgehen. Das Quota-System liegt gewöhnlich jeder Distribution bei. Mehr dazu finden Sie auch in einem Mini-Howto in deutscher Sprache.


#!/bin/sh
# DiskQuota = Das Tool analysiert den Plattenplatzverbrauch
# einzelner Benutzer
# Limit der Speicherplatzbenutzung pro User in MB
maxUsage=100
# temporäre Log-Datei -> besser wäre in /tmp
logfile="loghogs.$$"
# Verzeichnis(se), das/die pro User erfasst werden sollen
dirs="/home"
# uid_start: Alle User-IDs unter 100 sind gewöhnlich dem root
# und anderen Diensten vorbehalten. Echte User beginnen
# gewöhnlich ab 100 oder manchmal gar ab 1000
uid_start=99
# Beim ordentlichen Beenden logfile wieder löschen
trap "/bin/rm -f $logfile" EXIT
for name in `cut -d: -f1,3 /etc/passwd | \
             awk -F: '$2 > $uid_start { print $1 }'`
do
  echo -n "$name "
  find $dirs -user $name -xdev -type f -ls | \
      awk '{ sum += $7 } END { print sum / (1024*1024) }'
done | awk "\$2 > $maxUsage { print \$0 }" > $logfile
# Wenn vorhanden, haben wir einen "Übertreter"
# gefunden, ansonsten ...
if [ ! -s $logfile ]
then
  echo "Kein User hat das Limit ${maxUsage}MB überzogen"
  exit 0
fi
while read user diskspace
do
   cat <<MARKE | mail -s "Achtung Limit überzogen" $user
Hallo $user,
Soeben `date "+%Y-%h-%d %H:%M:%S "` wurde festgestellt, dass Sie
Ihr Limit von ${maxUsage}MB überzogen haben. Derzeit beträgt Ihr
verwendeter Speicher ${diskspace}MB. Bitte beheben Sie den
Umstand sobald wie möglich. Vielen Dank für das Verständnis.
MARKE
   echo "User $user hat den Account überzogen\
         (Ist:${diskspace}MB Soll:${maxUsage}MB)"
done < $logfile

Das Script bei der Ausführung:

# ./DiskQuota
User tot hat den Account überzogen (Ist:543,72MB Soll:100MB)
---- Inzwischen beim User tot ----
tot@host > mail
 ... ... ...
 N 14 tot@linux.site  Mon May  2 15:14  22/786  Limit überzogen
 ... ... ...
? 14
Message 14:
From: you@host.site (J.Wolf)
Hallo tot,
Soeben 2005-Mai-02 15:14:47 wurde festgestellt, dass Sie Ihr
Limit von 100MB überzogen haben. Derzeit beträgt Ihr verwendeter
Speicher 543,72MB. Bitte beheben Sie den Umstand sobald wie
möglich.
Vielen Dank für das Verständnis.

Hinweis   Bitte beachten Sie, wenn Sie weitere Verzeichnisse angeben, in denen nach Dateien eines bestimmten Benutzers gesucht werden soll (oder gar das Wurzelverzeichnis), dass dies sehr viel Zeit in Anspruch nehmen kann.


Prozesse nach User sortiert mit Instanzen ausgeben

Einen weiteren Komfort, den man als Systemadministrator gern nutzen würde, wäre eine Ausgabe der Prozessüberwachung einzelner Benutzer, sortiert nach diesen. Das folgende, gut kommentierte Script sortiert die Prozesse nach Benutzern und gar nach deren einzelnen Instanzen, wenn der Benutzer von einer Instanz mehrere ausführt. Führt der Benutzer z. B. dreimal bash als Shell aus, finden Sie in der Zusammenfassung 3 Instanz(en) von /bin/bash, statt jede Instanz einzeln aufzulisten.

#!/bin/sh
# Name: psusers
# Voraussetzung, dass dieses Script funktioniert, ist, dass die
# Ausgabe von ps -ef auf Ihrem Rechner folgendes Format hat:
#
# you@host > ps -ef
# UID        PID  PPID  C STIME TTY          TIME CMD
# root         1     0  0 00:38 ?        00:00:04 init [5]
# root         2     1  0 00:38 ?        00:00:00 [ksoftirqd/0]
#
# Wenn die Ausgabe bei Ihnen etwas anders aussieht, müssen Sie
# das Script entsprechend anpassen (erste Zuweisung von USERNAME
# und PROGNAME)
# Variablen deklarieren
#
COUNTER=0; CHECKER=0; UCOUNT=1
PSPROG='/bin/ps -ef'
SORTPROG='/bin/sort +0 –1 +7 –8'
TMPFILE=/tmp/proclist_$$
# Beim ordentlichen Beenden TMPFILE wieder löschen
trap "/bin/rm -f $TMPFILE" EXIT
# Die aktuelle Prozessliste in TMPFILE speichern
#
$PSPROG | $SORTPROG > $TMPFILE
# Daten in TMPFILE verarbeiten
#
grep -v 'UID[  ]*PID' $TMPFILE | while read LINE
do
   # Zeilen in einzelne Felder aufbrechen
   set -- $LINE
   # Einzelne Felder der Ausgabe von ps -ef lauten:
   # UID PID PPID C STIME TTY TIME CMD
   # Anzahl der Parameter einer Zeile größer als 0 ...
   if [ $# -gt 0 ]
   then
      # Erstes Feld (UID) einer Zeile der Variablen
      # USERNAME zuordnen
      USERNAME=$1
      # Die ersten sieben Felder einer Zeile entfernen
      shift 7
      # Kommandonamen (CMD) der Variablen PROGNAME zuordnen
      PROGNAME=$*
   fi
   # Testet die Kopfzeile
   #
   if [ "$USERNAME" = "UID" ]
   then
      continue # nächsten Wert in der Schleife holen ...
   fi
   # Überprüfen, ob es sich um die erste Zeile von Daten handelt
   #
   if [ "$CHECKER" = "0" ]
   then
      CHECKER=1
      UCOUNT=0
      LASTUSERNAME="$USERNAME"
      # Programmname für die Ausgabe formatieren
      # auf 40 Zeichen beschränken ....
      #
      LASTPROGNAME=`echo $PROGNAME | \
                    awk '{print substr($0, 0, 40)}'`
      COUNTER=1; LASTCOUNT=1
      echo ""
      echo "$USERNAME führt aus:....."
      continue # nächsten Wert von USERNAME holen
   fi
   # Logische Überprüfung durchführen
   #
   if [ $CHECKER -gt 0 -a "$USERNAME" = "$LASTUSERNAME" ]
   then
      if [ "$PROGNAME" = "$LASTPROGNAME" ]
         then
            COUNTER=`expr $COUNTER + 1`
         else
         # Ausgabe auf dem Bildschirm ...
         if [ $LASTCOUNT -gt 1 ]
         then
            echo "     $LASTCOUNT Instanz(en) von ->"\
                 " $LASTPROGNAME"
         else
            echo "         $LASTCOUNT Instanz(en) von ->"\
                 " $LASTPROGNAME"
         fi
         COUNTER=1
      fi
      # Programmname für die Ausgabe formatieren
      # auf 40 Zeichen beschränken ....
      #
      LASTPROGNAME=`echo $PROGNAME | \
                    awk '{print substr($0, 0, 40)}'`
      LASTCOUNT=$COUNTER
   elif [ $CHECKER -gt 0 -a "$USERNAME" != "$LASTUSERNAME" ]
   then
      if [ $LASTCOUNT -gt 1 ]
      then
         echo "     $LASTCOUNT Instanz(en) von >> $LASTPROGNAME"
      else
         echo "         $LASTCOUNT Instanz(en) von >>"\
              " $LASTPROGNAME"
      fi
      echo
      echo "$USERNAME führt aus:....."
      LASTUSERNAME="$USERNAME"
      # Programmname für die Ausgabe formatieren
      # auf 40 Zeichen beschränken ....
      #
      LASTPROGNAME=`echo $PROGNAME | \
                    awk '{print substr($0, 0, 40)}'`
      COUNTER=1
      LASTCOUNT=$COUNTER
   fi
done
# DISPLAY THE FINAL USER INSTANCE DETAILS
#
if [ $COUNTER -eq 1 -a $LASTCOUNT -ge 1 ]
then
   if [ $LASTCOUNT -gt 1 ]
   then
      echo "     $LASTCOUNT Instanz(en) von >> $LASTPROGNAME"
   else
      echo "         $LASTCOUNT Instanz(en) von >> $LASTPROGNAME"
   fi
fi
echo "------"
echo "Fertig"
echo "------"

Das Script bei der Ausführung:

you@host > ./psusers
bin führt aus:.....
         1 Instanz(en) von >> /sbin/portmap
lp führt aus:.....
         1 Instanz(en) von >> /usr/sbin/cupsd
postfix führt aus:.....
         1 Instanz(en) von -> pickup -l -t fifo -u
         1 Instanz(en) von >> qmgr -l -t fifo -u
root führt aus:.....
         1 Instanz(en) von -> -:0
         1 Instanz(en) von -> [aio/0]
         1 Instanz(en) von -> /bin/bash /sbin/hotplug pci
         1 Instanz(en) von -> /bin/bash /etc/hotplug/pci.agent
...
you führt aus:.....
     3 Instanz(en) von -> /bin/bash
         1 Instanz(en) von -> /bin/ps -ef
         1 Instanz(en) von -> /bin/sh /opt/kde3/bin/startkde
         1 Instanz(en) von -> /bin/sh ./testscript
         1 Instanz(en) von -> gpg-agent --daemon --no-detach
         1 Instanz(en) von -> kaffeine -session 117f000002000111
         1 Instanz(en) von -> kamix
         1 Instanz(en) von -> kdeinit: Running...
...

Prozesse bestimmter Benutzer beenden

Häufig kommt es vor, dass bei einem Benutzer einige Prozesse »Amok« laufen bzw. man einen Prozess einfach beenden will (warum auch immer). Mit dem folgenden Script können Sie (als root) die Prozesse eines Benutzers mithilfe einer interaktiven Abfrage beenden. Nach der Eingabe des Benutzers werden alle laufenden Prozesse in einem Array gespeichert. Anschließend wird das komplette Array durchlaufen und nachgefragt, ob Sie den Prozess beenden wollen oder nicht. Zuerst wird immer versucht, den Prozess normal mit SIGTERM zu beenden. Gelingt dies nicht mehr, muss SIGKILL herhalten.


Hinweis   Da dieses Script Arrays verwendet, ist es nur in der bash bzw. Korn-Shell ausführbar. Muss das Script unbedingt auch in einer Bourne-Shell laufen, so könnte man die einzelnen Prozesse statt in ein Array auch in eine Datei (zeilenweise) schreiben und aus dieser zeilenweise wieder lesen.


#!/bin/ksh
# Name: killuser
# Wegen der Benutzung von Arrays "nur" für bash und Korn-Shell
# nicht aber für Bourne-Shell (sh) geeignet
while true
do
   # Bildschirm löschen
   clear
   echo "Diese Script erlaubt Ihnen bestimmte Benutzer Prozesse"
   echo "zu beenden."
   echo
   echo "Name des Benutzers eingeben (mit q beenden) : " | \
       tr -d '\n'
   read unam
   # Wurde kein Benutzer angegeben
   unam=${unam:-null_value}
   export unam
   case $unam in
      null_value)
         echo "Bitte einen Namen eingeben!" ;;
      [Qq])
         exit 0 ;;
      root)
         echo "Benutzer 'root' ist nicht erlaubt!" ;;
      *)
         echo "Überprüfe $unam ..."
         typeset -i x=0
         typeset -i n=0
         if $(ps -ef | grep "^[  ]*$unam" > /dev/null)
         then
         for a in $(ps -ef |
            awk -v unam="$unam" '$1 ~ unam { print $2, $8}'| \
            sort -nr +1 –2 )
         do
         if [ $n -eq 0 ]
         then
            x=`expr $x + 1`
            var[$x]=$a
            n=1
         elif [ $n -eq 1 ]
         then
            var2[$x]=$a
            n=0
         fi
        done
        if [ $x -eq 0 ]
        then
           echo "Hier gibt es keine Prozesse zum Beenden!"
        else
           typeset -i y=1
           clear
           while [ $y -le $x ]
           do
              echo "Prozess beenden PID: ${var[$y]} -> CMD: "\
                   " ${var2[$y]}  (J/N) : " | tr -d '\n'
              read resp
              case "$resp" in
                 [Jj]*)
                    echo "Prozess wird beendet ..."
                    # Zuerst versuchen, "normal" zu beenden
                    echo "Versuche, normal zu beenden " \
                         " (15=SIGTERM)"
                    kill –15 ${var[$y]} 2>/dev/null
                    # Überprüfen, ob es geklappt hat 
                    # -> ansonsten
                    # mit dem Hammmer killen
                    if ps -p ${var[$y]} >/dev/null 2>&1
                    then
                       echo "Versuche, 'brutal' zu beenden"\
                            " (9=SIGKILL)"
                       kill –9 ${var[$y]} 2>/dev/null
                    fi
                    ;;
                 *)
                    echo "Prozess wird weiter ausgeführt"\
                         " ( ${var2[y]} )"
                    ;;
              esac
              y=`expr $y + 1`
              echo
           done
           fi
        fi
        ;;
     esac
sleep 2
done

Das Script bei der Ausführung:

# ./killuser
Dieses Script erlaubt Ihnen, bestimmte Benutzer-Prozesse
zu beenden.
Name des Benutzers eingeben (mit q beenden) : john
Prozess beenden PID: 4388 -> CMD: sleep  (J/N) : J
Prozess wird beendet ...
Versuche, normal zu beenden (15=SIGTERM)
Prozess beenden PID: 4259 -> CMD: holdonrunning  (J/N) : N
Prozess wird weiter ausgeführt ( holdonrunning )
Prozess beenden PID: 4203 -> CMD: -csh  (J/N) : ...

Überwachung, wer sich im System einloggt

Einen Überblick, wer sich alles im System einloggt und eingeloggt hat, können Sie sich wie bekannt mit dem Kommando last ermitteln lassen. Gern würde man sich den Aufruf von last ersparen, um so immer aktuell neu eingeloggte Benutzer im System zu ermitteln. Dies kann man dann z. B. verwenden, um dem Benutzer eine Nachricht zukommen zu lassen, oder eben zu Überwachungszwecken.

Das Überwachen, ob sich ein neuer Benutzer im System eingeloggt hat, lässt sich auch in einem Shellscript mit last relativ leicht ermitteln. Hierzu müssen Sie eigentlich nur die Anzahl von Zeilen von last zählen und in einer bestimmten Zeit wieder miteinander vergleichen, ob eine neue Zeile hinzugekommen ist. Die Differenz beider Werte lässt sich dann mit last und einer Pipe nach head ausgeben. Dabei werden immer nur die neu hinzugekommenen letzten Zeilen mit head ausgegeben. Im Beispiel wird die Differenz beider last-Aufrufe alle 30 Sekunden ermittelt. Dieser Wert lässt sich natürlich beliebig hoch- bzw. runtersetzen. Außerdem wird im Beispiel nur eine Ausgabe auf das aktuelle Terminal (/dev/tty) vorgenommen. Hierzu würde sich beispielsweise das Kommando write oder wall sehr gut eignen. Als root könnten Sie somit jederzeit einem User eine Nachricht zukommen lassen, wenn dieser sich einloggt.

#! /bin/sh
# Name: loguser
# Das Script überprüft, ob sich jemand im System eingeloggt hat
# Pseudonym für das aktuelle Terminal
outdev=/dev/tty
fcount=0; newcount=0; timer=30; displaylines=0
# Die Anzahl Zeilend des last-Kommandos zählen
fcount=`last | wc -l`
while true
do
   # Erneut die Anzahl Zeilen des last-Kommandos zählen ...
   newcount=`last | wc -l`
   # ... und vergleichen, ob neue hinzugekommen sind
   if [ $newcount -gt $fcount ]
   then
      # Wie viele neue Zeilen sind hinzugekommen ...
      displaylines=`expr $newcount – $fcount`
      # Entsprechend neue Zeilen ausgeben auf outdev
      # Hier würde sich auch eine Datei oder das Kommando
      # write sehr gut eignen, damit die Kommandozeile
      # nicht blockiert wird ...
      last | head -$displaylines > $outdev
      # neuen Wert an fcount zuweisen
      fcount=$newcount
      # timer Sekunden warten, bis zur nächsten Überprüfung
      sleep $timer
   fi
done

Das Script bei der Ausführung:

you@host > ./loguser
john     tty2                  Wed May  4 23:46   still logged in
root     tty4                  Wed May  4 23:46   still logged in
tot      tty2                  Wed May  4 23:47   still logged in
you      tty5                  Wed May  4 23:49   still logged in

Benutzer komfortabel anlegen, löschen, sperren und wieder aufheben

Eine ziemlich wichtige und regelmäßige Aufgabe, die Ihnen als Systemadministrator zufällt, dürfte das Anlegen und Löschen neuer Benutzerkonten sein.


Hinweis   Ich habe mir lange überlegt, diesen Part der User-Verwaltung wieder zu streichen. Zum einen zeigt das Script hervorragend, wie man sich eine eigene Userverwaltung bauen kann und was dabei alles so zu beachten ist. Allerdings bietet mittlerweile jede Distribution mindestens eine solche Userverwaltung (und das meistens erheblich komfortabler) an. Das Script sollte eigentlich nur unter Linux ordentlich laufen, aber selbst hier könnten Sie noch Probleme mit der Portabilität bekommen, weil auch die einzelnen Distributionen hier ihr eigenes Süppchen kochen, mal heißt es hier useradd dann wieder adduser, die Optionen von passwd bspw. sind teilweise auch unterschiedlich.


Um einen neuen Benutzer-Account anzulegen, wird ein neuer Eintrag in der Datei /etc/passwd angelegt. Dieser Eintrag beinhaltet gewöhnlich einen Benutzernamen aus acht Zeichen, eine User-ID (UID), eine Gruppen-ID (GID), ein Heimverzeichnis (/home) und eine Login-Shell. Die meisten Linux-/UNIX-Systeme speichern dann noch ein verschlüsseltes Password in /etc/shadow – was natürlich bedeutet, dass Sie auch hier einen Eintrag (mit passwd) vornehmen müssen. Beim Anlegen eines neuen Benutzers können Sie entweder zum Teil vorgegebene Standardwerte verwenden oder eben eigene Einträge anlegen. Es ist außerdem möglich, die Dauer der Gültigkeit des Accounts festzulegen.

Im Beispiel ist es nicht möglich, auch noch eine neue Gruppe anzulegen, sprich, Sie können nur einen neuen Benutzer anlegen und diesem eine bereits vorhandene Gruppe (setzt einen vorhandenen Eintrag in /etc/group voraus) zuweisen. Auf das Anlegen einer neuen Gruppe wurde aus Übersichtlichkeitsgründen verzichtet, da sich das Script sonst unnötig in die Länge ziehen würde. Allerdings sollte es Ihnen mit der Vorlage dieses Scripts nicht schwer fallen, ein recht ähnliches Script für das Anlegen einer Gruppe zu schreiben.

Nebenbei ist es auch realisierbar, einen Benutzer mit passwd zu sperren und die Sperre wieder aufzuheben. Beim Löschen eines Benutzers werden zuvor noch all seine Daten gesucht und gelöscht, bevor der eigentliche Benutzer-Account aus /etc/passwd gelöscht werden kann. Im Beispiel wird die Suche wieder nur auf das /home-Verzeichnis beschränkt, was Sie allerdings in der Praxis wieder den Gegebenheiten anpassen sollten.

#! /bin/sh
# Name: account
# Mit diesem Script können Sie einen Benutzer
# * Anlegen
# * Löschen
# * Sperren
# * Sperre wieder aufheben
# Pfade, die beim Löschen eines Accounts benötigt werden,
# ggf. erweitern und ergänzen um bspw. /var /tmp ...
# überall eben, wo sich Dateien vom Benutzer befinden können
searchpath="/home"
usage() {
   echo "Usage: $0 Benutzer      (Neuen Benutzer anlegen)"
   echo "Usage: $0 -d Benutzer   (Benutzer löschen)"
   echo "Usage: $0 -l Benutzer   (Benutzer sperren)"
   echo "Usage: $0 -u Benutzer   (Gesperrten Benutzer wieder freigeben)"
}
# Nur root darf dieses Script ausführen ...
#
if [ `id -u` != 0 ]
then
   echo "Es werden root-Rechte für dieses Script benötigt!"
   exit 1
fi
# Ist das Kommando useradd auf dem System vorhanden ...
#
which useradd > /dev/null 2>1&
if [ $? -ne 0 ]
then
   echo "Das Kommando 'useradd' konnte auf dem System nicht "\
        " gefunden werden!"
   exit 1
fi
if [ $# -eq 0 ]
then
   usage
   exit 0
fi
if [ $# -eq 2 ]
then
   case $1 in
      -d)
         # Existiert ein entsprechender Benutzer
         if [ "`grep $2 /etc/passwd | \
                awk -F : '{print $1}'`" = "$2" ]
         then
            echo "Dateien und Verzeichnisse von '$2' "\
                 "werden gelöscht"
            # Alle Dateien und Verz. des Benutzers löschen
            find $searchpath -user $2 -print | sort -r |
            while read file
            do
              if [ -d $file ]
              then
                 rmdir $file
              else
                 rm $file
              fi
            done
         else
            echo "Ein Benutzer '$2' existiert nicht in "\
                 "/etc/passwd!"
            exit 1
         fi
         # Benutzer aus /etc/passwd und /etc/shadow löschen
         userdel -r $2 2>/dev/null
         echo "Benutzer '$2' erfolgreich gelöscht!"
         exit 0 ;;
      -l)
         # Existiert ein entsprechender Benutzer
         if [ "`grep $2 /etc/passwd | \
                awk -F : '{print $1}'`" = "$2" ]
         then
            passwd -l $2
         fi
         echo "Benutzer '$2' wurde gesperrt"
         exit 0 ;;
      -u)
         # Existiert ein entsprechender Benutzer
         if [ "`grep $2 /etc/passwd | \
                awk -F : '{print $1}'`" = "$2" ]
         then
            passwd -u $2
         fi
         echo "Benutzer '$2': Sperre aufgehoben"
         exit 0 ;;
      -h)   usage
            exit 1 ;;
      -*)   usage
            exit 1 ;;
       *)   usage
            exit 1 ;;
   esac
fi
if [ $# -gt 2 ]
then
   usage
   exit 1
fi
#####################################################
#        Einen neuen Benutzer anlegen
#
# Existiert bereits ein entsprechender Benutzer
#
if [ "`grep $1 /etc/passwd | awk -F : '{print $1}'`" = "$1" ]
then
   echo "Ein Benutzer '$1' existiert bereits in /etc/passwd ...!"
   exit 1
fi
# Bildschirm löschen
clear
# Zuerst wird die erste freie verwendbare User-ID gesucht,
# vorgeschlagen und bei Bestätigung verwendet, oder es wird eine
# eingegebene User-ID verwendet, die allerdings ebenfalls
# überprüft wird, ob sie bereits in /etc/passwd existiert.
#
userid=`tail –1 /etc/passwd |awk -F : '{print $3 + 1}'`
echo "Eingabe der UID [default: $userid] " | tr -d '\n'
read _UIDOK
# ... wurde nur ENTER betätigt
if [ "$_UIDOK" = "" ]
then
   _UIDOK=$userid
# ... es wurde eine UID eingegeben ->
# Überprüfen ob bereits vorhanden ...
elif [ `grep $_UIDOK /etc/passwd | awk -F : '{print $3}'` = "" ]
then
   _UIDOK=$userid
else
   echo "UID existiert bereits! ENTER=Neustart / STRG+C=Ende"
   read
   $0 $1
fi
# Selbiges mit Gruppen-ID
#
groupid=`grep users /etc/group |awk -F : '{print $3}'`
echo "Eingabe der GID: [default: $groupid] " | tr -d '\n'
read _GIDOK
if [ "$_GIDOK" = "" ]
then
   _GIDOK=$groupid
elif [ "`grep $_GIDOK /etc/group`" = "" ]
then
   echo "Dies Gruppe existiert nicht in /etc/group! "\
        "ENTER=Neustart / STRG+C=Ende"
   read
   $0 $1
fi
# Das Benutzer-Heimverzeichnis /home abfragen
#
echo "Eingabe des Heimverzeichnisses: [default: /home/$1] " | \
      tr -d '\n'
read _HOME
# Wurde nur ENTER gedrückt, default verwenden ...
if [ "$_HOME" = "" ]
then
   _HOME="/home/$1"
fi
# Die Standard-Shell für den Benutzer festlegen
#
echo "Eingabe der Shell: [default: /bin/bash] " | tr -d '\n'
read _SHELL
# Wurde nur ENTER gedrückt, default verwenden ...
if [ "$_SHELL" = "" ]
then
   _SHELL=/bin/bash
# Gibt es überhaupt eine solche Shell in /etc/shells ...
elif [ "`grep $_SHELL /etc/shells`" = "" ]
then
   echo "'$_SHELL' gibt es nicht in /etc/shells! "\
        " ENTER=Neustart / STRG+C=Ende"
   read
   $0 $1
fi
# Kommentar oder Namen eingeben
echo "Eingabe eines Namens: [beliebig] " | tr -d '\n'
read _REALNAME
# Expire date
echo "Ablaufdatum des Accounts: [MM/DD/YY] " | tr -d '\n'
read _EXPIRE
clear
echo
echo "Folgende Eingaben wurden erfasst:"
echo "---------------------------------"
echo "User-ID           : [$_UIDOK]"
echo "Gruppen-ID        : [$_GIDOK]"
echo "Heimverzeichnis : [$_HOME]"
echo "Login-Shell       : [$_SHELL]"
echo "Name/Komentar     : [$_REALNAME]"
echo "Account läuft aus : [$_EXPIRE]"
echo
echo "Account erstellen? (j/n) "
read _verify
case $_verify in
   [nN]*)
      echo "Account wurde nicht erstellt!" | tr -d '\n'
      exit 0 ;;
   [jJ]*)
        useradd -u $_UIDOK -g $_GIDOK -d $_HOME -s $_SHELL \
                -c "$_REALNAME" -e "$_EXPIRE" $1
        cp -r /etc/skel $_HOME
        chown -R $_UIDOK:$_GIDOK $_HOME
        passwd $1
        echo "Benutzer $1 [$_REALNAME] hinzugefügt "\
             "am `date`" >> /var/adm/newuser.log
        finger -m $1 |head –2
        sleep 2
        echo "Benutzer $1 erfolgreich hinzugefügt!" ;;
      *) exit 1;;
esac

Das Script bei der Ausführung:

linux:/home/you # ./account jack
Eingabe der UID [default: 1003](ENTER)
Eingabe der GID: [default: 100](ENTER)
Eingabe des Heimverzeichnisses: [default: /home/jack](ENTER)
Eingabe der Shell: [default: /bin/bash](ENTER)
Eingabe eines Namens: [beliebig] J.Wolf
Ablaufdatum des Accounts : [MM/DD/YY](ENTER)
...
Folgende Eingaben wurden erfasst:
---------------------------------
User-ID           : [1003]
Gruppen-ID        : [100]
Heimverzeichnis   : [/home/jack]
Login-Shell       : [/bin/bash]
Name/Komentar     : [J.Wolf]
Account läuft aus : []
Account erstellen? (j/n) j
Changing password for jack.
New password:********
Re-enter new password:********
Password changed
Login: jack                             Name: J.Wolf
Directory: /home/jack                   Shell: /bin/bash
Benutzer jack erfolgreich hinzugefügt!
linux:/home/you # ./account -l jack
Passwort geändert.
Benutzer 'jack' wurde gesperrt
linux:/home/you # ./account -u jack
Passwort geändert.
Benutzer 'jack': Sperre aufgehoben
linux:/home/you # ./account -d jack
Dateien und Verzeichnisse von 'jack' werden gelöscht
Benutzer 'jack' erfolgreich gelöscht!

Rheinwerk Computing

15.3.2 Systemüberwachung  toptop

Warnung, dass der Plattenplatz des Dateisystems an seine Grenzen stößt

Gerade, wenn man mehrere Dateisysteme oder gar Server betreuen muss, fällt es oft schwer, sich auch noch über den Plattenplatz Gedanken zu machen. Hierzu eignet sich ein Script, mit dem Sie den fünften Wert von »df –k« auswerten und daraufhin überprüfen, ob eine bestimmte von Ihnen festgelegte Grenze erreicht wurde. Ist die Warnschwelle erreicht, können Sie eine Mail an eine bestimmte Adresse verschicken oder eventuell ein weiteres Script starten lassen, welches diverse Aufräum- oder Komprimierarbeiten durchführt. Natürlich macht dieses Script vor allem dann Sinn, wenn es im Intervall mit einem cron-Job gestartet wird.


Hinweis   Auch hier sei nochmals auf das schon beschriebene Quota-System vor dem Script »DiskQuota« hingewiesen.


#!/bin/sh
# Name: chcklimit
# Dieses Script verschickt eine Mail, wenn der Plattenverbrauch
# eines Filesystems an ein bestimmtes Limit stösst.
# Ab wie viel Prozent soll ein Warnung verschickt werden
WARN_CAPACITY=80
# Wohin soll eine Mail verschickt werden
TOUSER=user@host.de
call_mail_fn() {
   servername=`hostname`
   msg_subject="$servername – Dateisystem(${FILESYSTEM}) "\
               "verwendet ${FN_VAR1}% – festgestellt am: `date`"
   echo $msg_subject | mail -s "${servername}:Warnung" $TOUSER
}
if [ $# -lt 1 ]
then
   echo "usage: $0 FILESYSTEM"
   echo "Bpsw.: $0 /dev/hda6"
fi
# Format von df -k:
# Dateisystem   1K-Blöcke   Benutzt Verfügbar Ben% Eingehängt auf
# /dev/hda4     15528224    2610376 12917848  17  %  /
# Den fünften Wert wollen wir haben: 'Ben%'
#
VAR1=`df -k ${1} | /usr/bin/tail –1 | \
     /usr/bin/awk '{print $5}' `
# Prozentzeichen herausschneiden
VAR2=`echo $VAR1 | \
      /usr/bin/awk '{ print substr($1,1,length($1)-1) }' `
# Wurde die Warnschwelle erreicht ... ?
if [ $VAR2 -ge ${WARN_CAPACITY} ]
then
   FN_VAR1=$VAR2
   call_mail_fn
fi

Das Script bei der Ausführung:

you@host > ./chcklimit /dev/hda6
...
you@host > mail
>N  1 tot@linux.site Mon May  2 16:18  18/602   linux:Warnung
? 1
Message  1:
From: tot@linux.site (J.Wolf)
linux – Dateisystem() verwendet 88  % – festgestellt am: Mo Mai 2 16:17:59 CEST 2005

Kommandos bzw. Scripts auf einem entfernten Rechner ausführen

Das Ausführen von Kommandos oder gar von Scripts auf mehreren Rechnern, gestartet vom lokalen Rechner, hört sich komplizierter an als es ist. Und vor allem es ist auch sicherer, als manch einer jetzt vielleicht denken mag. Dank guter Verschlüsselungstechnik und hoher Präsenz bietet sich hierzu ssh an (die r-Tools fallen wegen der Sicherheitslücken flach – siehe Abschnitt 14.12.10). Die Syntax, um mit ssh Kommandos oder Scripts auf einem anderen Rechner auszuführen, sieht wie folgt aus:

ssh username@hostname "kommando1 ; kommando2 ; script"

Mit diesem Wissen fällt es nicht schwer, sich ein entsprechendes Script zusammenzubasteln. Damit es ein wenig flexibler ist, soll es auch möglich sein, Shellscripts, die noch nicht auf dem entfernten Rechner liegen, zuvor noch mit scp in ein bestimmtes Verzeichnis hochzuladen, um es anschließend auszuführen. Ebenso soll es möglich sein, in ein bestimmtes Verzeichnis zu wechseln, um dann entsprechende Kommandos oder Scripts auszuführen. Natürlich setzt dies voraus, dass auf den Rechnern auch ein entsprechendes Verzeichnis existiert. Die Rechner, auf denen Kommandos oder Scripts ausgeführt werden sollen, tragen Sie in die Datei namens hostlist.txt ein. Bei mir sieht diese Datei wie folgt aus:

you@host > cat hostlist.txt
us10129@myhoster.de
jwolf@192.135.147.2

Im Beispiel finden Sie also zwei entfernte Rechner, bei denen das gleich folgende Script dafür sorgt, dass Sie von Ihrem lokalen Rechner aus schnell beliebige Kommandos bzw. Scripts ausführen können.


Hinweis   Damit Sie nicht andauernd ein Passwort eingeben müssen, empfiehlt es sich auch hier, SSH-Schlüssel zu verwenden (siehe Abschnitt 14.12.12).


#!/bin/sh
# Name: sshell
# Kommandos bzw. Scripts auf entfernten Rechnern ausführen
# ggf. den Pfad zur Datei anpassen
HOSTS="hostlist.txt"
usage() {
   echo "usage: progname [-option] [Verzeichnis] "\
        " Kommando_oder_Script"
   echo
   echo "Option:"
   echo "-d :in ein bestimmtes Verzeichnis auf dem Host wechseln"
   echo "-s :Script in ein bestimmtes Verzeichnis hochladen und"\
        " Ausführen"
   echo
   echo "Syntax der Host-Liste: "
   echo "Username@hostname1"
   echo "Username@hostname2"
   echo "..."
   exit 1
}
if [ $# -eq 0 ]
then
   usage
fi
# Datei 'hostlist.txt' überprüfen
if [ -e $HOSTS ]
then :
else
   echo "Datei $HOSTS existiert nicht ..."
   touch hostlist.txt
   if [ $? -ne 0 ]
   then
      echo "Konnte $HOSTS nicht anlegen ...!"
      exit 1
   else
      echo "Datei $HOSTS erzeugt, aber noch leer ...!"
      usage
      exit 1
   fi
fi
# Optionen überprüfen ...
case $1 in
   -d)
      if [ $# -lt 3 ]
      then
         usage
      fi
      DIR=$2
      shift; shift  ;;
   -s)
      if [ $# -lt 3 ]
      then
         usage
      fi
      DIR=$2
      SCRIPT="yes"
      shift; shift  ;;
   -*)
      usage ;;
esac
# Die einzelnen Hosts durchlaufen ...
for host in `cat $HOSTS`
do
   echo "$host : "
   CMD=$*
   if [ "$SCRIPT" = "yes" ]
   then
      scp $CMD ${host}:${DIR}
   fi
   ret=`ssh $host "cd $DIR; $CMD"`
   echo "$ret"
done

Das Script bei der Ausführung:

Inhalt des Heimverzeichnisses ausgeben:

you@host > ./sshell ls -l
us10129@myhoster.de :
total 44
drwx------   4 us10129 us10129 4096 May 14 13:45 backups
drwxr-xr-x   8 us10129 us10129 4096 May  9 10:13 beta.pronix.de
-rw-rw-r--   1 us10129 us10129   66 Dec  2 02:13 db_cms.bak
drwxrwxr-x   2 us10129 us10129 4096 Mar 11 07:49 dump
-rw-------   1 us10129 us10129  952 May 14 14:00 mbox
drwxrwxr-x   2 us10129 us10129 4096 Mar 28 18:03 mysqldump
drwxr-xr-x  20 us10129 us10129 4096 May 19 19:56 www.pronix.de
jwolf@192.135.147.2 :
total 24
drwxr-xr-x  2 jwolf  jwolf    512 May  8 14:03 backups
drwxr-xr-x  3 jwolf  jwolf  21504 Sep  2  2004 dev

Inhalt des Verzeichnisses $HOME/backups ausgeben:

you@host > ./sshell -d backups ls -l
us10129@myhoster.de :
total 8
drwxrwxr-x  3 us10129 us10129 4096 May 18 18:38 Shellbuch
drwxrwxr-x  3 us10129 us10129 4096 May 14 13:46 Shellbuch_bak
-rw-rw-r--  1 us10129 us10129    0 May 20 12:45 file1
-rw-rw-r--  1 us10129 us10129    0 May 20 12:45 file2
-rw-rw-r--  1 us10129 us10129    0 May 20 12:45 file3
jwolf@192.135.147.2 :
total 6
-rw-r--r--  1 jwolf  jwolf   0 May  8 13:38 file1
-rw-r--r--  1 jwolf  jwolf   0 May  8 13:38 file2
-rw-r--r--  1 jwolf  jwolf   0 May  8 13:38 file3
-rwx------  1 jwolf  jwolf  29 May  8 13:58 hallo.sh
-rwx------  1 jwolf  jwolf  29 May  8 13:48 mhallo
-rwx------  1 jwolf  jwolf  29 May  8 14:03 nhallo

Dateien beginnend mit »file*« im Verzeichnis $HOME/backups löschen:

you@host > ./sshell -d backups rm file*
us10129@myhoster.de :
jwolf@192.135.147.2 :

Inhalt des Verzeichnisses $HOME/backups erneut ausgeben:

you@host > ./sshell -d backups ls -l
us10129@myhoster.de :
total 5
drwxrwxr-x  3 us10129 us10129 4096 May 18 18:38 Shellbuch
drwxrwxr-x  3 us10129 us10129 4096 May 14 13:46 Shellbuch_bak
jwolf@192.135.147.2 :
total 3
-rwx------  1 jwolf  jwolf  29 May  8 13:58 hallo.sh
-rwx------  1 jwolf  jwolf  29 May  8 13:48 mhallo
-rwx------  1 jwolf  jwolf  29 May  8 14:03 nhallo

Neues Verzeichnis testdir anlegen:

you@host > ./sshell mkdir testdir
us10129@myhoster.de :
jwolf@192.135.147.2 :

Script hallo.sh ins neue Verzeichnis (testscript) schieben und ausführen:

you@host > ./sshell -s testdir ./hallo.sh
us10129@myhoster.de :
hallo.sh                            100  %   67     0.1KB/s   00:00
Ich bin das Hallo Welt-Script!
jwolf@192.135.147.2 :
hallo.sh                            100  %   67     0.1KB/s   00:00
Ich bin das Hallo Welt-Script!

Verzeichnis testdir wieder löschen:

tot@linux:~> ./sshell rm -r testdir
us10129@myhoster.de :
jwolf@192.135.147.2 :


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 << zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Shell-Programmierung
Shell-Programmierung
bestellen
 Buchtipps
Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-Programmierung


Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern