]>
Commit | Line | Data |
---|---|---|
4e133789 | 1 | #!/bin/bash |
11f88f10 G |
2 | # vim: set ts=4 sw=4 expandtab |
3 | ||
4 | # Exit on error and treat unset variables as an error. | |
5 | set -eu | |
6 | ||
7 | # | |
8 | # LXC template for Sabayon OS, based of Alpine script. | |
9 | # | |
10 | ||
11 | # Authors: | |
12 | # Geaaru <geaaru@gmail.com> | |
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 | |
26 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
27 | ||
28 | ||
29 | #=========================== Constants ============================# | |
30 | ||
31 | # Make sure the usual locations are in PATH | |
32 | export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
33 | ||
34 | readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' | |
35 | readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' | |
36 | ||
37 | ||
38 | # Temporary static MIRROR LIST. I will get list from online path on the near future. | |
39 | readonly MIRRORS_LIST=" | |
40 | http://mirror.it.sabayon.org/ | |
41 | http://dl.sabayon.org/ | |
42 | http://ftp.kddilabs.jp/Linux/packages/sabayonlinux/ | |
43 | ftp://ftp.klid.dk/sabayonlinux/ | |
44 | http://ftp.fsn.hu/pub/linux/distributions/sabayon/ | |
45 | http://ftp.cc.uoc.gr/mirrors/linux/SabayonLinux/ | |
46 | http://ftp.rnl.ist.utl.pt/pub/sabayon/ | |
47 | ftp://ftp.nluug.nl/pub/os/Linux/distr/sabayonlinux/ | |
48 | http://ftp.surfnet.nl/pub/os/Linux/distr/sabayonlinux/ | |
49 | http://mirror.internode.on.net/pub/sabayon/ | |
50 | http://mirror.yandex.ru/sabayon/ | |
51 | http://sabayon.c3sl.ufpr.br/ | |
11f88f10 G |
52 | http://mirror.clarkson.edu/sabayon/ |
53 | http://na.mirror.garr.it/mirrors/sabayonlinux/" | |
54 | ||
55 | #======================== Global variables ========================# | |
56 | ||
57 | # Clean variables and set defaults. | |
58 | arch="$(uname -m)" | |
59 | debug='no' | |
60 | flush_cache='no' | |
61 | mirror_url= | |
62 | name= | |
63 | path= | |
64 | release="DAILY" | |
65 | rootfs= | |
66 | unprivileged=false | |
67 | mapped_uid= | |
68 | mapped_gid= | |
3d288bbe | 69 | flush_owner=false |
11f88f10 G |
70 | |
71 | #======================== Helper Functions ========================# | |
72 | ||
73 | usage() { | |
74 | cat <<-EOF | |
75 | Template specific options can be passed to lxc-create after a '--' like this: | |
76 | ||
77 | lxc-create --name=NAME [lxc-create-options] -- [template-options] | |
78 | ||
79 | Template options: | |
80 | -a ARCH, --arch=ARCH The container architecture (e.g. x86_64, armv7); defaults | |
81 | to the host arch. | |
82 | -d, --debug Run this script in a debug mode (set -x and wget w/o -q). | |
83 | -m URL --mirror=URL The Sabayon mirror to use; defaults to random mirror. | |
84 | -u, --unprivileged Tuning of rootfs for unprivileged containers. | |
11f88f10 G |
85 | -r, --release Identify release to use. Default is DAILY. |
86 | --mapped-gid Group Id to use on unprivileged container | |
87 | (based of value present on file /etc/subgid). | |
88 | --mapped-uid User Id to use on unprivileged container | |
89 | (based of value present on file /etc/subuid) | |
3d288bbe G |
90 | --flush-owner Only for directly creation of unprivileged containers |
91 | through lxc-create command. Execute fuidshift command. | |
92 | Require --mapped-gid,--mapped-uid and --unprivileged | |
93 | options. | |
11f88f10 G |
94 | |
95 | Environment variables: | |
96 | RELEASE Release version of Sabayon. Default is ${RELEASE}. | |
97 | EOF | |
98 | } | |
99 | ||
100 | random_mirror_url() { | |
101 | local url="" | |
102 | ||
103 | if [ $arch == 'amd64' ] ; then | |
104 | url=$(echo $MIRRORS_LIST | sed -e 's/ /\n/g' | sort -R --random-source=/dev/urandom | head -n 1) | |
105 | else | |
106 | if [ $arch == 'armv7l' ] ; then | |
107 | # Currently armv7l tarball is not on sabayon mirrored tree. | |
108 | url="https://dockerbuilder.sabayon.org/" | |
109 | fi | |
110 | fi | |
111 | ||
112 | [ -n "$url" ] && echo "$url" | |
113 | } | |
114 | ||
115 | die() { | |
116 | local retval=$1; shift | |
117 | ||
118 | echo -e "==> $@\n" | |
119 | exit $retval | |
120 | } | |
121 | ||
122 | einfo() { | |
123 | echo -e "==> $@\n" | |
124 | } | |
125 | ||
126 | fetch() { | |
127 | if [ "$debug" = 'yes' ]; then | |
128 | wget -T 10 -O - $@ | |
129 | else | |
130 | wget -T 10 -O - -q $@ | |
131 | fi | |
132 | } | |
133 | ||
134 | parse_arch() { | |
135 | case "$1" in | |
136 | x86_64 | amd64) echo 'amd64';; | |
137 | armv7 | armv7l) echo 'armv7l';; | |
138 | #arm*) echo 'armhf';; | |
139 | *) return 1;; | |
140 | esac | |
141 | } | |
142 | ||
143 | run_exclusively() { | |
144 | ||
145 | local lock_name="$1" | |
146 | local timeout=$2 | |
147 | local method=$3 | |
148 | shift 3 | |
149 | ||
150 | mkdir -p "$LOCAL_STATE_DIR/lock/subsys" | |
151 | ||
152 | local retval | |
153 | { | |
154 | echo -n "Obtaining an exclusive lock..." | |
155 | if ! flock -x 9; then | |
156 | echo ' failed.' | |
157 | return 1 | |
158 | fi | |
159 | echo ' done' | |
160 | ||
161 | ${method} $@ | |
162 | retval=$? | |
163 | } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-sabayon-$lock_name" | |
164 | ||
165 | return $retval | |
166 | } | |
167 | ||
168 | create_url () { | |
169 | ||
170 | local url="" | |
171 | # Example of amd64 tarball url | |
172 | # http://mirror.yandex.ru/sabayon/iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz | |
173 | ||
174 | if [ $arch == 'amd64' ] ; then | |
175 | ||
176 | if [ $release = 'DAILY' ] ; then | |
177 | url="${MIRROR_URL}iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz" | |
178 | else | |
179 | url="${MIRROR_URL}iso/monthly/Sabayon_Linux_${release}_amd64_tarball.tar.gz" | |
180 | fi | |
181 | else | |
182 | # https://dockerbuilder.sabayon.org/Sabayon_Linux_16_armv7l.tar.bz2 | |
183 | if [ $arch == 'armv7l' ] ; then | |
184 | ||
185 | # Currently $arch tarball is not on sabayon mirrored tree. | |
186 | url="${MIRROR_URL}Sabayon_Linux_16_armv7l.tar.bz2" | |
187 | ||
188 | fi | |
189 | fi | |
190 | ||
191 | echo $url | |
192 | } | |
193 | ||
194 | ||
195 | #=========================== Configure ===========================# | |
196 | ||
197 | unprivileged_rootfs() { | |
198 | ||
199 | pushd ${rootfs}/etc/systemd/system | |
200 | ||
201 | # Disable systemd-journald-audit.socket because it seems that doesn't | |
202 | # start correctly on unprivileged container | |
203 | ln -s /dev/null systemd-journald-audit.socket | |
204 | ||
205 | # Disable systemd-remount-fs.service because on unprivileged container | |
206 | # systemd can't remount filesystem | |
207 | ln -s /dev/null systemd-remount-fs.service | |
208 | ||
209 | # Remove mount of FUSE Control File system | |
210 | ln -s /dev/null sys-fs-fuse-connections.mount | |
211 | ||
212 | # Change execution of service systemd-sysctl to avoid errors. | |
213 | mkdir systemd-sysctl.service.d | |
214 | cat <<EOF > systemd-sysctl.service.d/00gentoo.conf | |
215 | [Service] | |
216 | ExecStart= | |
217 | ExecStart=/usr/lib/systemd/systemd-sysctl --prefix=/etc/sysctl.d/ | |
218 | EOF | |
219 | ||
220 | # Disable mount of hugepages | |
221 | ln -s /dev/null dev-hugepages.mount | |
222 | ||
11f88f10 G |
223 | popd |
224 | ||
225 | pushd ${rootfs} | |
226 | ||
227 | # Disable sabayon-anti-fork-bomb limits (already apply to lxc container manager) | |
228 | sed -i -e 's/^*/#*/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 | |
229 | sed -i -e 's/^root/#root/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 | |
230 | ||
231 | popd | |
232 | ||
233 | return 0 | |
234 | } | |
235 | ||
236 | unprivileged_shift_owner () { | |
237 | ||
238 | # I use /usr/bin/fuidshift from LXD project. | |
239 | ||
240 | einfo "Executing: fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ..." | |
241 | ||
242 | fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 || | |
243 | die 1 "Error on change owners of ${rootfs} directory" | |
244 | ||
245 | einfo "Done." | |
246 | ||
247 | # Fix permission of container directory | |
248 | chmod a+rx ${path} | |
249 | ||
250 | return 0 | |
251 | } | |
252 | ||
253 | systemd_container_tuning () { | |
254 | ||
255 | # To avoid error on start systemd-tmpfiles-setup service | |
256 | # it is needed clean journal directory | |
257 | rm -rf ${rootfs}/var/log/journal/ | |
258 | ||
259 | # Remove LVM service. Normally not needed on container system. | |
260 | rm -rf ${rootfs}/etc/systemd/system/sysinit.target.wants/lvm2-lvmetad.service | |
261 | ||
74e75741 G |
262 | # Comment unneeded entry on /etc/fstab |
263 | sed -e 's/\/dev/#\/dev/g' -i ${rootfs}/etc/fstab | |
264 | ||
265 | # Fix this stupid error until fix is available on sabayon image | |
266 | # /usr/lib/systemd/system-generators/gentoo-local-generator: line 4: cd: /etc/local.d: No such file or directory | |
267 | mkdir ${rootfs}/etc/local.d/ | |
268 | ||
269 | # Fix TERM variable for container console | |
270 | mkdir container-getty\@0.service.d | |
271 | cat <<EOF > container-getty\@0.service.d/00gentoo.conf | |
272 | [Service] | |
273 | Environment=TERM= | |
274 | Environment=TERM=linux | |
275 | EOF | |
276 | ||
11f88f10 G |
277 | return 0 |
278 | } | |
279 | ||
280 | configure_container() { | |
281 | local config="$1" | |
282 | local hostname="$2" | |
283 | local arch="$3" | |
284 | local privileged_options="" | |
285 | local unprivileged_options="" | |
286 | ||
287 | if [[ $unprivileged && $unprivileged == true ]] ; then | |
3d288bbe G |
288 | if [[ $flush_owner == true ]] ; then |
289 | unprivileged_options=" | |
11f88f10 G |
290 | lxc.id_map = u 0 ${mapped_uid} 65536 |
291 | lxc.id_map = g 0 ${mapped_gid} 65536 | |
3d288bbe G |
292 | " |
293 | fi | |
294 | ||
295 | unprivileged_options=" | |
296 | $unprivileged_options | |
74e75741 G |
297 | |
298 | # Include common configuration. | |
299 | lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.userns.conf | |
11f88f10 G |
300 | " |
301 | ||
302 | else | |
303 | privileged_options=" | |
74e75741 | 304 | ## Allow any mknod (but not reading/writing the node) |
11f88f10 G |
305 | lxc.cgroup.devices.allow = b *:* m |
306 | lxc.cgroup.devices.allow = c *:* m | |
74e75741 G |
307 | |
308 | ### /dev/pts/* | |
11f88f10 | 309 | lxc.cgroup.devices.allow = c 136:* rwm |
74e75741 | 310 | ### /dev/tty |
11f88f10 | 311 | lxc.cgroup.devices.allow = c 5:0 rwm |
74e75741 | 312 | ### /dev/console |
11f88f10 | 313 | lxc.cgroup.devices.allow = c 5:1 rwm |
74e75741 | 314 | ### /dev/ptmx |
11f88f10 | 315 | lxc.cgroup.devices.allow = c 5:2 rwm |
74e75741 | 316 | ### fuse |
11f88f10 | 317 | lxc.cgroup.devices.allow = c 10:229 rwm |
74e75741 | 318 | |
11f88f10 G |
319 | " |
320 | fi | |
321 | ||
322 | cat <<-EOF >> "$config" | |
323 | # Specify container architecture. | |
324 | lxc.arch = $arch | |
325 | ||
326 | # Set hostname. | |
b67771bc | 327 | lxc.uts.name = $hostname |
11f88f10 | 328 | |
74e75741 G |
329 | # Include common configuration. |
330 | lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.common.conf | |
11f88f10 G |
331 | |
332 | $unprivileged_options | |
333 | $privileged_options | |
11f88f10 G |
334 | EOF |
335 | } | |
336 | ||
337 | ||
338 | #============================= Main ==============================# | |
339 | ||
340 | parse_cmdline() { | |
341 | ||
342 | # Parse command options. | |
343 | local short_options="a:dm:n:p:r:hu" | |
3d288bbe | 344 | local long_options="arch:,debug,mirror:,name:,path:,release:,rootfs:,mapped-uid:,mapped-gid:,flush-owner,help" |
11f88f10 G |
345 | |
346 | options=$(getopt -u -q -a -o "$short_options" -l "$long_options" -- "$@") | |
347 | ||
348 | eval set -- "$options" | |
349 | ||
350 | # Process command options. | |
351 | while [ $# -gt 0 ]; do | |
352 | case $1 in | |
353 | -a | --arch) | |
354 | arch=$2 | |
355 | shift | |
356 | ;; | |
357 | -d | --debug) | |
358 | debug='yes' | |
359 | ;; | |
360 | -m | --mirror) | |
361 | mirror_url=$2 | |
362 | shift | |
363 | ;; | |
364 | -n | --name) | |
365 | name=$2 | |
366 | shift | |
367 | ;; | |
368 | -p | --path) | |
369 | path=$2 | |
370 | shift | |
371 | ;; | |
372 | -r | --release) | |
373 | release=$2 | |
374 | shift | |
375 | ;; | |
376 | --rootfs) | |
377 | rootfs=$2 | |
378 | shift | |
379 | ;; | |
380 | -u | --unprivileged) | |
381 | unprivileged=true | |
382 | ;; | |
383 | -h | --help) | |
384 | usage | |
385 | exit 1 | |
386 | ;; | |
387 | --mapped-uid) | |
388 | mapped_uid=$2 | |
389 | shift | |
390 | ;; | |
391 | --mapped-gid) | |
392 | mapped_gid=$2 | |
393 | shift | |
394 | ;; | |
3d288bbe G |
395 | --flush-owner) |
396 | flush_owner=true | |
397 | ;; | |
11f88f10 G |
398 | --) |
399 | break | |
400 | ;; | |
401 | *) | |
402 | einfo "Unknown option: $1" | |
403 | usage | |
404 | exit 1 | |
405 | ;; | |
406 | esac | |
407 | shift | |
408 | done | |
409 | ||
410 | if [ "$(id -u)" != "0" ]; then | |
411 | die 1 "This script must be run as 'root'" | |
412 | fi | |
413 | ||
414 | # Validate options. | |
415 | [ -n "$name" ] || die 1 'Missing required option --name' | |
416 | [ -n "$path" ] || die 1 'Missing required option --path' | |
417 | ||
418 | if [ -z "$rootfs" ] && [ -f "$path/config" ]; then | |
419 | rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" | |
420 | fi | |
421 | if [ -z "$rootfs" ]; then | |
422 | rootfs="$path/rootfs" | |
423 | fi | |
424 | ||
425 | [ -z "$path" ] && die 1 "'path' parameter is required." | |
426 | ||
427 | arch=$(parse_arch "$arch") \ | |
428 | || die 1 "Unsupported architecture: $arch" | |
429 | ||
3d288bbe | 430 | [[ $unprivileged == true && $flush_owner == true &&-z "$mapped_uid" ]] && \ |
11f88f10 G |
431 | die 1 'Missing required option --mapped-uid with --unprivileged option' |
432 | ||
3d288bbe | 433 | [[ $unprivileged == true && $flush_owner == true && -z "$mapped_gid" ]] && \ |
11f88f10 G |
434 | die 1 'Missing required option --mapped-gid with --unprivileged option' |
435 | ||
3d288bbe G |
436 | [[ $flush_owner == true && $unprivileged == false ]] && \ |
437 | die 1 'flush-owner require --unprivileged option' | |
438 | ||
11f88f10 G |
439 | return 0 |
440 | } | |
441 | ||
442 | main () { | |
443 | ||
444 | local tarball="" | |
445 | ||
446 | # Set global variables. | |
447 | RELEASE="${RELEASE:-"DAILY"}" | |
448 | ARCH="${ARCH:-`uname -m`}" | |
449 | OS="${OS:-"sabayon"}" | |
450 | ||
451 | einfo "Processing command line arguments: $@" | |
452 | ||
453 | # Parse command line options | |
454 | parse_cmdline "$@" | |
455 | ||
456 | DEBUG="$debug" | |
457 | MIRROR_URL="${mirror_url:-$(random_mirror_url)}" | |
458 | ||
3d288bbe | 459 | einfo "Use arch = $arch, mirror_url = $MIRROR_URL, path = $path, name = $name, release = $release, unprivileged = $unprivileged, rootfs = $rootfs, mapped_uid = $mapped_uid, mapped_gid = $mapped_gid, flush_owner = $flush_owner" |
11f88f10 G |
460 | |
461 | [ "$debug" = 'yes' ] && set -x | |
462 | ||
463 | # Download sabayon tarball | |
464 | tarball=$(create_url) | |
465 | einfo "Fetching tarball $tarball..." | |
466 | ||
467 | # TODO: use only a compression mode | |
468 | if [ $arch == 'amd64' ] ; then | |
469 | fetch "${tarball}" | tar -xpz -C "${rootfs}" | |
470 | else | |
471 | if [ $arch == 'armv7l' ] ; then | |
472 | fetch "${tarball}" | tar -xpj -C "${rootfs}" | |
473 | fi | |
474 | fi | |
475 | ||
476 | einfo "Tarball ${tarball} Extracted." | |
477 | ||
478 | systemd_container_tuning | |
479 | ||
480 | # Fix container for unprivileged mode. | |
3d288bbe | 481 | if [[ $unprivileged == true ]] ; then |
11f88f10 | 482 | unprivileged_rootfs |
3d288bbe G |
483 | if [[ $flush_owner == true ]] ; then |
484 | unprivileged_shift_owner | |
485 | fi | |
11f88f10 G |
486 | fi |
487 | ||
488 | return 0 | |
489 | } | |
490 | ||
491 | ||
492 | einfo "Prepare creation of sabayon container with params: $@ ($#)" | |
493 | ||
494 | # Here we go! | |
495 | run_exclusively 'main' 10 main "$@" | |
496 | configure_container "$path/config" "$name" "$arch" | |
497 | ||
498 | einfo "Container's rootfs and config have been created" | |
499 | cat <<-EOF | |
500 | Edit the config file $path/config to check/enable networking setup. | |
501 | The installed system is preconfigured for a loopback and single network | |
502 | interface configured via DHCP. | |
503 | ||
504 | To start the container, run "lxc-start -n $name". | |
505 | The root password is not set; to enter the container run "lxc-attach -n $name". | |
506 | ||
507 | Note: From kenel >= 4.6 for use unprivileged containers it is needed this mount on host: | |
508 | ||
509 | mkdir /sys/fs/cgroup/systemd | |
510 | mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd | |
511 | EOF |