]>
Commit | Line | Data |
---|---|---|
487ea5f6 ER |
1 | #!/bin/sh |
2 | ||
3 | # | |
4 | # template script for generating PLD Linux container for LXC | |
5 | # | |
6 | ||
7 | # | |
8 | # lxc: Linux Container library | |
9 | ||
10 | # Authors: | |
11 | # Elan Ruusamäe <glen@pld-linux.org> | |
12 | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 | ||
27 | # Detect use under userns (unsupported) | |
28 | for arg in "$@"; do | |
29 | [ "$arg" = "--" ] && break | |
30 | if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
31 | echo "This template can't be used for unprivileged containers." 1>&2 | |
32 | echo "You may want to try the \"download\" template instead." 1>&2 | |
33 | exit 1 | |
34 | fi | |
35 | done | |
36 | ||
37 | # Configuration | |
38 | arch=$(uname -m) | |
39 | cache_base=@LOCALSTATEDIR@/cache/lxc/pld/$arch | |
40 | default_path=@LXCPATH@ | |
487ea5f6 ER |
41 | |
42 | if [ -e /etc/os-release ]; then | |
43 | # This is a shell friendly configuration file. We can just source it. | |
44 | # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME | |
45 | . /etc/os-release | |
46 | echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" | |
47 | fi | |
48 | ||
49 | if [ "${CPE_NAME}" != "" -a "${ID}" = "pld" -a "${VERSION_ID}" != "" ]; then | |
50 | pld_host_ver=${VERSION_ID} | |
51 | is_pld=true | |
52 | elif [ -e /etc/pld-release ]; then | |
53 | # Only if all other methods fail, try to parse the pld-release file. | |
54 | pld_host_ver=$(sed -e '/PLD /!d' -e 's/^\([0-9.]*\)\sPLD.*/\1/' < /etc/pld-release) | |
55 | if [ "$pld_host_ver" != "" ]; then | |
56 | is_pld=true | |
57 | fi | |
58 | fi | |
59 | ||
60 | # Map a few architectures to their generic PLD Linux repository archs. | |
61 | case "$pld_host_ver:$arch" in | |
62 | 3.0:i586) arch=i486 ;; | |
63 | esac | |
64 | ||
65 | configure_pld() | |
66 | { | |
67 | ||
68 | # disable selinux | |
69 | mkdir -p $rootfs_path/selinux | |
70 | echo 0 > $rootfs_path/selinux/enforce | |
71 | ||
72 | # configure the network using the dhcp | |
73 | sed -i -e "s/^HOSTNAME=.*/HOSTNAME=${utsname}/" ${rootfs_path}/etc/sysconfig/network | |
74 | ||
75 | # set hostname on systemd | |
76 | if [ $release = "3.0" ]; then | |
77 | echo "${utsname}" > ${rootfs_path}/etc/hostname | |
78 | fi | |
79 | ||
80 | # set minimal hosts | |
81 | test -e $rootfs_path/etc/hosts || \ | |
82 | cat <<EOF > $rootfs_path/etc/hosts | |
83 | 127.0.0.1 localhost.localdomain localhost $utsname | |
84 | ::1 localhost6.localdomain6 localhost6 | |
85 | EOF | |
86 | ||
87 | dev_path="${rootfs_path}/dev" | |
88 | rm -rf $dev_path | |
89 | mkdir -p $dev_path | |
90 | mknod -m 666 ${dev_path}/null c 1 3 | |
91 | mknod -m 666 ${dev_path}/zero c 1 5 | |
92 | mknod -m 666 ${dev_path}/random c 1 8 | |
93 | mknod -m 666 ${dev_path}/urandom c 1 9 | |
94 | mkdir -m 755 ${dev_path}/pts | |
95 | mkdir -m 1777 ${dev_path}/shm | |
96 | mknod -m 666 ${dev_path}/tty c 5 0 | |
97 | mknod -m 666 ${dev_path}/tty0 c 4 0 | |
98 | mknod -m 666 ${dev_path}/tty1 c 4 1 | |
99 | mknod -m 666 ${dev_path}/tty2 c 4 2 | |
100 | mknod -m 666 ${dev_path}/tty3 c 4 3 | |
101 | mknod -m 666 ${dev_path}/tty4 c 4 4 | |
102 | mknod -m 600 ${dev_path}/console c 5 1 | |
103 | mknod -m 666 ${dev_path}/full c 1 7 | |
104 | mknod -m 600 ${dev_path}/initctl p | |
105 | mknod -m 666 ${dev_path}/ptmx c 5 2 | |
106 | ||
436ab4be EG |
107 | if [ -n "${root_password}" ]; then |
108 | echo "setting root passwd to $root_password" | |
109 | echo "root:$root_password" | chroot $rootfs_path chpasswd | |
110 | fi | |
487ea5f6 ER |
111 | |
112 | return 0 | |
113 | } | |
114 | ||
115 | configure_pld_init() | |
116 | { | |
117 | # default powerfail action waits 2 minutes. for lxc we want it immediately | |
118 | sed -i -e '/^pf::powerfail:/ s,/sbin/shutdown.*,/sbin/halt,' ${rootfs_path}/etc/inittab | |
119 | } | |
120 | ||
121 | configure_pld_systemd() | |
122 | { | |
123 | unlink ${rootfs_path}/etc/systemd/system/default.target | |
124 | chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service | |
125 | chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target | |
126 | ||
127 | # Actually, the After=dev-%i.device line does not appear in the | |
128 | # Fedora 17 or Fedora 18 systemd getty@.service file. It may be left | |
129 | # over from an earlier version and it's not doing any harm. We do need | |
130 | # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are | |
131 | # started on the ttys in the container. Lets do it in an override copy of | |
132 | # the service so it can still pass rpm verifies and not be automatically | |
133 | # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ | |
134 | ||
135 | sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
136 | -e 's/After=dev-%i.device/After=/' \ | |
137 | < ${rootfs_path}/lib/systemd/system/getty@.service \ | |
138 | > ${rootfs_path}/etc/systemd/system/getty@.service | |
139 | ||
140 | # Setup getty service on the 4 ttys we are going to allow in the | |
141 | # default config. Number should match lxc.tty | |
142 | for i in 1 2 3 4; do | |
143 | ln -sf ../getty@.service ${rootfs_path}/etc/systemd/system/getty.target.wants/getty@tty${i}.service | |
144 | done | |
145 | } | |
146 | ||
147 | download_pld() | |
148 | { | |
149 | ||
150 | # check the mini pld was not already downloaded | |
151 | INSTALL_ROOT=$cache/partial | |
152 | mkdir -p $INSTALL_ROOT | |
153 | if [ $? -ne 0 ]; then | |
154 | echo "Failed to create '$INSTALL_ROOT' directory" | |
155 | return 1 | |
156 | fi | |
157 | ||
158 | # download a mini pld into a cache | |
159 | echo "Downloading PLD Linux minimal ..." | |
160 | POLDEK="poldek --root $INSTALL_ROOT --noask --nohold --noignore" | |
161 | PKG_LIST="basesystem filesystem pld-release rpm poldek vserver-packages rc-scripts pwdutils mingetty" | |
162 | ||
163 | mkdir -p $INSTALL_ROOT@LOCALSTATEDIR@/lib/rpm | |
164 | rpm --root $INSTALL_ROOT --initdb | |
165 | $POLDEK -u $PKG_LIST | |
166 | ||
167 | if [ $? -ne 0 ]; then | |
168 | echo "Failed to download the rootfs, aborting." | |
169 | return 1 | |
170 | fi | |
171 | ||
172 | mv "$INSTALL_ROOT" "$cache/rootfs" | |
173 | echo "Download complete." | |
174 | ||
175 | return 0 | |
176 | } | |
177 | ||
178 | copy_pld() | |
179 | { | |
180 | ||
181 | # make a local copy of the minipld | |
182 | echo -n "Copying rootfs to $rootfs_path ..." | |
183 | cp -a $cache/rootfs/* $rootfs_path || return 1 | |
184 | return 0 | |
185 | } | |
186 | ||
187 | update_pld() | |
188 | { | |
189 | POLDEK="poldek --root $cache/rootfs --noask" | |
190 | $POLDEK --upgrade-dist | |
191 | } | |
192 | ||
193 | install_pld() | |
194 | { | |
195 | mkdir -p @LOCALSTATEDIR@/lock/subsys/ | |
196 | ( | |
197 | flock -x 9 | |
198 | if [ $? -ne 0 ]; then | |
199 | echo "Cache repository is busy." | |
200 | return 1 | |
201 | fi | |
202 | ||
203 | echo "Checking cache download in $cache/rootfs ... " | |
204 | if [ ! -e "$cache/rootfs" ]; then | |
205 | download_pld | |
206 | if [ $? -ne 0 ]; then | |
207 | echo "Failed to download 'pld base'" | |
208 | return 1 | |
209 | fi | |
210 | else | |
211 | echo "Cache found. Updating..." | |
212 | update_pld | |
213 | if [ $? -ne 0 ]; then | |
214 | echo "Failed to update 'pld base', continuing with last known good cache" | |
215 | else | |
216 | echo "Update finished" | |
217 | fi | |
218 | fi | |
219 | ||
220 | echo "Copy $cache/rootfs to $rootfs_path ... " | |
221 | copy_pld | |
222 | if [ $? -ne 0 ]; then | |
223 | echo "Failed to copy rootfs" | |
224 | return 1 | |
225 | fi | |
226 | ||
227 | return 0 | |
228 | ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld | |
229 | ||
230 | return $? | |
231 | } | |
232 | ||
233 | copy_configuration() | |
234 | { | |
235 | ||
236 | mkdir -p $config_path | |
7a96a068 | 237 | grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config |
487ea5f6 ER |
238 | cat <<EOF >> $config_path/config |
239 | # Most of below settings should be taken as defaults from | |
240 | # lxc.include = /usr/share/lxc/config/common.conf | |
b67771bc | 241 | lxc.uts.name = $utsname |
487ea5f6 ER |
242 | lxc.tty = 4 |
243 | lxc.pts = 1024 | |
244 | # Consider if below line is right for systemd container | |
245 | lxc.mount = $config_path/fstab | |
246 | lxc.cap.drop = sys_module mac_admin mac_override sys_time | |
247 | ||
248 | lxc.autodev = $auto_dev | |
249 | ||
250 | # When using LXC with apparmor, uncomment the next line to run unconfined: | |
251 | #lxc.aa_profile = unconfined | |
252 | ||
253 | ## Devices | |
254 | # Allow all devices | |
255 | #lxc.cgroup.devices.allow = a | |
256 | # Deny all devices | |
257 | lxc.cgroup.devices.deny = a | |
258 | # Allow to mknod all devices (but not using them) | |
259 | lxc.cgroup.devices.allow = c *:* m | |
260 | lxc.cgroup.devices.allow = b *:* m | |
261 | ||
262 | # /dev/null and zero | |
263 | lxc.cgroup.devices.allow = c 1:3 rwm | |
264 | lxc.cgroup.devices.allow = c 1:5 rwm | |
265 | # consoles | |
266 | lxc.cgroup.devices.allow = c 5:1 rwm | |
267 | lxc.cgroup.devices.allow = c 5:0 rwm | |
268 | lxc.cgroup.devices.allow = c 4:0 rwm | |
269 | lxc.cgroup.devices.allow = c 4:1 rwm | |
270 | # /dev/{,u}random | |
271 | lxc.cgroup.devices.allow = c 1:9 rwm | |
272 | lxc.cgroup.devices.allow = c 1:8 rwm | |
273 | lxc.cgroup.devices.allow = c 136:* rwm | |
274 | lxc.cgroup.devices.allow = c 5:2 rwm | |
275 | # rtc | |
276 | lxc.cgroup.devices.allow = c 254:0 rm | |
277 | EOF | |
278 | ||
279 | cat <<EOF > $config_path/fstab | |
280 | proc proc proc nodev,noexec,nosuid 0 0 | |
281 | sysfs sys sysfs defaults 0 0 | |
282 | EOF | |
283 | if [ $? -ne 0 ]; then | |
284 | echo "Failed to add configuration" | |
285 | return 1 | |
286 | fi | |
287 | ||
288 | return 0 | |
289 | } | |
290 | ||
291 | clean() | |
292 | { | |
293 | ||
294 | if [ ! -e $cache ]; then | |
295 | exit 0 | |
296 | fi | |
297 | ||
298 | # lock, so we won't purge while someone is creating a repository | |
299 | ( | |
300 | flock -x 9 | |
301 | if [ $? != 0 ]; then | |
302 | echo "Cache repository is busy." | |
303 | exit 1 | |
304 | fi | |
305 | ||
306 | echo -n "Purging the download cache for PLD Linux $release..." | |
307 | rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 | |
308 | exit 0 | |
309 | ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld | |
310 | } | |
311 | ||
312 | usage() | |
313 | { | |
314 | cat <<EOF | |
315 | usage: | |
316 | $1 -n|--name=<container_name> | |
317 | [-p|--path=<path>] [-c|--clean] [-R|--release=<PLD Release>] [--fqdn=<network name of container>] [-A|--arch=<arch of the container>] | |
318 | [-h|--help] | |
319 | Mandatory args: | |
320 | -n,--name container name, used to as an identifier for that container from now on | |
321 | Optional args: | |
322 | -p,--path path to where the container will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case | |
323 | --rootfs path for actual rootfs. | |
324 | -c,--clean clean the cache | |
325 | -R,--release PLD Linux release for the new container. if the host is PLD Linux, then it will default to the host's release. | |
326 | --fqdn fully qualified domain name (FQDN) for DNS and system naming | |
327 | -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] | |
328 | -h,--help print this help | |
329 | EOF | |
330 | return 0 | |
331 | } | |
332 | ||
333 | options=$(getopt -o hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,fqdn: -- "$@") | |
334 | if [ $? -ne 0 ]; then | |
335 | usage $(basename $0) | |
336 | exit 1 | |
337 | fi | |
338 | eval set -- "$options" | |
339 | ||
340 | while :; do | |
341 | case "$1" in | |
342 | -h|--help) usage $0 && exit 0;; | |
343 | -p|--path) path=$2; shift 2;; | |
344 | --rootfs) rootfs=$2; shift 2;; | |
345 | -n|--name) name=$2; shift 2;; | |
346 | -c|--clean) clean=$2; shift 2;; | |
347 | -R|--release) release=$2; shift 2;; | |
348 | --fqdn) utsname=$2; shift 2;; | |
349 | --) shift 1; break ;; | |
350 | *) break ;; | |
351 | esac | |
352 | done | |
353 | ||
354 | if [ ! -z "$clean" -a -z "$path" ]; then | |
355 | clean || exit 1 | |
356 | exit 0 | |
357 | fi | |
358 | ||
359 | if [ -z "${utsname}" ]; then | |
360 | utsname=${name} | |
361 | fi | |
362 | ||
363 | # This follows a standard "resolver" convention that an FQDN must have | |
364 | # at least two dots or it is considered a local relative host name. | |
365 | # If it doesn't, append the dns domain name of the host system. | |
366 | # | |
367 | # This changes one significant behavior when running | |
368 | # "lxc_create -n Container_Name" without using the | |
369 | # --fqdn option. | |
370 | # | |
371 | # Old behavior: | |
372 | # utsname and hostname = Container_Name | |
373 | # New behavior: | |
374 | # utsname and hostname = Container_Name.Domain_Name | |
375 | ||
376 | if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then | |
377 | if [ -n "$(dnsdomainname)" ]; then | |
378 | utsname=${utsname}.$(dnsdomainname) | |
379 | fi | |
380 | fi | |
381 | ||
382 | needed_pkgs="" | |
383 | type poldek >/dev/null 2>&1 | |
384 | if [ $? -ne 0 ]; then | |
385 | needed_pkgs="poldek $needed_pkgs" | |
386 | fi | |
387 | ||
388 | #type curl >/dev/null 2>&1 | |
389 | #if [ $? -ne 0 ]; then | |
390 | # needed_pkgs="curl $needed_pkgs" | |
391 | #fi | |
392 | ||
393 | if [ -n "$needed_pkgs" ]; then | |
394 | echo "Missing commands: $needed_pkgs" | |
395 | echo "Please install these using \"sudo poldek -u $needed_pkgs\"" | |
396 | exit 1 | |
397 | fi | |
398 | ||
399 | if [ -z "$path" ]; then | |
400 | path=$default_path/$name | |
401 | fi | |
402 | ||
403 | if [ -z "$release" ]; then | |
404 | if [ "$is_pld" -a "$pld_host_ver" ]; then | |
405 | release=$pld_host_ver | |
406 | else | |
407 | echo "This is not a PLD Linux host and release missing, defaulting to 3.0. use -R|--release to specify release" | |
408 | release=3.0 | |
409 | fi | |
410 | fi | |
411 | ||
412 | # pld th have systemd. We need autodev enabled to keep systemd from causing problems. | |
413 | if [ $release = 3.0 ]; then | |
414 | auto_dev="0" | |
415 | else | |
416 | auto_dev="0" | |
417 | fi | |
418 | ||
419 | if [ "$(id -u)" != "0" ]; then | |
420 | echo "This script should be run as 'root'" | |
421 | exit 1 | |
422 | fi | |
423 | ||
424 | if [ -z "$rootfs" ]; then | |
425 | rootfs_path=$path/rootfs | |
7a96a068 | 426 | # check for 'lxc.rootfs.path' passed in through default config by lxc-create |
487ea5f6 | 427 | # TODO: should be lxc.rootfs.mount used instead? |
7a96a068 CB |
428 | if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then |
429 | rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) | |
487ea5f6 ER |
430 | fi |
431 | else | |
432 | rootfs_path=$rootfs | |
433 | fi | |
434 | config_path=$default_path/$name | |
435 | cache=$cache_base/$release | |
436 | ||
437 | revert() | |
438 | { | |
439 | echo "Interrupted, so cleaning up" | |
440 | lxc-destroy -n $name | |
441 | # maybe was interrupted before copy config | |
442 | rm -rf $path | |
443 | rm -rf $default_path/$name | |
444 | echo "exiting..." | |
445 | exit 1 | |
446 | } | |
447 | ||
448 | trap revert SIGHUP SIGINT SIGTERM | |
449 | ||
450 | copy_configuration | |
451 | if [ $? -ne 0 ]; then | |
452 | echo "Failed write configuration file" | |
453 | exit 1 | |
454 | fi | |
455 | ||
456 | install_pld | |
457 | if [ $? -ne 0 ]; then | |
458 | echo "Failed to install PLD Linux" | |
459 | exit 1 | |
460 | fi | |
461 | ||
462 | configure_pld | |
463 | if [ $? -ne 0 ]; then | |
464 | echo "Failed to configure PLD Linux for a container" | |
465 | exit 1 | |
466 | fi | |
467 | ||
468 | # If the systemd configuration directory exists - set it up for what we need. | |
469 | if [ -d ${rootfs_path}/etc/systemd/system ]; then | |
470 | configure_pld_systemd | |
471 | fi | |
472 | ||
473 | # This configuration (rc.sysinit) is not inconsistent with the systemd stuff | |
474 | # above and may actually coexist on some upgraded systems. Let's just make | |
475 | # sure that, if it exists, we update this file, even if it's not used... | |
476 | if [ -f ${rootfs_path}/etc/rc.sysinit ]; then | |
477 | configure_pld_init | |
478 | fi | |
479 | ||
480 | if [ ! -z $clean ]; then | |
481 | clean || exit 1 | |
482 | exit 0 | |
483 | fi | |
484 | echo "container rootfs and config created" |