]>
Commit | Line | Data |
---|---|---|
3d7aa788 RG |
1 | #!/bin/bash |
2 | ||
3 | # | |
4 | # template script for generating Fedora container for LXC | |
5 | # | |
6 | ||
7 | # | |
8 | # lxc: linux Container library | |
9 | ||
10 | # Authors: | |
11 | # Daniel Lezcano <daniel.lezcano@free.fr> | |
12 | # Ramez Hanna <rhanna@informatiq.org> | |
13 | # Michael H. Warfield <mhw@WittsEnd.com> | |
14 | # Reto Gantenbein <reto.gantenbein@linuxmonk.ch> | |
15 | ||
16 | # This library is free software; you can redistribute it and/or | |
17 | # modify it under the terms of the GNU Lesser General Public | |
18 | # License as published by the Free Software Foundation; either | |
19 | # version 2.1 of the License, or (at your option) any later version. | |
20 | ||
21 | # This library is distributed in the hope that it will be useful, | |
22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
24 | # Lesser General Public License for more details. | |
25 | ||
26 | # You should have received a copy of the GNU Lesser General Public | |
27 | # License along with this library; if not, write to the Free Software | |
28 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
29 | ||
30 | # configurations | |
31 | FEDORA_RELEASE_MIN=24 | |
32 | FEDORA_RELEASE_DEFAULT=${FEDORA_RELEASE_DEFAULT:-25} | |
33 | FEDORA_RSYNC_URL="${FEDORA_RSYNC_URL:-archives.fedoraproject.org::fedora-enchilada}" | |
34 | MIRRORLIST_URL=${MIRRORLIST_URL:-http://mirrors.fedoraproject.org/mirrorlist} | |
35 | ||
36 | local_state_dir="@LOCALSTATEDIR@" | |
37 | lxc_path="@LXCPATH@" | |
38 | lxc_template_config="@LXCTEMPLATECONFIG@" | |
39 | lxc_default_conf="@LXC_DEFAULT_CONFIG@" | |
40 | ||
41 | # allows the cache directory to be set by environment variable | |
42 | LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${local_state_dir}/cache/lxc"}" | |
43 | ||
44 | # these are only going into comments in the resulting config... | |
45 | lxc_network_type=veth | |
46 | lxc_network_link=lxcbr0 | |
47 | ||
48 | # Some combinations of the tuning knobs below do not exactly make sense. | |
49 | # but that's ok. | |
50 | # | |
51 | # If the "root_password" is non-blank, use it, else set a default. | |
52 | # This can be passed to the script as an environment variable and is | |
53 | # set by a shell conditional assignment. Looks weird but it is what it is. | |
54 | # | |
55 | # If the root password contains a ding ($) then try to expand it. | |
56 | # That will pick up things like ${name} and ${RANDOM}. | |
57 | # If the root password contains more than 3 consecutive X's, pass it as | |
58 | # a template to mktemp and take the result. | |
59 | # | |
60 | # If root_display_password = yes, display the temporary root password at exit. | |
61 | # If root_store_password = yes, store it in the configuration directory | |
62 | # If root_prompt_password = yes, invoke "passwd" to force the user to change | |
63 | # the root password after the container is created. | |
64 | # If root_expire_password = yes, you will be prompted to change the root | |
65 | # password at the first login. | |
66 | # | |
67 | # These are conditional assignments... The can be overridden from the | |
68 | # preexisting environment variables... | |
69 | # | |
70 | # Make sure this is in single quotes to defer expansion to later! | |
71 | # :{root_password='Root-${name}-${RANDOM}'} | |
72 | : "${root_password='Root-${name}-XXXXXX'}" | |
73 | ||
74 | # Now, it doesn't make much sense to display, store, and force change | |
75 | # together. But, we gotta test, right??? | |
76 | : "${root_display_password='no'}" | |
77 | : "${root_store_password='yes'}" | |
78 | # Prompting for something interactive has potential for mayhem | |
79 | # with users running under the API... Don't default to "yes" | |
80 | : "${root_prompt_password='no'}" | |
81 | ||
82 | # Expire root password? Default to yes, but can be overridden from | |
83 | # the environment variable | |
84 | : "${root_expire_password='yes'}" | |
85 | ||
86 | # detect use under userns (unsupported) | |
87 | for arg in "$@"; do | |
88 | [ "${arg}" = "--" ] && break | |
89 | if [ "${arg}" = "--mapped-uid" ] || [ "${arg}" = "--mapped-gid" ] | |
90 | then | |
91 | echo "This template can't be used for unprivileged containers." 1>&2 | |
92 | echo "You may want to try the \"download\" template instead." 1>&2 | |
93 | exit 1 | |
94 | fi | |
95 | done | |
96 | ||
97 | # make sure the usual locations are in PATH | |
98 | export PATH=${PATH}:/usr/sbin:/usr/bin:/sbin:/bin | |
99 | ||
100 | # dnf package manager arguments | |
101 | dnf_args=( --assumeyes --best --allowerasing --disablerepo=* --enablerepo=fedora --enablerepo=updates ) | |
102 | ||
103 | # This function is going to setup a minimal Fedora bootstrap environment | |
104 | # which will be used to create new containers on non-Fedora hosts. | |
105 | # | |
106 | bootstrap_fedora() | |
107 | { | |
108 | local cache="${1}" | |
109 | ||
110 | local bootstrap="${cache}/bootstrap" | |
111 | if [ -d "${bootstrap}" ] | |
112 | then | |
113 | echo "Existing Fedora bootstrap environment found. Testing ..." | |
114 | if chroot_update_fedora "${bootstrap}" | |
115 | then | |
116 | CHROOT_DIR="${bootstrap}" | |
117 | CHROOT_CMD="chroot ${CHROOT_DIR} " | |
118 | ||
119 | echo "Bootstrap environment appears to be functional." | |
120 | return 0 | |
121 | else | |
122 | echo "Error: Bootstrap environment detected in ${bootstrap}" | |
123 | echo "but appears to be non-functional. Please remove and restart." | |
124 | return 1 | |
125 | fi | |
126 | fi | |
127 | ||
128 | echo "Setting up new Fedora ${FEDORA_RELEASE_DEFAULT} (${arch}) bootstrap environment." | |
129 | ||
130 | [[ -d "${cache}" ]] || mkdir -p "${cache}" | |
131 | ||
132 | tmp_bootstrap_dir=$( mktemp -d --tmpdir="${cache}" bootstrap_XXXXXX ) | |
133 | pushd "${tmp_bootstrap_dir}" >/dev/null | |
134 | ||
135 | mkdir squashfs liveos bootstrap | |
136 | ||
137 | # download the LiveOS squashfs image | |
138 | if [ ! -f "${cache}/install.img" ] | |
139 | then | |
140 | ||
141 | local image_path="/linux/releases/${FEDORA_RELEASE_DEFAULT}/Everything/${arch}/os/images/install.img" | |
142 | local ret=1 | |
143 | ||
144 | if [ -n "${mirror}" ] | |
145 | then | |
146 | echo -n "Downloading LiveOS squashfs image from ${mirror} ... " | |
147 | curl --silent --show-error --fail --remote-name "${mirror}${image_path}" | |
148 | ret=$? | |
149 | echo | |
150 | else | |
151 | echo "Syncing LiveOS squashfs image from ${FEDORA_RSYNC_URL} ... " | |
152 | rsync --archive --info=progress "${FEDORA_RSYNC_URL}${image_path}" . | |
153 | ret=$? | |
154 | fi | |
155 | if [ "${ret}" != 0 ] || [ ! -s install.img ] | |
156 | then | |
157 | echo "Error: Download of squashfs image failed." | |
158 | return 1 | |
159 | fi | |
160 | mv install.img "${cache}/" | |
161 | else | |
162 | echo "Using cached LiveOS squashfs image." | |
163 | fi | |
164 | ||
165 | echo "Mounting LiveOS squashfs file system." | |
df3e3fa1 | 166 | if ! mount -o loop -t squashfs "${cache}/install.img" squashfs/ |
3d7aa788 RG |
167 | then |
168 | echo " | |
169 | Error: Mount of LiveOS squashfs image failed | |
170 | -------------------------------------------- | |
171 | You must have squashfs support available to mount image. LiveOS image is now | |
172 | cached. Process may be rerun without penalty of downloading LiveOS again. If | |
173 | LiveOS is corrupt, remove ${cache}/install.img | |
174 | " | |
175 | return 1 | |
176 | fi | |
177 | ||
178 | # mount contained LiveOS | |
179 | if ! mount -o loop squashfs/LiveOS/rootfs.img liveos | |
180 | then | |
181 | echo " | |
182 | Error: Mount of LiveOS stage0 rootfs image failed | |
183 | ------------------------------------------------- | |
184 | LiveOS download may be corrupt. Remove ${cache}/LiveOS | |
185 | to force a new download. | |
186 | " | |
187 | return 1 | |
188 | fi | |
189 | ||
190 | echo "Copying LiveOS content to bootstrap environment ... " | |
191 | if ! rsync --archive --acls --hard-links --sparse liveos/. bootstrap/ | |
192 | then | |
193 | echo "Error: Build of bootstrap environment failed." | |
194 | echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." | |
195 | exit 1 | |
196 | fi | |
197 | ||
198 | # unmount liveos mounts - we're done with liveos at this point | |
199 | umount liveos | |
200 | umount squashfs | |
201 | ||
202 | # customize bootstrap rootfs | |
203 | pushd bootstrap >/dev/null | |
204 | ||
205 | # setup repositories in bootstrap chroot | |
206 | CHROOT_DIR="$(pwd)" | |
207 | CHROOT_CMD="chroot ${CHROOT_DIR} " | |
208 | INSTALL_ROOT="/" | |
209 | if ! setup_repositories "${cache}" "${arch}" "${FEDORA_RELEASE_DEFAULT}" "${mirror}" | |
210 | then | |
211 | echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" | |
212 | exit 1 | |
213 | fi | |
214 | if [ -n "${mirror}" ] | |
215 | then | |
216 | set_dnf_mirror_url ./etc/yum.repos.d/fedora*.repo | |
217 | fi | |
218 | ||
219 | # make sure /etc/resolv.conf is up to date in the chroot | |
220 | cp /etc/resolv.conf ./etc/ | |
221 | ||
222 | # verify bootstrap chroot by running a dnf update | |
223 | chroot_update_fedora "$(pwd)" | |
224 | ret=$? | |
225 | ||
226 | popd >/dev/null | |
227 | ||
228 | if [ "${ret}" != 0 ] | |
229 | then | |
230 | echo "Error: Build of bootstrap environment failed." | |
231 | echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." | |
232 | return 1 | |
233 | fi | |
234 | ||
235 | mv bootstrap "${cache}" | |
236 | ||
237 | popd >/dev/null | |
238 | rm -rf "${tmp_bootstrap_dir}" | |
239 | ||
240 | CHROOT_DIR="${bootstrap}" | |
241 | CHROOT_CMD="chroot ${CHROOT_DIR} " | |
242 | ||
243 | echo "Fedora bootstrap environment successfully created." | |
244 | return 0 | |
245 | } | |
246 | ||
247 | chroot_mounts() | |
248 | { | |
249 | test -n "${1}" && local chroot="${1}" || return 1 | |
250 | ||
251 | mount -t proc proc "${chroot}/proc" && | |
252 | mount -o bind /dev "${chroot}/dev" | |
253 | } | |
254 | ||
255 | chroot_umounts() | |
256 | { | |
257 | test -n "${1}" && local chroot="${1}" || return 1 | |
258 | ||
259 | umount "${chroot}/proc" && | |
260 | umount "${chroot}/dev" | |
261 | } | |
262 | ||
263 | clean_cache() | |
264 | { | |
265 | local cache="${1}" | |
266 | ||
267 | test ! -e "${cache}" && return 0 | |
268 | ||
269 | # lock, so we won't purge while someone is creating a repository | |
270 | ( | |
271 | if ! flock -x 9 | |
272 | then | |
273 | echo "Error: Cache repository is busy." | |
274 | exit 1 | |
275 | fi | |
276 | ||
277 | echo -n "Purging the Fedora bootstrap and download cache ... " | |
278 | rm --preserve-root --one-file-system -rf "${cache}" && echo "Done." || exit 1 | |
279 | ||
280 | exit 0 | |
281 | ||
282 | ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" | |
283 | ||
284 | return $? | |
285 | } | |
286 | ||
287 | # Customize container rootfs | |
288 | # | |
289 | configure_fedora() | |
290 | { | |
291 | local rootfs="${1}" | |
292 | local release="${2}" | |
293 | local utsname="${3}" | |
294 | ||
295 | # disable selinux | |
296 | mkdir -p "${rootfs}/selinux" | |
297 | echo 0 > "${rootfs}/selinux/enforce" | |
298 | ||
299 | # also kill it in the /etc/selinux/config file if it's there... | |
300 | if [ -f "${rootfs}/etc/selinux/config" ] | |
301 | then | |
302 | sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' "${rootfs}/etc/selinux/config" | |
303 | fi | |
304 | ||
305 | # nice catch from Dwight Engen in the Oracle template. | |
306 | # wantonly plagerized here with much appreciation. | |
307 | if [ -f "${rootfs}/usr/sbin/selinuxenabled" ] | |
308 | then | |
309 | rm -f "${rootfs}/usr/sbin/selinuxenabled" | |
310 | ln -s /bin/false "${rootfs}/usr/sbin/selinuxenabled" | |
311 | fi | |
312 | ||
313 | # set hostname | |
314 | echo "${utsname}" > "${rootfs}/etc/hostname" | |
315 | ||
316 | # set default localtime to the host localtime if not set... | |
317 | if [ -e /etc/localtime ] && [ ! -e "${rootfs}/etc/localtime" ] | |
318 | then | |
319 | # if /etc/localtime is a symlink, this should preserve it. | |
320 | cp -a /etc/localtime "${rootfs}/etc/localtime" | |
321 | fi | |
322 | ||
323 | # set minimal hosts | |
324 | cat <<EOF > "${rootfs}/etc/hosts" | |
325 | 127.0.0.1 localhost.localdomain localhost ${utsname} | |
326 | ::1 localhost6.localdomain6 localhost6 | |
327 | EOF | |
328 | ||
329 | # setup console and tty[1-4] for login. note that /dev/console and | |
330 | # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and | |
331 | # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. | |
332 | # lxc will maintain these links and bind mount ptys over /dev/lxc/* | |
333 | # since lxc.devttydir is specified in the config. | |
334 | ||
335 | # allow root login on console, tty[1-4], and pts/0 for libvirt | |
336 | cat <<EOF >> "${rootfs}/etc/securetty" | |
337 | # LXC (Linux Containers) | |
338 | lxc/console | |
339 | lxc/tty1 | |
340 | lxc/tty2 | |
341 | lxc/tty3 | |
342 | lxc/tty4 | |
343 | # For libvirt/Virtual Machine Monitor | |
344 | pts/0 | |
345 | EOF | |
346 | ||
347 | if [ "${root_display_password}" = yes ] | |
348 | then | |
349 | echo "Setting root password to '$root_password'" | |
350 | fi | |
351 | if [ "${root_store_password}" = yes ] | |
352 | then | |
353 | touch "${path}/tmp_root_pass" | |
354 | chmod 600 "${path}/tmp_root_pass" | |
355 | echo "${root_password}" > "${path}/tmp_root_pass" | |
356 | echo "Storing root password in '${path}/tmp_root_pass'" | |
357 | fi | |
358 | ||
359 | echo "root:$root_password" | chroot "${rootfs}" chpasswd | |
360 | ||
361 | if [ "${root_expire_password}" = yes ] | |
362 | then | |
363 | # also set this password as expired to force the user to change it! | |
364 | chroot "${rootfs}" passwd -e root | |
365 | fi | |
366 | ||
367 | chroot_mounts "${rootfs}" | |
368 | ||
369 | # always make sure /etc/resolv.conf is up to date in the target! | |
370 | cp /etc/resolv.conf "${rootfs}/etc/" | |
371 | ||
372 | # rebuild the rpm database based on the target rpm version... | |
373 | rm -f "${rootfs}"/var/lib/rpm/__db* | |
374 | chroot "${rootfs}" rpm --rebuilddb | |
375 | ||
376 | chroot_umounts "${rootfs}" | |
377 | ||
378 | # default systemd target | |
379 | chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target \ | |
380 | /etc/systemd/system/default.target | |
381 | ||
382 | # enable networking via systemd-networkd | |
383 | test -d "${rootfs}/etc/systemd/network" || mkdir "${rootfs}/etc/systemd/network" | |
384 | cat <<EOF > "${rootfs}/etc/systemd/network/eth0.network" | |
385 | [Match] | |
386 | Name=eth0 | |
387 | ||
388 | [Network] | |
389 | DHCP=both | |
390 | EOF | |
391 | mkdir -p "${rootfs}/etc/systemd/system/socket.target.wants" | |
392 | chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.socket \ | |
393 | /etc/systemd/system/socket.target.wants/systemd-networkd.socket | |
394 | chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.service \ | |
395 | /etc/systemd/system/multi-user.target.wants/systemd-networkd.service | |
396 | mkdir -p "${rootfs}/etc/systemd/system/network-online.target.wants" | |
397 | chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd-wait-online.service \ | |
398 | /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service | |
399 | ||
400 | # disable traditional network init | |
401 | chroot "${rootfs}" chkconfig network off | |
402 | ||
403 | # enable systemd-resolved | |
404 | rm -f "${rootfs}/etc/resolv.conf" | |
405 | chroot "${rootfs}" ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf | |
406 | chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-resolved.service \ | |
407 | /etc/systemd/system/multi-user.target.wants/systemd-resolved.service | |
408 | ||
409 | # if desired, prevent systemd from over-mounting /tmp with tmpfs | |
410 | if [ "${masktmp}" -eq 1 ] | |
411 | then | |
412 | chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/tmp.mount | |
413 | fi | |
414 | ||
415 | return 0 | |
416 | } | |
417 | ||
418 | copy_configuration() | |
419 | { | |
420 | local rootfs="${1}" | |
421 | local config="${2}" | |
422 | local utsname="${3}" | |
423 | ||
424 | # include configuration from default.conf if available | |
425 | grep -q "^lxc." "${lxc_default_conf}" > "${config}" 2>/dev/null | |
426 | ||
427 | grep -q "^lxc.rootfs" "${config}" 2>/dev/null || echo " | |
428 | lxc.rootfs = ${rootfs} | |
429 | " >> "${config}" | |
430 | ||
431 | # The following code is to create static MAC addresses for each | |
432 | # interface in the container. This code will work for multiple | |
433 | # interfaces in the default config. It will also strip any | |
434 | # hwaddr stanzas out of the default config since we can not share | |
435 | # MAC addresses between containers. | |
436 | mv "${config}" "${config}.orig" | |
437 | ||
438 | local line key | |
439 | while read -r line | |
440 | do | |
441 | # This should catch variable expansions from the default config... | |
442 | if expr "${line}" : '.*\$' > /dev/null 2>&1 | |
443 | then | |
444 | line=$(eval "echo \"${line}\"") | |
445 | fi | |
446 | ||
447 | # There is a tab and a space in the regex bracket below! | |
448 | # Seems that \s doesn't work in brackets. | |
449 | key=$(expr "${line}" : '\s*\([^ ]*\)\s*=') | |
450 | ||
451 | if [ "${key}" != "lxc.network.hwaddr" ] | |
452 | then | |
453 | echo "${line}" >> "${config}" | |
454 | ||
455 | if [ "${key}" == "lxc.network.link" ] | |
456 | then | |
457 | echo "lxc.network.hwaddr = $(create_hwaddr)" >> "${config}" | |
458 | fi | |
459 | fi | |
460 | done < "${config}.orig" | |
461 | rm -f "${config}.orig" | |
462 | ||
463 | if [ -e "${lxc_template_config}/fedora.common.conf" ] | |
464 | then | |
465 | echo " | |
466 | # Include common configuration | |
467 | lxc.include = ${lxc_template_config}/fedora.common.conf | |
468 | " >> "${config}" | |
469 | fi | |
470 | ||
471 | cat <<EOF >> "${path}/config" | |
472 | # Container specific configuration | |
473 | lxc.arch = ${basearch} | |
474 | lxc.utsname = ${utsname} | |
475 | ||
476 | # When using LXC with apparmor, uncomment the next line to run unconfined: | |
477 | #lxc.aa_profile = unconfined | |
478 | ||
479 | # example simple networking setup, uncomment to enable | |
480 | #lxc.network.type = ${lxc_network_type} | |
481 | #lxc.network.flags = up | |
482 | #lxc.network.link = ${lxc_network_link} | |
483 | #lxc.network.name = eth0 | |
484 | # Additional example for veth network type | |
485 | # static MAC address, | |
486 | #lxc.network.hwaddr = $(create_hwaddr) | |
487 | # persistent veth device name on host side | |
488 | # Note: This may potentially collide with other containers of same name! | |
489 | #lxc.network.veth.pair = v-${name}-e0 | |
490 | EOF | |
491 | ||
492 | if [ $? -ne 0 ] | |
493 | then | |
494 | echo "Failed to add configuration" | |
495 | return 1 | |
496 | fi | |
497 | ||
498 | return 0 | |
499 | } | |
500 | ||
501 | copy_fedora() | |
502 | { | |
503 | local cache="${1}" | |
504 | local rootfs="${2}" | |
505 | echo -n "Copying ${cache} to ${rootfs} ... " | |
506 | ||
507 | mkdir -p "${rootfs}" && | |
508 | rsync --archive --hard-links --sparse "${cache}/" "${rootfs}/" && | |
509 | echo || return 1 | |
510 | ||
511 | return 0 | |
512 | } | |
513 | ||
514 | # Generate a random hardware (MAC) address composed of FE followed by | |
515 | # 5 random bytes... | |
516 | # | |
517 | create_hwaddr() | |
518 | { | |
519 | openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' | |
520 | } | |
521 | ||
522 | # Make sure a fully functional rootfs of the requested release and architecture | |
523 | # will be setup in the given cache directory. If this is a Fedora host the | |
524 | # commands will run natively otherwise in a minimal Fedora bootstrap chroot. | |
525 | # | |
526 | download_fedora() | |
527 | { | |
528 | local cache_rootfs="${1}" | |
529 | local setup_rootfs="${cache_rootfs%%/rootfs}/partial" | |
530 | ||
531 | # suppress errors due to unknown locales | |
532 | LC_ALL=C | |
533 | ||
534 | echo "Downloading ${basearch} rootfs for Fedora ${release} ..." | |
535 | ||
536 | # The following variables are going to be overwritten if the rootfs setup | |
537 | # is run in a separate boostrap environment (can not build natively). | |
538 | # These are the defaults for the non-boostrap (native) mode. | |
539 | CHROOT_DIR= | |
540 | CHROOT_CMD= | |
541 | INSTALL_ROOT=${setup_rootfs} | |
542 | ||
7b40d728 | 543 | if [ ! "${is_fedora}" ] || [ "${fedora_host_ver}" -lt "${FEDORA_VERSION_MINIMAL}" ] |
3d7aa788 RG |
544 | then |
545 | # if this is not a supported Fedora host, use minimal bootstrap chroot | |
546 | echo "Non-Fedora host detected. Checking for bootstrap environment ... " | |
547 | if ! bootstrap_fedora "${cache}" | |
548 | then | |
549 | echo "Error: Fedora Bootstrap setup failed" | |
550 | return 1 | |
551 | fi | |
552 | echo "Using bootstrap environment at ${CHROOT_DIR}" | |
553 | fi | |
554 | ||
555 | if ! mkdir -p "${setup_rootfs}" | |
556 | then | |
557 | echo "Error: Failed to create '${setup_rootfs}' directory." | |
558 | return 1 | |
559 | fi | |
560 | ||
561 | trap revert_rootfs SIGHUP SIGINT SIGTERM | |
562 | ||
563 | mkdir -p "${setup_rootfs}/var/lib/rpm" | |
564 | ||
565 | # if the setup is going to be run in a chroot, mount the related file systems | |
566 | if [ -n "${CHROOT_DIR}" ] | |
567 | then | |
568 | chroot_mounts "${CHROOT_DIR}" | |
569 | ||
570 | # make sure rootfs is available in bootstrap chroot | |
571 | INSTALL_ROOT="/run/install" | |
572 | test -d "${CHROOT_DIR}${INSTALL_ROOT}" || mkdir -p "${CHROOT_DIR}${INSTALL_ROOT}" | |
573 | mount -o bind "${setup_rootfs}" "${CHROOT_DIR}${INSTALL_ROOT}" | |
574 | fi | |
575 | ||
576 | if ! setup_repositories "${cache}" "${basearch}" "${release}" "${mirror}" | |
577 | then | |
578 | echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" | |
579 | revert_rootfs >/dev/null | |
580 | return 1 | |
581 | fi | |
582 | ||
583 | # Unforunately <dnf-2.0 doesn't respect the repository configuration of the | |
584 | # installroot but use the one from the host. This obviously doesn't work | |
585 | # with a custom mirror or target architecture. Therefore a temporary dnf.conf | |
586 | # is created and passed to the chroot command. | |
587 | cat "${CHROOT_DIR}${INSTALL_ROOT}"/etc/yum.repos.d/{fedora,fedora-updates}.repo > "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" | |
588 | if [ -n "${mirror}" ] | |
589 | then | |
590 | set_dnf_mirror_url "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" | |
591 | fi | |
592 | ||
593 | # install minimal container file system | |
594 | local pkg_list="dnf initscripts passwd vim-minimal openssh-server openssh-clients dhclient rootfiles policycoreutils fedora-release fedora-repos" | |
595 | if ! ${CHROOT_CMD}dnf --installroot "${INSTALL_ROOT}" \ | |
596 | --config="${INSTALL_ROOT}/dnf.conf" \ | |
597 | --releasever "${release}" \ | |
598 | ${dnf_args[@]} \ | |
599 | install ${pkg_list} | |
600 | then | |
601 | echo "Error: Failed to setup the rootfs in ${CHROOT_DIR}${INSTALL_ROOT}." | |
602 | revert_rootfs >/dev/null | |
603 | return 1 | |
604 | fi | |
605 | ||
606 | unmount_installroot | |
607 | ||
608 | # from now on we'll work in the new rootfs | |
609 | chroot_mounts "${setup_rootfs}" | |
610 | ||
611 | # It might happen, that the dnf used above will write an incompatible | |
612 | # RPM database for the version running in the rootfs. Rebuild it. | |
613 | echo "Fixing up RPM databases" | |
614 | rm -f "${CHROOT_DIR}${INSTALL_ROOT}"/var/lib/rpm/__db* | |
615 | chroot "${setup_rootfs}" rpm --rebuilddb | |
616 | ||
617 | chroot_umounts "${setup_rootfs}" | |
618 | ||
619 | # reset traps | |
620 | trap SIGHUP | |
621 | trap SIGINT | |
622 | trap SIGTERM | |
623 | ||
624 | # use generated rootfs as future cache | |
625 | mv "${setup_rootfs}" "${cache_rootfs}" | |
626 | ||
627 | echo "Download of Fedora rootfs complete." | |
628 | return 0 | |
629 | } | |
630 | ||
631 | # Install a functional Fedora rootfs into the container root | |
632 | # | |
633 | install_fedora() | |
634 | { | |
635 | local rootfs="${1}" | |
636 | local cache="${2}" | |
637 | local cache_rootfs="${cache}/${release}-${basearch}/rootfs" | |
638 | ||
639 | mkdir -p "${local_state_dir}/lock/subsys/" | |
640 | ( | |
641 | if ! flock -x 9 | |
642 | then | |
643 | echo "Error: Cache repository is busy." | |
644 | return 1 | |
645 | fi | |
646 | ||
647 | echo "Checking cache download in ${cache_rootfs} ... " | |
648 | if [ ! -e "${cache_rootfs}" ] | |
649 | then | |
650 | if ! download_fedora "${cache_rootfs}" | |
651 | then | |
652 | echo "Error: Failed to download Fedora ${release} (${basearch})" | |
653 | return 1 | |
654 | fi | |
655 | else | |
656 | echo "Cache found at ${cache_rootfs}. Updating ..." | |
657 | if ! chroot_update_fedora "${cache_rootfs}" | |
658 | then | |
659 | echo "Failed to update cached rootfs, continuing with previously cached version." | |
660 | else | |
661 | echo "Fedora update finished." | |
662 | fi | |
663 | fi | |
664 | ||
665 | trap revert_container SIGHUP SIGINT SIGTERM | |
666 | ||
667 | if ! copy_fedora "${cache_rootfs}" "${rootfs}" | |
668 | then | |
669 | echo "Error: Failed to copy rootfs" | |
670 | return 1 | |
671 | fi | |
672 | ||
673 | chroot_mounts "${rootfs}" | |
674 | ||
675 | # install additional user provided packages | |
676 | if [ -n "${packages}" ] | |
677 | then | |
678 | # always make sure /etc/resolv.conf is up to date in the target! | |
679 | cp /etc/resolv.conf "${rootfs}/etc/" | |
680 | ||
681 | echo "Installing user requested RPMs: ${packages}" | |
c172e264 | 682 | if ! chroot "${rootfs}" dnf install ${dnf_args[@]} $(tr ',' ' ' <<< "${packages}") |
3d7aa788 RG |
683 | then |
684 | echo "Error: Installation of user provided packages failed." | |
685 | echo "Cleaning up ... " | |
686 | sleep 3 # wait for all file handles to properly close | |
687 | chroot_umounts "${setup_rootfs}" | |
688 | return 1 | |
689 | fi | |
690 | fi | |
691 | ||
692 | # cleanup dnf cache in new container | |
693 | chroot "${rootfs}" dnf clean all | |
694 | ||
695 | sleep 3 # wait for all file handles to properly close | |
696 | chroot_umounts "${rootfs}" | |
697 | ||
698 | return 0 | |
699 | ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" | |
700 | ||
701 | return $? | |
702 | } | |
703 | ||
704 | # Cleanup partial container | |
705 | # | |
706 | revert_container() | |
707 | { | |
708 | echo "Interrupted, so cleaning up ..." | |
709 | lxc-destroy -n "${name}" 2>/dev/null | |
710 | # maybe was interrupted before copy config, try to prevent some mistakes | |
711 | if [ -d "${path}" ] && | |
712 | [ "${path}" != "/" ] && [ "${path}" != "/tmp" ] && [ "${path}" != "/bin" ] | |
713 | then | |
714 | rm -rf "${path}" | |
715 | fi | |
716 | echo "Exiting ..." | |
717 | exit 1 | |
718 | } | |
719 | ||
720 | # Cleanup partial rootfs cache | |
721 | # | |
722 | revert_rootfs() | |
723 | { | |
724 | echo "Interrupted, so cleaning up ..." | |
725 | unmount_installroot | |
726 | rm -rf "${setup_rootfs}" | |
727 | echo "Exiting ..." | |
728 | exit 1 | |
729 | } | |
730 | ||
731 | # Set dnf repository mirror in given repo files | |
732 | # | |
733 | set_dnf_mirror_url() | |
734 | { | |
735 | sed -i -e 's/^\(metalink=.*\)$/#\1/g' "${@}" | |
736 | sed -i -e '/baseurl/ s|^#||g' "${@}" | |
737 | sed -i -e "/baseurl/ s|http://download.fedoraproject.org/pub/fedora|${mirror}|g" "${@}" | |
738 | } | |
739 | ||
740 | # Setup dnf repository configuration. It can be run in a chroot by specifying | |
741 | # $CHROOT_DIR (chroot directory) and $CHROOT_CMD (chroot command) and/or | |
742 | # with an alternative RPM install root defined in $INSTALL_ROOT. | |
743 | # | |
744 | setup_repositories() | |
745 | { | |
746 | local cache="${1}" | |
747 | local target_arch="${2}" | |
748 | local release="${3}" | |
749 | local mirror="${4}" | |
750 | ||
751 | # download repository packages if not found in cache | |
752 | pushd "${cache}" >/dev/null | |
753 | if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || | |
754 | [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] | |
755 | then | |
756 | # if no mirror given, get an appropriate mirror from the mirror list | |
757 | if [ -z "${mirror}" ] | |
758 | then | |
759 | for trynumber in 1 2 3 4 | |
760 | do | |
761 | [ "${trynumber}" != 1 ] && echo -n "Trying again ... " | |
762 | ||
763 | # choose some mirrors by parsing directory index | |
764 | mirror_urls=$(curl --silent --show-error --fail "${MIRRORLIST_URL}?repo=fedora-${release}&arch=${target_arch}" | sed -e '/^http:/!d' -e '2,6!d') | |
765 | ||
766 | # shellcheck disable=SC2181 | |
767 | if [ $? -eq 0 ] && [ -n "${mirror_urls}" ] | |
768 | then | |
769 | break | |
770 | fi | |
771 | ||
772 | echo "Warning: Failed to get a mirror on try ${trynumber}." | |
773 | sleep 3 | |
774 | done | |
775 | else | |
776 | # construct release-specific mirror url | |
777 | mirror="${mirror}/linux/releases/${release}/Everything/${target_arch}/os" | |
778 | fi | |
779 | ||
780 | # this will fall through if we didn't get any mirrors | |
781 | for mirror_url in ${mirror:-${mirror_urls}} | |
782 | do | |
783 | local release_url="${mirror_url}/Packages/f" | |
784 | ||
785 | for pkg in fedora-release-${release} fedora-repos-${release} | |
786 | do | |
787 | test -n "$(ls -1 ./${pkg}*.noarch.rpm 2>/dev/null)" && continue | |
788 | ||
789 | # query package name by parsing directory index | |
790 | echo "Requesting '${pkg}' package version from ${release_url}." | |
791 | pkg_name=$(curl --silent --show-error --fail "${release_url}/" | sed -n -e "/${pkg}/ s/.*href=\"\(${pkg}-.*\.noarch\.rpm\)\">.*/\1/p" | tail -1) | |
792 | ||
793 | # shellcheck disable=SC2181 | |
794 | if [ $? -ne 0 ] || [ -z "${pkg_name}" ] | |
795 | then | |
796 | echo "Error: Failed to get '${pkg}' version from ${release_url}/." | |
797 | continue | |
798 | fi | |
799 | ||
800 | echo "Downloading '${release_url}/${pkg_name} ... " | |
801 | if ! curl --silent --show-error --fail --remote-name "${release_url}/${pkg_name}" | |
802 | then | |
803 | echo "Error: Package download failed." | |
804 | continue | |
805 | fi | |
806 | done | |
807 | ||
808 | # if we have both packages continue | |
809 | if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || | |
810 | [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] | |
811 | then | |
812 | break | |
813 | fi | |
814 | done | |
815 | fi | |
816 | ||
817 | # copy packages to chroot file system | |
818 | if [ -n "${CHROOT_DIR}" ] | |
819 | then | |
820 | cp ./fedora-release-${release}*.noarch.rpm "${CHROOT_DIR}" && | |
821 | cp ./fedora-repos-${release}*.noarch.rpm "${CHROOT_DIR}" | |
822 | else | |
823 | local pkgdir="${cache}" | |
824 | fi | |
825 | ||
826 | # use '--nodeps' to work around 'fedora-release-24-*' bash dependency | |
827 | ${CHROOT_CMD}rpm --root "${INSTALL_ROOT}" -ivh --nodeps "${pkgdir}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} | |
828 | local ret=$? | |
829 | ||
830 | # dnf will take $basearch from host, so force the arch we want | |
831 | sed -i "s|\$basearch|${target_arch}|" ${CHROOT_DIR}${INSTALL_ROOT}/etc/yum.repos.d/* | |
832 | ||
833 | popd >/dev/null | |
834 | ||
835 | if [ "${ret}" -ne 0 ] | |
836 | then | |
837 | echo "Failed to setup repositories in ${CHROOT_DIR}${INSTALL_ROOT}" | |
838 | exit 1 | |
839 | fi | |
840 | ||
841 | # cleanup installed packages | |
842 | if [ -n "${CHROOT_DIR}" ] | |
843 | then | |
844 | # shellcheck disable=SC2086 | |
845 | rm -f "${CHROOT_DIR}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} | |
846 | fi | |
847 | ||
848 | return 0 | |
849 | } | |
850 | ||
851 | # Run dnf update in the given chroot directory. | |
852 | # | |
853 | chroot_update_fedora() | |
854 | { | |
855 | local chroot="${1}" | |
856 | chroot_mounts "${chroot}" | |
857 | ||
858 | # always make sure /etc/resolv.conf is up to date in the target! | |
859 | cp /etc/resolv.conf "${chroot}/etc/" | |
860 | ||
861 | chroot "${chroot}" dnf -y update | |
862 | local ret=$? | |
863 | ||
864 | sleep 3 # wait for all file handles to properly close | |
865 | ||
866 | chroot_umounts "${chroot}" | |
867 | ||
868 | return ${ret} | |
869 | } | |
870 | ||
871 | # Unmount installroot after bootstrapping or on error. | |
872 | # | |
873 | unmount_installroot() { | |
874 | if [ -n "${CHROOT_DIR}" ] | |
875 | then | |
876 | sleep 3 # wait for all file handles to properly close | |
877 | chroot_umounts "${CHROOT_DIR}" 2>/dev/null | |
878 | umount "${CHROOT_DIR}${INSTALL_ROOT}" 2>/dev/null | |
879 | fi | |
880 | } | |
881 | ||
882 | usage() | |
883 | { | |
884 | cat <<EOF | |
885 | LXC Container configuration for Fedora images. | |
886 | ||
887 | Template specific options can be passed to lxc-create after a '--' like this: | |
888 | ||
889 | lxc-create --name=NAME -t fedora [OPTION..] -- [TEMPLATE_OPTION..] | |
890 | ||
891 | Template options: | |
892 | ||
893 | -a, --arch Define what arch the container will be [i686,x86_64] | |
894 | -c, --clean Clean bootstrap and download cache | |
895 | -d, --debug Run with 'set -x' to debug errors | |
896 | --fqdn Fully qualified domain name (FQDN) | |
897 | -h, --help Print this help text | |
898 | --mask-tmp Prevent systemd from over-mounting /tmp with tmpfs. | |
899 | --mirror=MIRROR Fedora mirror to use during installation. Overrides the | |
900 | FEDORA_RSYNC_URL environment variable (see below). | |
901 | -p, --path=PATH Path to where the container will be created, | |
902 | defaults to ${lxc_path}. | |
903 | -P, --packages=PKGS Comma-separated list of additional RPM packages to | |
904 | install into the container. | |
905 | -R, --release=RELEASE Fedora release number of the container, defaults | |
906 | to host's release if the host is Fedora. | |
907 | --rootfs=ROOTFS Path for the actual container root file system | |
908 | ||
909 | Environment variables: | |
910 | ||
911 | LXC_CACHE_PATH Cache directory for image bootstrap. Defaults to | |
912 | '${LXC_CACHE_PATH}' | |
913 | ||
914 | MIRRORLIST_URL List of Fedora mirrors queried if no custom mirror is | |
915 | given. Defaults to '${MIRRORLIST_URL}' | |
916 | ||
917 | FEDORA_RSYNC_URL Fedora rsync mirror to use for bootstrap setup. | |
918 | Defaults to '${FEDORA_RSYNC_URL}' | |
919 | ||
920 | FEDORA_RELEASE_DEFAULT Set default Fedora release if not detected from the | |
921 | host. Is set to '${FEDORA_RELEASE_DEFAULT}' | |
922 | ||
923 | EOF | |
924 | return 0 | |
925 | } | |
926 | ||
927 | options=$(getopt -o a:hp:n:cR:dP: -l help,path:,rootfs:,name:,clean,release:,arch:,debug,fqdn:,mask-tmp,mirror:,packages: -- "$@") | |
928 | # shellcheck disable=SC2181 | |
929 | if [ $? -ne 0 ]; then | |
930 | usage | |
931 | exit 1 | |
932 | fi | |
933 | ||
934 | arch=$(uname -m) | |
935 | debug=0 | |
936 | masktmp=0 | |
937 | ||
938 | eval set -- "$options" | |
939 | while true | |
940 | do | |
941 | case "${1}" in | |
942 | -h|--help) usage; exit 0 ;; | |
943 | -n|--name) name="${2}"; shift 2 ;; | |
944 | -p|--path) path="${2}"; shift 2 ;; | |
945 | --rootfs) rootfs="${2}"; shift 2 ;; | |
946 | -a|--arch) newarch="${2}"; shift 2 ;; | |
947 | -c|--clean) clean=1; shift 1 ;; | |
948 | -d|--debug) debug=1; shift 1 ;; | |
949 | --fqdn) utsname="${2}"; shift 2 ;; | |
950 | --mask-tmp) masktmp=1; shift 1 ;; | |
951 | --mirror) mirror="${2}"; shift 2 ;; | |
952 | -P|--packages) packages="${2}"; shift 2 ;; | |
953 | -R|--release) release="${2}"; shift 2 ;; | |
954 | --) shift 1; break ;; | |
955 | *) break ;; | |
956 | esac | |
957 | done | |
958 | ||
959 | if [ "${debug}" -eq 1 ] | |
960 | then | |
961 | set -x | |
962 | fi | |
963 | ||
964 | # change to a safe directory | |
965 | cd || exit $? | |
966 | ||
967 | # Is this Fedora? | |
968 | # Allow for weird remixes like the Raspberry Pi | |
969 | # | |
970 | # Use the Mitre standard CPE identifier for the release ID if possible... | |
971 | # This may be in /etc/os-release or /etc/system-release-cpe. We | |
972 | # should be able to use EITHER. Give preference to /etc/os-release for now. | |
973 | ||
974 | if [ -e /etc/os-release ] | |
975 | then | |
976 | # This is a shell friendly configuration file. We can just source it. | |
977 | # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME | |
978 | . /etc/os-release | |
979 | fi | |
980 | ||
981 | if [ "${CPE_NAME}" = "" ] && [ -e /etc/system-release-cpe ] | |
982 | then | |
983 | CPE_NAME=$(head -n1 /etc/system-release-cpe) | |
984 | CPE_URI=$(expr "${CPE_NAME}" : '\([^:]*:[^:]*\)') | |
985 | if [ "${CPE_URI}" != "cpe:/o" ] | |
986 | then | |
987 | CPE_NAME= | |
988 | else | |
989 | echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" | |
990 | # Probably a better way to do this but sill remain posix | |
991 | # compatible but this works, shrug... | |
992 | # Must be nice and not introduce convenient bashisms here. | |
993 | ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') | |
994 | VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') | |
995 | fi | |
996 | fi | |
997 | ||
998 | if [ "${ID}" = "fedora" ] && [ -n "${CPE_NAME}" ] && [ -n "${VERSION_ID}" ] | |
999 | then | |
1000 | fedora_host_ver=${VERSION_ID} | |
1001 | is_fedora=true | |
1002 | fi | |
1003 | ||
1004 | basearch=${arch} | |
1005 | # Map a few architectures to their generic Fedora repository archs. | |
1006 | # The two ARM archs are a bit of a guesstimate for the v5 and v6 | |
1007 | # archs. V6 should have hardware floating point (Rasberry Pi). | |
1008 | # The "arm" arch is safer (no hardware floating point). So | |
1009 | # there may be cases where we "get it wrong" for some v6 other | |
1010 | # than RPi. | |
1011 | case "$arch" in | |
1012 | i686) basearch=i386 ;; | |
1013 | armv3l|armv4l|armv5l) basearch=arm ;; | |
1014 | armv6l|armv7l|armv8l) basearch=armhfp ;; | |
1015 | *) ;; | |
1016 | esac | |
1017 | ||
1018 | case "${basearch}" in | |
1019 | ppc64|s390x) FEDORA_RSYNC_URL="archives.fedoraproject.org::fedora-secondary" ;; | |
1020 | *) ;; | |
1021 | esac | |
1022 | ||
1023 | # Somebody wants to specify an arch. This is very limited case. | |
1024 | # i386/i586/i686 on i386/x86_64 | |
1025 | # - or - | |
1026 | # x86_64 on x86_64 | |
1027 | if [ "${newarch}" != "" ] && [ "${newarch}" != "${arch}" ] | |
1028 | then | |
1029 | case "${newarch}" in | |
1030 | i386|i586|i686) | |
1031 | if [ "${basearch}" = "i386" ] || [ "${basearch}" = "x86_64" ] | |
1032 | then | |
1033 | # Make the arch a generic x86 32 bit... | |
1034 | basearch=i386 | |
1035 | else | |
1036 | basearch=bad | |
1037 | fi | |
1038 | ;; | |
1039 | *) | |
1040 | basearch=bad | |
1041 | ;; | |
1042 | esac | |
1043 | ||
1044 | if [ "${basearch}" = "bad" ] | |
1045 | then | |
1046 | echo "Error: You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" | |
1047 | exit 1 | |
1048 | fi | |
1049 | fi | |
1050 | ||
1051 | # Let's do something better for the initial root password. | |
1052 | # It's not perfect but it will defeat common scanning brute force | |
1053 | # attacks in the case where ssh is exposed. It will also be set to | |
1054 | # expired, forcing the user to change it at first login. | |
1055 | if [ "${root_password}" = "" ] | |
1056 | then | |
1057 | root_password=Root-${name}-${RANDOM} | |
1058 | else | |
1059 | # If it's got a ding in it, try and expand it! | |
1060 | if [ "$(expr "${root_password}" : '.*$.')" != 0 ] | |
1061 | then | |
1062 | root_password=$(eval echo "${root_password}") | |
1063 | fi | |
1064 | ||
1065 | # If it has more than 3 consecutive X's in it, feed it | |
1066 | # through mktemp as a template. | |
1067 | if [ "$(expr "${root_password}" : '.*XXXX')" != 0 ] | |
1068 | then | |
1069 | root_password=$(mktemp -u "${root_password}") | |
1070 | fi | |
1071 | fi | |
1072 | ||
1073 | if [ -z "${utsname}" ]; then | |
1074 | utsname=${name} | |
1075 | fi | |
1076 | ||
1077 | # This follows a standard "resolver" convention that an FQDN must have | |
1078 | # at least two dots or it is considered a local relative host name. | |
1079 | # If it doesn't, append the dns domain name of the host system. | |
1080 | # | |
1081 | # This changes one significant behavior when running | |
1082 | # "lxc_create -n Container_Name" without using the | |
1083 | # --fqdn option. | |
1084 | # | |
1085 | # Old behavior: | |
1086 | # utsname and hostname = Container_Name | |
1087 | # New behavior: | |
1088 | # utsname and hostname = Container_Name.Domain_Name | |
1089 | ||
1090 | if [ "$(expr "${utsname}" : '.*\..*\.')" = 0 ] | |
1091 | then | |
1092 | if [ -n "$(dnsdomainname)" ] && [ "$(dnsdomainname)" != "localdomain" ] | |
1093 | then | |
1094 | utsname="${utsname}.$(dnsdomainname)" | |
1095 | fi | |
1096 | fi | |
1097 | ||
1098 | # check if the pre-requisite binaries are available | |
1099 | prerequisite_pkgs=( curl openssl rsync ) | |
1100 | needed_pkgs="" | |
1101 | for pkg in "${prerequisite_pkgs[@]}" | |
1102 | do | |
1103 | if ! type "${pkg}" >/dev/null 2>&1 | |
1104 | then | |
1105 | needed_pkgs="${pkg} ${needed_pkgs}" | |
1106 | fi | |
1107 | done | |
1108 | if [ -n "${needed_pkgs}" ] | |
1109 | then | |
1110 | echo "Error: Missing command(s): ${needed_pkgs}" | |
1111 | exit 1 | |
1112 | fi | |
1113 | ||
1114 | if [ "$(id -u)" != "0" ] | |
1115 | then | |
1116 | echo "This script should be run as 'root'" | |
1117 | exit 1 | |
1118 | fi | |
1119 | ||
1120 | # cleanup cache if requested | |
1121 | cache="${LXC_CACHE_PATH}/fedora" | |
1122 | if [ -n "${clean}" ] | |
1123 | then | |
1124 | clean_cache "${cache}" || exit 1 | |
1125 | exit 0 | |
1126 | fi | |
1127 | ||
1128 | # set container directory | |
1129 | if [ -z "${path}" ] | |
1130 | then | |
1131 | path="${lxc_path}/${name}" | |
1132 | fi | |
1133 | ||
1134 | # set container rootfs and configuration path | |
1135 | config="${path}/config" | |
1136 | if [ -z "${rootfs}" ] | |
1137 | then | |
1138 | # check for 'lxc.rootfs' passed in through default config by lxc-create | |
1139 | if grep -q '^lxc.rootfs' "${config}" 2>/dev/null | |
1140 | then | |
1141 | rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' "${config}") | |
1142 | else | |
1143 | rootfs="${path}/rootfs" | |
1144 | fi | |
1145 | fi | |
1146 | ||
1147 | # set release if not given | |
1148 | if [ -z "${release}" ] | |
1149 | then | |
1150 | if [ "${is_fedora}" ] && [ -n "${fedora_host_ver}" ] | |
1151 | then | |
1152 | echo "Detected Fedora ${fedora_host_ver} host. Set release to ${fedora_host_ver}." | |
1153 | release="${fedora_host_ver}" | |
1154 | else | |
1155 | echo "This is not a Fedora host or release is missing, defaulting release to ${FEDORA_RELEASE_DEFAULT}." | |
1156 | release="${FEDORA_RELEASE_DEFAULT}" | |
1157 | fi | |
1158 | fi | |
1159 | if [ "${release}" -lt "${FEDORA_RELEASE_MIN}" ] | |
1160 | then | |
1161 | echo "Error: Fedora release ${release} is not supported. Set -R at least to ${FEDORA_RELEASE_MIN}." | |
1162 | exit 1 | |
1163 | fi | |
1164 | ||
1165 | # bootstrap rootfs and copy to container file system | |
1166 | if ! install_fedora "${rootfs}" "${cache}" | |
1167 | then | |
1168 | echo "Error: Failed to create Fedora container" | |
1169 | exit 1 | |
1170 | fi | |
1171 | ||
1172 | # customize container file system | |
1173 | if ! configure_fedora "${rootfs}" "${release}" "${utsname}" | |
1174 | then | |
1175 | echo "Error: Failed to configure Fedora container" | |
1176 | exit 1 | |
1177 | fi | |
1178 | ||
1179 | # create container configuration (will be overwritten by newer lxc-create) | |
1180 | if ! copy_configuration "${rootfs}" "${config}" "${utsname}" | |
1181 | then | |
1182 | echo "Error: Failed write container configuration file" | |
1183 | exit 1 | |
1184 | fi | |
1185 | ||
1186 | if [ -n "${clean}" ]; then | |
1187 | clean || exit 1 | |
1188 | exit 0 | |
1189 | fi | |
1190 | ||
1191 | echo "Successfully created container '${name}'" | |
1192 | ||
1193 | if [ "${root_display_password}" = "yes" ] | |
1194 | then | |
1195 | echo "The temporary password for root is: '$root_password' | |
1196 | ||
1197 | You may want to note that password down before starting the container. | |
1198 | " | |
1199 | fi | |
1200 | ||
1201 | if [ "${root_store_password}" = "yes" ] | |
1202 | then | |
1203 | echo "The temporary root password is stored in: | |
1204 | ||
1205 | '${config}/tmp_root_pass' | |
1206 | " | |
1207 | fi | |
1208 | ||
1209 | if [ "${root_prompt_password}" = "yes" ] | |
1210 | then | |
1211 | echo "Invoking the passwd command in the container to set the root password. | |
1212 | ||
1213 | chroot ${rootfs} passwd | |
1214 | " | |
1215 | chroot "${rootfs}" passwd | |
1216 | else | |
1217 | if [ "${root_expire_password}" = "yes" ] | |
1218 | then | |
1219 | if ( mountpoint -q -- "${rootfs}" ) | |
1220 | then | |
1221 | echo "To reset the root password, you can do: | |
1222 | ||
1223 | lxc-start -n ${name} | |
1224 | lxc-attach -n ${name} -- passwd | |
1225 | lxc-stop -n ${name} | |
1226 | " | |
1227 | else | |
1228 | echo " | |
1229 | The root password is set up as expired and will require it to be changed | |
1230 | at first login, which you should do as soon as possible. If you lose the | |
1231 | root password or wish to change it without starting the container, you | |
1232 | can change it from the host by running the following command (which will | |
1233 | also reset the expired flag): | |
1234 | ||
1235 | chroot ${rootfs} passwd | |
1236 | " | |
1237 | fi | |
1238 | fi | |
1239 | fi | |
1240 | ||
1241 | # vim: set ts=4 sw=4 expandtab: |