Support a base cgroup.
[util-vserver.git] / scripts / vserver.functions
1 # $Id$  --*- sh -*--
2
3 # Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 #  
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
8 #  
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #  
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 ## Expected env:
19 #  $VSERVER_DIR   ... path to vserver-cfg dir
20 #  $VSERVER_NAME  ... name of vserver
21
22 declare -a NICE_CMD=()
23 declare -a IONICE_CMD=()
24 declare -a CHBIND_CMD=()
25 declare -a CAP_OPTS=()
26 declare -a CHCONTEXT_INIT_OPTS=()
27 declare -a CHCONTEXT_FLAG_OPTS=()
28 declare -a CHCONTEXT_OPTS=()
29 declare -a CAPCHROOT_OPTS=()
30 declare -a INTERFACES=()
31
32 declare -a INITCMD_RESCUE=( /bin/sleep 900 )
33 declare -a INITCMD_START=()
34 declare -a INITCMD_START_SYNC=()
35 declare -a INITCMD_STOP=()
36 declare -a INITCMD_STOP_SYNC=()
37 declare -a INITCMD_PREPARE=()
38 declare -a INITKILL_SEQ=()
39 declare -a ENTER_SHELL=()
40
41 declare -a OPTS_VCONTEXT_CREATE=()
42 declare -a OPTS_VCONTEXT_MIGRATE=()
43 declare -a OPTS_VCONTEXT_ENTER=()
44 OPT_VCONTEXT_CHROOT=--chroot
45 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
46 declare -a OPTS_VSCHED=()
47 declare -a OPTS_ENV=()
48 declare -a OPTS_VTAG_CREATE=()
49 declare -a OPTS_VTAG_ENTER=()
50 declare -a OPTS_VMEMCTRL=()
51 declare -a OPTS_VSPACE=( --default )
52 declare -a OPTS_VSPACE_SHARED=()
53 declare -a VSPACE_SHARED_CMD=()
54
55 declare -a STOPCMD_PREPARE=()
56
57 declare -a VSERVER_EXTRA_CMDS=()
58
59 INIT_RESCUE=
60 VSHELPER_SYNC_TIMEOUT=30
61 USE_VNAMESPACE=
62 INTERFACE_CMDS_IDX=0
63 RUNLEVEL_START=
64 RUNLEVEL_STOP=
65 _HAVE_INTERFACE_OPTIONS=
66 _HAVE_CHBIND_OPTIONS=
67 _NEED_VSHELPER_SYNC=
68 _IS_FAKEINIT=
69
70 INITSTYLE=sysv
71
72 S_CONTEXT=
73 N_CONTEXT=
74
75 SILENT_OPT=
76
77 CGROUP_MNT=/dev/cgroup
78 CGROUP_SUBSYS=all
79 declare -a CGROUP_INHERIT=( cpuset.cpus cpuset.mems )
80 CGROUP_BASE=""
81
82 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
83
84 if test -e "$VSERVER_DIR"/noisy -o -n "$OPTION_VERBOSE"; then
85     SILENT_OPT=
86 else
87     SILENT_OPT='--silent'
88 fi
89
90 function _readFileToArray
91 {
92     local _rfta_f="$1"
93     local _rfta_a="$2"
94     local _rfta_p="$3"
95     local _rfta_v
96
97     test -e "$_rfta_f" || return 0
98     while read _rfta_v; do
99         case x"$_rfta_v" in
100             (x|x\#*)    ;;
101             (*)         eval "$_rfta_a=( \"\${$_rfta_a[@]}\" $_rfta_p \"$_rfta_v\" )";;
102         esac
103     done <"$_rfta_f"
104 }
105
106 function _generateChbindOptions
107 {
108     local vdir="$1"
109     local i
110     local bcast=
111     local lback=
112     local nid=
113
114     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
115
116     if test -e "$vdir"/noncontext -o \
117             \( -e "$vdir"/spaces/net -a ! -e "$vdir"/ncontext \); then
118         _HAVE_CHBIND_OPTIONS=1
119         return 0
120     fi
121
122     local f="$vdir"/interfaces/bcast
123     getFileValue bcast "$f"
124     f="$vdir"/interfaces/lback
125     getFileValue lback "$f"
126
127     CHBIND_CMD=( $_CHBIND $SILENT_OPT --secure ${N_CONTEXT:+--nid "$N_CONTEXT"}
128                  ${bcast:+--bcast "$bcast"} ${lback:+--lback "$lback"}
129                 )
130
131     for i in "${INTERFACES[@]}"; do
132         CHBIND_CMD=( "${CHBIND_CMD[@]}" --ip "$i" )
133     done
134
135     _readFileToArray "$vdir"/nflags        CHBIND_CMD --flag
136     _readFileToArray "$vdir"/ncapabilities CHBIND_CMD --ncap
137
138     _HAVE_CHBIND_OPTIONS=1
139 }
140
141 function _generateNiceCommand
142 {
143     local vdir=$1
144     local nice=0
145     local current_nice=`$_NICE`
146
147     test -r "$vdir/nice" && read nice <"$vdir"/nice
148
149     let nice=$nice-$current_nice || :
150     NICE_CMD=( $_NICE -n $nice )
151 }
152
153 function _generateIONiceCommand
154 {
155     local vdir=$1
156     local ionice_class=2
157     local ionice_priority=0
158
159     test -n "$_IONICE" || return 0
160
161     test -r "$vdir/ionice/class" && read ionice_class <"$vdir"/ionice/class
162     test -r "$vdir/ionice/priority" && read ionice_priority <"$vdir"/ionice/priority
163
164     IONICE_CMD=( $_IONICE -c$ionice_class -n$ionice_priority )
165 }
166
167 function _generatePersonalityOptions
168 {
169     local vdir="$1"
170     local f="$vdir"/personality
171     local type flags
172
173     test -s "$f" || return 0
174
175     {
176         local delim tmp
177
178         read type
179         while read tmp; do
180             case x$tmp in
181                 (x\#*|x)        ;;
182                 (*)             flags=$flags$delim$tmp
183                                 delim=,
184                                 ;;
185             esac
186         done
187     } <"$f"
188
189     OPTS_VCONTEXT_ENTER=( "${OPTS_VCONTEXT_ENTER[@]}"
190                           --personality-type "$type"
191                           ${flags:+--personality-flags "$flags"} )
192 }
193
194 function _generateCCapabilityOptions
195 {
196     local vdir=$1
197
198     _readFileToArray "$vdir"/ccapabilities OPTS_VATTRIBUTE --ccap
199 }
200
201 function _generateBCapabilityOptions
202 {
203     local vdir=$1
204
205     _readFileToArray "$vdir"/bcapabilities OPTS_VATTRIBUTE --bcap
206 }
207
208 function _generateCapabilityOptions
209 {
210     local vdir=$1
211     local cap
212
213     _generateBCapabilityOptions "$vdir"
214     _generateCCapabilityOptions "$vdir"
215     
216     test -e "$vdir"/capabilities || return 0
217
218     CAP_OPTS=()
219     CAPCHROOT_OPTS=()
220
221     while read cap; do
222         case x"$cap" in
223             (x|x\#*)    ;;
224             (!CAP_SYSCHROOT)
225                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
226                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
227                 ;;
228             (*)
229                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
230                 ;;
231         esac
232     done <"$vdir"/capabilities
233 }
234
235 function getEnterShell
236 {
237     local vdir=$1
238     local shell_file
239
240     ENTER_SHELL=()
241
242     getFileValue ENTER_SHELL "$vdir"/shell "$__CONFDIR"/.defaults/shell
243     
244     test -n "$ENTER_SHELL" || {
245         local i
246         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
247             set -- $i
248             test -x "$vdir/vdir/$1" || continue
249             ENTER_SHELL=( "$@" )
250             break
251         done
252     }
253 }
254
255 ## Usage: sendKillSequence <ctx> <signal> [<wait> <signal>]*
256 function sendKillSequence
257 {
258     local ctx=$1
259     local wait=
260     shift
261
262     while isCtxRunning "$ctx"; do
263         test -z "$wait" || sleep "$wait"
264
265         killContext "$ctx" "$1"
266         test -n "$2" || break
267         wait="$2"
268         shift 2
269     done
270 }
271
272 function _generateInitOptions
273 {
274     local vdir=$1
275     local cfgdir=$vdir/apps/init
276     local i f
277
278     INITCMD_START=()
279     INITCMD_STOP=()
280     INITCMD_START_SYNC=()
281     INITCMD_STOP_SYNC=()
282     INITCMD_PREPARE=()
283     STOPCMD_PREPARE=()
284
285     INITKILL_SEQ=( 15 5 9 )
286     CHCONTEXT_INIT_OPTS=()
287
288
289     test x"$INITSTYLE" = xrescue || \
290       getFileValue INITSTYLE    "$cfgdir"/style
291     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
292     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
293     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
294     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
295
296     findFile _gio_env           "$cfgdir"/environment \
297         "$__CONFDIR"/.defaults/apps/init/environment \
298         "$__PKGLIBDEFAULTDIR"/environment
299     getFileArray OPTS_ENV       "$_gio_env" || :
300
301     case x"$INITSTYLE" in
302         (xrescue)
303             INITCMD_START=( "${INITCMD_RESCUE[@]}" )
304             INITCMD_STOP=( /sbin/killall5 )
305             ;;
306             
307         (xsysv)
308             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
309             test -n "$RUNLEVEL_STOP"  || RUNLEVEL_STOP=6
310
311             for i in /etc/init.d/rc /etc/rc.d/rc; do
312                 test -x "$vdir/vdir/$i" || continue
313                 INITCMD_START=( "$i" "$RUNLEVEL_START" )
314                 INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
315             done
316             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
317             OPTS_ENV=( "${OPTS_ENV[@]}" PREVLEVEL=N RUNLEVEL="$RUNLEVEL_START" )
318             if test -n "$OPTION_DEBUG_SYSV"; then
319                 INITCMD_START=( /bin/bash -x "${INITCMD_START[@]}" )
320                 INITCMD_STOP=( /bin/bash -x "${INITCMD_STOP[@]}" )
321             fi
322             ;;
323             
324         (xplain)
325             INITCMD_START=( /sbin/init )
326             INITCMD_STOP=(  /sbin/init )
327             _IS_FAKEINIT=1
328             _NEED_VSHELPER_SYNC=1
329             test -z "$RUNLEVEL_START" || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
330             test -z "$RUNLEVEL_STOP"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
331             ;;
332             
333         (xminit)
334             INITCMD_START=( /sbin/minit-start )
335             INITCMD_STOP=(  /sbin/minit-stop  )
336             _IS_FAKEINIT=1
337             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
338             _NEED_VSHELPER_SYNC=1
339             test -z "$RUNLEVEL_START"         || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
340             test -z "$RUNLEVEL_STOP"          || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
341             ! isNumber "${RUNLEVEL_START:-3}" || INITCMD_PREPARE=( $_FAKE_RUNLEVEL "${RUNLEVEL_START:-3}" /var/run/utmp )
342             ;;
343
344         (xgentoo)
345             test -n "$RUNLEVEL_START" || RUNLEVEL_START="default"
346             RC_PATH=/usr/sbin:/usr/bin:/sbin:/bin
347
348             if test -x "$vdir/vdir/lib/rcscripts/sh/init-vserver.sh"; then
349                 RC_WRAP=/lib/rcscripts/sh/init-vserver.sh
350             elif test -x "$vdir/vdir/lib/rc/sh/init-vserver.sh"; then
351                 RC_WRAP=/lib/rc/sh/init-vserver.sh
352             else
353                 panic "init-vserver.sh not found; aborting"
354             fi
355
356             OPTS_ENV=( "${OPTS_ENV[@]}" TERM=$TERM )
357             INITCMD_START=( $RC_WRAP "$RUNLEVEL_START" )
358             INITCMD_STOP=( env -i PATH=$RC_PATH TERM=$TERM RUNLEVEL=0 /sbin/rc shutdown )
359             INITCMD_PREPARE=( $_FAKE_RUNLEVEL 3 /var/run/utmp )
360             ;;
361
362         (xarch)
363             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
364             INITCMD_START=( /etc/rc.multi )
365             INITCMD_STOP=( /etc/rc.shutdown )
366             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
367             ;;
368
369         (x) ;;
370         (*) panic "Unknown init-style '$INITSTYLE'; aborting";;
371     esac
372
373     if test x"$INITSTYLE" != xrescue; then
374         getFileArray INITCMD_START      "$cfgdir"/cmd.start      || :
375         getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop       || :
376         getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync || :
377         getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync  || :
378         getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare    || :
379     fi
380     
381     test -n "$OPTION_FORCE_SYNC" -o -e "$cfgdir"/sync || {
382         INITCMD_START_SYNC=()
383         INITCMD_STOP_SYNC=()
384         _NEED_VSHELPER_SYNC=
385     }
386
387     if vshelper.isEnabled; then
388         vshelper.getSyncTimeout "$vdir" VSHELPER_SYNC_TIMEOUT || :
389     else
390         _NEED_VSHELPER_SYNC=
391     fi
392 }
393
394 function _generateFlagOptions
395 {
396     local vdir=$1
397     local file
398
399     CHCONTEXT_FLAG_OPTS=()
400
401     findFile file "$vdir"/cflags "$vdir"/flags ""
402     test -z "$file" || \
403     while read flag; do
404         case x"$flag" in
405             (x|x\#*)            ;;
406             (xnamespace)        ;;
407             (xfakeinit)
408                 _IS_FAKEINIT=1
409                 ;;
410             (*)
411                 OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --flag "$flag" )
412                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
413                                       --flag "$flag" )
414                 ;;
415         esac
416     done <"$file"
417
418     isAvoidNamespace "$vdir" || {
419         USE_VNAMESPACE=1
420         CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" --flag namespace )
421         ! $_VSERVER_INFO - FEATURE PIVOT_ROOT || \
422             OPT_VCONTEXT_CHROOT=--pivot-root
423     }
424 }
425
426 function _generateChcontextOptions
427 {
428     local vdir=$1
429     local ctx hostname domainname
430     local cap_opts
431     local flag
432
433     {
434         read ctx        <"$vdir"/context        || :
435         ## LEGACY ALERT
436         read hostname   <"$vdir"/uts/nodename   || read hostname   <"$vdir"/hostname   || :
437         read domainname <"$vdir"/uts/domainname || read domainname <"$vdir"/domainname || :
438     } 2>/dev/null
439
440     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
441
442     _generateCapabilityOptions "$vdir"
443     _generateFlagOptions       "$vdir"
444
445     CHCONTEXT_OPTS=( $SILENT_OPT \
446                      "${CHCONTEXT_FLAG_OPTS[@]}" \
447                      "${CAP_OPTS[@]}" \
448                      --secure
449                      ${ctx:+--ctx "$ctx"} \
450                      ${hostname:+--hostname "$hostname"} \
451                      ${domainname:+--domainname "$domainname"} )
452
453     OPTS_VCONTEXT_CREATE=( $SILENT_OPT \
454                            ${ctx:+--xid "$ctx"} )
455     ## put '--secure' at front so that it can be overridden
456     OPTS_VATTRIBUTE=( --secure --flag default "${OPTS_VATTRIBUTE[@]}" )
457 }
458
459 function _generateScheduleOptions
460 {
461     local vdir=$1
462     if test -d "$vdir"/sched; then
463       OPTS_VSCHED=( --dir "$vdir"/sched --missingok )
464       return 0
465     fi
466
467     local f="$vdir"/schedule
468     test -e "$f" || return 0
469
470     local fill_rate interval tokens tokens_min tokens_max prio_bias
471     {
472         {
473             read fill_rate   && \
474             read interval    && \
475             read tokens      && \
476             read tokens_min  && \
477             read tokens_max  && \
478             read prio_bias   || prio_bias=
479         } <"$f"
480     } 2>/dev/null
481
482     test -n "$prio_bias" || {
483         echo $"Bad content in '$f'; aborting..." >&2
484         false
485     }
486
487     OPTS_VSCHED=( --fill-rate  "$fill_rate"  --interval "$interval" \
488                   --tokens     "$tokens"     --tokens_min "$tokens_min" \
489                   --tokens_max "$tokens_max" --priority-bias "$prio_bias" )
490 }
491
492 function _getInterfaceValue
493 {
494     local _giv_val=$1
495     local _giv_dflt=$2
496     shift 2
497     
498     local _giv_i
499     local _giv_tmp
500
501     for _giv_i; do
502         read _giv_tmp  <"$_giv_i/$_giv_val" && break || :
503     done 2>/dev/null
504
505     : ${_giv_tmp:=$_giv_dflt}
506     eval $_giv_val=\$_giv_tmp
507 }
508
509 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
510 function _transformMask2Prefix
511 {
512     local _tm2p_tmp=$2
513     
514     test -n "$_tm2p_tmp" || {
515         $_MASK2PREFIX "$3" || _tm2p_tmp=$?
516     }
517
518     eval $1=\$_tm2p_tmp
519     return 0
520 }
521
522 function _addInterfaceCmd
523 {
524     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
525     let ++INTERFACE_CMDS_IDX
526 }
527
528 ## Usage: _generateMac <var> <iface> <ctx>
529 function _generateMac
530 {
531     isNumber "$2" || {
532         echo $"Interface basename '$iface' must be either a number, or the mac must be configured explicitly" >&2
533         return 1
534     }
535
536     eval $1=$(printf "f0:ff:%02x:%02x:%02x:%02x" $[ (~($2>>8)) & 0xff ] $[ ($2 & 0xff) ] $[ ($3>>8) & 0xff ] $[ $3 & 0xff ])
537 }
538
539 function _getVLANInfo
540 {
541     case "$1" in
542         (vlan????)
543             panic "\
544 creation of VLAN_PLUS_VID devices is not supported; please create them
545 before starting the vserver and use the 'nodev' flag then"
546             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID"
547             ;;
548         (vlan*)
549             panic "\
550 creation of VLAN_PLUS_VID_NO_PAD devices is not supported; please
551 create them before starting the vserver and use the 'nodev' flag then"
552             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID_N0_PAD"
553             ;;
554         (*.????)        echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID";;
555         (*.*)           echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID_NO_PAD";;
556         (*)             return 1
557     esac
558
559     return 0
560 }
561
562 function _getTunInfo
563 {
564     local iface="$1"
565
566     test -e "$iface/tun" -o -e "$iface/tap" || return 1
567     test ! -e "$iface/tun"     || echo --tun
568     test ! -e "$iface/tap"     || echo --tap
569     test ! -e "$iface/nocsum"  || echo --~checksum
570     test   -e "$iface/shared"  || echo --nid-failure-ok "$N_CONTEXT"
571     if test -e "$iface/uid"; then
572         local uid
573         getFileValue uid "$iface/uid"
574         echo --uid "$uid"
575     fi
576     if test -e "$iface/gid"; then
577         local gid
578         getFileValue gid "$iface/gid"
579         echo --gid "$gid"
580     fi
581     if test -e "$iface/linktype"; then
582         local linktype
583         getFileValue linktype "$iface/linktype"
584         echo --linktype "$linktype"
585     fi
586     return 0
587 }
588
589 ## Usage: _processSingleInterface <interface-directory>
590 function _processSingleInterface
591 {
592     local iface=$1
593
594     local ip
595     local dev
596     local prefix
597     local mask
598     local bcast
599     local name
600     local scope
601     local mac
602     local extip
603     local up="up"
604     local peer
605
606     _getInterfaceValue ip     '' "$iface"
607     _getInterfaceValue extip  '' "$iface" "$iface/.."
608     _getInterfaceValue dev    '' "$iface" "$iface/.."
609     _getInterfaceValue prefix '' "$iface" "$iface/.."
610     _getInterfaceValue mask   '' "$iface" "$iface/.."
611     _getInterfaceValue bcast  '' "$iface" "$iface/.."
612     _getInterfaceValue name   '' "$iface"
613     _getInterfaceValue scope  '' "$iface" "$iface/.."
614     _getInterfaceValue mac    '' "$iface"
615     _getInterfaceValue peer   '' "$iface"
616
617     test -n "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
618     test -n "$dev" -o -e "$iface"/nodev || {
619         echo $"No device specified for '$iface'" >&2
620         return 1;
621     }
622
623     test ! -e "$iface"/down || up=
624
625     while true; do
626         _transformMask2Prefix prefix "$prefix" "$mask"
627         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
628
629         test ! -e "$iface"/nodev   || break
630         ## LEGACY ALERT
631         test ! -e "$iface"/only_ip || break
632
633         test -e "$iface/vlandev" \
634              -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
635              -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
636                    -a ! -e "$iface/novlandev" \
637                    -a ! -e "$iface/../novlandev" \) && {
638             local vlan_info
639             if vlan_info=$(_getVLANInfo "$dev"); then
640                 test -d /proc/net/vlan || {
641                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
642                     return 1
643                 }
644                 _addInterfaceCmd VCONFIG $vlan_info
645             fi
646         }
647
648         if ! test -e "$iface"/indirect; then
649             # XXX: IPv6 hack
650             local use_bcast="broadcast ${bcast:-+}"
651             echo "$ip" | $_GREP -q : && use_bcast=
652
653             local tun_info
654             if tun_info=$(_getTunInfo "$iface"); then
655                 _addInterfaceCmd TUNCTL "$dev" $tun_info
656             fi
657
658             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_bcast ${name:+label "$dev:$name"} dev "$dev" ${peer:+peer "$peer"}
659             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
660             _addInterfaceCmd IP_LINK  "$dev" $up
661         elif ! test -n "$N_CONTEXT"; then
662             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
663             return 1
664         else
665             test -z "$mac" || _generateMac mac "$(basename $iface)" "$N_CONTEXT" || return 1
666             _addInterfaceCmd MODPROBE dummy "$dev"
667             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
668             _addInterfaceCmd NAMEIF   "$dev" "$mac"
669             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
670             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$N_CONTEXT" "$extip"
671         fi
672
673         break
674     done
675 }
676
677 ## Usage: _generateInterfaceOptions <vserver-directory>
678 function _generateInterfaceOptions
679 {
680     local iface
681
682     # XXX: This is here instead of in _generateChbindOptions
683     #      to avoid a circular dependency
684     getFileValue N_CONTEXT "$1/ncontext" "$1/context"
685     test -n "$N_CONTEXT" -o -z "$S_CONTEXT" || N_CONTEXT="$S_CONTEXT"
686
687     for iface in "$1/interfaces/"*; do
688         test   -d "$iface"          || continue
689         test ! -e "$iface"/disabled || continue
690     
691         _processSingleInterface "$iface"
692     done
693     _HAVE_INTERFACE_OPTIONS=1
694 }
695
696 function enableInterfaces
697 {
698     local i=0
699     declare -a var
700
701     lock "$__LOCKDIR"/vserver.interfaces
702
703     while test $i -lt $INTERFACE_CMDS_IDX; do
704         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
705         local type=${var[0]}
706         unset var[0]
707
708         set -- "${var[@]}"
709         case "$type" in
710             IPTABLES)   ;; ## TODO
711             MODPROBE)
712                 local mod=$1
713                 local name=$2
714                 shift 2
715                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
716                 ;;
717             NAMEIF)             $_NAMEIF   "$@";;
718             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
719                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
720             IP_ADDR)            $_IP addr  add   "$@";;
721             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
722             IP_LINK)            $_IP link  set   "$@";;
723             IP_ROUTE)           $_IP route add   "$@";;
724             TUNCTL)
725                 local dev="$1"
726                 shift
727                 $_TUNCTL --persist "$@" "$dev"
728                 ;;
729             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
730         esac
731
732         let ++i
733     done
734
735     unlock 1
736 }
737
738 function disableInterfaces
739 {
740     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
741
742     local i=$INTERFACE_CMDS_IDX
743     declare -a var
744
745     lock "$__LOCKDIR"/vserver.interfaces
746     
747     while test $i -gt 0; do
748         let --i || :
749
750         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
751         local type=${var[0]}
752         unset var[0]
753         
754         set -- "${var[@]}"
755         case "$type" in
756             IPTABLES)           ;; ## TODO
757             MODPROBE)           $_RMMOD "${2:-$1}";;
758             NAMEIF)             ;;
759             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
760             IP_ADDR)            $_IP addr  del "$@";;
761             IP_ADDR_FLUSH)      ;;
762             IP_LINK)            ;; ## Ignore the link-down command for now
763             IP_ROUTE)           $_IP route del "$@";;
764             TUNCTL)             $_TUNCTL --~persist "$1";;
765             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
766         esac
767     done
768
769     unlock 1
770 }
771
772 function _generateTagOptions
773 {
774     local vdir="$1"
775     local tag
776
777     getFileValue tag "$vdir/tag" "$vdir/context"
778     test -n "$tag" || return 0
779
780     OPTS_VTAG_CREATE=( --tag "$tag" )
781     OPTS_VTAG_ENTER=( --tag "$tag" )
782 }
783
784 function _generateMemctrlOptions
785 {
786     local vdir="$1"
787     local badness
788
789     getFileValue badness "$vdir/badness"
790     test -n "$badness" || return 0
791
792     OPTS_VMEMCTRL=( --badness "$badness" )
793 }
794
795 function _generateSpaceOptions
796 {
797     local vdir="$1"
798     local d="$vdir"/spaces
799     local shared
800     local space
801
802     for space in pid net; do
803         if test -e "$d"/$space; then
804             getFileValue shared "$d"/$space || shared=""
805             if test -z "$shared"; then
806                 OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --$space )
807             elif test "$shared" = "0"; then
808                 : # Do nothing
809             else
810                 OPTS_VSPACE_SHARED=( "${OPTS_VSPACE_SHARED[@]}" --$space )
811                 VSPACE_SHARED_CMD=( "${VSPACE_SHARED_CMD[@]}" $_VSPACE --enter "$shared" --$space -- )
812             fi
813         fi
814     done
815
816     local mask
817     getFileValue mask "$d"/mask || \
818       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --mask "$mask" )
819 }
820
821 ## Usage: prepareInit <vserver-directory>
822 function prepareInit
823 {
824     pushd "$1/vdir" >/dev/null
825     case "$INITSTYLE" in
826         sysv)
827             { $_FIND var/run  ! -type d -print0; \
828               $_FIND var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
829             ;;
830         plain)
831             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
832             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
833             ;;
834         minit)
835             ;;
836     esac
837     "${INITCMD_PREPARE[@]}"
838     popd >/dev/null
839 }
840
841 ## Usage: prepareInit <vserver-directory>
842 function prepareStop
843 {
844     pushd "$1/vdir" >/dev/null
845     case "$INITSTYLE" in
846         (sysv)
847             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
848             ;;
849     esac
850     "${STOPCMD_PREPARE[@]}"
851     popd >/dev/null
852 }
853
854
855 function generateOptions
856 {
857     _generateInterfaceOptions   "$1"
858     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
859     _generateNiceCommand        "$1"
860     _generateIONiceCommand      "$1"
861     _generateInitOptions        "$1"
862     _generateChcontextOptions   "$1"
863     _generateScheduleOptions    "$1"
864     _generatePersonalityOptions "$1"
865     _generateTagOptions         "$1"
866     _generateMemctrlOptions     "$1"
867     _generateSpaceOptions       "$1"
868     _generateCgroupOptions
869
870     if test -n "$_IS_FAKEINIT"; then
871         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
872         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
873     fi
874 }
875
876 function addtoCPUSET
877 {
878     local vdir=$1
879     local cpuset
880     local f="$vdir"/cpuset
881     local i
882     local configured=0
883
884     test -d "$f" || return 0
885     test -e "$f"/name || return 0
886
887     read cpuset < "$f"/name
888     test -e "$f"/nocreate || {
889        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
890        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
891            if test -e "$f"/"$i"; then
892                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
893                    configured=1
894                    break
895                }
896            fi
897        done
898     }
899
900     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
901     if [ "$configured" -ne 0 ]; then
902        warning $"\
903 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
904        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
905        return 0
906     fi
907 }
908
909 function removeCPUSET
910 {
911     local vdir=$1
912     local cpuset
913     local f="$vdir"/cpuset
914
915     test -d "$f" || return 0
916     test -e "$f"/name || return 0
917
918     read cpuset < "$f"/name
919     test -e "$f"/nocreate || {
920        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
921     }
922 }
923
924 function _mountVserverInternal
925 {
926     local fstab="$1"
927     local xflag=
928     
929     test -e "$fstab" || return 0
930     shift
931
932     pushd "$vdir" >/dev/null
933     # check whether / is mounted readonly or whether there is special
934     # magic regarding the mtab file; when etc/mtab can not be touched,
935     # add the '-n' flag to mount
936     test -w etc -o -w etc/mtab || xflag=-n
937     if test -h etc/mtab; then
938         local l=$($_READLINK etc/mtab)
939         test "${l##/proc/}" = "$l" || xflag=-n
940     fi
941     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
942     popd >/dev/null
943 }
944
945 function mountRootFS
946 {
947     local cfgdir=$1
948     local vdir=$1/vdir
949     local fstab="$cfgdir"/fstab
950     local xflag=
951
952     test -e "$fstab" || return 0
953     pushd "$vdir" >/dev/null
954     # check whether / is mounted readonly or whether there is special
955     # magic regarding the mtab file; when etc/mtab can not be touched,
956     # add the '-n' flag to mount
957     test -w etc -o -w etc/mtab || xflag=-n
958     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
959     popd >/dev/null
960 }
961
962 function mountVserver
963 {
964     local cfgdir=$1
965     local ns_opt=$2
966     local vdir=$1/vdir
967     local mtab_src
968     local extra_opt=
969     local real_vdir
970
971     test -e "$cfgdir"/fstab -o \
972          -e "$cfgdir"/fstab.local -o \
973          -e "$cfgdir"/fstab.remote || return 0
974
975     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
976     
977     pushd "$vdir" >/dev/null
978     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
979     popd >/dev/null
980
981     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
982
983     _mountVserverInternal "$cfgdir"/fstab
984     _mountVserverInternal "$cfgdir"/fstab.local
985     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
986
987     isNamespaceCleanup "$cfgdir" && \
988         _namespaceCleanup "$cfgdir"
989
990     real_vdir=$(getPhysicalDir "$vdir")
991     isAvoidNamespace "$cfgdir" || \
992         $_SECURE_MOUNT --rbind -n -o dev "$vdir" "$real_vdir"
993     ! $_VSERVER_INFO - FEATURE PIVOT_ROOT || \
994         $_SECURE_MOUNT -n -o shared,rec "$real_vdir" "$real_vdir"
995 }
996
997 function _umountVserverInternal
998 {
999     local fstab="$1"
1000     test -e "$fstab" || return 0
1001     shift
1002
1003     $_TAC "$fstab" | {
1004         is_ok=1
1005         while read src dst tmp; do
1006             test -n "$tmp" || continue
1007             case x"$src" in
1008                 (x\#*)  continue;;
1009             esac
1010
1011         
1012             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
1013         done
1014         test -n "$is_ok"
1015     }
1016 }
1017
1018 function umountVserver
1019 {
1020     local cfgdir=$1
1021     local vdir=$1/vdir
1022     local is_ok=1
1023
1024     isAvoidNamespace "$cfgdir"    || return 0
1025     test -e "$cfgdir"/fstab -o \
1026          -e "$cfgdir"/fstab.local -o \
1027          -e "$cfgdir"/fstab.remote || return 0
1028     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
1029     
1030     pushd "$vdir/" >/dev/null || return 1
1031         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
1032         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
1033         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
1034     popd >/dev/null           || return 1
1035
1036     test -n "$is_ok"
1037 }
1038
1039 function fsckAllFS
1040 {
1041     local cfgdir=$1
1042     local fstab="$cfgdir"/fstab
1043     local FSTAB_FILE
1044     local fsck_exitcode
1045
1046     test -e "$fstab" || return 0
1047
1048     export FSTAB_FILE="$fstab"
1049     $_FSCK -s -n -A -T
1050     fsck_exitcode=$?
1051     test "$fsck_exitcode" -eq 0 -o \
1052          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
1053 }
1054
1055 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
1056 function initSync
1057 {
1058     local _is_meth=sync
1059     test -n "$_NEED_VSHELPER_SYNC" && \
1060         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
1061
1062     vshelper.initSync "$1" "$3" "$_is_meth"
1063 }
1064
1065 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
1066 function initWait
1067 {
1068     if $_VSERVER_INFO - FEATURE vwait; then
1069         local _is_tmpdir
1070         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
1071
1072         (
1073             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
1074                 --status-fd 3 "$2" \
1075                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
1076             rc=$?
1077
1078             if test "$rc" -ne 0 -a "$rc" -ne 1; then
1079                 $_VPS axf | $_EGREP -e "^[ \t]*[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
1080                 killContext "$S_CONTEXT" 9
1081             fi
1082
1083             exit $rc
1084         ) 4>$_is_tmpdir/procs &
1085             
1086         echo "$!" >$_is_tmpdir/pid
1087         eval "$3"=$_is_tmpdir
1088     fi </dev/null
1089 }
1090
1091
1092 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
1093 function _waitForVWait
1094 {
1095     wait "$3" || :
1096
1097     declare -a status
1098     declare -r procs=$(cat $4)
1099
1100     getFileArray status "$2"
1101     set -- ${status[0]}
1102
1103     case "$1" in
1104         (ERROR)         warning $"\
1105 'vwait' exited with error '$2' which indicates that vserver could not
1106 be stopped properly"
1107                         ;;
1108         (FINISHED)      ;;
1109         (KILLED)        warning $"\
1110 A timeout occured while waiting for the vserver to finish and it was
1111 killed by sending a SIGKILL signal. Please investigate the reasons
1112 and/or increase the timeout in apps/vshelper/sync-timeout."
1113                         ;;
1114
1115         (TIMEOUT)       warning $"\
1116 A timeout occured while waiting for the vserver to finish and it will
1117 be killed by sending a SIGKILL signal. The following process list
1118 might be useful for finding out the reason of this behavior:
1119
1120 ----------------------------------------------------------------------
1121 ${procs:+$procs
1122 }----------------------------------------------------------------------"
1123                         ;;
1124
1125         (\?\?\?|*)      warning $"\
1126 internal error: 'vwait' exited with an unexpected status '$1'; I will
1127 try to continue but be prepared for unexpected events."
1128                     ;;
1129     esac
1130
1131     return 0
1132 }
1133
1134 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
1135 function waitForSync
1136 {
1137     local cfgdir=$1
1138     local fifo=$2
1139     local vwait_statdir=$3
1140     local vwait_pid=$4
1141
1142     if test -d "$vwait_statdir"; then
1143         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1144     elif test -n "$_NEED_VSHELPER_SYNC"; then
1145         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1146             warning $"\
1147 A timeout or other error occured while waiting for the synchronization
1148 signal from vserver '$VSERVER_NAME'.
1149 The vserver will be killed nevertheless..."
1150     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1151         "${INITCMD_STOP_SYNC[@]}" || \
1152             warning $"\
1153 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1154 will be killed nevertheless..."
1155     fi
1156
1157     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1158         sleep 1
1159 }
1160
1161 function _sourceWrap
1162 {
1163     local vdir name flavor start i already_handled base
1164     . "$@"
1165 }
1166
1167 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1168 function execScriptlets
1169 {
1170     declare -r vdir=$1
1171     declare -r name=$2
1172     declare -r flavor=$3
1173     local base i
1174
1175     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1176         local   DONT_SKIP_DEFAULTS=
1177         local   already_handled=
1178         
1179         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1180             isRegularFile "$i" || continue
1181             test  -r "$i"      || continue
1182
1183             already_handled=1
1184             local start=
1185             test -x "$i" || start=_sourceWrap
1186             $start "$i" "$flavor" "$name"
1187         done
1188
1189         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1190     done
1191 }
1192
1193
1194 function sanityCheck
1195 {
1196     declare -r cfgdir=$1
1197
1198     ! test -e "$cfgdir"/fstab.local ||
1199         warning $"\
1200 WARNING: 'fstab' will *not* be executed in the network context of the
1201   vserver anymore. Therefore, 'fstab.local' has the same functionality
1202   and is obsoleted. When you need the old behaviour, put the mounts
1203   into 'fstab.remote'"
1204
1205     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1206         warning $"\
1207 WARNING: The hostname is now configured in 'uts/nodename' but not in
1208   'hostname'."
1209
1210     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1211         warning $"\
1212 WARNING: The domainname is now configured in 'uts/domainname' but not
1213   in 'domainname'." >&2
1214
1215   
1216     local i
1217     for i in "$cfgdir"/interfaces/*/only_ip; do
1218         if test -e "$i"; then
1219             local iface
1220             iface=${i##$cfgdir/interfaces/}
1221             iface=${iface%%/only_ip}
1222             warning $"\
1223 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1224   'nodev' instead of"
1225         fi
1226     done
1227
1228     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1229         warning $"\
1230 WARNING: There is no cachedirectory configured for this vserver;
1231   please create '$cfgdir/cache' e.g. by executing
1232
1233   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1234 "
1235
1236     $_FIND "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1237
1238     vshelper.doSanityCheck
1239
1240     $_VSERVER_INFO - VERIFYCAP ||
1241         panic $"capabilities are not enabled in kernel-setup"
1242
1243     $_VSERVER_INFO - VERIFYPROC ||
1244         panic $"\
1245 /proc/uptime can not be accessed. Usually, this is caused by
1246 procfs-security. Please read the FAQ for more details
1247 http://linux-vserver.org/Proc-Security"
1248
1249     test -e "$cfgdir"/context || {
1250         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1251         test "$TYPE" != "static" || panic $"\
1252 The kernel does not have dynamic contexts enabled. Please configure
1253 a static one by executing
1254
1255   echo [number between 2 and 49151] > $cfgdir/context"
1256     }
1257 }
1258
1259
1260 function _setSingleDiskLimit
1261 {
1262     local vdir=$1
1263     local dlimit=$2
1264     local space_used=
1265     local space_total=
1266     local inodes_used=
1267     local inodes_total=
1268     local reserved=
1269     local directory=
1270     local ctx=
1271
1272     getFileValue ctx          "$vdir/context"
1273     getFileValue directory    "$dlimit/directory"    || return 0
1274     getFileValue space_total  "$dlimit/space_total"  || return 0
1275     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1276     getFileValue reserved     "$dlimit/reserved"     || return 0
1277
1278     local cachename=$ctx$directory
1279     cachename=dlimits/${cachename//\//_}
1280
1281     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1282     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1283     $_RM -f "$vdir/cache/$cachename"
1284
1285     if test -z "$inodes_used" -o -z "$space_used"; then
1286         local tmpvdu
1287         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1288         inodes_used=${tmpvdu##* }
1289         space_used=${tmpvdu%% *}
1290     fi
1291
1292     $_VDLIMIT --xid $ctx \
1293         --set space_used=$space_used \
1294         --set space_total=$space_total \
1295         --set inodes_used=$inodes_used \
1296         --set inodes_total=$inodes_total \
1297         --set reserved=$reserved \
1298         "$directory"
1299 }
1300
1301
1302 function setDiskLimits
1303 {
1304     local vdir=$1
1305     local dlimit
1306
1307     # Disk Limits without a static context are useless
1308     test -e "$vdir"/context || return 0
1309
1310     for dlimit in "$vdir/dlimits/"*; do
1311         test   -d "$dlimit"          || continue
1312         test ! -e "$dlimit/disabled" || continue
1313
1314         _setSingleDiskLimit "$vdir" "$dlimit"
1315     done
1316 }
1317
1318
1319 function _saveSingleDiskLimit
1320 {
1321     local vdir=$1
1322     local dlimit=$2
1323     local ctx=
1324     local directory=
1325
1326     getFileValue ctx       "$vdir/context"
1327     getFileValue directory "$dlimit/directory" || return 0
1328
1329     local cachename=$ctx$directory
1330     cachename=${cachename//\//_}
1331
1332     # Things are getting ugly here... LFS says that /var/cache (where
1333     # cachename is usually pointing to) can vanish and applications
1334     # have to deal with it. So, we have to interprete the $vdir/cache
1335     # symlink and have to create the needed directories manually.
1336     if   test -d "$vdir/cache"; then
1337         :       # ok, exists already
1338     elif test -L "$vdir/cache"; then
1339         # it's a dangling symlink
1340         local link
1341         link=$($_READLINK "$vdir/cache")
1342         ( cd $vdir && $_MKDIR -p "$link" )
1343     else
1344         return 0
1345     fi
1346
1347     test -d "$vdir/cache"
1348     $_MKDIR -p "$vdir"/cache/dlimits
1349
1350     $_VDLIMIT --xid $ctx "$directory" | \
1351         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1352
1353     $_VDLIMIT --xid $ctx --remove "$directory"
1354 }
1355
1356
1357 function saveDiskLimits
1358 {
1359     local vdir=$1
1360     local dlimit
1361
1362     test -e "$vdir"/context || return 0
1363
1364     for dlimit in "$vdir/dlimits/"*; do
1365         test   -d "$dlimit"          || continue
1366         test ! -e "$dlimit/disabled" || continue
1367
1368         _saveSingleDiskLimit "$vdir" "$dlimit"
1369     done
1370 }
1371
1372 function _namespaceCleanup
1373 {
1374     local vdir="$1"
1375     local root=$($_VSERVER_INFO "$1" VDIR 1)
1376     local -a list
1377     local -a skip
1378     local i
1379     local j
1380
1381     getFileArray skip "$vdir"/namespace-cleanup-skip \
1382         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1383
1384     # these are things that have to be accessible post-cleanup
1385     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1386         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1387         local real=`getPhysicalDir "$i"`
1388         test "$i" != "$real" || real=
1389         for j in "$i" "$real"; do
1390             while test -n "$j"; do
1391                 list=( "${list[@]}" "$j" )
1392                 j="${j%/*}"
1393             done
1394         done
1395     done
1396
1397     local -a list_umount
1398     while read -r dev path opts; do
1399         test -n "$path" || continue
1400         for i in "$root" /dev /proc; do
1401             path_dir="${path}/"
1402             test "${path_dir#${i}/}" != "${path_dir}" && continue 2
1403         done
1404         for i in "${list[@]}" /; do
1405             test "$path" = "$i" && continue 2
1406         done
1407         # unmount them in reverse order so mounts further down the tree get unmounted first
1408         list_umount=( "$path" "${list_umount[@]}" )
1409     done < /proc/mounts
1410     # separate loop to avoid races while reading /proc/mounts
1411     for i in "${list_umount[@]}"; do
1412         $_UMOUNT -l -n -i "$i"
1413     done
1414 }
1415
1416 function handleDeviceMap
1417 {
1418     local op="$1"
1419     local xid="$2"
1420     local dir="$3"
1421     local flags device target
1422
1423     test -d "$dir" || return 0
1424     test -n "$xid" || return 0
1425
1426     for i in "$dir"/*; do
1427         test -d "$i" || continue
1428
1429         local -a vdevmap_opts=()
1430         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1431         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1432         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1433
1434         getFileValue flags "$i/flags" || :
1435         getFileValue device "$i/device" || :
1436         getFileValue target "$i/target" || :
1437         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1438                         ${device:+--device "$device"} ${target:+--target "$target"} )
1439
1440         $_VDEVMAP --xid "$xid" "$op" "${vdevmap_opts[@]}" || return $?
1441     done
1442 }
1443
1444 function hasCgroup
1445 {
1446     $_GREP -q "cgroup" /proc/filesystems
1447 }
1448
1449 function _generateCgroupOptions
1450 {
1451     local file
1452
1453     hasCgroup || return 0
1454
1455     findFile file "$__CONFDIR/.defaults/cgroup/mnt" ""
1456     if test -n "$file"; then
1457         read CGROUP_MNT < "$file"
1458     fi
1459     findFile file "$__CONFDIR/.defaults/cgroup/subsys" ""
1460     if test -n "$file"; then
1461         read CGROUP_SUBSYS < "$file"
1462     elif $_GREP -q '^ns[[:space:]]' /proc/cgroups; then
1463         # Hack for the ns subsystem, with which we are incompatible
1464         CGROUP_SUBSYS=$($_SED '/^#/d;/^ns[[:space:]]/d;s/[[:space:]].*//' /proc/cgroups | \
1465                         (s=""; while read x; do test -n "$s" && s="$s,"; s="$s$x"; done; echo "$s"))
1466     fi
1467     findFile file "$__CONFDIR/.defaults/cgroup/inherit" ""
1468     if test -n "$file"; then
1469         _readFileToArray CGROUP_INHERIT "$file" ""
1470     fi
1471     findFile file "$__CONFDIR/.defaults/cgroup/base" ""
1472     if test -n "$file"; then
1473         read CGROUP_BASE < "$file"
1474         test "$CGROUP_BASE" != "${CGROUP_BASE%/}" || \
1475             CGROUP_BASE="${CGROUP_BASE}/"
1476     fi
1477
1478     return 0
1479 }
1480
1481 function useCgroup
1482 {
1483     hasCgroup || return 1
1484     test -d "$CGROUP_MNT" || return 1
1485     memcg=""
1486     if $_VSERVER_INFO - FEATURE memcg; then
1487         memcg=1
1488     fi
1489     test -d "$1/cgroup" -o \
1490         \( \( -d "$__CONFDIR/.defaults/cgroup" -o -n "$memcg" \) -a \
1491            ! -e "$1/nocgroup" \)
1492 }
1493
1494 function _handleCgroup
1495 {
1496     local action="$1"
1497     local vdir="$2"
1498     local dir
1499     local name
1500     local i
1501     local j
1502     local parent
1503
1504     useCgroup "$vdir" || return 0
1505
1506     findDir dir "$vdir/cgroup" "$__CONFDIR/.defaults/cgroup" ""
1507
1508     if test -n "$dir" -a -r "$dir"/name; then
1509         read name < "$dir"/name
1510     else
1511         name="$VSERVER_NAME"
1512     fi
1513
1514     if test "$action" = "attach"; then
1515         if test -n "$CGROUP_BASE"; then
1516             local -a dirs=()
1517             i="$CGROUP_MNT/$CGROUP_BASE"
1518             while test "$CGROUP_MNT" != "$i"; do
1519                 dirs=( "$i" "${dirs[@]}" )
1520                 i="${i%/*}"
1521             done
1522             for i in "${dirs[@]}"; do
1523                 if mkdir "$i" 2>/dev/null; then
1524                     parent="${i%/*}"
1525                     for j in "${CGROUP_INHERIT[@]}"; do
1526                         test -f "$parent/$j" || continue
1527                         cat "$parent/$j" > "$i/$j"
1528                     done
1529                 fi
1530             done
1531         fi
1532         if mkdir "$CGROUP_MNT/$CGROUP_BASE$name" 2>/dev/null; then
1533             parent="$CGROUP_MNT/$CGROUP_BASE$name"
1534             parent="${parent%/*}"
1535             for i in "${CGROUP_INHERIT[@]}"; do
1536                 test -f "$parent/$i" || continue
1537                 cat "$parent/$i" > "$CGROUP_MNT/$CGROUP_BASE$name/$i"
1538             done
1539
1540             if test -n "$dir"; then
1541                 shopt -s nullglob
1542                 for i in "$dir"/*; do
1543                     f="${i##*/}"
1544                     test "$f" != mnt -a "$f" != subsys -a \
1545                         "$f" != inherit -a "$f" != name || continue
1546                     cat "$i" > "$CGROUP_MNT/$CGROUP_BASE$name/$f"
1547                 done
1548             fi
1549         fi
1550         echo "$$" > "$CGROUP_MNT/$CGROUP_BASE$name/tasks"
1551     elif test "$action" = "destroy"; then
1552         rmdir "$CGROUP_MNT/$name" 2>/dev/null || :
1553     fi
1554
1555     return 0
1556 }
1557
1558 function attachToCgroup
1559 {
1560     _handleCgroup attach "$@"
1561 }
1562
1563 function destroyCgroup
1564 {
1565     _handleCgroup destroy "$@"
1566 }