]> git.proxmox.com Git - mirror_lxc.git/blame - templates/lxc-download.in
coverity: free gw when not used
[mirror_lxc.git] / templates / lxc-download.in
CommitLineData
71d3a659
SG
1#!/bin/sh
2
3# Client script for LXC container images.
4#
5# Copyright © 2014 Stéphane Graber <stgraber@ubuntu.com>
6#
7# This library is free software; you can redistribute it and/or
8# modify it under the terms of the GNU Lesser General Public
9# License as published by the Free Software Foundation; either
10# version 2.1 of the License, or (at your option) any later version.
11
12# This library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# Lesser General Public License for more details.
16
17# You should have received a copy of the GNU Lesser General Public
18# License along with this library; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20# USA
21
22set -eu
23
24LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
25LXC_HOOK_DIR="@LXCHOOKDIR@"
26LOCALSTATEDIR="@LOCALSTATEDIR@"
27
28# Defaults
29DOWNLOAD_DIST=
30DOWNLOAD_RELEASE=
31DOWNLOAD_ARCH=
32DOWNLOAD_VARIANT="default"
33DOWNLOAD_SERVER="images.linuxcontainers.org"
34DOWNLOAD_KEYID="0xBAEFF88C22F6E216"
35DOWNLOAD_KEYSERVER="pool.sks-keyservers.net"
36DOWNLOAD_VALIDATE="true"
37DOWNLOAD_FLUSH_CACHE="false"
41670b35 38DOWNLOAD_FORCE_CACHE="false"
71d3a659
SG
39DOWNLOAD_MODE="system"
40DOWNLOAD_USE_CACHE="false"
41DOWNLOAD_URL=
42DOWNLOAD_SHOW_HTTP_WARNING="true"
43DOWNLOAD_SHOW_GPG_WARNING="true"
44DOWNLOAD_COMPAT_LEVEL=1
10a5fab6 45DOWNLOAD_LIST_IMAGES="false"
9accc2ef 46DOWNLOAD_BUILD=
71d3a659
SG
47
48LXC_NAME=
49LXC_PATH=
50LXC_ROOTFS=
51LXC_MAPPED_UID=
52
53# Some useful functions
54cleanup() {
55 if [ -d "$DOWNLOAD_TEMP" ]; then
56 rm -Rf $DOWNLOAD_TEMP
57 fi
58}
59
60download_file() {
61 if ! wget -q https://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
62 if ! wget -q http://${DOWNLOAD_SERVER}/$1 -O $2 >/dev/null 2>&1; then
63 if [ "$3" = "noexit" ]; then
64 return 1
65 else
fad96766 66 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
71d3a659
SG
67 exit 1
68 fi
69 elif [ "$DOWNLOAD_SHOW_HTTP_WARNING" = "true" ]; then
70 DOWNLOAD_SHOW_HTTP_WARNING="false"
71 echo "WARNING: Failed to download the file over HTTPs." 1>&2
72 echo -n " The file was instead download over HTTP. " 1>&2
73 echo "A server replay attack may be possible!" 1>&2
74 fi
75 fi
76}
77
fad96766 78download_sig() {
33aa351a
SG
79 if ! download_file $1 $2 noexit; then
80 if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
81 if [ "$3" = "normal" ]; then
82 echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
83 exit 1
84 else
85 return 1
86 fi
87 else
88 return 0
89 fi
fad96766
DE
90 fi
91}
92
71d3a659
SG
93gpg_setup() {
94 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
95 return
96 fi
97
98 echo "Setting up the GPG keyring"
99
100 mkdir -p "$DOWNLOAD_TEMP/gpg"
101 chmod 700 "$DOWNLOAD_TEMP/gpg"
102 export GNUPGHOME="$DOWNLOAD_TEMP/gpg"
103 if ! gpg --keyserver $DOWNLOAD_KEYSERVER \
104 --recv-keys ${DOWNLOAD_KEYID} >/dev/null 2>&1; then
105 echo "ERROR: Unable to fetch GPG key from keyserver."
106 exit 1
107 fi
108}
109
110gpg_validate() {
111 if [ "$DOWNLOAD_VALIDATE" = "false" ]; then
112 if [ "$DOWNLOAD_SHOW_GPG_WARNING" = "true" ]; then
113 echo "WARNING: Running without gpg validation!" 1>&2
114 fi
115 DOWNLOAD_SHOW_GPG_WARNING="false"
116 return 0
117 fi
118
119 if ! gpg --verify $1 >/dev/zero 2>&1; then
120 echo "ERROR: Invalid signature for $1" 1>&2
121 exit 1
122 fi
123}
124
125in_userns() {
126 [ -e /proc/self/uid_map ] || { echo no; return; }
127 [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || \
128 { echo yes; return; }
129 line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
130 [ "$line" = "0 0 4294967295" ] && { echo no; return; }
131 echo yes
132}
133
134relevant_file() {
135 FILE_PATH="${LXC_CACHE_PATH}/$1"
136 if [ -e "${FILE_PATH}-${DOWNLOAD_MODE}" ]; then
137 FILE_PATH="${FILE_PATH}-${DOWNLOAD_MODE}"
138 fi
139 if [ -e "$FILE_PATH.${DOWNLOAD_COMPAT_LEVEL}" ]; then
140 FILE_PATH="${FILE_PATH}.${DOWNLOAD_COMPAT_LEVEL}"
141 fi
142
143 echo $FILE_PATH
144}
145
146usage() {
147 cat <<EOF
148LXC container image downloader
149
150Required arguments:
151[ -d | --dist <distribution> ]: The name of the distribution
152[ -r | --release <release> ]: Release name/version
153[ -a | --arch <architecture> ]: Architecture of the container
71d3a659
SG
154
155Optional arguments:
10a5fab6
SG
156[ -h | --help ]: This help message
157[ -l | --list ]: List all available images
71d3a659
SG
158[ --variant <variant> ]: Variant of the image (default: "default")
159[ --server <server> ]: Image server (default: "images.linuxcontainers.org")
160[ --keyid <keyid> ]: GPG keyid (default: 0x...)
161[ --keyserver <keyserver> ]: GPG keyserver to use
162[ --no-validate ]: Disable GPG validation (not recommended)
163[ --flush-cache ]: Flush the local copy (if present)
9accc2ef 164[ --force-cache ]; Force the use of the local copy even if expired
71d3a659
SG
165
166LXC internal arguments (do not pass manually!):
167[ --name <name> ]: The container name
168[ --path <path> ]: The path to the container
169[ --rootfs <rootfs> ]: The path to the container's rootfs
170[ --mapped-uid <map> ]: A uid/gid map (user namespaces)
171EOF
172 return 0
173}
174
10a5fab6 175options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\
c1becef2 176server:,keyid:,no-validate,flush-cache,force-cache,name:,path:,\
9accc2ef 177rootfs:,mapped-uid: -- "$@")
71d3a659
SG
178
179if [ $? -ne 0 ]; then
180 usage
181 exit 1
182fi
183eval set -- "$options"
184
185while :; do
186 case "$1" in
10a5fab6
SG
187 -h|--help) usage && exit 1;;
188 -l|--list) DOWNLOAD_LIST_IMAGES="true"; shift 1;;
71d3a659
SG
189 -d|--dist) DOWNLOAD_DIST=$2; shift 2;;
190 -r|--release) DOWNLOAD_RELEASE=$2; shift 2;;
191 -a|--arch) DOWNLOAD_ARCH=$2; shift 2;;
192 --variant) DOWNLOAD_VARIANT=$2; shift 2;;
193 --server) DOWNLOAD_SERVER=$2; shift 2;;
194 --keyid) DOWNLOAD_KEYID=$2; shift 2;;
195 --no-validate) DOWNLOAD_VALIDATE="false"; shift 1;;
196 --flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;;
9accc2ef 197 --force-cache) DOWNLOAD_FORCE_CACHE="true"; shift 1;;
71d3a659
SG
198 --name) LXC_NAME=$2; shift 2;;
199 --path) LXC_PATH=$2; shift 2;;
200 --rootfs) LXC_ROOTFS=$2; shift 2;;
201 --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
202 *) break;;
203 esac
204done
205
206# Check for required binaries
207for bin in tar xz wget; do
208 if ! type $bin >/dev/null 2>&1; then
209 echo "ERROR: Missing required tool: $bin" 1>&2
210 exit 1
211 fi
212done
213
214# Check for GPG
215if [ "$DOWNLOAD_VALIDATE" = "true" ]; then
216 if ! type gpg >/dev/null 2>&1; then
217 echo "ERROR: Missing recommended tool: gpg" 1>&2
218 echo "You can workaround this by using --no-validate." 1>&2
219 exit 1
220 fi
221fi
222
223# Check that we have all variables we need
224if [ -z "$LXC_NAME" ] || [ -z "$LXC_PATH" ] || [ -z "$LXC_ROOTFS" ]; then
225 echo "ERROR: Not running through LXC." 1>&2
226 exit 1
227fi
228
229if [ "$(in_userns)" = "yes" ]; then
230 if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then
231 echo "ERROR: In a user namespace without a map." 1>&2
232 exit 1
233 fi
234 DOWNLOAD_MODE="user"
235fi
236
10a5fab6
SG
237if ([ -z "$DOWNLOAD_DIST" ] || [ -z "$DOWNLOAD_RELEASE" ] || \
238 [ -z "$DOWNLOAD_ARCH" ]) && [ "$DOWNLOAD_LIST_IMAGES" = "false" ]; then
71d3a659
SG
239 echo "ERROR: Missing required argument" 1>&2
240 usage
241 exit 1
242fi
243
244# Trap all exit signals
245trap cleanup EXIT HUP INT TERM
246DOWNLOAD_TEMP=$(mktemp -d)
247
10a5fab6
SG
248# Simply list images
249if [ "$DOWNLOAD_LIST_IMAGES" = "true" ]; then
250 # Initialize GPG
251 gpg_setup
252
253 # Grab the index
254 DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE}
255
256 echo "Downloading the image index"
257 if ! download_file ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL} \
258 ${DOWNLOAD_TEMP}/index noexit ||
259 ! download_sig ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc \
260 ${DOWNLOAD_TEMP}/index.asc noexit; then
261 download_file ${DOWNLOAD_INDEX_PATH} ${DOWNLOAD_TEMP}/index normal
262 download_sig ${DOWNLOAD_INDEX_PATH}.asc \
263 ${DOWNLOAD_TEMP}/index.asc normal
264 fi
265
266 gpg_validate ${DOWNLOAD_TEMP}/index.asc
267
268 # Parse it
269 echo ""
270 echo "---"
271 echo "DIST\tRELEASE\tARCH\tVARIANT\tBUILD"
272 echo "---"
273 while read line; do
274 # Basic CSV parser
275 OLD_IFS=$IFS
276 IFS=";"
277 set -- $line
278 IFS=$OLD_IFS
279
280 [ -n "$DOWNLOAD_DIST" ] && [ "$1" != "$DOWNLOAD_DIST" ] && continue
281 [ -n "$DOWNLOAD_RELEASE" ] && [ "$2" != "$DOWNLOAD_RELEASE" ] && continue
282 [ -n "$DOWNLOAD_ARCH" ] && [ "$3" != "$DOWNLOAD_ARCH" ] && continue
283 [ -n "$DOWNLOAD_VARIANT" ] && [ "$4" != "$DOWNLOAD_VARIANT" ] && continue
284 [ -z "$5" ] || [ -z "$6" ] && continue
285
286 echo "$1\t$2\t$3\t$4\t$5"
287 done < ${DOWNLOAD_TEMP}/index
288 echo "---"
289
290 exit 1
291fi
292
71d3a659
SG
293# Setup the cache
294if [ "$DOWNLOAD_MODE" = "system" ]; then
295 LXC_CACHE_BASE="$LOCALSTATEDIR/cache/"
296 LXC_CACHE_PATH="$LOCALSTATEDIR/cache/lxc/download/$DOWNLOAD_DIST"
297 LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH"
298else
299 LXC_CACHE_BASE="$HOME/.cache/lxc/"
300 LXC_CACHE_PATH="$HOME/.cache/lxc/download/$DOWNLOAD_DIST"
301 LXC_CACHE_PATH="$LXC_CACHE_PATH/$DOWNLOAD_RELEASE/$DOWNLOAD_ARCH"
302fi
303
304if [ -d "$LXC_CACHE_PATH" ]; then
305 if [ "$DOWNLOAD_FLUSH_CACHE" = "true" ]; then
306 echo "Flushing the cache..."
307 rm -Rf $LXC_CACHE_PATH
9accc2ef
SG
308 elif [ "$DOWNLOAD_FORCE_CACHE" = "true" ]; then
309 DOWNLOAD_USE_CACHE="true"
71d3a659
SG
310 else
311 DOWNLOAD_USE_CACHE="true"
312 if [ -e "$(relevant_file expiry)" ]; then
313 if [ "$(cat $(relevant_file expiry))" -lt $(date +%s) ]; then
314 echo "The cached copy has expired, re-downloading..."
315 DOWNLOAD_USE_CACHE="false"
71d3a659
SG
316 fi
317 fi
318 fi
319fi
320
321# Download what's needed
322if [ "$DOWNLOAD_USE_CACHE" = "false" ]; then
323 # Initialize GPG
324 gpg_setup
325
326 # Grab the index
327 DOWNLOAD_INDEX_PATH=/meta/1.0/index-${DOWNLOAD_MODE}
328
329 echo "Downloading the image index"
330 if ! download_file ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL} \
331 ${DOWNLOAD_TEMP}/index noexit ||
33aa351a 332 ! download_sig ${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc \
71d3a659
SG
333 ${DOWNLOAD_TEMP}/index.asc noexit; then
334 download_file ${DOWNLOAD_INDEX_PATH} ${DOWNLOAD_TEMP}/index normal
fad96766 335 download_sig ${DOWNLOAD_INDEX_PATH}.asc \
33aa351a 336 ${DOWNLOAD_TEMP}/index.asc normal
71d3a659
SG
337 fi
338
339 gpg_validate ${DOWNLOAD_TEMP}/index.asc
340
341 # Parse it
342 while read line; do
343 # Basic CSV parser
344 OLD_IFS=$IFS
345 IFS=";"
346 set -- $line
347 IFS=$OLD_IFS
348
349 if [ "$1" != "$DOWNLOAD_DIST" ] || \
350 [ "$2" != "$DOWNLOAD_RELEASE" ] || \
351 [ "$3" != "$DOWNLOAD_ARCH" ] || \
352 [ "$4" != "$DOWNLOAD_VARIANT" ] || \
353 [ -z "$6" ]; then
354 continue
355 fi
356
9accc2ef 357 DOWNLOAD_BUILD=$5
71d3a659
SG
358 DOWNLOAD_URL=$6
359 break
360 done < ${DOWNLOAD_TEMP}/index
361
362 if [ -z "$DOWNLOAD_URL" ]; then
363 echo "ERROR: Couldn't find a matching image." 1>&1
364 exit 1
365 fi
366
9accc2ef
SG
367 if [ -d "$LXC_CACHE_PATH" ] && [ -f "$LXC_CACHE_PATH/build_id" ] && \
368 [ "$(cat $LXC_CACHE_PATH/build_id)" = "$DOWNLOAD_BUILD" ]; then
369 echo "The cache is already up to date."
370 echo "Using image from local cache"
371 else
372 # Download the actual files
373 echo "Downloading the rootfs"
374 download_file $DOWNLOAD_URL/rootfs.tar.xz \
375 ${DOWNLOAD_TEMP}/rootfs.tar.xz normal
376 download_sig $DOWNLOAD_URL/rootfs.tar.xz.asc \
377 ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc normal
378 gpg_validate ${DOWNLOAD_TEMP}/rootfs.tar.xz.asc
379
380 echo "Downloading the metadata"
381 download_file $DOWNLOAD_URL/meta.tar.xz \
382 ${DOWNLOAD_TEMP}/meta.tar.xz normal
383 download_sig $DOWNLOAD_URL/meta.tar.xz.asc \
384 ${DOWNLOAD_TEMP}/meta.tar.xz.asc normal
385 gpg_validate ${DOWNLOAD_TEMP}/meta.tar.xz.asc
386
387 if [ -d $LXC_CACHE_PATH ]; then
388 rm -Rf $LXC_CACHE_PATH
389 fi
390 mkdir -p $LXC_CACHE_PATH
391 mv ${DOWNLOAD_TEMP}/rootfs.tar.xz $LXC_CACHE_PATH
392 if ! tar Jxf ${DOWNLOAD_TEMP}/meta.tar.xz -C $LXC_CACHE_PATH; then
393 echo "ERROR: Invalid rootfs tarball." 2>&1
394 exit 1
395 fi
71d3a659 396
9accc2ef
SG
397 echo $DOWNLOAD_BUILD > $LXC_CACHE_PATH/build_id
398
399 if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
0d656b05 400 chown -R $LXC_MAPPED_UID $LXC_CACHE_BASE >/dev/null 2>&1 || true
9accc2ef
SG
401 fi
402 echo "The image cache is now ready"
71d3a659 403 fi
71d3a659
SG
404else
405 echo "Using image from local cache"
406fi
407
408# Unpack the rootfs
409echo "Unpacking the rootfs"
fecf101c
SG
410
411EXCLUDES=""
412excludelist=$(relevant_file excludes)
413if [ -f "${excludelist}" ]; then
414 while read line; do
415 EXCLUDES="$EXCLUDES --exclude=$line"
416 done < $excludelist
71d3a659
SG
417fi
418
fecf101c
SG
419tar --anchored ${EXCLUDES} --numeric-owner -xpJf \
420 ${LXC_CACHE_PATH}/rootfs.tar.xz -C ${LXC_ROOTFS}
421
422mkdir -p ${LXC_ROOTFS}/dev/pts/
423
71d3a659
SG
424# Setup the configuration
425configfile=$(relevant_file config)
426fstab=$(relevant_file fstab)
427if [ ! -e $configfile ]; then
428 echo "ERROR: meta tarball is missing the configuration file" 1>&2
429 exit 1
430fi
431
432## Extract all the network config entries
433sed -i -e "/lxc.network/{w ${LXC_PATH}/config-network" -e "d}" \
434 ${LXC_PATH}/config
435
436## Extract any other config entry
437sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" ${LXC_PATH}/config
438
439## Append the defaults
440echo "" >> ${LXC_PATH}/config
441echo "# Distribution configuration" >> ${LXC_PATH}/config
442cat $configfile >> ${LXC_PATH}/config
443
444## Add the container-specific config
445echo "" >> ${LXC_PATH}/config
446echo "# Container specific configuration" >> ${LXC_PATH}/config
447if [ -e "${LXC_PATH}/config-auto" ]; then
448 cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
449 rm ${LXC_PATH}/config-auto
450fi
451if [ -e "$fstab" ]; then
452 echo "lxc.mount = ${LXC_PATH}/fstab" >> ${LXC_PATH}/config
453fi
454echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
455
456## Re-add the previously removed network config
457if [ -e "${LXC_PATH}/config-network" ]; then
458 echo "" >> ${LXC_PATH}/config
459 echo "# Network configuration" >> ${LXC_PATH}/config
460 cat ${LXC_PATH}/config-network >> ${LXC_PATH}/config
461 rm ${LXC_PATH}/config-network
462fi
463
464TEMPLATE_FILES="${LXC_PATH}/config"
465
466# Setup the fstab
467if [ -e $fstab ]; then
468 cp ${fstab} ${LXC_PATH}/fstab
469 TEMPLATE_FILES="$TEMPLATE_FILES ${LXC_PATH}/fstab"
470fi
471
472# Look for extra templates
473if [ -e "$(relevant_file templates)" ]; then
474 while read line; do
475 fullpath=${LXC_ROOTFS}/$line
476 [ ! -e "$fullpath" ] && continue
477 TEMPLATE_FILES="$TEMPLATE_FILES $fullpath"
478 done < $(relevant_file templates)
479fi
480
481# Replace variables in all templates
482for file in $TEMPLATE_FILES; do
fad96766 483 [ ! -f "$file" ] && continue
71d3a659
SG
484
485 sed -i "s#LXC_NAME#$LXC_NAME#g" $file
486 sed -i "s#LXC_PATH#$LXC_PATH#g" $file
487 sed -i "s#LXC_ROOTFS#$LXC_ROOTFS#g" $file
488 sed -i "s#LXC_TEMPLATE_CONFIG#$LXC_TEMPLATE_CONFIG#g" $file
489 sed -i "s#LXC_HOOK_DIR#$LXC_HOOK_DIR#g" $file
490done
491
492if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
0d656b05 493 chown $LXC_MAPPED_UID $LXC_PATH/config $LXC_PATH/fstab >/dev/null 2>&1 || true
71d3a659
SG
494fi
495
496if [ -e "$(relevant_file create-message)" ]; then
497 echo ""
498 echo "---"
499 cat "$(relevant_file create-message)"
500fi
501
502exit 0