Tips & Tricks


Hier werde ich einige kleine Lösungen zu manchen Problemen, die mir irgendwann viel Zeit gekostet haben. Es gibt natürlich immer viele andere Ansätze und Lösungen, die vielleicht besser sind oder schneller. Ich behaupte nicht, dass die Lösungen perfekt sind, es kann sein, dass sie den einen oder anderen Fehler haben, also diese auf eigene Gefahr nutzen!

Vermeidung von Fork Bomben

Wer schon ein GNU/Linux und andere Unix-Like Systeme administriert hat, weiß wie gefährlig eine Fork-Bombe untee GNU/Linux sein kann. In den meisten Distributionen gibt es standardmässig keine Beschränkung für Benutzer, wie viele Kind-Prozesse erzeugt werden dürfen, und damit kann man eine GNU/Linux Maschine in die Knie zwingen, indem man eine Fork-Bombe ausführt. Die bekanntesten Bombem, die man in Foren, Signaturen, usw. findet, sind:

Fork Bombe mit BASH
$ :(){ :|:&};:
# oder
$ perl -e 'while() { fork(); }'

Die 2. Variante ist intuitiv klar, eine Endlosschleife, die fork aufruft. Dabei werden ein Kind-Prozess erzeugt, und beide Prozesse laufen ab der fork-Aufruf-Stelle weiter, also wird das Kind-Prozess ebenso weitere Kind-Prozesse erzeugen, die weiterhin weitere Kind-Prozesse erzeugen werden, usw. Also aus einem Prozess werden 2, dann 4, dann 8, usw. Irgendwann sind so viele Prozesse offen, dass die CPU mit eine 100% Auslastung läuft, es gibt keinen Speicher mehr und der Computer wird in wenigen Sekunden unbenutzbar, weil kein anderer Prozess mehr reagiert oder Kind-Prozesse erzeugen kann.

Die erste Variante sieht ein wenig kryptisch aus, ist sie aber keineswegs. Wer ein wenig BASH Programmierung kennt, wird relativ schnell bemerken, was diese Zeile bewirkt. Es wird eine Funktion definiert wegen () und der Name der Funktion ist :. Der Rumpf der Funktion steht zwischen den geschweiften Klammern, also :|:&. Zuerst wird die Funktion : (rekursiv) aufgerufen, dann wird ein neuer Prozess erzeugt, dessen Standardeingabe mit der Standardausgabe des ersten Aufrufes durch eine Pipe (erkennbar an |) verbunden und anschließend in en Hintergrund geschickt (erkennbar an &), das beduetet, es werden mindestens 3 Prozesse erzeugt, die ebenfalls 3 Prozesse erzeugen, usw. Am Ende des Funktionsrumpfes wird die Funktion : aufgerufen. Das Ergebnis ist dasselbe.

Die Lösung ist ganz einfach, man muss nur die Datei /etc/security/limits.conf um eine Zeile anpassen. Diese Datei enthält eine kleine Dokumentation, wie man sie anpassen kann. Ich hab auf meine Systeme:

Lösung zum Fork-Bombe Problem
# /etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
#<domain>        <type>  <item>  <value>
 
*                hard    nproc           1024

Damit wird gewährleistet, dass kein Benutzer mehr als 1024 Prozesse gleichzeigtig öffnen kann. Damit sind solche Fork-Bomben nicht mehr schädlich, denn die Prozesse irgendwann keine weitere Kind-Prozesse erzeugen können.

Backups

Wer kennt nicht das Problem, dass man ein wenig mit seinen Festplatten und deren Partitionstabellen spielt, oder man installiert andere Betriebsysteme oder nach einem Festplattenausfall ist man ganz erschroken, dass 1. der Rechner nicht mehr bootet, 2. oder die Daten weg sind, 3. oder die Partitionstabelle verschwunden ist, 4. ...

Wer ein Backup hat, kann froh sein, dass man nicht so viel verloren hat, wenn überhaupt. Wer aber kein Backup hat, sitzt einfach da, schweigt und weiß nicht wirklich, was zu tun. Vielleicht schreien? Oder jemanden verdammen? Oder weinen? Leider hilft da nicht viel, außer man kennt ungefähr die Ursache.

Das einzige, was da zuverlässig hilft, sind Backups. Aber warum werden sie nicht in der Regel getan? Weil man denkt, das passiert nur dena anderen, oder man hat keine Zeit, sich damit zu beschäftigen, oder man hat einfach keine Lust, weil man meistens per Hand alles kopieren muss, entpacken, auf CD/DVD Brennen und viele wissen nicht, dass es Programme gibt, die das für einen erledigen.

Ich gehöhe eher der faulen Sorte, und wurde schon 2 Mal wirklich bestraft. Da ich aber keine Lust, ein spezielles Programm zu finden, zu testen, es bedienen zu lernen, usw. hab ich mir einfach ein Paar Skripte geschrieben, die für mich zuverlässig arbeiten und in wenig Schritten benutzt werden können.

backup-restore hilft dabei, Backups zu erstellen und wiederzuspielen. Ich benutze ein einfacher tar mit gzip oder bzip2 Kompression und die resultierende tar.gz bzw. tar.bz2 Datei wird in gleich größe Dateien gesplittet, damit sie einfach gebrannt werden können. Mit backup-restore kann man die gesplittete Datein entpacken.

backup-restore
#!/bin/bash
# Author: Pablo Yanez Trujillo
# creates a tar.gz/tar.bz2 backup and split into files with an equal size
 
# to backup
# backup-restore -b -s size [-t gz] -p prefix path1 path2 [path3 path4 ...]
 
# to restore
# backup-restore -r -p prefix path1 path2 
 
# for gentoo users
 
function ebegin()
{
        echo -e "$@" ...
}
 
function einfo()
{
        echo -e "$@"
}
function ewarn()
{
        echo -e "$@"
}
function eerror()
{
        echo -e "$@"
}
function eend()
{
        local retval=${1:-0}
        shift
        return ${retval}
}
 
# inclduing Gentoo Layout functions
[[ -f /sbin/functions.sh ]] && . /sbin/functions.sh
 
function usage()
{
    [[ -n "$1" ]] && echo "${BAD}!!!${NORMAL} $1"
    echo "Usage: ${HILITE}`basename $0`${NORMAL} ${GOOD}type ${WARN}<options>${NORMAL} path1 path2"
    echo "where ${GOOD}type${NORMAL} is one of"
    echo "${GOOD}-b${NORMAL}        backup"
    echo "${GOOD}-r${NORMAL}        restore"
    echo
    echo "where ${WARN}<options>${NORMAL} is one of"
    echo "${WARN}-s size${NORMAL}       size in bytes of the splitted files to be saved as backup"
    echo "      Use the format of split(1)"
    echo "${WARN}[-t]${NORMAL}      type of backup. Use ${WARN}gz${NORMAL} or ${WARN}bz2${NORMAL}. Default ${WARN}gz${NORMAL}"
    echo "${WARN}-p prefix${NORMAL} prefix of splitted file names"
    echo
    echo "path1: using ${GOOD}-b${NORMAL} is the absolute path of directory where the splitted files have to be saved"
    echo "path2: using ${GOOD}-b${NORMAL} absolute path of file/dir. to be backuped"
    echo
    echo "path1: using ${GOOD}-r${NORMAL} is the absolute path where the splitted file are saved"
    echo "path2: using ${GOOD}-r${NORMAL} is the absolute path where the backup has to be restored"
    echo
    echo "Examples:"
    echo "To backup the /home directory in /export/backup/home with max. file size 1GB"
    echo "${HILITE}`basename $0`${NORMAL} ${GOOD}-b ${WARN}-s 1000m -t bz2 -p home-\`date +%d.%m.%y\`${NORMAL} /export/backup/home /home"
    echo
    echo "To restore the backup of 03.06.05 of /export/backup/home/home-03.06.05* in /home"
    echo "${HILITE}`basename $0`${NORMAL} ${GOOD}-r ${WARN}-p home-03.06.05 -t bz2 ${NORMAL} /export/backup/home /home"
    echo
    echo "Comments and bugs to Pablo Yanez Trujillo <yanezp@informatik.uni-freiburg.de>"
}
 
TAR_TYPE="gz"
 
while getopts s:t:p:rb c; do
    case ${c} in
        b)
            TYPE="backup"
            ;;
        r)
            TYPE="restore"
            ;;
        s)
            SIZE="${OPTARG}"
            ;;
        t)
            if [ "${OPTARG}" != "gz" ] && [ "${OPTARG}" != "bz2" ] ; then
                usage "Unkown compress type ${OPTARG}"
                exit 1
            fi
 
            TAR_TYPE="${OPTARG}"
            ;;
        p)
            PREFIX="${OPTARG}"
            ;;
        ?)
            usage "option -${c} ${OPTARG} unknown"
            exit 1
            ;;
        *)
            usage "unknown error"
            exit 1
            ;;
    esac
done
 
if [ -z "${TYPE}" ] ; then
    usage "Use -b/-r options"
    exit 1
fi
 
if [ -z "${PREFIX}" ] ; then
    usage "Use -p option to set the prefix"
    exit 1
fi
 
if [ -z "${SIZE}" ] && [ "${TYPE}" == "backup" ] ; then
        usage "Use -s option to set the size"
        exit 1
fi
 
shift `expr ${OPTIND} - 1`
 
if [ -z "$1" ] || [ -z "$2" ] ; then
    usage "No paths given"
    exit 1
fi
 
if [ "${TYPE}" == "backup" ] ; then
    if [ "${TAR_TYPE}" == "gz" ] ; then
        TAR_COMMAND="tar cpzO"
        SPLIT_END=".tar.gz."
    else
        TAR_COMMAND="tar cpjO"
        SPLIT_END=".tar.bz2."
    fi
 
    SPLIT_PATH="$1/${PREFIX}${SPLIT_END}"
 
    cd $2
 
    ${TAR_COMMAND} . | split -d -b ${SIZE} - ${SPLIT_PATH}
else
    if [ "${TAR_TYPE}" == "gz" ] ; then
                TAR_COMMAND="tar xpz"
                SPLIT_END=".tar.gz."
        else
                TAR_COMMAND="tar xpj"
                SPLIT_END=".tar.bz2."
        fi
 
    SPLIT_PATH="$1/${PREFIX}${SPLIT_END}"
 
    COUNT=`ls ${SPLIT_PATH}* 2>/dev/null | wc -w`
 
    if [ "$COUNT" == "0" ] ; then
        eerror "There are not backup files in $1"
        exit 1
    fi
 
    cd $2 || exit 1
 
    COUNT=`expr ${COUNT} - 1`
 
    (for i in `seq 0 ${COUNT}` ; do
        cat $SPLIT_PATH`printf "%0.2d\n" ${i}`
    done) | ${TAR_COMMAND}
fi

Beispiele: Um /home in /export/bak/home zu sichern in Dateien mit max. 1GB Größe:

backup ertsellen
$ backup-restore -b -s 1000m -p home-`date +%d.%m.%y` /export/bak/home /home

Somit werden (Bsp: am 01.06.06) Dateien in /export/bak/home mit den Namen home-01.06.06.tar.gz.00, home-01.06.06.tar.gz.01, home-01.06.06.tar.gz.02, usw. gesichert. Um irgendwann diese Dateien wiederzuspielen:

backup wiederspielen
$ backup-restore -r -p home-01.06.06 /export/bak/home /home

Download

ISO Dateien schnell erstellen und brennen

Manchmal will man sehr schnell eine ISO Datei erstellen und brennen, ohne aufwändige Brennprogramme wie K3B starten zu müssen. Daher habe ich mir 2 Skripte geschrieben, die das Ermöglichen: makeiso

makeiso
#!/bin/bash
# Author: Pablo Yanez Trujillo
 
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] ; then
    echo "usage: `basename $0` directory_path ISO_filename Description"
    exit 1
fi
 
mkisofs -volid "$3" \
    -o $2 \
    -appid "makeiso shell script" \
    -publisher "Pablo Yanez Trujillo" \
    -preparer "Pablo Yanez Trujillo" \
    -sysid "GNU-LINUX" -J -R $1

und burniso

burniso
#!/bin/bash
# burniso - burns ISO images
# Author: Pablo Yanez Trujillo
 
# color list
# GOOD (green), WARN (yellow), BAD (red), HILITE (cyan), BRACKET (blue), NORMAL (xterm default)
 
CDRECORD="/usr/bin/cdrecord"
GROWISOFS="/usr/bin/growisofs"
 
function ebegin()
{
        echo -e "$@" ...
}
 
function einfo()
{
        echo -e "$@"
}
function ewarn()
{
        echo -e "$@"
}
function eerror()
{
        echo -e "$@"
}
function eend()
{
        local retval=${1:-0}
        shift
        return ${retval}
}
 
# inclduing Gentoo Layout functions
[[ -f /sbin/functions.sh ]] && . /sbin/functions.sh
 
function usage()
{
cat << EOF
${BAD}!!!${NORMAL} $@
usage: ${HILITE}`basename $0` ${GOOD}type ${WARN}isofile ${BRACKET}[${BAD}-r${BRACKET}]${NORMAL}
where ${GOOD}type${NORMAL} is one of
${GOOD}cd${NORMAL}      burns a CD ISO image. Together with the option
        ${BAD}-r${NORMAL} `basename $0` clears the CD-RW disc before
        burning the ISO image
${GOOD}dvd${NORMAL}     burns a DVD ISO image
 
Report bugs to Pablo Yanez Trujillo <yanezp@informatik.uni-freiburg.de>
EOF
}
 
function burn()
{
    case "$1" in
        cd)
        echo ${CDRECORD} -v gracetime=2 dev=/dev/hdc speed=52 -dao driveropts=burnfree -eject -data $2
        ;;
 
        dvd)
        ${GROWISOFS} -Z /dev/hdc=$2 -dvd-compat ; eject /dev/hdc
        ;;
 
        *)
        echo false
    esac
}
 
function blank_cd()
{
    echo ${CDRECORD} dev=/dev/hdc blank=fast
}
 
 
if [ -z "$1" ] ; then
    usage "No type or unknown type given"
    exit 1
fi
 
CMDLINE=true
 
case "$1" in
    cd)
        if [ -z "$2" ] ; then
            usage "iso filename not given"
            exit 1
        fi
        if [ -n "$3" ] ; then
            if [ "$3" == "-r" ] ; then
                CMDLINE="`blank_cd`"
            else
                usage "unknown option for type \"cd\""
                exit 1
            fi
        fi
 
        ERR=true
 
        ebegin "Ereasing the cd-rw"
        ${CMDLINE}
        eend $? "Cannot erease the cd-rw" || exit $?
 
        CMDLINE="`burn $1 $2`"
 
        ebegin "Burning $2. Please wait"
        ${CMDLINE}
        eend $? "Cannot burn $2" || exit $?
        ;;
    dvd)
        if [ -z "$2" ] ; then
            usage "iso filename not given"
            exit 1
        fi
 
        ebegin "Burning $2. Please wait"
        burn $1 $2
        eend $? "Cannot burn $2" || exit $?
        ;;
    *)
        usage "Unknown type"
        exit 1
        ;;
esac

Wenn man die GNU Bash als Shelll benutzt, kann man auch die Autocompletion Funktion dieser Shell benutzen, um burniso mit der Autocompletion Funktion zu benutzen. Folgendes muss geladen werden

BASH
#   bash_completion - programmable completion functions for bash 3.x
#                     (backwards compatible with bash 2.05b)
# bash command-line completion for burniso
# Author: Pablo Yanez Trujillo
 
_burniso()
{
    local cur
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
 
    if [[ $COMP_CWORD -eq 1 ]]; then
        COMPREPLY=($(compgen -W 'cd dvd' ${cur}))
    else
        case "${COMP_WORDS[1]}" in
            cd)
                if [ -z "${COMP_WORDS[3]}" ] && [ ! "${COMP_WORDS[2]%.iso}" != "${COMP_WORDS[2]}"  ] ; then
                    COMPREPLY=($(compgen -W '$(for i in *.iso; do echo ${i##*/}; done)' ${cur}))
                elif [ -z "${COMP_WORDS[3]}" ] ; then
                    COMPREPLY=($(compgen -W '-r' ${cur}))
                fi
                ;;
            dvd)
                if [ ! "${COMP_WORDS[2]%.iso}" != "${COMP_WORDS[2]}"  ] ; then
                    COMPREPLY=($(compgen -W '$(for i in *.iso; do echo ${i##*/}; done)' ${cur}))
                fi
                ;;
        esac
    fi
}
 
complete -F _burniso burniso

Beispiele: Um den Inhalt des Verzeichnisses abc in eine ISO Datei packen und anschließend diese Datei brennen

ISO erstellen und brennen
# Um die ISO zu erzeugen
$ makeiso abc/ abc.iso "Meine CD ABC"
 
# wenn abc.iso eine CD ISO ist
 
$ burniso cd abc.iso
 
# wenn abc.iso eine CD ISO ist
# und soll auf eine CD-RW schreiben,
# die gelöscht werden soll
 
$ burniso cd abc.iso -r
 
# wenn abc.iso eine DVD ISO ist
 
$ burniso dvd abc.iso

Download

Valid XHTML 1.0 Strict   Valid CSS!