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