]>
Commit | Line | Data |
---|---|---|
3f033aa8 WM |
1 | #!/bin/bash |
2 | ||
3 | # | |
e2b4064f | 4 | # template script for generating ubuntu container for LXC |
3f033aa8 | 5 | # |
e2b4064f | 6 | # This script consolidates and extends the existing lxc ubuntu scripts |
3f033aa8 WM |
7 | # |
8 | ||
8cd80b50 SG |
9 | # Copyright © 2011 Serge Hallyn <serge.hallyn@canonical.com> |
10 | # Copyright © 2010 Wilhelm Meier | |
3f033aa8 WM |
11 | # Author: Wilhelm Meier <wilhelm.meier@fh-kl.de> |
12 | # | |
acbb59f5 SH |
13 | # This library is free software; you can redistribute it and/or |
14 | # modify it under the terms of the GNU Lesser General Public | |
15 | # License as published by the Free Software Foundation; either | |
16 | # version 2.1 of the License, or (at your option) any later version. | |
17 | ||
18 | # This library is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | # Lesser General Public License for more details. | |
22 | ||
23 | # You should have received a copy of the GNU Lesser General Public | |
24 | # License along with this library; if not, write to the Free Software | |
25 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
3f033aa8 | 26 | |
1d61e5b9 SH |
27 | set -e |
28 | ||
f2a95ee1 SG |
29 | LOCALSTATEDIR="@LOCALSTATEDIR@" |
30 | LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" | |
703d065d | 31 | |
e2b4064f SH |
32 | if [ -r /etc/default/lxc ]; then |
33 | . /etc/default/lxc | |
34 | fi | |
35 | ||
3f033aa8 WM |
36 | configure_ubuntu() |
37 | { | |
38 | rootfs=$1 | |
39 | hostname=$2 | |
f6144f0c | 40 | release=$3 |
2004e7da GL |
41 | user=$4 |
42 | password=$5 | |
3f033aa8 | 43 | |
a2abaa9e | 44 | # configure the network using the dhcp |
3f033aa8 | 45 | cat <<EOF > $rootfs/etc/network/interfaces |
adca8543 SH |
46 | # This file describes the network interfaces available on your system |
47 | # and how to activate them. For more information, see interfaces(5). | |
48 | ||
49 | # The loopback network interface | |
3f033aa8 WM |
50 | auto lo |
51 | iface lo inet loopback | |
52 | ||
53 | auto eth0 | |
54 | iface eth0 inet dhcp | |
55 | EOF | |
56 | ||
57 | # set the hostname | |
58 | cat <<EOF > $rootfs/etc/hostname | |
59 | $hostname | |
60 | EOF | |
61 | # set minimal hosts | |
62 | cat <<EOF > $rootfs/etc/hosts | |
adca8543 SH |
63 | 127.0.0.1 localhost |
64 | 127.0.1.1 $hostname | |
65 | ||
66 | # The following lines are desirable for IPv6 capable hosts | |
67 | ::1 ip6-localhost ip6-loopback | |
68 | fe00::0 ip6-localnet | |
69 | ff00::0 ip6-mcastprefix | |
70 | ff02::1 ip6-allnodes | |
71 | ff02::2 ip6-allrouters | |
3f033aa8 WM |
72 | EOF |
73 | ||
adca8543 | 74 | if [ ! -f $rootfs/etc/init/container-detect.conf ]; then |
f6144f0c SH |
75 | # suppress log level output for udev |
76 | sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf | |
3f033aa8 | 77 | |
f6144f0c SH |
78 | # remove jobs for consoles 5 and 6 since we only create 4 consoles in |
79 | # this template | |
80 | rm -f $rootfs/etc/init/tty{5,6}.conf | |
81 | fi | |
3f033aa8 | 82 | |
ce5dbd82 | 83 | if [ -z "$bindhome" ]; then |
2004e7da GL |
84 | chroot $rootfs useradd --create-home -s /bin/bash $user |
85 | echo "$user:$password" | chroot $rootfs chpasswd | |
ce5dbd82 SH |
86 | fi |
87 | ||
18f823c1 | 88 | # make sure we have the current locale defined in the container |
dc7f6545 | 89 | if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then |
3fefd6e6 SG |
90 | chroot $rootfs locale-gen en_US.UTF-8 || true |
91 | chroot $rootfs update-locale LANG=en_US.UTF-8 || true | |
18f823c1 | 92 | else |
3fefd6e6 SG |
93 | chroot $rootfs locale-gen $LANG || true |
94 | chroot $rootfs update-locale LANG=$LANG || true | |
18f823c1 SG |
95 | fi |
96 | ||
a2abaa9e | 97 | # generate new SSH keys |
07219a02 | 98 | if [ -x $rootfs/var/lib/dpkg/info/openssh-server.postinst ]; then |
6cda3f5a SG |
99 | cat > $rootfs/usr/sbin/policy-rc.d << EOF |
100 | #!/bin/sh | |
101 | exit 101 | |
102 | EOF | |
103 | chmod +x $rootfs/usr/sbin/policy-rc.d | |
104 | ||
a2abaa9e SG |
105 | rm -f $rootfs/etc/ssh/ssh_host_*key* |
106 | mv $rootfs/etc/init/ssh.conf $rootfs/etc/init/ssh.conf.disabled | |
07219a02 | 107 | DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure |
a2abaa9e | 108 | mv $rootfs/etc/init/ssh.conf.disabled $rootfs/etc/init/ssh.conf |
6cda3f5a | 109 | |
c5d32181 SG |
110 | sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub |
111 | ||
6cda3f5a | 112 | rm -f $rootfs/usr/sbin/policy-rc.d |
a2abaa9e SG |
113 | fi |
114 | ||
ce5dbd82 SH |
115 | return 0 |
116 | } | |
117 | ||
118 | # finish setting up the user in the container by injecting ssh key and | |
119 | # adding sudo group membership. | |
120 | # passed-in user is either 'ubuntu' or the user to bind in from host. | |
121 | finalize_user() | |
122 | { | |
123 | user=$1 | |
124 | ||
adca8543 SH |
125 | sudo_version=$(chroot $rootfs dpkg-query -W -f='${Version}' sudo) |
126 | ||
127 | if chroot $rootfs dpkg --compare-versions $sudo_version gt "1.8.3p1-1"; then | |
ce5dbd82 SH |
128 | groups="sudo" |
129 | else | |
130 | groups="sudo admin" | |
131 | fi | |
96bd45c8 SG |
132 | |
133 | for group in $groups; do | |
134 | chroot $rootfs groupadd --system $group >/dev/null 2>&1 || true | |
ce5dbd82 | 135 | chroot $rootfs adduser ${user} $group >/dev/null 2>&1 || true |
96bd45c8 SG |
136 | done |
137 | ||
4759162d | 138 | if [ -n "$auth_key" -a -f "$auth_key" ]; then |
1e1f8eeb SG |
139 | u_path="/home/${user}/.ssh" |
140 | root_u_path="$rootfs/$u_path" | |
141 | mkdir -p $root_u_path | |
142 | cp $auth_key "$root_u_path/authorized_keys" | |
143 | chroot $rootfs chown -R ${user}: "$u_path" | |
4759162d | 144 | |
1e1f8eeb | 145 | echo "Inserted SSH public key from $auth_key into /home/${user}/.ssh/authorized_keys" |
4759162d | 146 | fi |
3a3ba44a | 147 | return 0 |
3f033aa8 WM |
148 | } |
149 | ||
4213a747 CG |
150 | # A function to try and autodetect squid-deb-proxy servers on the local network |
151 | # if either the squid-deb-proxy-client package is installed on the host or | |
152 | # a parent container set the 50squid-deb-proxy-client file. | |
153 | squid_deb_proxy_autodetect() | |
154 | { | |
155 | local apt_discover=/usr/share/squid-deb-proxy-client/apt-avahi-discover | |
156 | local proxy_file=/etc/apt/apt.conf.d/50squid-deb-proxy-client | |
157 | squid_proxy_line= # That's a global :/ | |
158 | ||
159 | # Maybe the host is aware of a squid-deb-proxy? | |
160 | if [ -f $apt_discover ]; then | |
161 | echo -n "Discovering squid-deb-proxy..." | |
162 | squid_proxy_line=$($apt_discover) | |
163 | if [ -n "$squid_proxy_line" ]; then | |
164 | echo "found squid-deb-proxy: $squid_proxy_line" | |
165 | else | |
166 | echo "no squid-deb-proxy found" | |
167 | fi | |
168 | fi | |
169 | ||
170 | # Are we in a nested container, and the parent already knows of a proxy? | |
171 | if [ -f $proxy_file ]; then | |
172 | # Extract the squid URL from the file (whatever is between "") | |
173 | squid_proxy_line=`cat $proxy_file | sed "s/.*\"\(.*\)\".*/\1/"` | |
174 | fi | |
175 | } | |
176 | ||
cf0f9033 SH |
177 | # |
178 | # Choose proxies for container | |
179 | # http_proxy will be used by debootstrap on the host. | |
180 | # APT_PROXY will be used to set /etc/apt/apt.conf.d/70proxy in the container. | |
181 | # | |
182 | choose_container_proxy() | |
183 | { | |
184 | local rootfs=$1 | |
185 | local arch=$2 | |
186 | ||
187 | if [ -z "$HTTP_PROXY" ]; then | |
188 | HTTP_PROXY="none" | |
189 | fi | |
190 | case "$HTTP_PROXY" in | |
191 | none) | |
4213a747 CG |
192 | squid_deb_proxy_autodetect |
193 | if [ -n "$squid_proxy_line" ]; then | |
194 | APT_PROXY=$squid_proxy_line | |
195 | export http_proxy=$squid_proxy_line | |
196 | else | |
197 | APT_PROXY= | |
198 | fi | |
cf0f9033 SH |
199 | ;; |
200 | apt) | |
201 | RES=`apt-config shell APT_PROXY Acquire::http::Proxy` | |
202 | eval $RES | |
203 | [ -z "$APT_PROXY" ] || export http_proxy=$APT_PROXY | |
204 | ;; | |
205 | *) | |
206 | APT_PROXY=$HTTP_PROXY | |
207 | export http_proxy=$HTTP_PROXY | |
208 | ;; | |
209 | esac | |
210 | } | |
211 | ||
5a50e09a SH |
212 | write_sourceslist() |
213 | { | |
214 | # $1 => path to the rootfs | |
215 | # $2 => architecture we want to add | |
216 | # $3 => whether to use the multi-arch syntax or not | |
217 | ||
cf0f9033 SH |
218 | if [ -n "$APT_PROXY" ]; then |
219 | mkdir -p $rootfs/etc/apt/apt.conf.d | |
220 | cat > $rootfs/etc/apt/apt.conf.d/70proxy << EOF | |
221 | Acquire::http::Proxy "$APT_PROXY" ; | |
222 | EOF | |
223 | fi | |
224 | ||
5a50e09a SH |
225 | case $2 in |
226 | amd64|i386) | |
227 | MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} | |
228 | SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} | |
229 | ;; | |
5a50e09a SH |
230 | *) |
231 | MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} | |
232 | SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} | |
233 | ;; | |
234 | esac | |
235 | if [ -n "$3" ]; then | |
236 | cat >> "$1/etc/apt/sources.list" << EOF | |
237 | deb [arch=$2] $MIRROR ${release} main restricted universe multiverse | |
238 | deb [arch=$2] $MIRROR ${release}-updates main restricted universe multiverse | |
239 | deb [arch=$2] $SECURITY_MIRROR ${release}-security main restricted universe multiverse | |
240 | EOF | |
241 | else | |
242 | cat >> "$1/etc/apt/sources.list" << EOF | |
243 | deb $MIRROR ${release} main restricted universe multiverse | |
244 | deb $MIRROR ${release}-updates main restricted universe multiverse | |
245 | deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse | |
246 | EOF | |
247 | fi | |
248 | } | |
249 | ||
28b62856 GL |
250 | install_packages() |
251 | { | |
252 | local rootfs="$1" | |
253 | shift | |
254 | local packages="$*" | |
255 | if [ -z $update ] | |
256 | then | |
257 | chroot $rootfs apt-get update | |
258 | update=true | |
259 | fi | |
260 | if [ -n "${packages}" ] | |
261 | then | |
262 | chroot $rootfs apt-get install --force-yes -y --no-install-recommends ${packages} | |
263 | fi | |
264 | } | |
265 | ||
f1ccde27 SH |
266 | cleanup() |
267 | { | |
268 | rm -rf $cache/partial-$arch | |
269 | rm -rf $cache/rootfs-$arch | |
270 | } | |
271 | ||
91a5df88 SH |
272 | suggest_flush() |
273 | { | |
274 | echo "Container upgrade failed. The container cache may be out of date," | |
3f5f5d99 | 275 | echo "in which case flushing the cache (see -F in the help output) may help." |
91a5df88 SH |
276 | } |
277 | ||
3f033aa8 WM |
278 | download_ubuntu() |
279 | { | |
3f033aa8 WM |
280 | cache=$1 |
281 | arch=$2 | |
e2b4064f | 282 | release=$3 |
28b62856 | 283 | |
e8eab8b6 | 284 | packages_template=${packages_template:-"ssh,vim"} |
fade719e SG |
285 | |
286 | # Try to guess a list of langpacks to install | |
287 | langpacks="language-pack-en" | |
288 | ||
289 | if which dpkg >/dev/null 2>&1; then | |
89573feb | 290 | langpacks=`(echo $langpacks && |
fade719e SG |
291 | dpkg -l | grep -E "^ii language-pack-[a-z]* " | |
292 | cut -d ' ' -f3) | sort -u` | |
293 | fi | |
28b62856 | 294 | packages_template="${packages_template},$(echo $langpacks | sed 's/ /,/g')" |
fade719e SG |
295 | |
296 | ||
28b62856 | 297 | echo "Installing packages in template: ${packages_template}" |
3f033aa8 | 298 | |
f1ccde27 | 299 | trap cleanup EXIT SIGHUP SIGINT SIGTERM |
3f033aa8 WM |
300 | # check the mini ubuntu was not already downloaded |
301 | mkdir -p "$cache/partial-$arch" | |
302 | if [ $? -ne 0 ]; then | |
e2b4064f SH |
303 | echo "Failed to create '$cache/partial-$arch' directory" |
304 | return 1 | |
3f033aa8 WM |
305 | fi |
306 | ||
cf0f9033 | 307 | choose_container_proxy $cache/partial-$arch/ $arch |
3f033aa8 | 308 | # download a mini ubuntu into a cache |
e2b4064f | 309 | echo "Downloading ubuntu $release minimal ..." |
5a50e09a | 310 | if [ -n "$(which qemu-debootstrap)" ]; then |
28b62856 | 311 | qemu-debootstrap --verbose --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR |
5a50e09a | 312 | else |
28b62856 | 313 | debootstrap --verbose --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR |
5a50e09a SH |
314 | fi |
315 | ||
3f033aa8 | 316 | if [ $? -ne 0 ]; then |
e2b4064f SH |
317 | echo "Failed to download the rootfs, aborting." |
318 | return 1 | |
3f033aa8 WM |
319 | fi |
320 | ||
2e44ed1e SH |
321 | # Serge isn't sure whether we should avoid doing this when |
322 | # $release == `distro-info -d` | |
323 | echo "Installing updates" | |
5a50e09a SH |
324 | > $cache/partial-$arch/etc/apt/sources.list |
325 | write_sourceslist $cache/partial-$arch/ $arch | |
326 | ||
2e44ed1e SH |
327 | chroot "$1/partial-${arch}" apt-get update |
328 | if [ $? -ne 0 ]; then | |
329 | echo "Failed to update the apt cache" | |
330 | return 1 | |
331 | fi | |
332 | cat > "$1/partial-${arch}"/usr/sbin/policy-rc.d << EOF | |
333 | #!/bin/sh | |
334 | exit 101 | |
335 | EOF | |
336 | chmod +x "$1/partial-${arch}"/usr/sbin/policy-rc.d | |
337 | ||
91a5df88 | 338 | lxc-unshare -s MOUNT -- chroot "$1/partial-${arch}" apt-get dist-upgrade -y || { suggest_flush; false; } |
2e44ed1e SH |
339 | rm -f "$1/partial-${arch}"/usr/sbin/policy-rc.d |
340 | ||
06f5c632 SH |
341 | chroot "$1/partial-${arch}" apt-get clean |
342 | ||
15da01b3 | 343 | mv "$1/partial-$arch" "$1/rootfs-$arch" |
f1ccde27 SH |
344 | trap EXIT |
345 | trap SIGINT | |
346 | trap SIGTERM | |
347 | trap SIGHUP | |
15da01b3 | 348 | echo "Download complete" |
3f033aa8 WM |
349 | return 0 |
350 | } | |
351 | ||
352 | copy_ubuntu() | |
353 | { | |
354 | cache=$1 | |
355 | arch=$2 | |
356 | rootfs=$3 | |
357 | ||
358 | # make a local copy of the miniubuntu | |
f6144f0c | 359 | echo "Copying rootfs to $rootfs ..." |
6d8ac56b | 360 | mkdir -p $rootfs |
44d39789 | 361 | rsync -Ha $cache/rootfs-$arch/ $rootfs/ || return 1 |
3f033aa8 WM |
362 | return 0 |
363 | } | |
364 | ||
365 | install_ubuntu() | |
366 | { | |
3f033aa8 | 367 | rootfs=$1 |
e2b4064f | 368 | release=$2 |
bb59e078 | 369 | flushcache=$3 |
703d065d GL |
370 | cache="$LOCALSTATEDIR/cache/lxc/$release" |
371 | mkdir -p $LOCALSTATEDIR/lock/subsys/ | |
adca8543 | 372 | |
3f033aa8 | 373 | ( |
17abf278 | 374 | flock -x 9 |
adca8543 SH |
375 | if [ $? -ne 0 ]; then |
376 | echo "Cache repository is busy." | |
377 | return 1 | |
378 | fi | |
3f033aa8 | 379 | |
3f033aa8 | 380 | |
adca8543 SH |
381 | if [ $flushcache -eq 1 ]; then |
382 | echo "Flushing cache..." | |
383 | rm -rf "$cache/partial-$arch" | |
384 | rm -rf "$cache/rootfs-$arch" | |
385 | fi | |
bb59e078 | 386 | |
adca8543 SH |
387 | echo "Checking cache download in $cache/rootfs-$arch ... " |
388 | if [ ! -e "$cache/rootfs-$arch" ]; then | |
28b62856 | 389 | download_ubuntu $cache $arch $release |
adca8543 SH |
390 | if [ $? -ne 0 ]; then |
391 | echo "Failed to download 'ubuntu $release base'" | |
392 | return 1 | |
393 | fi | |
394 | fi | |
3f033aa8 | 395 | |
adca8543 SH |
396 | echo "Copy $cache/rootfs-$arch to $rootfs ... " |
397 | copy_ubuntu $cache $arch $rootfs | |
398 | if [ $? -ne 0 ]; then | |
399 | echo "Failed to copy rootfs" | |
400 | return 1 | |
401 | fi | |
3f033aa8 | 402 | |
adca8543 | 403 | return 0 |
3f033aa8 | 404 | |
17abf278 | 405 | ) 9>$LOCALSTATEDIR/lock/subsys/lxc-ubuntu$release |
3f033aa8 WM |
406 | |
407 | return $? | |
408 | } | |
409 | ||
410 | copy_configuration() | |
411 | { | |
412 | path=$1 | |
413 | rootfs=$2 | |
414 | name=$3 | |
e2b4064f | 415 | arch=$4 |
f6144f0c | 416 | release=$5 |
e2b4064f SH |
417 | |
418 | if [ $arch = "i386" ]; then | |
419 | arch="i686" | |
420 | fi | |
3f033aa8 | 421 | |
4759162d SH |
422 | # if there is exactly one veth network entry, make sure it has an |
423 | # associated hwaddr. | |
424 | nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` | |
425 | if [ $nics -eq 1 ]; then | |
daaf41b3 | 426 | 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 |
4759162d SH |
427 | fi |
428 | ||
f2a95ee1 SG |
429 | # Generate the configuration file |
430 | ## Create the fstab (empty by default) | |
431 | touch $path/fstab | |
432 | ||
433 | ## Relocate all the network config entries | |
434 | sed -i -e "/lxc.network/{w ${path}/config-network" -e "d}" $path/config | |
435 | ||
436 | ## Relocate any other config entries | |
437 | sed -i -e "/lxc./{w ${path}/config-auto" -e "d}" $path/config | |
438 | ||
439 | ## Add all the includes | |
440 | echo "" >> $path/config | |
441 | echo "# Common configuration" >> $path/config | |
442 | if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" ]; then | |
443 | echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" >> $path/config | |
444 | fi | |
445 | if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" ]; then | |
446 | echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" >> $path/config | |
447 | fi | |
448 | ||
449 | ## Add the container-specific config | |
450 | echo "" >> $path/config | |
451 | echo "# Container specific configuration" >> $path/config | |
452 | [ -e "$path/config-auto" ] && cat $path/config-auto >> $path/config && rm $path/config-auto | |
1881820a | 453 | grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config |
3a3ba44a | 454 | cat <<EOF >> $path/config |
daaf41b3 | 455 | lxc.mount = $path/fstab |
daaf41b3 | 456 | lxc.utsname = $name |
e2b4064f | 457 | lxc.arch = $arch |
3f033aa8 WM |
458 | EOF |
459 | ||
f2a95ee1 SG |
460 | ## Re-add the previously removed network config |
461 | echo "" >> $path/config | |
462 | echo "# Network configuration" >> $path/config | |
463 | cat $path/config-network >> $path/config | |
464 | rm $path/config-network | |
3f033aa8 WM |
465 | |
466 | if [ $? -ne 0 ]; then | |
adca8543 SH |
467 | echo "Failed to add configuration" |
468 | return 1 | |
3f033aa8 WM |
469 | fi |
470 | ||
471 | return 0 | |
472 | } | |
473 | ||
e2b4064f SH |
474 | post_process() |
475 | { | |
476 | rootfs=$1 | |
477 | release=$2 | |
28b62856 | 478 | packages=$3 |
e2b4064f | 479 | |
7ed86e44 SG |
480 | # Disable service startup |
481 | cat > $rootfs/usr/sbin/policy-rc.d << EOF | |
482 | #!/bin/sh | |
483 | exit 101 | |
484 | EOF | |
485 | chmod +x $rootfs/usr/sbin/policy-rc.d | |
486 | ||
2ef89d56 | 487 | if [ ! -f $rootfs/etc/init/container-detect.conf ]; then |
adca8543 SH |
488 | # Make sure we have a working resolv.conf |
489 | cresolvonf="${rootfs}/etc/resolv.conf" | |
490 | mv $cresolvonf ${cresolvonf}.lxcbak | |
491 | cat /etc/resolv.conf > ${cresolvonf} | |
492 | ||
f34ff296 | 493 | # for lucid, if not trimming, then add the ubuntu-virt |
e2b4064f | 494 | # ppa and install lxcguest |
f34ff296 | 495 | if [ $release = "lucid" ]; then |
91a5df88 | 496 | chroot $rootfs apt-get update |
28b62856 | 497 | install_packages $rootfs "python-software-properties" |
e2b4064f | 498 | chroot $rootfs add-apt-repository ppa:ubuntu-virt/ppa |
e2b4064f | 499 | fi |
adca8543 | 500 | |
2e44ed1e | 501 | chroot $rootfs apt-get update |
e2b4064f | 502 | chroot $rootfs apt-get install --force-yes -y lxcguest |
adca8543 SH |
503 | |
504 | # Restore old resolv.conf | |
505 | rm -f ${cresolvonf} | |
506 | mv ${cresolvonf}.lxcbak ${cresolvonf} | |
e2b4064f | 507 | fi |
5a50e09a SH |
508 | |
509 | # If the container isn't running a native architecture, setup multiarch | |
510 | if [ -x "$(ls -1 ${rootfs}/usr/bin/qemu-*-static 2>/dev/null)" ]; then | |
40f6ee00 SH |
511 | dpkg_version=$(chroot $rootfs dpkg-query -W -f='${Version}' dpkg) |
512 | if chroot $rootfs dpkg --compare-versions $dpkg_version ge "1.16.2"; then | |
513 | chroot $rootfs dpkg --add-architecture ${hostarch} | |
514 | else | |
515 | mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d | |
516 | echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch | |
517 | fi | |
5a50e09a SH |
518 | |
519 | # Save existing value of MIRROR and SECURITY_MIRROR | |
520 | DEFAULT_MIRROR=$MIRROR | |
521 | DEFAULT_SECURITY_MIRROR=$SECURITY_MIRROR | |
522 | ||
523 | # Write a new sources.list containing both native and multiarch entries | |
524 | > ${rootfs}/etc/apt/sources.list | |
525 | write_sourceslist $rootfs $arch "native" | |
526 | ||
527 | MIRROR=$DEFAULT_MIRROR | |
528 | SECURITY_MIRROR=$DEFAULT_SECURITY_MIRROR | |
529 | write_sourceslist $rootfs $hostarch "multiarch" | |
530 | ||
531 | # Finally update the lists and install upstart using the host architecture | |
d08c3aae | 532 | HOST_PACKAGES="upstart:${hostarch} mountall:${hostarch} isc-dhcp-client:${hostarch}" |
5a50e09a | 533 | chroot $rootfs apt-get update |
d08c3aae SG |
534 | if chroot $rootfs dpkg -l iproute2 | grep -q ^ii; then |
535 | HOST_PACKAGES="$HOST_PACKAGES iproute2:${hostarch}" | |
536 | else | |
537 | HOST_PACKAGES="$HOST_PACKAGES iproute:${hostarch}" | |
538 | fi | |
28b62856 GL |
539 | install_packages $rootfs $HOST_PACKAGES |
540 | fi | |
541 | ||
542 | # Install Packages in container | |
d2305c4c | 543 | if [ -n "$packages" ] |
28b62856 GL |
544 | then |
545 | local packages="`echo $packages | sed 's/,/ /g'`" | |
546 | echo "Installing packages: ${packages}" | |
547 | install_packages $rootfs $packages | |
5a50e09a | 548 | fi |
42ff5f0f | 549 | |
0a3673e8 SG |
550 | # Set initial timezone as on host |
551 | if [ -f /etc/timezone ]; then | |
552 | cat /etc/timezone > $rootfs/etc/timezone | |
553 | chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
554 | elif [ -f /etc/sysconfig/clock ]; then | |
17abf278 | 555 | . /etc/sysconfig/clock |
0a3673e8 SG |
556 | echo $ZONE > $rootfs/etc/timezone |
557 | chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
558 | else | |
559 | echo "Timezone in container is not configured. Adjust it manually." | |
560 | fi | |
561 | ||
adca8543 | 562 | # rmdir /dev/shm for containers that have /run/shm |
42ff5f0f SH |
563 | # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did |
564 | # get bind mounted to the host's /run/shm. So try to rmdir | |
565 | # it, and in case that fails move it out of the way. | |
5ff33774 | 566 | # NOTE: This can only be removed once 12.04 goes out of support |
542939c3 | 567 | if [ ! -L $rootfs/dev/shm ] && [ -d $rootfs/run/shm ] && [ -e $rootfs/dev/shm ]; then |
5ff33774 | 568 | rmdir $rootfs/dev/shm 2>/dev/null || mv $rootfs/dev/shm $rootfs/dev/shm.bak |
42ff5f0f SH |
569 | ln -s /run/shm $rootfs/dev/shm |
570 | fi | |
7ed86e44 SG |
571 | |
572 | # Re-enable service startup | |
573 | rm $rootfs/usr/sbin/policy-rc.d | |
e2b4064f SH |
574 | } |
575 | ||
576 | do_bindhome() | |
577 | { | |
578 | rootfs=$1 | |
579 | user=$2 | |
580 | ||
e2b4064f | 581 | # copy /etc/passwd, /etc/shadow, and /etc/group entries into container |
39aa5856 SH |
582 | pwd=`getent passwd $user` || { echo "Failed to copy password entry for $user"; false; } |
583 | echo $pwd >> $rootfs/etc/passwd | |
584 | ||
585 | # make sure user's shell exists in the container | |
586 | shell=`echo $pwd | cut -d: -f 7` | |
587 | if [ ! -x $rootfs/$shell ]; then | |
588 | echo "shell $shell for user $user was not found in the container." | |
589 | pkg=`dpkg -S $(readlink -m $shell) | cut -d ':' -f1` | |
590 | echo "Installing $pkg" | |
28b62856 | 591 | install_packages $rootfs $pkg |
e2b4064f | 592 | fi |
39aa5856 | 593 | |
e2b4064f | 594 | shad=`getent shadow $user` |
39aa5856 | 595 | echo "$shad" >> $rootfs/etc/shadow |
8565ea1c SH |
596 | |
597 | # bind-mount the user's path into the container's /home | |
598 | h=`getent passwd $user | cut -d: -f 6` | |
599 | mkdir -p $rootfs/$h | |
adca8543 SH |
600 | |
601 | # use relative path in container | |
602 | h2=${h#/} | |
603 | while [ ${h2:0:1} = "/" ]; do | |
604 | h2=${h2#/} | |
605 | done | |
e4208a19 | 606 | echo "$h $h2 none bind 0 0" >> $path/fstab |
39aa5856 SH |
607 | |
608 | # Make sure the group exists in container | |
9db1aba4 SH |
609 | grp=`echo $pwd | cut -d: -f 4` # group number for $user |
610 | grpe=`getent group $grp` || return 0 # if host doesn't define grp, ignore in container | |
611 | chroot $rootfs getent group "$grpe" || echo "$grpe" >> $rootfs/etc/group | |
e2b4064f SH |
612 | } |
613 | ||
3f033aa8 WM |
614 | usage() |
615 | { | |
616 | cat <<EOF | |
2ef89d56 | 617 | $1 -h|--help [-a|--arch] [-b|--bindhome <user>] [-d|--debug] |
52c8f624 | 618 | [-F | --flush-cache] [-r|--release <release>] [ -S | --auth-key <keyfile>] |
2004e7da | 619 | [--rootfs <rootfs>] [--packages <packages>] [-u|--user <user>] [--password <password>] |
adca8543 | 620 | release: the ubuntu release (e.g. precise): defaults to host release on ubuntu, otherwise uses latest LTS |
e2b4064f | 621 | bindhome: bind <user>'s home into the container |
ce5dbd82 | 622 | The ubuntu user will not be created, and <user> will have |
adca8543 SH |
623 | sudo access. |
624 | arch: the container architecture (e.g. amd64): defaults to host arch | |
52c8f624 | 625 | auth-key: SSH Public key file to inject into container |
2004e7da | 626 | packages: list of packages to add comma separated |
3f033aa8 WM |
627 | EOF |
628 | return 0 | |
629 | } | |
630 | ||
5eb28ae4 | 631 | options=$(getopt -o a:b:hp:r:n:FS:du: -l arch:,bindhome:,help,path:,release:,name:,flush-cache,auth-key:,debug,rootfs:,packages:,user:,password:,mirror:,security-mirror: -- "$@") |
3f033aa8 | 632 | if [ $? -ne 0 ]; then |
bc24fe4d WM |
633 | usage $(basename $0) |
634 | exit 1 | |
3f033aa8 WM |
635 | fi |
636 | eval set -- "$options" | |
637 | ||
adca8543 | 638 | release=precise # Default to the last Ubuntu LTS release for non-Ubuntu systems |
c6992ecf SH |
639 | if [ -f /etc/lsb-release ]; then |
640 | . /etc/lsb-release | |
adca8543 SH |
641 | if [ "$DISTRIB_ID" = "Ubuntu" ]; then |
642 | release=$DISTRIB_CODENAME | |
643 | fi | |
c6992ecf SH |
644 | fi |
645 | ||
e2b4064f | 646 | bindhome= |
e2b4064f | 647 | |
8339b4c8 SH |
648 | # Code taken from debootstrap |
649 | if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then | |
650 | arch=`/usr/bin/dpkg --print-architecture` | |
169bf5e0 | 651 | elif which udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then |
8339b4c8 SH |
652 | arch=`/usr/bin/udpkg --print-architecture` |
653 | else | |
ed4616b1 | 654 | arch=$(uname -m) |
8339b4c8 SH |
655 | if [ "$arch" = "i686" ]; then |
656 | arch="i386" | |
657 | elif [ "$arch" = "x86_64" ]; then | |
658 | arch="amd64" | |
659 | elif [ "$arch" = "armv7l" ]; then | |
8a63c0a9 | 660 | arch="armhf" |
8339b4c8 | 661 | fi |
e2b4064f SH |
662 | fi |
663 | ||
52c8f624 | 664 | debug=0 |
e2b4064f | 665 | hostarch=$arch |
bb59e078 | 666 | flushcache=0 |
d2305c4c | 667 | packages="" |
2004e7da GL |
668 | user="ubuntu" |
669 | password="ubuntu" | |
3f033aa8 WM |
670 | while true |
671 | do | |
672 | case "$1" in | |
e2b4064f | 673 | -h|--help) usage $0 && exit 0;; |
1897e3bc | 674 | --rootfs) rootfs=$2; shift 2;; |
e2b4064f SH |
675 | -p|--path) path=$2; shift 2;; |
676 | -n|--name) name=$2; shift 2;; | |
2004e7da GL |
677 | -u|--user) user=$2; shift 2;; |
678 | --password) password=$2; shift 2;; | |
bb59e078 | 679 | -F|--flush-cache) flushcache=1; shift 1;; |
e2b4064f | 680 | -r|--release) release=$2; shift 2;; |
2004e7da | 681 | --packages) packages=$2; shift 2;; |
e2b4064f SH |
682 | -b|--bindhome) bindhome=$2; shift 2;; |
683 | -a|--arch) arch=$2; shift 2;; | |
52c8f624 SH |
684 | -S|--auth-key) auth_key=$2; shift 2;; |
685 | -d|--debug) debug=1; shift 1;; | |
5eb28ae4 GL |
686 | --mirror) MIRROR=$2; shift 2;; |
687 | --security-mirror) SECURITY_MIRROR=$2; shift 2;; | |
e2b4064f | 688 | --) shift 1; break ;; |
bc24fe4d | 689 | *) break ;; |
3f033aa8 WM |
690 | esac |
691 | done | |
692 | ||
52c8f624 SH |
693 | if [ $debug -eq 1 ]; then |
694 | set -x | |
695 | fi | |
696 | ||
9db1aba4 SH |
697 | if [ -n "$bindhome" ]; then |
698 | pwd=`getent passwd $bindhome` | |
699 | if [ $? -ne 0 ]; then | |
700 | echo "Error: no password entry found for $bindhome" | |
701 | exit 1 | |
702 | fi | |
8565ea1c SH |
703 | fi |
704 | ||
705 | ||
17abf278 | 706 | if [ "$arch" = "i686" ]; then |
e2b4064f SH |
707 | arch=i386 |
708 | fi | |
709 | ||
e2b4064f | 710 | if [ $hostarch = "i386" -a $arch = "amd64" ]; then |
8a63c0a9 SG |
711 | echo "can't create $arch container on $hostarch" |
712 | exit 1 | |
713 | fi | |
714 | ||
715 | if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ | |
716 | [ $arch != "armhf" -a $arch != "armel" ]; then | |
717 | echo "can't create $arch container on $hostarch" | |
718 | exit 1 | |
719 | fi | |
720 | ||
721 | if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then | |
722 | echo "can't create $arch container on $hostarch" | |
e2b4064f SH |
723 | exit 1 |
724 | fi | |
725 | ||
169bf5e0 | 726 | which debootstrap >/dev/null 2>&1 || { echo "'debootstrap' command is missing" >&2; false; } |
3f033aa8 WM |
727 | |
728 | if [ -z "$path" ]; then | |
729 | echo "'path' parameter is required" | |
730 | exit 1 | |
731 | fi | |
732 | ||
733 | if [ "$(id -u)" != "0" ]; then | |
734 | echo "This script should be run as 'root'" | |
735 | exit 1 | |
736 | fi | |
737 | ||
1881820a SH |
738 | # detect rootfs |
739 | config="$path/config" | |
1897e3bc SH |
740 | # if $rootfs exists here, it was passed in with --rootfs |
741 | if [ -z "$rootfs" ]; then | |
742 | if grep -q '^lxc.rootfs' $config 2>/dev/null ; then | |
853d58fd | 743 | rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) |
1897e3bc SH |
744 | else |
745 | rootfs=$path/rootfs | |
746 | fi | |
1881820a | 747 | fi |
3f033aa8 | 748 | |
28b62856 | 749 | install_ubuntu $rootfs $release $flushcache |
3f033aa8 | 750 | if [ $? -ne 0 ]; then |
e2b4064f | 751 | echo "failed to install ubuntu $release" |
3f033aa8 WM |
752 | exit 1 |
753 | fi | |
754 | ||
2004e7da | 755 | configure_ubuntu $rootfs $name $release $user $password |
3f033aa8 | 756 | if [ $? -ne 0 ]; then |
e2b4064f | 757 | echo "failed to configure ubuntu $release for a container" |
3f033aa8 WM |
758 | exit 1 |
759 | fi | |
760 | ||
f6144f0c | 761 | copy_configuration $path $rootfs $name $arch $release |
3f033aa8 WM |
762 | if [ $? -ne 0 ]; then |
763 | echo "failed write configuration file" | |
764 | exit 1 | |
765 | fi | |
766 | ||
28b62856 | 767 | post_process $rootfs $release $trim_container $packages |
ce5dbd82 SH |
768 | |
769 | if [ -n "$bindhome" ]; then | |
770 | do_bindhome $rootfs $bindhome | |
771 | finalize_user $bindhome | |
772 | else | |
2004e7da | 773 | finalize_user $user |
e2b4064f SH |
774 | fi |
775 | ||
f6144f0c SH |
776 | echo "" |
777 | echo "##" | |
c6ed4d04 | 778 | if [ -n "$bindhome" ]; then |
1e1f8eeb | 779 | echo "# Log in as user $bindhome" |
c6ed4d04 | 780 | else |
2004e7da | 781 | echo "# The default user is '$user' with password '$password'!" |
1e1f8eeb | 782 | echo "# Use the 'sudo' command to run tasks as root in the container." |
c6ed4d04 | 783 | fi |
f6144f0c SH |
784 | echo "##" |
785 | echo "" |