]>
Commit | Line | Data |
---|---|---|
f6267d90 AV |
1 | #!/bin/bash |
2 | ||
3 | # | |
4 | # template script for generating Arch linux container for LXC | |
5 | # | |
6 | ||
7 | # | |
8 | # lxc: linux Container library | |
9 | ||
10 | # Authors: | |
11 | # Alexander Vladimirov <idkfa@vlan1.ru> | |
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) |
f6267d90 AV |
43 | lxc_network_type="veth" |
44 | lxc_network_link="br0" | |
f3849d01 AV |
45 | default_path="@LXCPATH@" |
46 | default_locale="en-US.UTF-8" | |
47 | default_timezone="UTC" | |
14831534 | 48 | pacman_config="/etc/pacman.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 |
21ca73b9 LI |
76 | cp -p /etc/vconsole.conf /etc/locale.conf /etc/locale.gen \ |
77 | "${rootfs_path}/etc/" | |
f6267d90 | 78 | else |
f3849d01 AV |
79 | echo "LANG=${default_lang}" > "${rootfs_path}/etc/locale.conf" |
80 | echo "KEYMAP=us" > "${rootfs_path}/etc/vconsole.conf" | |
81 | cat > "${rootfs_path}/etc/adjtime" << EOF | |
82 | 0.0 0.0 0.0 | |
83 | 0 | |
84 | LOCAL | |
f6267d90 | 85 | EOF |
f3849d01 AV |
86 | if [ -e "${rootfs_path}/etc/locale.gen" ]; then |
87 | sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" | |
88 | if [ ! "${default_locale}" = "en_US.UTF-8" ]; then | |
89 | echo "${default_locale} ${default_locale##*.}" >> "${rootfs_path}/etc/locale.gen" | |
90 | fi | |
f6267d90 | 91 | fi |
f6267d90 | 92 | fi |
21ca73b9 LI |
93 | |
94 | # hostname and nameservers | |
f3849d01 | 95 | echo "${name}" > "${rootfs_path}/etc/hostname" |
21ca73b9 LI |
96 | while read r; do |
97 | [ "${r#nameserver}" = "$r" ] || echo "$r" | |
98 | done < /etc/resolv.conf > "${rootfs_path}/etc/resolv.conf" | |
f6267d90 | 99 | |
21ca73b9 | 100 | # chroot and configure system |
f3849d01 AV |
101 | arch-chroot "${rootfs_path}" /bin/bash -s << EOF |
102 | mkdir /run/lock | |
103 | locale-gen | |
104 | ln -s /usr/share/zoneinfo/${default_timezone} /etc/localtime | |
105 | # disable services unavailable for container | |
21ca73b9 LI |
106 | for i in systemd-udevd.service \ |
107 | systemd-udevd-control.socket \ | |
108 | systemd-udevd-kernel.socket \ | |
109 | proc-sys-fs-binfmt_misc.automount; do | |
110 | ln -s /dev/null /etc/systemd/system/\$i | |
111 | done | |
14831534 | 112 | # set default systemd target |
f3849d01 | 113 | ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target |
31efc34c EK |
114 | # enable sigpwr signal handling in systemd as otherwise lxc-stop won't work |
115 | ln -s /usr/lib/systemd/system/poweroff.target /etc/systemd/system/sigpwr.target | |
44b03630 LI |
116 | # initialize pacman keyring |
117 | pacman-key --init | |
118 | pacman-key --populate archlinux | |
f3849d01 | 119 | EOF |
f6267d90 AV |
120 | return 0 |
121 | } | |
122 | ||
123 | # write container configuration files | |
17abf278 | 124 | copy_configuration() { |
f6267d90 AV |
125 | mkdir -p "${config_path}" |
126 | cat > "${config_path}/config" << EOF | |
f3849d01 AV |
127 | lxc.utsname=${name} |
128 | lxc.autodev=1 | |
129 | lxc.tty=1 | |
130 | lxc.pts=1024 | |
f3849d01 | 131 | lxc.mount=${config_path}/fstab |
d8c77af0 | 132 | lxc.cap.drop=sys_module mac_admin mac_override sys_time |
6139e7e5 AV |
133 | lxc.kmsg=0 |
134 | lxc.stopsignal=SIGRTMIN+4 | |
f6267d90 | 135 | #networking |
f3849d01 AV |
136 | lxc.network.type=${lxc_network_type} |
137 | lxc.network.link=${lxc_network_link} | |
138 | lxc.network.flags=up | |
139 | lxc.network.name=eth0 | |
140 | lxc.network.mtu=1500 | |
f6267d90 AV |
141 | #cgroups |
142 | lxc.cgroup.devices.deny = a | |
f3849d01 AV |
143 | lxc.cgroup.devices.allow = c *:* m |
144 | lxc.cgroup.devices.allow = b *:* m | |
f6267d90 AV |
145 | lxc.cgroup.devices.allow = c 1:3 rwm |
146 | lxc.cgroup.devices.allow = c 1:5 rwm | |
f3849d01 | 147 | lxc.cgroup.devices.allow = c 1:7 rwm |
f6267d90 | 148 | lxc.cgroup.devices.allow = c 1:8 rwm |
f3849d01 | 149 | lxc.cgroup.devices.allow = c 1:9 rwm |
f3849d01 AV |
150 | lxc.cgroup.devices.allow = c 4:1 rwm |
151 | lxc.cgroup.devices.allow = c 5:0 rwm | |
152 | lxc.cgroup.devices.allow = c 5:1 rwm | |
f6267d90 | 153 | lxc.cgroup.devices.allow = c 5:2 rwm |
f3849d01 | 154 | lxc.cgroup.devices.allow = c 136:* rwm |
f6267d90 AV |
155 | EOF |
156 | ||
44464003 | 157 | grep -q "^lxc.rootfs" ${config_path}/config 2>/dev/null || echo "lxc.rootfs = ${path}/rootfs" >> ${config_path}/config |
1897e3bc | 158 | |
f6267d90 | 159 | cat > "${config_path}/fstab" << EOF |
b335cf8d | 160 | sysfs sys sysfs defaults 0 0 |
f3849d01 | 161 | proc proc proc nodev,noexec,nosuid 0 0 |
f6267d90 AV |
162 | EOF |
163 | ||
f6267d90 AV |
164 | return 0 |
165 | } | |
166 | ||
f6267d90 | 167 | # install packages within container chroot |
17abf278 | 168 | install_arch() { |
734d0bed JL |
169 | [ "${arch}" != "$(uname -m)" ] && different_arch=true |
170 | ||
171 | if [ "${different_arch}" = "true" ]; then | |
172 | container_pacman_config=$(mktemp) | |
173 | container_mirrorlist=$(mktemp) | |
174 | sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
175 | -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ | |
176 | "${pacman_config}" > "${container_pacman_config}" | |
177 | sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ | |
178 | /etc/pacman.d/mirrorlist > "${container_mirrorlist}" | |
179 | ||
180 | pacman_config="${container_pacman_config}" | |
181 | fi | |
182 | ||
44b03630 LI |
183 | if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ |
184 | ${base_packages[@]}; then | |
f6267d90 AV |
185 | echo "Failed to install container packages" |
186 | return 1 | |
187 | fi | |
734d0bed JL |
188 | |
189 | if [ "${different_arch}" = "true" ]; then | |
190 | sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
191 | "${rootfs_path}"/etc/pacman.conf | |
192 | cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist | |
193 | rm "${container_pacman_config}" "${container_mirrorlist}" | |
194 | fi | |
195 | ||
f6267d90 | 196 | [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" |
f6267d90 AV |
197 | return 0 |
198 | } | |
199 | ||
f3849d01 | 200 | usage() { |
f6267d90 AV |
201 | cat <<EOF |
202 | usage: | |
203 | ${1} -n|--name=<container_name> | |
f3849d01 | 204 | [-P|--packages=<pkg1,pkg2,...>] [-p|--path=<path>] [-t|--network_type=<type>] [-l|--network_link=<link>] [-h|--help] |
f6267d90 AV |
205 | Mandatory args: |
206 | -n,--name container name, used to as an identifier for that container from now on | |
207 | Optional args: | |
f3849d01 | 208 | -p,--path path to where the container rootfs will be created, defaults to ${default_path}/rootfs. The container config will go under ${default_path} in that case |
f6267d90 | 209 | -P,--packages preinstall additional packages, comma-separated list |
012f591a | 210 | -e,--enable_units Enable additional systemd units, comma-separated list |
14831534 | 211 | -c,--config use specified pacman config when installing container packages |
b408e70d | 212 | -a,--arch use specified architecture instead of host's architecture |
f3849d01 AV |
213 | -t,--network_type set container network interface type (${lxc_network_type}) |
214 | -l,--network_link set network link device (${lxc_network_link}) | |
d0800999 | 215 | -r,--root_passwd set container root password |
f6267d90 AV |
216 | -h,--help print this help |
217 | EOF | |
218 | return 0 | |
219 | } | |
220 | ||
d0800999 | 221 | options=$(getopt -o hp:P:e:n:c:a:l:t:r: -l help,rootfs:,path:,packages:,enable_units:,name:,config:,arch:,network_type:,network_link:,root_passwd: -- "${@}") |
f6267d90 AV |
222 | if [ ${?} -ne 0 ]; then |
223 | usage $(basename ${0}) | |
224 | exit 1 | |
225 | fi | |
226 | eval set -- "${options}" | |
227 | ||
228 | while true | |
229 | do | |
230 | case "${1}" in | |
f3849d01 AV |
231 | -h|--help) usage ${0} && exit 0;; |
232 | -p|--path) path=${2}; shift 2;; | |
233 | -n|--name) name=${2}; shift 2;; | |
1897e3bc | 234 | --rootfs) rootfs_path=${2}; shift 2;; |
f3849d01 | 235 | -P|--packages) additional_packages=${2}; shift 2;; |
012f591a | 236 | -e|--enable_units) enable_units=${2}; shift 2;; |
14831534 | 237 | -c|--config) pacman_config=${2}; shift 2;; |
734d0bed | 238 | -a|--arch) arch=${2}; shift 2;; |
f3849d01 AV |
239 | -t|--network_type) lxc_network_type=${2}; shift 2;; |
240 | -l|--network_link) lxc_network_link=${2}; shift 2;; | |
d0800999 | 241 | -r|--root_passwd) 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 | ||
f3849d01 | 252 | if [ ! -e /sys/class/net/${lxc_network_link} ]; then |
8dac6e74 | 253 | echo "network link interface, ${lxc_network_link}, does not exist" |
f3849d01 AV |
254 | exit 1 |
255 | fi | |
256 | ||
f6267d90 AV |
257 | type pacman >/dev/null 2>&1 |
258 | if [ ${?} -ne 0 ]; then | |
259 | echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" | |
260 | exit 1 | |
261 | fi | |
262 | ||
263 | if [ -z "${path}" ]; then | |
264 | path="${default_path}/${name}" | |
265 | fi | |
266 | ||
267 | if [ "${EUID}" != "0" ]; then | |
268 | echo "This script should be run as 'root'" | |
269 | exit 1 | |
270 | fi | |
271 | ||
1897e3bc SH |
272 | if [ -z "$rootfs_path" ]; then |
273 | rootfs_path="${path}/rootfs" | |
274 | fi | |
f6267d90 AV |
275 | config_path="${default_path}/${name}" |
276 | ||
f3849d01 AV |
277 | revert() { |
278 | echo "Interrupted, cleaning up" | |
f6267d90 | 279 | lxc-destroy -n "${name}" |
f6267d90 AV |
280 | rm -rf "${path}/${name}" |
281 | rm -rf "${default_path}/${name}" | |
282 | exit 1 | |
283 | } | |
284 | ||
285 | trap revert SIGHUP SIGINT SIGTERM | |
286 | ||
287 | copy_configuration | |
288 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 289 | echo "failed to write configuration file" |
f6267d90 AV |
290 | rm -rf "${config_path}" |
291 | exit 1 | |
292 | fi | |
293 | ||
294 | if [ ${#additional_packages[@]} -gt 0 ]; then | |
295 | split_string ${additional_packages} | |
296 | base_packages+=(${result[@]}) | |
297 | fi | |
298 | ||
f3849d01 | 299 | mkdir -p "${rootfs_path}" |
f6267d90 AV |
300 | install_arch |
301 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 302 | echo "failed to install Arch Linux" |
f6267d90 AV |
303 | rm -rf "${config_path}" "${path}" |
304 | exit 1 | |
305 | fi | |
306 | ||
307 | configure_arch | |
308 | if [ ${?} -ne 0 ]; then | |
f3849d01 | 309 | echo "failed to configure Arch Linux for a container" |
f6267d90 AV |
310 | rm -rf "${config_path}" "${path}" |
311 | exit 1 | |
312 | fi | |
313 | ||
012f591a JL |
314 | if [ ${#enable_units[@]} -gt 0 ]; then |
315 | split_string ${enable_units} | |
316 | for unit in ${result[@]}; do | |
317 | [ "${unit}" = *'.'* ] || unit="${unit}.service" | |
318 | ln -s /usr/lib/systemd/system/"${unit}" \ | |
319 | "${rootfs_path}"/etc/systemd/system/multi-user.target.wants | |
320 | done | |
321 | fi | |
322 | ||
d0800999 JL |
323 | if [ -n "${root_passwd}" ]; then |
324 | echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd | |
325 | fi | |
326 | ||
21ca73b9 LI |
327 | cat << EOF |
328 | ArchLinux container ${name} is successfully created! The configuration is | |
329 | stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for | |
330 | information about configuring ArchLinux. | |
331 | EOF |