]>
Commit | Line | Data |
---|---|---|
1 | #!/bin/bash | |
2 | ||
3 | # | |
4 | # lxc: linux Container library | |
5 | ||
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 | |
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | # Lesser General Public License for more details. | |
18 | ||
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 | |
21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | ||
23 | MIRROR=${MIRROR:-http://cdn.debian.net/debian} | |
24 | LOCALSTATEDIR="@LOCALSTATEDIR@" | |
25 | LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" | |
26 | ||
27 | configure_debian() | |
28 | { | |
29 | rootfs=$1 | |
30 | hostname=$2 | |
31 | ||
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 | |
35 | if [ ! -e $rootfs/dev/tty$tty ]; then | |
36 | mknod $rootfs/dev/tty$tty c 4 $tty | |
37 | fi | |
38 | done | |
39 | ||
40 | # configure the inittab | |
41 | cat <<EOF > $rootfs/etc/inittab | |
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 | |
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 | |
58 | p6::ctrlaltdel:/sbin/init 6 | |
59 | p0::powerfail:/sbin/init 0 | |
60 | EOF | |
61 | ||
62 | # disable selinux in debian | |
63 | mkdir -p $rootfs/selinux | |
64 | echo 0 > $rootfs/selinux/enforce | |
65 | ||
66 | # configure the network using the dhcp | |
67 | cat <<EOF > $rootfs/etc/network/interfaces | |
68 | auto lo | |
69 | iface lo inet loopback | |
70 | ||
71 | auto eth0 | |
72 | iface eth0 inet dhcp | |
73 | EOF | |
74 | ||
75 | # set the hostname | |
76 | cat <<EOF > $rootfs/etc/hostname | |
77 | $hostname | |
78 | EOF | |
79 | ||
80 | # reconfigure some services | |
81 | if [ -z "$LANG" ]; then | |
82 | chroot $rootfs locale-gen en_US.UTF-8 UTF-8 | |
83 | chroot $rootfs update-locale LANG=en_US.UTF-8 | |
84 | else | |
85 | encoding=$(echo $LANG | cut -d. -f2) | |
86 | chroot $rootfs sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \ | |
87 | -i /etc/locale.gen 2>/dev/null | |
88 | chroot $rootfs locale-gen $LANG $encoding | |
89 | chroot $rootfs update-locale LANG=$LANG | |
90 | fi | |
91 | ||
92 | # remove pointless services in a container | |
93 | chroot $rootfs /usr/sbin/update-rc.d -f checkroot.sh remove | |
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 | |
97 | ||
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 | ||
122 | # set initial timezone as on host | |
123 | if [ -f /etc/timezone ]; then | |
124 | cat /etc/timezone > $rootfs/etc/timezone | |
125 | chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
126 | elif [ -f /etc/sysconfig/clock ]; then | |
127 | . /etc/sysconfig/clock | |
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 | ||
134 | echo "root:root" | chroot $rootfs chpasswd | |
135 | echo "Root password is 'root', please change !" | |
136 | ||
137 | return 0 | |
138 | } | |
139 | ||
140 | cleanup() | |
141 | { | |
142 | rm -rf $cache/partial-$release-$arch | |
143 | rm -rf $cache/rootfs-$release-$arch | |
144 | } | |
145 | ||
146 | download_debian() | |
147 | { | |
148 | packages=\ | |
149 | ifupdown,\ | |
150 | locales,\ | |
151 | libui-dialog-perl,\ | |
152 | dialog,\ | |
153 | isc-dhcp-client,\ | |
154 | netbase,\ | |
155 | net-tools,\ | |
156 | iproute,\ | |
157 | openssh-server | |
158 | ||
159 | cache=$1 | |
160 | arch=$2 | |
161 | release=$3 | |
162 | ||
163 | trap cleanup EXIT SIGHUP SIGINT SIGTERM | |
164 | # check the mini debian was not already downloaded | |
165 | mkdir -p "$cache/partial-$release-$arch" | |
166 | if [ $? -ne 0 ]; then | |
167 | echo "Failed to create '$cache/partial-$release-$arch' directory" | |
168 | return 1 | |
169 | fi | |
170 | ||
171 | # download a mini debian into a cache | |
172 | echo "Downloading debian minimal ..." | |
173 | debootstrap --verbose --variant=minbase --arch=$arch \ | |
174 | --include=$packages \ | |
175 | "$release" "$cache/partial-$release-$arch" $MIRROR | |
176 | if [ $? -ne 0 ]; then | |
177 | echo "Failed to download the rootfs, aborting." | |
178 | return 1 | |
179 | fi | |
180 | ||
181 | mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" | |
182 | echo "Download complete." | |
183 | trap EXIT | |
184 | trap SIGINT | |
185 | trap SIGTERM | |
186 | trap SIGHUP | |
187 | ||
188 | return 0 | |
189 | } | |
190 | ||
191 | copy_debian() | |
192 | { | |
193 | cache=$1 | |
194 | arch=$2 | |
195 | rootfs=$3 | |
196 | release=$4 | |
197 | ||
198 | # make a local copy of the minidebian | |
199 | echo -n "Copying rootfs to $rootfs..." | |
200 | mkdir -p $rootfs | |
201 | rsync -Ha "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 | |
202 | return 0 | |
203 | } | |
204 | ||
205 | install_debian() | |
206 | { | |
207 | cache="$LOCALSTATEDIR/cache/lxc/debian" | |
208 | rootfs=$1 | |
209 | release=$2 | |
210 | arch=$3 | |
211 | mkdir -p $LOCALSTATEDIR/lock/subsys/ | |
212 | ( | |
213 | flock -x 9 | |
214 | if [ $? -ne 0 ]; then | |
215 | echo "Cache repository is busy." | |
216 | return 1 | |
217 | fi | |
218 | ||
219 | echo "Checking cache download in $cache/rootfs-$release-$arch ... " | |
220 | if [ ! -e "$cache/rootfs-$release-$arch" ]; then | |
221 | download_debian $cache $arch $release | |
222 | if [ $? -ne 0 ]; then | |
223 | echo "Failed to download 'debian base'" | |
224 | return 1 | |
225 | fi | |
226 | fi | |
227 | ||
228 | copy_debian $cache $arch $rootfs $release | |
229 | if [ $? -ne 0 ]; then | |
230 | echo "Failed to copy rootfs" | |
231 | return 1 | |
232 | fi | |
233 | ||
234 | return 0 | |
235 | ||
236 | ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian | |
237 | ||
238 | return $? | |
239 | } | |
240 | ||
241 | copy_configuration() | |
242 | { | |
243 | path=$1 | |
244 | rootfs=$2 | |
245 | hostname=$3 | |
246 | arch=$4 | |
247 | ||
248 | # Generate the configuration file | |
249 | ## Create the fstab (empty by default) | |
250 | touch $path/fstab | |
251 | ||
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 | ||
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 | |
272 | grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config | |
273 | ||
274 | cat <<EOF >> $path/config | |
275 | lxc.mount = $path/fstab | |
276 | lxc.utsname = $hostname | |
277 | lxc.arch = $arch | |
278 | EOF | |
279 | ||
280 | if [ $? -ne 0 ]; then | |
281 | echo "Failed to add configuration" | |
282 | return 1 | |
283 | fi | |
284 | ||
285 | return 0 | |
286 | } | |
287 | ||
288 | clean() | |
289 | { | |
290 | cache="$LOCALSTATEDIR/cache/lxc/debian" | |
291 | ||
292 | if [ ! -e $cache ]; then | |
293 | exit 0 | |
294 | fi | |
295 | ||
296 | # lock, so we won't purge while someone is creating a repository | |
297 | ( | |
298 | flock -x 9 | |
299 | if [ $? != 0 ]; then | |
300 | echo "Cache repository is busy." | |
301 | exit 1 | |
302 | fi | |
303 | ||
304 | echo -n "Purging the download cache..." | |
305 | rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 | |
306 | exit 0 | |
307 | ||
308 | ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian | |
309 | } | |
310 | ||
311 | usage() | |
312 | { | |
313 | cat <<EOF | |
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 | |
317 | EOF | |
318 | return 0 | |
319 | } | |
320 | ||
321 | options=$(getopt -o hp:n:a:r:c -l help,rootfs:,path:,name:,arch:,release:,clean -- "$@") | |
322 | if [ $? -ne 0 ]; then | |
323 | usage $(basename $0) | |
324 | exit 1 | |
325 | fi | |
326 | eval set -- "$options" | |
327 | ||
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 | ||
342 | while true | |
343 | do | |
344 | case "$1" in | |
345 | -h|--help) usage $0 && exit 1;; | |
346 | -p|--path) path=$2; shift 2;; | |
347 | --rootfs) rootfs=$2; shift 2;; | |
348 | -a|--arch) arch=$2; shift 2;; | |
349 | -r|--release) release=$2; shift 2;; | |
350 | -n|--name) name=$2; shift 2;; | |
351 | -c|--clean) clean=$2; shift 2;; | |
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 | ||
362 | if [ "$arch" = "i686" ]; then | |
363 | arch=i386 | |
364 | fi | |
365 | ||
366 | if [ "$arch" = "x86_64" ]; then | |
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 | ||
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 | |
400 | fi | |
401 | ||
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 | ||
412 | # detect rootfs | |
413 | config="$path/config" | |
414 | if [ -z "$rootfs" ]; then | |
415 | if grep -q '^lxc.rootfs' $config 2>/dev/null ; then | |
416 | rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) | |
417 | else | |
418 | rootfs=$path/rootfs | |
419 | fi | |
420 | fi | |
421 | ||
422 | ||
423 | install_debian $rootfs $release $arch | |
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 | ||
435 | copy_configuration $path $rootfs $name $arch | |
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 |