jail revision 191620
152284Sobrien#!/bin/sh
252284Sobrien#
352284Sobrien# $FreeBSD: head/etc/rc.d/jail 191620 2009-04-28 09:45:32Z ru $
490075Sobrien#
552284Sobrien
652284Sobrien# PROVIDE: jail
790075Sobrien# REQUIRE: LOGIN cleanvar
852284Sobrien# BEFORE: securelevel
990075Sobrien# KEYWORD: nojail shutdown
1090075Sobrien
1152284Sobrien# WARNING: This script deals with untrusted data (the data and
1290075Sobrien# processes inside the jails) and care must be taken when changing the
1390075Sobrien# code related to this!  If you have any doubt whether a change is
1490075Sobrien# correct and have security impact, please get the patch reviewed by
1590075Sobrien# the FreeBSD Security Team prior to commit.
1690075Sobrien
1752284Sobrien. /etc/rc.subr
1890075Sobrien
1990075Sobrienname="jail"
2090075Sobrienrcvar=`set_rcvar`
2190075Sobrienstart_cmd="jail_start"
2290075Sobrienstop_cmd="jail_stop"
2390075Sobrien
2490075Sobrien# init_variables _j
2590075Sobrien#	Initialize the various jail variables for jail _j.
2690075Sobrien#
2790075Sobrieninit_variables()
2890075Sobrien{
2990075Sobrien	_j="$1"
3090075Sobrien
3190075Sobrien	if [ -z "$_j" ]; then
3252284Sobrien		warn "init_variables: you must specify a jail"
3352284Sobrien		return
3490075Sobrien	fi
3590075Sobrien
3690075Sobrien	eval _rootdir=\"\$jail_${_j}_rootdir\"
3790075Sobrien	_devdir="${_rootdir}/dev"
3890075Sobrien	_fdescdir="${_devdir}/fd"
3990075Sobrien	_procdir="${_rootdir}/proc"
4090075Sobrien	eval _hostname=\"\$jail_${_j}_hostname\"
4190075Sobrien	eval _ip=\"\$jail_${_j}_ip\"
4290075Sobrien	eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\"
4390075Sobrien	eval _exec=\"\$jail_${_j}_exec\"
4490075Sobrien
4590075Sobrien	i=0
4690075Sobrien	while : ; do
4790075Sobrien		eval _exec_prestart${i}=\"\${jail_${_j}_exec_prestart${i}:-\${jail_exec_prestart${i}}}\"
4890075Sobrien		[ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break
4990075Sobrien		i=$((i + 1))
5090075Sobrien	done
5190075Sobrien
5290075Sobrien	eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\"
5390075Sobrien
5490075Sobrien	i=1
5590075Sobrien	while [ true ]; do
5690075Sobrien		eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\"
5752284Sobrien		[ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] &&  break
5852284Sobrien		i=$((i + 1))
5952284Sobrien	done
6052284Sobrien
6152284Sobrien	i=0
6252284Sobrien	while : ; do
6352284Sobrien		eval _exec_poststart${i}=\"\${jail_${_j}_exec_poststart${i}:-\${jail_exec_poststart${i}}}\"
6452284Sobrien		[ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break
6552284Sobrien		i=$((i + 1))
6652284Sobrien	done
6752284Sobrien
6852284Sobrien	i=0
6952284Sobrien	while : ; do
7052284Sobrien		eval _exec_prestop${i}=\"\${jail_${_j}_exec_prestop${i}:-\${jail_exec_prestop${i}}}\"
7152284Sobrien		[ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break
7252284Sobrien		i=$((i + 1))
7352284Sobrien	done
7452284Sobrien
7552284Sobrien	eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\"
7652284Sobrien
7752284Sobrien	i=0
7852284Sobrien	while : ; do
7952284Sobrien		eval _exec_poststop${i}=\"\${jail_${_j}_exec_poststop${i}:-\${jail_exec_poststop${i}}}\"
8052284Sobrien		[ -z "$(eval echo \"\$_exec_poststop${i}\")" ] && break
8152284Sobrien		i=$((i + 1))
8252284Sobrien	done
8352284Sobrien
8452284Sobrien	if [ -n "${_exec}" ]; then
8552284Sobrien		#   simple/backward-compatible execution
8652284Sobrien		_exec_start="${_exec}"
8752284Sobrien		_exec_stop=""
8852284Sobrien	else
8952284Sobrien		#   flexible execution
9052284Sobrien		if [ -z "${_exec_start}" ]; then
9152284Sobrien			_exec_start="/bin/sh /etc/rc"
9252284Sobrien			if [ -z "${_exec_stop}" ]; then
9352284Sobrien				_exec_stop="/bin/sh /etc/rc.shutdown"
9452284Sobrien			fi
9552284Sobrien		fi
9652284Sobrien	fi
9752284Sobrien
9852284Sobrien	# The default jail ruleset will be used by rc.subr if none is specified.
9952284Sobrien	eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\"
10052284Sobrien	eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\"
10152284Sobrien	[ -z "${_devfs}" ] && _devfs="NO"
10252284Sobrien	eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\"
10352284Sobrien	[ -z "${_fdescfs}" ] && _fdescfs="NO"
10452284Sobrien	eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\"
10552284Sobrien	[ -z "${_procfs}" ] && _procfs="NO"
10652284Sobrien
10752284Sobrien	eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\"
10852284Sobrien	[ -z "${_mount}" ] && _mount="NO"
10952284Sobrien	# "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified.
11052284Sobrien	eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\"
11152284Sobrien	[ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}"
11252284Sobrien	eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\"
11352284Sobrien	[ -z "${_flags}" ] && _flags="-l -U root"
11452284Sobrien	eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\"
11552284Sobrien	[ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log"
11652284Sobrien	eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\"
11752284Sobrien
11852284Sobrien	# Debugging aid
11952284Sobrien	#
12052284Sobrien	debug "$_j devfs enable: $_devfs"
12152284Sobrien	debug "$_j fdescfs enable: $_fdescfs"
12252284Sobrien	debug "$_j procfs enable: $_procfs"
12352284Sobrien	debug "$_j mount enable: $_mount"
12452284Sobrien	debug "$_j hostname: $_hostname"
12552284Sobrien	debug "$_j ip: $_ip"
12652284Sobrien	jail_show_addresses ${_j}
12752284Sobrien	debug "$_j interface: $_interface"
12852284Sobrien	debug "$_j fib: $_fib"
12952284Sobrien	debug "$_j root: $_rootdir"
13052284Sobrien	debug "$_j devdir: $_devdir"
13152284Sobrien	debug "$_j fdescdir: $_fdescdir"
13290075Sobrien	debug "$_j procdir: $_procdir"
13390075Sobrien	debug "$_j ruleset: $_ruleset"
13490075Sobrien	debug "$_j fstab: $_fstab"
13552284Sobrien	debug "$_j consolelog: $_consolelog"
13690075Sobrien
13790075Sobrien	i=0
13890075Sobrien	while : ; do
13990075Sobrien		eval out=\"\${_exec_prestart${i}:-''}\"
14090075Sobrien		if [ -z "$out" ]; then
14190075Sobrien			break
14290075Sobrien		fi
14390075Sobrien		debug "$_j exec pre-start #${i}: ${out}"
14490075Sobrien		i=$((i + 1))
14590075Sobrien	done
14690075Sobrien
14790075Sobrien	debug "$_j exec start: $_exec_start"
14890075Sobrien
14990075Sobrien	i=1
15090075Sobrien	while [ true ]; do
15190075Sobrien		eval out=\"\${_exec_afterstart${i}:-''}\"
15290075Sobrien
15390075Sobrien		if [ -z "$out" ]; then
154			break;
155		fi
156
157		debug "$_j exec after start #${i}: ${out}"
158		i=$((i + 1))
159	done
160
161	i=0
162	while : ; do
163		eval out=\"\${_exec_poststart${i}:-''}\"
164		if [ -z "$out" ]; then
165			break
166		fi
167		debug "$_j exec post-start #${i}: ${out}"
168		i=$((i + 1))
169	done
170
171	i=0
172	while : ; do
173		eval out=\"\${_exec_prestop${i}:-''}\"
174		if [ -z "$out" ]; then
175			break
176		fi
177		debug "$_j exec pre-stop #${i}: ${out}"
178		i=$((i + 1))
179	done
180
181	debug "$_j exec stop: $_exec_stop"
182
183	i=0
184	while : ; do
185		eval out=\"\${_exec_poststop${i}:-''}\"
186		if [ -z "$out" ]; then
187			break
188		fi
189		debug "$_j exec post-stop #${i}: ${out}"
190		i=$((i + 1))
191	done
192
193	debug "$_j flags: $_flags"
194	debug "$_j consolelog: $_consolelog"
195
196	if [ -z "${_hostname}" ]; then
197		err 3 "$name: No hostname has been defined for ${_j}"
198	fi
199	if [ -z "${_rootdir}" ]; then
200		err 3 "$name: No root directory has been defined for ${_j}"
201	fi
202}
203
204# set_sysctl rc_knob mib msg
205#	If the mib sysctl is set according to what rc_knob
206#	specifies, this function does nothing. However if
207#	rc_knob is set differently than mib, then the mib
208#	is set accordingly and msg is displayed followed by
209#	an '=" sign and the word 'YES' or 'NO'.
210#
211set_sysctl()
212{
213	_knob="$1"
214	_mib="$2"
215	_msg="$3"
216
217	_current=`${SYSCTL} -n $_mib 2>/dev/null`
218	if checkyesno $_knob ; then
219		if [ "$_current" -ne 1 ]; then
220			echo -n " ${_msg}=YES"
221			${SYSCTL_W} 1>/dev/null ${_mib}=1
222		fi
223	else
224		if [ "$_current" -ne 0 ]; then
225			echo -n " ${_msg}=NO"
226			${SYSCTL_W} 1>/dev/null ${_mib}=0
227		fi
228	fi
229}
230
231# is_current_mountpoint()
232#	Is the directory mount point for a currently mounted file
233#	system?
234#
235is_current_mountpoint()
236{
237	local _dir _dir2
238
239	_dir=$1
240
241	_dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'`
242	[ ! -d "${_dir}" ] && return 1
243	_dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'`
244	[ "${_dir}" = "${_dir2}" ]
245	return $?
246}
247
248# is_symlinked_mountpoint()
249#	Is a mount point, or any of its parent directories, a symlink?
250#
251is_symlinked_mountpoint()
252{
253	local _dir
254
255	_dir=$1
256
257	[ -L "$_dir" ] && return 0
258	[ "$_dir" = "/" ] && return 1
259	is_symlinked_mountpoint `dirname $_dir`
260	return $?
261}
262
263# secure_umount
264#	Try to unmount a mount point without being vulnerable to
265#	symlink attacks.
266#
267secure_umount()
268{
269	local _dir
270
271	_dir=$1
272
273	if is_current_mountpoint ${_dir}; then
274		umount -f ${_dir} >/dev/null 2>&1
275	else
276		debug "Nothing mounted on ${_dir} - not unmounting"
277	fi
278}
279
280
281# jail_umount_fs
282#	This function unmounts certain special filesystems in the
283#	currently selected jail. The caller must call the init_variables()
284#	routine before calling this one.
285#
286jail_umount_fs()
287{
288	local _device _mountpt _rest
289
290	if checkyesno _fdescfs; then
291		if [ -d "${_fdescdir}" ] ; then
292			secure_umount ${_fdescdir}
293		fi
294	fi
295	if checkyesno _devfs; then
296		if [ -d "${_devdir}" ] ; then
297			secure_umount ${_devdir}
298		fi
299	fi
300	if checkyesno _procfs; then
301		if [ -d "${_procdir}" ] ; then
302			secure_umount ${_procdir}
303		fi
304	fi
305	if checkyesno _mount; then
306		[ -f "${_fstab}" ] || warn "${_fstab} does not exist"
307		tail -r ${_fstab} | while read _device _mountpt _rest; do
308			case ":${_device}" in
309			:#* | :)
310				continue
311				;;
312			esac
313			secure_umount ${_mountpt}
314		done
315	fi
316}
317
318# jail_mount_fstab()
319#	Mount file systems from a per jail fstab while trying to
320#	secure against symlink attacks at the mount points.
321#
322#	If we are certain we cannot secure against symlink attacks we
323#	do not mount all of the file systems (since we cannot just not
324#	mount the file system with the problematic mount point).
325#
326#	The caller must call the init_variables() routine before
327#	calling this one.
328#
329jail_mount_fstab()
330{
331	local _device _mountpt _rest
332
333	while read _device _mountpt _rest; do
334		case ":${_device}" in
335		:#* | :)
336			continue
337			;;
338		esac
339		if is_symlinked_mountpoint ${_mountpt}; then
340			warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}"
341			return
342		fi
343	done <${_fstab}
344	mount -a -F "${_fstab}"
345}
346
347# jail_show_addresses jail
348#	Debug print the input for the given _multi aliases
349#	for a jail for init_variables().
350#
351jail_show_addresses()
352{
353	local _j _type alias
354	_j="$1"
355	alias=0
356
357	if [ -z "${_j}" ]; then
358		warn "jail_show_addresses: you must specify a jail"
359		return
360	fi
361
362	while : ; do
363		eval _addr=\"\$jail_${_j}_ip_multi${alias}\"
364		if [ -n "${_addr}" ]; then
365			debug "${_j} ip_multi${alias}: $_addr"
366			alias=$((${alias} + 1))
367		else
368			break
369		fi
370	done
371}
372
373# jail_extract_address argument
374#	The second argument is the string from one of the _ip
375#	or the _multi variables. In case of a comma separated list
376#	only one argument must be passed in at a time.
377#	The function alters the _type, _iface, _addr and _mask variables.
378#
379jail_extract_address()
380{
381	local _i
382	_i=$1
383
384	if [ -z "${_i}" ]; then
385		warn "jail_extract_address: called without input"
386		return
387	fi
388
389	# Check if we have an interface prefix given and split into
390	# iFace and rest.
391	case "${_i}" in
392	*\|*)	# ifN|.. prefix there
393		_iface=${_i%%|*}
394		_r=${_i##*|}
395		;;
396	*)	_iface=""
397		_r=${_i}
398		;;
399	esac
400
401	# In case the IP has no interface given, check if we have a global one.
402	_iface=${_iface:-${_interface}}
403
404	# Set address, cut off any prefix/netmask/prefixlen.
405	_addr=${_r}
406	_addr=${_addr%%[/ ]*}
407
408	# Theoretically we can return here if interface is not set,
409	# as we only care about the _mask if we call ifconfig.
410	# This is not done because we may want to santize IP addresses
411	# based on _type later, and optionally change the type as well.
412
413	# Extract the prefix/netmask/prefixlen part by cutting off the address.
414	_mask=${_r}
415	_mask=`expr "${_mask}" : "${_addr}\(.*\)"`
416
417	# Identify type {inet,inet6}.
418	case "${_addr}" in
419	*\.*\.*\.*)	_type="inet" ;;
420	*:*)		_type="inet6" ;;
421	*)		warn "jail_extract_address: type not identified"
422			;;
423	esac
424
425	# Handle the special /netmask instead of /prefix or
426	# "netmask xxx" case for legacy IP.
427	# We do NOT support shortend class-full netmasks.
428	if [ "${_type}" = "inet" ]; then
429		case "${_mask}" in
430		/*\.*\.*\.*)	_mask=" netmask ${_mask#/}" ;;
431		*)		;;
432		esac
433
434		# In case _mask is still not set use /32.
435		_mask=${_mask:-/32}
436
437	elif [ "${_type}" = "inet6" ]; then
438		# In case _maske is not set for IPv6, use /128.
439		_mask=${_mask:-/128}
440	fi
441}
442
443# jail_handle_ips_option {add,del} input
444#	Handle a single argument imput which can be a comma separated
445#	list of addresses (theoretically with an option interface and
446#	prefix/netmask/prefixlen).
447#
448jail_handle_ips_option()
449{
450	local _x _action _type _i
451	_action=$1
452	_x=$2
453
454	if [ -z "${_x}" ]; then
455		# No IP given. This can happen for the primary address
456		# of each address family.
457		return
458	fi
459
460	# Loop, in case we find a comma separated list, we need to handle
461	# each argument on its own.
462	while [ ${#_x} -gt 0 ]; do
463		case "${_x}" in
464		*,*)	# Extract the first argument and strip it off the list.
465			_i=`expr "${_x}" : '^\([^,]*\)'`
466			_x=`expr "${_x}" : "^[^,]*,\(.*\)"`
467			;;
468		*)	_i=${_x}
469			_x=""
470			;;
471		esac
472
473		_type=""
474		_iface=""
475		_addr=""
476		_mask=""
477		jail_extract_address "${_i}"
478
479		# make sure we got an address.
480		case "${_addr}" in
481		"")	continue ;;
482		*)	;;
483		esac
484
485		# Append address to list of addresses for the jail command.
486		case "${_addrl}" in
487		"")	_addrl="${_addr}" ;;
488		*)	_addrl="${_addrl},${_addr}" ;;
489		esac
490
491		# Configure interface alias if requested by a given interface
492		# and if we could correctly parse everything.
493		case "${_iface}" in
494		"")	continue ;;
495		esac
496		case "${_type}" in
497		inet)	;;
498		inet6)	;;
499		*)	warn "Could not determine address family.  Not going" \
500			    "to ${_action} address '${_addr}' for ${_jail}."
501			continue
502			;;
503		esac
504		case "${_action}" in
505		add)	ifconfig ${_iface} ${_type} ${_addr}${_mask} alias
506			;;
507		del)	# When removing the IP, ignore the _mask.
508			ifconfig ${_iface} ${_type} ${_addr} -alias
509			;;
510		esac
511	done
512}
513
514# jail_ips {add,del}
515#	Extract the comma separated list of addresses and return them
516#	for the jail command.
517#	Handle more than one address via the _multi option as well.
518#	If an interface is given also add/remove an alias for the
519#	address with an optional netmask.
520#
521jail_ips()
522{
523	local _action
524	_action=$1
525
526	case "${_action}" in
527	add)	;;
528	del)	;;
529	*)	warn "jail_ips: invalid action '${_action}'"
530		return
531		;;
532	esac
533
534	# Handle addresses.
535	jail_handle_ips_option ${_action} "${_ip}"
536	# Handle jail_xxx_ip_multi<N>
537	alias=0
538	while : ; do
539		eval _x=\"\$jail_${_jail}_ip_multi${alias}\"
540		case "${_x}" in
541		"")	break ;;
542		*)	jail_handle_ips_option ${_action} "${_x}"
543			alias=$((${alias} + 1))
544			;;
545		esac
546	done
547}
548
549jail_start()
550{
551	echo -n 'Configuring jails:'
552	set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \
553	    set_hostname_allow
554	set_sysctl jail_socket_unixiproute_only \
555	    security.jail.socket_unixiproute_only unixiproute_only
556	set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \
557	    sysvipc_allow
558	echo '.'
559
560	echo -n 'Starting jails:'
561	_tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \
562	    err 3 "$name: Can't create temp dir, exiting..."
563	for _jail in ${jail_list}
564	do
565		init_variables $_jail
566		if [ -f /var/run/jail_${_jail}.id ]; then
567			echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]"
568			continue;
569		fi
570		_addrl=""
571		jail_ips "add"
572		if [ -n "${_fib}" ]; then
573			_setfib="setfib -F '${_fib}'"
574		else
575			_setfib=""
576		fi
577		if checkyesno _mount; then
578			info "Mounting fstab for jail ${_jail} (${_fstab})"
579			if [ ! -f "${_fstab}" ]; then
580				err 3 "$name: ${_fstab} does not exist"
581			fi
582			jail_mount_fstab
583		fi
584		if checkyesno _devfs; then
585			# If devfs is already mounted here, skip it.
586			df -t devfs "${_devdir}" >/dev/null
587			if [ $? -ne 0 ]; then
588				if is_symlinked_mountpoint ${_devdir}; then
589					warn "${_devdir} has symlink as parent - not starting jail ${_jail}"
590					continue
591				fi
592				info "Mounting devfs on ${_devdir}"
593				devfs_mount_jail "${_devdir}" ${_ruleset}
594				# Transitional symlink for old binaries
595				if [ ! -L "${_devdir}/log" ]; then
596					__pwd="`pwd`"
597					cd "${_devdir}"
598					ln -sf ../var/run/log log
599					cd "$__pwd"
600				fi
601			fi
602
603			# XXX - It seems symlinks don't work when there
604			#	is a devfs(5) device of the same name.
605			# Jail console output
606			#	__pwd="`pwd`"
607			#	cd "${_devdir}"
608			#	ln -sf ../var/log/console console
609			#	cd "$__pwd"
610		fi
611		if checkyesno _fdescfs; then
612			if is_symlinked_mountpoint ${_fdescdir}; then
613				warn "${_fdescdir} has symlink as parent, not mounting"
614			else
615				info "Mounting fdescfs on ${_fdescdir}"
616				mount -t fdescfs fdesc "${_fdescdir}"
617			fi
618		fi
619		if checkyesno _procfs; then
620			if is_symlinked_mountpoint ${_procdir}; then
621				warn "${_procdir} has symlink as parent, not mounting"
622			else
623				info "Mounting procfs onto ${_procdir}"
624				if [ -d "${_procdir}" ] ; then
625					mount -t procfs proc "${_procdir}"
626				fi
627			fi
628		fi
629		_tmp_jail=${_tmp_dir}/jail.$$
630
631		i=0
632		while : ; do
633			eval out=\"\${_exec_prestart${i}:-''}\"
634			[ -z "$out" ] && break
635			${out}
636			i=$((i + 1))
637		done
638
639		eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \
640			\"${_addrl}\" ${_exec_start} > ${_tmp_jail} 2>&1
641
642		if [ "$?" -eq 0 ] ; then
643			_jail_id=$(head -1 ${_tmp_jail})
644			i=1
645			while [ true ]; do
646				eval out=\"\${_exec_afterstart${i}:-''}\"
647
648				if [ -z "$out" ]; then
649					break;
650				fi
651
652				jexec "${_jail_id}" ${out}
653				i=$((i + 1))
654			done
655
656			echo -n " $_hostname"
657			tail +2 ${_tmp_jail} >${_consolelog}
658			echo ${_jail_id} > /var/run/jail_${_jail}.id
659
660			i=0
661			while : ; do
662				eval out=\"\${_exec_poststart${i}:-''}\"
663				[ -z "$out" ] && break
664				${out}
665				i=$((i + 1))
666			done
667		else
668			jail_umount_fs
669			jail_ips "del"
670			echo " cannot start jail \"${_jail}\": "
671			tail +2 ${_tmp_jail}
672		fi
673		rm -f ${_tmp_jail}
674	done
675	rmdir ${_tmp_dir}
676	echo '.'
677}
678
679jail_stop()
680{
681	echo -n 'Stopping jails:'
682	for _jail in ${jail_list}
683	do
684		if [ -f "/var/run/jail_${_jail}.id" ]; then
685			_jail_id=$(cat /var/run/jail_${_jail}.id)
686			if [ ! -z "${_jail_id}" ]; then
687				init_variables $_jail
688
689				i=0
690				while : ; do
691					eval out=\"\${_exec_prestop${i}:-''}\"
692					[ -z "$out" ] && break
693					${out}
694					i=$((i + 1))
695				done
696
697				if [ -n "${_exec_stop}" ]; then
698					eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \
699						>> ${_consolelog} 2>&1
700				fi
701				killall -j ${_jail_id} -TERM > /dev/null 2>&1
702				sleep 1
703				killall -j ${_jail_id} -KILL > /dev/null 2>&1
704				jail_umount_fs
705				echo -n " $_hostname"
706
707				i=0
708				while : ; do
709					eval out=\"\${_exec_poststop${i}:-''}\"
710					[ -z "$out" ] && break
711					${out}
712					i=$((i + 1))
713				done
714			fi
715			jail_ips "del"
716			rm /var/run/jail_${_jail}.id
717		else
718			echo " cannot stop jail ${_jail}. No jail id in /var/run"
719		fi
720	done
721	echo '.'
722}
723
724load_rc_config $name
725cmd="$1"
726if [ $# -gt 0 ]; then
727	shift
728fi
729if [ -n "$*" ]; then
730	jail_list="$*"
731fi
732run_rc_command "${cmd}"
733