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