Date: Sat, 27 Aug 2011 10:47:12 -0700 From: Devin Teske <devin.teske@fisglobal.com> To: FreeBSD Hackers <freebsd-hackers@freebsd.org> Cc: Devin Teske <dteske@vicor.com>, FreeBSD Jail <freebsd-jail@freebsd.org>, FreeBSD RC <freebsd-rc@freebsd.org>, Dave Robison <daver@vicor.com> Subject: [PATCH] Add /etc/rc.d/vimage startup script for creating vnet jails Message-ID: <CAC979C8-3129-4E62-9D76-D1D0CCE001F0@fisglobal.com>
next in thread | raw e-mail | index | archive | help
--Apple-Mail-25--994899661
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="ISO-8859-1"
Hi All,
I'd like to submit a patch for review (attached) that adds a new /etc/rc.d =
script named "vimage".
_____________
The information contained in this message is proprietary and/or confidentia=
l. If you are not the intended recipient, please: (i) delete the message an=
d all copies; (ii) do not disclose, distribute or use the message in any ma=
nner; and (iii) notify the sender immediately. In addition, please be aware=
that any message addressed to our domain is subject to archiving and revie=
w by persons other than the intended recipient. Thank you.
_____________
--Apple-Mail-25--994899661
Content-Disposition: attachment; filename="vimage_rc.20110827104104.patch"
Content-Type: application/octet-stream; name="vimage_rc.20110827104104.patch"
Content-Transfer-Encoding: 7bit
--- etc/defaults/rc.conf.orig Fri Aug 26 20:36:52 2011
+++ etc/defaults/rc.conf Sat Aug 27 10:34:54 2011
@@ -697,6 +697,43 @@
#jail_example_flags="-l -U root" # flags for jail(8)
##############################################################
+### Vimage Configuration #####################################
+##############################################################
+vimage_enable="NO" # Set to NO to disable starting of any vimages
+vimage_parallel_start="NO" # Start vimages in the background
+vimage_list="" # Space separated list of names of vimages
+vimage_set_hostname_allow="YES" # Allow root user in a vimage to change its hostname
+vimage_socket_unixiproute_only="NO" # Route only TCP/IP within a vimage
+vimage_sysvipc_allow="YES" # Allow SystemV IPC use from within a vimage
+
+#
+# To use rc's built-in vimage infrastructure create entries for
+# each vimage, specified in vimage_list, with the following variables.
+# NOTES:
+# - replace 'example' with the vimage's name.
+# - except rootdir, and hostname, all of the following variables may be made
+# global vimage variables if you don't specify a vimage name (ie.
+# vimage_fib, vimage_devfs_ruleset).
+#
+#vimage_example_rootdir="/usr/jail/default" # Vimage's root directory
+#vimage_example_hostname="default.domain.com" # Vimage's hostname
+#vimage_example_vnets="epair0b" # Vimage's vnet interfaces
+#vimage_example_exec_start="/bin/sh /etc/rc" # command to execute in vimage for starting
+#vimage_example_services="sshd ipfw zfs" # services to start after starting vimage
+#vimage_example_exec_afterstart0="/bin/sh command" # command to execute after the one for
+ # starting the vimage. More than one can
+ # be specified using a trailing number
+#vimage_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in vimage for stopping
+#vimage_example_devfs_enable="NO" # mount devfs in the vimage
+#vimage_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to vimage -
+ # usually you want "devfsrules_jail".
+#vimage_example_fdescfs_enable="NO" # mount fdescfs in the vimage
+#vimage_example_procfs_enable="NO" # mount procfs in vimage
+#vimage_example_mount_enable="NO" # mount/umount vimage's fs
+#vimage_example_fstab="" # fstab(5) for mount/umount
+#vimage_example_flags="-l -U root" # flags for jail(8)
+
+##############################################################
### Define source_rc_confs, the mechanism used by /etc/rc.* ##
### scripts to source rc_conf_files overrides safely. ##
##############################################################
--- etc/rc.d/vimage.orig Sat Aug 27 10:26:53 2011
+++ etc/rc.d/vimage Sat Aug 27 10:36:03 2011
@@ -0,0 +1,551 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: vimage
+# REQUIRE: LOGIN cleanvar
+# BEFORE: securelevel
+# KEYWORD: nojail shutdown
+
+# WARNING: This script deals with untrusted data (the data and
+# processes inside the vimage) and care must be taken when changing the
+# code related to this! If you have any doubt whether a change is
+# correct and have security impact, please get the patch reviewed by
+# the FreeBSD Security Team prior to commit.
+
+. /etc/rc.subr
+
+name="vimage"
+rcvar=`set_rcvar`
+
+start_precmd="vimage_prestart"
+start_cmd="vimage_start"
+stop_cmd="vimage_stop"
+
+# init_variables _v
+# Initialize the various vimage variables for vimage _v.
+#
+init_variables()
+{
+ _v="$1"
+
+ if [ -z "$_v" ]; then
+ warn "init_variables: you must specify a vimage"
+ return
+ fi
+
+ eval _rootdir=\"\$vimage_${_v}_rootdir\"
+ _devdir="${_rootdir}/dev"
+ _fdescdir="${_devdir}/fd"
+ _procdir="${_rootdir}/proc"
+ eval _hostname=\"\$vimage_${_v}_hostname\"
+ eval _vnets=\"\$vimage_${_v}_vnets\"
+ eval _exec=\"\$vimage_${_v}_exec\"
+
+ i=0
+ while : ; do
+ eval _exec_prestart${i}=\"\${vimage_${_v}_exec_prestart${i}:-\${vimage_exec_prestart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ eval _exec_start=\"\${vimage_${_v}_exec_start:-${vimage_exec_start}}\"
+ eval _services=\"\${vimage_${_v}_services:-${vimage_services}}\"
+
+ i=1
+ while : ; do
+ eval _exec_afterstart${i}=\"\${vimage_${_v}_exec_afterstart${i}:-\${vimage_exec_afterstart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval _exec_poststart${i}=\"\${vimage_${_v}_exec_poststart${i}:-\${vimage_exec_poststart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval _exec_prestop${i}=\"\${vimage_${_v}_exec_prestop${i}:-\${vimage_exec_prestop${i}}}\"
+ [ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ eval _exec_stop=\"\${vimage_${_v}_exec_stop:-${vimage_exec_stop}}\"
+
+ i=0
+ while : ; do
+ eval _exec_poststop${i}=\"\${vimage_${_v}_exec_poststop${i}:-\${vimage_exec_poststop${i}}}\"
+ [ -z "$(eval echo \"\$_exec_poststop${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ if [ -n "${_exec}" ]; then
+ # simple/backward-compatible execution
+ _exec_start="${_exec}"
+ _exec_stop=""
+ else
+ # flexible execution
+ if [ -z "${_exec_start}" ]; then
+ _exec_start="/bin/sh /etc/rc"
+ if [ -z "${_exec_stop}" ]; then
+ _exec_stop="/bin/sh /etc/rc.shutdown"
+ fi
+ fi
+ fi
+
+ # The default jail ruleset will be used by rc.subr if none is specified.
+ eval _ruleset=\"\${vimage_${_v}_devfs_ruleset:-${vimage_devfs_ruleset}}\"
+ eval _devfs=\"\${vimage_${_v}_devfs_enable:-${vimage_devfs_enable}}\"
+ [ -z "${_devfs}" ] && _devfs="NO"
+ eval _fdescfs=\"\${vimage_${_v}_fdescfs_enable:-${vimage_fdescfs_enable}}\"
+ [ -z "${_fdescfs}" ] && _fdescfs="NO"
+ eval _procfs=\"\${vimage_${_v}_procfs_enable:-${vimage_procfs_enable}}\"
+ [ -z "${_procfs}" ] && _procfs="NO"
+
+ eval _mount=\"\${vimage_${_v}_mount_enable:-${vimage_mount_enable}}\"
+ [ -z "${_mount}" ] && _mount="NO"
+ # "/etc/fstab.${_v}" will be used for {,u}mount(8) if none is specified.
+ eval _fstab=\"\${vimage_${_v}_fstab:-${vimage_fstab}}\"
+ [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_v}"
+ eval _flags=\"\${vimage_${_v}_flags:-${vimage_flags}}\"
+ [ -z "${_flags}" ] && _flags="-l -U root"
+ eval _consolelog=\"\${vimage_${_v}_consolelog:-${vimage_consolelog}}\"
+ [ -z "${_consolelog}" ] && _consolelog="/var/log/vimage_${_v}_console.log"
+
+ # Debugging aid
+ #
+ debug "$_v devfs enable: $_devfs"
+ debug "$_v fdescfs enable: $_fdescfs"
+ debug "$_v procfs enable: $_procfs"
+ debug "$_v mount enable: $_mount"
+ debug "$_v hostname: $_hostname"
+ debug "$_v vnets: $_vnets"
+ debug "$_v services: $_services"
+ debug "$_v root: $_rootdir"
+ debug "$_v devdir: $_devdir"
+ debug "$_v fdescdir: $_fdescdir"
+ debug "$_v procdir: $_procdir"
+ debug "$_v ruleset: $_ruleset"
+ debug "$_v fstab: $_fstab"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestart${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec pre-start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v exec start: $_exec_start"
+
+ i=1
+ while : ; do
+ eval out=\"\${_exec_afterstart${i}:-''}\"
+
+ if [ -z "$out" ]; then
+ break;
+ fi
+
+ debug "$_v exec after start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststart${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec post-start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestop${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec pre-stop #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v exec stop: $_exec_stop"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststop${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec post-stop #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v flags: $_flags"
+ debug "$_v consolelog: $_consolelog"
+
+ if [ -z "${_hostname}" ]; then
+ err 3 "$name: No hostname has been defined for ${_v}"
+ fi
+ if [ -z "${_rootdir}" ]; then
+ err 3 "$name: No root directory has been defined for ${_v}"
+ fi
+}
+
+# set_sysctl rc_knob mib msg
+# If the mib sysctl is set according to what rc_knob
+# specifies, this function does nothing. However if
+# rc_knob is set differently than mib, then the mib
+# is set accordingly and msg is displayed followed by
+# an '=" sign and the word 'YES' or 'NO'.
+#
+set_sysctl()
+{
+ _knob="$1"
+ _mib="$2"
+ _msg="$3"
+
+ _current=`${SYSCTL} -n $_mib 2>/dev/null`
+ if checkyesno $_knob ; then
+ if [ "$_current" -ne 1 ]; then
+ echo -n " ${_msg}=YES"
+ ${SYSCTL} 1>/dev/null ${_mib}=1
+ fi
+ else
+ if [ "$_current" -ne 0 ]; then
+ echo -n " ${_msg}=NO"
+ ${SYSCTL} 1>/dev/null ${_mib}=0
+ fi
+ fi
+}
+
+# is_current_mountpoint()
+# Is the directory mount point for a currently mounted file
+# system?
+#
+is_current_mountpoint()
+{
+ local _dir _dir2
+
+ _dir=$1
+
+ _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'`
+ [ ! -d "${_dir}" ] && return 1
+ _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'`
+ [ "${_dir}" = "${_dir2}" ]
+ return $?
+}
+
+# is_symlinked_mountpoint()
+# Is a mount point, or any of its parent directories, a symlink?
+#
+is_symlinked_mountpoint()
+{
+ local _dir
+
+ _dir=$1
+
+ [ -L "$_dir" ] && return 0
+ [ "$_dir" = "/" ] && return 1
+ is_symlinked_mountpoint `dirname $_dir`
+ return $?
+}
+
+# secure_umount
+# Try to unmount a mount point without being vulnerable to
+# symlink attacks.
+#
+secure_umount()
+{
+ local _dir
+
+ _dir=$1
+
+ if is_current_mountpoint ${_dir}; then
+ umount -f ${_dir} >/dev/null 2>&1
+ else
+ debug "Nothing mounted on ${_dir} - not unmounting"
+ fi
+}
+
+
+# vimage_umount_fs
+# This function unmounts certain special filesystems in the
+# currently selected vimage. The caller must call the init_variables()
+# routine before calling this one.
+#
+vimage_umount_fs()
+{
+ local _device _mountpt _rest
+
+ if checkyesno _fdescfs; then
+ if [ -d "${_fdescdir}" ] ; then
+ secure_umount ${_fdescdir}
+ fi
+ fi
+ if checkyesno _devfs; then
+ if [ -d "${_devdir}" ] ; then
+ secure_umount ${_devdir}
+ fi
+ fi
+ if checkyesno _procfs; then
+ if [ -d "${_procdir}" ] ; then
+ secure_umount ${_procdir}
+ fi
+ fi
+ if checkyesno _mount; then
+ [ -f "${_fstab}" ] || warn "${_fstab} does not exist"
+ tail -r ${_fstab} | while read _device _mountpt _rest; do
+ case ":${_device}" in
+ :#* | :)
+ continue
+ ;;
+ esac
+ secure_umount ${_mountpt}
+ done
+ fi
+}
+
+# vimage_mount_fstab()
+# Mount file systems from a per vimage fstab while trying to
+# secure against symlink attacks at the mount points.
+#
+# If we are certain we cannot secure against symlink attacks we
+# do not mount all of the file systems (since we cannot just not
+# mount the file system with the problematic mount point).
+#
+# The caller must call the init_variables() routine before
+# calling this one.
+#
+vimage_mount_fstab()
+{
+ local _device _mountpt _rest
+
+ while read _device _mountpt _rest; do
+ case ":${_device}" in
+ :#* | :)
+ continue
+ ;;
+ esac
+ if is_symlinked_mountpoint ${_mountpt}; then
+ warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}"
+ return
+ fi
+ done <${_fstab}
+ mount -a -F "${_fstab}"
+}
+
+vimage_prestart()
+{
+ if checkyesno vimage_parallel_start; then
+ command_args="&"
+ fi
+}
+
+vimage_start()
+{
+ echo -n 'Configuring vimages:'
+ set_sysctl vimage_set_hostname_allow \
+ security.jail.set_hostname_allowed \
+ set_hostname_allow
+ set_sysctl vimage_socket_unixiproute_only \
+ security.jail.socket_unixiproute_only unixiproute_only
+ set_sysctl vimage_sysvipc_allow security.jail.sysvipc_allowed \
+ sysvipc_allow
+ echo '.'
+
+ echo -n 'Starting vimages:'
+ _tmp_dir=`mktemp -d /tmp/vimage.XXXXXXXX` || \
+ err 3 "$name: Can't create temp dir, exiting..."
+ for _vimage in ${vimage_list}
+ do
+ init_variables $_vimage
+ if [ -f /var/run/vimage_${_vimage}.id ]; then
+ echo -n " [${_hostname} already running (/var/run/vimage_${_vimage}.id exists)]"
+ continue;
+ fi
+ if checkyesno _mount; then
+ info "Mounting fstab for vimage ${_vimage} (${_fstab})"
+ if [ ! -f "${_fstab}" ]; then
+ err 3 "$name: ${_fstab} does not exist"
+ fi
+ vimage_mount_fstab
+ fi
+ if checkyesno _devfs; then
+ # If devfs is already mounted here, skip it.
+ df -t devfs "${_devdir}" >/dev/null
+ if [ $? -ne 0 ]; then
+ if is_symlinked_mountpoint ${_devdir}; then
+ warn "${_devdir} has symlink as parent - not starting vimage ${_vimage}"
+ continue
+ fi
+ info "Mounting devfs on ${_devdir}"
+ devfs_mount_jail "${_devdir}" ${_ruleset}
+ # Transitional symlink for old binaries
+ if [ ! -L "${_devdir}/log" ]; then
+ __pwd="`pwd`"
+ cd "${_devdir}"
+ ln -sf ../var/run/log log
+ cd "$__pwd"
+ fi
+ fi
+
+ # XXX - It seems symlinks don't work when there
+ # is a devfs(5) device of the same name.
+ # Jail console output
+ # __pwd="`pwd`"
+ # cd "${_devdir}"
+ # ln -sf ../var/log/console console
+ # cd "$__pwd"
+ fi
+ if checkyesno _fdescfs; then
+ if is_symlinked_mountpoint ${_fdescdir}; then
+ warn "${_fdescdir} has symlink as parent, not mounting"
+ else
+ info "Mounting fdescfs on ${_fdescdir}"
+ mount -t fdescfs fdesc "${_fdescdir}"
+ fi
+ fi
+ if checkyesno _procfs; then
+ if is_symlinked_mountpoint ${_procdir}; then
+ warn "${_procdir} has symlink as parent, not mounting"
+ else
+ info "Mounting procfs onto ${_procdir}"
+ if [ -d "${_procdir}" ] ; then
+ mount -t procfs proc "${_procdir}"
+ fi
+ fi
+ fi
+ _tmp_vimage=${_tmp_dir}/vimage.$$
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestart${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+
+ eval jail ${_flags} -i -c vnet name=\"${_vimage}\" \
+ host.hostname=\"${_hostname}\" \
+ path=\"${_rootdir}\" persist > ${_tmp_vimage} 2>&1
+
+ if [ "$?" -eq 0 ] ; then
+ _vimage_id=$(head -1 ${_tmp_vimage})
+
+ for _vnet in ${_vnets}; do
+ ifconfig ${_vnet} vnet "${_vimage_id}" \
+ > /dev/null 2>&1
+
+ case ${_vnet} in
+ epair[0-9]*[ab])
+ ifconfig ${_vnet%?}a up \
+ > /dev/null 2>&1;;
+ esac
+ done
+
+ eval jexec \"${_vimage_id}\" \
+ ${_exec_start} >> ${_tmp_vimage} 2>&1
+
+ for _service in netif routing ${_services}; do
+ eval jexec \"${_vimage_id}\" /bin/sh \
+ /usr/sbin/service ${_service} start \
+ >> ${_tmp_vimage} 2>&1
+ done
+
+ i=1
+ while : ; do
+ eval out=\"\${_exec_afterstart${i}:-''}\"
+
+ if [ -z "$out" ]; then
+ break;
+ fi
+
+ jexec "${_vimage_id}" ${out}
+ i=$((i + 1))
+ done
+
+ echo -n " $_hostname"
+ tail +2 ${_tmp_vimage} >${_consolelog}
+ echo ${_vimage_id} > /var/run/vimage_${_vimage}.id
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststart${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+ else
+ vimage_umount_fs
+ echo " cannot start vimage \"${_vimage}\": "
+ tail +2 ${_tmp_vimage}
+ fi
+ rm -f ${_tmp_vimage}
+ done
+ rmdir ${_tmp_dir}
+ echo '.'
+}
+
+vimage_stop()
+{
+ echo -n 'Stopping vimages:'
+ for _vimage in ${vimage_list}
+ do
+ if [ -f "/var/run/vimage_${_vimage}.id" ]; then
+ _vimage_id=$(cat /var/run/vimage_${_vimage}.id)
+ if [ ! -z "${_vimage_id}" ]; then
+ init_variables $_vimage
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestop${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+
+ if [ -n "${_exec_stop}" ]; then
+ eval env -i /usr/sbin/jexec ${_vimage_id} ${_exec_stop} \
+ >> ${_consolelog} 2>&1
+ fi
+ killall -j ${_vimage_id} -TERM > /dev/null 2>&1
+ sleep 1
+ killall -j ${_vimage_id} -KILL > /dev/null 2>&1
+ vimage_umount_fs
+ echo -n " $_hostname"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststop${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+ fi
+ rm /var/run/vimage_${_vimage}.id
+ jail -r ${_vimage}
+ else
+ echo " cannot stop vimage ${_vimage}. No vimage id in /var/run"
+ fi
+ done
+ echo '.'
+}
+
+load_rc_config $name
+cmd="$1"
+if [ $# -gt 0 ]; then
+ shift
+fi
+if [ -n "$*" ]; then
+ vimage_list="$*"
+fi
+
+run_rc_command "${cmd}"
--Apple-Mail-25--994899661
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="us-ascii"
Essentially, a hand-tweaked version of /etc/rc.d/jail with added/removed =
features.
Here's how we're using it in /etc/rc.conf to successfully start up =
vimage jails at boot time:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D BEGIN EXCERPT =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
#
# Vimages
#
vimage_enable=3D"YES" # Set to NO to disable starting of any vimages
vimage_list=3D"
vnettest
" # Space-separated list of names of vimages
clone_interfaces=3D"" # Initialize list of epair/bridge interfaces =
to create
#
# Global settings for all Vimages
#
vimage_services=3D"sshd"
####################### VIMAGE: vnettest
cloned_interfaces=3D"$cloned_interfaces epair0 bridge0"
ifconfig_bridge0=3D"addm fxp0 addm epair0a"
vimage_vnettest_rootdir=3D"/usr/jails/vnettest" # root =
directory
vimage_vnettest_hostname=3D"vnettest.jbsd.vicor.com" # hostname
vimage_vnettest_devfs_enable=3D"YES" # mount devfs
vimage_vnettest_vnets=3D"epair0b" # network =
interfaces
####################### VIMAGE: {name}
#cloned_interfaces=3D"$cloned_interfaces epair{N} bridge{N}"
#ifconfig_bridge{N}=3D"addm {iface} addm epair{N}a"
#vimage_{name}_rootdir=3D"/usr/jails/{name}" # root =
directory
#vimage_{name}_hostname=3D"{hostname}" # hostname
#vimage_{name}_devfs_enable=3D"YES" # mount devfs
#vimage_{name}_vnets=3D"epair{N}b" # network =
interfaces
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D END EXCERPT =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--=20
Cheers,
Devin=
--Apple-Mail-25--994899661--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAC979C8-3129-4E62-9D76-D1D0CCE001F0>
