]>
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 | 43 | default_path="@LXCPATH@" |
c86234b6 | 44 | default_locale="en_US.UTF-8" |
14831534 | 45 | pacman_config="/etc/pacman.conf" |
23cc88ba | 46 | common_config="@LXCTEMPLATECONFIG@/common.conf" |
c194ffc1 | 47 | shared_config="@LXCTEMPLATECONFIG@/archlinux.common.conf" |
f6267d90 | 48 | |
b7b7d388 LI |
49 | # by default, install 'base' except the kernel |
50 | pkg_blacklist="linux" | |
51 | base_packages=() | |
52 | for pkg in $(pacman -Sqg base); do | |
53 | [ "${pkg_blacklist#*$pkg}" = "$pkg_blacklist" ] && base_packages+=($pkg) | |
54 | done | |
f6267d90 AV |
55 | declare -a additional_packages |
56 | ||
f6267d90 AV |
57 | # split comma-separated string into an array |
58 | # ${1} - string to split | |
59 | # ${2} - separator (default is ",") | |
60 | # ${result} - result value on success | |
17abf278 | 61 | split_string() { |
f6267d90 AV |
62 | local ifs=${IFS} |
63 | IFS="${2:-,}" | |
64 | read -a result < <(echo "${1}") | |
65 | IFS=${ifs} | |
66 | return 0 | |
67 | } | |
68 | ||
f3849d01 AV |
69 | [ -f /etc/arch-release ] && is_arch=true |
70 | ||
f6267d90 | 71 | # Arch-specific preconfiguration for container |
17abf278 | 72 | configure_arch() { |
21ca73b9 | 73 | # on ArchLinux, read defaults from host systemd configuration |
f6267d90 | 74 | if [ "${is_arch}" ]; then |
c194ffc1 | 75 | cp -p /etc/locale.conf /etc/locale.gen "${rootfs_path}/etc/" |
f6267d90 | 76 | else |
8fefbee4 | 77 | echo "LANG=${default_locale}" > "${rootfs_path}/etc/locale.conf" |
f3849d01 AV |
78 | if [ -e "${rootfs_path}/etc/locale.gen" ]; then |
79 | sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" | |
80 | if [ ! "${default_locale}" = "en_US.UTF-8" ]; then | |
c194ffc1 AV |
81 | echo "${default_locale} ${default_locale##*.}" >> \ |
82 | "${rootfs_path}/etc/locale.gen" | |
f3849d01 | 83 | fi |
f6267d90 | 84 | fi |
f6267d90 | 85 | fi |
21ca73b9 LI |
86 | |
87 | # hostname and nameservers | |
f3849d01 | 88 | echo "${name}" > "${rootfs_path}/etc/hostname" |
f6267d90 | 89 | |
b6a72639 SG |
90 | # network configuration |
91 | cat > "${rootfs_path}/etc/systemd/network/eth0.network" << EOF | |
92 | [Match] | |
93 | Name=eth0 | |
94 | ||
95 | [Network] | |
96 | DHCP=ipv4 | |
97 | EOF | |
98 | ||
21ca73b9 | 99 | # chroot and configure system |
f3849d01 AV |
100 | arch-chroot "${rootfs_path}" /bin/bash -s << EOF |
101 | mkdir /run/lock | |
102 | locale-gen | |
c194ffc1 | 103 | # set default boot target |
f3849d01 | 104 | ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target |
c194ffc1 AV |
105 | # override getty@.service for container ttys |
106 | sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
107 | -e 's/After=dev-%i.device/After=/' \ | |
108 | < /lib/systemd/system/getty\@.service \ | |
109 | > /etc/systemd/system/getty\@.service | |
427d4293 JS |
110 | # fix systemd-sysctl service |
111 | sed -e 's/^ConditionPathIsReadWrite=\/proc\/sys\/$/ConditionPathIsReadWrite=\/proc\/sys\/net\//' \ | |
112 | -e 's/^ExecStart=\/usr\/lib\/systemd\/systemd-sysctl$/ExecStart=\/usr\/lib\/systemd\/systemd-sysctl --prefix net/' \ | |
113 | -i /usr/lib/systemd/system/systemd-sysctl.service | |
99cbd299 AV |
114 | # initialize pacman keyring |
115 | pacman-key --init | |
116 | pacman-key --populate archlinux | |
b6a72639 SG |
117 | |
118 | # enable networkd | |
119 | systemctl enable systemd-networkd | |
137162e8 SG |
120 | systemctl enable systemd-resolved |
121 | ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf | |
f3849d01 | 122 | EOF |
c194ffc1 | 123 | # enable getty on active ttys |
fe1c5887 | 124 | local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.max" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") |
42e53c29 | 125 | local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.dir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") |
23cc88ba | 126 | local devtty="" |
42e53c29 | 127 | # bind getty instances to /dev/<devttydir>/tty* if lxc.tty.dir is set |
23cc88ba | 128 | [ -n "${devttydir}" ] && devtty="${devttydir}-" |
c194ffc1 | 129 | if [ ${nttys:-0} -gt 1 ]; then |
23cc88ba AV |
130 | ( cd "${rootfs_path}/etc/systemd/system/getty.target.wants" |
131 | for i in $(seq 1 $nttys); do ln -sf "../getty@.service" "getty@${devtty}tty${i}.service"; done ) | |
c194ffc1 | 132 | fi |
23cc88ba AV |
133 | # update securetty to allow console login if devttydir is set |
134 | if [ -n "${devttydir}" ]; then | |
135 | for i in $(seq 1 ${nttys:-1}); do | |
136 | echo "${devttydir}/tty${i}" >> "${rootfs_path}/etc/securetty" | |
137 | done | |
138 | fi | |
139 | [ -n "${devttydir}" ] && echo "${devttydir}/console" >> "${rootfs_path}/etc/securetty" | |
140 | # Arch default configuration allows only tty1-6 for login | |
c194ffc1 AV |
141 | [ ${nttys:-0} -gt 6 ] && echo \ |
142 | "You may want to modify container's /etc/securetty \ | |
143 | file to allow root logins on tty7 and higher" | |
f6267d90 AV |
144 | return 0 |
145 | } | |
146 | ||
147 | # write container configuration files | |
17abf278 | 148 | copy_configuration() { |
f6267d90 | 149 | mkdir -p "${config_path}" |
c194ffc1 | 150 | local config="${config_path}/config" |
b67771bc | 151 | echo "lxc.uts.name = ${name}" >> "${config}" |
c194ffc1 AV |
152 | grep -q "^lxc.arch" "${config}" 2>/dev/null \ |
153 | || echo "lxc.arch = ${arch}" >> "${config}" | |
7a96a068 CB |
154 | grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null \ |
155 | || echo "lxc.rootfs.path = ${rootfs_path}" >> "${config}" | |
c194ffc1 AV |
156 | [ -e "${shared_config}" ] \ |
157 | && echo "lxc.include = ${shared_config}" >> "${config}" | |
158 | if [ $? -ne 0 ]; then | |
159 | echo "Failed to configure container" | |
160 | return 1 | |
161 | fi | |
f6267d90 AV |
162 | return 0 |
163 | } | |
164 | ||
f6267d90 | 165 | # install packages within container chroot |
17abf278 | 166 | install_arch() { |
734d0bed JL |
167 | [ "${arch}" != "$(uname -m)" ] && different_arch=true |
168 | ||
169 | if [ "${different_arch}" = "true" ]; then | |
170 | container_pacman_config=$(mktemp) | |
171 | container_mirrorlist=$(mktemp) | |
172 | sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
173 | -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ | |
174 | "${pacman_config}" > "${container_pacman_config}" | |
175 | sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ | |
176 | /etc/pacman.d/mirrorlist > "${container_mirrorlist}" | |
177 | ||
178 | pacman_config="${container_pacman_config}" | |
179 | fi | |
180 | ||
44b03630 | 181 | if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ |
c194ffc1 | 182 | ${base_packages[@]}; then |
f6267d90 AV |
183 | echo "Failed to install container packages" |
184 | return 1 | |
185 | fi | |
734d0bed JL |
186 | |
187 | if [ "${different_arch}" = "true" ]; then | |
188 | sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
189 | "${rootfs_path}"/etc/pacman.conf | |
190 | cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist | |
191 | rm "${container_pacman_config}" "${container_mirrorlist}" | |
192 | fi | |
193 | ||
f6267d90 | 194 | [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" |
f6267d90 AV |
195 | return 0 |
196 | } | |
197 | ||
f3849d01 | 198 | usage() { |
f6267d90 AV |
199 | cat <<EOF |
200 | usage: | |
99cbd299 AV |
201 | ${1} -n|--name=<container_name> [-p|--path=<path>] [-a|--arch=<arch of the container>] |
202 | [-r|--root_password=<root password>] [-P|--packages=<pkg1,pkg2,...>] | |
203 | [-e|--enable_units=unit1,unit2...] [-d|--disable_units=unit1,unit2...] | |
204 | [-c|--config=<pacman config path>] [-h|--help] | |
c194ffc1 | 205 | |
f6267d90 | 206 | Mandatory args: |
c194ffc1 | 207 | -n,--name container name, used to as an identifier for that container from now on |
f6267d90 | 208 | Optional args: |
c194ffc1 | 209 | -p,--path path to where the container rootfs will be created (${default_path}) |
69cbc333 | 210 | --rootfs path for actual container rootfs, (${default_path}/rootfs) |
c194ffc1 AV |
211 | -P,--packages preinstall additional packages, comma-separated list |
212 | -e,--enable_units enable systemd services, comma-separated list | |
213 | -d,--disable_units disable systemd services, comma-separated list | |
214 | -c,--config use specified pacman config when installing container packages | |
215 | -a,--arch use specified architecture instead of host's architecture | |
216 | -r,--root_password set container root password | |
217 | -h,--help print this help | |
f6267d90 AV |
218 | EOF |
219 | return 0 | |
220 | } | |
221 | ||
c194ffc1 | 222 | 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 |
223 | if [ ${?} -ne 0 ]; then |
224 | usage $(basename ${0}) | |
225 | exit 1 | |
226 | fi | |
227 | eval set -- "${options}" | |
228 | ||
229 | while true | |
230 | do | |
231 | case "${1}" in | |
f3849d01 AV |
232 | -h|--help) usage ${0} && exit 0;; |
233 | -p|--path) path=${2}; shift 2;; | |
234 | -n|--name) name=${2}; shift 2;; | |
1897e3bc | 235 | --rootfs) rootfs_path=${2}; shift 2;; |
f3849d01 | 236 | -P|--packages) additional_packages=${2}; shift 2;; |
012f591a | 237 | -e|--enable_units) enable_units=${2}; shift 2;; |
c194ffc1 | 238 | -d|--disable_units) disable_units=${2}; shift 2;; |
14831534 | 239 | -c|--config) pacman_config=${2}; shift 2;; |
734d0bed | 240 | -a|--arch) arch=${2}; shift 2;; |
c194ffc1 | 241 | -r|--root_password) root_passwd=${2}; shift 2;; |
f6267d90 AV |
242 | --) shift 1; break ;; |
243 | *) break ;; | |
244 | esac | |
245 | done | |
246 | ||
247 | if [ -z "${name}" ]; then | |
248 | echo "missing required 'name' parameter" | |
249 | exit 1 | |
250 | fi | |
251 | ||
252 | type pacman >/dev/null 2>&1 | |
253 | if [ ${?} -ne 0 ]; then | |
254 | echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" | |
255 | exit 1 | |
256 | fi | |
257 | ||
258 | if [ -z "${path}" ]; then | |
259 | path="${default_path}/${name}" | |
260 | fi | |
261 | ||
262 | if [ "${EUID}" != "0" ]; then | |
263 | echo "This script should be run as 'root'" | |
264 | exit 1 | |
265 | fi | |
266 | ||
1897e3bc SH |
267 | if [ -z "$rootfs_path" ]; then |
268 | rootfs_path="${path}/rootfs" | |
269 | fi | |
c194ffc1 | 270 | config_path="${path}" |
f6267d90 | 271 | |
f3849d01 AV |
272 | revert() { |
273 | echo "Interrupted, cleaning up" | |
f6267d90 | 274 | lxc-destroy -n "${name}" |
f6267d90 AV |
275 | rm -rf "${path}/${name}" |
276 | rm -rf "${default_path}/${name}" | |
277 | exit 1 | |
278 | } | |
279 | ||
280 | trap revert SIGHUP SIGINT SIGTERM | |
281 | ||
282 | copy_configuration | |
283 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 284 | echo "failed to write configuration file" |
f6267d90 AV |
285 | rm -rf "${config_path}" |
286 | exit 1 | |
287 | fi | |
288 | ||
289 | if [ ${#additional_packages[@]} -gt 0 ]; then | |
290 | split_string ${additional_packages} | |
291 | base_packages+=(${result[@]}) | |
292 | fi | |
293 | ||
f3849d01 | 294 | mkdir -p "${rootfs_path}" |
f6267d90 AV |
295 | install_arch |
296 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 297 | echo "failed to install Arch Linux" |
f6267d90 AV |
298 | rm -rf "${config_path}" "${path}" |
299 | exit 1 | |
300 | fi | |
301 | ||
302 | configure_arch | |
303 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 304 | echo "failed to configure Arch Linux for a container" |
f6267d90 AV |
305 | rm -rf "${config_path}" "${path}" |
306 | exit 1 | |
307 | fi | |
308 | ||
012f591a JL |
309 | if [ ${#enable_units[@]} -gt 0 ]; then |
310 | split_string ${enable_units} | |
311 | for unit in ${result[@]}; do | |
c194ffc1 AV |
312 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" |
313 | ln -s "/usr/lib/systemd/system/${unit}" \ | |
314 | "${rootfs_path}/etc/systemd/system/multi-user.target.wants/" | |
315 | done | |
316 | fi | |
317 | ||
318 | if [ ${#disable_units[@]} -gt 0 ]; then | |
319 | split_string ${disable_units} | |
320 | for unit in ${result[@]}; do | |
321 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" | |
322 | ln -s /dev/null "${rootfs_path}/etc/systemd/system/${unit}" | |
012f591a JL |
323 | done |
324 | fi | |
325 | ||
d0800999 JL |
326 | if [ -n "${root_passwd}" ]; then |
327 | echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd | |
328 | fi | |
329 | ||
21ca73b9 | 330 | cat << EOF |
c194ffc1 | 331 | Arch Linux container ${name} is successfully created! The configuration is |
21ca73b9 | 332 | stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for |
c194ffc1 | 333 | information about configuring Arch Linux. |
21ca73b9 | 334 | EOF |