]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-centos.in
Merge pull request #1575 from brauner/2017-05-18/fix_tmp_mount_proc
[mirror_lxc.git] / templates / lxc-centos.in
1 #!/bin/bash
2
3 #
4 # template script for generating CentOS container for LXC
5
6 #
7 # lxc: linux Container library
8
9 # Authors:
10 # Daniel Lezcano <daniel.lezcano@free.fr>
11 # Ramez Hanna <rhanna@informatiq.org>
12 # Fajar A. Nugraha <github@fajar.net>
13 # Michael H. Warfield <mhw@WittsEnd.com>
14
15 # This library is free software; you can redistribute it and/or
16 # modify it under the terms of the GNU Lesser General Public
17 # License as published by the Free Software Foundation; either
18 # version 2.1 of the License, or (at your option) any later version.
19
20 # This library is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 # Lesser General Public License for more details.
24
25 # You should have received a copy of the GNU Lesser General Public
26 # License along with this library; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
29 #Configurations
30 default_path=@LXCPATH@
31
32 # Some combinations of the tuning knobs below do not exactly make sense.
33 # but that's ok.
34 #
35 # If the "root_password" is non-blank, use it, else set a default.
36 # This can be passed to the script as an environment variable and is
37 # set by a shell conditional assignment. Looks weird but it is what it is.
38 #
39 # If the root password contains a ding ($) then try to expand it.
40 # That will pick up things like ${name} and ${RANDOM}.
41 # If the root password contains more than 3 consecutive X's, pass it as
42 # a template to mktemp and take the result.
43 #
44 # If root_display_password = yes, display the temporary root password at exit.
45 # If root_store_password = yes, store it in the configuration directory
46 # If root_prompt_password = yes, invoke "passwd" to force the user to change
47 # the root password after the container is created.
48 # If root_expire_password = yes, you will be prompted to change the root
49 # password at the first login.
50 #
51 # These are conditional assignments... The can be overridden from the
52 # preexisting environment variables...
53 #
54 # Make sure this is in single quotes to defer expansion to later!
55 # :{root_password='Root-${name}-${RANDOM}'}
56 : ${root_password='Root-${name}-XXXXXX'}
57
58 # Now, it doesn't make much sense to display, store, and force change
59 # together. But, we gotta test, right???
60 : ${root_display_password='no'}
61 : ${root_store_password='yes'}
62 # Prompting for something interactive has potential for mayhem
63 # with users running under the API... Don't default to "yes"
64 : ${root_prompt_password='no'}
65
66 # Expire root password? Default to yes, but can be overridden from
67 # the environment variable
68 : ${root_expire_password='yes'}
69
70 # These are only going into comments in the resulting config...
71 lxc_network_type=veth
72 lxc_network_link=lxcbr0
73
74 # is this CentOS?
75 # Alow for weird remixes like the Raspberry Pi
76 #
77 # Use the Mitre standard CPE identifier for the release ID if possible...
78 # This may be in /etc/os-release or /etc/system-release-cpe. We
79 # should be able to use EITHER. Give preference to /etc/os-release for now.
80
81 # Detect use under userns (unsupported)
82 for arg in "$@"; do
83 [ "$arg" = "--" ] && break
84 if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
85 echo "This template can't be used for unprivileged containers." 1>&2
86 echo "You may want to try the \"download\" template instead." 1>&2
87 exit 1
88 fi
89 done
90
91 # Make sure the usual locations are in PATH
92 export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
93
94 if [ -e /etc/os-release ]
95 then
96 # This is a shell friendly configuration file. We can just source it.
97 # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME
98 . /etc/os-release
99 echo "Host CPE ID from /etc/os-release: ${CPE_NAME}"
100 fi
101
102 if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ]
103 then
104 CPE_NAME=$(head -n1 /etc/system-release-cpe)
105 CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)')
106 if [ "${CPE_URI}" != "cpe:/o" ]
107 then
108 CPE_NAME=
109 else
110 # Probably a better way to do this but sill remain posix
111 # compatible but this works, shrug...
112 # Must be nice and not introduce convenient bashisms here.
113 #
114 # According to the official registration at Mitre and NIST,
115 # this should have been something like this for CentOS:
116 # cpe:/o:centos:centos:6
117 # or this:
118 # cpe:/o:centos:centos:6.5
119 #
120 ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
121 # The "enterprise_linux" is a bone toss back to RHEL.
122 # Since CentOS and RHEL are so tightly coupled, we'll
123 # take the RHEL version if we're running on it and do the
124 # equivalent version for CentOS.
125 if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ]
126 then
127 # Instead we got this: cpe:/o:centos:linux:6
128 ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)')
129 fi
130
131 VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
132 echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
133 fi
134 fi
135
136 if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ]
137 then
138 centos_host_ver=${VERSION_ID}
139 is_centos=true
140 elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -o "${ID}" = "rhel" -a "${VERSION_ID}" != "" ]
141 then
142 # RHEL 7+ /etc/os-release ID = 'rhel', which doesn't enter this elif without the added OR statement
143 redhat_host_ver=${VERSION_ID}
144 is_redhat=true
145 elif [ -e /etc/centos-release ]
146 then
147 # Only if all other methods fail, try to parse the redhat-release file.
148 centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release )
149 if [ "$centos_host_ver" != "" ]
150 then
151 is_centos=true
152 fi
153 fi
154
155 force_mknod()
156 {
157 # delete a device node if exists, and create a new one
158 rm -f $2 && mknod -m $1 $2 $3 $4 $5
159 }
160
161 configure_centos()
162 {
163
164 # disable selinux in CentOS
165 mkdir -p $rootfs_path/selinux
166 echo 0 > $rootfs_path/selinux/enforce
167
168 # Also kill it in the /etc/selinux/config file if it's there...
169 if [ -f $rootfs_path/etc/selinux/config ]
170 then
171 sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config
172 fi
173
174 # Nice catch from Dwight Engen in the Oracle template.
175 # Wantonly plagerized here with much appreciation.
176 if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then
177 mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig
178 ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled
179 fi
180
181 # This is a known problem and documented in RedHat bugzilla as relating
182 # to a problem with auditing enabled. This prevents an error in
183 # the container "Cannot make/remove an entry for the specified session"
184 sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login
185 sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd
186
187 if [ -f ${rootfs_path}/etc/pam.d/crond ]
188 then
189 sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond
190 fi
191
192 # In addition to disabling pam_loginuid in the above config files
193 # we'll also disable it by linking it to pam_permit to catch any
194 # we missed or any that get installed after the container is built.
195 #
196 # Catch either or both 32 and 64 bit archs.
197 if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ]
198 then
199 ( cd ${rootfs_path}/lib/security/
200 mv pam_loginuid.so pam_loginuid.so.disabled
201 ln -s pam_permit.so pam_loginuid.so
202 )
203 fi
204
205 if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ]
206 then
207 ( cd ${rootfs_path}/lib64/security/
208 mv pam_loginuid.so pam_loginuid.so.disabled
209 ln -s pam_permit.so pam_loginuid.so
210 )
211 fi
212
213 # Set default localtime to the host localtime if not set...
214 if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ]
215 then
216 # if /etc/localtime is a symlink, this should preserve it.
217 cp -a /etc/localtime ${rootfs_path}/etc/localtime
218 fi
219
220 # Deal with some dain bramage in the /etc/init.d/halt script.
221 # Trim it and make it our own and link it in before the default
222 # halt script so we can intercept it. This also preventions package
223 # updates from interferring with our interferring with it.
224 #
225 # There's generally not much in the halt script that useful but what's
226 # in there from resetting the hardware clock down is generally very bad.
227 # So we just eliminate the whole bottom half of that script in making
228 # ourselves a copy. That way a major update to the init scripts won't
229 # trash what we've set up.
230 if [ -f ${rootfs_path}/etc/init.d/halt ]
231 then
232 sed -e '/hwclock/,$d' \
233 < ${rootfs_path}/etc/init.d/halt \
234 > ${rootfs_path}/etc/init.d/lxc-halt
235
236 echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt
237 chmod 755 ${rootfs_path}/etc/init.d/lxc-halt
238
239 # Link them into the rc directories...
240 (
241 cd ${rootfs_path}/etc/rc.d/rc0.d
242 ln -s ../init.d/lxc-halt S00lxc-halt
243 cd ${rootfs_path}/etc/rc.d/rc6.d
244 ln -s ../init.d/lxc-halt S00lxc-reboot
245 )
246 fi
247
248 # configure the network using the dhcp
249 cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
250 DEVICE=eth0
251 BOOTPROTO=dhcp
252 ONBOOT=yes
253 HOSTNAME=${utsname}
254 NM_CONTROLLED=no
255 TYPE=Ethernet
256 MTU=${MTU}
257 DHCP_HOSTNAME=\`hostname\`
258 EOF
259
260 # set the hostname
261 cat <<EOF > ${rootfs_path}/etc/sysconfig/network
262 NETWORKING=yes
263 HOSTNAME=${utsname}
264 EOF
265
266 # set minimal hosts
267 cat <<EOF > $rootfs_path/etc/hosts
268 127.0.0.1 localhost $name
269 EOF
270
271 # set minimal fstab
272 cat <<EOF > $rootfs_path/etc/fstab
273 /dev/root / rootfs defaults 0 0
274 EOF
275
276 # create lxc compatibility init script
277 if [ "$release" = "6" ]; then
278 cat <<EOF > $rootfs_path/etc/init/lxc-sysinit.conf
279 start on startup
280 env container
281
282 pre-start script
283 if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then
284 stop;
285 fi
286
287 rm -f /var/lock/subsys/*
288 rm -f /var/run/*.pid
289 [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab
290 mkdir -p /dev/shm
291 mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm
292
293 initctl start tty TTY=console
294 telinit 3
295 exit 0
296 end script
297 EOF
298 elif [ "$release" = "5" ]; then
299 cat <<EOF > $rootfs_path/etc/rc.d/lxc.sysinit
300 #! /bin/bash
301 rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/*
302 rm -rf {/,/var}/tmp/*
303 echo "/dev/root / rootfs defaults 0 0" > /etc/mtab
304 exit 0
305 EOF
306 chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit
307 sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab
308 # prevent mingetty from calling vhangup(2) since it fails with userns.
309 # Same issue as oracle template: prevent mingetty from calling vhangup(2)
310 # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589.
311 sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab
312 sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab
313 fi
314
315 dev_path="${rootfs_path}/dev"
316 rm -rf $dev_path
317 mkdir -p $dev_path
318 mknod -m 666 ${dev_path}/null c 1 3
319 mknod -m 666 ${dev_path}/zero c 1 5
320 mknod -m 666 ${dev_path}/random c 1 8
321 mknod -m 666 ${dev_path}/urandom c 1 9
322 mkdir -m 755 ${dev_path}/pts
323 mkdir -m 1777 ${dev_path}/shm
324 mknod -m 666 ${dev_path}/tty c 5 0
325 mknod -m 666 ${dev_path}/tty0 c 4 0
326 mknod -m 666 ${dev_path}/tty1 c 4 1
327 mknod -m 666 ${dev_path}/tty2 c 4 2
328 mknod -m 666 ${dev_path}/tty3 c 4 3
329 mknod -m 666 ${dev_path}/tty4 c 4 4
330 mknod -m 600 ${dev_path}/console c 5 1
331 mknod -m 666 ${dev_path}/full c 1 7
332 mknod -m 600 ${dev_path}/initctl p
333 mknod -m 666 ${dev_path}/ptmx c 5 2
334
335 # setup console and tty[1-4] for login. note that /dev/console and
336 # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and
337 # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks.
338 # lxc will maintain these links and bind mount ptys over /dev/lxc/*
339 # since lxc.devttydir is specified in the config.
340
341 # allow root login on console, tty[1-4], and pts/0 for libvirt
342 echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty
343 echo "lxc/console" >>${rootfs_path}/etc/securetty
344 echo "lxc/tty1" >>${rootfs_path}/etc/securetty
345 echo "lxc/tty2" >>${rootfs_path}/etc/securetty
346 echo "lxc/tty3" >>${rootfs_path}/etc/securetty
347 echo "lxc/tty4" >>${rootfs_path}/etc/securetty
348 echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty
349 echo "pts/0" >>${rootfs_path}/etc/securetty
350
351 # prevent mingetty from calling vhangup(2) since it fails with userns.
352 # Same issue as oracle template: prevent mingetty from calling vhangup(2)
353 # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589.
354 sed -i 's|mingetty|mingetty --nohangup|' $rootfs_path/etc/init/tty.conf
355
356 if [ ${root_display_password} = "yes" ]
357 then
358 echo "Setting root password to '$root_password'"
359 fi
360 if [ ${root_store_password} = "yes" ]
361 then
362 touch ${config_path}/tmp_root_pass
363 chmod 600 ${config_path}/tmp_root_pass
364 echo ${root_password} > ${config_path}/tmp_root_pass
365 echo "Storing root password in '${config_path}/tmp_root_pass'"
366 fi
367
368 echo "root:$root_password" | chroot $rootfs_path chpasswd
369
370 if [ ${root_expire_password} = "yes" ]
371 then
372 # Also set this password as expired to force the user to change it!
373 chroot $rootfs_path passwd -e root
374 fi
375
376 # This will need to be enhanced for CentOS 7 when systemd
377 # comes into play... /\/\|=mhw=|\/\/
378
379 return 0
380 }
381
382 configure_centos_init()
383 {
384 sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit
385 sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit
386 if [ "$release" = "6" ]; then
387 chroot ${rootfs_path} chkconfig udev-post off
388 fi
389 chroot ${rootfs_path} chkconfig network on
390
391 if [ -d ${rootfs_path}/etc/init ]
392 then
393 # This is to make upstart honor SIGPWR
394 cat <<EOF >${rootfs_path}/etc/init/power-status-changed.conf
395 # power-status-changed - shutdown on SIGPWR
396 #
397 start on power-status-changed
398
399 exec /sbin/shutdown -h now "SIGPWR received"
400 EOF
401 fi
402 }
403
404 download_centos()
405 {
406
407 # check the mini CentOS was not already downloaded
408 INSTALL_ROOT=$cache/partial
409 mkdir -p $INSTALL_ROOT
410 if [ $? -ne 0 ]; then
411 echo "Failed to create '$INSTALL_ROOT' directory"
412 return 1
413 fi
414
415 # download a mini CentOS into a cache
416 echo "Downloading CentOS minimal ..."
417 YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck"
418
419 if yum -h | grep -q 'releasever=RELEASEVER'; then
420 YUM="$YUM0 --releasever=$release"
421 else
422 YUM="$YUM0"
423 fi
424 PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie"
425
426 # use temporary repository definition
427 REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo
428 mkdir -p $(dirname $REPO_FILE)
429 if [ -n "$repo" ]; then
430 cat <<EOF > $REPO_FILE
431 [base]
432 name=local repository
433 baseurl="$repo"
434 EOF
435 else
436 cat <<EOF > $REPO_FILE
437 [base]
438 name=CentOS-$release - Base
439 mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os
440
441 [updates]
442 name=CentOS-$release - Updates
443 mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates
444 EOF
445 fi
446
447 # create minimal device nodes, needed for "yum install" and "yum update" process
448 mkdir -p $INSTALL_ROOT/dev
449 force_mknod 666 $INSTALL_ROOT/dev/null c 1 3
450 force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9
451
452 $YUM install $PKG_LIST
453
454 # create symlink for /var/run -> ../run
455 if [ "$release" = "7" ]; then
456 mv $INSTALL_ROOT/var/run/* $INSTALL_ROOT/run/
457 rmdir $INSTALL_ROOT/var/run
458 ln -sf ../run $INSTALL_ROOT/var/run
459 fi
460
461 if [ $? -ne 0 ]; then
462 echo "Failed to download the rootfs, aborting."
463 return 1
464 fi
465
466 # use same nameservers as hosts, needed for "yum update later"
467 cp /etc/resolv.conf $INSTALL_ROOT/etc/
468
469 # check whether rpmdb is under $HOME
470 if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then
471 echo "Fixing rpmdb location ..."
472 mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/
473 rm -rf $INSTALL_ROOT/$HOME/.rpmdb
474 chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null
475 fi
476
477 # check whether rpmdb version is correct
478 chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null
479 ret=$?
480
481 # if "rpm -q" doesn't work due to rpmdb version difference,
482 # then we need to redo the process using the newly-installed yum
483 if [ $ret -gt 0 ]; then
484 echo "Reinstalling packages ..."
485 mv $REPO_FILE $REPO_FILE.tmp
486 mkdir $INSTALL_ROOT/etc/yum.repos.disabled
487 mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/
488 mv $REPO_FILE.tmp $REPO_FILE
489 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc
490 cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/
491 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev
492 mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3
493 mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9
494 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum
495 cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/
496 chroot $INSTALL_ROOT $YUM0 install $PKG_LIST
497 if [ $? -ne 0 ]; then
498 echo "Failed to download the rootfs, aborting."
499 return 1
500 fi
501 mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp
502 rm -rf $INSTALL_ROOT
503 mv $INSTALL_ROOT.tmp $INSTALL_ROOT
504 fi
505
506 rm -f $REPO_FILE
507 rm -rf $INSTALL_ROOT/var/cache/yum/*
508
509 mv "$INSTALL_ROOT" "$cache/rootfs"
510 echo "Download complete."
511
512 return 0
513 }
514
515 copy_centos()
516 {
517
518 # make a local copy of the mini CentOS
519 echo -n "Copying rootfs to $rootfs_path ..."
520 #cp -a $cache/rootfs-$arch $rootfs_path || return 1
521 # i prefer rsync (no reason really)
522 mkdir -p $rootfs_path
523 rsync -a $cache/rootfs/ $rootfs_path/
524 echo
525 return 0
526 }
527
528 update_centos()
529 {
530 YUM="chroot $cache/rootfs yum -y --nogpgcheck"
531 $YUM update
532 if [ $? -ne 0 ]; then
533 return 1
534 fi
535 $YUM clean packages
536 }
537
538 install_centos()
539 {
540 mkdir -p /var/lock/subsys/
541 (
542 flock -x 9
543 if [ $? -ne 0 ]; then
544 echo "Cache repository is busy."
545 return 1
546 fi
547
548 echo "Checking cache download in $cache/rootfs ... "
549 if [ ! -e "$cache/rootfs" ]; then
550 download_centos
551 if [ $? -ne 0 ]; then
552 echo "Failed to download 'CentOS base'"
553 return 1
554 fi
555 else
556 echo "Cache found. Updating..."
557 update_centos
558 if [ $? -ne 0 ]; then
559 echo "Failed to update 'CentOS base', continuing with last known good cache"
560 else
561 echo "Update finished"
562 fi
563 fi
564
565 echo "Copy $cache/rootfs to $rootfs_path ... "
566 copy_centos
567 if [ $? -ne 0 ]; then
568 echo "Failed to copy rootfs"
569 return 1
570 fi
571
572 return 0
573
574 ) 9>/var/lock/subsys/lxc-centos
575
576 return $?
577 }
578
579 create_hwaddr()
580 {
581 openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/'
582 }
583
584 copy_configuration()
585 {
586 mkdir -p $config_path
587
588 grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "
589 lxc.rootfs = $rootfs_path
590 " >> $config_path/config
591
592 # The following code is to create static MAC addresses for each
593 # interface in the container. This code will work for multiple
594 # interfaces in the default config.
595 mv $config_path/config $config_path/config.def
596 while read LINE
597 do
598 # This should catch variable expansions from the default config...
599 if expr "${LINE}" : '.*\$' > /dev/null 2>&1
600 then
601 LINE=$(eval "echo \"${LINE}\"")
602 fi
603
604 # There is a tab and a space in the regex bracket below!
605 # Seems that \s doesn't work in brackets.
606 KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=')
607
608 if [[ "${KEY}" != "lxc.network.hwaddr" ]]
609 then
610 echo ${LINE} >> $config_path/config
611
612 if [[ "${KEY}" == "lxc.network.link" ]]
613 then
614 echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config
615 fi
616 fi
617 done < $config_path/config.def
618
619 rm -f $config_path/config.def
620
621 if [ -e "@LXCTEMPLATECONFIG@/centos.common.conf" ]; then
622 echo "
623 # Include common configuration
624 lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf
625 " >> $config_path/config
626 fi
627
628 # Append things which require expansion here...
629 cat <<EOF >> $config_path/config
630 lxc.arch = $arch
631 lxc.utsname = $utsname
632
633 # When using LXC with apparmor, uncomment the next line to run unconfined:
634 #lxc.aa_profile = unconfined
635
636 # example simple networking setup, uncomment to enable
637 #lxc.network.type = $lxc_network_type
638 #lxc.network.flags = up
639 #lxc.network.link = $lxc_network_link
640 #lxc.network.name = eth0
641 # Additional example for veth network type
642 # static MAC address,
643 #lxc.network.hwaddr = 00:16:3e:77:52:20
644 # persistent veth device name on host side
645 # Note: This may potentially collide with other containers of same name!
646 #lxc.network.veth.pair = v-$name-e0
647
648 EOF
649
650 if [ $? -ne 0 ]; then
651 echo "Failed to add configuration"
652 return 1
653 fi
654
655 return 0
656 }
657
658 clean()
659 {
660
661 if [ ! -e $cache ]; then
662 exit 0
663 fi
664
665 # lock, so we won't purge while someone is creating a repository
666 (
667 flock -x 9
668 if [ $? != 0 ]; then
669 echo "Cache repository is busy."
670 exit 1
671 fi
672
673 echo -n "Purging the download cache for CentOS-$release..."
674 rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
675 exit 0
676
677 ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-centos
678 }
679
680 usage()
681 {
682 cat <<EOF
683 usage:
684 $1 -n|--name=<container_name>
685 [-p|--path=<path>] [-c|--clean] [-R|--release=<CentOS_release>] [-a|--arch=<arch of the container>]
686 [-h|--help]
687 Mandatory args:
688 -n,--name container name, used to as an identifier for that container from now on
689 Optional args:
690 -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc/name.
691 -c,--clean clean the cache
692 -R,--release CentOS release for the new container. If the host is CentOS, then it will default to the host's release.
693 --fqdn fully qualified domain name (FQDN) for DNS and system naming
694 --repo repository to use (url)
695 -a,--arch Define what arch the container will be [i686,x86_64]
696 -h,--help print this help
697 EOF
698 return 0
699 }
700
701 options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@")
702 if [ $? -ne 0 ]; then
703 usage $(basename $0)
704 exit 1
705 fi
706
707 arch=$(uname -m)
708 eval set -- "$options"
709 while true
710 do
711 case "$1" in
712 -h|--help) usage $0 && exit 0;;
713 -p|--path) path=$2; shift 2;;
714 --rootfs) rootfs_path=$2; shift 2;;
715 -n|--name) name=$2; shift 2;;
716 -c|--clean) clean=1; shift 1;;
717 -R|--release) release=$2; shift 2;;
718 --repo) repo="$2"; shift 2;;
719 -a|--arch) newarch=$2; shift 2;;
720 --fqdn) utsname=$2; shift 2;;
721 --) shift 1; break ;;
722 *) break ;;
723 esac
724 done
725
726 if [ ! -z "$clean" -a -z "$path" ]; then
727 clean || exit 1
728 exit 0
729 fi
730
731 basearch=${arch}
732 # Map a few architectures to their generic CentOS repository archs.
733 # The two ARM archs are a bit of a guesstimate for the v5 and v6
734 # archs. V6 should have hardware floating point (Rasberry Pi).
735 # The "arm" arch is safer (no hardware floating point). So
736 # there may be cases where we "get it wrong" for some v6 other
737 # than RPi.
738 case "$arch" in
739 i686) basearch=i386 ;;
740 armv3l|armv4l|armv5l) basearch=arm ;;
741 armv6l|armv7l|armv8l) basearch=armhfp ;;
742 *) ;;
743 esac
744
745 # Somebody wants to specify an arch. This is very limited case.
746 # i386/i586/i686 on i386/x86_64
747 # - or -
748 # x86_64 on x86_64
749 if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ]
750 then
751 case "${newarch}" in
752 i386|i586|i686)
753 if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ]
754 then
755 # Make the arch a generic x86 32 bit...
756 arch=${newarch}
757 basearch=i386
758 else
759 basearch=bad
760 fi
761 ;;
762 *)
763 basearch=bad
764 ;;
765 esac
766
767 if [ "${basearch}" = "bad" ]
768 then
769 echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!"
770 exit 1
771 fi
772 fi
773
774 # Allow the cache base to be set by environment variable
775 cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/centos/$basearch
776
777 # Let's do something better for the initial root password.
778 # It's not perfect but it will defeat common scanning brute force
779 # attacks in the case where ssh is exposed. It will also be set to
780 # expired, forcing the user to change it at first login.
781 if [ "${root_password}" = "" ]
782 then
783 root_password=Root-${name}-${RANDOM}
784 else
785 # If it's got a ding in it, try and expand it!
786 if [ $(expr "${root_password}" : '.*$.') != 0 ]
787 then
788 root_password=$(eval echo "${root_password}")
789 fi
790
791 # If it has more than 3 consecutive X's in it, feed it
792 # through mktemp as a template.
793 if [ $(expr "${root_password}" : '.*XXXX') != 0 ]
794 then
795 root_password=$(mktemp -u ${root_password})
796 fi
797 fi
798
799 if [ -z "${utsname}" ]; then
800 utsname=${name}
801 fi
802
803 # This follows a standard "resolver" convention that an FQDN must have
804 # at least two dots or it is considered a local relative host name.
805 # If it doesn't, append the dns domain name of the host system.
806 #
807 # This changes one significant behavior when running
808 # "lxc_create -n Container_Name" without using the
809 # --fqdn option.
810 #
811 # Old behavior:
812 # utsname and hostname = Container_Name
813 # New behavior:
814 # utsname and hostname = Container_Name.Domain_Name
815
816 if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
817 if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then
818 utsname=${utsname}.$(dnsdomainname)
819 fi
820 fi
821
822 type yum >/dev/null 2>&1
823 if [ $? -ne 0 ]; then
824 echo "'yum' command is missing"
825 exit 1
826 fi
827
828 if [ -z "$path" ]; then
829 path=$default_path/$name
830 fi
831
832 if [ -z "$release" ]; then
833 if [ "$is_centos" -a "$centos_host_ver" ]; then
834 release=$centos_host_ver
835 elif [ "$is_redhat" -a "$redhat_host_ver" ]; then
836 # This is needed to clean out bullshit like 6workstation and 6server.
837 release=$(expr $redhat_host_ver : '\([0-9.]*\)')
838 else
839 echo "This is not a CentOS or Redhat host and release is missing, defaulting to 6 use -R|--release to specify release"
840 release=6
841 fi
842 fi
843
844 if [ "$(id -u)" != "0" ]; then
845 echo "This script should be run as 'root'"
846 exit 1
847 fi
848
849 if [ -z "$rootfs_path" ]; then
850 rootfs_path=$path/rootfs
851 # check for 'lxc.rootfs' passed in through default config by lxc-create
852 if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then
853 rootfs_path=$(sed -e '/^lxc.rootfs\s*=/!d' -e 's/\s*#.*//' \
854 -e 's/^lxc.rootfs\s*=\s*//' -e q $path/config)
855 fi
856 fi
857 config_path=$path
858 cache=$cache_base/$release
859
860 revert()
861 {
862 echo "Interrupted, so cleaning up"
863 lxc-destroy -n $name
864 # maybe was interrupted before copy config
865 rm -rf $path
866 echo "exiting..."
867 exit 1
868 }
869
870 trap revert SIGHUP SIGINT SIGTERM
871
872 copy_configuration
873 if [ $? -ne 0 ]; then
874 echo "failed write configuration file"
875 exit 1
876 fi
877
878 install_centos
879 if [ $? -ne 0 ]; then
880 echo "failed to install CentOS"
881 exit 1
882 fi
883
884 configure_centos
885 if [ $? -ne 0 ]; then
886 echo "failed to configure CentOS for a container"
887 exit 1
888 fi
889
890 configure_centos_init
891
892 if [ ! -z "$clean" ]; then
893 clean || exit 1
894 exit 0
895 fi
896 echo "
897 Container rootfs and config have been created.
898 Edit the config file to check/enable networking setup.
899 "
900
901 if [ ${root_display_password} = "yes" ]
902 then
903 echo "The temporary password for root is: '$root_password'
904
905 You may want to note that password down before starting the container.
906 "
907 fi
908
909 if [ ${root_store_password} = "yes" ]
910 then
911 echo "The temporary root password is stored in:
912
913 '${config_path}/tmp_root_pass'
914 "
915 fi
916
917 if [ ${root_prompt_password} = "yes" ]
918 then
919 echo "Invoking the passwd command in the container to set the root password.
920
921 chroot ${rootfs_path} passwd
922 "
923 chroot ${rootfs_path} passwd
924 else
925 if [ ${root_expire_password} = "yes" ]
926 then
927 if ( mountpoint -q -- "${rootfs_path}" )
928 then
929 echo "To reset the root password, you can do:
930
931 lxc-start -n ${name}
932 lxc-attach -n ${name} -- passwd
933 lxc-stop -n ${name}
934 "
935 else
936 echo "
937 The root password is set up as "expired" and will require it to be changed
938 at first login, which you should do as soon as possible. If you lose the
939 root password or wish to change it without starting the container, you
940 can change it from the host by running the following command (which will
941 also reset the expired flag):
942
943 chroot ${rootfs_path} passwd
944 "
945 fi
946 fi
947 fi