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 #
28 error()
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
37 fatal()
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 #
49 log()
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 #
62 vlog()
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.
72 safe_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.
82 safe_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.
93 safe_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
104 safe_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 #
134 get_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 #
176 mnt_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 #
206 umnt_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 #
224 get_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 #
235 get_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 #
255 install_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 #
397 install_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 #
414 install_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 #
432 install_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 #
452 install_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
485 TEXTDOMAIN="SUNW_OST_OSCMD"
486 export TEXTDOMAIN
487
488 e_baddir=$(gettext "Invalid '%s' directory within the zone")
489 e_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 #
512 ZONE_SUBPROC_OK=0
513 ZONE_SUBPROC_USAGE=253
514 ZONE_SUBPROC_NOTCOMPLETE=254
515 ZONE_SUBPROC_FATAL=255
516