xref: /illumos-gate/usr/src/lib/brand/shared/zone/common.ksh (revision edfa49ff6d1bd39465e21e3b28aee863e91c5e3f)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
22# Use is subject to license terms.
23#
24
25#
26# Send the error message to the screen and to the logfile.
27#
28error()
29{
30        typeset fmt="$1"
31        shift
32
33        printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
34        [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2
35}
36
37fatal()
38{
39        typeset fmt="$1"
40        shift
41
42	error "$fmt" "$@"
43	exit $EXIT_CODE
44}
45
46#
47# Send the provided printf()-style arguments to the screen and to the logfile.
48#
49log()
50{
51        typeset fmt="$1"
52        shift
53
54        printf "${MSG_PREFIX}${fmt}\n" "$@"
55        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
56}
57
58#
59# Print provided text to the screen if the shell variable "OPT_V" is set.
60# The text is always sent to the logfile.
61#
62vlog()
63{
64        typeset fmt="$1"
65        shift
66
67        [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
68        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
69}
70
71# Validate that the directory is safe.
72safe_dir()
73{
74	typeset dir="$1"
75
76	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
77		fatal "$e_baddir" "$dir"
78	fi
79}
80
81# Only make a copy if we haven't already done so.
82safe_backup()
83{
84	typeset src="$1"
85	typeset dst="$2"
86
87	if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then
88		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
89	fi
90}
91
92# Make a copy even if the destination already exists.
93safe_copy()
94{
95	typeset src="$1"
96	typeset dst="$2"
97
98	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
99		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
100	fi
101}
102
103# Move a file
104safe_move()
105{
106	typeset src="$1"
107	typeset dst="$2"
108
109	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
110		/usr/bin/mv $src $dst || fatal "$e_badfile" "$src"
111	fi
112}
113
114#
115# Read zonecfg ipd and fs entries and save the relevant data, one entry per
116# line.
117# This assumes the properties from the zonecfg output, e.g.:
118#	inherit-pkg-dir:
119#		dir: /usr
120#	fs:
121#		dir: /opt
122#		special: /opt
123#		raw not specified
124#		type: lofs
125#		options: [noexec,ro,noatime]
126#
127# and it assumes the order of the fs properties as above.  This also saves the
128# inherit-pkg-dir patterns into the ipd.{cpio|pax} temporary files for
129# filtering while extracting the image into the zonepath.  We have to save the
130# IPD patterns in the appropriate format for filtering with the different
131# archivers and we don't know what format we'll get until after the flash
132# archive is unpacked.
133#
134get_fs_info()
135{
136	zonecfg -z $zonename info inherit-pkg-dir | \
137	    nawk -v ipdcpiof=$ipdcpiofile -v ipdpaxf=$ipdpaxfile '{
138		if ($1 == "dir:") {
139			dir=$2;
140			printf("%s lofs %s ro\n", dir, dir);
141
142			if (substr(dir, 1, 1) == "/") {
143				printf("%s\n", substr(dir, 2)) >> ipdcpiof
144				printf("%s/*\n", substr(dir, 2)) >> ipdcpiof
145			} else {
146				printf("%s\n", dir) >> ipdcpiof
147				printf("%s/*\n", dir) >> ipdcpiof
148			}
149
150			if (substr(dir, 1, 1) == "/") {
151				printf("%s ", substr(dir, 2)) >> ipdpaxf
152			} else {
153				printf("%s ", dir) >> ipdpaxf
154			}
155		}
156	}' >> $fstmpfile
157
158	zonecfg -z $zonename info fs | nawk '{
159		if ($1 == "options:") {
160			# Remove brackets.
161			options=substr($2, 2, length($2) - 2);
162			printf("%s %s %s %s\n", dir, type, special, options);
163		} else if ($1 == "dir:") {
164			dir=$2;
165		} else if ($1 == "special:") {
166			special=$2;
167		} else if ($1 == "type:") {
168			type=$2
169		}
170	}' >> $fstmpfile
171}
172
173#
174# Mount zonecfg fs entries into the zonepath.
175#
176mnt_fs()
177{
178	if [ ! -s $fstmpfile ]; then
179		return;
180	fi
181
182	# Sort the fs entries so we can handle nested mounts.
183	sort $fstmpfile | nawk -v zonepath=$zonepath '{
184		if (NF == 4)
185			options="-o " $4;
186		else
187			options=""
188
189		# Create the mount point.  Ignore errors since we might have
190		# a nested mount with a pre-existing mount point.
191		cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1"
192		system(cmd);
193
194		cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \
195		    zonepath "/root" $1;
196		if (system(cmd) != 0) {
197			printf("command failed: %s\n", cmd);
198			exit 1;
199		}
200	}' >>$LOGFILE
201}
202
203#
204# Unmount zonecfg fs entries from the zonepath.
205#
206umnt_fs()
207{
208	if [ ! -s $fstmpfile ]; then
209		return;
210	fi
211
212	# Reverse sort the fs entries so we can handle nested unmounts.
213	sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
214		cmd="/usr/sbin/umount " zonepath "/root" $1
215		if (system(cmd) != 0) {
216			printf("command failed: %s\n", cmd);
217		}
218	}' >>$LOGFILE
219}
220
221#
222# Determine flar compression style from identification file.
223#
224get_compression()
225{
226	typeset ident=$1
227	typeset line=$(grep "^files_compressed_method=" $ident)
228
229	print ${line##*=}
230}
231
232#
233# Determine flar archive style from identification file.
234#
235get_archiver()
236{
237        typeset ident=$1
238        typeset line=$(grep "^files_archived_method=" $ident)
239
240        print ${line##*=}
241}
242
243#
244# Unpack flar into current directory (which should be zoneroot).  The flash
245# archive is standard input.  See flash_archive(4) man page.
246#
247# We can't use "flar split" since it will only unpack into a directory called
248# "archive".  We need to unpack in place in order to properly handle nested
249# fs mounts within the zone root.  This function does the unpacking into the
250# current directory.
251#
252# This code is derived from the gen_split() function in /usr/sbin/flar so
253# we keep the same style as the original.
254#
255install_flar()
256{
257	typeset result
258        typeset archiver_command
259        typeset archiver_arguments
260
261	vlog "cd $ZONEROOT && do_flar < \"$install_archive\""
262
263	# Read cookie
264	read -r input_line
265	if (( $? != 0 )); then
266		log "$not_readable" "$install_media"
267		return 1
268	fi
269	# The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
270	if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
271		log "$not_flar"
272		return 1
273	fi
274
275	while [ true ]
276	do
277		# We should always be at the start of a section here
278		read -r input_line
279		if [[ ${input_line%%=*} != "section_begin" ]]; then
280			log "$bad_flar"
281			return 1
282		fi
283		section_name=${input_line##*=}
284
285		# If we're at the archive, we're done skipping sections.
286		if [[ "$section_name" == "archive" ]]; then
287			break
288		fi
289
290		#
291		# Save identification section to a file so we can determine
292		# how to unpack the archive.
293		#
294		if [[ "$section_name" == "identification" ]]; then
295			/usr/bin/rm -f identification
296			while read -r input_line
297			do
298				if [[ ${input_line%%=*} == \
299				    "section_begin" ]]; then
300					/usr/bin/rm -f identification
301					log "$bad_flar"
302					return 1
303				fi
304
305				if [[ $input_line == \
306				    "section_end=$section_name" ]]; then
307					break;
308				fi
309				echo $input_line >> identification
310			done
311
312			continue
313		fi
314
315		#
316		# Otherwise skip past this section; read lines until detecting
317		# section_end.  According to flash_archive(4) we can have
318		# an arbitrary number of sections but the archive section
319		# must be last.
320		#
321		success=0
322		while read -r input_line
323		do
324			if [[ $input_line == "section_end=$section_name" ]];
325			then
326				success=1
327				break
328			fi
329			# Fail if we miss the end of the section
330			if [[ ${input_line%%=*} == "section_begin" ]]; then
331				/usr/bin/rm -f identification
332				log "$bad_flar"
333				return 1
334			fi
335		done
336		if (( $success == 0 )); then
337			#
338			# If we get here we read to the end of the file before
339			# seeing the end of the section we were reading.
340			#
341			/usr/bin/rm -f identification
342			log "$bad_flar"
343			return 1
344		fi
345	done
346
347	# Get the information needed to unpack the archive.
348	archiver=$(get_archiver identification)
349	if [[ $archiver == "pax" ]]; then
350		# pax archiver specified
351		archiver_command="/usr/bin/pax"
352		if [[ -s $ipdpaxfile ]]; then
353			archiver_arguments="-r -p e -c \
354			    $(/usr/bin/cat $ipdpaxfile)"
355		else
356			archiver_arguments="-r -p e"
357		fi
358	elif [[ $archiver == "cpio" || -z $archiver ]]; then
359		# cpio archived specified OR no archiver specified - use default
360		archiver_command="/usr/bin/cpio"
361		archiver_arguments="-icdumfE $ipdcpiofile"
362	else
363		# unknown archiver specified
364		log "$unknown_archiver" $archiver
365		return 1
366	fi
367
368	if [[ ! -x $archiver_command ]]; then
369		/usr/bin/rm -f identification
370		log "$cmd_not_exec" $archiver_command
371		return 1
372	fi
373
374	compression=$(get_compression identification)
375
376	# We're done with the identification file
377	/usr/bin/rm -f identification
378
379	# Extract archive
380	if [[ $compression == "compress" ]]; then
381		/usr/bin/zcat | ppriv -e -s A=all,-sys_devices \
382		    $archiver_command $archiver_arguments 2>/dev/null
383	else
384		ppriv -e -s A=all,-sys_devices \
385		    $archiver_command $archiver_arguments 2>/dev/null
386	fi
387	result=$?
388
389	(( $result != 0 )) && return 1
390
391	return 0
392}
393
394#
395# Unpack cpio archive into zoneroot.
396#
397install_cpio()
398{
399	stage1=$1
400	archive=$2
401
402	cpioopts="-idmfE $ipdcpiofile"
403
404	vlog "cd \"$ZONEROOT\" && $stage1 \"$archive\" | "
405	vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts"
406
407	( cd "$ZONEROOT" && $stage1 "$archive" | \
408	     ppriv -e -s A=all,-sys_devices cpio $cpioopts )
409}
410
411#
412# Unpack pax archive into zoneroot.
413#
414install_pax()
415{
416	archive=$1
417
418	if [[ -s $ipdpaxfile ]]; then
419		filtopt="-c $(/usr/bin/cat $ipdpaxfile)"
420	fi
421
422	vlog "cd \"$ZONEROOT\" && "
423	vlog "ppriv -e -s A=all,-sys_devices pax -r -f \"$archive\" $filtopt"
424
425	( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \
426	    pax -r -f "$archive" $filtopt )
427}
428
429#
430# Unpack UFS dump into zoneroot.
431#
432install_ufsdump()
433{
434	archive=$1
435
436	vlog "cd \"$ZONEROOT\" && "
437	vlog "ppriv -e -s A=all,-sys_devices ufsrestore rf \"$archive\""
438
439	#
440	# ufsrestore goes interactive if you ^C it.  To prevent that,
441	# we make sure its stdin is not a terminal.
442	# Note that there is no way to filter inherit-pkg-dirs for a full
443	# restore so there will be warnings in the log file.
444	#
445	( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \
446	    ufsrestore rf "$archive" < /dev/null )
447}
448
449#
450# Copy directory hierarchy into zoneroot.
451#
452install_dir()
453{
454	source_dir=$1
455
456	cpioopts="-pdm"
457
458	first=1
459	filt=$(for i in $(cat $ipdpaxfile)
460		do
461			echo $i | egrep -s "/" && continue
462			if [[ $first == 1 ]]; then
463				printf "^%s" $i
464				first=0
465			else
466				printf "|^%s" $i
467			fi
468		done)
469
470	list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
471	flist=$(for i in $list
472	do
473		printf "%s " "$i"
474	done)
475	findopts="-xdev ( -type d -o -type f -o -type l ) -print"
476
477	vlog "cd \"$source_dir\" && find $flist $findopts | "
478	vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts \"$ZONEROOT\""
479
480	( cd "$source_dir" && find $flist $findopts | \
481	    ppriv -e -s A=all,-sys_devices cpio $cpioopts "$ZONEROOT" )
482}
483
484# Setup i18n output
485TEXTDOMAIN="SUNW_OST_OSCMD"
486export TEXTDOMAIN
487
488e_baddir=$(gettext "Invalid '%s' directory within the zone")
489e_badfile=$(gettext "Invalid '%s' file within the zone")
490
491#
492# Exit values used by the script, as #defined in <sys/zone.h>
493#
494#	ZONE_SUBPROC_OK
495#	===============
496#	Installation was successful
497#
498#	ZONE_SUBPROC_USAGE
499#	==================
500#	Improper arguments were passed, so print a usage message before exiting
501#
502#	ZONE_SUBPROC_NOTCOMPLETE
503#	========================
504#	Installation did not complete, but another installation attempt can be
505#	made without an uninstall
506#
507#	ZONE_SUBPROC_FATAL
508#	==================
509#	Installation failed and an uninstall will be required before another
510#	install can be attempted
511#
512ZONE_SUBPROC_OK=0
513ZONE_SUBPROC_USAGE=253
514ZONE_SUBPROC_NOTCOMPLETE=254
515ZONE_SUBPROC_FATAL=255
516
517