#!/bin/sh # ------------------------------------- # Jail management script # Copyright (c) 2004-2008 Eirik Oeverby # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * 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. # * The name(s) of the author(s) may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. # ------------------------------------- ############################################################################## # Configuration section # # # # Change the CFGFILE variable to point to your jail configuration file. # ############################################################################## # Clear the hook lists (for security reasons) unset BEFORESTART_HOOKS unset AFTERSTART_HOOKS unset BEFORESTOP_HOOKS unset AFTERSTOP_HOOKS unset BEFORESTATUS_HOOKS unset AFTERSTATUS_HOOKS unset BEFOREBACKUP_HOOKS unset AFTERBACKUP_HOOKS unset BEFORERESTORE_HOOKS unset AFTERRESTORE_HOOKS unset BEFORECREATE_HOOKS unset AFTERCREATE_HOOKS unset BEFOREUPGRADE_HOOKS unset AFTERUPGRADE_HOOKS unset BEFOREDELETE_HOOKS unset AFTERDELETE_HOOKS CFGFILE="/usr/local/etc/jails.conf" . ${CFGFILE} PROCFS=`expr "$PROCFS" : "[tT][rR][uU][eE]"` LINPROCFS=`expr "$LINPROCFS" : "[tT][rR][uU][eE]"` ############################################################################## # Main function section # # # # These functions are wrappers for the action functions below. They do # # various sanity checking of input parameters, and verify the existence and # # required states of the affected jail(s). # ############################################################################## usage() { ## Output usage information echo "Usage:" >&2 echo "jailctl []" >&2 echo " = start|stop|status|create|delete|upgrade|backup|restore" >&2 echo " = hostname|all" >&2 echo " = Backup destination / restore source" >&2 echo >&2 } jail_status() { ## Output the status of one or several jails if [ ! $JAIL ] || [ $JAIL = "all" ] || jail_exists ; then # Jail exists (or "all" was specified), we can query its status if [ ! $JAIL ] || [ $JAIL = "all" ] ; then # Output a brief list for all jails echo "Jail status (*=running, !=not configured):" for JAIL in $JAILS ; do # Loop through jails JAIL=`expr "$JAIL" : "\(.*\):.*"` # Run hooks jail_run_hooks before-status if jail_exists && jail_running ; then # Jail is running echo "*$JAIL ($(jail_ip))" elif jail_exists ; then # Jail not running echo " $JAIL ($(jail_ip))" else # Jail nonexistant or not configured echo "!$JAIL ($(jail_ip))" fi # Run hooks jail_run_hooks after-status done else # Output information for a specific jail # Run hooks jail_run_hooks before-status if jail_running ; then # Jail is running, be verbose echo "$JAIL ($(jail_ip)) is up." echo "Path: $(jail_path)" echo "Interface: $(jail_if)" echo "Process list:" # Output process list for jail jps elif jail_exists; then # Jail not running echo "$JAIL ($(jail_ip)) is down." echo "Path: $(jail_path)" echo "Interface: $(jail_if)" else # Jail nonexistant or not configured echo "Unable to query jail $JAIL!" echo "Incomplete configuration?" fi # Run hooks jail_run_hooks after-status fi else echo "Path: $(jail_path)" echo "Interface: $(jail_if)" # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } jail_start() { ## Start one or several jails if [ $JAIL ] && (jail_exists || [ $JAIL = "all" ]) ; then # Jail exists (or "all" was specified), we can attempt to start it if [ $JAIL = "all" ] ; then # Attempting to start all jails for JAIL in $JAILS ; do # Loop through jails JAIL=`expr "$JAIL" : "\(.*\):.*"` if jail_running ; then # Jail is running, cannot start echo "Jail already running!" elif jail_exists ; then # Jail not running, starting echo "Starting jail $JAIL..." jstart else # Jail nonexistant or not configured echo "Unable to start jail $JAIL!" echo "Incomplete configuration?" fi done else # Start a specific jail if jail_running && [ ! $FORCE ] ; then # Jail is running, cannot start echo "Jail already running!" elif jail_exists ; then # Jail not running, starting echo "Starting jail $JAIL..." jstart else # Jail nonexistant or not configured echo "Unable to start jail $JAIL!" echo "Incomplete configuration?" fi fi else # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } jail_stop() { ## Stop one or several jails if [ $JAIL ] && (jail_exists || [ $JAIL = "all" ]) ; then # Jail exists (or "all" was specified), we can attempt to stop it if [ $JAIL = "all" ] ; then # Attempting to stop all jails if [ $VERSION -eq 4 ] && [ `ls /proc | wc -l` -eq 0 ] ; then # We are on FreeBSD 4.x, and we have no /proc to rely on jstop else for JAIL in $JAILS ; do # Loop through jails JAIL=`expr "$JAIL" : "\(.*\):.*"` if jail_exists && jail_running ; then # Jail is running, stopping echo "Stopping jail $JAIL..." jstop elif jail_exists && ! jail_running ; then # Jail not running, cannot stop echo "Jail not running ($JAIL)!" else # Jail nonexistant or not configured echo "Unable to stop jail $JAIL!" echo "Incomplete configuration?" fi done fi else if jail_exists && jail_running ; then if [ $VERSION -eq 4 ] && [ `ls /proc | wc -l` -eq 0 ] ; then # We are on FreeBSD 4.x, and we have no /proc to rely on echo 'Without a proc filesystem, you must use "jailctl stop all"!' else # Jail running, stopping echo "Stopping jail $JAIL..." jstop fi elif jail_exists && ! jail_running ; then # Jail not running, cannot stop echo "Jail not running!" else # Jail nonexistant or not configured echo "Unable to start jail $JAIL!" echo "Incomplete configuration?" fi fi else # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } jail_create() { ## Create a jail # Be more specific in distinguishing return codes from jail_exists jail_exists ; RC=$? if [ $JAIL ] && [ $RC -eq 1 ] ; then # If the jail is configured but does not exist on disk, create the jail echo "Creating jail $JAIL..." jcreate else # Jail not configured or already exists echo "Jail $JAIL cannot be created!" fi return 0 } jail_upgrade() { ## Upgrade one or several jails if [ $JAIL ] && (jail_exists || [ $JAIL = "all" ]) ; then # Jail exists (or "all" was specified), we can attempt to upgrade it if [ $JAIL = "all" ] ; then # Attempting to upgrade all jails for JAIL in $JAILS ; do # Loop through jails JAIL=`expr "$JAIL" : "\(.*\):.*"` if jail_exists ; then # Jail exists and is not running, upgrading echo "Upgrading jail $JAIL..." jupgrade else # Jail nonexistant or not configured echo "Jail does not exist, or not configured!" fi done else if jail_exists ; then # Jail exists and is not running, upgrading echo "Upgrading jail $JAIL..." jupgrade else # Jail nonexistant or not configured echo "Jail does not exist, or not configured!" fi fi else # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } jail_delete() { ## Delete a specific jail if [ $JAIL ] && ((jail_exists && ! jail_running) || [ $FORCE ]) ; then # Jail exists and is not running, deleting echo "Deleting jail $JAIL..." jdelete else # Jail nonexistant, running or not configured, cannot delete echo "Jail $JAIL cannot be deleted!" fi return 0 } jail_backup() { ## Back up one or several jails if [ $JAIL ] && (jail_exists || [ $JAIL = "all" ]) ; then # Jail exists (or "all" was specified), we can attempt to back it up if [ $JAIL = "all" ] ; then # Attempting to back up all jails for JAIL in $JAILS ; do # Loop through jails JAIL=`expr "$JAIL" : "\(.*\):.*"` if jail_exists && ! jail_running ; then # Jail exists and is not running, doing cold backup echo "Doing cold backup of jail $JAIL..." jbackup cold elif jail_exists && jail_running ; then # Jail is running, doing warm backup echo "Doing warm backup of jail $JAIL..." jbackup else # Jail nonexistant or not configured echo "Jail does not exist, or not configured!" fi done else if jail_exists && ! jail_running ; then # Jail exists and is not running, doing cold backup echo "Doing cold backup of jail $JAIL..." jbackup cold elif jail_exists && jail_running ; then # Jail is running, doing warm backup echo "Doing warm backup of jail $JAIL..." jbackup else # Jail nonexistant or not configured echo "Jail does not exist, or not configured!" fi fi else # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } jail_restore() { ## Restore a jail # Be more specific in distinguishing return codes from jail_exists jail_exists ; RC=$? if [ $JAIL ] && [ $RC -eq 1 ] ; then echo "Restoring jail $JAIL from backup" local JP=$(jail_path) # Restore the jail jrestore else # No jail was specified, or the specified jail doesn't exist (on disk # or in jails.conf. Show usage information) echo "No valid jail specified!" echo usage fi } # ### Helper function section # jail_exists() { ## Query a jails existence local J=`echo $JAILS | tr " " "\n" | grep "^${JAIL}:"` # for J in $JAILS ; do # Loop through configured jails local J_NAME=`expr "$J" : "\(.*\):.*"` if [ $J_NAME = $JAIL ] && [ -d "$(jail_path)" ] && \ [ `expr $(ls -a $(jail_path) | grep -v "^.snap$" | wc -l)` -gt 2 ]; then # Jail is configured, its directory exists, and the # directory contains more than . and .. return 0 elif [ $J_NAME = $JAIL ] && [ ! -d "$(jail_path)" ] || \ ( [ -d "$(jail_path)" ] && \ [ `expr $(ls -a $(jail_path) | grep -v "^.snap$" | wc -l)` -le 2 ]); then # Jail is defined, but not yet created (directory missing or empty) return 1 fi # done # Jail doesn't exist return 2 } jail_name() { ## Query the name of a jail ## Must be updated to be able to search per IP. Searching per name makes ## no sense local J=`echo $JAILS | tr " " "\n" | grep "^${JAIL}:"` # for J in $JAILS ; do # Loop through configured jails local J_NAME=`expr "$J" : "\(.*\):.*"` if [ $J_NAME = $JAIL ] ; then # Name part of jail definition matches echo $J_NAME return 0 fi # done # Jail not found or error return 1 } jail_ip() { ## Query the IP of a jail local J=`echo $JAILS | tr " " "\n" | grep "^${JAIL}:"` # for J in $JAILS ; do # Loop through configured jails local J_NAME=`expr "$J" : "\([^;].*\):.*"` if [ $J_NAME = $JAIL ] ; then # Name part of jail definition matches, output IP echo $J > debug.file local J_IP=`expr "$J" : ".*:\([^;]*\)"` echo $J_IP return 0 fi # done # Jail not found or error return 1 } jail_path() { ## Query the path to a jail local J=`echo $JAILS | tr " " "\n" | grep "^${JAIL}:"` # for J in $JAILS ; do # Loop through configured jails local J_NAME=`expr "$J" : "\(.*\):.*"` if [ $J_NAME = $JAIL ] ; then # Name part of jail definition matches, output path local J_HOME=`expr "$J" : "[^;]*;\([^;]*\).*$"` if [ -n "$J_HOME" ] ; then if [ `expr "$J_HOME" : ".*\/$"` -gt 0 ] ; then local J_PATH=`expr "$J_HOME" : "\(.*\)/$"` else local J_PATH="${J_HOME}/${J_NAME}" fi else local J_PATH="${JAIL_HOME}$J_NAME" fi echo $J_PATH return 0 fi # done # Jail not found or error return 1 } jail_if() { ## Query the path to a jail local J=`echo $JAILS | tr " " "\n" | grep "^${JAIL}:"` # for J in $JAILS ; do # Loop through configured jails local J_NAME=`expr "$J" : "\(.*\):.*"` if [ $J_NAME = $JAIL ] ; then # Name part of jail definition matches, output path local J_HOME=`expr "$J" : "[^;]*;\([^;]*\).*$"` local J_IF=`expr "$J" : ".*;[^;]*;\([^;]*\).*$"` if [ -z "$J_IF" ] ; then local J_IF=$IF fi echo $J_IF return 0 fi # done # Jail not found or error return 1 } jail_running() { ## Query the running state of a jail if [ $VERSION -ge 5 ] ; then # We are on FreeBSD 5.x, using jls(1) tool local JLS="`/usr/sbin/jls | grep \"\/${JAIL}$\"`" if [ ! "$JLS" ] ; then # Jail is not running return 1 fi for i in "$JLS" ; do # Fetching output string, concatenating local J_LIST="$J_LIST $i" done # Setting JAIL_ID variable; this is the system jail ID JAIL_ID=`echo $J_LIST | cut -d \ -f 1` local JPS=`expr "\`/usr/sbin/jexec $JAIL_ID /bin/ps ax | grep -v \"ps\ ax\" | wc -l | cut -f 2\`" : "[[:space:]]*\([0-9]*\).*"` if [ "$JPS" -lt 2 ] ; then # Jail is not running (no processes, anyway) return 1 fi else # We are on FreeBSD 4.x, use old dirty trick if [ ! -f "/var/run/jails/${JAIL}.running" ] ; then # Jail is not running return 1 fi fi # Jail is running return 0 } # ### Activity function section # jail_run_hooks() { ## Select a hook list to run case $1 in before-start) jrunhooks "$1" "$BEFORESTART_HOOKS" ;; after-start) jrunhooks "$1" "$AFTERSTART_HOOKS" ;; before-stop) jrunhooks "$1" "$BEFORESTOP_HOOKS" ;; after-stop) jrunhooks "$1" "$AFTERSTOP_HOOKS" ;; before-status) jrunhooks "$1" "$BEFORESTATUS_HOOKS" ;; after-status) jrunhooks "$1" "$AFTERSTATUS_HOOKS" ;; before-backup) jrunhooks "$1" "$BEFOREBACKUP_HOOKS" ;; after-backup) jrunhooks "$1" "$AFTERBACKUP_HOOKS" ;; before-restore) jrunhooks "$1" "$BEFORERESTORE_HOOKS" ;; after-restore) jrunhooks "$1" "$AFTERRESTORE_HOOKS" ;; before-create) jrunhooks "$1" "$BEFORECREATE_HOOKS" ;; after-create) jrunhooks "$1" "$AFTERCREATE_HOOKS" ;; before-upgrade) jrunhooks "$1" "$BEFOREUPGRADE_HOOKS" ;; after-upgrade) jrunhooks "$1" "$AFTERUPGRADE_HOOKS" ;; before-delete) jrunhooks "$1" "$BEFOREDELETE_HOOKS" ;; after-delete) jrunhooks "$1" "$AFTERDELETE_HOOKS" ;; esac } jps() { ## List running processes in a jail if [ $VERSION -ge 5 ] ; then # We are on FreeBSD 5.x, use jexec(1) tool /usr/sbin/jexec $JAIL_ID ps auxwww else # We are on FreeBSD 4.x, use old dirty trick (requires /proc on host!) echo "CMD PID TIME UTIME STIME" cat /proc/*/status | grep "${JAIL}" | cut -d \ -f 1 -f 2 -f 8 -f 9 -f 10 fi return 0 } jstart() { ## Start a jail local JP=$(jail_path) local IP=$(jail_ip) local JIF=$(jail_if) if [ $PROCFS -gt 0 ] ; then # Mount proc filesystem into jail /sbin/mount_procfs procfs ${JP}/proc fi if [ $LINPROCFS -gt 0 ] ; then # Mount Linux proc filesystem into jail /sbin/mount_linprocfs linprocfs ${JP}/compat/linux/proc fi if [ $VERSION -ge 5 ] ; then # Run hooks jail_run_hooks before-start # We are on FreeBSD 5.x, use devfs /sbin/mount -t devfs devfs ${JP}/dev devfs -m ${JP}/dev ruleset 4 devfs -m ${JP}/dev rule applyset fi # Bring up network interface alias and start jail [ $(echo $JIF | cut -c 1-4) != 'carp' ] && ifconfig $JIF inet $IP netmask 0xffffffff alias echo >> ${JP}/var/log/jailstart.log echo $(date) >> ${JP}/var/log/jailstart.log jail $JP $JAIL $IP /bin/sh /etc/rc 2>&1 >${JP}/var/log/jailstart.log & if [ $VERSION -eq 4 ] ; then # We're on FreeBSD 4.x, Create run file touch /var/run/jails/${JAIL}.running fi if [ $VERSION -ge 5 ] ; then # Run hooks jail_run_hooks after-start fi return 0 } jstop() { ## Stop a jail local JP=$(jail_path) local IP=$(jail_ip) local JIF=$(jail_if) if [ $VERSION -ge 5 ] ; then # Run hooks jail_run_hooks before-stop fi echo "Sending TERM signal to jail processes..." if [ $VERSION -ge 5 ] ; then # We are on FreeBSD 5.x, use jexec(1) tool /usr/sbin/jexec $JAIL_ID /bin/sh /etc/rc.shutdown sleep 2 /usr/sbin/jexec $JAIL_ID kill -15 -1 # Waiting for processes to die sleep 4 while jail_running ; do # Some processes are still running, do a kill -9 -1 echo "Some processes would not terminate; sending KILL signal..." /usr/sbin/jexec $JAIL_ID kill -9 -1 # Give processes some time to die sleep 2 done umount -f ${JP}/dev else # We are on FreeBSD 4.x if [ "$JAIL" = "all" ] ; then # /proc is unavailable, so we can only stop ALL jails at once local PS="`ps ax|cut -c 1-16|grep J|cut -d \ -f 1`" for PID in "$PS" ; do kill -15 $PID 2>/dev/null 1>/dev/null done # Waiting for processes to die sleep 4 local PS="`ps ax|cut -c 1-16|grep J|cut -d \ -f 1`" while [ "$PS" ] ; do # Some processes are still running, do a kill -9 on each echo "Some processes would not terminate; sending KILL signal..." for PID in "$PS" ; do # Sending KILL signal to all processes in the jail kill -9 $PID 2>/dev/null 1>/dev/null done # Give processes some time to die sleep 2 local PS="`ps ax|cut -c 1-16|grep J|cut -d \ -f 1`" done else # Use /proc filesystem (REQUIRED for single-jail operation!) local PS="`cat /proc/*/status | cut -d \ -f 2 -f 15 2>/dev/null | grep \" ${JAIL} \" | cut -d \ -f 1`" for PID in "$PS" ; do # Sending TERM signal to all processes in the jail kill -15 $PID 2>/dev/null 1>/dev/null done # Waiting for processes to die sleep 4 local PS="`cat /proc/*/status | cut -d \ -f 2 -f 15 2>/dev/null | grep \" ${JAIL} \" | cut -d \ -f 1`" while [ "$PS" ] ; do # Some processes are still running, do a kill -9 on each echo "Some processes would not terminate; sending KILL signal..." for PID in "$PS" ; do # Sending KILL signal to all processes in the jail kill -9 $PID 2>/dev/null 1>/dev/null done # Give processes some time to die sleep 2 local PS="`cat /proc/*/status | cut -d \ -f 2 -f 15 2>/dev/null | grep \" ${JAIL} \" | cut -d \ -f 1`" done fi fi if [ $PROCFS -gt 0 ] ; then # Unmount the jail proc filesystem umount -f ${JP}/proc fi if [ $LINPROCFS -gt 0 ] ; then # Unmount the jail Linux proc filesystem umount -f ${JP}/compat/linux/proc fi if [ $VERSION -eq 4 ] ; then # We are on FreeBSD 4.x, remove runfile rm /var/run/jails/${JAIL}.running fi # Bring down network interface alias [ $(echo $JIF | cut -c 1-4) != 'carp' ] && ifconfig $JIF inet $IP netmask 0xffffffff -alias if [ $VERSION -ge 5 ] ; then # Run hooks jail_run_hooks after-stop fi return 0 } jcreate() { jail_run_hooks before-create ## Create a jail local JP=$(jail_path) local IP=$(jail_ip) local JIF=$(jail_if) # Create jail directory mkdir -p $JP # Populate jail directory jpopulate # Initialize jail directory contents jinit # Remove unneeded files and clean up jcleanup jail_run_hooks after-create return 0 } jupgrade() { jail_run_hooks before-upgrade ## Upgrade a jail local JP=$(jail_path) # Run mergemaster to prepare the jail for upgrade mergemaster -Upi -D $JP # Populate jail directory jpopulate # Run mergemaster to update default configuration files mergemaster -Ui -D $JP # Remove unneeded files and clean up jcleanup if jail_running ; then echo "Jail running, please restart!" fi jail_run_hooks after-upgrade } jdelete() { jail_run_hooks before-delete ## Delete a jail local JP=$(jail_path) local m_search="" if [ $PROCFS -gt 0 ] ; then m_search="${JP}/proc" ; fi if [ $LINPROCFS -gt 0 ] ; then if [ -n "${m_search}" ] ; then m_search="${m_search}\|" ; fi m_search="${m_search}${JP}/compat/linux/proc" fi if [ $VERSION -ge 5 ] ; then if [ -n "${m_search}" ] ; then m_search="${m_search}\|" ; fi m_search="${m_search}${JP}/dev" fi MOUNTS=`mount | grep "$JP" | grep -v "${m_search} | cut -d \ -f 3"` MOUNTS_NO=`echo -n $MOUNTS | wc -l` if [ $MOUNTS_NO -gt 0 ]; then echo "WARNING: Mounted directories found in ${JP}:" echo $MOUNTS echo -n "Unmount ('n' will cancel delete)? [y/n] " read ANS if [ x$ANS = xy ]; then for m in $MOUNTS ; do echo -n "Unmounting $m ..." umount_msg=`umount -f $m 2>&1` umount_res=$? sleep 1 if [ $umount_res -ne 0 ] ; then echo "FAILED!" echo "$umount_msg" else echo "ok." fi done MOUNTS=`mount | grep "$JP" | grep -v "${m_search}"` MOUNTS_NO=`echo -n $MOUNTS | wc -l` echo $MOUNTS_NO if [ $MOUNTS_NO -gt 0 ] ; then echo "Unmounting failed. Jail not deleted." else echo "All filesystems unmounted successfully. Deleting jail." # Pass one: removing unprotected files rm -Rf $JP 2>&1 > /dev/null # Pass two: removing SCHG flag from jail tree chflags -R noschg $JP # Pass three: Removing jail directory rm -Rf $JP fi fi else # Pass one: removing unprotected files rm -Rf $JP 2>&1 > /dev/null # Pass two: removing SCHG flag from jail tree chflags -R noschg $JP # Pass three: Removing jail directory rm -Rf $JP fi jail_run_hooks after-delete } jbackup() { ## Back up a jail local JP=$(jail_path) # Determine target file for backup if [ -n "$CMD" ] ; then TARGET=$CMD else TARGET="${BACKUPDIR}/${JAIL}.tar" fi # Run backup jail_run_hooks before-backup if [ ! "$1" = "cold" -a $VERSION -ge 5 ] ; then # Run warm backup - FreeBSD >=5 only if [ -z "$GPG" ] ; then /usr/sbin/jexec $JAIL_ID /usr/bin/tar --one-file-system -C / $BACKUP_EXCLUDE -cf - ./. | gzip --fast > ${TARGET}.gz else /usr/sbin/jexec $JAIL_ID /usr/bin/tar --one-file-system -C / $BACKUP_EXCLUDE -cf - ./. | gzip --fast | $GPG -o ${TARGET}.gz.gpg fi else # Run cold backup if [ -z "$GPG" ] ; then chroot $JP /usr/bin/tar --one-file-system -C / $BACKUP_EXCLUDE -cf - ./. | gzip --fast > ${TARGET}.gz else chroot $JP /usr/bin/tar --one-file-system -C / $BACKUP_EXCLUDE -cf - ./. | gzip --fast | $GPG -o ${TARGET}.gz.gpg fi fi jail_run_hooks after-backup } jrestore() { ## Restore a jail from backup # Create jail home mkdir -p $JP && cd $JP # Determine source file for backup if [ -n "$CMD" ] ; then SOURCE=$CMD else SOURCE="${BACKUPDIR}/${JAIL}.tar.gz" fi [ -f $SOURCE ] && jail_run_hooks before-restore # Restore tar -zpxf $SOURCE [ $? -eq 0 ] && jail_run_hooks after-restore } # ### Activity helper function section # jpopulate() { ## Populate a jail directory cd /usr/src # Running installworld into jail directory make installworld ${INSTALLWORLD_FLAGS} DESTDIR=${JP} 2>&1 | grep '>>>' } jcleanup() { ## Remove unneeded files and clean up a jail # Copying the most recent list of files to delete if [ $VERSION -ge 5 ] ; then cp ${JAIL_HOME}addons/dellist5.txt $JP/dellist.txt else cp ${JAIL_HOME}addons/dellist4.txt $JP/dellist.txt fi # Removing protection from files to be deleted chroot $JP chflags -R noschg $(cat ${JP}/dellist.txt) 2>/dev/null 1>/dev/null # Deleting files chroot $JP rm -Rf $(cat ${JP}/dellist.txt) 2>/dev/null 1>/dev/null # Changing binaries to be jail compatible chroot ${JP} ln -f /usr/bin/true /sbin/mount chroot ${JP} ln -f /usr/bin/true /sbin/umount chroot ${JP} ln -f /usr/bin/true /sbin/swapon chroot ${JP} ln -f /usr/bin/true /sbin/swapoff chroot ${JP} chflags noschg /sbin/init chroot ${JP} rm /sbin/init chroot ${JP} ln -f /usr/bin/true /sbin/init chroot ${JP} ln -f /usr/bin/true /sbin/adjkerntz chroot ${JP} ln -f /usr/bin/true /sbin/ifconfig # Copy timezone information from host cp /etc/localtime ${JP}/etc/localtime } jinit() { ## Install default set of configuration files cd /usr/src/etc # Installing distribution files to jail directory make distribution DESTDIR=${JP} -DNO_MAKEDEV_RUN 2>/dev/null 1>/dev/null # Create directories in jail mkdir -p ${JP}/proc mkdir -p ${JP}/usr/home mkdir -p ${JP}/root/.ssh mkdir -p ${JP}/compat/linux/proc mkdir -p ${JP}/usr/local/bin chroot ${JP} ln -sf /usr/home /home # Update passwd database with default root user/pw IFS2=$IFS IFS=$(echo -e '\n\t') if [ -z "$BATCH" ] ; then echo "Setting root password in jail" chroot ${JP} /usr/bin/passwd root else for L in $(cat ${JP}/etc/master.passwd) ; do if [ "$L" = 'root::0:0::0:0:Charlie &:/root:/bin/csh' ] ; then echo "root:${ROOT_PW}:0:0::0:0:Charlie &:/root:/bin/csh" >> ${JP}/tmp/jailctl.001 else echo $L >> ${JP}/tmp/jailctl.001 fi done pwd_mkdb -p -d ${JP}/etc ${JP}/tmp/jailctl.001 IFS=$IFS2 fi # Install jail hostname and IP into hosts file JAIL_HOST=$(expr $JAIL : "\([a-zA-Z0-9\-]*\)\..*") JAIL_DOMAIN=$(expr $JAIL : "${JAIL_HOST}\.\(.*\)") echo "$IP $JAIL $JAIL_HOST" >> ${JP}/etc/hosts # Create new rc.conf echo '# Default jail rc.conf' > ${JP}/etc/rc.conf for L in $RC_CONF ; do echo $L >> ${JP}/etc/rc.conf done echo "hostname=\"$JAIL\"" >> ${JP}/etc/rc.conf # Update SSH configuration sed -i .jailctl -Ee "s/#?PermitRootLogin no/PermitRootLogin yes/" ${JP}/etc/ssh/sshd_config # Update resolv.conf echo "domain $JAIL_DOMAIN" > ${JP}/etc/resolv.conf # Update resolv.conf for L in $NAMESERVERS ; do echo "nameserver $L" >> ${JP}/etc/resolv.conf done # Creating symlinks chroot ${JP} ln -sf /dev/null /kernel if [ $VERSION -ge 5 ] ; then # We are on FreeBSD 5.x, work around distribution bug chroot ${JP} ln -sf /lib/libm.so.3 /lib/libm.so.2 fi if [ $VERSION -eq 4 ] ; then # We are on FreeBSD 4.x, initializing device tree cd ${JP}/dev sh MAKEDEV jail fi # Installing addons cp -R ${JAIL_HOME}addons/* ${JP}/ # Starting jail for the first time, calling runme.sh to install software [ $(echo $JIF | cut -c 1-4) != 'carp' ] && ifconfig $JIF inet $IP netmask 0xffffffff alias JSTART=$(jail $JP $JAIL $IP /bin/sh /runme.sh) [ $(echo $JIF | cut -c 1-4) != 'carp' ] && ifconfig $JIF inet $IP netmask 0xffffffff -alias # Output commmands used to run installation script for easy re-run [ $(echo $JIF | cut -c 1-4) != 'carp' ] && echo "ifconfig $JIF inet $IP netmask 0xffffffff alias" echo "jail $JP $JAIL $IP /bin/sh /runme.sh" [ $(echo $JIF | cut -c 1-4) != 'carp' ] && echo "ifconfig $JIF inet $IP netmask 0xffffffff -alias" } jrunhooks() { ## Run a hook list # Find jail ID on FreeBSD >5 local JAIL_ID=0 if [ $VERSION -ge 5 ] && jail_running ; then local JLS="`/usr/sbin/jls | grep \"\/${JAIL}$\"`" for i in "$JLS" ; do # Fetching output string, concatenating local J_LIST="$J_LIST $i" done # Setting JAIL_ID variable; this is the system jail ID local JAIL_ID=`echo $J_LIST | cut -d \ -f 1` fi for HOOK in $@; do # Skip first parameter [ ! "$HOOK" = "$1" ] && $HOOK $1 $JAIL $(jail_path) $JAIL_ID done } # ### Main block # ## Get current working directory CWD=$(pwd) ## Get command line parameters ACTION=$1 JAIL=$2 CMD=$3 if [ "$CMD" = "force" ] ; then FORCE=1 fi ## Checking current FreeBSD version VERSION="`uname -r | cut -c 1`" if [ $VERSION -eq 4 ] ; then # We are on FreeBSD 4.x, creating statefile directory if [ ! -d "/var/run/jails" ] ; then mkdir -p /var/run/jails fi fi case "$ACTION" in status) jail_status ;; start) jail_start ;; stop) jail_stop ;; create) jail_create ;; upgrade) jail_upgrade ;; delete) jail_delete ;; backup) jail_backup ;; restore) jail_restore ;; *) usage ;; esac cd $CWD exit 0