Admin-Skripte

Auf dieser Seite gibt es verschiedene Skripte, welche ich auf dem Caipirinha-Server einsetze, um anfallende Verwaltungsaufgaben zu lösen. Diese Skripte sollen als Anregung zur Erstellung eigener Skripte dienen, können aber natürlich auch komplett übernommen und bei Bedarf angepasst werden. Es finden sich hier:

Nach dem Booten

Dieses Skript führe ich als root nach einem Booten der Maschine durch. Es macht folgende Dinge:

  • Das Akustik-Management der Festplatten wird auf “Performance” gestellt (sprich: “Geschwindigkeit” statt “Ruhe”).
  • Für einige Festplatten wird ein automatischer Ruhezustand eingestellt. Die Systemplatten sollen aber nicht abschalten (Dauerbetrieb).
  • Die Festplatten mit Videos (/home/public/Video) wird eingebunden. Dies erfolgt nicht gleich beim Start, weil diese Platte in die verschlüsselte Partition /home/public eingebunden wird.
  • Der snmp– und der snmptrap-Dienst wird gestartet. Der snmp-Dienst wird erst nach dem Einbinden der Video-Festplatten gestartet, weil snmp auch die Plattenauslastung überwachen soll, und erst nach dem Einbinden der Video-Festplatten alle snmp-Variablen verfügbar sind.
  • mrtg wird gestartet, um Netzwerk-Verkehr und CPU-Auslastung zu überwachen.
  • Der sensor-Dienst wird gestartet, um die Temperatur im Server zu überwachen.
  • Ein VLC-Server mit Telnet-Interface wird für die Caipithek gestartet. Dabei muss natürlich geheimes_passwort durch ein selbst gewähltes, geheimes Passwort ersetzt werden.
  • Ein DLNA-Server wird mit dem Programm minidlna gestartet.
  • openVPN wird gestartet und gleich danach noch MySQL. Der Grund dafür ist, dass der MySQL-Server auf dem Caipirinha-Server als Slave Server des MySQL-Servers auf rueeck.name läuft, und die Verbindung über openVPN getunnelt wird.
#! /bin/bash
#
# This script contains some commands that shall be executed after the
# caipirinha server has booted up.
#
# Gabriel Rüeck 13.09.2010
#

# Configure power and acustic management of the hard disks.
hdparm -S   0 /dev/sda
hdparm -S   0 /dev/sdb
hdparm -S 180 /dev/sdc
hdparm -S 180 /dev/sdd
hdparm -S 180 /dev/sde
hdparm -M 254 /dev/sda
hdparm -M 254 /dev/sdb
hdparm -M 254 /dev/sdc
hdparm -M 254 /dev/sdd
hdparm -M 254 /dev/sde

# Mount Video Array
mount /dev/md3 /home/public/Video

# Start SNMP Services (must only be started after mounting /home/public/Video)
/etc/init.d/snmpd start
snmptrapd

# Start mrtg
touch /var/log/mrtg.log /var/run/mrtg.pid
chown wwwrun:www /var/log/mrtg.log /var/run/mrtg.pid
env LANG=C mrtg --user wwwrun --group www --logging /var/log/mrtg.log --lock-file /var/tmp/mrtg --pid-file=/var/run/mrtg.pid /etc/mrtg.conf

# Start the sensor daemon
rm /var/log/sensord.rrd 2>/dev/null
modprobe coretemp it87
sensord -r /var/log/sensord.rrd -p /var/run/sensord.pid
chmod a+r /var/log/sensord.rrd

# Start cvlc for the Caipithek
su -l wwwrun -c 'rm c*.log; cvlc -I oldtelnet --telnet-password geheimes_passwort > cvlc.log 2>&1 &'

# Start DLNA Server (minidlna)
su -l wwwrun -c '/usr/sbin/minidlna'

# Start openVPN and mysql
/etc/init.d/openvpn start
/etc/init.d/mysql start

Wahrscheinlich könnte man dieses Skript auch automatisch nach dem Abschluss des Boot-Vorgangs ausführen lassen. Falls jemand eine entsprechende Idee hat, bin ich für Hinweise dankbar.

Neue Dateien auflisten lassen

Da verschiedene Benutzer des Caipirinha-Servers Schreibberechtigung auf dessen öffentliche Ordner haben, verliere ich oft selbst den Überblick darüber, welche Dateien wo neu abgelegt sind. Außerdem ist es schwierig, andere Benutzer über neu abgelegte Dateien zu informieren. Das soll nun das folgende Skript machen, welches einen RSS-Feed erzeugt, der dann mit modernen Browsern oder Mail-Clients oder RSS-Clients lesbar ist. Über einen cron-Job wird dieses Skript einmal pro Nacht ausgeführt und erzeugt dann einen neuen RSS-Feed, den andere Benutzer einbinden können. Was ein RSS-Feed ist und wie er aufgebaut ist, wird auf [1] beschrieben.

update_rss.sh:

#!/bin/bash
#
# This script searches various folders for new files, extracts a description of the files and generates an RSS 2.0 feed in XML.
#
# Gabriel Rüeck, gabriel@caipirinha.homelinux.org, 10.10.2012
#

# Pre-define some variables and read the configuration files.
umask 0022
readonly GENERATOR='Caipirinha RSS Generator'
readonly CONFIGPATH='/etc/rss'
         DURATION=7
         CHANNEL_NAME=$(hostname)
         CHANNEL_LINK="http://$(hostname --fqdn)/"
         TMPFILE='/tmp/rss'$RANDOM

# Add the PATH variable so that it is the same whether the program is called from the shell or from within the cron environment.
PATH='/usr/bin:/bin:/opt/gnome/bin'

# Get additional options, if available
while getopts :D:o:p:t:T: OPT
do case "$OPT" in
     "D") OPT_DESCRIPTION=$OPTARG;;
     "o") if [ ! -f $OPTARG ]; then
             echo -e "ERROR: The configuration file \""$OPTARG"\" does not exist.\n"
             exit 2
          else
             CONFIGFILE=$OPTARG
          fi;;
     "p") OPT_PATHFILE=$OPTARG;;
     "t") OPT_DURATION=$OPTARG;;
     "T") OPT_TITLE=$OPTARG;;
   esac
done

# Read the configuration file. If no configuration file has been specified, try to read "/etc/rss/rss.conf".
# However, there will not be an error message if no configuration file is available.
test -e ${CONFIGFILE:=$CONFIGPATH'/rss.conf'} && . $CONFIGFILE

# Process optional arguments; they have precedence over the values defined in the configuration files.
test -n "$OPT_DESCRIPTION" && DESCRIPTION=$OPT_DESCRIPTION
test -n "$OPT_DURATION" && DURATION=$OPT_DURATION
test -n "$OPT_PATHFILE" && PATH_FILE=$OPT_PATHFILE
test -n "$OPT_TITLE" && TITLE=$OPT_TITLE

# Check if sufficient arguments have been provided.
shift $[$OPTIND-1]
if [ $# -lt 1 ]; then
   echo -e "Usage:  "$0"  [OPTIONS]  DST_FILE_1  [DST_FILE_2  [...]]\n"
   exit 1
fi

# Check if PATH_FILE exists.
if [ ! -f $PATH_FILE ]; then
   echo -e "ERROR: The path file \""$PATH_FILE"\" does not exist.\n"
   exit 2
fi

# Get the lower link bar which has links to all subfolders that are available on the server.
# As the pipe (|) creates a subshell, the while loop has to be located in () and has to have an echo command at the end which will then
# send the accumulated variable back to the calling shell. That may look strange, but otherwise, things will not work... Bad trap for newbies...
# Create a footer that contains links to all folders that shall be searched, with some CSS-style formatting, actuallz oriented towards www.faz.net
FINAL='<div style="margin:5px 0 5px 0; border-top:1px solid blue; font:9px arial;"><a href="'$CHANNEL_LINK'">'$CHANNEL_NAME'</a> '\
$(sed '/^ *#.*/d' $PATH_FILE | (while read LINE;
                                do HTML_PATH=$(echo $LINE | cut -f2 -d"|")
                                   DESCRIPTION=$(echo $LINE | cut -f3 -d"|")
                                   FINAL=$FINAL'| <a href="'$HTML_PATH/'">'$DESCRIPTION'</a> '
                                done;
                                echo $FINAL;))'</div>'

# Make the file readable for everyone
umask 022

# Create page headers for the RSS file
echo '<?xml version="1.0" encoding="UTF-8"?>' > $TMPFILE
echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">' >> $TMPFILE
echo '<channel>' >> $TMPFILE
echo " <title>$TITLE</title>" >> $TMPFILE
echo " <link>$CHANNEL_LINK</link>" >> $TMPFILE
echo " <description>$DESCRIPTION</description>" >> $TMPFILE
echo " <managingEditor>$EDITOR</managingEditor>" >> $TMPFILE
echo " <generator>$GENERATOR</generator>" >> $TMPFILE
echo ' <pubDate>'$(date -R)'</pubDate>' >> $TMPFILE
# Put an image of the server on the headline.
echo ' <image>' >> $TMPFILE
echo "  <title>$TITLE</title>" >> $TMPFILE
echo "  <url>$IMG_URL</url>" >> $TMPFILE
echo "  <link>$CHANNEL_LINK</link>" >> $TMPFILE
echo ' </image>' >> $TMPFILE

# Scan all folders on whether there are new files. Extract the file name, create a link on it, get a description of the file, the date of the
# last modification, and extract an icon from the Apache2 icons.
sed '/^ *#.*/d' $PATH_FILE | while read LINE;
     do SEARCH_PATH=$(echo $LINE | cut -f1 -d"|")
        HTML_PATH=$(echo $LINE | cut -f2 -d"|")
 OMIT_PATH=$(echo $LINE | cut -f4 -d"|")
        # The "cd" command must be connected with the "find" command because otherwise, if the "cd" path did not exist, the script would search the entire users's home
        # directory and deliver sensitive information with the file names listed.
 cd "$SEARCH_PATH" && find -L -not -path "$OMIT_PATH" -type f -mtime -$DURATION -perm -004 -name "[[:alnum:]]*" -not -name "*~" -not -name "*.old" -not -name "metadata.xml" -not -path "*/\.*" -print 2>/dev/null |
             # The following commands will be executed in a subshell; they would anyway because they follow the pipe (|) symbol.
             # But first the variable IFS is set to "|" as separator. Normally, IFS is set to " ". This is important for the read command as IFS
             # contains the character that is used as a separator for the read command. A " " as separator leads to problems when the file name has
             # a double white space like "  " or so in the name as read would report this back as a single white space " ". That !$#% cost me a day...
             (OLD_IFS=$IFS
              IFS=""
              while read -r NAME;
              do LINK_NAME=$(echo $NAME | sed -f $CONFIGPATH/link_name.sed)
                 HTML_NAME=$(basename $NAME | sed -f $CONFIGPATH/html_name.sed)
                 FILE_TYPE_KNOWN=false
                 # Try first to determine the file type with the "file" command as sometimes, this yields the more exact result.
                 FILE_TYPE=$(file -b $NAME)
                 case "$FILE_TYPE" in
                   "ASCII text")                  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/txt.png"> '; FILE_TYPE_KNOWN=true;;
                   *"perl script"*)               FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_pl.png"> '; FILE_TYPE_KNOWN=true;;
                   *"shell script"*)              FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/shellscript.png"> '; FILE_TYPE_KNOWN=true;;
                   "vCard"*)                      FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/vcalendar.png"> '; FILE_TYPE_KNOWN=true;;
                 esac
                 if ! $FILE_TYPE_KNOWN; then
                    # If the file type could not be determined with the "file" command, then proceed with "gnomeinfo-vfs".
                    FILE_TYPE=$(gnomevfs-info $NAME | sed -n 's/\(^MIME type *:\) \(.*\)/\2/p')
                    # unset $FILE_DESC because there may be old content in it.
                    unset FILE_DESC
                    case "$FILE_TYPE" in
                      "application/msaccess")                                           FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/database.png"> ';;
                      "application/msword")                                             FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/wordprocessing.png"> ';;
                      "application/octet-stream")                                       FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/binary.png"> ';;
                      "application/pdf")                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/pdf.png"> ';;
                      "application/rtf")                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/document.png"> ';;
                      "application/vnd.ms-excel")                                       FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/spreadsheet.png"> ';;
                      "application/vnd.ms-outlook")                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/message.png"> ';;
                      "application/vnd.ms-project")                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/txt2.png"> ';;
                      "application/vnd.ms-powerpoint")                                  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/presentation.png"> ';;
                      "application/vnd.openxmlformats-officedocument.presentation"*)    FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/presentation.png"> ';;
                      "application/vnd.openxmlformats-officedocument.spreadsheet"*)     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/spreadsheet.png"> ';;
                      "application/vnd.openxmlformats-officedocument.wordprocessing"*)  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/wordprocessing.png"> ';;
                      "application/vnd.oasis.opendocument.spreadsheet")                 FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/spreadsheet.png"> ';;
                      "application/vnd.oasis.opendocument.text")                        FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/wordprocessing.png"> ';;
                      "application/x-bittorrent")                                       FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/torrent.png"> ';;
                      "application/x-cd-image")                                         FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/cdimage.png"> ';;
                      "application/x-compressed-tar")                                   FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/tgz.png"> ';;
                      "application/x-font-speedo")                                      FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/font.png"> ';;
                      "application/x-font-ttf")                                         FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/font_truetype.png"> ';;
                      "application/x-font-type1")                                       FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/font_type1.png"> ';;
                      "application/x-java-archive")                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_java.png"> ';;
                      "application/x-ms-dos-executable")                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/binary.png"> ';;
                      "application/x-msi")                                              FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/make.png"> ';;
                      "application/x-rpm")                                              FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/rpm.png"> ';;
                      "application/x-subrip")                                           FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/txt2.png"> ';;
                      "application/x-509"*)                                             FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/encrypted.png"> ';;
                      "application/xml")                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/html.png"> ';;
                      "application/zip")                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/zip.png"> ';;
                      "audio/midi")                                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/midi.png"> ';;
                      "audio/"*)                                                        FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/sound.png"> ';;
                      "image/"*)                                                        FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/image.png"> ';;
                      "text/calendar")                                                  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/vcalendar.png"> ';;
                      "text/csv")                                                       FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/spreadsheet.png"> ';;
                      "text/html")                                                      FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/html.png"> ';;
                      "text/plain")                                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/txt.png"> ';;
                      "text/x-chdr")                                                    FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_h.png"> ';;
                      "text/x-c++hdr")                                                  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_h.png"> ';;
                      "text/x-csrc")                                                    FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_c.png"> ';;
                      "text/x-c++src")                                                  FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/source_c.png"> ';;
                      "text/x-log")                                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/widget_doc.png"> ';;
                      "text/x-uri")                                                     FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/html.png"> ';;
                      "video/quicktime")                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/quicktime.png"> ';;
                      "video/mp"*)                                                      FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/video.png"> ';;
                      "video/x-"*)                                                      FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/video.png"> ';;
                      *)                                                                FILE_DESC='<img src="'${CHANNEL_LINK}'kde-icons/unknown.png"> ';;
                    esac
                 fi

                 # Add the file description to the selected image.
                 if [ ${FILE_TYPE%%/*} = "audio" ]; then
                    DATA=$(mediainfo -f "$NAME")
                    MEDIA_DURATION=$(echo $DATA | sed -n 's/^Duration[^:]*:[^0-9]*\([0-9].*[^0-9].*\)/\1/p' | head -n1)
                    FILE_DESC="$FILE_DESC $FILE_TYPE $MEDIA_DURATION, "$(stat -c%s" Bytes" $NAME)
                 elif [ ${FILE_TYPE%%/*} = "video" ]; then
                    DATA=$(mediainfo -f "$NAME")
                    MEDIA_WIDTH=$(echo $DATA | sed -n 's/^Width[^:]*:[^0-9]*\([0-9].*\)/\1/p' | head -n1)
                    MEDIA_HEIGHT=$(echo $DATA | sed -n 's/^Height[^:]*:[^0-9]*\([0-9].*\)/\1/p' | head -n1)
                    MEDIA_DURATION=$(echo $DATA | sed -n 's/^Duration[^:]*:[^0-9]*\([0-9].*[^0-9].*\)/\1/p' | head -n1)
                    VIDEO_STREAMS=$(echo $DATA | sed -n 's/^Count of video streams[^:]*:[^0-9]*\([0-9].*\)/\1/p' | head -n1)
                    AUDIO_STREAMS=$(echo $DATA | sed -n 's/^Count of audio streams[^:]*:[^0-9]*\([0-9].*\)/\1/p' | head -n1)
                    FILE_DESC="$FILE_DESC $FILE_TYPE ${MEDIA_WIDTH}x${MEDIA_HEIGHT}, $MEDIA_DURATION, $VIDEO_STREAMS video streams, $AUDIO_STREAMS audio streams, "$(stat -c%s" Bytes" $NAME)
                 else
                    FILE_DESC="$FILE_DESC $FILE_TYPE, "$(stat -c%s" Bytes" $NAME)
                 fi
                 # Create a new item for each newly found file.
                 echo ' <item>' >> $TMPFILE
                 echo '  <title>'$HTML_NAME'</title>' >> $TMPFILE
                 echo '  <link>'$HTML_PATH/$LINK_NAME'</link>' >> $TMPFILE
                 echo '  <pubDate>'$(date -Rr $NAME)'</pubDate>' >> $TMPFILE
                 echo '  <description>'$FILE_DESC$FINAL'</description>' >> $TMPFILE
                 echo '  <guid>'$HTML_PATH/$LINK_NAME'</guid>' >> $TMPFILE
                 echo ' </item>' >> $TMPFILE
              done;
              # Reset IFS to its old value
              IFS=$OLD_IFS)
     done;
echo '</channel>' >> $TMPFILE
echo '</rss>' >> $TMPFILE

# Move the file from the TMP folder to the final destinations.
for i in $*; do
    cp $TMPFILE $i
done
rm $TMPFILE

Leider funktioniert dieses Skript nicht ganz allein, sondern benötigt noch mehrere Hilfsdateien, was die Anwendung erschwert. In /etc/rss sollten folgende vier Dateien abgelegt werden:

  • /etc/rss/link_name.sed
  • /etc/rss/html_name.sed
  • /etc/rss/rss.conf

Die sed-Hilfsdateien werden im Browser leider nicht richtig wieder gegeben. Deshalb kann man sie als Archiv unter [2] herunter laden. Sie werden für die sed-Aufrufe im Skript benötigt.

Die vierte Hilfsdatei ist hier abgedruckt:

/etc/rss/rss.conf:

# CONFIGURATION FILE FOR THE RSS SCRIPT
# This File will be read by the RSS script. The settings in this file are the general settings for the script.
#
# General Settings for the RSS Script
#
TITLE="New Files on CAIPIRINHA"
CHANNEL_NAME="CAIPIRINHA Server"
CHANNEL_LINK="http://caipirinha.homelinux.org/"
DESCRIPTION="Photographs, Music, Books and other Documents"
EDITOR="gabriel@caipirinha.homelinux.org (Gabriel Rüeck)"
#
# Settings for the Image of the Website
#
IMG_URL="http://caipirinha.homelinux.org/jpg/Caipirinha.jpg"
#
# Settings for the Path File (containing all paths that shall be searched and their respective HTML links)
PATH_FILE="/home/public/paths.txt"
#
# Miscellaneous Settings
#
# Duration sets the number of days for the "find" command. Files which were modified up to $DURATION days, will be
# taken into account.
#
DURATION=30

Die ersten beiden Dateien beschreiben Ersetzungen, welche das mehrfach eingesetzte sed-Kommando im Skript vornehmen soll. Ein RSS-Feed ist ja eine XML-Datei, und deshalb müssen bei den auf dem Caipirinha-Server vorkommenden Dateinamen, welche ja im Idealfall beliebige Zeichen enthalten dürfen, zahlreiche Ersetzungen vorgenommen werden, um den Eigenheiten von XML und HTML gerecht zu werden.

Die dritte Datei ist eine Standardkonfiguration, welche verschiedene Parameter des RSS-Feeds (beispielsweise den Link zum Piktogramm des RSS-Feeds) festlegt. Alle in dieser Standard-Konfiguration angegebenen Werte können aber auch durch eine eigene Konfigurationsdatei überschrieben werden. Eine solche modifizierte Konfigurationsdatei ist hier abgedruckt:

Eigene Konfigurationsdatei:

# CONFIGURATION FILE FOR THE RSS SCRIPT
# This File will be read by the RSS script. The settings in this file are the general settings for the script.
#
# General Settings for the RSS Script
#
TITLE="Gabriel Rüeck"
CHANNEL_NAME="Gabriel Rüeck"
CHANNEL_LINK="http://caipirinha.homelinux.org/~gabriel/"
DESCRIPTION="News from Gabriel Rüeck"
EDITOR="gabriel@caipirinha.homelinux.org (Gabriel Rüeck)"
#
# Settings for the Image of the Website
#
IMG_URL="http://caipirinha.homelinux.org/~gabriel/Bilder/Zeitung.jpg"
#
# Settings for the Path File (containing all paths that shall be searched and their respective HTML links)
PATH_FILE="/home/gabriel/rss/paths.txt"
#
# Miscellaneous Settings
#
# Duration sets the number of days for the "find" command. Files which were modified up to $DURATION days, will be
# taken into account.
#
DURATION=30

Wichtig ist bei eigenen Konfigurationsdateien nur, dass die entsprechenden Schlüsselworte genau so geschrieben werden wie in der allgemeinen Konfigurationsdatei /etc/rss/rss.conf. Abgesehen von einer eigenen Konfigurationsdatei kann man aber auch dediziert eigene Optionen beim Aufruf des Skripts angeben. Folgende Optionen sind möglich:

  • -D Beschreibung definiert die Beschreibung des RSS-Feeds.
  • -o Dateiname gibt an, wo sich die zu verwendende Konfigurationsdatei befindet. Der Default-Wert ist /etc/rss/rss.conf.
  • -p Dateiname gibt an, wo sich die Pfaddatei befindet, in welcher die zu durchsuchenden Verzeichnisse angegeben sind (siehe unten).
  • -t Zeitdauer gibt an, wieviele Tage die zu berücksichtigenden Dateien (die in den RSS-Feed aufgenommen werden), alt sein dürfen.
  • -T Titel definiert den Titel des RSS-Feeds.

Die dediziert angegebenen Optionen haben Vorrang gegenüber den Optionen in einer angegebenen Konfigurationsdatei. Eine mit -o angebene Konfigurationsdatei hat wiederum Vorrang gegenüber der allgemeinen Konfigurationsdatei /etc/rss/rss.conf.

Als letzte Datei in diesem Sammelsurium wird noch die üblicherweise in der Konfigurationsdatei referenzierte Pfaddatei benötigt. Ein Beispiel einer Pfaddatei ist hier gezeigt:

/home/public/paths.txt:

# Path List for the RSS Script
#
# This file contains the folders that will be scanned by the RSS script.
# Each line contains three elements which have to be separated by a | character. The structure is:
#
# path_to_be_scanned | HTML_prefix | Title | Optional path which is to be omitted
#
# It is important that the HTML_prefix does not have a trailing / character.
#
/home/public/Bücher|https://caipirinha.homelinux.org/MM/Bücher|Books
/home/public/Dokumente|https://caipirinha.homelinux.org/MM/Dokumente|Documents
/home/public/Unterhaltung|https://caipirinha.homelinux.org/MM/Unterhaltung|Entertainment
/home/public/Spiele|https://caipirinha.homelinux.org/MM/Spiele|Games
/home/public/Linux|https://caipirinha.homelinux.org/MM/Linux|Linux
/home/public/Video|https://caipirinha.homelinux.org/MM/Filme|Movies
#/home/public/Video|https://caipirinha.homelinux.org/MM/Filme|Movies|./[[:digit:]]-*
#/home/public/Audio|https://caipirinha.homelinux.org/MM/Musik|Music
/home/public/Bilder|https://caipirinha.homelinux.org/MM/Fotos|Pictures
/home/public/Software|https://caipirinha.homelinux.org/MM/Software|Windows
Akregator View
Akregator View

Wegen des Bash-Skriptes sind hier leider einige Eigenarten zu beachten:

  • Jede gültige Zeile besteht aus drei Teilen: dem zu durchsuchenden Pfad ohne “/” am Ende, dem HTML-Link, unter dem dieses Verzeichnis zu finden ist und der Beschreibung, welche diesem Verzeichnis zuzuordnen ist
  • Die letzte Zeile muss mit einem Zeilenendekennzeichen abgeschlossen werden, d.h. der Cursor muss beim Speichern in der letzten und leeren Zeile sein.

Bei der Konfiguration des Apache-Servers muss natürlich berücksichtigt werden, dass die einzelnen Verzeichnisse wie beispielsweise /home/public/Bücher dann auch über HTML unter https://caipirinha.homelinux.org/MM/Bücher zugänglich sind. Damit keine unbefugten Zugriffe stattfinden, empfehlen sich Zugangsbeschränkungen für den HTML-Zugriff.

Firefox View
Firefox View

Außerdem müssen die KDE-eigenen Piktogramme unter /opt/kde3/share/icons/crystalsvg/48×48/mimetypes unter dem Link http://caipirinha.homelinux.org/kde-icons zugänglich gemacht werden, denn das Skript bezieht sich auf dieses Verzeichnis, um Bilder in den RSS-Feed einzufügen. Allerdings wird das Auflisten der Piktogramme in diesem Webverzeichnis untersagt; es wird lediglich das Anzeigen eines dedizierten Piktogrammes gestattet, wenn man den vollen Dateinamen eingibt.

Das hier abgebildete Skript update_rss.sh funktioniert also nur dann richtig, wenn die zwei genannten sed-Skripte sowie mindestens eine gültige Konfigurationsdatei korrekt installiert sind. Wurde alles korrekt eingerichtet, dann kann man mit

./update_rss.sh /home/public/rss_index.xml

einen neuen RSS-Feed unter /home/public/rss_index.xml erzeugen lassen. Man kann übrigens anstatt einer Zieldatei für den RSS-Feed auch gleich mehrere Zieldateien angeben. Dann werden gleich mehrere identische Feeds in unterschiedlichen Verzeichnissen generiert.

Internet Explorer View
Internet Explorer View

Der Aufruf

./update_rss.sh -o $HOME/rss/rss.conf /home/public/rss_index.xml

liest noch die Konfigurationsdatei $HOME/rss/rss.conf, wertet diese aus und erzeugt dann den RSS-Feed /home/public/rss_index.xml.

Ich will nicht verschweigen, dass der erzeugte RSS-Feed nicht immer die RSS-Spezifikationen erfüllt. Dies hängt meist mit irgendwelchen Sonderzeichen im Dateinamen zusammen. Allerdings lässt sich der Feed in den meisten Browsern oder Mail-Clients ohne Probleme einbinden.

Doppelte Dateien löschen

In den Benutzerverzeichnissen auf dem Server tummeln sich oft Dateien, welche bereits in öffentlichen Ordnern des Servers existieren. Dadurch wird die gleiche Datei mehrfach auf dem Server abgelegt. Ein klarer Fall von Platzverschwendung. Das soll sich mit dem folgenden Skript ändern:

kill_doubles.sh:

#!/bin/bash
#
# This script performs the following steps:
#      1. Scan dedicated public folders and create MD5 hashes of all non-hidden files.
#      2. Save a list of these files with MD5 hashes.
#      3. Scan user folders and create MD5 hashes of all non-hidden files.
#      4. Compare these MD5 hashes with the ones in the public folders.
#      5. If a double file is detected, deöete the public file in the personal folder and replace it with a link
#         to the respective file in the public folder.
#
# Gabriel Rüeck, gabriel@caipirinha.homelinux.org, 10.09.2008
#                last update:                      11.12.2011
#

# Pre-define some variables and read the configuration files.
readonly CHANGED_FILES='/tmp/changed_files.txt'
readonly CHECKSUM_FILE='/home/public/Prüfsummen.txt'
readonly GROUP='users'
readonly MAIL_RCPT='root@caipirinha'
readonly MAIL_SUBJ='Geänderte Benutzer-Dateien'
# Do not bother with small files.
readonly SIZE='+30k'

umask 022
# Scan dedicated public folders, create files with MD5 hashes in these folders and save the name of those files in another file named $CHECKSUM_LOCATIONS.
rm ${CHECKSUM_FILE} 2>/dev/null
for SCANPATH in /home/public/Audio /home/public/Bilder; do
    find ${SCANPATH} -type f -size ${SIZE} -name "[[:alnum:]]*" -and -not -name "Thumbs.db*" -exec md5sum {} 2>/dev/null \; >> ${CHECKSUM_FILE}
done;

# Browse through all MD5 hashes listed in $CHECKSUM_FILE.
rm ${CHANGED_FILES} 2>/dev/null
# Browse through various folders
# Observe the [ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ] which is NOT equivalent to [A-ZÄÖÜ] under the locale de_DE.UTF-8, but only under the locale C.
for SCANPATH in /home/gabriel/[ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ]* \
                /home/joselia/[ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ]*; do
    # Extract user name.
    USER=$(echo ${SCANPATH} | sed -n 's/^\/home\/\([^\/]*\).*/\1/p')
    # Find all normal files starting with an alphanumeric name (skip .files).
    find "${SCANPATH}" -not -path '*/.*' -type f -size ${SIZE} -exec md5sum {} 2>/dev/null \; |
    (while read -r DATASET
     do CHKSUM=$(echo "${DATASET}" | cut -c-32)
        FILEPATH_IN_CHECK=$(echo "${DATASET}" | cut -c35-)
        # Look for the first occurency of an identical checksum in $CHECKSUM_FILE.
        RESULT=$(fgrep -m1 "${CHKSUM}" "${CHECKSUM_FILE}")
        if [ -n "${RESULT}" ]; then
           FILEPATH_REPLACE=$(echo "$RESULT" | cut -c35-)
           # Check if the file that shall be linked to has a generic digikam file name. Those names are like:
           # IMG_0000.JPG or P0000000.JPG
           FNAME_IN_CHECK=${FILEPATH_IN_CHECK##*/}
           PNAME_REPLACE=${FILEPATH_REPLACE%/*}"/"
           FNAME_REPLACE=${FILEPATH_REPLACE##*/}
           if [[ "${FNAME_REPLACE}" =~ ^[PI].*[0-9]\.JPG$ ]] && [ "${FNAME_IN_CHECK}" != "${FNAME_REPLACE}" ]; then
              # Replace the generic digikam name by the file name of the respective user file.
              echo "Umbenennung: ${FILEPATH_REPLACE}  →  ${PNAME_REPLACE}${FNAME_IN_CHECK}" >> ${CHANGED_FILES}
              mv "${FILEPATH_REPLACE}" "${PNAME_REPLACE}${FNAME_IN_CHECK}"
              # Update the variable $FILEPATH_REPLACE as the reference file has been renamed.
              FILEPATH_REPLACE=${PNAME_REPLACE}${FNAME_IN_CHECK}
           fi
           # Write result into a tmp file.
           echo "Link: ${FILEPATH_IN_CHECK}  →  ${FILEPATH_REPLACE}" >> ${CHANGED_FILES}
           # Replace the examined file by a link to the existing file.
           rm "${FILEPATH_IN_CHECK}"
           ln -s "${FILEPATH_REPLACE}" "${FILEPATH_IN_CHECK}"
           chown -h ${USER}:${GROUP} "${FILEPATH_IN_CHECK}"
        fi
     done)
done

# Send an email to $MAIL_RCPT if files have been replaced.
if [ -s "${CHANGED_FILES}" ]; then
   cat ${CHANGED_FILES} | mail -s "${MAIL_SUBJ}" "${MAIL_RCPT}"
fi

In diesem Skript werden zunächst alle Dateien in den öffentlichen Verzeichnissen /home/public/Audio und /home/public/Bilder erfasst. Für jede Datei wird die MD5-Prüfsumme berechnet und in /home/public/Prüfsummen.txt abgelegt.

Dann werden zwei Benutzerverzeichnisse durchsucht, und für die dort gefundenen Dateien, welche größer als eine bestimmte, im Skript festgelegte Mindestgröße sind, wird ebenfalls die MD5-Prüfsumme berechnet. Das Skript vergleicht nun, ob eine solche Prüfsumme schon in der Datei /home/public/Prüfsummen.txt existiert. Wenn dem so ist, dann bedeutet dies, dass die gerade bearbeitete Datei schon auf dem Server existiert. In diesem Fall löscht das Skript die entsprechende Datei im Benutzerverzeichnis und ersetzt sie durch einen Link auf die jeweilige Datei in /home/public/Audio oder /home/public/Bilder. Da es sich bei den meisten mehrfach vorkommenden Dateien um Fotos handelt, prüft das Skript aber noch behelfsmäßig, ob die bereits existierende Datei einen generischen Namen hat, wie er von Digitalkameras kommt, also beispielsweise “P1234567.JPG” oder so. Diese Prüfung ist im hier abgedruckten Skript aber nicht vollkommen, sondern wird nur näherungsweise durchgeführt. Wenn also die bereits existierende Datei in /home/public/Audio oder /home/public/Bilder einen solchen generischen Namen trägt, im Benutzerverzeichnis aber schon umbenannt worden ist (z.B. weil der jeweilige Benutzer nun einen “richtigen” Namen vergeben hat), dann wird dieser Dateiname auch für die jeweilige Datei auf /home/public/Audio oder /home/public/Bilder übernommen, damit auch andere Benutzer wissen, worum es sich bei dem entsprechenden Foto handelt.

Zum Schluss verschickt das Skript dann noch eine Email, in der über die ersetzten und umbenannten Dateien berichtet wird.

Bluetooth-Infobox

Die Bluetooth-Infobox ist ein Skript, welches über einen Eintrag in der crontab die Umgebung nach Geräten mit Bluetooth scannt und dann versucht, Dateien über das OBEX Push-Protokoll an die gefundenen Geräte zu verschicken. Damit kann man eine Art Informations-Box aufbauen, die Dateien automatisch an Mobiltelefone in der näheren Umgebung verschickt. Auf einer Messe ist mir das selbst einmal passiert.

Dazu muss der Server natürlich mit einem USB-Dongle versehen werden. Auf meiner Maschine habe ich einen USB-Bluetooth-Adapter der Klasse 1 von DELOCK (DELOCK 61477) eingesetzt, mit dem ich gute Erfahrungen gemacht habe.

Weiterhin müssen über YaST folgende Software-Pakete installiert werden:

  • bluez-libs
  • bluez-utils
  • libusb-devel
  • openobex-devel
  • yast2-bluetooth

Ferner muss noch das Programm ussp-push von Davide Libenzi compiliert und installiert werden. Die Beschreibung des Programmes und den Link zum Download gibt es auf [3]. Mit ussp-push kann man Dateien über das Object Push-Protokoll [4] verschicken. Wenn man sich das Paket von Davide Libenzis Seite herunterlädt, muss man es nur noch entpacken und dann das Programm entsprechend der dem tar-Archiv beigefügten Anleitung compilieren. Dann sollte man das daraus entstandene Programm an eine zentrale Stelle kopieren, beispielsweise an /usr/sbin/ussp-push.

Jetzt schafft man sich in einem für alle zugänglichen Pfad ein neues Verzeichnis, beispielsweise:

drwxr-sr-x 4 nobody users 128 25. Sep 18:00 /home/public/Bluetooth-Infobox

Ich habe dieses Verzeichnis dem Benutzer nobody zugeordnet, weil das unten abgebildete Skript aus Sicherheitsgründen ebenfalls unter diesem Benutzer läuft. In disem Verzeichnis wird das Skript auch die Log-Dateien anlegen, welche die Ausgaben des ussp-push-Kommandos protokolliert. In diesem Verzeichnis müssen nun zwei Unterverzeichnisse mit entsprechenden Rechten angelegt werden, und zwar:

drwxr-sr-x 2 nobody users 128 26. Sep 21:13 /home/public/Bluetooth-Infobox/database
drwxrwsr-x 2 nobody users  80 25. Sep 15:38 /home/public/Bluetooth-Infobox/outbox

Im Unterverzeichnis outbox können alle Benutzer der Maschine beliebige Dateien ablegen, die dann an die Mobiltelefone verschickt werden sollen, wenn das Skript abgearbeitet wird. Im Unterverzeichnis database wird das Skript später eine Art Datenbank anlegen, in welcher für jedes Mobiltelefon, an das mindestens eine Datei erfolgreich verschickt worden ist, eine Datei existiert, welche die Unix-Zeitstempel und den Namen der verschickten Datei enthält.

Mit diesen Vorbereitungen kann man nun das eigentliche Skript bt_infobox.sh über einen Eintrag in der crontab von nobody aktivieren:

bt_infobox.sh:

#!/bin/bash
#
# Bluetooth Info Box
#
# This script searches for Bluetooth-enabled mobile phones in the proximity, registers their hardware address
# and looks whether there are any new files in the outbox that have not yet been sent to these mobile phones.
# It then sends the missing files to the mobile phones.
#
# Gabriel Rüeck, gabriel@caipirinha.homelinux.org, 25.09.2008
#

# Pre-define some variables and read the configuration files.
readonly WORKDIR='/home/public/Bluetooth-Infobox'

# Set the PATH variable and include /usr/sbin (where ussp-push has been put).
PATH='/usr/sbin:/usr/bin:/bin'

# Scan for Bluetooth-enabled phones in the proximity and extract their machine address.
# Check if a database file exists for the respective HW address.
#   If that is the case, browse through all files in the outbox and see if they have already been sent to that phone.
#      If they have not been sent to that phone, send them and write the file info into the database file.
#   If no database file exists, send all files in the outbox to the phone and write the file info into the database file.
# The database file contains the timestamp and the name of the files that have been sent from the outbox to the respective phone.

hcitool scan | egrep -o '\b[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}\b' | \
        while read HW_ADDRESS
        do if [ -f "${WORKDIR}/database/${HW_ADDRESS}.txt" ]
           then
             cd "${WORKDIR}/outbox" && find . -maxdepth 1 -type f -print 2>/dev/null |
                while read LOCAL_FNAME
                do LONG_FNAME=$(stat -c '%Y %n' "${LOCAL_FNAME}")
                   if (! fgrep -q "${LONG_FNAME}" "${WORKDIR}/database/${HW_ADDRESS}.txt")
                   then REMOTE_FNAME=$(basename "${LOCAL_FNAME}")
                        ussp-push "${HW_ADDRESS}@" "${LOCAL_FNAME}" "${REMOTE_FNAME}" >> "${WORKDIR}/ussp-push.log" 2>&1 && (echo "${LONG_FNAME}" >> "${WORKDIR}/database/${HW_ADDRESS}.txt"; sleep 3s)
                   fi
                done
           else
             cd "${WORKDIR}/outbox" && find . -maxdepth 1 -type f -print 2>/dev/null |
                while read LOCAL_FNAME
                do REMOTE_FNAME=$(basename "${LOCAL_FNAME}")
                   ussp-push "${HW_ADDRESS}@" "${LOCAL_FNAME}" "${REMOTE_FNAME}" >> "${WORKDIR}/ussp-push.log" 2>&1 && (stat -c '%Y %n' "${LOCAL_FNAME}" >> "${WORKDIR}/database/${HW_ADDRESS}.txt"; sleep 3s)
                done
           fi
        done;

Der zugehörige Eintrag in der crontab lautet dann beispielsweise:

# Crontab für nobody
#
LANG=de_DE.UTF-8
LC_ALL=de_DE.UTF-8
MAILTO=root
SHELL=/bin/bash
#
 0 10-23 *  *  *        /home/gabriel/bin/bt_infobox.sh

Damit wird dieses Skript dann täglich von 10:00-23:00 Uhr alle Stunde ausgeführt. Wenn man eine Infobox realisieren will, an der sich die Leute nicht lange aufhalten, wie auf einer Messe, dann sollte man dieses Skript natürlich öfter ausführen lassen.

Wie funktioniert dieses Skript?

Beim Ausführen wird mit dem Kommando hcitool scan nach Bluetooth-fähigen Geräten gefahndet. Dann wird für jedes gefundene Gerät eine Schleife durchlaufen. In diesem Schleife prüft das Skript zunächst, ob für dieses Gerät bereits einmal eine Datei verschickt worden ist. Dazu prüft es, ob im Unterverzeichnis database eine Datei mit dem Namen der Bluetooth-Netzwerkadresse existiert. Ist dies der Fall, dann wird eine weitere Schleife gestartet, welche alle Dateien im Unterverzeichnis outbox durchläuft und prüft, ob eine Datei unter dem jeweiligen Namen und mit dem jeweiligen Unix-Zeitstempel schon einmal verschickt worden ist. Ist dies nicht der Fall, dann wird versucht, diese Datei zu verschicken und bei Erfolg ein Eintrag in der Datenbank gemacht. Die Berücksichtigung des Unix-Zeitstempels ermöglicht es, eine Datei ohne Änderung ihres Dateinamens immer wieder in der outbox zu aktualisieren. Sie wird vom Skript dann als neu erkannt. So etwas macht Sinn für zu verschickende Dateien, die immer wieder unter dem gleichen Namen einen neuen Inhalt bekommen, eventuell durch ein anderes Skript.

Ist ein neues Bluetooth-fähiges Gerät erkannt worden, an das noch nie Dateien verschickt worden ist, dann wird ebenfalls eine Schleife durchlaufen, welche alle Dateien im Unterverzeichnis outbox abarbeitet, diese an das entsprechende Gerät verschickt und dann in die Datenbank schreibt.

Die Schleife mit dem Befehl sleep 3s versucht sicherzustellen, dass beim Versand vieler kleiner Dateien keine Datei beim Versand “verschluckt” wird. Das Skript durchsucht nur eine Ebene im Unterverzeichnis outbox. Es dürfen daher dort keine Unterverzeichnisse angelegt werden.

Hier ist ein Beispiel für Einträge in der Datenbank, also für Dateien im Unterverzeichnis database:

-rw-r--r-- 1 nobody users 25 26. Sep 21:13 /home/public/Bluetooth-Infobox/database/00:16:B8:13:1D:64.txt
-rw-r--r-- 1 nobody users 25 25. Sep 18:00 /home/public/Bluetooth-Infobox/database/00:1E:45:0F:96:32.txt

In einer solchen Datei befinden sich dann Einträge wie:

1166212980 ./Gabriel.jpg

Das zeigt, dass eine Datei namens Gabriel.jpg verschickt worden ist. Die Zahl am Anfang ist der Unix-Zeitstempel. Da es sich bei der Datenbank um Textdateien handelt, wird auf dem Server nicht viel Speicherplatz verbraucht.

Größe eines Mailordners

Dieses einfache und nicht optimierte Skript bestimmt die Postfachgröße eines Benutzers im Ordner /home/public/Mail/ auf dem Caipirinha-Server. Es wird im Rahmen einer snmp-Abfrage zur Postfachgröße benutzt und so aufgerufen:

mailsize.sh Benutzer

mailsize.sh:

#!/bin/bash
# Dieses Skript bestimmt die Größe eines Benutzer-Mailordners.
# Der Benutzername muss als Argument übergeben werden.
# Gabriel Rüeck, 07.01.2010

readonly MAILFOLDER='/home/public/Mail/'

# Prüfe auf das Vorhandensein eines gültigen Arguments.
# Wurde kein Argument geliefert, beende mit Fehlercode=1.
# Wurde kein gültiges Argument geliefert, beende mit Fehlercode=2.
if [ $# -lt 1 ]; then
   exit 1
elif [ ! -d $MAILFOLDER$1 ]; then
   exit 2
else
   du -b --max-depth=0 $MAILFOLDER$1 | cut -f1
   exit 0
fi

Bandbreite zu entfernten Maschinen

Dieses einfache und nicht optimierte Skript ermittelt die Bandbreite einer TCP-Verbindung von caipirinha.homelinux.org zu den Maschinen caipiroska.homelinux.org und rueeck.name. Es wird ebenfalls im Rahmen einer snmp-Abfrage benutzt und so aufgerufen (hier mit rueeck.name als Beispiel-Argument):

bandwidth.sh rueeck.name

bandwidth.sh:

#!/bin/bash
# Dieses Skript ermittelt die Bandbreite einer TCP-Verbindung zum Zielrechner.
# Gabriel Rüeck, 07.07.2012

# Prüfe auf das Vorhandensein eines gültigen Arguments.
# Wurde kein Argument geliefert, beende mit Fehlercode=1.
if [ $# -lt 1 ]; then
   exit 1
fi

# Messe die Bandbreite und berechne den Wert in B/s
bps=$(iperf -f k -x CMSV -c ${1} | sed -n 's/.*Bytes *\([0-9.]\{1,5\} [KM]\)bits.*/\1/p')
value=$(echo ${bps} | cut -f1 -d" ")
decimal=$(echo ${bps} | cut -f2 -d" ")
if [ "${decimal}" = "K" ]; then
   echo $(echo "${value:=0}*125" | bc)
elif [ "${decimal}" = "M" ]; then
   echo $(echo "${value:=0}*125000" | bc)
fi

Auf den entfernten Maschinen muss dann ein iperf-Dienst laufen. Momentan benutze ich dieses Skript aber nicht, weil der iperf-Dienst auf meinen Maschinen häufig zu Problemen geführt hat.

Ping-Zeiten zu entfernten Maschinen

Dieses einfache und nicht optimierte Skript ermittelt die Ping-Zeiten von caipirinha.homelinux.org zu 3 im Skript selbst definierten Maschinen, in diesem Fall caipiroska.homelinux.org, rueeck.name und zu ein kommerzieller VPN-Anbieter. Es kann die Ping-Zeiten auf den offenen und über das VPN selbst ermitteln. Die gewonnenen Daten werden für mrtg-Grafiken benutzt. Das Skript wird so aufgerufen:

pingtimes.sh gw0|gw1|gw2|tun0|tun1|tun2

pingtimes.sh:

#!/bin/bash
# Dieses speziell auf meine Anwendungszwecke abgestimmte Skript ermittelt die Pingzeiten zu meinen VPN-Gateways.
# Gabriel Rüeck, 08.12.2012

# Prüfe auf das Vorhandensein eines gültigen Arguments. Wurde kein Argument geliefert, beende mit Fehlercode=1.
if [ $# -lt 1 ]; then
   exit 1
fi

readonly EVPNLOG='/var/log/openvpn.log'

case "$1" in
     gw0)  DEST='rueeck.name';;
     tun0) DEST=$(ifconfig tun0 2>/dev/null | sed -n 's/.*inet addr:\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.\)[0-9]\{1,3\}.*/\1/p')"1"
             if [ "${DEST}" == "1" ]; then
                exit 2
             fi;;
     gw1)  DEST='caipiroska.homelinux.org';;
     tun1) DEST=$(ifconfig tun1 2>/dev/null | sed -n 's/.*inet addr:\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.\)[0-9]\{1,3\}.*/\1/p')"1"
             if [ "${DEST}" == "1" ]; then
                exit 2
             fi;;
     gw2)  DEST=$(cat ${EVPNLOG} | sed -n 's/.*Peer Connection Initiated with \([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/p' | tail -n1);;
     tun2) DEST=$(ifconfig tun2 2>/dev/null | sed -n 's/.*inet addr:\([0-9]\{1,3\}\.[0-9]\{1,3\}\.\)[0-9]\{1,3\}\.[0-9]\{1,3\}.*/\1/p')"0.1"
             if [ "${DEST}" == "0.1" ]; then
                exit 2
             fi;;
     *)      exit 1;;
esac

# Bestimme die durchschnittliche Ping-Dauer in ms aus 2 Versuchen
PINGTIME=$(ping -c2 -W3 ${DEST} | fgrep "avg" | cut -d"/" -f5 | cut -d"." -f1)
if [ -z "${PINGTIME}" ]; then
   echo "0"
else
   echo ${PINGTIME}
fi

In diesem Skript ist /var/log/openvpn.log die Log-Datei eines kommerziellen Anbieters. Diese Log-Datei muss natürlich auch in der Konfiguration von openvpn entsprechend konfiguriert werden. In der Log-Datei findet sich nach einem erfolgreichen Aufbau der VPN-Verbindung die IP-Adresse des VPN-Servers, und zwar in einer Zeile mit den Begriffen Peer Connection Initiated with. Das ist eine sinnvolle Sache, weil viele kommerzielle VPN-Anbieter mehrere Server zur Auswahl haben oder gar ein Load-Balancing machen, in dessen Rahmen ein FQDN auf verschiedene IPs aufgelöst werden kann. Der Automatismus in diesem Skript macht daher Sinn.

Wenn eine Verbindung blockiert wird, dann gibt es einen Timeout oder eine Fehlermeldung beim Ping-Kommando, und die nachfolgende Verarbeitung mit dem sed-Kommando resultiert dann in einem leeren String. In diesem Fall gibt das Skript eine 0 zurück. Somit sehe ich in der mrtg-Grafik direkt, dass es bei der entsprechenden Verbindung zu Problemen gekommen ist, weil sich dann eine (blaue) Nulllinie einstellt.

VPN-Datenvolumen ermitteln

Dieses einfache und nicht optimierte Skript ermittelt das aufgelaufene VPN-Datenvolumen von caipirinha.homelinux.org zu 3 entfernten Maschinen, zu denen eine openvpn-Verbindung besteht, in diesem Fall die Maschinen caipiroska.homelinux.org, rueeck.name und zu ein kommerzieller VPN-Anbieter. Das Skript wird ebenfalls im Rahmen einer snmp-Abfrage zur Darstellung einer mrtg-Grafik benutzt. Es wird so aufgerufen:

vpn_traffic.sh tun0|tun1|tun2

vpn_traffic.sh:

#!/bin/bash
# Dieses speziell auf meine Anwendungszwecke abgestimmte Skript ermittelt Verkehrsdaten zu meinen VPN-Gateways.
# Gabriel Rüeck, 05.12.2012

# Prüfe auf das Vorhandensein gültiger Argumente. Wurden keine ausreichenden Argumente geliefert, beende mit Fehlercode=1.
if [ $# -lt 2 ]; then
   exit 1
fi

readonly STATUSDIR='/var/run/openvpn/'

case "$1" in
     tun0)  STATUSFILE='status-rueeck';;
     tun1)  STATUSFILE='status-caipiroska';;
     tun2)  STATUSFILE='status-Kommerzielles_VPN';;
     *)     exit 2;;
esac

case "$2" in
     r|read)   SEARCHTERM='TCP/UDP read bytes';;
     w|write)  SEARCHTERM='TCP/UDP write bytes';;
     *)        exit 2;;
esac

# Lese den aufgelaufenen Datenverkehr aus der gewählten Variable aus
cat ${STATUSDIR}${STATUSFILE} | fgrep "${SEARCHTERM}" | cut -d"," -f2

Kommerzielles_VPN muss freilich durch den Hostnamen oder die IP-Adresse des VPN-Gateways eines kommerziellen Anbieters ersetzt werden. Das Skript benutzt die minütlich aktualisierten Stati der openvpn-Verbindungen und nimmt weiterhin an, dass diese Verbindungen auch ihre Stati im Verzeichnis /var/run/openvpn/ ablegen. Die openvpn-Verbindungen müssen demnach auch entsprechend konfiguriert worden sein.

Posted on: 2012-12-08Gabriel Rüeck