]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - etc/rc.d/jail
Add 410.status-mfi, a periodic script for mfi(4) arrays
[FreeBSD/FreeBSD.git] / etc / rc.d / jail
1 #!/bin/sh
2 #
3 # $FreeBSD$
4 #
5
6 # PROVIDE: jail
7 # REQUIRE: LOGIN FILESYSTEMS
8 # BEFORE: securelevel
9 # KEYWORD: nojail shutdown
10
11 . /etc/rc.subr
12
13 name="jail"
14 desc="Manage system jails"
15 rcvar="jail_enable"
16
17 start_cmd="jail_start"
18 start_postcmd="jail_warn"
19 stop_cmd="jail_stop"
20 config_cmd="jail_config"
21 console_cmd="jail_console"
22 status_cmd="jail_status"
23 extra_commands="config console status"
24 : ${jail_conf:=/etc/jail.conf}
25 : ${jail_program:=/usr/sbin/jail}
26 : ${jail_consolecmd:=/usr/bin/login -f root}
27 : ${jail_jexec:=/usr/sbin/jexec}
28 : ${jail_jls:=/usr/sbin/jls}
29
30 need_dad_wait=
31
32 # extract_var jv name param num defval
33 #       Extract value from ${jail_$jv_$name} or ${jail_$name} and
34 #       set it to $param.  If not defined, $defval is used.
35 #       When $num is [0-9]*, ${jail_$jv_$name$num} are looked up and
36 #       $param is set by using +=.  $num=0 is optional (params may start at 1).
37 #       When $num is YN or NY, the value is interpreted as boolean.
38 #       When $num is @, the value is interpreted as an array separted by IFS.
39 extract_var()
40 {
41         local i _jv _name _param _num _def _name1 _name2
42         _jv=$1
43         _name=$2
44         _param=$3
45         _num=$4
46         _def=$5
47
48         case $_num in
49         YN)
50                 _name1=jail_${_jv}_${_name}
51                 _name2=jail_${_name}
52                 eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
53                 if checkyesno $_name1; then
54                         echo "  $_param = 1;"
55                 else
56                         echo "  $_param = 0;"
57                 fi
58         ;;
59         NY)
60                 _name1=jail_${_jv}_${_name}
61                 _name2=jail_${_name}
62                 eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
63                 if checkyesno $_name1; then
64                         echo "  $_param = 0;"
65                 else
66                         echo "  $_param = 1;"
67                 fi
68         ;;
69         [0-9]*)
70                 i=$_num
71                 while : ; do
72                         _name1=jail_${_jv}_${_name}${i}
73                         _name2=jail_${_name}${i}
74                         eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
75                         if [ -n "$_tmpargs" ]; then 
76                                 echo "  $_param += \"$_tmpargs\";"
77                         elif [ $i != 0 ]; then
78                                 break;
79                         fi
80                         i=$(($i + 1))
81                 done
82         ;;
83         @)
84                 _name1=jail_${_jv}_${_name}
85                 _name2=jail_${_name}
86                 eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
87                 set -- $_tmpargs
88                 if [ $# -gt 0 ]; then
89                         echo -n "       $_param = "
90                         while [ $# -gt 1 ]; do
91                                 echo -n "\"$1\", "
92                                 shift
93                         done
94                         echo "\"$1\";"
95                 fi
96         ;;
97         *)
98                 _name1=jail_${_jv}_${_name}
99                 _name2=jail_${_name}
100                 eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
101                 if [ -n "$_tmpargs" ]; then
102                         echo "  $_param = \"$_tmpargs\";"
103                 fi
104         ;;
105         esac
106 }
107
108 # parse_options _j _jv
109 #       Parse options and create a temporary configuration file if necessary.
110 #
111 parse_options()
112 {
113         local _j _jv _p
114         _j=$1
115         _jv=$2
116
117         _confwarn=0
118         if [ -z "$_j" ]; then
119                 warn "parse_options: you must specify a jail"
120                 return
121         fi
122         eval _jconf=\"\${jail_${_jv}_conf:-/etc/jail.${_j}.conf}\"
123         eval _rootdir=\"\$jail_${_jv}_rootdir\"
124         eval _hostname=\"\$jail_${_jv}_hostname\"
125         if [ -z "$_rootdir" -o \
126              -z "$_hostname" ]; then
127                 if [ -r "$_jconf" ]; then
128                         _conf="$_jconf"
129                         return 0
130                 elif [ -r "$jail_conf" ]; then
131                         _conf="$jail_conf"
132                         return 0
133                 else
134                         warn "Invalid configuration for $_j " \
135                             "(no jail.conf, no hostname, or no path).  " \
136                             "Jail $_j was ignored."
137                 fi
138                 return 1
139         fi
140         eval _ip=\"\$jail_${_jv}_ip\"
141         if [ -z "$_ip" ] && ! check_kern_features vimage; then
142                 warn "no ipaddress specified and no vimage support.  " \
143                     "Jail $_j was ignored."
144                 return 1
145         fi
146         _conf=/var/run/jail.${_j}.conf
147         #
148         # To relieve confusion, show a warning message.
149         #
150         : ${jail_confwarn:=YES}
151         checkyesno jail_confwarn && _confwarn=1
152         if [ -r "$jail_conf" -o -r "$_jconf" ]; then
153                 if ! checkyesno jail_parallel_start; then
154                         warn "$_conf is created and used for jail $_j."
155                 fi
156         fi
157         /usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1
158
159         eval : \${jail_${_jv}_flags:=${jail_flags}}
160         eval _exec=\"\$jail_${_jv}_exec\"
161         eval _exec_start=\"\$jail_${_jv}_exec_start\"
162         eval _exec_stop=\"\$jail_${_jv}_exec_stop\"
163         if [ -n "${_exec}" ]; then
164                 #   simple/backward-compatible execution
165                 _exec_start="${_exec}"
166                 _exec_stop=""
167         else
168                 #   flexible execution
169                 if [ -z "${_exec_start}" ]; then
170                         _exec_start="/bin/sh /etc/rc"
171                         if [ -z "${_exec_stop}" ]; then
172                                 _exec_stop="/bin/sh /etc/rc.shutdown"
173                         fi
174                 fi
175         fi
176         eval _interface=\"\${jail_${_jv}_interface:-${jail_interface}}\"
177         eval _parameters=\"\${jail_${_jv}_parameters:-${jail_parameters}}\"
178         eval _fstab=\"\${jail_${_jv}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\"
179         (
180                 date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S"
181                 echo "$_j {"
182                 extract_var $_jv hostname host.hostname - ""
183                 extract_var $_jv rootdir path - ""
184                 if [ -n "$_ip" ]; then
185                         extract_var $_jv interface interface - ""
186                         jail_handle_ips_option $_ip $_interface
187                         alias=0
188                         while : ; do
189                                 eval _x=\"\$jail_${_jv}_ip_multi${alias}\"
190                                 [ -z "$_x" ] && break
191
192                                 jail_handle_ips_option $_x $_interface
193                                 alias=$(($alias + 1))
194                         done
195                         case $need_dad_wait in
196                         1)
197                                 # Sleep to let DAD complete before
198                                 # starting services.
199                                 echo "  exec.start += \"sleep " \
200                                 $(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \
201                                 "\";"
202                         ;;
203                         esac
204                         # These are applicable only to non-vimage jails. 
205                         extract_var $_jv fib exec.fib - ""
206                         extract_var $_jv socket_unixiproute_only \
207                             allow.raw_sockets NY YES
208                 else
209                         echo "  vnet;"
210                         extract_var $_jv vnet_interface vnet.interface @ ""
211                 fi
212
213                 echo "  exec.clean;"
214                 echo "  exec.system_user = \"root\";"
215                 echo "  exec.jail_user = \"root\";"
216                 extract_var $_jv exec_prestart exec.prestart 0 ""
217                 extract_var $_jv exec_poststart exec.poststart 0 ""
218                 extract_var $_jv exec_prestop exec.prestop 0 ""
219                 extract_var $_jv exec_poststop exec.poststop 0 ""
220
221                 echo "  exec.start += \"$_exec_start\";"
222                 extract_var $_jv exec_afterstart exec.start 0 ""
223                 echo "  exec.stop = \"$_exec_stop\";"
224
225                 extract_var $_jv consolelog exec.consolelog - \
226                     /var/log/jail_${_j}_console.log
227
228                 if [ -r $_fstab ]; then
229                         echo "  mount.fstab = \"$_fstab\";"
230                 fi
231
232                 eval : \${jail_${_jv}_devfs_enable:=${jail_devfs_enable:-NO}}
233                 if checkyesno jail_${_jv}_devfs_enable; then
234                         echo "  mount.devfs;"
235                         eval _ruleset=\${jail_${_jv}_devfs_ruleset:-${jail_devfs_ruleset}}
236                         case $_ruleset in
237                         "")     ;;
238                         [0-9]*) echo "  devfs_ruleset = \"$_ruleset\";" ;;
239                         devfsrules_jail)
240                                 # XXX: This is the default value,
241                                 # Let jail(8) to use the default because
242                                 # mount(8) only accepts an integer. 
243                                 # This should accept a ruleset name.
244                         ;;
245                         *)      warn "devfs_ruleset must be an integer." ;;
246                         esac
247                 fi
248                 eval : \${jail_${_jv}_fdescfs_enable:=${jail_fdescfs_enable:-NO}}
249                 if checkyesno jail_${_jv}_fdescfs_enable; then
250                         echo "  mount.fdescfs;"
251                 fi
252                 eval : \${jail_${_jv}_procfs_enable:=${jail_procfs_enable:-NO}}
253                 if checkyesno jail_${_jv}_procfs_enable; then
254                         echo "  mount.procfs;"
255                 fi
256
257                 eval : \${jail_${_jv}_mount_enable:=${jail_mount_enable:-NO}}
258                 if checkyesno jail_${_jv}_mount_enable; then
259                         echo "  allow.mount;"
260                 fi
261
262                 extract_var $_jv set_hostname_allow allow.set_hostname YN NO
263                 extract_var $_jv sysvipc_allow allow.sysvipc YN NO
264                 extract_var $_jv enforce_statfs enforce_statfs - 2
265                 extract_var $_jv osreldate osreldate
266                 extract_var $_jv osrelease osrelease
267                 for _p in $_parameters; do
268                         echo "  ${_p%\;};"
269                 done
270                 echo "}"
271         ) >> $_conf
272
273         return 0
274 }
275
276 # jail_extract_address argument iface
277 #       The second argument is the string from one of the _ip
278 #       or the _multi variables. In case of a comma separated list
279 #       only one argument must be passed in at a time.
280 #       The function alters the _type, _iface, _addr and _mask variables.
281 #
282 jail_extract_address()
283 {
284         local _i _interface
285         _i=$1
286         _interface=$2
287
288         if [ -z "${_i}" ]; then
289                 warn "jail_extract_address: called without input"
290                 return
291         fi
292
293         # Check if we have an interface prefix given and split into
294         # iFace and rest.
295         case "${_i}" in
296         *\|*)   # ifN|.. prefix there
297                 _iface=${_i%%|*}
298                 _r=${_i##*|}
299                 ;;
300         *)      _iface=""
301                 _r=${_i}
302                 ;;
303         esac
304
305         # In case the IP has no interface given, check if we have a global one.
306         _iface=${_iface:-${_interface}}
307
308         # Set address, cut off any prefix/netmask/prefixlen.
309         _addr=${_r}
310         _addr=${_addr%%[/ ]*}
311
312         # Theoretically we can return here if interface is not set,
313         # as we only care about the _mask if we call ifconfig.
314         # This is not done because we may want to santize IP addresses
315         # based on _type later, and optionally change the type as well.
316
317         # Extract the prefix/netmask/prefixlen part by cutting off the address.
318         _mask=${_r}
319         _mask=`expr "${_mask}" : "${_addr}\(.*\)"`
320
321         # Identify type {inet,inet6}.
322         case "${_addr}" in
323         *\.*\.*\.*)     _type="inet" ;;
324         *:*)            _type="inet6" ;;
325         *)              warn "jail_extract_address: type not identified"
326                         ;;
327         esac
328
329         # Handle the special /netmask instead of /prefix or
330         # "netmask xxx" case for legacy IP.
331         # We do NOT support shortend class-full netmasks.
332         if [ "${_type}" = "inet" ]; then
333                 case "${_mask}" in
334                 /*\.*\.*\.*)    _mask=" netmask ${_mask#/}" ;;
335                 *)              ;;
336                 esac
337
338                 # In case _mask is still not set use /32.
339                 _mask=${_mask:-/32}
340
341         elif [ "${_type}" = "inet6" ]; then
342                 # In case _mask is not set for IPv6, use /128.
343                 _mask=${_mask:-/128}
344         fi
345 }
346
347 # jail_handle_ips_option input iface
348 #       Handle a single argument imput which can be a comma separated
349 #       list of addresses (theoretically with an option interface and
350 #       prefix/netmask/prefixlen).
351 #
352 jail_handle_ips_option()
353 {
354         local _x _type _i _defif
355         _x=$1
356         _defif=$2
357
358         if [ -z "${_x}" ]; then
359                 # No IP given. This can happen for the primary address
360                 # of each address family.
361                 return
362         fi
363
364         # Loop, in case we find a comma separated list, we need to handle
365         # each argument on its own.
366         while [ ${#_x} -gt 0 ]; do
367                 case "${_x}" in
368                 *,*)    # Extract the first argument and strip it off the list.
369                         _i=`expr "${_x}" : '^\([^,]*\)'`
370                         _x=`expr "${_x}" : "^[^,]*,\(.*\)"`
371                 ;;
372                 *)      _i=${_x}
373                         _x=""
374                 ;;
375                 esac
376
377                 _type=""
378                 _addr=""
379                 _mask=""
380                 _iface=""
381                 jail_extract_address $_i $_defif
382
383                 # make sure we got an address.
384                 case $_addr in
385                 "")     continue ;;
386                 *)      ;;
387                 esac
388
389                 # Append address to list of addresses for the jail command.
390                 case $_type in
391                 inet)
392                         echo "  ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
393                 ;;
394                 inet6)
395                         echo "  ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
396                         need_dad_wait=1
397                 ;;
398                 esac
399         done
400 }
401
402 jail_config()
403 {
404         local _j _jv
405
406         case $1 in
407         _ALL)   return ;;
408         esac
409         for _j in $@; do
410                 _j=$(echo $_j | tr /. _)
411                 _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
412                 if parse_options $_j $_jv; then 
413                         echo "$_j: parameters are in $_conf."
414                 fi
415         done
416 }
417
418 jail_console()
419 {
420         local _j _jv _cmd
421
422         # One argument that is not _ALL.
423         case $#:$1 in
424         0:*|1:_ALL)     err 3 "Specify a jail name." ;;
425         1:*)            ;;
426         esac
427         _j=$(echo $1 | tr /. _)
428         _jv=$(echo -n $1 | tr -c '[:alnum:]' _)
429         shift
430         case $# in
431         0)      eval _cmd=\${jail_${_jv}_consolecmd:-$jail_consolecmd} ;;
432         *)      _cmd=$@ ;;
433         esac
434         $jail_jexec $_j $_cmd
435 }
436
437 jail_status()
438 {
439
440         $jail_jls -N
441 }
442
443 jail_start()
444 {
445         local _j _jv _jid _id _name
446
447         if [ $# = 0 ]; then
448                 return
449         fi
450         echo -n 'Starting jails:'
451         case $1 in
452         _ALL)
453                 command=$jail_program
454                 rc_flags=$jail_flags
455                 command_args="-f $jail_conf -c"
456                 if ! checkyesno jail_parallel_start; then
457                         command_args="$command_args -p1"
458                 fi
459                 _tmp=`mktemp -t jail` || exit 3
460                 if $command $rc_flags $command_args >> $_tmp 2>&1; then
461                         $jail_jls jid name | while read _id _name; do
462                                 echo -n " $_name"
463                                 echo $_id > /var/run/jail_${_name}.id
464                         done
465                 else
466                         cat $_tmp
467                 fi
468                 rm -f $_tmp
469                 echo '.'
470                 return
471         ;;
472         esac
473         if checkyesno jail_parallel_start; then
474                 #
475                 # Start jails in parallel and then check jail id when
476                 # jail_parallel_start is YES.
477                 #
478                 for _j in $@; do
479                         _j=$(echo $_j | tr /. _)
480                         _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
481                         parse_options $_j $_jv || continue
482
483                         eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
484                         eval command=\${jail_${_jv}_program:-$jail_program}
485                         command_args="-i -f $_conf -c $_j"
486                         (
487                                 _tmp=`mktemp -t jail_${_j}` || exit 3
488                                 if $command $rc_flags $command_args \
489                                     >> $_tmp 2>&1 </dev/null; then
490                                         echo -n " ${_hostname:-${_j}}"
491                                         _jid=$($jail_jls -j $_j jid)
492                                         echo $_jid > /var/run/jail_${_j}.id
493                                 else
494                                         echo " cannot start jail " \
495                                             "\"${_hostname:-${_j}}\": "
496                                         cat $_tmp
497                                 fi
498                                 rm -f $_tmp
499                         ) &
500                 done
501                 wait
502         else
503                 #
504                 # Start jails one-by-one when jail_parallel_start is NO.
505                 #
506                 for _j in $@; do
507                         _j=$(echo $_j | tr /. _)
508                         _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
509                         parse_options $_j $_jv || continue
510
511                         eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
512                         eval command=\${jail_${_jv}_program:-$jail_program}
513                         command_args="-i -f $_conf -c $_j"
514                         _tmp=`mktemp -t jail` || exit 3
515                         if $command $rc_flags $command_args \
516                             >> $_tmp 2>&1 </dev/null; then
517                                 echo -n " ${_hostname:-${_j}}"
518                                 _jid=$($jail_jls -j $_j jid)
519                                 echo $_jid > /var/run/jail_${_j}.id
520                         else
521                                 echo " cannot start jail " \
522                                     "\"${_hostname:-${_j}}\": "
523                                 cat $_tmp
524                         fi
525                         rm -f $_tmp
526                 done
527         fi
528         echo '.'
529 }
530
531 jail_stop()
532 {
533         local _j _jv
534
535         if [ $# = 0 ]; then
536                 return
537         fi
538         echo -n 'Stopping jails:'
539         case $1 in
540         _ALL)
541                 command=$jail_program
542                 rc_flags=$jail_flags
543                 command_args="-f $jail_conf -r"
544                 if checkyesno jail_reverse_stop; then
545                         $jail_jls name | tail -r
546                 else
547                         $jail_jls name
548                 fi | while read _j; do
549                         echo -n " $_j"
550                         _tmp=`mktemp -t jail` || exit 3
551                         $command $rc_flags $command_args $_j >> $_tmp 2>&1
552                         if $jail_jls -j $_j > /dev/null 2>&1; then
553                                 cat $_tmp
554                         else
555                                 rm -f /var/run/jail_${_j}.id
556                         fi
557                         rm -f $_tmp
558                 done
559                 echo '.'
560                 return
561         ;;
562         esac
563         checkyesno jail_reverse_stop && set -- $(reverse_list $@)
564         for _j in $@; do
565                 _j=$(echo $_j | tr /. _)
566                 _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
567                 parse_options $_j $_jv || continue
568                 if ! $jail_jls -j $_j > /dev/null 2>&1; then
569                         continue
570                 fi
571                 eval command=\${jail_${_jv}_program:-$jail_program}
572                 echo -n " ${_hostname:-${_j}}"
573                 _tmp=`mktemp -t jail` || exit 3
574                 $command -q -f $_conf -r $_j >> $_tmp 2>&1
575                 if $jail_jls -j $_j > /dev/null 2>&1; then
576                         cat $_tmp
577                 else
578                         rm -f /var/run/jail_${_j}.id
579                 fi
580                 rm -f $_tmp
581         done
582         echo '.'
583 }
584
585 jail_warn()
586 {
587
588         # To relieve confusion, show a warning message.
589         case $_confwarn in
590         1)      warn "Per-jail configuration via jail_* variables " \
591                     "is obsolete.  Please consider migrating to $jail_conf."
592         ;;
593         esac
594 }
595
596 load_rc_config $name
597 case $# in
598 1)      run_rc_command $@ ${jail_list:-_ALL} ;;
599 *)      jail_reverse_stop="no"
600         run_rc_command $@ ;;
601 esac