]>
Commit | Line | Data |
---|---|---|
bad69158 | 1 | #!/bin/bash |
7afc269d | 2 | |
fb7460fe DL |
3 | # |
4 | # lxc: linux Container library | |
06388011 | 5 | |
fb7460fe DL |
6 | # Authors: |
7 | # Daniel Lezcano <daniel.lezcano@free.fr> | |
8 | ||
9 | # This library is free software; you can redistribute it and/or | |
10 | # modify it under the terms of the GNU Lesser General Public | |
11 | # License as published by the Free Software Foundation; either | |
12 | # version 2.1 of the License, or (at your option) any later version. | |
13 | ||
14 | # This library is distributed in the hope that it will be useful, | |
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14d9c0f0 | 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
fb7460fe | 17 | # Lesser General Public License for more details. |
06388011 | 18 | |
fb7460fe DL |
19 | # You should have received a copy of the GNU Lesser General Public |
20 | # License along with this library; if not, write to the Free Software | |
250b1eec | 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
06388011 | 22 | |
49a630b8 | 23 | MIRROR=${MIRROR:-http://cdn.debian.net/debian} |
00fe5e1d SG |
24 | LOCALSTATEDIR="@LOCALSTATEDIR@" |
25 | LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" | |
2a7c16dc | 26 | |
fb7460fe DL |
27 | configure_debian() |
28 | { | |
29 | rootfs=$1 | |
30 | hostname=$2 | |
31 | ||
4e0eb765 DB |
32 | # squeeze only has /dev/tty and /dev/tty0 by default, |
33 | # therefore creating missing device nodes for tty1-4. | |
34 | for tty in $(seq 1 4); do | |
14d9c0f0 SG |
35 | if [ ! -e $rootfs/dev/tty$tty ]; then |
36 | mknod $rootfs/dev/tty$tty c 4 $tty | |
37 | fi | |
4e0eb765 DB |
38 | done |
39 | ||
fb7460fe DL |
40 | # configure the inittab |
41 | cat <<EOF > $rootfs/etc/inittab | |
06388011 | 42 | id:3:initdefault: |
43 | si::sysinit:/etc/init.d/rcS | |
44 | l0:0:wait:/etc/init.d/rc 0 | |
45 | l1:1:wait:/etc/init.d/rc 1 | |
46 | l2:2:wait:/etc/init.d/rc 2 | |
47 | l3:3:wait:/etc/init.d/rc 3 | |
48 | l4:4:wait:/etc/init.d/rc 4 | |
49 | l5:5:wait:/etc/init.d/rc 5 | |
50 | l6:6:wait:/etc/init.d/rc 6 | |
51 | # Normally not reached, but fallthrough in case of emergency. | |
52 | z6:6:respawn:/sbin/sulogin | |
53 | 1:2345:respawn:/sbin/getty 38400 console | |
b0a33c1e | 54 | c1:12345:respawn:/sbin/getty 38400 tty1 linux |
55 | c2:12345:respawn:/sbin/getty 38400 tty2 linux | |
56 | c3:12345:respawn:/sbin/getty 38400 tty3 linux | |
57 | c4:12345:respawn:/sbin/getty 38400 tty4 linux | |
f79d43bb | 58 | p6::ctrlaltdel:/sbin/init 6 |
6bf8daf9 | 59 | p0::powerfail:/sbin/init 0 |
06388011 | 60 | EOF |
06388011 | 61 | |
fb7460fe DL |
62 | # disable selinux in debian |
63 | mkdir -p $rootfs/selinux | |
64 | echo 0 > $rootfs/selinux/enforce | |
06388011 | 65 | |
fb7460fe DL |
66 | # configure the network using the dhcp |
67 | cat <<EOF > $rootfs/etc/network/interfaces | |
68 | auto lo | |
69 | iface lo inet loopback | |
06388011 | 70 | |
fb7460fe DL |
71 | auto eth0 |
72 | iface eth0 inet dhcp | |
06388011 | 73 | EOF |
1846e71a | 74 | |
fb7460fe DL |
75 | # set the hostname |
76 | cat <<EOF > $rootfs/etc/hostname | |
77 | $hostname | |
78 | EOF | |
06388011 | 79 | |
fb7460fe | 80 | # reconfigure some services |
f1fa1a08 | 81 | if [ -z "$LANG" ]; then |
14d9c0f0 SG |
82 | chroot $rootfs locale-gen en_US.UTF-8 UTF-8 |
83 | chroot $rootfs update-locale LANG=en_US.UTF-8 | |
f1fa1a08 | 84 | else |
90ccc878 | 85 | encoding=$(echo $LANG | cut -d. -f2) |
09da66e6 | 86 | chroot $rootfs sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \ |
90ccc878 LV |
87 | -i /etc/locale.gen 2>/dev/null |
88 | chroot $rootfs locale-gen $LANG $encoding | |
14d9c0f0 | 89 | chroot $rootfs update-locale LANG=$LANG |
f1fa1a08 | 90 | fi |
06388011 | 91 | |
fb7460fe | 92 | # remove pointless services in a container |
7593bdfb | 93 | chroot $rootfs /usr/sbin/update-rc.d -f checkroot.sh remove |
fb7460fe DL |
94 | chroot $rootfs /usr/sbin/update-rc.d -f umountfs remove |
95 | chroot $rootfs /usr/sbin/update-rc.d -f hwclock.sh remove | |
96 | chroot $rootfs /usr/sbin/update-rc.d -f hwclockfirst.sh remove | |
19d618b1 | 97 | |
ce68d5b4 SG |
98 | # generate new SSH keys |
99 | if [ -x $rootfs/var/lib/dpkg/info/openssh-server.postinst ]; then | |
100 | cat > $rootfs/usr/sbin/policy-rc.d << EOF | |
101 | #!/bin/sh | |
102 | exit 101 | |
103 | EOF | |
104 | chmod +x $rootfs/usr/sbin/policy-rc.d | |
105 | ||
106 | if [ -f $rootfs/etc/init/ssh.conf ]; then | |
107 | mv $rootfs/etc/init/ssh.conf $rootfs/etc/init/ssh.conf.disabled | |
108 | fi | |
109 | ||
110 | rm -f $rootfs/etc/ssh/ssh_host_*key* | |
111 | ||
112 | DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure | |
113 | sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub | |
114 | ||
115 | if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then | |
116 | mv $rootfs/etc/init/ssh.conf.disabled $rootfs/etc/init/ssh.conf | |
117 | fi | |
118 | ||
119 | rm -f $rootfs/usr/sbin/policy-rc.d | |
120 | fi | |
121 | ||
f7365a24 | 122 | # set initial timezone as on host |
123 | if [ -f /etc/timezone ]; then | |
0a3673e8 | 124 | cat /etc/timezone > $rootfs/etc/timezone |
f7365a24 | 125 | chroot $rootfs dpkg-reconfigure -f noninteractive tzdata |
126 | elif [ -f /etc/sysconfig/clock ]; then | |
17abf278 | 127 | . /etc/sysconfig/clock |
f7365a24 | 128 | echo $ZONE > $rootfs/etc/timezone |
129 | chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
130 | else | |
131 | echo "Timezone in container is not configured. Adjust it manually." | |
132 | fi | |
133 | ||
19d618b1 DL |
134 | echo "root:root" | chroot $rootfs chpasswd |
135 | echo "Root password is 'root', please change !" | |
136 | ||
137 | return 0 | |
06388011 | 138 | } |
139 | ||
f1ccde27 SH |
140 | cleanup() |
141 | { | |
b269b8ad LV |
142 | rm -rf $cache/partial-$release-$arch |
143 | rm -rf $cache/rootfs-$release-$arch | |
f1ccde27 SH |
144 | } |
145 | ||
fb7460fe DL |
146 | download_debian() |
147 | { | |
148 | packages=\ | |
149 | ifupdown,\ | |
150 | locales,\ | |
151 | libui-dialog-perl,\ | |
152 | dialog,\ | |
06a1e1db | 153 | isc-dhcp-client,\ |
fb7460fe DL |
154 | netbase,\ |
155 | net-tools,\ | |
156 | iproute,\ | |
157 | openssh-server | |
158 | ||
159 | cache=$1 | |
160 | arch=$2 | |
b269b8ad | 161 | release=$3 |
fb7460fe | 162 | |
f1ccde27 | 163 | trap cleanup EXIT SIGHUP SIGINT SIGTERM |
fb7460fe | 164 | # check the mini debian was not already downloaded |
b269b8ad | 165 | mkdir -p "$cache/partial-$release-$arch" |
fb7460fe | 166 | if [ $? -ne 0 ]; then |
b269b8ad | 167 | echo "Failed to create '$cache/partial-$release-$arch' directory" |
14d9c0f0 | 168 | return 1 |
81f6a40a RT |
169 | fi |
170 | ||
fb7460fe DL |
171 | # download a mini debian into a cache |
172 | echo "Downloading debian minimal ..." | |
173 | debootstrap --verbose --variant=minbase --arch=$arch \ | |
14d9c0f0 | 174 | --include=$packages \ |
b269b8ad | 175 | "$release" "$cache/partial-$release-$arch" $MIRROR |
fb7460fe | 176 | if [ $? -ne 0 ]; then |
14d9c0f0 SG |
177 | echo "Failed to download the rootfs, aborting." |
178 | return 1 | |
c952d1b9 | 179 | fi |
cd830f33 | 180 | |
b269b8ad | 181 | mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" |
fb7460fe | 182 | echo "Download complete." |
f1ccde27 SH |
183 | trap EXIT |
184 | trap SIGINT | |
185 | trap SIGTERM | |
186 | trap SIGHUP | |
cd830f33 | 187 | |
fb7460fe | 188 | return 0 |
c952d1b9 | 189 | } |
bad69158 | 190 | |
fb7460fe | 191 | copy_debian() |
c952d1b9 | 192 | { |
fb7460fe DL |
193 | cache=$1 |
194 | arch=$2 | |
195 | rootfs=$3 | |
b269b8ad | 196 | release=$4 |
bad69158 | 197 | |
fb7460fe DL |
198 | # make a local copy of the minidebian |
199 | echo -n "Copying rootfs to $rootfs..." | |
6d8ac56b | 200 | mkdir -p $rootfs |
b269b8ad | 201 | rsync -Ha "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 |
fb7460fe | 202 | return 0 |
c952d1b9 | 203 | } |
204 | ||
fb7460fe DL |
205 | install_debian() |
206 | { | |
00fe5e1d | 207 | cache="$LOCALSTATEDIR/cache/lxc/debian" |
fb7460fe | 208 | rootfs=$1 |
b269b8ad | 209 | release=$2 |
1927a6be | 210 | arch=$3 |
00fe5e1d | 211 | mkdir -p $LOCALSTATEDIR/lock/subsys/ |
fb7460fe | 212 | ( |
17abf278 | 213 | flock -x 9 |
14d9c0f0 SG |
214 | if [ $? -ne 0 ]; then |
215 | echo "Cache repository is busy." | |
216 | return 1 | |
217 | fi | |
7afc269d | 218 | |
b269b8ad LV |
219 | echo "Checking cache download in $cache/rootfs-$release-$arch ... " |
220 | if [ ! -e "$cache/rootfs-$release-$arch" ]; then | |
221 | download_debian $cache $arch $release | |
14d9c0f0 SG |
222 | if [ $? -ne 0 ]; then |
223 | echo "Failed to download 'debian base'" | |
224 | return 1 | |
225 | fi | |
226 | fi | |
c952d1b9 | 227 | |
b269b8ad | 228 | copy_debian $cache $arch $rootfs $release |
14d9c0f0 SG |
229 | if [ $? -ne 0 ]; then |
230 | echo "Failed to copy rootfs" | |
231 | return 1 | |
232 | fi | |
c952d1b9 | 233 | |
14d9c0f0 | 234 | return 0 |
c952d1b9 | 235 | |
00fe5e1d | 236 | ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian |
85cbaa06 | 237 | |
fb7460fe | 238 | return $? |
bad69158 | 239 | } |
240 | ||
fb7460fe DL |
241 | copy_configuration() |
242 | { | |
243 | path=$1 | |
244 | rootfs=$2 | |
16501521 | 245 | hostname=$3 |
1927a6be | 246 | arch=$4 |
bad69158 | 247 | |
00fe5e1d SG |
248 | # Generate the configuration file |
249 | ## Create the fstab (empty by default) | |
250 | touch $path/fstab | |
251 | ||
aea1cd3c SG |
252 | # if there is exactly one veth network entry, make sure it has an |
253 | # associated hwaddr. | |
254 | nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` | |
255 | if [ $nics -eq 1 ]; then | |
256 | grep -q "^lxc.network.hwaddr" $path/config || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config | |
257 | fi | |
258 | ||
00fe5e1d SG |
259 | ## Add all the includes |
260 | echo "" >> $path/config | |
261 | echo "# Common configuration" >> $path/config | |
262 | if [ -e "${LXC_TEMPLATE_CONFIG}/debian.common.conf" ]; then | |
263 | echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.common.conf" >> $path/config | |
264 | fi | |
265 | if [ -e "${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" ]; then | |
266 | echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" >> $path/config | |
267 | fi | |
268 | ||
269 | ## Add the container-specific config | |
270 | echo "" >> $path/config | |
271 | echo "# Container specific configuration" >> $path/config | |
1881820a | 272 | grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config |
00fe5e1d | 273 | |
fb7460fe | 274 | cat <<EOF >> $path/config |
00fe5e1d | 275 | lxc.mount = $path/fstab |
16501521 | 276 | lxc.utsname = $hostname |
00fe5e1d | 277 | lxc.arch = $arch |
fb7460fe | 278 | EOF |
bad69158 | 279 | |
fb7460fe | 280 | if [ $? -ne 0 ]; then |
14d9c0f0 SG |
281 | echo "Failed to add configuration" |
282 | return 1 | |
bad69158 | 283 | fi |
284 | ||
285 | return 0 | |
286 | } | |
287 | ||
fb7460fe DL |
288 | clean() |
289 | { | |
00fe5e1d | 290 | cache="$LOCALSTATEDIR/cache/lxc/debian" |
bad69158 | 291 | |
fb7460fe | 292 | if [ ! -e $cache ]; then |
14d9c0f0 | 293 | exit 0 |
bad69158 | 294 | fi |
295 | ||
296 | # lock, so we won't purge while someone is creating a repository | |
297 | ( | |
17abf278 | 298 | flock -x 9 |
14d9c0f0 SG |
299 | if [ $? != 0 ]; then |
300 | echo "Cache repository is busy." | |
301 | exit 1 | |
302 | fi | |
bad69158 | 303 | |
14d9c0f0 SG |
304 | echo -n "Purging the download cache..." |
305 | rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 | |
306 | exit 0 | |
bad69158 | 307 | |
00fe5e1d | 308 | ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian |
bad69158 | 309 | } |
310 | ||
fb7460fe DL |
311 | usage() |
312 | { | |
313 | cat <<EOF | |
1927a6be LV |
314 | $1 -h|--help -p|--path=<path> [-a|--arch] [-r|--release=<release>] [-c|--clean] |
315 | release: the debian release (e.g. wheezy): defaults to current stable | |
316 | arch: the container architecture (e.g. amd64): defaults to host arch | |
fb7460fe DL |
317 | EOF |
318 | return 0 | |
319 | } | |
320 | ||
1927a6be | 321 | options=$(getopt -o hp:n:a:r:c -l help,rootfs:,path:,name:,arch:,release:,clean -- "$@") |
fb7460fe DL |
322 | if [ $? -ne 0 ]; then |
323 | usage $(basename $0) | |
14d9c0f0 | 324 | exit 1 |
fb7460fe DL |
325 | fi |
326 | eval set -- "$options" | |
327 | ||
1927a6be LV |
328 | if which dpkg >/dev/null 2>&1 ; then |
329 | arch=$(dpkg --print-architecture) | |
330 | else | |
331 | arch=$(uname -m) | |
332 | if [ "$arch" = "i686" ]; then | |
333 | arch="i386" | |
334 | elif [ "$arch" = "x86_64" ]; then | |
335 | arch="amd64" | |
336 | elif [ "$arch" = "armv7l" ]; then | |
337 | arch="armhf" | |
338 | fi | |
339 | fi | |
340 | hostarch=$arch | |
341 | ||
fb7460fe DL |
342 | while true |
343 | do | |
344 | case "$1" in | |
b269b8ad | 345 | -h|--help) usage $0 && exit 1;; |
fb7460fe | 346 | -p|--path) path=$2; shift 2;; |
1897e3bc | 347 | --rootfs) rootfs=$2; shift 2;; |
1927a6be | 348 | -a|--arch) arch=$2; shift 2;; |
b269b8ad | 349 | -r|--release) release=$2; shift 2;; |
14d9c0f0 SG |
350 | -n|--name) name=$2; shift 2;; |
351 | -c|--clean) clean=$2; shift 2;; | |
fb7460fe DL |
352 | --) shift 1; break ;; |
353 | *) break ;; | |
354 | esac | |
355 | done | |
356 | ||
357 | if [ ! -z "$clean" -a -z "$path" ]; then | |
358 | clean || exit 1 | |
359 | exit 0 | |
360 | fi | |
361 | ||
17abf278 | 362 | if [ "$arch" = "i686" ]; then |
1927a6be LV |
363 | arch=i386 |
364 | fi | |
365 | ||
17abf278 | 366 | if [ "$arch" = "x86_64" ]; then |
1927a6be LV |
367 | arch=amd64 |
368 | fi | |
369 | ||
370 | if [ $hostarch = "i386" -a $arch = "amd64" ]; then | |
371 | echo "can't create $arch container on $hostarch" | |
372 | exit 1 | |
373 | fi | |
374 | ||
375 | if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ | |
376 | [ $arch != "armhf" -a $arch != "armel" ]; then | |
377 | echo "can't create $arch container on $hostarch" | |
378 | exit 1 | |
379 | fi | |
380 | ||
381 | if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then | |
382 | echo "can't create $arch container on $hostarch" | |
383 | exit 1 | |
384 | fi | |
385 | ||
fb7460fe DL |
386 | type debootstrap |
387 | if [ $? -ne 0 ]; then | |
388 | echo "'debootstrap' command is missing" | |
389 | exit 1 | |
390 | fi | |
391 | ||
392 | if [ -z "$path" ]; then | |
393 | echo "'path' parameter is required" | |
394 | exit 1 | |
395 | fi | |
396 | ||
397 | if [ "$(id -u)" != "0" ]; then | |
398 | echo "This script should be run as 'root'" | |
399 | exit 1 | |
c01d62f2 | 400 | fi |
bad69158 | 401 | |
b269b8ad LV |
402 | current_release=`wget ${MIRROR}/dists/stable/Release -O - 2>/dev/null |\ |
403 | head |awk '/^Codename: (.*)$/ { print $2; }'` | |
404 | release=${release:-${current_release}} | |
405 | valid_releases=('squeeze' 'wheezy' 'jessie' 'sid') | |
406 | if [[ ! "${valid_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]] | |
407 | then | |
408 | echo "Invalid release ${release}, valid ones are: ${valid_releases[*]}" | |
409 | exit 1 | |
410 | fi | |
411 | ||
1881820a SH |
412 | # detect rootfs |
413 | config="$path/config" | |
1897e3bc SH |
414 | if [ -z "$rootfs" ]; then |
415 | if grep -q '^lxc.rootfs' $config 2>/dev/null ; then | |
853d58fd | 416 | rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) |
1897e3bc SH |
417 | else |
418 | rootfs=$path/rootfs | |
419 | fi | |
1881820a SH |
420 | fi |
421 | ||
fb7460fe | 422 | |
1927a6be | 423 | install_debian $rootfs $release $arch |
fb7460fe DL |
424 | if [ $? -ne 0 ]; then |
425 | echo "failed to install debian" | |
426 | exit 1 | |
427 | fi | |
428 | ||
429 | configure_debian $rootfs $name | |
430 | if [ $? -ne 0 ]; then | |
431 | echo "failed to configure debian for a container" | |
432 | exit 1 | |
433 | fi | |
434 | ||
1927a6be | 435 | copy_configuration $path $rootfs $name $arch |
fb7460fe DL |
436 | if [ $? -ne 0 ]; then |
437 | echo "failed write configuration file" | |
438 | exit 1 | |
439 | fi | |
440 | ||
441 | if [ ! -z $clean ]; then | |
442 | clean || exit 1 | |
443 | exit 0 | |
444 | fi |