]>
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 AV |
27 | |
28 | # defaults | |
f3849d01 | 29 | arch=$(uname -m) |
f6267d90 AV |
30 | lxc_network_type="veth" |
31 | lxc_network_link="br0" | |
f3849d01 AV |
32 | default_path="@LXCPATH@" |
33 | default_locale="en-US.UTF-8" | |
34 | default_timezone="UTC" | |
14831534 | 35 | pacman_config="/etc/pacman.conf" |
f6267d90 AV |
36 | |
37 | # sort of minimal package set | |
38 | base_packages=( | |
f3849d01 AV |
39 | "systemd" |
40 | "systemd-sysvcompat" | |
f6267d90 | 41 | "filesystem" |
f6267d90 | 42 | "coreutils" |
4852d800 | 43 | "kmod" |
f6267d90 AV |
44 | "procps" |
45 | "psmisc" | |
46 | "pacman" | |
47 | "bash" | |
f6267d90 AV |
48 | "cronie" |
49 | "iproute2" | |
50 | "iputils" | |
51 | "inetutils" | |
52 | "dhcpcd" | |
53 | "dnsutils" | |
54 | "nano" | |
55 | "grep" | |
56 | "less" | |
57 | "gawk" | |
58 | "sed" | |
59 | "tar" | |
f6267d90 AV |
60 | "gzip" |
61 | "which" | |
62 | ) | |
63 | declare -a additional_packages | |
64 | ||
f6267d90 AV |
65 | # split comma-separated string into an array |
66 | # ${1} - string to split | |
67 | # ${2} - separator (default is ",") | |
68 | # ${result} - result value on success | |
17abf278 | 69 | split_string() { |
f6267d90 AV |
70 | local ifs=${IFS} |
71 | IFS="${2:-,}" | |
72 | read -a result < <(echo "${1}") | |
73 | IFS=${ifs} | |
74 | return 0 | |
75 | } | |
76 | ||
f3849d01 AV |
77 | [ -f /etc/arch-release ] && is_arch=true |
78 | ||
f6267d90 | 79 | # Arch-specific preconfiguration for container |
17abf278 | 80 | configure_arch() { |
f6267d90 AV |
81 | # read locale and timezone defaults from system rc.conf if running on Arch |
82 | if [ "${is_arch}" ]; then | |
f3849d01 | 83 | cp -p /etc/vconsole.conf /etc/locale.conf /etc/locale.gen "${rootfs_path}/etc/" |
f6267d90 | 84 | else |
f3849d01 AV |
85 | echo "LANG=${default_lang}" > "${rootfs_path}/etc/locale.conf" |
86 | echo "KEYMAP=us" > "${rootfs_path}/etc/vconsole.conf" | |
87 | cat > "${rootfs_path}/etc/adjtime" << EOF | |
88 | 0.0 0.0 0.0 | |
89 | 0 | |
90 | LOCAL | |
f6267d90 | 91 | EOF |
f3849d01 AV |
92 | if [ -e "${rootfs_path}/etc/locale.gen" ]; then |
93 | sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" | |
94 | if [ ! "${default_locale}" = "en_US.UTF-8" ]; then | |
95 | echo "${default_locale} ${default_locale##*.}" >> "${rootfs_path}/etc/locale.gen" | |
96 | fi | |
f6267d90 | 97 | fi |
f6267d90 | 98 | fi |
f3849d01 | 99 | echo "${name}" > "${rootfs_path}/etc/hostname" |
f6267d90 AV |
100 | cat > "${rootfs_path}/etc/hosts" << EOF |
101 | 127.0.0.1 localhost.localdomain localhost ${name} | |
102 | ::1 localhost.localdomain localhost | |
103 | EOF | |
f6267d90 AV |
104 | grep nameserver /etc/resolv.conf > "${rootfs_path}/etc/resolv.conf" |
105 | ||
f3849d01 AV |
106 | arch-chroot "${rootfs_path}" /bin/bash -s << EOF |
107 | mkdir /run/lock | |
108 | locale-gen | |
109 | ln -s /usr/share/zoneinfo/${default_timezone} /etc/localtime | |
110 | # disable services unavailable for container | |
111 | ln -s /dev/null /etc/systemd/system/systemd-udevd.service | |
112 | ln -s /dev/null /etc/systemd/system/systemd-udevd-control.socket | |
113 | ln -s /dev/null /etc/systemd/system/systemd-udevd-kernel.socket | |
114 | ln -s /dev/null /etc/systemd/system/proc-sys-fs-binfmt_misc.automount | |
14831534 | 115 | # set default systemd target |
f3849d01 | 116 | ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target |
f3849d01 | 117 | EOF |
f6267d90 AV |
118 | return 0 |
119 | } | |
120 | ||
121 | # write container configuration files | |
17abf278 | 122 | copy_configuration() { |
f6267d90 AV |
123 | mkdir -p "${config_path}" |
124 | cat > "${config_path}/config" << EOF | |
f3849d01 AV |
125 | lxc.utsname=${name} |
126 | lxc.autodev=1 | |
127 | lxc.tty=1 | |
128 | lxc.pts=1024 | |
f3849d01 | 129 | lxc.mount=${config_path}/fstab |
d8c77af0 | 130 | lxc.cap.drop=sys_module mac_admin mac_override sys_time |
6139e7e5 AV |
131 | lxc.kmsg=0 |
132 | lxc.stopsignal=SIGRTMIN+4 | |
f6267d90 | 133 | #networking |
f3849d01 AV |
134 | lxc.network.type=${lxc_network_type} |
135 | lxc.network.link=${lxc_network_link} | |
136 | lxc.network.flags=up | |
137 | lxc.network.name=eth0 | |
138 | lxc.network.mtu=1500 | |
f6267d90 AV |
139 | #cgroups |
140 | lxc.cgroup.devices.deny = a | |
f3849d01 AV |
141 | lxc.cgroup.devices.allow = c *:* m |
142 | lxc.cgroup.devices.allow = b *:* m | |
f6267d90 AV |
143 | lxc.cgroup.devices.allow = c 1:3 rwm |
144 | lxc.cgroup.devices.allow = c 1:5 rwm | |
f3849d01 | 145 | lxc.cgroup.devices.allow = c 1:7 rwm |
f6267d90 | 146 | lxc.cgroup.devices.allow = c 1:8 rwm |
f3849d01 AV |
147 | lxc.cgroup.devices.allow = c 1:9 rwm |
148 | lxc.cgroup.devices.allow = c 1:9 rwm | |
149 | lxc.cgroup.devices.allow = c 4:1 rwm | |
150 | lxc.cgroup.devices.allow = c 5:0 rwm | |
151 | lxc.cgroup.devices.allow = c 5:1 rwm | |
f6267d90 | 152 | lxc.cgroup.devices.allow = c 5:2 rwm |
f3849d01 | 153 | lxc.cgroup.devices.allow = c 136:* rwm |
f6267d90 AV |
154 | EOF |
155 | ||
1897e3bc SH |
156 | grep -q "^lxc.rootfs" ${config_path}/config 2>/dev/null || echo "lxc.rootfs = ${rootfs_path}" >> ${config_path}/config |
157 | ||
f6267d90 | 158 | cat > "${config_path}/fstab" << EOF |
b335cf8d | 159 | sysfs sys sysfs defaults 0 0 |
f3849d01 | 160 | proc proc proc nodev,noexec,nosuid 0 0 |
f6267d90 AV |
161 | EOF |
162 | ||
f6267d90 AV |
163 | return 0 |
164 | } | |
165 | ||
f6267d90 | 166 | # install packages within container chroot |
17abf278 | 167 | install_arch() { |
734d0bed JL |
168 | [ "${arch}" != "$(uname -m)" ] && different_arch=true |
169 | ||
170 | if [ "${different_arch}" = "true" ]; then | |
171 | container_pacman_config=$(mktemp) | |
172 | container_mirrorlist=$(mktemp) | |
173 | sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ | |
174 | -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ | |
175 | "${pacman_config}" > "${container_pacman_config}" | |
176 | sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ | |
177 | /etc/pacman.d/mirrorlist > "${container_mirrorlist}" | |
178 | ||
179 | pacman_config="${container_pacman_config}" | |
180 | fi | |
181 | ||
14831534 | 182 | if ! pacstrap -dcC "${pacman_config}" "${rootfs_path}" ${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: | |
201 | ${1} -n|--name=<container_name> | |
f3849d01 | 202 | [-P|--packages=<pkg1,pkg2,...>] [-p|--path=<path>] [-t|--network_type=<type>] [-l|--network_link=<link>] [-h|--help] |
f6267d90 AV |
203 | Mandatory args: |
204 | -n,--name container name, used to as an identifier for that container from now on | |
205 | Optional args: | |
f3849d01 | 206 | -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 | 207 | -P,--packages preinstall additional packages, comma-separated list |
012f591a | 208 | -e,--enable_units Enable additional systemd units, comma-separated list |
14831534 | 209 | -c,--config use specified pacman config when installing container packages |
b408e70d | 210 | -a,--arch use specified architecture instead of host's architecture |
f3849d01 AV |
211 | -t,--network_type set container network interface type (${lxc_network_type}) |
212 | -l,--network_link set network link device (${lxc_network_link}) | |
d0800999 | 213 | -r,--root_passwd set container root password |
f6267d90 AV |
214 | -h,--help print this help |
215 | EOF | |
216 | return 0 | |
217 | } | |
218 | ||
d0800999 | 219 | 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 |
220 | if [ ${?} -ne 0 ]; then |
221 | usage $(basename ${0}) | |
222 | exit 1 | |
223 | fi | |
224 | eval set -- "${options}" | |
225 | ||
226 | while true | |
227 | do | |
228 | case "${1}" in | |
f3849d01 AV |
229 | -h|--help) usage ${0} && exit 0;; |
230 | -p|--path) path=${2}; shift 2;; | |
231 | -n|--name) name=${2}; shift 2;; | |
1897e3bc | 232 | --rootfs) rootfs_path=${2}; shift 2;; |
f3849d01 | 233 | -P|--packages) additional_packages=${2}; shift 2;; |
012f591a | 234 | -e|--enable_units) enable_units=${2}; shift 2;; |
14831534 | 235 | -c|--config) pacman_config=${2}; shift 2;; |
734d0bed | 236 | -a|--arch) arch=${2}; shift 2;; |
f3849d01 AV |
237 | -t|--network_type) lxc_network_type=${2}; shift 2;; |
238 | -l|--network_link) lxc_network_link=${2}; shift 2;; | |
d0800999 | 239 | -r|--root_passwd) root_passwd=${2}; shift 2;; |
f6267d90 AV |
240 | --) shift 1; break ;; |
241 | *) break ;; | |
242 | esac | |
243 | done | |
244 | ||
245 | if [ -z "${name}" ]; then | |
246 | echo "missing required 'name' parameter" | |
247 | exit 1 | |
248 | fi | |
249 | ||
f3849d01 AV |
250 | if [ ! -e /sys/class/net/${lxc_network_link} ]; then |
251 | echo "network link interface does not exist" | |
252 | exit 1 | |
253 | fi | |
254 | ||
f6267d90 AV |
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 | |
f6267d90 AV |
273 | config_path="${default_path}/${name}" |
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 | |
315 | [ "${unit}" = *'.'* ] || 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 | ||
d0800999 JL |
321 | if [ -n "${root_passwd}" ]; then |
322 | echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd | |
323 | fi | |
324 | ||
f3849d01 | 325 | echo "container config is ${config_path}/config" |