]>
Commit | Line | Data |
---|---|---|
f6267d90 AV |
1 | #!/bin/bash |
2 | ||
3 | # | |
c194ffc1 | 4 | # template script for generating Arch Linux container for LXC |
f6267d90 AV |
5 | # |
6 | ||
7 | # | |
8 | # lxc: linux Container library | |
9 | ||
10 | # Authors: | |
c194ffc1 | 11 | # Alexander Vladimirov <alexander.idkfa.vladimirov@gmail.com> |
d0800999 | 12 | # John Lane <lxc@jelmail.com> |
f6267d90 AV |
13 | |
14 | # This library is free software; you can redistribute it and/or | |
15 | # modify it under the terms of the GNU Lesser General Public | |
16 | # License as published by the Free Software Foundation; either | |
17 | # version 2.1 of the License, or (at your option) any later version. | |
18 | ||
19 | # This library is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
22 | # Lesser General Public License for more details. | |
23 | ||
24 | # You should have received a copy of the GNU Lesser General Public | |
25 | # License along with this library; if not, write to the Free Software | |
250b1eec | 26 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
f6267d90 | 27 | |
8ec981fc | 28 | # Detect use under userns (unsupported) |
c63c04fc | 29 | for arg in "$@"; do |
96283b54 SG |
30 | [ "$arg" = "--" ] && break |
31 | if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
8ec981fc SG |
32 | echo "This template can't be used for unprivileged containers." 1>&2 |
33 | echo "You may want to try the \"download\" template instead." 1>&2 | |
34 | exit 1 | |
35 | fi | |
36 | done | |
37 | ||
207bf0e4 SG |
38 | # Make sure the usual locations are in PATH |
39 | export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
40 | ||
f6267d90 | 41 | # defaults |
f3849d01 | 42 | arch=$(uname -m) |
f3849d01 AV |
43 | default_path="@LXCPATH@" |
44 | default_locale="en-US.UTF-8" | |
45 | default_timezone="UTC" | |
14831534 | 46 | pacman_config="/etc/pacman.conf" |
23cc88ba | 47 | common_config="@LXCTEMPLATECONFIG@/common.conf" |
c194ffc1 | 48 | shared_config="@LXCTEMPLATECONFIG@/archlinux.common.conf" |
f6267d90 | 49 | |
b7b7d388 LI |
50 | # by default, install 'base' except the kernel |
51 | pkg_blacklist="linux" | |
52 | base_packages=() | |
53 | for pkg in $(pacman -Sqg base); do | |
54 | [ "${pkg_blacklist#*$pkg}" = "$pkg_blacklist" ] && base_packages+=($pkg) | |
55 | done | |
f6267d90 AV |
56 | declare -a additional_packages |
57 | ||
f6267d90 AV |
58 | # split comma-separated string into an array |
59 | # ${1} - string to split | |
60 | # ${2} - separator (default is ",") | |
61 | # ${result} - result value on success | |
17abf278 | 62 | split_string() { |
f6267d90 AV |
63 | local ifs=${IFS} |
64 | IFS="${2:-,}" | |
65 | read -a result < <(echo "${1}") | |
66 | IFS=${ifs} | |
67 | return 0 | |
68 | } | |
69 | ||
f3849d01 AV |
70 | [ -f /etc/arch-release ] && is_arch=true |
71 | ||
f6267d90 | 72 | # Arch-specific preconfiguration for container |
17abf278 | 73 | configure_arch() { |
21ca73b9 | 74 | # on ArchLinux, read defaults from host systemd configuration |
f6267d90 | 75 | if [ "${is_arch}" ]; then |
c194ffc1 | 76 | cp -p /etc/locale.conf /etc/locale.gen "${rootfs_path}/etc/" |
f6267d90 | 77 | else |
8fefbee4 | 78 | echo "LANG=${default_locale}" > "${rootfs_path}/etc/locale.conf" |
f3849d01 AV |
79 | if [ -e "${rootfs_path}/etc/locale.gen" ]; then |
80 | sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" | |
81 | if [ ! "${default_locale}" = "en_US.UTF-8" ]; then | |
c194ffc1 AV |
82 | echo "${default_locale} ${default_locale##*.}" >> \ |
83 | "${rootfs_path}/etc/locale.gen" | |
f3849d01 | 84 | fi |
f6267d90 | 85 | fi |
f6267d90 | 86 | fi |
21ca73b9 LI |
87 | |
88 | # hostname and nameservers | |
f3849d01 | 89 | echo "${name}" > "${rootfs_path}/etc/hostname" |
21ca73b9 LI |
90 | while read r; do |
91 | [ "${r#nameserver}" = "$r" ] || echo "$r" | |
92 | done < /etc/resolv.conf > "${rootfs_path}/etc/resolv.conf" | |
f6267d90 | 93 | |
b6a72639 SG |
94 | # network configuration |
95 | cat > "${rootfs_path}/etc/systemd/network/eth0.network" << EOF | |
96 | [Match] | |
97 | Name=eth0 | |
98 | ||
99 | [Network] | |
100 | DHCP=ipv4 | |
101 | EOF | |
102 | ||
21ca73b9 | 103 | # chroot and configure system |
f3849d01 AV |
104 | arch-chroot "${rootfs_path}" /bin/bash -s << EOF |
105 | mkdir /run/lock | |
106 | locale-gen | |
107 | ln -s /usr/share/zoneinfo/${default_timezone} /etc/localtime | |
c194ffc1 | 108 | # set default boot target |
f3849d01 | 109 | ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target |
c194ffc1 AV |
110 | # override getty@.service for container ttys |
111 | sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
112 | -e 's/After=dev-%i.device/After=/' \ | |
113 | < /lib/systemd/system/getty\@.service \ | |
114 | > /etc/systemd/system/getty\@.service | |
427d4293 JS |
115 | # fix systemd-sysctl service |
116 | sed -e 's/^ConditionPathIsReadWrite=\/proc\/sys\/$/ConditionPathIsReadWrite=\/proc\/sys\/net\//' \ | |
117 | -e 's/^ExecStart=\/usr\/lib\/systemd\/systemd-sysctl$/ExecStart=\/usr\/lib\/systemd\/systemd-sysctl --prefix net/' \ | |
118 | -i /usr/lib/systemd/system/systemd-sysctl.service | |
99cbd299 AV |
119 | # initialize pacman keyring |
120 | pacman-key --init | |
121 | pacman-key --populate archlinux | |
b6a72639 SG |
122 | |
123 | # enable networkd | |
124 | systemctl enable systemd-networkd | |
f3849d01 | 125 | EOF |
c194ffc1 | 126 | # enable getty on active ttys |
23cc88ba AV |
127 | local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") |
128 | local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.devttydir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") | |
129 | local devtty="" | |
130 | # bind getty instances to /dev/<devttydir>/tty* if lxc.devttydir is set | |
131 | [ -n "${devttydir}" ] && devtty="${devttydir}-" | |
c194ffc1 | 132 | if [ ${nttys:-0} -gt 1 ]; then |
23cc88ba AV |
133 | ( cd "${rootfs_path}/etc/systemd/system/getty.target.wants" |
134 | for i in $(seq 1 $nttys); do ln -sf "../getty@.service" "getty@${devtty}tty${i}.service"; done ) | |
c194ffc1 | 135 | fi |
23cc88ba AV |
136 | # update securetty to allow console login if devttydir is set |
137 | if [ -n "${devttydir}" ]; then | |
138 | for i in $(seq 1 ${nttys:-1}); do | |
139 | echo "${devttydir}/tty${i}" >> "${rootfs_path}/etc/securetty" | |
140 | done | |
141 | fi | |
142 | [ -n "${devttydir}" ] && echo "${devttydir}/console" >> "${rootfs_path}/etc/securetty" | |
143 | # Arch default configuration allows only tty1-6 for login | |
c194ffc1 AV |
144 | [ ${nttys:-0} -gt 6 ] && echo \ |
145 | "You may want to modify container's /etc/securetty \ | |
146 | file to allow root logins on tty7 and higher" | |
f6267d90 AV |
147 | return 0 |
148 | } | |
149 | ||
150 | # write container configuration files | |
17abf278 | 151 | copy_configuration() { |
f6267d90 | 152 | mkdir -p "${config_path}" |
c194ffc1 AV |
153 | local config="${config_path}/config" |
154 | echo "lxc.utsname = ${name}" >> "${config}" | |
155 | grep -q "^lxc.arch" "${config}" 2>/dev/null \ | |
156 | || echo "lxc.arch = ${arch}" >> "${config}" | |
157 | grep -q "^lxc.rootfs" "${config}" 2>/dev/null \ | |
158 | || echo "lxc.rootfs = ${rootfs_path}" >> "${config}" | |
159 | [ -e "${shared_config}" ] \ | |
160 | && echo "lxc.include = ${shared_config}" >> "${config}" | |
161 | if [ $? -ne 0 ]; then | |
162 | echo "Failed to configure container" | |
163 | return 1 | |
164 | fi | |
f6267d90 AV |
165 | return 0 |
166 | } | |
167 | ||
f6267d90 | 168 | # install packages within container chroot |
17abf278 | 169 | install_arch() { |
734d0bed JL |
170 | [ "${arch}" != "$(uname -m)" ] && different_arch=true |
171 | ||
172 | if [ "${different_arch}" = "true" ]; then | |
173 | container_pacman_config=$(mktemp) | |
174 | container_mirrorlist=$(mktemp) | |
175 | sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
176 | -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ | |
177 | "${pacman_config}" > "${container_pacman_config}" | |
178 | sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ | |
179 | /etc/pacman.d/mirrorlist > "${container_mirrorlist}" | |
180 | ||
181 | pacman_config="${container_pacman_config}" | |
182 | fi | |
183 | ||
44b03630 | 184 | if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ |
c194ffc1 | 185 | ${base_packages[@]}; then |
f6267d90 AV |
186 | echo "Failed to install container packages" |
187 | return 1 | |
188 | fi | |
734d0bed JL |
189 | |
190 | if [ "${different_arch}" = "true" ]; then | |
191 | sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
192 | "${rootfs_path}"/etc/pacman.conf | |
193 | cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist | |
194 | rm "${container_pacman_config}" "${container_mirrorlist}" | |
195 | fi | |
196 | ||
f6267d90 | 197 | [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" |
f6267d90 AV |
198 | return 0 |
199 | } | |
200 | ||
f3849d01 | 201 | usage() { |
f6267d90 AV |
202 | cat <<EOF |
203 | usage: | |
99cbd299 AV |
204 | ${1} -n|--name=<container_name> [-p|--path=<path>] [-a|--arch=<arch of the container>] |
205 | [-r|--root_password=<root password>] [-P|--packages=<pkg1,pkg2,...>] | |
206 | [-e|--enable_units=unit1,unit2...] [-d|--disable_units=unit1,unit2...] | |
207 | [-c|--config=<pacman config path>] [-h|--help] | |
c194ffc1 | 208 | |
f6267d90 | 209 | Mandatory args: |
c194ffc1 | 210 | -n,--name container name, used to as an identifier for that container from now on |
f6267d90 | 211 | Optional args: |
c194ffc1 | 212 | -p,--path path to where the container rootfs will be created (${default_path}) |
69cbc333 | 213 | --rootfs path for actual container rootfs, (${default_path}/rootfs) |
c194ffc1 AV |
214 | -P,--packages preinstall additional packages, comma-separated list |
215 | -e,--enable_units enable systemd services, comma-separated list | |
216 | -d,--disable_units disable systemd services, comma-separated list | |
217 | -c,--config use specified pacman config when installing container packages | |
218 | -a,--arch use specified architecture instead of host's architecture | |
219 | -r,--root_password set container root password | |
220 | -h,--help print this help | |
f6267d90 AV |
221 | EOF |
222 | return 0 | |
223 | } | |
224 | ||
c194ffc1 | 225 | options=$(getopt -o hp:P:e:d:n:c:a:r: -l help,rootfs:,path:,packages:,enable_units:,disable_units:,name:,config:,arch:,root_password: -- "${@}") |
f6267d90 AV |
226 | if [ ${?} -ne 0 ]; then |
227 | usage $(basename ${0}) | |
228 | exit 1 | |
229 | fi | |
230 | eval set -- "${options}" | |
231 | ||
232 | while true | |
233 | do | |
234 | case "${1}" in | |
f3849d01 AV |
235 | -h|--help) usage ${0} && exit 0;; |
236 | -p|--path) path=${2}; shift 2;; | |
237 | -n|--name) name=${2}; shift 2;; | |
1897e3bc | 238 | --rootfs) rootfs_path=${2}; shift 2;; |
f3849d01 | 239 | -P|--packages) additional_packages=${2}; shift 2;; |
012f591a | 240 | -e|--enable_units) enable_units=${2}; shift 2;; |
c194ffc1 | 241 | -d|--disable_units) disable_units=${2}; shift 2;; |
14831534 | 242 | -c|--config) pacman_config=${2}; shift 2;; |
734d0bed | 243 | -a|--arch) arch=${2}; shift 2;; |
c194ffc1 | 244 | -r|--root_password) root_passwd=${2}; shift 2;; |
f6267d90 AV |
245 | --) shift 1; break ;; |
246 | *) break ;; | |
247 | esac | |
248 | done | |
249 | ||
250 | if [ -z "${name}" ]; then | |
251 | echo "missing required 'name' parameter" | |
252 | exit 1 | |
253 | fi | |
254 | ||
255 | type pacman >/dev/null 2>&1 | |
256 | if [ ${?} -ne 0 ]; then | |
257 | echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" | |
258 | exit 1 | |
259 | fi | |
260 | ||
261 | if [ -z "${path}" ]; then | |
262 | path="${default_path}/${name}" | |
263 | fi | |
264 | ||
265 | if [ "${EUID}" != "0" ]; then | |
266 | echo "This script should be run as 'root'" | |
267 | exit 1 | |
268 | fi | |
269 | ||
1897e3bc SH |
270 | if [ -z "$rootfs_path" ]; then |
271 | rootfs_path="${path}/rootfs" | |
272 | fi | |
c194ffc1 | 273 | config_path="${path}" |
f6267d90 | 274 | |
f3849d01 AV |
275 | revert() { |
276 | echo "Interrupted, cleaning up" | |
f6267d90 | 277 | lxc-destroy -n "${name}" |
f6267d90 AV |
278 | rm -rf "${path}/${name}" |
279 | rm -rf "${default_path}/${name}" | |
280 | exit 1 | |
281 | } | |
282 | ||
283 | trap revert SIGHUP SIGINT SIGTERM | |
284 | ||
285 | copy_configuration | |
286 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 287 | echo "failed to write configuration file" |
f6267d90 AV |
288 | rm -rf "${config_path}" |
289 | exit 1 | |
290 | fi | |
291 | ||
292 | if [ ${#additional_packages[@]} -gt 0 ]; then | |
293 | split_string ${additional_packages} | |
294 | base_packages+=(${result[@]}) | |
295 | fi | |
296 | ||
f3849d01 | 297 | mkdir -p "${rootfs_path}" |
f6267d90 AV |
298 | install_arch |
299 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 300 | echo "failed to install Arch Linux" |
f6267d90 AV |
301 | rm -rf "${config_path}" "${path}" |
302 | exit 1 | |
303 | fi | |
304 | ||
305 | configure_arch | |
306 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 307 | echo "failed to configure Arch Linux for a container" |
f6267d90 AV |
308 | rm -rf "${config_path}" "${path}" |
309 | exit 1 | |
310 | fi | |
311 | ||
012f591a JL |
312 | if [ ${#enable_units[@]} -gt 0 ]; then |
313 | split_string ${enable_units} | |
314 | for unit in ${result[@]}; do | |
c194ffc1 AV |
315 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" |
316 | ln -s "/usr/lib/systemd/system/${unit}" \ | |
317 | "${rootfs_path}/etc/systemd/system/multi-user.target.wants/" | |
318 | done | |
319 | fi | |
320 | ||
321 | if [ ${#disable_units[@]} -gt 0 ]; then | |
322 | split_string ${disable_units} | |
323 | for unit in ${result[@]}; do | |
324 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" | |
325 | ln -s /dev/null "${rootfs_path}/etc/systemd/system/${unit}" | |
012f591a JL |
326 | done |
327 | fi | |
328 | ||
d0800999 JL |
329 | if [ -n "${root_passwd}" ]; then |
330 | echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd | |
331 | fi | |
332 | ||
21ca73b9 | 333 | cat << EOF |
c194ffc1 | 334 | Arch Linux container ${name} is successfully created! The configuration is |
21ca73b9 | 335 | stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for |
c194ffc1 | 336 | information about configuring Arch Linux. |
21ca73b9 | 337 | EOF |