Git Repositories

Salix 14.1
[lxc-salix.git] / lxc-salix
1 #!/bin/bash
2
3 #
4 # lxc: linux Container library
5
6 # Authors:
7 # Daniel Lezcano <daniel.lezcano@free.fr>
8 # ponce <matteo.bernardini@gmail.com>
9 # Frédéric Galusik <fredg~at~salixos~dot~org>
10 # Cyrille Pontvieux <jrd@enialis.net>
11
12 # template for Salix OS by Frédéric Galusik <fredg~at~salixos~dot~org>
13 # Heavily based on the one for Slackware that build a very minimal
14 # slackware container.
15 # This template will build a ready to use Salix core system in a
16 # linux container.
17 # Homepage: http://lxc.sourceforge.net/
18
19 # template for slackware by ponce <matteo.bernardini@gmail.com>
20 # some parts are taken from the debian one (used as model)
21 # https://github.com/Ponce/lxc-slackware
22
23
24 # This library is free software; you can redistribute it and/or
25 # modify it under the terms of the GNU Lesser General Public
26 # License as published by the Free Software Foundation; either
27 # version 2.1 of the License, or (at your option) any later version.
28
29 # This library is distributed in the hope that it will be useful,
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of
31  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
32 # Lesser General Public License for more details.
33
34 # You should have received a copy of the GNU Lesser General Public
35 # License along with this library; if not, write to the Free Software
36 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37
38 default_path=/var/lib/lxc
39 default_release=14.1
40 root_password=root
41 if [ -z "$arch" ]; then
42   case "$( uname -m )" in
43       i?86) arch=i486 ;;
44       arm*) arch=arm ;;
45          *) arch=$(uname -m) ;;
46   esac
47 fi
48 MIRROR=${MIRROR:-http://download.salixos.org}
49 CORE_PKGS=${CORE_PKGS:-https://raw.github.com/gapan/iso-creation/master/lists-xfce/CORE}
50 SETTINGS_PKGS=${SETTINGS_PKGS:-https://raw.github.com/gapan/iso-creation/master/lists-xfce/SETTINGS}
51
52 # is this slackware
53 [ -f /etc/slackware-version ] && is_slack=true
54 # is this salix
55 [ -n "$is_slack" ] && [ -f /etc/slapt-get/slapt-getrc ] && grep -q salix /etc/slapt-get/slapt-getrc && is_salix=true
56
57 copy_configuration() {
58   mkdir -p $config_path
59   cat <<EOF >> $config_path/config
60 lxc.utsname = $name
61 lxc.rootfs = $rootfs_path
62 lxc.mount = $config_path/fstab
63 lxc.tty = 2
64 lxc.pts = 1024
65 lxc.cgroup.devices.deny = a
66 # /dev/null and zero
67 lxc.cgroup.devices.allow = c 1:3 rwm
68 lxc.cgroup.devices.allow = c 1:5 rwm
69 # consoles
70 lxc.cgroup.devices.allow = c 5:1 rwm
71 lxc.cgroup.devices.allow = c 5:0 rwm
72 lxc.cgroup.devices.allow = c 4:0 rwm
73 lxc.cgroup.devices.allow = c 4:1 rwm
74 # /dev/{,u}random
75 lxc.cgroup.devices.allow = c 1:9 rwm
76 lxc.cgroup.devices.allow = c 1:8 rwm
77 lxc.cgroup.devices.allow = c 136:* rwm
78 lxc.cgroup.devices.allow = c 5:2 rwm
79 # rtc
80 lxc.cgroup.devices.allow = c 254:0 rwm
81
82 # we don't trust root user in the container, better safe than sorry.
83 # comment out only if you know what you're doing.
84 lxc.cap.drop = sys_module mknod
85 lxc.cap.drop = mac_override sys_time
86 lxc.cap.drop = setfcap setpcap sys_boot
87
88 # if you want to be even more restrictive with your container's root
89 # user comment the three lines above and uncomment the following one
90 # lxc.cap.drop=sys_admin
91 EOF
92   cat <<EOF > $config_path/fstab
93 proc            $rootfs_path/proc         proc    nodev,noexec,nosuid 0 0
94 sysfs           $rootfs_path/sys          sysfs   defaults 0 0
95 tmpfs           $rootfs_path/dev/shm      tmpfs   defaults 0 0
96 lxcpts          $rootfs_path/dev/pts      devpts  defaults,newinstance 0 0
97 EOF
98   if [ $? -ne 0 ]; then
99     echo "Failed to add configuration." >&2
100     return 1
101   fi
102   return 0
103 }
104
105 download_salix() {
106   mkdir -p $cache
107   if [ -e $cache/pkgs.lst ] && [ -e $cache/location-1.lst ]; then
108     echo "Using cache"
109     return 0
110   fi
111   echo "Downloading Salix OS core packages lists..."
112   wget -nv -O $cache/CORE $CORE_PKGS && wget -nv -O $cache/SETTINGS $SETTINGS_PKGS
113   if [ $? -ne 0 ]; then
114     echo "Failed to download the packages lists, aborting." >&2
115     return 1
116   fi
117   grep -v '^kernel' $cache/CORE | sort > $cache/pkgs.lst
118   grep -v '^kernel' $cache/SETTINGS | sort >> $cache/pkgs.lst
119   rm -f $cache/CORE $cache/SETTINGS
120   if [ "$arch" = "i486" ] || [ "$arch" = "x86_64" ]; then
121     slackfolder=slackware
122   elif [ "$arch" = "arm" ]; then
123     slackfolder=slackwarearm
124   fi
125   PACKAGES_PATH_LIST="$arch/$release $arch/$slackfolder-$release/patches $arch/$slackfolder-$release/extra $arch/$slackfolder-$release"
126   i=0
127   for P in $PACKAGES_PATH_LIST; do
128     i=$(($i + 1))
129     echo "$P" > $cache/location-$i.lst
130     wget -nv -O - "$MIRROR"/$P/PACKAGES.TXT.gz | gunzip -c | grep '^PACKAGE NAME:\|PACKAGE MIRROR:\|PACKAGE LOCATION:' >> $cache/location-$i.lst
131     if [ $? -ne 0 ]; then
132       echo "Failed to download the packages location, aborting." >&2
133       return 1
134     fi
135   done
136   echo "Downloading Salix OS core packages..."
137   pkg_info=$(mktemp)
138   for p in $(cat $cache/pkgs.lst); do
139     pkg_path=''
140     pkg_name=''
141     pkg_mirror=''
142     pkg_location=''
143     for lst in $cache/location-*.lst; do
144       pkg_path=$(head -n 1 $lst | sed 's,/patches$\|/extra$,,')
145       grep -A 3 "^PACKAGE NAME: *$p-[^-]\+-[^-]\+-[^-]\+\.t[gblx]z" $lst | head -n 3 > $pkg_info
146       while read l; do
147         if [ -z "$pkg_name" ] && echo "$l" | grep -q "^PACKAGE NAME:"; then
148           pkg_name=$(echo "$l" | sed 's/^PACKAGE NAME: *//')
149         fi
150         if [ -z "$pkg_mirror" ] && echo "$l" | grep -q "^PACKAGE MIRROR:"; then
151           pkg_mirror=$(echo "$l" | sed 's/^PACKAGE MIRROR: *//')
152         fi
153         if [ -z "$pkg_location" ] && echo "$l" | grep -q "^PACKAGE LOCATION:"; then
154           pkg_location=$(echo "$l" | sed 's/^PACKAGE LOCATION: *//')
155         fi
156       done < $pkg_info
157       [ -n "$pkg_name" ] && break
158     done
159     if [ -n "$pkg_name" ]; then
160       [ -z "$pkg_mirror" ] && pkg_mirror="$MIRROR"/$pkg_path/
161       pkg_url=${pkg_mirror}${pkg_location}/${pkg_name}
162       ( cd $cache && wget -nv $pkg_url )
163     fi
164   done
165   rm $pkg_info
166   echo "Download complete."; echo
167 }
168
169 installpkg() {
170   ipkg_root=/
171   while [ -n "$1" ]; do
172     case "$1" in
173       -root) ipkg_root="$2"; shift 2 ;;
174       *) ipkg_pkg="$1"; shift; break ;;
175     esac
176   done
177   [ -f "$ipkg_pkg" ] || return 1
178   mkdir -p $ipkg_root
179   (
180     cd $ipkg_root
181     tmp=$cache/install.log
182     ipkg_shortname=$(basename "$ipkg_pkg" $(echo "$ipkg_pkg" | sed 's/.*\(\.t[glbx]z\)$/\1/'))
183     ipkg_basename=$(echo "$ipkg_shortname" | sed 's/\(.*\)-[^-]\+-[^-]\+-[^-]\+/\1/')
184     ipkg_compressed="$(du -sh "$(readlink -f $ipkg_pkg)" | cut -f 1)"
185     tar xvif $ipkg_pkg > $tmp
186     ipkg_log="$ipkg_root/var/log"
187     for PKGDBDIR in packages removed_packages removed_scripts scripts setup ; do
188       if [ ! -d $ipkg_log/$PKGDBDIR ]; then
189         rm -rf $ipkg_log/$PKGDBDIR # make sure it's not a symlink or something stupid
190         mkdir -p $ipkg_log/$PKGDBDIR
191         chmod 755 $ipkg_log/$PKGDBDIR 
192       fi
193     done
194     echo "PACKAGE NAME:     $ipkg_shortname" > $ipkg_log/packages/$ipkg_shortname
195     echo "COMPRESSED PACKAGE SIZE:     $ipkg_compressed" >> $ipkg_log/packages/$ipkg_shortname
196     echo "UNCOMPRESSED PACKAGE SIZE:     0M" >> $ipkg_log/packages/$ipkg_shortname
197     echo "PACKAGE LOCATION: $ipkg_pkg" >> $ipkg_log/packages/$ipkg_shortname
198     echo "PACKAGE DESCRIPTION:" >> $ipkg_log/packages/$ipkg_shortname
199     if [ -e install/slack-desc ]; then
200       grep "^$ipkg_basename:" install/slack-desc >> $ipkg_log/packages/$ipkg_shortname
201     fi
202     echo "FILE LIST:" >> $ipkg_log/packages/$ipkg_shortname
203     if [ "$(cat $tmp | grep '^\./' | wc -l | tr -d ' ')" = "1" ]; then
204       cat $tmp >> $ipkg_log/packages/$ipkg_shortname
205     else
206       echo './' >> $ipkg_log/packages/$ipkg_shortname
207       cat $tmp >> $ipkg_log/packages/$ipkg_shortname
208     fi
209     rm -f $tmp
210     [ -x /sbin/ldconfig ] && /sbin/ldconfig
211     if [ -f install/doinst.sh ]; then
212       sh install/doinst.sh -install || true
213       cp install/doinst.sh $ipkg_log/scripts/$ipkg_shortname
214       chmod 755 $ipkg_log/scripts/$ipkg_shortname
215     fi
216     [ -e install ] && rm -rf install
217   )
218 }
219
220 install_salix() {
221   mkdir -p /var/lock/subsys/
222   (
223     flock -n -x 200
224     if [ $? -ne 0 ]; then
225       echo "Cache repository is busy." >&2
226       return 1
227     fi
228     download_salix
229     if [ $? -ne 0 ]; then
230       echo "Download failed." >&2
231       return 1
232     fi
233     echo "Installing packages from $cache into $rootfs_path..."
234     if [ -e "$rootfs_path"/slackware-version ]; then
235       echo "cleaning up existing $rootfs_path..."
236       rm -rf "$rootfs_path"
237     fi
238     mkdir -p $rootfs_path
239     # installing pkgs
240     for package in $cache/*.t?z ; do
241       echo "* Installing $(basename $package | sed 's/\(.*\)\.t[gblx]z$/\1/')..."
242       installpkg -root $rootfs_path $package
243     done
244     return 0
245   ) 200>/var/lock/subsys/lxc
246   return $?
247 }
248
249 configure_salix() {
250   echo "Configuring..." ; echo
251   # the next part contains excerpts taken from SeTconfig (written by
252   # Patrick Volkerding) from the slackware setup disk.
253   # but before pasting them just set a variable to use them as they are
254   T_PX=$rootfs_path
255   ( cd $T_PX ; chmod 755 ./ )
256   ( cd $T_PX ; chmod 755 ./var )
257   if [ -d $T_PX/usr/src/linux ]; then
258     chmod 755 $T_PX/usr/src/linux
259   fi
260   if [ ! -d $T_PX/proc ]; then
261     mkdir $T_PX/proc
262     chown root.root $T_PX/proc
263   fi
264   if [ ! -d $T_PX/sys ]; then
265     mkdir $T_PX/sys
266     chown root.root $T_PX/sys
267   fi
268   chmod 1777 $T_PX/tmp
269   if [ ! -d $T_PX/var/spool/mail ]; then
270     mkdir -p $T_PX/var/spool/mail
271     chmod 755 $T_PX/var/spool
272     chown root.mail $T_PX/var/spool/mail
273     chmod 1777 $T_PX/var/spool/mail
274   fi
275   echo "#!/bin/sh" > $T_PX/etc/rc.d/rc.keymap
276   echo "# Load the keyboard map.  More maps are in /usr/share/kbd/keymaps." >> $T_PX/etc/rc.d/rc.keymap
277   echo "if [ -x /usr/bin/loadkeys ]; then" >> $T_PX/etc/rc.d/rc.keymap
278   echo "  /usr/bin/loadkeys us" >> $T_PX/etc/rc.d/rc.keymap
279   echo "fi" >> $T_PX/etc/rc.d/rc.keymap
280   chmod 755 $T_PX/etc/rc.d/rc.keymap
281   # network configuration is left to the user
282   # editing /etc/rc.d/rc.inet1.conf and /etc/resolv.conf of the container
283   # just set the hostname
284   echo "${UTSNAME}" > $rootfs_path/etc/HOSTNAME
285   cp $rootfs_path/etc/HOSTNAME $rootfs_path/etc/hostname
286   # make needed devices, from Chris Willing's MAKEDEV.sh
287   # http://www.vislab.uq.edu.au/howto/lxc/MAKEDEV.sh
288   DEV=$rootfs_path/dev
289   # cleanup & creat the few devices needed by the container
290   rm -rf ${DEV} 
291   mkdir ${DEV}
292   mkdir -m 755 ${DEV}/pts
293   mkdir -m 1777 ${DEV}/shm
294   mknod -m 666 ${DEV}/null c 1 3
295   mknod -m 666 ${DEV}/zero c 1 5
296   mknod -m 666 ${DEV}/random c 1 8
297   mknod -m 666 ${DEV}/urandom c 1 9
298   mknod -m 666 ${DEV}/tty c 5 0
299   mknod -m 600 ${DEV}/console c 5 1
300   mknod -m 666 ${DEV}/tty0 c 4 0
301   mknod -m 666 ${DEV}/tty1 c 4 1
302   mknod -m 666 ${DEV}/tty2 c 4 2
303   mknod -m 666 ${DEV}/tty3 c 4 3
304   mknod -m 666 ${DEV}/tty4 c 4 4
305   mknod -m 666 ${DEV}/tty5 c 4 5
306   mknod -m 666 ${DEV}/full c 1 7
307   mknod -m 600 ${DEV}/initctl p
308   mknod -m 660 ${DEV}/loop0 b 7 0
309   mknod -m 660 ${DEV}/loop1 b 7 1
310   ln -s pts/ptmx ${DEV}/ptmx
311   cp $config_path/fstab $rootfs_path/etc/fstab
312   # disable pointless services in a container
313   chmod -x $rootfs_path/etc/rc.d/rc.acpid
314   chmod -x $rootfs_path/etc/rc.d/rc.inet1 # normally not needed with bridge
315   chmod -x $rootfs_path/etc/rc.d/rc.keymap
316   chmod -x $rootfs_path/etc/rc.d/rc.ntpd
317   chmod -x $rootfs_path/etc/rc.d/rc.pcmcia
318   chmod -x $rootfs_path/etc/rc.d/rc.sshd
319   chmod -x $rootfs_path/etc/rc.d/rc.udev
320   # simplify rc.6 and rc.S, http://www.vislab.uq.edu.au/howto/lxc/create_container.html
321   # and some other small fixes for a clean boot
322   touch $rootfs_path/.container
323   cat << 'EOF' > $rootfs_path/.rc.d-lxcify
324 #!/bin/sh
325 [ -e etc ] || cd /
326 [ -d etc/rc.d ] || exit 1
327 if ! grep -q /\.container etc/rc.d/rc.S; then
328   sed -i '
329 /# Try to mount \/proc:/i \
330 if [ ! -e /.container ]; then
331 ; /# Done checking root filesystem/a \
332 fi # end container check
333 ; /# Remounting the \/ partition will initialize the new \/etc\/mtab:/i \
334 if [ ! -e /.container ]; then
335 ; /\/sbin\/mount -w -o remount \//a \
336 fi # end container check
337 ; /# Fix \/etc\/mtab to list sys and proc/i \
338 if [ ! -e /.container ]; then
339 ; /# Add entry for \/ to \/etc\/mtab:/i \
340 if [ ! -e /.container ]; then
341 ; /# Clean up some temporary files:/i \
342 fi # end container check
343 ; /# Run serial port setup script:/i \
344 if [ ! -e /.container ]; then
345 ; /# Carry an entropy pool/i \
346 fi # end container check
347     ' etc/rc.d/rc.S
348 fi
349 if ! grep -q /\.container etc/rc.d/rc.6; then
350   sed -i '
351 /# Save the system time to the hardware clock/i \
352 if [ ! -e /.container ]; then
353 ; /# Run any local shutdown scripts:/ i\
354 fi # end container check
355 ; /# Turn off swap:/i \
356 if [ ! -e /.container ]; then
357 ; /# This never hurts:/i \
358 fi # end container check
359 ; /# Close any volumes opened by cryptsetup:/i \
360 if [ ! -e /.container ]; then
361 ; $i \
362 else \
363   # confirm successful shutdown \
364   echo; echo -e "${BOLDYELLOW}Container stopped.${COLOR_RESET}"; echo \
365 fi # end container check
366     ' etc/rc.d/rc.6
367 fi
368 if ! grep -q /\.container etc/rc.d/rc.M; then
369   sed -i '
370 /# Screen blanks/i \
371 if [ ! -e /.container ]; then
372 ; /# Set the permissions on \/var\/log\/dmesg/i \
373 fi # end container check
374     ' etc/rc.d/rc.M
375 fi
376 if ! grep -q /\.container etc/rc.d/rc.inet1; then
377   sed -i '
378 /# If the interface isn.t in the kernel yet/i \
379 if [ ! -e /.container ]; then
380 ; /then # interface exists/i \
381 fi # end container check
382     ' etc/rc.d/rc.inet1
383 fi
384 EOF
385   chmod +x $rootfs_path/.rc.d-lxcify
386   (cd $rootfs_path ./.rc.d-lxcify)
387   echo "lxc container" >>  $rootfs_path/etc/motd
388   # reduce the number of local consoles: two should be enough
389   sed -i '/^c3\|^c4\|^c5\|^c6/s/^/# /' $rootfs_path/etc/inittab
390   # create another way to command init in the container, using a socket /var/run/telinit.sock
391   cat <<'EOF' > $rootfs_path/usr/sbin/telinitlxc
392 #!/bin/sh
393 rm -f /var/run/telinit.sock
394 mkfifo /var/run/telinit.sock
395 exec 10< /var/run/telinit.sock
396 while true; do
397   sleep 3
398   read l <&10
399   if [ -n "$l" ]; then
400     echo telinit $l > /var/log/telinit-lxc.log
401     rm /var/run/telinit.sock
402     telinit $l
403     break
404   fi
405 done
406 EOF
407   chmod +x $rootfs_path/usr/sbin/telinitlxc
408   echo 'lxc:12345:respawn:/usr/sbin/telinitlxc' >> $rootfs_path/etc/inittab
409   # set the default runlevel to 3
410   sed -i 's/id:4:initdefault:/id:3:initdefault:/' $rootfs_path/etc/inittab 
411   # fix some broken links
412   if [ $arch == "x86_64" ]; then
413     LIBDIRSUFFIX="64"
414   else
415     LIBDIRSUFFIX=""
416   fi
417   ( 
418     cd $rootfs_path/usr/lib${LIBDIRSUFFIX}
419     [ -e libcrypto.so.0 ] || ln -s libcrypto.so libcryto.so.0
420     [ -e libssl.so.0 ] || ln -s libssl.so libssl.so.0
421   )
422   # add a message to rc.local that confirms successful container startup
423   echo "if [ -e /.container ]; then echo; echo \"* container $name started. *\"; echo; fi" >> $rootfs_path/etc/rc.d/rc.local
424   # set a default combination for the luggage
425   echo "root:$root_password" | chroot $rootfs_path chpasswd
426   echo "Root password is '$root_password', please change it!"
427   return 0
428 }
429
430 clean() {
431   [ -e $cache ] || exit 0
432   # lock, so we won't purge while someone is creating a repository
433   (
434     flock -n -x 200
435     if [ $? != 0 ]; then
436       echo "Cache repository is busy." >&2
437       exit 1
438     fi
439     echo -n "Purging the download cache..."
440     rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
441     exit 0
442   ) 200>/var/lock/subsys/lxc
443 }
444
445 usage() {
446   cat <<EOF
447 usage:
448     $1 -n|--name=<container_name>
449         [-p|--path=<path>] [-c|--clean] [-R|--release=<Salix_release>]
450         [-h|--help]
451 Mandatory args:
452   -n,--name         container name, used to as an identifier for that container from now on
453 Optional args:
454   -p,--path         path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in that case
455   -c,--clean        clean the cache
456   -R,--release      Salix release for the new container. if the host is Salix or Slackware, then it will default to the host's release, else version $default_release
457   -A,--arch         Salix architecture for the new container. By default it's $arch. Supported architectures are: i486, x86_64 and arm
458   -h,--help         print this help
459 EOF
460   exit 1
461 }
462
463 set -e
464
465 options=$(getopt -o hp:n:cR:A: -l help,path:,name:,clean,release:,arch: -- "$@")
466 if [ $? -ne 0 ]; then
467   usage $(basename "$0")
468   exit 1
469 fi
470 eval set -- "$options"
471
472 while true; do
473   case "$1" in
474     -h|--help)      usage $0;;
475     -p|--path)      path=$2; shift 2;;
476     -n|--name)      name=$2; shift 2;;
477     -c|--clean)     clean=$2; shift 2;;
478     -R|--release)   release=$2; shift 2;;
479     -A|--arch)      arch=$2; shift 2;;
480     --)             shift 1; break ;;
481     *)              break ;;
482   esac
483 done
484
485 if [ ! -z "$clean" -a -z "$path" ]; then
486   clean || exit 1
487   exit 0
488 fi
489
490 if [ -z "$path" ]; then
491   echo "'path' parameter is required." >&2
492   exit 1
493 fi
494
495 if [ -z "$release" ]; then
496   if [ "$is_slack" ]; then
497     release=$(sed 's/.* \([^.]\+\)\.\([^.]\+\)\(\..*\)?/\1.\2/' /etc/slackware-version)
498   else
499     echo "This is not a slackware or salix host and release is missing, defaulting to $default_release. use -R|--release to specify release"
500     release=$default_release
501   fi
502 fi
503
504 if [ "$(id -u)" != "0" ]; then
505   echo "This script should be run as 'root'." >&2
506   exit 1
507 fi
508
509 if [ -z "$name" ]; then
510   # no name given? set a default one
511   name=salix-$release-$arch
512 fi
513
514 rootfs_path=$path/rootfs
515 config_path=$default_path/$name
516 cache_base=${cache_base:-/var/cache/lxc/salix/$arch}
517 cache=$cache_base/$release
518
519 revert() {
520   echo "Interrupted, so cleaning up" >&2
521   #lxc-destroy -n $name
522   # maybe was interrupted before copy config
523   #rm -rf $path/$name
524   #rm -rf $default_path/$name
525   echo "exiting..." >&2
526   exit 1
527 }
528 trap revert SIGHUP SIGINT SIGTERM
529
530 copy_configuration
531 if [ $? -ne 0 ]; then
532   echo "failed write configuration file" >&2
533   exit 1
534 fi
535
536 install_salix
537 if [ $? -ne 0 ]; then
538   echo "Failed to install Salix" >&2
539   exit 1
540 fi
541
542 configure_salix
543 if [ $? -ne 0 ]; then
544   echo "Failed to configure Salix for a container" >&2
545   exit 1
546 fi
547
548 if [ -n "$clean" ]; then
549   clean || exit 1
550   exit 0
551 fi
552
553 echo "Salix $release ($arch) container installed"
554 echo "Container rootfs and config created"
555 echo "Default root password is $root_password"