Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Oct 2010 20:29:20 -0700
From:      Devin Teske <dteske@vicor.com>
To:        Brandon Gooch <jamesbrandongooch@gmail.com>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: sysrc -- a sysctl(8)-like utility for managing /etc/rc.conf et. al.
Message-ID:  <51B4504F-5AA4-47C5-BF23-FA51DE5BC8C8@vicor.com>
In-Reply-To: <AANLkTikoohMo5ng-RM3tctTH__P6cqhQpm=FPhSE9mMg@mail.gmail.com>
References:  <1286397912.27308.40.camel@localhost.localdomain> <AANLkTikoohMo5ng-RM3tctTH__P6cqhQpm=FPhSE9mMg@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

On Oct 6, 2010, at 4:09 PM, Brandon Gooch wrote:

> On Wed, Oct 6, 2010 at 3:45 PM, Devin Teske <dteske@vicor.com> wrote:
>> Hello fellow freebsd-hackers,
>>=20
>> Long-time hacker, first-time poster.
>>=20
>> I'd like to share a shell script that I wrote for FreeBSD system
>> administration.
>>=20
>=20
> It seems the list ate the attachment :(


Here she is ^_^ Comments welcome.

#!/bin/sh
# -*- tab-width:  4 -*- ;; Emacs
# vi: set tabstop=3D4     :: Vi/ViM
#
# Revision: 1.0
# Last Modified: October 6th, 2010
############################################################ COPYRIGHT
#
# (c)2010. Devin Teske. All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the =
distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY =
WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# AUTHOR      DATE      DESCRIPTION
# dteske   2010.09.29   Initial version.
#
############################################################ INFORMATION
#
# Command Usage:
#
#   sysrc [OPTIONS] name[=3Dvalue] ...
#
#   OPTIONS:
#   	-h --help   Print this message to stderr and exit.
#   	-d          Print list of internal dependencies before exit.
#   	-e          Print query results as `var=3Dvalue' (useful for =
producing
#   	            output to be fed back in). Ignored if -n is =
specified.
#   	-n          Show only variable values, not their names.
#=20
#   ENVIRONMENT:
#   	SYSRC_SHOW_DEPS       Dump list of dependencies. Must be zero or =
one
#   	                      (default: `0')
#   	RC_DEFAULTS           Location of `/etc/defaults/rc.conf' file.
#
# About the Utility:
#
#   This utility works similar to sysctl(8). It shares the `-e' and `-n'
#   options (detailed above and in sysctl(8)) and also has the same
#   `name[=3Dvalue]' syntax for querying/setting configuration options.
#
#   Fundamentally it could be said that sysctl(8) munges sysctl MIBs in =
the
#   entrant kernel, while sysrc(8) -- this utility -- munges values in =
the
#   system rc.conf(5) configuration files.
#
#   NOTE: System configuration files are by-default configured in the =
file
#   `/etc/defaults/rc.conf' in the variable `rc_conf_files', which =
by-default
#   contains a space-separated list of pathnames. On all FreeBSD =
systems, this
#   defaults to the value "/etc/rc.conf /etc/rc.conf.local". Each =
pathname is
#   sourced in-order upon startup. It is in the same fashion that this =
script
#   sources the configuration files before returning the value of the =
given
#   variable by-name.
#
#   When you supply this utility with a variable name, it will return =
the value
#   of the variable. If the variable does not appear in any of the =
configured
#   rc_conf_files, an error is printed and we return with error status.
#
#   When changing values of a given variable it does not matter if the =
variable
#   appears in any of the rc_conf_files or not. If the variable does not =
appear
#   in any of the files, it is appended to the end of the first pathname =
in the
#   rc_conf_files variable. Otherwise, the sysrc_set() function will =
replace
#   only the last-occurrence in the last-file found to contain the =
variable.
#   This effectively gets the value to take effect next reboot without =
heavily
#   modifying these integral files (yet taking care not to allow the =
file to
#   grow unweildy should the script be called repeatedly infinitum).
#
# About the Code:
#
#   I hope that you enjoy the cleanliness, verbosity (but at the same =
time,
#   utilitarian brevity) and -- dare I say -- beauty of the code of this
#   utility. I have been refining my =
cross-platform/POSIX-compliant/baseline-
#   compatible method of pure-bourne shell scripting for 10 years now. =
Shell is
#   not the only language that I could have chosen to written this =
utility in,
#   but it assuredly was the most satisfying. Satisfying in the sense =
that such
#   a utility is infinitely more useful to the masses if programmed in =
the
#   baseline-compatibility mindset.
#
#   This fundamental utility takes a grass-roots approach to warming its =
way
#   into the hearts and minds of each and every FreeBSD system =
administrator,
#   power-user, and developer. Designed to be a reliable, stable, core =
utility
#   that is depended upon for daily-use without failing in any way. This =
shell
#   script is unique in that it has absolutely no external dependencies. =
The
#   POSIX bourne-shell made available in FreeBSD (/bin/sh) is all that =
is
#   needed. Though, if grep(1) is available in the shell's search path =
($PATH;
#   search "Path Search" in sh(1)), the script will take advantage of it =
for
#   better efficiency and use of regular expressions rather than glob(7)
#   patterns.
#
#   This approach makes this utility uniquely adept at functioning in an
#   embedded environment where very little may be available (for =
example, in
#   the mfsroot ``floppy images'' produced by the release(7) processs; =
also
#   detailed in the ``FreeBSD Release Engineering'' portion of the =
online
#   handbook available at =
http://www.freebsd.org/doc/en_US.ISO8859-1/articles\
#   /releng/release-build.html).
#
#   If space is an issue for the embedded environment, the first place I =
would
#   start stripping would be comments. There are enough that trimming =
all
#   comments from this file would be enough to drop multiples of the =
storage
#   container's filesystem fragment size (usually 2KB in UFS -- as is =
the
#   format of the mfsroot that FreeBSD boots off of these days). The =
second
#   place I would start trimming would be the depend() function and all =
of its
#   many uses. Next, I would see the whitespace go before finally =
attacking
#   code structure.
#
#   Within, you'll find that the code is very lean. Though, I admit that =
there
#   are a few places that could be enhanced for CPU efficiency. The =
lrev()
#   function is particularly slow as it must use the shell's internal =
buffering
#   to buffer the entire input before finally returning the =
line-reversed
#   output. This leads to significant (100s of milliseconds) delays when
#   writing out the changes when the "varname=3Dnew_value" syntax is =
invoked.
#   If we give up the stringent requirement of requiring no external =
executable
#   then we can significantly improve the performance of the lrev() =
function by
#   instead relying on awk(1) to reverse the lines of a file (as awk is
#   infinitely faster than buffering lines of a file into a local shell
#   variable -- twice). Though, through the power of the have() =
function, we
#   quite simply add the power of awk(1) without relying on its =
presence.
#
#   On a liter note, the sysrc() function is perhaps at it's tenth =
generation
#   for myself and has taken on a quintiscential beauty in its awe =
inspiring
#   simplicity and undulating flexibility. I could only dream that it be =
one-
#   day find a home directly in `/etc/rc.subr'.
#
#   Alas, if you've read this far, you might be the type to wonder what =
those
#   two lines at the top (after the shell interpreter invocation-line) =
do:
#
#      # -*- tab-width:  4 -*- ;; Emacs
#      # vi: set tabstop=3D4     :: Vi/ViM
#
#   The first achieves the amazing feat of getting Emacs to always use =
real-
#   tab characters while maintaining the visual display of =
4-character-wide
#   indentation. Seasoned Emacs users are blissfully unaware that as =
they
#   modify the file, they are using real tabs versus =
4-spaces-then-tab-then-4-
#   spaces-then-tab-...
#
#   The second line gets vim (and later versions of vi -- but not nvi =
which
#   is used for vi(1) in FreeBSD) to render real tabs as =
4-characters-wide.
#
#   The two lines together make it so that the file can be passed =
back-and-
#   forth between different developers using Emacs, Vim and vi. Real =
tabs are
#   preserved in all editors and developers are encouraged to use real =
tabs.
#
#   Vim/vi users are then given the flexibility of redefining at-will =
the tab
#   width by setting the "ts" internal variable, though Emacs users are
#   discouraged from changing the tab-width since any value other than 4 =
will
#   cause mixtures of tab-and-space to entered into the file during =
auto-indent
#   events.
#
#   Last, my personal coding technique is to code to 80-character-wide =
terminal
#   widths (while I have tab-width set to 8 in vim -- via ``:set =
ts=3D8'').
#   That's just personal preference (because I find resizing a terminal =
with
#   the mouse to be a heinously-inefficient motion that takes up too =
much of
#   the time that I could be typing into my shell, coding, or navigating =
with
#   the keyboard).
#
# About Copying (the License):
#
#   Those that are familiar with the license (displayed above in the =
COPYING
#   section) will recognize the Berkeley Open License. It is only =
slightly more
#   restrictive than the in/famous Beer-ware license. You must adhere to =
the
#   two restrictions above which are simple enough. Display the =
copyright in
#   any facsimile representations, modified or un-modified.
#
#   http://www.plainlanguage.gov/
#
############################################################ =
CONFIGURATION

#
# Default setting whether to dump a list of internal dependencies upon =
exit
#
: ${SYSRC_SHOW_DEPS:=3D0}

############################################################ GLOBALS

# Global exit status variables
: ${SUCCESS:=3D0}
: ${FAILURE:=3D1}

#
# Program name
#
progname=3D"${0##*/}"

#
# Options
#
SHOW_EQUALS=3D
SHOW_NAME=3D1

# Reserved for internal use
_depend=3D

############################################################ FUNCTION

# fprintf $fd $fmt [ $opts ... ]
#
# Like printf, except allows you to print to a specific file-descriptor. =
Useful
# for printing to stderr (fd=3D2) or some other known file-descriptor.
#
: dependency checks performed after depend-function declaration
: function ; fprintf ( ) # $fd $fmt [ $opts ... ]
{
	local fd=3D$1
	[ $# -gt 1 ] || return ${FAILURE-1}
	shift 1
	printf "$@" >&$fd
}

# eprintf $fmt [ $opts ... ]
#
# Print a message to stderr (fd=3D2).
#
: dependency checks performed after depend-function declaration
: function ; eprintf ( ) # $fmt [ $opts ... ]
{
	fprintf 2 "$@"
}

# show_deps
#
# Print the current list of dependencies.
#
: dependency checks performed after depend-function declaration
: function ; show_deps ( ) #
{
	if [ "$SYSRC_SHOW_DEPS" =3D "1" ]; then
		eprintf "Running internal dependency list:\n"

		local d
		for d in $_depend; do
			eprintf "\t%-15ss%s\n" "$d" "$( type "$d" )"
		done
	fi
}

# die [ $err_msg ... ]
#
# Optionally print a message to stderr before exiting with failure =
status.
#
: dependency checks performed after depend-function declaration
: function ; die ( ) # [ $err_msg ... ]
{
	local fmt=3D"$1"
	[ $# -gt 0 ] && shift 1
	[  "$fmt"  ] && eprintf "$fmt\n" "$@"

	show_deps
	exit ${FAILURE-1}
}

# have $anything
#
# Used for dependency calculations. Arguments are passed to the `type' =
built-in
# to determine if a given command is available (either as a shell =
built-in or
# as an external binary). The return status is true if the given =
argument is
# for an existing built-in or executable, otherwise false.
#
# This is a convenient method for building dependency lists and it aids =
in the
# readability of a script. For example,
#
# 	Example 1: have sed || die "sed is missing"
# 	Example 2: if have awk; then
# 	           	# We have awk...
# 	           else
# 	           	# We DON'T have awk...
# 	           fi
# 	Example 3: have reboot && reboot
#
: dependency checks performed after depend-function declaration
: function ; have ( ) # $anything
{
	type "$@" > /dev/null 2>&1
}

# depend $name [ $dependency ... ]
#
# Add a dependency. Die with error if dependency is not met.
#
: dependency checks performed after depend-function declaration
: function ; depend ( ) # $name [ $dependency ... ]
{
	local by=3D"$1" arg
	shift 1

	for arg in "$@"; do
		local d
		for d in $_depend ""; do
			[ "$d" =3D "$arg" ] && break
		done
		if [ ! "$d" ]; then
			have "$arg" || die \
				"%s: Missing dependency '%s' required by =
%s" \
				"${progname:-$0}" "$arg" "$by"
			_depend=3D"$_depend${_depend:+ }$arg"
		fi
	done
}

#
# Perform dependency calculations for above rudimentary functions.
# NOTE: Beyond this point, use the depend-function BEFORE dependency-use
#
depend fprintf   'local' '[' 'return' 'shift' 'printf'
depend eprintf   'fprintf'
depend show_deps 'if' '[' 'then' 'eprintf' 'local' 'for' 'do' 'done' =
'fi'
depend die       'local' '[' 'shift' 'eprintf' 'show_deps' 'exit'
depend have      'local' 'type' 'return'
depend depend    'local' 'shift' 'for' 'do' '[' 'break' 'done' 'if' =
'then' \
                 'have' 'die' 'fi'

# usage
#
# Prints a short syntax statement and exits.
#
depend usage 'local' 'eprintf' 'die'
: function ; usage ( ) #
{
	local optfmt=3D"\t%-12s%s\n"
	local envfmt=3D"\t%-22s%s\n"

	eprintf "Usage: %s [OPTIONS] name[=3Dvalue] ...\n" =
"${progname:-$0}"

	eprintf "OPTIONS:\n"
	eprintf "$optfmt" "-h --help" \
		"Print this message to stderr and exit."
	eprintf "$optfmt" "-d" \
		"Print list of internal dependencies before exit."
	eprintf "$optfmt" "-e" \
	        "Print query results as \`var=3Dvalue' (useful for =
producing"
	eprintf "$optfmt" "" \
	        "output to be fed back in). Ignored if -n is specified."
	eprintf "$optfmt" "-n" \
	        "Show only variable values, not their names."
	eprintf "\n"

	eprintf "ENVIRONMENT:\n"
	eprintf "$envfmt" "SYSRC_SHOW_DEPS" \
		"Dump list of dependencies. Must be zero or one"
	eprintf "$envfmt" "" \
		"(default: \`0')"
	eprintf "$envfmt" "RC_DEFAULTS" \
	        "Location of \`/etc/defaults/rc.conf' file."

	die
}

# sysrc $setting
#
# Get a system configuration setting from the collection of system-
# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf
# and /etc/rc.conf).
#
# Examples:
#
# 	sysrc sshd_enable
# 		returns YES or NO
# 	sysrc defaultrouter
# 		returns IP address of default router (if configured)
# 	sysrc 'hostname%%.*'
# 		returns $hostname up to (but not including) first `.'
# 	sysrc 'network_interfaces%%[$IFS]*'
# 		returns first word of $network_interfaces
# 	sysrc 'ntpdate_flags##*[$IFS]'
# 		returns last word of $ntpdate_flags (time server =
address)
# 	sysrc usbd_flags-"default"
# 		returns $usbd_flags or "default" if unset
# 	sysrc usbd_flags:-"default"
# 		returns $usbd_flags or "default" if unset or NULL
# 	sysrc cloned_interfaces+"alternate"
# 		returns "alternate" if $cloned_interfaces is set
# 	sysrc cloned_interfaces:+"alternate"
# 		returns "alternate" if $cloned_interfaces is set and =
non-NULL
# 	sysrc '#kern_securelevel'
# 		returns length in characters of $kern_securelevel
# 	sysrc 'hostname?'
# 		returns NULL and error status 2 if $hostname is unset =
(or if
# 		set, returns the value of $hostname with no error =
status)
# 	sysrc 'hostname:?'
# 		returns NULL and error status 2 if $hostname is unset or =
NULL
# 		(or if set and non-NULL, returns value without error =
status)
#
depend sysrc 'local' '[' 'return' '.' 'have' 'eval' 'echo'
: function ; sysrc ( ) # $varname
{
	: ${RC_DEFAULTS:=3D"/etc/defaults/rc.conf"}

	local defaults=3D"$RC_DEFAULTS"
	local varname=3D"$1"

	# Check arguments
	[ -r "$defaults" ] || return
	[ "$varname" ] || return

	( # Execute within sub-shell to protect parent environment
		[ -f "$defaults" -a -r "$defaults" ] && . "$defaults"
		have source_rc_confs && source_rc_confs
		eval echo '"${'"$varname"'}"' 2> /dev/null
	)
}

# ... | lrev
# lrev $file ...
#
# Reverse lines of input. Unlike rev(1) which reverses the ordering of
# characters on a single line, this function instead reverses the line
# sequencing.
#
# For example, the following input:
#
# 	Line 1
# 	Line 2
# 	Line 3
#
# Becomes reversed in the following manner:
#
# 	Line 3
# 	Line 2
# 	Line 1
#
depend lrev 'local' 'if' '[' 'then' 'while' 'do' 'shift' 'done' 'else' =
'read' \
            'fi' 'echo'
: function ; lrev ( ) # $file ...
{
	local stdin_rev=3D
	if [ $# -gt 0 ]; then
		#
		# Reverse lines from files passed as positional =
arguments.
		#
		while [ $# -gt 0 ]; do
			local file=3D"$1"
			[ -f "$file" ] && lrev < "$file"
			shift 1
		done
	else
		#
		# Reverse lines from standard input
		#
		while read -r LINE; do
			stdin_rev=3D"$LINE
$stdin_rev"
		done
	fi

	echo -n "$stdin_rev"
}

# sysrc_set $setting $new_value
#
# Change a setting in the system configuration files (edits the files =
in-place
# to change the value in the last assignment to the variable). If the =
variable
# does not appear in the source file, it is appended to the end of the =
primary
# system configuration file `/etc/rc.conf'.
#
depend sysrc_set 'local' 'sysrc' '[' 'return' 'for' 'do' 'done' 'if' =
'have' \
                 'then' 'else' 'while' 'read' 'case' 'esac' 'fi' 'break' =
\
                 'eprintf' 'echo' 'lrev'
: function ; sysrc_set ( ) # $varname $new_value
{
	local rc_conf_files=3D"$( sysrc rc_conf_files )"
	local varname=3D"$1" new_value=3D"$2"
	local file conf_files=3D

	# Check arguments
	[ "$rc_conf_files" ] || return ${FAILURE-1}
	[ "$varname" ] || return ${FAILURE-1}

	# Reverse the order of files in rc_conf_files
	for file in $rc_conf_files; do
		conf_files=3D"$file${conf_files:+ }$conf_files"
	done

	#
	# Determine which file we are to operate on. If no files match, =
we'll
	# simply append to the last file in the list (`/etc/rc.conf').
	#
	local found=3D
	local regex=3D"^[[:space:]]*$varname=3D"
	for file in $conf_files; do
		#if have grep; then
		if false; then
			grep -q "$regex" $file && found=3D1
		else
			while read LINE; do \
				case "$LINE" in \
				$varname=3D*) found=3D1;; \
				esac; \
			done < $file
		fi
		[ "$found" ] && break
	done

	#
	# Perform sanity checks.
	#
	if [ ! -w $file ]; then
		eprintf "\n%s: cannot create %s: permission denied\n" \
		        "${progname:-$0}" "$file"
		return ${FAILURE-1}
	fi

	#
	# If not found, append new value to last file and return.
	#
	if [ ! "$found" ]; then
		echo "$varname=3D\"$new_value\"" >> $file
		return ${SUCCESS-0}
	fi

	#
	# Operate on the matching file, replacing only the last =
occurrence.
	#
	local new_contents=3D"`lrev $file 2> /dev/null | \
	( found=3D
	  while read -r LINE; do
	  	if [ ! "$found" ]; then
			#if have grep; then
			if false; then
	  			match=3D"$( echo "$LINE" | grep "$regex" =
)"
			else
				case "$LINE" in
				$varname=3D*) match=3D1;;
				         *) match=3D;;
				esac
			fi
	  		if [ "$match" ]; then
	  			LINE=3D"$varname"'=3D"'"$new_value"'"'
	  			found=3D1
	  		fi
	  	fi
	  	echo "$LINE"
	  done
	) | lrev`"

	[ "$new_contents" ] \
		&& echo "$new_contents" > $file
}

############################################################ MAIN SOURCE

#
# Perform sanity checks
#
depend main '[' 'usage'
[ $# -gt 0 ] || usage

#
# Process command-line options
#
depend main 'while' '[' 'do' 'case' 'usage' 'eprintf' \
            'break' 'esac' 'shift' 'done'
while [ $# -gt 0 ]; do
	case "$1" in
	-h|--help) usage;;
	-d) SYSRC_SHOW_DEPS=3D1;;
	-e) SHOW_EQUALS=3D1;;
	-n) SHOW_NAME=3D;;
	-*) eprintf "%s: unrecognized option \`$1'\n" "${progname:-$0}"
	    usage;;
	 *) # Since it is impossible (in many shells, including bourne, =
c,
	    # tennex-c, and bourne-again) to name a variable beginning =
with a
	    # dash/hyphen [-], we will terminate the option-list at the =
first
	    # item that doesn't begin with a dash.
	    break;;
	esac
	shift 1
done
[ "$SHOW_NAME" ] || SHOW_EQUALS=3D

#
# Process command-line arguments
#
depend main '[' 'while' 'do' 'case' 'echo' 'sysrc' 'if' 'sysrc_set' =
'then' \
            'fi' 'esac' 'shift' 'done'
SEP=3D': '
[ "$SHOW_EQUALS" ] && SEP=3D'=3D"'
while [ $# -gt 0 ]; do
	NAME=3D"${1%%=3D*}"
	case "$1" in
	*=3D*)
		echo -n "${SHOW_NAME:+$NAME$SEP}$(
		         sysrc "$1" )${SHOW_EQUALS:+\"}"
		if sysrc_set "$NAME" "${1#*=3D}"; then
			echo " -> $( sysrc "$NAME" )"
		fi
		;;
	*)
		if ! IGNORED=3D"$( sysrc "$NAME?" )"; then
			echo "${progname:-$0}: unknown variable '$NAME'"
		else
			echo "${SHOW_NAME:+$NAME$SEP}$(
			      sysrc "$1" )${SHOW_EQUALS:+\"}"
		fi
	esac
	shift 1
done

#
# Ending routines
#
depend main 'show_deps'
show_deps

=
##########################################################################=
######
# END
=
##########################################################################=
######
#
# $Header$
#
# $Copyright: 1999-2010. Vicor Inc. All Rights Reserved. $
#
# $Log$
#
=
##########################################################################=
######

--
Cheers,
Devin Teske

-> CONTACT INFORMATION <-
Business Solutions Consultant II
FIS - fisglobal.com
510-735-5650 Mobile
510-621-2038 Office
510-621-2020 Office Fax
909-477-4578 Home/Fax
devin.teske@fisglobal.com

-> LEGAL DISCLAIMER <-
This message  contains confidential  and proprietary  information
of the sender,  and is intended only for the person(s) to whom it
is addressed. Any use, distribution, copying or disclosure by any
other person  is strictly prohibited.  If you have  received this
message in error,  please notify  the e-mail sender  immediately,
and delete the original message without making a copy.

-> FUN STUFF <-
-----BEGIN GEEK CODE BLOCK-----
Version 3.1
GAT/CS d(+) s: a- C++(++++) UB++++$ P++(++++) L++(++++) !E--- W++ N? o? =
K- w O
M+ V- PS+ PE Y+ PGP- t(+) 5? X+(++) R>++ tv(+) b+(++) DI+(++) D(+) G+>++ =
e>+ h
r>++ y+=20
------END GEEK CODE BLOCK------
http://www.geekcode.com/

-> END TRANSMISSION <-




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?51B4504F-5AA4-47C5-BF23-FA51DE5BC8C8>