From daad2691f8aacff5affdd76b673e6720283def27 Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Tue, 8 Nov 2011 17:26:59 +0000 Subject: [PATCH] Jailrc is an improved startup/shutdown script for FreeBSD jails. It contains the following changes to the original /etc/rc.d/jail script: - parameters support: you can specify any parameters supported by jail(8) - ZFS support: you can deletate ZFS datasets to jails - jails are not identified by a file in /var/spool/jail anymore - two new commands "create" and "remove" to manage persistent jails Please refer to the README file for more information. Martin Matuska --- sysutils/Makefile | 1 + sysutils/jailrc/Makefile | 30 ++ sysutils/jailrc/files/README | 53 ++ sysutils/jailrc/files/jailrc.in | 923 ++++++++++++++++++++++++++++++++ sysutils/jailrc/pkg-descr | 12 + 5 files changed, 1019 insertions(+) create mode 100644 sysutils/jailrc/Makefile create mode 100644 sysutils/jailrc/files/README create mode 100644 sysutils/jailrc/files/jailrc.in create mode 100644 sysutils/jailrc/pkg-descr diff --git a/sysutils/Makefile b/sysutils/Makefile index b2baf03f7353..5e13d2da1d46 100644 --- a/sysutils/Makefile +++ b/sysutils/Makefile @@ -378,6 +378,7 @@ SUBDIR += jailctl SUBDIR += jailer SUBDIR += jailme + SUBDIR += jailrc SUBDIR += jailuser SUBDIR += jailutils SUBDIR += javaservicewrapper diff --git a/sysutils/jailrc/Makefile b/sysutils/jailrc/Makefile new file mode 100644 index 000000000000..78955f2e22fd --- /dev/null +++ b/sysutils/jailrc/Makefile @@ -0,0 +1,30 @@ +# New ports collection makefile for: jailrc +# Date created: 8 November 2011 +# Whom: Martin Matuska +# +# $FreeBSD$ +# + +PORTNAME= jailrc +PORTVERSION= 1.0 +CATEGORIES= sysutils +MASTER_SITES= # +DISTFILES= # + +MAINTAINER= mm@FreeBSD.org +COMMENT= Improved jail startup/shutdown script + +NO_BUILD= yes +NO_INSTALL= yes + +USE_RC_SUBR= jailrc + +PORTDOCS= * + +post-install: +.if !defined(NOPORTDOCS) + @${MKDIR} ${DOCSDIR} + @${INSTALL_DATA} ${FILESDIR}/README ${DOCSDIR}/README +.endif + +.include diff --git a/sysutils/jailrc/files/README b/sysutils/jailrc/files/README new file mode 100644 index 000000000000..9799b204d367 --- /dev/null +++ b/sysutils/jailrc/files/README @@ -0,0 +1,53 @@ +# Jailrc README +# Martin Matuska +# +# $FreeBSD$ +# + +Jailrc is an improved startup/shutdown script for FreeBSD jails. + +It contains the following changes to the original /etc/rc.d/jail script: + +- parameters support: you can specify parameters supported by jail(8) +- ZFS support: you can deletate ZFS datasets to jails +- jails are not identified by a file in /var/spool/jail anymore +- two new commands "create" and "remove" to manage persistent jails + +To start jails with the jailrc script instead of /etc/rc.d/jail, use +instead of jail_enable="YES" the keyword jailrc_enable="YES". + +------------------------------------------------------------------ +Jailrc reads all settings supported by /etc/rc.d/jail +(see /etc/defaults/rc.conf) and the following additional settings: + +jail_example_name="" # Set to desired jail name + # defaults to the name in "jail_list" +jail_example_persist="" # Set to YES to create a persistent jail +jail_example_params="" # Space-separated list of additional + # user-supplied parameters for jail(8) +jail_example_zfs="" # Space-separated list of ZFS datasets to be + # managed from this jail. For proper operation, + # allow.mount=1 and enforce_statfs=1 (or 0) + # must be added to jail_example_params. + # The "jailed" property must be set to "on" + # on desired datasets before starting the jail. + +------------------------------------------------------------------ +Example rc.conf configuration with IPv4, IPv6 and a fixed jail ID: + +jail_enable="NO" +jailrc_enable="YES" +jail_list="test" +jail_test_hostname="test.bb.cc" +jail_test_rootdir="/jail/test" +jail_test_ip="192.168.0.2,2001:db8:2:1::2" +jail_test_devfs_enable="YES" +jail_test_params="jid=8 enforce_statfs=1 allow.raw_sockets=1" + +------------------------------------------------------------------ +To enable the ZFS device (/dev/zfs) in jails, add the following lines +to your devfs.rules configuration file: + +[devfsrules_jail_zfs=5] +add include $devfsrules_jail +add path zfs unhide diff --git a/sysutils/jailrc/files/jailrc.in b/sysutils/jailrc/files/jailrc.in new file mode 100644 index 000000000000..06c7834d7a55 --- /dev/null +++ b/sysutils/jailrc/files/jailrc.in @@ -0,0 +1,923 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: jail +# REQUIRE: LOGIN cleanvar +# BEFORE: securelevel +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="jailrc" +rcvar=`set_rcvar` + +start_precmd="jail_prestart" +start_cmd="jail_start" +stop_cmd="jail_stop" +extra_commands="status create remove" +status_cmd="jail_status" +create_cmd="jail_create" +remove_cmd="jail_remove" + +# init_name _j +# Initialize the name variable for jail _j. +# +init_name() +{ + _j="$1" + + if [ -z "$_j" ]; then + warn "init_name: you must specify a jail" + return + fi + + eval _name=\"\$jail_${_j}_name\" + + if [ -z "${_name}" ]; then + _name=${_j} + fi + + # Debugging aid + debug "$_j name: $_name" +} + +# init_variables _j +# Initialize the various jail variables for jail _j. +# +init_variables() +{ + _j="$1" + + if [ -z "$_j" ]; then + warn "init_variables: you must specify a jail" + return + fi + + eval _rootdir=\"\$jail_${_j}_rootdir\" + _devdir="${_rootdir}/dev" + _fdescdir="${_devdir}/fd" + _procdir="${_rootdir}/proc" + eval _hostname=\"\$jail_${_j}_hostname\" + eval _ip=\"\$jail_${_j}_ip\" + eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" + eval _exec=\"\$jail_${_j}_exec\" + eval _params=\"\${jail_${_j}_params:-${jail_params}}\" + eval _zfs=\"\${jail_${_j}_zfs:-}\" + + i=0 + while : ; do + eval _exec_prestart${i}=\"\${jail_${_j}_exec_prestart${i}:-\${jail_exec_prestart${i}}}\" + [ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break + i=$((i + 1)) + done + + eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" + + i=1 + while : ; do + eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" + [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break + i=$((i + 1)) + done + + i=0 + while : ; do + eval _exec_poststart${i}=\"\${jail_${_j}_exec_poststart${i}:-\${jail_exec_poststart${i}}}\" + [ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break + i=$((i + 1)) + done + + i=0 + while : ; do + eval _exec_prestop${i}=\"\${jail_${_j}_exec_prestop${i}:-\${jail_exec_prestop${i}}}\" + [ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break + i=$((i + 1)) + done + + eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" + + i=0 + while : ; do + eval _exec_poststop${i}=\"\${jail_${_j}_exec_poststop${i}:-\${jail_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. + if [ -n "${_zfs}" ]; then + eval jail_devfs_ruleset=\"${jail_devfs_ruleset:-devfsrules_jail_zfs}\" + else + eval jail_devfs_ruleset=\"${jail_devfs_ruleset:-devfsrules_jail}\" + fi + eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" + eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" + [ -z "${_devfs}" ] && _devfs="NO" + eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" + [ -z "${_fdescfs}" ] && _fdescfs="NO" + eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" + [ -z "${_procfs}" ] && _procfs="NO" + + eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" + [ -z "${_mount}" ] && _mount="NO" + # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. + eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" + [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" + eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" + [ -z "${_flags}" ] && _flags="-l -U root" + eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" + [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" + eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\" + eval _persist=\"\${jail_${_j}_persist:-${jail_persist}}\" + [ -z "${_persist}" ] && _persist="NO" + + # Debugging aid + # + debug "$_j devfs enable: $_devfs" + debug "$_j fdescfs enable: $_fdescfs" + debug "$_j procfs enable: $_procfs" + debug "$_j mount enable: $_mount" + debug "$_j hostname: $_hostname" + debug "$_j ip: $_ip" + jail_show_addresses ${_j} + debug "$_j interface: $_interface" + debug "$_j fib: $_fib" + debug "$_j root: $_rootdir" + debug "$_j devdir: $_devdir" + debug "$_j fdescdir: $_fdescdir" + debug "$_j procdir: $_procdir" + debug "$_j ruleset: $_ruleset" + debug "$_j fstab: $_fstab" + debug "$_j persist: $_persist" + debug "$_j params: $_params" + debug "$_j zfs: $_zfs" + + i=0 + while : ; do + eval out=\"\${_exec_prestart${i}:-''}\" + if [ -z "$out" ]; then + break + fi + debug "$_j exec pre-start #${i}: ${out}" + i=$((i + 1)) + done + + debug "$_j exec start: $_exec_start" + + i=1 + while : ; do + eval out=\"\${_exec_afterstart${i}:-''}\" + + if [ -z "$out" ]; then + break; + fi + + debug "$_j 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 "$_j 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 "$_j exec pre-stop #${i}: ${out}" + i=$((i + 1)) + done + + debug "$_j exec stop: $_exec_stop" + + i=0 + while : ; do + eval out=\"\${_exec_poststop${i}:-''}\" + if [ -z "$out" ]; then + break + fi + debug "$_j exec post-stop #${i}: ${out}" + i=$((i + 1)) + done + + debug "$_j flags: $_flags" + debug "$_j consolelog: $_consolelog" + + _params="${_params} name=${_name}" + [ -n "${_hostname}" ] && _params="${_params} host.hostname=${_hostname}" + + if [ -z "${_rootdir}" ]; then + err 3 "$name: No root directory has been defined for ${_j}" + else + _params="${_params} path=${_rootdir}" + fi + + # Compatibility with old sysctl knobs + # + eval _set_hostname_allow="\${jail_${_j}_set_hostname_allow:-${jail_set_hostname_allow}}" + eval _socket_unixiproute_only="\${jail_${_j}_socket_unixiproute_only:-${jail_socket_unixiproute_only}}" + eval _sysvipc_allow="\${jail_${_j}_sysvipc_allow:-${jail_sysvipc_allow}}" + [ -z "${_set_hostname_allow}" ] && _set_hostname_allow="YES" + [ -z "${_socket_unixiproute_only}" ] && _socket_unixiproute_only="YES" + [ -z "${_sysvipc_allow}" ] && _sysvipc_allow="NO" + + if ! checkyesno _set_hostname_allow; then + _params="${_params} allow.set_hostname=0" + fi + + if ! checkyesno _socket_unixiproute_only; then + _params="${_params} allow.socket_af=1" + fi + + if checkyesno _sysvipc_allow; then + _params="${_params} allow.sysvipc=1" + 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 +} + + +# jail_umount_fs +# This function unmounts certain special filesystems in the +# currently selected jail. The caller must call the init_variables() +# routine before calling this one. +# +jail_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 +} + +# jail_mount_fstab() +# Mount file systems from a per jail 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. +# +jail_mount_fstab() +{ + local _device _mountpt _field3 _field4 _field5 _field6 + + while read _device _mountpt _field3 _field4 _field5 _field6; do + case ":${_device}:" in + :#*:|::) + # empty line or comment + continue + ;; + esac + if [ -z "${_field5}" ]; then + # a fstab entry needs at least 5 fields + # everything else is suspicious + warn "${_device} ${_mountpt} ${_field3} ${_field4} ${_field5} ${_field6} not conforming to 5 or more whitespace separated fields" + return + fi + if is_symlinked_mountpoint ${_mountpt}; then + warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" + return + fi + done <${_fstab} + mount -a -F "${_fstab}" +} + +# jail_zfs_jailin +# Make zfs datasets manageable from inside a jail +# the "jailed" dataset property must be set to "on" +jail_zfs_jailin() +{ + if [ -n "${_zfs}" ]; then + for _ds in ${_zfs}; do + _jailed=`zfs get -H jailed ${_ds} 2>/dev/null | awk '{ print $3 }'` + if [ "${_jailed}" = "on" ]; then + debug "${j} jailing zfs dataset: ${_ds}" + zfs jail "${_jail_id}" ${_ds} 2>/dev/null + fi + done + fi +} + +# jail_zfs_jailout +# Unjail zfs datasets +# the "jailed" dataset property must be set to "on" +jail_zfs_jailout() +{ + if [ -n "${_zfs}" ]; then + for _ds in ${_zfs}; do + _jailed=`zfs get -H jailed ${_ds} 2>/dev/null | awk '{ print $3 }'` + if [ "${_jailed}" = "on" ]; then + debug "${j} unjailing zfs dataset: ${_ds}" + zfs unjail "${_jail_id}" ${_ds} 2>/dev/null + fi + done + fi +} + +# jail_show_addresses jail +# Debug print the input for the given _multi aliases +# for a jail for init_variables(). +# +jail_show_addresses() +{ + local _j _type alias + _j="$1" + alias=0 + + if [ -z "${_j}" ]; then + warn "jail_show_addresses: you must specify a jail" + return + fi + + while : ; do + eval _addr=\"\$jail_${_j}_ip_multi${alias}\" + if [ -n "${_addr}" ]; then + debug "${_j} ip_multi${alias}: $_addr" + alias=$((${alias} + 1)) + else + break + fi + done +} + +# jail_extract_address argument +# The second argument is the string from one of the _ip +# or the _multi variables. In case of a comma separated list +# only one argument must be passed in at a time. +# The function alters the _type, _iface, _addr and _mask variables. +# +jail_extract_address() +{ + local _i + _i=$1 + + if [ -z "${_i}" ]; then + warn "jail_extract_address: called without input" + return + fi + + # Check if we have an interface prefix given and split into + # iFace and rest. + case "${_i}" in + *\|*) # ifN|.. prefix there + _iface=${_i%%|*} + _r=${_i##*|} + ;; + *) _iface="" + _r=${_i} + ;; + esac + + # In case the IP has no interface given, check if we have a global one. + _iface=${_iface:-${_interface}} + + # Set address, cut off any prefix/netmask/prefixlen. + _addr=${_r} + _addr=${_addr%%[/ ]*} + + # Theoretically we can return here if interface is not set, + # as we only care about the _mask if we call ifconfig. + # This is not done because we may want to santize IP addresses + # based on _type later, and optionally change the type as well. + + # Extract the prefix/netmask/prefixlen part by cutting off the address. + _mask=${_r} + _mask=`expr "${_mask}" : "${_addr}\(.*\)"` + + # Identify type {inet,inet6}. + case "${_addr}" in + *\.*\.*\.*) _type="inet" ;; + *:*) _type="inet6" ;; + *) warn "jail_extract_address: type not identified" + ;; + esac + + # Handle the special /netmask instead of /prefix or + # "netmask xxx" case for legacy IP. + # We do NOT support shortend class-full netmasks. + if [ "${_type}" = "inet" ]; then + case "${_mask}" in + /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; + *) ;; + esac + + # In case _mask is still not set use /32. + _mask=${_mask:-/32} + + elif [ "${_type}" = "inet6" ]; then + # In case _maske is not set for IPv6, use /128. + _mask=${_mask:-/128} + fi +} + +# jail_handle_ips_option {add,del} input +# Handle a single argument imput which can be a comma separated +# list of addresses (theoretically with an option interface and +# prefix/netmask/prefixlen). +# +jail_handle_ips_option() +{ + local _x _action _type _i + _action=$1 + _x=$2 + + if [ -z "${_x}" ]; then + # No IP given. This can happen for the primary address + # of each address family. + return + fi + + # Loop, in case we find a comma separated list, we need to handle + # each argument on its own. + while [ ${#_x} -gt 0 ]; do + case "${_x}" in + *,*) # Extract the first argument and strip it off the list. + _i=`expr "${_x}" : '^\([^,]*\)'` + _x=`expr "${_x}" : "^[^,]*,\(.*\)"` + ;; + *) _i=${_x} + _x="" + ;; + esac + + _type="" + _iface="" + _addr="" + _mask="" + jail_extract_address "${_i}" + + # make sure we got an address. + case "${_addr}" in + "") continue ;; + *) ;; + esac + + case "${_type}" in + inet) + # Append address to list of ipv4 addresses for the + # jail command. + case "${_addrl}" in + "") _addrl="${_addr}" ;; + *) _addrl="${_addrl},${_addr}" ;; + esac + ;; + inet6) + # Append address to list of ipv6 addresses for the + # jail command. + case "${_addrl6}" in + "") _addrl6="${_addr}" ;; + *) _addrl6="${_addrl6},${_addr}" ;; + esac + ;; + *) warn "Could not determine address family. Not going" \ + "to set address '${_addr}' for ${_jail}." + continue + ;; + esac + + # Configure interface alias if requested by a given interface + # and if we could correctly parse everything. + case "${_iface}" in + "") continue ;; + esac + + case "${_action}" in + add) ifconfig ${_iface} ${_type} ${_addr}${_mask} alias + ;; + del) # When removing the IP, ignore the _mask. + ifconfig ${_iface} ${_type} ${_addr} -alias + ;; + esac + done +} + +# jail_ips {add,del} +# Extract the comma separated list of addresses and return them +# for the jail command. +# Handle more than one address via the _multi option as well. +# If an interface is given also add/remove an alias for the +# address with an optional netmask. +# +jail_ips() +{ + local _action + _action=$1 + + case "${_action}" in + add) ;; + del) ;; + *) warn "jail_ips: invalid action '${_action}'" + return + ;; + esac + + # Handle addresses. + jail_handle_ips_option ${_action} "${_ip}" + # Handle jail_xxx_ip_multi + alias=0 + while : ; do + eval _x=\"\$jail_${_jail}_ip_multi${alias}\" + case "${_x}" in + "") break ;; + *) jail_handle_ips_option ${_action} "${_x}" + alias=$((${alias} + 1)) + ;; + esac + done +} + +jail_prestart() +{ + if checkyesno jail_parallel_start; then + command_args='&' + fi +} + +jail_init() +{ + _jail=$1 + init_name $_jail + init_variables $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + return 1 + fi + _addrl="" + _addrl6="" + jail_ips "add" + + [ -n "${_addrl}" ] && _params="${_params} ip4.addr=${_addrl}" + [ -n "${_addrl6}" ] && _params="${_params} ip6.addr=${_addrl6}" + + if [ -n "${_fib}" ]; then + _setfib="setfib -F '${_fib}'" + else + _setfib="" + fi + if checkyesno _mount; then + info "Mounting fstab for jail ${_jail} (${_fstab})" + if [ ! -f "${_fstab}" ]; then + err 3 "$name: ${_fstab} does not exist" + fi + jail_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 jail ${_jail}" + return 2 + 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 + + _jail_id=`${_setfib} jail -i ${_flags} -c \ + ${_params} persist=1` + + if [ -n "${_jail_id}" ]; then + jail_zfs_jailin + return 0 + else + return 2 + fi +} + +jail_destroy() +{ + _jail=$1 + init_name $_jail + init_variables $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -z "${_jail_id}" ]; then + return 1 + fi + jail -m jid=${_jail_id} persist=1 + killall -j ${_jail_id} -TERM > /dev/null 2>&1 + sleep 1 + killall -j ${_jail_id} -KILL > /dev/null 2>&1 + + jail_umount_fs + jail_zfs_jailout + jail -m jid="${_jail_id}" persist=0 + return 0 +} + +jail_create() +{ + echo -n 'Creating jails:' + for _jail in ${jail_list} + do + init_name $_jail + init_variables $_jail + jail_init $_jail + _ret=$? + if [ "$_ret" -eq 0 ]; then + echo -n " ${_jail}" + elif [ "$_ret" -eq 1 ]; then + echo -n " [${_jail} already exists (JID: ${_jail_id})]" + else + echo -n "[${_jail} init error]" + fi + done + echo "." +} + +jail_remove() +{ + echo -n 'Removing jails:' + for _jail in ${jail_list} + do + init_name $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -z "${_jail_id}" ]; then + continue + elif `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} has running processes (JID: ${_jail_id})]" + continue + fi + jail_destroy $_jail + if [ "$?" -eq 0 ] ; then + echo -n " ${_jail}" + fi + done + echo "." +} + +jail_start() +{ + echo -n 'Starting jails:' + _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ + err 3 "$name: Can't create temp dir, exiting..." + + for _jail in ${jail_list} + do + init_name $_jail + init_variables $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + if `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} already running (JID: ${_jail_id})]" + continue + fi + else + jail_init $_jail + if [ "$?" -ne 0 ] ; then + echo -n " [${_jail} initialization error]" + continue + fi + fi + + _tmp_jail=${_tmp_dir}/jail.$$ + + i=0 + while : ; do + eval out=\"\${_exec_prestart${i}:-''}\" + [ -z "$out" ] && break + ${out} + i=$((i + 1)) + done + + eval jail ${_flags} -m jid="${_jail_id}" \ + command="${_exec_start}" > ${_tmp_jail} 2>&1 \ + ${_consolelog} + else + echo " cannot start jail \"${_jail}\": " + tail +2 ${_tmp_jail} + fi + rm -f ${_tmp_jail} + done + echo "." +} + +jail_stop() +{ + echo -n 'Stopping jails:' + for _jail in ${jail_list} + do + init_name $_jail + _jail_id=$(jls -j ${_name} jid 2>/dev/null) + if [ -n "${_jail_id}" ]; then + init_variables $_jail + + if ! `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} has no running processes (JID: ${_jail_id})]" + continue + fi + + 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 ${_jail_id} ${_exec_stop} \ + >> ${_consolelog} 2>&1 + fi + + jail -m jid=${_jail_id} persist=1 + killall -j ${_jail_id} -TERM > /dev/null 2>&1 + sleep 1 + killall -j ${_jail_id} -KILL > /dev/null 2>&1 + + if ! checkyesno _persist; then + jail_destroy $_jail + fi + echo -n " $_jail" + + i=0 + while : ; do + eval out=\"\${_exec_poststop${i}:-''}\" + [ -z "$out" ] && break + ${out} + i=$((i + 1)) + done + + fi + jail_ips "del" + done + echo '.' +} + +jail_status() +{ + for _jail in ${jail_list} + do + init_name $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + _procs=`pgrep -j ${_jail_id} | grep -c .` + if [ "${_procs}" -gt 0 ]; then + _procs=$((${_procs} - 1)) + fi + echo "${_jail} online (JID: ${_jail_id}, processes: ${_procs})" + else + echo "${_jail} offline" + fi + done +} + +load_rc_config $name +: ${jailrc_enable="NO"} +cmd="$1" +if [ $# -gt 0 ]; then + shift +fi +if [ -n "$*" ]; then + jail_list="$*" +fi + +run_rc_command "${cmd}" diff --git a/sysutils/jailrc/pkg-descr b/sysutils/jailrc/pkg-descr new file mode 100644 index 000000000000..d04d3802cd65 --- /dev/null +++ b/sysutils/jailrc/pkg-descr @@ -0,0 +1,12 @@ +Jailrc is an improved startup/shutdown script for FreeBSD jails. + +It contains the following changes to the original /etc/rc.d/jail script: + +- parameters support: you can specify any parameters supported by jail(8) +- ZFS support: you can deletate ZFS datasets to jails +- jails are not identified by a file in /var/spool/jail anymore +- two new commands "create" and "remove" to manage persistent jails + +Please refer to the README file for more information. + +Martin Matuska