Kategorien

Virtualbox und Intrepid – das perfekte Setup – Netzwerk

Nach der Installation von Virtualbox erhält man zunächst folgende Fehlermeldung: „Could not load the Host USB Proxy Service (VERR_FILE_NOT_FOUND). The service might be not installed on the host computer. Fehlercode:  NS_ERROR_FAILURE (0x00004005)“. Damit USB wie gewünscht funktioniert und die Fehlermeldung verschwindet muss man unter Intrepid folgende Zeile in /etc/fstab einfügen:

none	/proc/bus/usb	usbfs	devgid=46,devmode=775	0	0

Als nächstes machen wir uns an die Netzwerkeinstellungen. Für die virtuellen Maschinen möchte ich tap Interfaces nutzen, die über eine Proxy ARP Bridge in mein Netzwerk eingebunden werden und per dhcp eine IP zugewiesen bekommen sollen. Die Proxy ARP Bridge hat den Vorteil, dass sie auch in einem drahtlosen Netzwerk funktioniert, wo eine normale Bridge mit brctl Probleme verursacht. Benötigt werden dazu die Programme parprouted und bcrelay. Gilbert Mendoza beschreibt ausführlich, wie man dieses Setup manuell einrichten kann. Zunächst müssen wir verschiedene Programme installieren:

sudo apt-get install parprouted uml-utilities bcrelay

Damit bei jedem Rechnerstart die Netzwerkinterfaces automatisch erstellt werden, habe ich ein Skript gebastelt, das die entsprechenden Netzwerkinterfaces, z.B. tap0 bereitstellt. Dieses Skript basiert auf der Vorarbeit von Jochem Kossen und der Anleitung von Dominic Edmonds. Zur Einrichtung müssen wir zunächst die Konfigurationsdatei erstellen und einrichten:

sudo mkdir /etc/virtualbox
sudo nano /etc/virtualbox/config

Diese Datei hat bei mir folgenden Inhalt:

HOST_IF="eth1"
HOST_IP="192.168.0.64"
VM_USER="markus"
USE_NAT="no"
TAPS="tap0 tap1"
USE_ARPBRIDGE="yes"
ARP_IPS="169.254.0.1/32 169.254.0.2/32"

Die Datei config muss nun natürlich angepasst werden. HOST_IF bezeichnet das Netzwerkinterfaces, mit dem die virtuellen Systeme verbunden werden sollen, die HOST_IP ist die entsprechene IP-Adresse des Hosts. Als VM_USER muss der Benutzer eingetragen werden, der die virtuellen Maschinen nutzt. USE_NAT wird auf no gesetzt, da ein Anbindung über NAT bei Proxy ARP nicht notwendig ist. Unter TAPS werden die zu erzeugenden tap-Netzwerkinterfaces durch ein Leerzeichen getrennt aufgelistet. USE_ARPBRIDGE setzen wir auf yes, denn wir wollen eine Proxy ARP Bridge erzeugen. Unter ARP_IPS listen wir die zu den tap-Interfaces zugehörigen IP-Adressen auf. Dazu muss man allerdings wissen, dass diese IP-Adressen keinerlei Bedeutung haben und eigentlich völlig beliebig sind. Man sollte jedoch darauf achten, dass die vergebenen IP-Adressen nicht mit denen im Netzwerksetup im Konflikt stehen. Ich verwende deshalb die oben angegebenen Microsoft IP-Adressen, die eigentlich von Windows vergeben werden, wenn kein DHCP-Server eine IP-Adresse zuweist.
Im nächsten Schritt erzeugen wir eine weitere Konfigurationsdatei, die die Namen derjenigen virtuellen Systeme enthält (jedes in einer Zeile), die automatisch gestartet werden sollen, wenn das System hochfährt. Ich persönlich habe hierauf verzichtet und erzeuge deshalb eine leere Datei.

sudo touch /etc/virtualbox/machines_enabled

Nun erzeugen wir das eigentlich Skript vboxcontrol, das die gesamte Arbeit erledigt mit

sudo nano /etc/init.d/vboxcontrol

Die Datei füllen wir mit folgendem Inhalt (alternativ kann man das Script herunterladen):

#! /bin/sh
### BEGIN INIT INFO
# Provides: virtualbox_vms
# Required-Start:    $local_fs $syslog $remote_fs
# Required-Stop:     $local_fs $syslog $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Control VirtualBox Virtual Machine instances
### END INIT INFO
#
# Modified by Markus Effinger, http://www.effinger.org
# based on
# Version 2008051100 by Jochem Kossen <jochem.kossen@gmail.com>
# http://farfewertoes.com
#
# Released in the public domain
#
# This file came with a README file containing the instructions on how
# to use this script.
#
 
. /lib/lsb/init-functions
 
# Are we running from init?
run_by_init() {
    ([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}
 
################################################################################
# INITIAL CONFIGURATION
VBOXDIR="/etc/virtualbox"
VM_USER="root"
USE_NAT="yes"
USE_ARPBRIDGE="no"
 
export PATH="${PATH:+$PATH:}/bin:/usr/bin:/usr/sbin:/sbin"
 
if [ -f $VBOXDIR/config ]; then
    . $VBOXDIR/config
else
    echo "ERROR: $VBOXDIR/config does not exist. Exiting."
    exit 1
fi
 
SU="su $VM_USER -c"
VBOXMANAGE="VBoxManage -nologo"
PARPROUTEDBIN=`which parprouted`
BCRELAYBIN=`which bcrelay`
 
################################################################################
# FUNCTIONS
 
# Determine if USE_NAT is set to "yes"
use_nat() {
    if [ "$USE_NAT" = "yes" ]; then
        return `true`
    else
        return `false`
    fi
}
 
# Determine if USE_ARPBRIDGE is set to "yes"
use_arpbridging() {
    if [ "$USE_ARPBRIDGE" = "yes" ]; then
        return `true`
    else
        return `false`
    fi
}
 
# Bring up the bridge interface
enable_bridge() {
    # If NAT is enabled, don't do anything
    use_nat && return
 
    # If ARP Bridge is enabled, don't do anything
    use_arpbridging && return
 
    # Load the tun module
    if [ ! -e /dev/net/tun ]; then
        modprobe tun
    fi
 
    brctl addbr br0 || /bin/true
 
    # Disable $HOST_IF; host will use br0 instead
    ifdown $HOST_IF
    ifconfig $HOST_IF 0.0.0.0 promisc
    brctl addif br0 $HOST_IF
 
    # Bring up br0
    ifup br0
 
    # Answer ARP requests for $HOST_IP (which now come on br0) with the MAC
    # address of $HOST_IF
    arp -Ds $HOST_IP $HOST_IF pub
}
 
# Bring down the bridge interface
disable_bridge() {
    # If NAT is enabled, don't do anything
    use_nat && return
 
    ifdown br0
    brctl delbr br0
    ifup $HOST_IF
}
 
# Activate tap interfaces
enable_taps() {
    # If NAT is enabled, don't do anything
    use_nat && return
 
    # If ARP Bridge is enabled, don't do anything
    use_arpbridging && return
 
    for TAP in $TAPS; do
        # Check if $TAP is configured already
        ifconfig $TAP > /dev/null 2>&1
        if [ $? != 0 ]; then
            tunctl -t $TAP -u $VM_USER
            brctl addif br0 $TAP
 
            # Disable tap interfaces for host; guest will activate them for themselves
            ifconfig $TAP 0.0.0.0 promisc
 
            # Enable proxy_arp so that ARP requests can be answered correctly
            # by the host
            sysctl net.ipv4.conf.$TAP.proxy_arp=1
 
            # Add a route for the tap device
            route add -host $HOST_IP dev $TAP
        else
            log_failure_msg "Interface $TAP already configured"
        fi
    done
}
 
# Activate tap interfaces for ARP Bridging
enable_taps_arp() {
    # If ARP Bridge is disabled, don't do anything
    ! use_arpbridging && return
 
    # Enable routing that pakets can be transfered from $TAP to $HOST_IF
    # by the host
    sysctl net.ipv4.ip_forward=1
 
    # i contains the index of the current TAP device in the array of all TAP devices
    i=0
    for TAP in $TAPS; do
        # make sure that ARP_IP contains the corresponding IP of the interface TAP
        # j contains the index of the ip address in the array of all ip addresses
        j=0
        for ARP_IP in $ARP_IPS; do
           if [ $i = $j ]; then
              break
           fi
           j=$(( $j + 1 ))
        done
        i=$(( $i + 1 ))
 
        # Check if $TAP is configured already
        ifconfig $TAP > /dev/null 2>&1
        if [ $? != 0 ]; then
            tunctl -t $TAP -u $VM_USER
 
            # Disable tap interfaces for host; guest will activate them for themselves
            #ifconfig $TAP 0.0.0.0 promisc
            ip link set $TAP up
            ip addr add $ARP_IP dev $TAP
 
            # Enable proxy_arp so that ARP requests can be answered correctly
            # by the host
            sysctl net.ipv4.conf.$TAP.proxy_arp=1
 
            # Start parprouted for Proxy ARP Bridging
            start-stop-daemon --background --start --quiet --pidfile /var/run/parprouted.$TAP.pid \
               --exec $PARPROUTEDBIN -- $HOST_IF $TAP
 
            # Start relaying broadcasts - required for dhcp to work,
            # do not use the -d switch for bcrelay - start-stop-daemon would not work then
            start-stop-daemon --background --start --quiet --make-pidfile --pidfile /var/run/bcrelay.$TAP.pid \
               --exec $BCRELAYBIN -- -n -i $TAP -o $HOST_IF
 
        else
            log_failure_msg "Interface $TAP already configured"
        fi
    done
}
 
# Disable/deconfigure tap interfaces for ARP Bridging
disable_taps_arp() {
    # If ARP Bridge is disabled, don't do anything
    ! use_arpbridging && return
 
    #Delete all created tap interfaces and kill corresponding processes
    for TAP in $TAPS; do
        start-stop-daemon --stop --quiet --pidfile /var/run/parprouted.$TAP.pid \
           --exec $PARPROUTEDBIN
        start-stop-daemon --stop --quiet --pidfile /var/run/bcrelay.$TAP.pid \
           --exec $BCRELAYBIN
        tunctl -d $TAP
    done
}
 
# Disable/deconfigure tap interfaces
disable_taps() {
    # If NAT is enabled, don't do anything
    use_nat && return
 
    # If ARP Bridge is enabled, don't do anything
    use_arpbridging && return
 
    for TAP in $TAPS; do
        route del -host $HOST_IP dev $TAP
        brctl delif br0 $TAP
        tunctl -d $TAP
    done
}
 
# Check for running machines every few seconds; return when all machines are
# down
wait_for_closing_machines() {
    RUNNING_MACHINES=`$SU "$VBOXMANAGE list runningvms" | wc -l`
    if [ $RUNNING_MACHINES != 0 ]; then
        sleep 5
        wait_for_closing_machines
    fi
}
 
################################################################################
# RUN
case "$1" in
    start)
        if [ -f /etc/virtualbox/machines_enabled ]; then
            if [ ! use_nat ] && [ ! use_arpbridging ]; then
                enable_bridge
                enable_taps
 
                chown root.vboxusers /dev/net/tun
                chmod 0660 /dev/net/tun
            fi
 
            if [ use_arpbridging ]; then
                enable_taps_arp
                chown root.vboxusers /dev/net/tun
                chmod 0660 /dev/net/tun
            fi
 
            cat /etc/virtualbox/machines_enabled | while read VM; do
                log_action_msg "Starting VM: $VM ..."
                $SU "$VBOXMANAGE startvm \"$VM\" -type vrdp"
            done
        fi
        ;;
    stop)
        # NOTE: this stops all running VM's. Not just the ones listed in the
        # config
        $SU "$VBOXMANAGE list runningvms" | while read VM; do
            log_action_msg "Shutting down VM: $VM ..."
            $SU "$VBOXMANAGE controlvm \"$VM\" acpipowerbutton"
        done
 
        wait_for_closing_machines
 
        if [ ! use_nat ] && [ ! use_arpbridging ]; then
            disable_taps
            disable_bridge
        fi
 
        if [ use_arpbridging ]; then
            disable_taps_arp
        fi
        ;;
    bridge-up)
        enable_bridge
        ;;
    bridge-down)
        disable_bridge
        ;;
    taps-up)
        enable_taps
        ;;
    taps-down)
        disable_taps
        ;;
    taps-arp-up)
        enable_taps_arp
        ;;
    taps-arp-down)
        disable_taps_arp
        ;;
    start-vm)
        log_action_msg "Starting VM: $2 ..."
        $SU "$VBOXMANAGE startvm \"$2\" -type vrdp"
        ;;
    stop-vm)
        log_action_msg "Stopping VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" acpipowerbutton"
        ;;
    poweroff-vm)
        log_action_msg "Powering off VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" poweroff"
        ;;
    status)
        log_action_msg "The following virtual machines are currently running:"
        $SU "$VBOXMANAGE list runningvms" | while read VM; do
            echo -n "$VM ("
            echo -n `$SU "VBoxManage showvminfo $VM|grep Name:|sed -e 's/^Name:\s*//g'"`
            echo ')'
        done
        ;;
    *)
        log_failure_msg "Usage: $0 {start|stop|status|start-vm <VM name>|stop-vm <VM name>|poweroff-vm <VM name>|bridge-up|bridge-down|taps-up|taps-down}"
        exit 3
esac
 
exit 0

Nach dem Speichern der Datei setzen wir noch entsprechende Dateiberechtigungen und sorgen dafür, dass dieses Skript in den Startvorgang des Systems aufgenommen wird:

sudo chown root.root /etc/init.d/vboxcontrol
sudo chmod 755 /etc/init.d/vboxcontrol
sudo update-rc.d vboxcontrol defaults 99 10

Damit wir in Virtualbox gleich loslegen können, starten wir das Skript durch direkten Aufruf mit

sudo /etc/init.d/vboxcontrol start

Bei der virtuellen Maschine passen wir unter dem Punkt Netzwerk folgende Punkte an:
Angeschlossen an: Hostinterface
Name des Interfaces: tap0 (also eines der Interfaces, welches oben unter TAPS angegeben wurde)

Sehr praktisch an dem vboxcontrol Skript ist, dass beim Herunterfahren des Systems oder dem Aufruf des Skripts mit dem Parameter stop alle laufenden virtuellen Maschinen ordnungsgemäß heruntergefahren werden. Das Wort alle habe ich deswegen kursiv hervorgehoben, da nicht nur die
/etc/virtualbox/machines_enabled gelisteten Maschinen heruntergefahren werden.

Arbeitserleichterung durch .bashrc und .bash_aliases

Eine derjenigen Dateien, die man auf jeden Fall an seine persönlichen Bedürfnisse anpassen sollte, ist die .bashrc. Diese Datei ermöglicht es unter anderem, den Prompt der Bash-Shell zu setzen und eigene Funktionen sowie Aliasnamen für häufig benutzte Befehle festzulegen. Meine bashrc sieht aktuell so aus und enthält Code-Schnipsel von John Lawrence, geekosphere.org und Isaac Schlueter:

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
 
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
 
# don't put duplicate lines in the history. See bash(1) for more options
export HISTCONTROL=ignoredups
# ... and ignore same sucessive entries.
export HISTCONTROL=ignoreboth
 
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
 
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
 
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi
 
function jobcount {
	jobs | wc -l | tr -d " "
}
 
# Color prompt
bash_color_prompt() {
 
	local BLACK="\[\e[0;30m\]"
	local BLUE="\[\e[0;34m\]"
	local GREEN="\[\e[0;32m\]"
	local CYAN="\[\e[0;36m\]"
	local RED="\[\e[0;31m\]"
	local PURPLE="\[\e[0;35m\]"
	local BROWN="\[\e[0;33m\]"
	local LIGHTGRAY="\[\e[0;37m\]"
	local DARKGRAY="\[\e[1;30m\]"
	local LIGHTBLUE="\[\e[1;34m\]"
	local LIGHTGREEN="\[\e[1;32m\]"
	local LIGHTCYAN="\[\e[1;36m\]"
	local LIGHTRED="\[\e[1;31m\]"
	local LIGHTPURPLE="\[\e[1;35m\]"
	local YELLOW="\[\e[1;33m\]"
	local WHITE="\[\e[1;37m\]"
	# No Color
	local NC="\[\e[0m\]"
	local cur_tty=$(tty | sed -e "s/.*tty\(.*\)/\1/")
	local loadavg='$(uptime | sed -e "s/.*load average: \(.*\...\), \(.*\...\), \(.*\...\)/\1/" -e "s/ //g")'
 
	PS1="${LIGHTRED}\u${NC}@${LIGHTBLUE}\h${NC}-${GREEN}$(date +"%d.%m.%Y %H:%M:%S")${NC}:${YELLOW}\w\n${BROWN}[j:`jobcount`, t:$cur_tty, l:$loadavg]${WHITE}\n\\$ ${NC}"
}
 
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac
 
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes
 
if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
	# We have color support; assume it's compliant with Ecma-48
	# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
	# a case would tend to support setf rather than setaf.)
	color_prompt=yes
    else
	color_prompt=
    fi
fi
 
if [ "$color_prompt" = yes ]; then
    #PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
	bash_color_prompt
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
 
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\007"'
    ;;
*)
    ;;
esac
 
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
 
if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi
 
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi
 
#Sicherstellen, dass Sprache deutsch und Zeitzone für Deutschland passt
export LC_CTYPE=de_DE.UTF-8
export LC_ALL=""
export LANG=$LC_CTYPE
export LANGUAGE=$LANG
export TZ=Europe/Berlin
 
################## Start functions from http://www.perlmonks.org/?viewmode=public;node_id=359168 ###################################
# path-manipulating shell functions
 
function lspath {
    echo $PATH | tr ':' '\n'
}
 
function makepath {
    unset NEW_PATH
    while read PATHEL; do
        NEW_PATH="$NEW_PATH:$PATHEL"
    done
    echo ${NEW_PATH#:}
}
 
function uniqpath {
    PATH=`lspath | awk '{seen[$0]++; if (seen[$0]==1){print}}' | makepath`
}
 
function cleanpath {
    uniqpath
    PATH=`lspath | sed -e 's|\/$||' -ne '/./p' | makepath`
}
 
function addpath {
    PATH=$1:$PATH
    cleanpath
}
 
function appendpath {
    PATH=$PATH:$1
    cleanpath
}
 
function delpath {
    PATH=`lspath | grep -v "^$1\$" | makepath`
}
 
function editpath {
    TEMP=`mktemp "/tmp/${FUNCNAME}.XXXXXX"`
    lspath > $TEMP
    ${EDITOR:-vi} $TEMP
    PATH=`makepath < $TEMP`
    rm -f $TEMP
}
################## End functions from http://www.perlmonks.org/?viewmode=public;node_id=359168 ###################################
 
################## Begin functions from http://www.johnlawrence.net/code/?f=sfbashrc ###################################
 
#compare files using comm (requires perl) - http://www.shell-fu.org/lister.php?id=186
compare(){
  comm $1 $2 | perl -pe 's/^/1: /g;s/1: \t/2: /g;s/2: \t/A: /g;' | sort
}
 
# overwrite a file with zeroes - http://www.shell-fu.org/lister.php?id=94
zero() {
  case "$1" in
    "")     echo "Usage: zero "
            return -1;
  esac
  filesize=`wc -c  "$1" | awk '{print $1}'`
  dd if=/dev/zero of=$1 count=$filesize bs=1
}
 
# create a terminal calculator - http://www.shell-fu.org/lister.php?id=216
calc(){ echo "${1}"|bc -l; }
 
# copy and paste from the command line - http://www.shell-fu.org/lister.php?id=177
ccopy(){ cp $1 /tmp/ccopy.$1; }
alias cpaste="ls /tmp/ccopy* | sed 's|[^\.]*.\.||' | xargs -I % mv /tmp/ccopy.% ./%"
 
# bash function to decompress archives - http://www.shell-fu.org/lister.php?id=375
extract () {
    if [ -f $1 ] ; then
        case $1 in
            *.tar.bz2)   tar xvjf $1        ;;
            *.tar.gz)    tar xvzf $1     ;;
            *.bz2)       bunzip2 $1       ;;
            *.rar)       unrar x $1     ;;
            *.gz)        gunzip $1     ;;
            *.tar)       tar xvf $1        ;;
            *.tbz2)      tar xvjf $1      ;;
            *.tgz)       tar xvzf $1       ;;
            *.zip)       unzip $1     ;;
            *.Z)         uncompress $1  ;;
            *.7z)        7z x $1    ;;
            *)           echo "'$1' cannot be extracted via >extract<" ;;
        esac
    else
        echo "'$1' is not a valid file"
    fi
}
################## End functions from http://www.johnlawrence.net/code/?f=sfbashrc ###################################
 
################## Begin functions from http://foohack.com/tests/bash_extras/.extra.bashrc ###################################
 
# chooses the first argument that matches a file in the path.
choose_first () {
	for i in "$@"; do
		if ! [ -f "$i" ] && inpath "$i"; then
			i="`which $i`"
		fi
		if [ -f "$i" ]; then
			echo $i
			break
		fi
	done
}
inpath () {
	! [ $# -eq 1 ] && echo "usage: inpath <file>" && return 1
	f="`which $1 2>/dev/null`"
	[ -f "$f" ] && return 0
	return 1
}
 
# headless <command> [<key>]
# to reconnect, do: headless "" <key>
headless () {
	if [ "$2" == "" ]; then
		hash=`md5 -qs "$1"`
	else
		hash="$2"
	fi
	if [ "$1" != "" ]; then
		dtach -n /tmp/headless-$hash bash -l -c "$1"
	else
		dtach -A /tmp/headless-$hash bash -l
	fi
}
 
# function to do something in the background
back () {
	( $@ ) &
}
# do something very quietly.
quiet () {
	( $@ ) >/dev/null 2>/dev/null
}
 
# useful commands:
_set_editor () {
	edit_cmd="`choose_first $@`"
	if [ -f "$edit_cmd" ]; then
		if [ -f "${edit_cmd}_wait" ]; then
			export EDITOR="${edit_cmd}_wait"
		else
			export EDITOR="$edit_cmd"
		fi
	fi
	alias edit="$edit_cmd"
	alias sued="sudo $edit_cmd"
}
# my list of editors, by preference.
_set_editor mate vim vi pico ed
 
# shebang <file> <program> [<args>]
shebang () {
	sb="shebang"
	if [ $# -lt 2 ]; then
		echo "usage: $sb <file> <program> [<arg string>]"
		return 1
	elif ! [ -f "$1" ]; then
		echo "$sb: $1 is not a file."
		return 1
	fi
	if ! [ -w "$1" ]; then
		echo "$sb: $1 is not writable."
		return 1
	fi
	prog="$2"
	! [ -f "$prog" ] && prog="`which $prog 2>/dev/null`"
	if ! [ -x "$prog" ]; then
		echo "$sb: $2 is not executable, or not in path."
		return 1
	fi
	chmod ogu+x "$1"
	prog="#!$prog"
	[ "$3" != "" ] && prog="$prog $3"
	if ! [ "`head -n 1 \"$1\"`" == "$prog" ]; then
		contents="`cat \"$1\"`"
		newcontents=`cat <<ENDSHEBANG
$prog
$contents
ENDSHEBANG`
		echo -n "$newcontents" > $1
	fi
	return 0
}
 
rand () {
	# r=``
	# echo $r
	echo `php -r 'echo rand();'`
}
 
pickrand () {
	cnt=0
	if [ $# == 1 ]; then
		tst=$1
	else
		tst="-d"
	fi
	for i in *; do
		[ $tst "$i" ] && let 'cnt += 1'
	done
	r=`rand`
	p=0
	[ $cnt -eq 0 ] && return 1
	let 'p = r % cnt'
	# echo "[$cnt $r --- $p]"
	cnt=0
	for i in *; do
		# echo "[$cnt]"
		[ $tst "$i" ] && let 'cnt += 1' && [ $cnt -eq $p ] && echo "$i" && return
	done
}
 
# a friendlier delete on the command line
! [ -d ~/.Trash ] && mkdir ~/.Trash
chmod 700 ~/.Trash
alias emptytrash="find ~/.Trash -not -path ~/.Trash -exec rm -rf {} \; 2>/dev/null"
if ! inpath del; then
	if [ -d ~/.Trash ]; then
		del () {
			for i in "$@"; do
				mv "$i" ~/.Trash/
			done
		}
	else
		alias del=rm
	fi
fi
 
# find files in current dir by name (not in package dirs)
f () {
	find . -name "$1" -not -path "*/rhel.*.*.package/*" -not -path "*/CVS/*" -not -path "*/CVS" -not -path "*/rhel.*.*.package"
}
 
getip () {
	for each in $@; do
		echo $each
		echo "nslookup:"
		nslookup $each | grep Address: | grep -v '#' | egrep -o '([0-9]+\.){3}[0-9]+'
		echo "ping:"
		ping -c1 -t1 $each | egrep -o '([0-9]+\.){3}[0-9]+' | head -n1
	done
}
 
ips () {
  ip addr show | awk -F":" '($1>0){split($2,a," ");print a[1] "- "}($1 ~ /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/){split($1,a," "); print a[2]}' | tr '\n' '\t' | sed 's/\/[0-9]\+//g' | sed 's/\(\w\+\)-/\n\1/g' | sed '/^$/d' && echo
}
 
macs () {
  ip addr show | awk -F":" '($1>0){split($2,a," ");print a[1] "- "}($1 ~ /link/){split($0,a," "); print a[2]}' | tr '\n' '\t' | sed 's/\/[0-9]\+//g' | sed 's/\(\w\+\)-/\n\1/g' | sed '/^$/d' && echo
}
 
pg () {
	ps aux | grep $@ | grep -v grep
}
pid () {
	pg $@ | awk '{print $2}'
}
 
repeat () {
	if [ "$2" == "" ]; then
		delay=1
	else
		delay=$2
	fi
	while sleep $delay; do
		clear
		date +%s
		$1
	done
}
 
watch () {
	if [ "$2" == "" ]; then
		delay=1
	else
		delay="$2"
	fi
	i="";
	j="";
	while sleep $delay; do
		i=`$1`;
		if [ "$i" != "$j" ]; then
			date +%s
			echo $i
		fi
	done
}
 
# more persistent wget for fetching files to a specific filename.
fetch_to () {
	[ $# -ne 2 ] && echo "usage: fetch_to <url> <filename>" && return 1
	urltofetch=$1
	fname="$2"
	wget -O "$fname" $urltofetch
}
 
################## End functions from http://foohack.com/tests/bash_extras/.extra.bashrc ###################################

Durch Aufruf von

source ~/.bashrc

kann man ohne Neustart der Shell die Konfigurationsdatei laden. Die in der .bashrc referenzierte .bash_aliases sieht folgendermaßen aus:

# Alias definitions
# some more ls aliases
alias ll='ls -lF'
alias la='ls -aF'
alias lla='ls -laF'
alias l='ls -CF'
alias ld='ls -al -d * | egrep "^d"' # only subdirectories
alias lt='ls -alt | head -20' # recently changed files
alias ag="alias | $grep"
 
# convenience redefinitions
alias cd..='cd ..'
alias cd-='cd -'
alias dselect='dselect --expert'
alias fg-='fg -'
alias more="less -e"
alias lsdevs="sudo lsof | $grep /dev"
alias md='mkdir -p'
alias rd=rmdir
alias ..='cd ..'
alias ...='cd ../..'
#also remember popd
alias +='pushd .'
alias mvsafe="mv -i"
 
# enable color support of ls and also add handy aliases
if [ "$TERM" != "dumb" ] && [ -x /usr/bin/dircolors ]; then
    eval "`dircolors -b`"
    alias ls='ls --color=auto'
    alias dir='ls --color=auto --format=vertical'
    alias vdir='ls --color=auto --format=long'
 
    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi
 
# inventions
alias fgg=jobs
alias ghist='history|grep'
alias lf='find -type f|sort'
alias load='cat /proc/loadavg'
alias meminfo='cat /proc/meminfo'
 
# Kalendar
alias kal='clear;echo -n "Heutiges Datum: ";date;echo;cal -3m'
 
#make tree a little cooler looking.
alias tree="tree -CAFa -I 'CVS|rhel.*.*.package|.svn|.git' --dirsfirst"
 
alias processes="ps axMuc | egrep '^[a-zA-Z0-9]'"
 
# command-line perl prog
alias pie="perl -p -i -e "
 
################## Begin functions from http://www.johnlawrence.net/code/?f=sfbashrc ###################################
 
# directory tree - http://www.shell-fu.org/lister.php?id=209
alias dirf='find . -type d | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/"'
 
#calendar with today highlighted - http://www.shell-fu.org/lister.php?id=210
alias tcal='cal | sed "s/^/ /;s/$/ /;s/ $(date +%e) / $(date +%e | sed '\''s/./#/g'\'') /"'
 
# count files by type - http://www.shell-fu.org/lister.php?id=173
alias ftype='find ${*-.} -type f | xargs file | awk -F, '\''{print $1}'\'' | awk '\''{$1=NULL;print $0}'\'' | sort | uniq -c | sort -nr'
 
# convert permissions to octal - http://www.shell-fu.org/lister.php?id=205
alias lo='ls -l | sed -e 's/--x/1/g' -e 's/-w-/2/g' -e 's/-wx/3/g' -e 's/r--/4/g' -e 's/r-x/5/g' -e 's/rw-/6/g' -e 's/rwx/7/g' -e 's/---/0/g''
 
# portscan in one line - http://www.shell-fu.org/lister.php?id=295
portscan(){
  HOST="$1";for((port=1;port<=65535;++port));do echo -en "$port ";if echo -en "open $HOST $port\nlogout\quit" | telnet 2>/dev/null | grep 'Connected to' > /dev/null;then echo -en "\n\nport $port/tcp is open\n\n";fi;done
}
 
# print a random shell-fu tip - http://www.shell-fu.org/lister.php?id=192
alias shell-fu='links -dump "http://www.shell-fu.org/lister.php?random" | grep -A 100 -- ----'
 
# get an ordered list of subdirectory sizes - http://www.shell-fu.org/lister.php?id=275
alias dux='du -sk ./* | sort -n | awk '\''BEGIN{ pref[1]="K"; pref[2]="M"; pref[3]="G";} { total = total + $1; x = $1; y = 1; while( x > 1024 ) { x = (x + 1023)/1024; y++; } printf("%g%s\t%s\n",int(x*10)/10,pref[y],$2); } END { y = 1; while( total > 1024 ) { total = (total + 1023)/1024; y++; } printf("Total: %g%s\n",int(total*10)/10,pref[y]); }'\'''
 
# share current tree over the web - http://www.shell-fu.org/lister.php?id=54
alias webshare='python -c "import SimpleHTTPServer;SimpleHTTPServer.test()"'
 
################## End functions from http://www.johnlawrence.net/code/?f=sfbashrc ###################################

Dash Shell Scripting – Mit Arrays arbeiten

Bei der Bourne-Shell ist es ohne Probleme möglich verschiedenste Dinge mit Arrays anzustellen. Viele interessante und lehrreiche Beispiele finden sich im Unix Bash Scripting Blog. Allerdings verwendet Ubuntu für Scripts mittlerweile die dash, die zwar nicht den Funktionsumfang der Bash bereitstellt, aber dafür dem POSIX-Standard entspricht und schneller sein soll. Leider kann man damit nicht wie von der Bash gewohnt mit Arrays arbeiten. Wie kann man sich nun behelfen? Ich wollte für mein Beispiel zwei Arrays (TAPS, ARP_IPS) so kombinieren, dass jeweils die zusammengehörigen Parameter (gleicher Index im Array) ausgegeben werden. So habe ich es hinbekommen:

#!/bin/sh
TAPS="tap0 tap1 tap2"
ARP_IPS="169.254.0.1/32 169.254.0.2/32 169.254.0.3/32"
i=0
for TAP in $TAPS; do
  j=0
  for ARP_IP in $ARP_IPS; do
    if [ $i = $j ]; then
      break
    fi
    j=$(( $j + 1 ))
  done
  echo $TAP $ARP_IP
  i=$(( $i + 1 ))
done

Als Ausgabe des Scripts erhält man folgendes:

tap0 169.254.0.1/32
tap1 169.254.0.2/32
tap2 169.254.0.3/32

Intrepid: Ordner werden mit totem statt Nautilus geöffnet

Nach dem Upgrade von Ubuntu Hardy Heron auf Intrepid Ibex hatte ich das Problem, dass mein Persönlicher Ordner und alle Lesezeichen auf Ordner auf einmal mit totem geöffnet wurden. Nach langem Suchen im Internet stellte sich heraus, dass das ein Bug ist, dessen Ursache in der Datei .local/share/applications/mimeapps.list liegt. Bei mir sah der relevante Abschnitt dieser Datei so aus:

inode/directory=totem-xine.desktop;totem.desktop;nautilus-folder-handler.desktop;nautilus.desktop;

Nachdem ich die Reihenfolge wie nachfolgend geändert hatte, funktionierte alles wieder wie gewünscht:

inode/directory=nautilus-folder-handler.desktop;nautilus.desktop;totem-xine.desktop;totem.desktop;

DVD-Menü mit totem unter Ubuntu

Mit der Ubuntu-Standardinstallation hatte ich bei totem das Problem, dass das DVD-Menü einfach nicht erscheinen wollte – weder beim Start einer DVD noch beim expliziten Auswählen über Gehe zu>DVD-Menü. Nach etwas Suchen im Internet bin ich auf einen Forumseintrag von steve.dreger gestoßen, der Abhilfe schafft. Mit

sudo apt-get install totem-xine libxine1-plugins libxine1-ffmpeg

installiert man das xine-library, so dass totem über diesen Alternativweg DVDs abspielen kann. Allerdings wird beim Start von totem standardmäßig weiterhin der Zugriff über gstreamer gewählt. Man muss totem also explizit aufrufen als

totem-xine

oder sich einen entsprechenden Eintrag im Anwendungsmenü eintragen. Dazu einfach Rechtsklick auf „Anwendungen“ (im Panel) > Menüs bearbeiten und dann unter Unterhaltungsmedien das Häkchen bei „Video-Player (Xine)“ setzen. So kann nun immer selbst entscheiden, welche Variante von totem man benutzen möchte.
Und wenn wir schon beim Thema DVDs sind – diese lassen sich mit dvdbackup ganz einfach als Sicherungskopie auf die Festplatte kopieren. Nach einem

sudo apt-get install dvdbackup

kann man durch Aufrufen von

dvdbackup -M -i/dev/cdrom -o/home/user/dvd/backup/

die DVD ins Verzeichnis /home/user/dvd/backup/ sichern.