]>
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" |
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 |
f3849d01 | 77 | echo "LANG=${default_lang}" > "${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" |
21ca73b9 LI |
89 | while read r; do |
90 | [ "${r#nameserver}" = "$r" ] || echo "$r" | |
91 | done < /etc/resolv.conf > "${rootfs_path}/etc/resolv.conf" | |
f6267d90 | 92 | |
21ca73b9 | 93 | # chroot and configure system |
f3849d01 AV |
94 | arch-chroot "${rootfs_path}" /bin/bash -s << EOF |
95 | mkdir /run/lock | |
96 | locale-gen | |
97 | ln -s /usr/share/zoneinfo/${default_timezone} /etc/localtime | |
c194ffc1 | 98 | # set default boot target |
f3849d01 | 99 | ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target |
c194ffc1 AV |
100 | # override getty@.service for container ttys |
101 | sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
102 | -e 's/After=dev-%i.device/After=/' \ | |
103 | < /lib/systemd/system/getty\@.service \ | |
104 | > /etc/systemd/system/getty\@.service | |
f3849d01 | 105 | EOF |
c194ffc1 AV |
106 | # enable getty on active ttys |
107 | nttys=$(grep lxc.tty ${config_path}/config | cut -d= -f 2 | tr -d "[:blank:]") | |
108 | if [ ${nttys:-0} -gt 1 ]; then | |
109 | ( cd ${rootfs_path}/etc/systemd/system/getty.target.wants | |
110 | for i in $(seq 1 $nttys); do ln -sf ../getty\@.service getty@tty${i}.service; done ) | |
111 | fi | |
112 | [ ${nttys:-0} -gt 6 ] && echo \ | |
113 | "You may want to modify container's /etc/securetty \ | |
114 | file to allow root logins on tty7 and higher" | |
f6267d90 AV |
115 | return 0 |
116 | } | |
117 | ||
118 | # write container configuration files | |
17abf278 | 119 | copy_configuration() { |
f6267d90 | 120 | mkdir -p "${config_path}" |
c194ffc1 AV |
121 | local config="${config_path}/config" |
122 | echo "lxc.utsname = ${name}" >> "${config}" | |
123 | grep -q "^lxc.arch" "${config}" 2>/dev/null \ | |
124 | || echo "lxc.arch = ${arch}" >> "${config}" | |
125 | grep -q "^lxc.rootfs" "${config}" 2>/dev/null \ | |
126 | || echo "lxc.rootfs = ${rootfs_path}" >> "${config}" | |
127 | [ -e "${shared_config}" ] \ | |
128 | && echo "lxc.include = ${shared_config}" >> "${config}" | |
129 | if [ $? -ne 0 ]; then | |
130 | echo "Failed to configure container" | |
131 | return 1 | |
132 | fi | |
f6267d90 AV |
133 | return 0 |
134 | } | |
135 | ||
f6267d90 | 136 | # install packages within container chroot |
17abf278 | 137 | install_arch() { |
734d0bed JL |
138 | [ "${arch}" != "$(uname -m)" ] && different_arch=true |
139 | ||
140 | if [ "${different_arch}" = "true" ]; then | |
141 | container_pacman_config=$(mktemp) | |
142 | container_mirrorlist=$(mktemp) | |
143 | sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
144 | -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ | |
145 | "${pacman_config}" > "${container_pacman_config}" | |
146 | sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ | |
147 | /etc/pacman.d/mirrorlist > "${container_mirrorlist}" | |
148 | ||
149 | pacman_config="${container_pacman_config}" | |
150 | fi | |
151 | ||
44b03630 | 152 | if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ |
c194ffc1 | 153 | ${base_packages[@]}; then |
f6267d90 AV |
154 | echo "Failed to install container packages" |
155 | return 1 | |
156 | fi | |
734d0bed JL |
157 | |
158 | if [ "${different_arch}" = "true" ]; then | |
159 | sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
160 | "${rootfs_path}"/etc/pacman.conf | |
161 | cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist | |
162 | rm "${container_pacman_config}" "${container_mirrorlist}" | |
163 | fi | |
164 | ||
f6267d90 | 165 | [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" |
f6267d90 AV |
166 | return 0 |
167 | } | |
168 | ||
f3849d01 | 169 | usage() { |
f6267d90 AV |
170 | cat <<EOF |
171 | usage: | |
c194ffc1 AV |
172 | ${1} -n|--name=<container_name> [-p|--path=<path>] [-a|--arch=<arch of the container>] [-r|--root_password=<root password>] |
173 | [-P|--packages=<pkg1,pkg2,...>] [-e|--enable_units=unit1,unit2...] [-c|--config=<pacman config path>] [-h|--help] | |
174 | ||
f6267d90 | 175 | Mandatory args: |
c194ffc1 | 176 | -n,--name container name, used to as an identifier for that container from now on |
f6267d90 | 177 | Optional args: |
c194ffc1 AV |
178 | -p,--path path to where the container rootfs will be created (${default_path}) |
179 | --rootfs path for actual container rootfs, (${default_path/rootfs) | |
180 | -P,--packages preinstall additional packages, comma-separated list | |
181 | -e,--enable_units enable systemd services, comma-separated list | |
182 | -d,--disable_units disable systemd services, comma-separated list | |
183 | -c,--config use specified pacman config when installing container packages | |
184 | -a,--arch use specified architecture instead of host's architecture | |
185 | -r,--root_password set container root password | |
186 | -h,--help print this help | |
f6267d90 AV |
187 | EOF |
188 | return 0 | |
189 | } | |
190 | ||
c194ffc1 | 191 | 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 |
192 | if [ ${?} -ne 0 ]; then |
193 | usage $(basename ${0}) | |
194 | exit 1 | |
195 | fi | |
196 | eval set -- "${options}" | |
197 | ||
198 | while true | |
199 | do | |
200 | case "${1}" in | |
f3849d01 AV |
201 | -h|--help) usage ${0} && exit 0;; |
202 | -p|--path) path=${2}; shift 2;; | |
203 | -n|--name) name=${2}; shift 2;; | |
1897e3bc | 204 | --rootfs) rootfs_path=${2}; shift 2;; |
f3849d01 | 205 | -P|--packages) additional_packages=${2}; shift 2;; |
012f591a | 206 | -e|--enable_units) enable_units=${2}; shift 2;; |
c194ffc1 | 207 | -d|--disable_units) disable_units=${2}; shift 2;; |
14831534 | 208 | -c|--config) pacman_config=${2}; shift 2;; |
734d0bed | 209 | -a|--arch) arch=${2}; shift 2;; |
c194ffc1 | 210 | -r|--root_password) root_passwd=${2}; shift 2;; |
f6267d90 AV |
211 | --) shift 1; break ;; |
212 | *) break ;; | |
213 | esac | |
214 | done | |
215 | ||
216 | if [ -z "${name}" ]; then | |
217 | echo "missing required 'name' parameter" | |
218 | exit 1 | |
219 | fi | |
220 | ||
221 | type pacman >/dev/null 2>&1 | |
222 | if [ ${?} -ne 0 ]; then | |
223 | echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" | |
224 | exit 1 | |
225 | fi | |
226 | ||
227 | if [ -z "${path}" ]; then | |
228 | path="${default_path}/${name}" | |
229 | fi | |
230 | ||
231 | if [ "${EUID}" != "0" ]; then | |
232 | echo "This script should be run as 'root'" | |
233 | exit 1 | |
234 | fi | |
235 | ||
1897e3bc SH |
236 | if [ -z "$rootfs_path" ]; then |
237 | rootfs_path="${path}/rootfs" | |
238 | fi | |
c194ffc1 | 239 | config_path="${path}" |
f6267d90 | 240 | |
f3849d01 AV |
241 | revert() { |
242 | echo "Interrupted, cleaning up" | |
f6267d90 | 243 | lxc-destroy -n "${name}" |
f6267d90 AV |
244 | rm -rf "${path}/${name}" |
245 | rm -rf "${default_path}/${name}" | |
246 | exit 1 | |
247 | } | |
248 | ||
249 | trap revert SIGHUP SIGINT SIGTERM | |
250 | ||
251 | copy_configuration | |
252 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 253 | echo "failed to write configuration file" |
f6267d90 AV |
254 | rm -rf "${config_path}" |
255 | exit 1 | |
256 | fi | |
257 | ||
258 | if [ ${#additional_packages[@]} -gt 0 ]; then | |
259 | split_string ${additional_packages} | |
260 | base_packages+=(${result[@]}) | |
261 | fi | |
262 | ||
f3849d01 | 263 | mkdir -p "${rootfs_path}" |
f6267d90 AV |
264 | install_arch |
265 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 266 | echo "failed to install Arch Linux" |
f6267d90 AV |
267 | rm -rf "${config_path}" "${path}" |
268 | exit 1 | |
269 | fi | |
270 | ||
271 | configure_arch | |
272 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 273 | echo "failed to configure Arch Linux for a container" |
f6267d90 AV |
274 | rm -rf "${config_path}" "${path}" |
275 | exit 1 | |
276 | fi | |
277 | ||
012f591a JL |
278 | if [ ${#enable_units[@]} -gt 0 ]; then |
279 | split_string ${enable_units} | |
280 | for unit in ${result[@]}; do | |
c194ffc1 AV |
281 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" |
282 | ln -s "/usr/lib/systemd/system/${unit}" \ | |
283 | "${rootfs_path}/etc/systemd/system/multi-user.target.wants/" | |
284 | done | |
285 | fi | |
286 | ||
287 | if [ ${#disable_units[@]} -gt 0 ]; then | |
288 | split_string ${disable_units} | |
289 | for unit in ${result[@]}; do | |
290 | [ "${unit##*.}" = "service" ] || unit="${unit}.service" | |
291 | ln -s /dev/null "${rootfs_path}/etc/systemd/system/${unit}" | |
012f591a JL |
292 | done |
293 | fi | |
294 | ||
d0800999 JL |
295 | if [ -n "${root_passwd}" ]; then |
296 | echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd | |
297 | fi | |
298 | ||
21ca73b9 | 299 | cat << EOF |
c194ffc1 | 300 | Arch Linux container ${name} is successfully created! The configuration is |
21ca73b9 | 301 | stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for |
c194ffc1 | 302 | information about configuring Arch Linux. |
21ca73b9 | 303 | EOF |