1 #!/bin/ksh93 -p
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22 #
23 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 # Use is subject to license terms.
25 #
26
27 #
28 # itu - converts packages to Driver Update format and patches Solaris install
29 # media for Install Time Update (ITU).
30 #
31
32 readonly PROG=$0
33 readonly ORIGPWD=$PWD
34
35 # Must-have utilities
36 readonly CPIO=/usr/bin/cpio
37 readonly GZIP=/usr/bin/gzip
38 readonly MKISOFS=/usr/bin/mkisofs
39 readonly PATCHADD=/usr/sbin/patchadd
40 readonly PKGTRANS=/usr/bin/pkgtrans
41 readonly PKGADD=/usr/sbin/pkgadd
42 readonly LOFIADM=/usr/sbin/lofiadm
43 readonly MKDIR=/usr/bin/mkdir
44 readonly RM=/usr/bin/rm
45 readonly CP=/usr/bin/cp
46 readonly HEAD=/usr/bin/head
47 readonly SORT=/usr/bin/sort
48 readonly MKBOOTMEDIA=/usr/bin/mkbootmedia
49 readonly PKG2DU=/usr/bin/pkg2du
50 readonly TOUCH=/usr/bin/touch
51 readonly NAWK=/usr/bin/nawk
52 readonly CHMOD=/usr/bin/chmod
53 readonly GREP=/usr/bin/grep
54 readonly LS=/usr/bin/ls
55 readonly LN=/usr/bin/ln
56 readonly SED=/usr/bin/sed
57 readonly CAT=/usr/bin/cat
58 readonly FIND=/usr/bin/find
59 readonly UNAME=/usr/bin/uname
60 readonly MACH=`$UNAME -p`
61
62 ROOT_ARCHIVE=/usr/sbin/root_archive
63 BOOTBLOCK=
64 MINIROOT=
65 # Relative to a Solaris media root.
66 if [ "$MACH" = "sparc" ]; then
67 BOOTBLOCK=boot/hsfs.bootblock
68 MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot
69 else
70 # x86/x64
71 BOOTBLOCK=boot/grub/stage2_eltorito
72 MINIROOT=$MEDIA_ROOT/boot/x86.miniroot
73 fi
74
75 readonly TMP_DIR=${TMPDIR:-/tmp}/${PROG##*/}.$$
76 readonly LOGFILE=${TMPDIR:-/tmp}/${PROG##*/}-log.$$
77
78 # Paths we need.
79 export PATH=/usr/bin:/usr/sbin:/sbin:/boot/solaris/bin:$PATH
80
81 # for gettext
82 TEXTDOMAIN=SUNW_OST_OSCMD
83 export TEXTDOMAIN
84
85
86 function cleanup
87 {
88 $RM -rf "$TMP_DIR"
89 }
90
91
92 function usage_long
93 {
94 usage_short
95 print -u2
96 usage_options
97 }
98
99
100 function usage_short
101 {
102 gettext "Usage:\n"
103 gettext "${PROG##*/} makedu -r solaris_release [-v] [-f] [-d output_dir]\n [-o iso_file] [-l iso_label] package [package ...]\n"
104 gettext "${PROG##*/} patchmedia -R media_root [-v] [-f]\n [-o iso_file] [-l iso_label] pkg_or_patch [pkg_or_patch ...]\n"
105 gettext "${PROG##*/} makeiso -o iso_file [-v] [-f] [-l iso_label] media_root\n"
106 }
107
108
109 function usage_options {
110 gettext "Options:\n"
111 gettext " -d output_dir\n Directory where the Driver Update directory should be created.\n"
112 gettext " -f\n If output_dir/DU or iso_file already exists, remove it without\n asking first.\n"
113 gettext " -l iso_label\n Label/volume name of the ISO image (if -o option is specified).\n"
114 gettext " -o iso_file\n Path of ISO image file to create. For
115 subcommands patchmedia and\n makeiso this will be a bootable ISO image.\n This option must be specified for subcommand makeiso.\n"
116 gettext " -R media_root\n Top-level directory of on-disk image of Solaris installation media.\n This option must be specified for subcommand patchmedia.\n"
117 gettext " -r solaris_release\n Solaris release number for which the Driver Update is intended.\n It takes the form of 5.10.\n This option must be specified for subcommand makedu.\n"
118 gettext " -v\n Verbose. Multiple -v options increase verbosity.\n"
119
120 echo;
121 }
122
123
124 #
125 # Process command line options.
126 # Note: since $OPTIND is a local variable inside functions, upon return
127 # from this function global variable $MYOPTIND is set to this value.
128 #
129 function process_options # <arg> ...
130 {
131 typeset opt optlist
132
133 case "$SUBCOMMAND" in
134 makedu) optlist='d:fl:o:r:v' ;;
135 patchmedia) optlist='fl:o:R:v' ;;
136 makeiso) optlist='fl:o:v' ;;
137 esac
138
139 while getopts ":$optlist" opt
140 do
141 case $opt in
142 d) DU_OUTDIR=$OPTARG
143 ;;
144 f) FORCE=1
145 ;;
146 l) ISOLABEL=$OPTARG
147 ;;
148 o) ISO=$OPTARG
149 if [ ! -z `echo $ISO | $GREP "^/tmp"` ]; then
150 gettext "ISO images will not be created on /tmp.\n"
151 gettext "Please choose a different output location.\n"
152 exit 3
153 fi
154 ;;
155 R) MEDIA_ROOT=$OPTARG
156 ;;
157 r) RELEASE=$OPTARG
158 ;;
159 v) (( VERBOSE_LEVEL += 1 ))
160 VERBOSE_OPTS="${VERBOSE_OPTS:--}$opt" # collect options
161 ;;
162 :) gettext "Option -$OPTARG missing argument.\n"
163 usage_short
164 return 1
165 ;;
166 *) gettext "Option -$OPTARG invalid for $SUBCOMMAND.\n"
167 usage_short
168 return 1
169 ;;
170 esac
171 done
172
173 MYOPTIND=$OPTIND
174 return 0
175 }
176
177
178 #
179 # Check some prerequisites
180 #
181 function check_prereqs
182 {
183 typeset utils f
184
185 # List of must-have utilities depends on subcommand.
186 case "$SUBCOMMAND" in
187 makedu)
188 set -A utils $GZIP ${ISO:+$MKISOFS} $PKGTRANS
189 ;;
190 patchmedia)
191 set -A utils $CPIO $GZIP ${ISO:+$MKISOFS} $PATCHADD \
192 $ROOT_ARCHIVE
193 ;;
194 makeiso)
195 set -A utils $MKISOFS
196 ;;
197 esac
198
199 for f in "${utils[@]}"
200 do
201 if [[ ! -x "$f" ]]
202 then
203 gettext "Can't find required utility $f.\n"
204 return 1
205 fi
206 done
207
208 # Subcommand packmedia uses the "root_archive unpack_media" command
209 # which calls lofiadm -a, which requires write access as
210 # determined by /dev/lofictl. See lofiadm(1m).
211 if [[ $SUBCOMMAND = patchmedia && ! -w /dev/lofictl ]]
212 then
213 gettext "You don't have enough privileges to run lofiadm -a.\n"
214 gettext "See lofiadm(1m) for more information.\n"
215 return 1
216 fi
217
218 return 0
219 }
220
221
222 #
223 # Verifies the given packages and collects them in the PACKAGES array.
224 #
225 function collect_packages # <arg> ...
226 {
227 typeset obj
228
229 for obj in "$@"
230 do
231 if [[ ! -e "$obj" ]]
232 then
233 gettext "Can't find package $obj.\n"
234 return 1
235 elif [[ ! -f "$obj/pkginfo" ]]
236 then
237 gettext "$obj is not a package.\n"
238 return 1
239 fi
240 PACKAGES[ ${#PACKAGES[*]} ]=$obj
241 done
242 return 0
243 }
244
245
246 #
247 # Verifies the given packages and patches. Packages are then collected in
248 # the array PACKAGES. Patches are stored in the PATCHES array.
249 #
250 function collect_packages_patches # <arg> ...
251 {
252 typeset obj
253
254 for obj in "$@"
255 do
256 if [[ -f "$obj/patchinfo" ]]
257 then
258 # Collect patches.
259 PATCHES[ ${#PATCHES[*]} ]=$obj
260 elif [[ -f "$obj/pkginfo" ]]
261 then
262 # Collect packages.
263 PACKAGES[ ${#PACKAGES[*]} ]=$obj
264 elif [[ -e "$obj" ]]
265 then
266 gettext "$obj is not a package or patch.\n"
267 return 1
268 else
269 gettext "$obj does not exist.\n"
270 return 1
271 fi
272 done
273 return 0
274 }
275
276
277 #
278 # Ask user whether to overwrite an object, unless -f option was given.
279 #
280 function is_overwrite
281 {
282 typeset arg=$1
283 typeset -l ans
284
285 (( FORCE )) && return 0
286 while true
287 do
288 gettext "$arg already exists. Overwrite it? (y/n) "
289 read ans
290 case $ans in
291 y*|Y*) return 0 ;; # go ahead, overwrite
292 n*|N*) return 1 ;; # don't overwrite
293 esac
294 done
295 }
296
297
298 #
299 # Check the format of the Solaris release number $RELEASE.
300 # Also set $VERSION (for DU format) based on $RELEASE.
301 #
302 function check_release
303 {
304 # Allow Major.Minor or Major.Minor.Micro format.
305 if [[ $RELEASE != +([0-9]).+([0-9])?(.+([0-9])) ]]
306 then
307 gettext "Invalid release number specified: $RELEASE.\n"
308 return 1
309 fi
310
311 # As defined by the ITU spec, a Solaris release number 5.x corresponds
312 # to version number 2x (e.g. 5.10 -> 210). Hopefully, by the time we
313 # do a 6.x Release we won't need ITUs any more.
314 VERSION=$(echo $RELEASE | $SED 's/5\./2/')
315 }
316
317
318 #
319 # If an ISO file was specified, get realpath of its parent directory ($ISODIR).
320 # If the ISO file already exists, ask user to overwrite it, unless -f option
321 # was specified.
322 #
323 function check_iso
324 {
325 if [[ "$ISO" = */* ]]
326 then
327 ISODIR=$(cd "${ISO%/*}" 2>/dev/null && pwd -P)
328 if (( $? ))
329 then
330 gettext "Can't access parent directory of ISO image.\n"
331 return 1
332 fi
333 else
334 ISODIR=$(pwd -P)
335 fi
336
337 if [[ -f "$ISO" ]]
338 then
339 is_overwrite "$ISO" || return 2
340 $RM -f "$ISO"
341 fi
342
343 return 0
344 }
345
346
347 #
348 # If specified, check the Driver Update output directory $DU_OUTDIR (-d option).
349 # Else set $DU_OUTDIR to a temporary directory. Also if $DU_OUTDIR/DU
350 # already exists, ask user whether to overwrite it, unless -f option was given.
351 #
352 function check_dudir
353 {
354 typeset realpath
355
356 if [[ -z "$DU_OUTDIR" ]]
357 then
358 DU_OUTDIR=$TMP_DIR/dudir
359 return 0
360 fi
361
362 # Verify user-specified DU output directory.
363 if [[ ! -d "$DU_OUTDIR" ]]
364 then
365 if [ `$MKDIR -p $DU_OUTDIR` ]; then
366 gettext "$DU_OUTDIR is not a directory.\n"
367 return 1
368 fi
369 elif [[ ! -w "$DU_OUTDIR" ]]
370 then
371 gettext "Directory $DU_OUTDIR is not writable.\n"
372 return 1
373 fi
374
375 # If an ISO image path is also specified, make sure it's not under
376 # $DU_OUTDIR since we might take the ISO image of $DU_OUTDIR.
377 if [[ -n "$ISODIR" ]]
378 then
379 realpath=$(cd "$DU_OUTDIR" 2>/dev/null && pwd -P)
380 if [[ "$ISODIR" = "$realpath"?(/*) ]]
381 then
382 gettext "ISO image must not be under Driver Update's output directory ($realpath).\n"
383 return 1
384 fi
385 fi
386
387 # If the DU directory already exists, ask user permission to
388 # remove it unless -f option was given.
389 if [[ -d "$DU_OUTDIR/DU" ]]
390 then
391 is_overwrite "$DU_OUTDIR/DU" || return 2
392 $RM -rf "$DU_OUTDIR/DU" || return 1
393 fi
394
395 return 0
396 }
397
398
399 #
400 # Verify $MEDIA_ROOT is indeed a Solaris install media.
401 #
402 function check_media_root
403 {
404 if [[ ! -d $(echo "$MEDIA_ROOT"/Solaris*/Tools/Boot) ]]
405 then
406 gettext "$MEDIA_ROOT is not a Solaris install media.\n"
407 return 1
408 fi
409 return 0
410 }
411
412
413 #
414 # Verify there's a miniroot file under $MEDIA_ROOT. Also set $MINIROOT
415 # to the path of the miniroot.
416 #
417 function check_miniroot
418 {
419 MINIROOT=$MEDIA_ROOT/boot/x86.miniroot
420 if [[ ! -f "$MINIROOT" ]]
421 then
422 return 0
423 fi
424 MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot
425 if [[ ! -f "$MINIROOT" ]]
426 then
427 return 0
428 fi
429 gettext "Can't find $MEDIA_ROOT/boot/x86.miniroot or $MEDIA_ROOT/boot/sparc.miniroot.\n"
430 return 1
431 }
432
433
434 #
435 # Create a non-bootable ISO image of the given directory.
436 #
437 function create_nonboot_iso # <dir>
438 {
439 typeset dir vflag i
440
441 if (( $# != 1 ))
442 then
443 gettext "create_nonboot_iso missing argument.\n"
444 return 1
445 fi
446 dir=$1
447
448 # Skip if no ISO image was specified.
449 [[ -z "$ISO" ]] && return 0
450
451 # Determine mkisofs' verbose flag depending on $VERBOSE_LEVEL.
452 case $VERBOSE_LEVEL in
453 0) vflag=-quiet
454 ;;
455 1) vflag= # mkisofs' default verboseness
456 ;;
457 *) vflag=
458 i=$VERBOSE_LEVEL
459 while ((i > 0))
460 do
461 vflag="-v $vflag"
462 (( i -= 1 ))
463 done
464 ;;
465 esac
466
467 print "Creating ISO image ..."
468
469 # Note: the "-log-file >(cat -u >&2)" and "2>/dev/null" below is a
470 # trick to filter out mkisofs's warning message about being
471 # non-conforming to ISO-9660.
472 # We do some funky architecture-specific stuff here so that we can
473 # actually create a bootable media image for UltraSPARC systems
474
475 sparc_ISOARGS="-B ... -joliet-long -U"
476 i386_ISOARGS="-d -N -r -relaxed-filenames"
477 if [[ "$MACH" = "i386" ]]
478 then
479 ISOARGS=$i386_ISOARGS
480 else
481 ISOARGS=$sparc_ISOARGS
482 fi
483
484 $MKISOFS -o "$ISO" \
485 -allow-leading-dots \
486 $ISOARGS \
487 -l -ldots \
488 -R -J \
489 -V "$ISOLABEL" \
490 $vflag \
491 -log-file >(cat -u >&2) \
492 "$dir" 2>/dev/null
493 }
494
495
496 #
497 # Create a bootable Solaris ISO image of the given Solaris install directory.
498 #
499 function create_bootable_iso # <dir>
500 {
501 typeset dir vflag saved i
502
503 if (( $# != 1 ))
504 then
505 gettext "create_bootable_iso missing argument.\n"
506 return 1
507 fi
508 dir=$1
509
510 # Skip if no ISO image was specified.
511 [[ -z "$ISO" ]] && return 0
512
513 # Determine mkisofs' verbose flag depending on $VERBOSE_LEVEL.
514 case $VERBOSE_LEVEL in
515 0) vflag=-quiet
516 ;;
517 1) vflag= # mkisofs' default verboseness
518 ;;
519 *) vflag=
520 i=$VERBOSE_LEVEL
521 while ((i > 0))
522 do
523 vflag="-v $vflag"
524 (( i -= 1 ))
525 done
526 ;;
527 esac
528
529 # Verify the boot block exists under media root. If it does,
530 # verify it's writable since it will be modified with some boot
531 # information by mkisofs' -boot-info-table option.
532 if [[ ! -f "$dir/$BOOTBLOCK" ]]
533 then
534 gettext "Can't find $dir/$BOOTBLOCK.\n"
535 return 1
536 elif [[ ! -w "$dir/$BOOTBLOCK" ]]
537 then
538 gettext "$dir/$BOOTBLOCK is not writable.\n"
539 return 1
540 fi
541
542 gettext "Creating bootable ISO image ..."
543
544 # Since mkisofs below will modify the file $BOOTBLOCK in-place, save
545 # a copy of it first.
546 saved=$TMP_DIR/${BOOTBLOCK##*/}
547 $CP -f "$dir/$BOOTBLOCK" "$saved" || return
548
549 # Note: the "-log-file >(cat -u >&2)" and "2>/dev/null" below is a
550 # trick to filter out mkisofs's warning message about being
551 # non-conforming to ISO-9660.
552 # We do some funky architecture-specific stuff here so that we can
553 # actually create a bootable media image for UltraSPARC systems
554 sparc_ISOARGS="-G $BOOTBLOCK -B ... -joliet-long -U"
555 i386_ISOARGS="-b boot/grub/stage2_eltorito -boot-info-table "
556 i386_ISOARGS="$i386_ISOARGS -boot-load-size 4 -c .catalog -d -N "
557 i386_ISOARGS="$i386_ISOARGS -no-emul-boot -r -relaxed-filenames"
558 if [[ "$MACH" = "i386" ]]
559 then
560 ISOARGS=$i386_ISOARGS
561 else
562 ISOARGS=$sparc_ISOARGS
563 fi
564
565 cd $dir
566 $MKISOFS -o "$ISO" \
567 -allow-leading-dots \
568 $ISOARGS \
569 -l -ldots \
570 -R -J \
571 -V "$ISOLABEL" \
572 $vflag \
573 -log-file >(cat -u >&2) \
574 "$dir" 2>/dev/null
575 i=$?
576
577 # Restore saved El Torito file
578 $CP -f "$saved" "$dir/$ELTORITO" 2>/dev/null
579
580 return $i
581 }
582
583
584 #
585 # Create a Driver Update (DU) format directory from packages
586 #
587 function create_du
588 {
589 typeset distdir tmpdudir pkgs obj statusfile
590
591 # Create DU directory first.
592 distdir=$DU_OUTDIR/DU/sol_$VERSION/$MACH
593 $MKDIR -p "$distdir/Tools" "$distdir/Product"
594
595 echo "start create DU with MACH $MACH"
596
597 # If we're running this script on sun4[vu], then create a symlink
598 # to the other UltraSPARC architecture
599 if [[ "$MACH" != "i386" ]]
600 then
601 cd $DU_OUTDIR/DU/sol_$VERSION
602 $LN -s sparc sun4v
603 $LN -s sparc sun4u
604 else
605 cd $DU_OUTDIR/DU/sol_$VERSION
606 $LN -s i386 i86pc
607 fi
608
609 # Unfortunately pkgtrans insists that all packages must be in
610 # <device1> (see pkgtrans(1)). The packages can't have any path
611 # components. So we'll create a temporary directory first and then
612 # symlinks to the specified packages. Then run pkgtrans with
613 # the temporary directory as <device1>.
614 tmpdudir=$TMP_DIR/create_du
615 $RM -rf "$tmpdudir"
616 $MKDIR -p "$tmpdudir"
617
618 for obj in "${PACKAGES[@]}"
619 do
620 # Get rid of trailing /'s, if any.
621 [[ "$obj" == */ ]] && obj=${obj%%+(/)}
622
623 # Make sure it's full pathname.
624 [[ "$obj" != /* ]] && obj=$ORIGPWD/$obj
625
626 ln -s "$obj" "$tmpdudir" || return
627
628 # Remember just the file component.
629 pkgs[ ${#pkgs[*]} ]=${obj##*/}
630 done
631
632 # Package up packages as compressed data stream.
633 statusfile=$TMP_DIR/.pkgtrans.status
634 (
635 # Use fd 9 for redirecting pkgtrans' "Transferring..."
636 # messages which normally go to stderr to current stdout
637 # (not the following pipeline's stdout).
638 exec 9>&1
639 {
640 $PKGTRANS -s "$tmpdudir" /dev/stdout "${pkgs[@]}" 2>&9
641 echo $? > $statusfile
642 $TOUCH $statusfile # make sure file is created
643 } | $GZIP -9 > "$distdir/Product/pkgs.gz"
644 )
645
646 [[ -s $statusfile && $(<$statusfile) != 0 ]] && return 1
647
648 # Create admin file for pkgadd
649 $CAT > "$distdir/Tools/admin" <<"EOF"
650 mail=
651 instance=overwrite
652 partial=nocheck
653 runlevel=nocheck
654 idepend=nocheck
655 rdepend=nocheck
656 space=nocheck
657 setuid=nocheck
658 conflict=nocheck
659 action=nocheck
660 EOF
661
662 # Create install.sh
663 $CAT > "$distdir/Tools/install.sh" <<"EOF"
664 #!/sbin/sh
665 # install.sh -R <basedir> - install packages to basedir
666 basedir=/
667 toolsdir=`dirname $0`
668 tmpfile=/tmp/`basename $0`.$$
669 gzip=/usr/bin/gzip
670 while getopts "R:" arg
671 do
672 case "$arg" in
673 R) basedir=$OPTARG;;
674 esac
675 done
676
677 # /etc/driver_aliases ,/etc/driver_classes
678 # /etc/name_to_major /etc/minor_perm
679 # The four file can't append due to ACL defect.
680 # workaround this by mv and cp
681
682 /usr/bin/touch /etc/driver_aliases
683 if [ $? -ne 0 ] ; then
684 /bin/cp /etc/driver_aliases /tmp/driver_aliases
685 /bin/mv /etc/driver_aliases /tmp/driver_aliases.bak
686 /bin/mv /tmp/driver_aliases /etc
687 fi
688 /usr/bin/touch /etc/driver_classes
689 if [ $? -ne 0 ] ; then
690 /bin/cp /etc/driver_classes /tmp/driver_classes
691 /bin/mv /etc/driver_classes /tmp/driver_classes.bak
692 /bin/mv /tmp/driver_classes /etc
693 fi
694 /usr/bin/touch /etc/name_to_major
695 if [ $? -ne 0 ] ; then
696 /bin/cp /etc/name_to_major /tmp/name_to_major
697 /bin/mv /etc/name_to_major /tmp/name_to_major.bak
698 /bin/mv /tmp/name_to_major /etc
699 fi
700 /usr/bin/touch /etc/minor_perm
701 if [ $? -ne 0 ] ; then
702 /bin/cp /etc/minor_perm /tmp/minor_perm
703 /bin/mv /etc/minor_perm /tmp/minor_perm.bak
704 /bin/mv /tmp/minor_perm /etc
705 fi
706
707 # Make sure that we've got our own copy of /usr/bin/gzip
708 # in the tools directory
709
710 if [ ! -f $gzip ] ; then
711 gzip=$toolsdir/gzip
712 /usr/bin/chmod a+x "$toolsdir/gzip" 2>/dev/null
713 fi
714
715 $gzip -c -d "$toolsdir/../Product/pkgs.gz" > $tmpfile &&
716 /usr/sbin/pkgadd -R "$basedir" -d "$tmpfile" -a "$toolsdir/admin" all
717 status=$?
718 rm -f "$tmpfile"
719 exit $status
720 EOF
721 $CHMOD a+rx "$distdir/Tools/install.sh"
722
723 $CP -f /usr/bin/gzip "$distdir/Tools" 2>/dev/null
724 }
725
726
727 #
728 # Unpack the miniroot of a Solaris install media.
729 #
730 function unpack_media
731 {
732 # Create temp directory to unpack the miniroot.
733 $MKDIR -p "$UNPACKED_ROOT"
734
735 # We need to use the unpackmedia option to correctly apply patches
736 gettext "Unpacking media ... "
737 $ROOT_ARCHIVE unpackmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1
738 if [ $? != 0 ]; then
739 if [ -d $MEDIA_ROOT/Solaris_10 -a -d $MEDIA_ROOT/Solaris_11 ]; then
740 # we _do_ care, because we're not patching a Solaris
741 # update media instance
742 gettext "There was an error unpacking the media from $MEDIA_ROOT\n"
743 exit 1
744 fi
745 fi
746 }
747
748
749 #
750 # Pack an unpacked miniroot onto a Solaris install media.
751 #
752 function repack_media
753 {
754 gettext "Repacking media ..."
755
756 # We need to ensure that we're using the appropriate version
757 # of root_archive for the media that we're packing/unpacking.
758 # The onnv version of root_archive differs from the S10 version,
759 # and this will cause problems on re-packing. So we sneakily
760 # use the version that we've just unpacked
761 if [ -d $MEDIA_ROOT/Solaris_10 ]; then
762 ROOT_ARCHIVE=$UNPACKED_ROOT/boot/solaris/bin/root_archive
763 fi
764
765 $ROOT_ARCHIVE packmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1
766 if [ $? != 0 ]; then
767 if [ -d $MEDIA_ROOT/Solaris_10 -a -d $MEDIA_ROOT/Solaris_11 ]; then
768 # we _do_ care, because we're not patching a Solaris
769 # update media instance
770 gettext "There was an error packing the media from $MEDIA_ROOT\n"
771 exit 1
772 fi
773 fi
774 gettext "Done.\n"
775 }
776
777
778 #
779 # Add packages to a Solaris install media. Also install these packages
780 # onto the miniroot.
781 #
782 function add_pkgs
783 {
784 typeset icmd statusfile i
785
786 (( ${#PACKAGES[*]} == 0 )) && return
787
788 statusfile=$TMP_DIR/.add_pkgs.status
789
790 DU_OUTDIR=$ITUDIR/$ITU_COUNTDIR
791 (( ITU_COUNTDIR += 1 ))
792 $MKDIR "$DU_OUTDIR" || return
793
794 #
795 # Add a Driver Update directory on the media
796 #
797 echo;
798 gettext "Adding package(s) to media root.\n"
799 create_du || return
800
801 #
802 # Using the Driver Update above install the packages onto the miniroot.
803 #
804 echo;
805 gettext "Installing package(s) onto miniroot.\n"
806 icmd=$DU_OUTDIR/DU/sol_$VERSION/$MACH/Tools/install.sh
807 if [[ ! -f "$icmd" ]]
808 then
809 # This shouldn't happen, but just in case.
810 gettext "Cannot find $icmd.\n"
811 return 1
812 fi
813 [[ ! -x "$icmd" ]] && chmod a+x "$icmd"
814
815 $RM -f "$statusfile"
816 {
817 "$icmd" -R "$UNPACKED_ROOT"
818 if (( i=$? ))
819 then
820 echo $i > "$statusfile"
821 $TOUCH "$statusfile" # make sure file is created
822 fi
823 } 2>&1 | $NAWK -v logfile="$LOGFILE" -v vlevel=$VERBOSE_LEVEL '
824 # If not verbose, print certain lines from patchadd.
825 (vlevel == 0) && /^Installing/ {print}
826 (vlevel == 0) && /^Installation.*successful/ {print}
827
828 # If verbose, print every line to stderr.
829 (vlevel > 0) {print > "/dev/stderr"}
830
831 # Save every line to logfile.
832 {print >> logfile}
833 ' || return
834 [[ -s "$statusfile" ]] && return $(<$statusfile)
835 return 0
836 }
837
838
839 #
840 # Add patches to a Solaris install media. Also patch the miniroot with
841 # these patches
842 #
843 function add_patches
844 {
845 typeset distdir tmpdir icmd obj patches statusfile
846
847 (( ${#PATCHES[*]} == 0 )) && return
848
849 tmpdir=$TMP_DIR/patches
850 statusfile=$TMP_DIR/.add_patches.status
851
852 $RM -rf "$tmpdir"
853
854 distdir=$ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION/$MACH
855 (( ITU_COUNTDIR += 1 ))
856
857 $MKDIR -p "$distdir/Tools" "$distdir/Product" "$tmpdir" || return
858
859 # If we're running this script on sun4[vu], then create a symlink
860 # to the other UltraSPARC architecture
861 if [[ "$MACH" != "i386" ]]
862 then
863 cd $ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION
864 $LN -s sparc sun4v
865 $LN -s sparc sun4u
866 else
867 cd $ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION
868 $LN -s i386 i86pc
869 fi
870
871 #
872 # Add packages onto media root
873 #
874 echo;
875 gettext "Adding patch(es) to media root.\n"
876
877 # Symlink each patch in a temp dir so a single cpio/gzip can work.
878 for obj in "${PATCHES[@]}"
879 do
880 # Get rid of trailing /'s, if any.
881 [[ "$obj" == */ ]] && obj=${obj%%+(/)}
882
883 # Make sure it's a full pathname.
884 [[ "$obj" != /* ]] && obj=$ORIGPWD/$obj
885
886 $LN -s "$obj" "$tmpdir" || return
887
888 # Remember just the file component.
889 patches[ ${#patches[*]} ]=${obj##*/}
890 done
891
892 # Package up patches as compressed cpio archive.
893 $RM -f "$statusfile"
894 (
895 # Save current stdout as fd 8. This doesn't point to the
896 # gzip pipeline below.
897 exec 8>&1
898
899 {
900 # Fd 9 is used later on for filtering out cpio's
901 # reporting total blocks to stderr but yet still
902 # print other error messages. fd 9 refers to the
903 # pipeline to gzip.
904 exec 9>&1
905
906 cd "$tmpdir"
907 for obj in "${patches[@]}"
908 do
909 print -u8 "Transferring patch $obj."
910 $FIND "$obj/." -follow -print
911 if (( i=$? ))
912 then
913 echo $i > "$statusfile"
914 $TOUCH "$statusfile"
915 return $i
916 fi
917 done | $CPIO -oc 2>&1 >&9 | $GREP -v '^[0-9]* blocks' >&2
918 } | $GZIP -9 > "$distdir/Product/patches.gz"
919 ) || return
920
921 [[ -s "$statusfile" ]] && return $(<$statusfile)
922
923 # Create install.sh
924 $CAT > "$distdir/Tools/install.sh" <<"EOF"
925 #!/sbin/sh
926 # install.sh -R <basedir> - install patches to basedir
927 basedir=/
928 toolsdir=`dirname $0`
929 tmpdir=/tmp/`basename $0`.$$
930 gzip=/usr/bin/gzip
931 trap "/bin/rm -rf $tmpdir" 0
932 while getopts "R:" arg
933 do
934 case "$arg" in
935 R) basedir=$OPTARG;;
936 esac
937 done
938 /bin/mkdir -p "$tmpdir" || exit
939 tmpfile=$tmpdir/patches
940 patchdir=$tmpdir/patchdir
941 /bin/mkdir "$patchdir" || exit
942
943 # Make sure that we've got our own copy of /usr/bin/gzip
944 # in the tools directory
945
946 if [ ! -f $gzip ] ; then
947 gzip=$toolsdir/gzip
948 /usr/bin/chmod a+x "$toolsdir/gzip" 2>/dev/null
949 fi
950
951 $gzip -c -d "$toolsdir/../Product/patches.gz" > $tmpfile || exit
952 cd "$patchdir"
953 /bin/cpio -idum < "$tmpfile" || exit
954 patchadd -R "$basedir" -nu *
955 EOF
956 $CHMOD a+rx "$distdir/Tools/install.sh"
957
958 $CP -f /usr/bin/gzip "$distdir/Tools" 2>/dev/null
959
960 #
961 # Patch the miniroot
962 #
963 echo;
964 gettext "Installing patch(es) onto miniroot.\n"
965 $RM -f "$statusfile"
966 {
967 $PATCHADD -udn -C "$UNPACKED_ROOT" "${PATCHES[@]}"
968 if (( i=$? ))
969 then
970 echo $i > "$statusfile"
971 $TOUCH "$statusfile" # make sure file is created
972 fi
973 } 2>&1 | $NAWK -v logfile="$LOGFILE" -v vlevel=$VERBOSE_LEVEL '
974 # If not verbose, print certain lines from patchadd.
975 (vlevel == 0) && /^Patch.*successful/ {print}
976
977 # If verbose, print every line to stderr.
978 (vlevel > 0) {print > "/dev/stderr"}
979
980 # Save every line to logfile.
981 {print >> logfile}
982 ' || return
983
984 [[ -s "$statusfile" ]] && return $(<$statusfile)
985
986 # Remove patch log files to save space when miniroot is repacked.
987 $RM -rf "$UNPACKED_ROOT"/var/sadm/patch
988 }
989
990
991 #
992 # Starting point for makedu subcommand:
993 #
994 # Convert packages into Driver Update (DU) directory format.
995 #
996 function makedu # <arg> ...
997 {
998 typeset i
999
1000 process_options "$@" || return
1001 shift 'MYOPTIND - 1'
1002
1003 if (( $# == 0 ))
1004 then
1005 gettext "Please specify one or more packages.\n"
1006 usage_short
1007 return 1
1008 fi
1009
1010 # Release number must be specified.
1011 if [[ -z "$RELEASE" ]]
1012 then
1013 gettext "Please specify Solaris release number (-r option).\n"
1014 usage_short
1015 return 1
1016 fi
1017 check_release || return
1018
1019 # Either -d or -o option, or both, must be specified.
1020 if [[ -z "$DU_OUTDIR" && -z "$ISO" ]]
1021 then
1022 gettext "Please specify either -d or -o option (or both).\n"
1023 usage_short
1024 return 1
1025 fi
1026
1027 if [[ -n "$ISO" ]]
1028 then
1029 check_iso || return
1030 ${ISOLABEL:=DU sol_$VERSION} 2>/dev/null # default ISO label
1031 fi
1032 check_dudir || return # should be called after check_iso
1033
1034 # Rest of arguments must be packages.
1035 collect_packages "$@" || return
1036
1037 check_prereqs || return
1038
1039 # Create DU and the (non-bootable) ISO image (if requested).
1040 create_du && create_nonboot_iso "$DU_OUTDIR"
1041 if (( i=$? ))
1042 then
1043 $RM -rf "$DU_OUTDIR/DU"
1044 [[ -n "$ISO" ]] && rm -f "$ISO"
1045 else
1046 if [[ "$MACH" != "i386" ]]
1047 then
1048 echo "This DU must be written as either an ISO (hsfs)"
1049 echo "or a *ufs* filesystem. DO NOT USE pcfs!"
1050 fi
1051 fi
1052 return $i
1053 }
1054
1055
1056 #
1057 # Starting point for patchmedia subcommand:
1058 #
1059 # Patch a Solaris install image with the given packages and patches.
1060 #
1061 function patchmedia # <arg> ...
1062 {
1063 typeset soldir
1064
1065 process_options "$@" || return
1066 shift 'MYOPTIND - 1'
1067
1068 if (( $# == 0 ))
1069 then
1070 gettext "Please specify one or more packages or patches.\n"
1071 usage_short
1072 return 1
1073 fi
1074
1075 # -R option must be specified
1076 if [[ -z "$MEDIA_ROOT" ]]
1077 then
1078 gettext "Please specify Solaris media root (-R option).\n"
1079 usage_short
1080 return 1
1081 fi
1082 check_media_root || return
1083
1084 # Get the Solaris directory under $MEDIA_ROOT.
1085 soldir=$($LS -d $MEDIA_ROOT/Solaris* 2>/dev/null)
1086 if [[ -z "$soldir" ]]
1087 then
1088 gettext "Can't find Solaris directory in $MEDIA_ROOT.\n"
1089 return 1
1090 fi
1091
1092 # Extract the Solaris release number from the Solaris_* directory.
1093 RELEASE=5.${soldir##*Solaris_}
1094 check_release || return
1095
1096 # If user specifies an ISO image to create.
1097 if [[ -n "$ISO" ]]
1098 then
1099 check_iso || return
1100 ${ISOLABEL:=${soldir##*/}} # default ISO label
1101 fi
1102
1103 # Rest of arguments must be packages or patches.
1104 collect_packages_patches "$@" || return
1105
1106 # Verify we have some important utilities we need.
1107 check_prereqs || return
1108
1109 # Verify there's miniroot file in $MEDIA_ROOT.
1110 check_miniroot || return
1111
1112 # Create the ITU directory on the media root, if necessary
1113 ITUDIR=$MEDIA_ROOT/ITUs
1114 $MKDIR -p "$ITUDIR" || return
1115
1116 # The ITU directory might contain multiple driver updates already,
1117 # each in a separate numbered subdirectory. So look for the
1118 # subdirectory with the highest number and we'll add the packages
1119 # and patches on the next one.
1120 ITU_COUNTDIR=$($LS -d "$ITUDIR"/+([0-9]) 2>/dev/null |
1121 $SED 's;.*/;;' | $SORT -rn | $HEAD -1)
1122 if [[ $ITU_COUNTDIR == *( ) ]] # ITU_COUNTDIR is a typeset -Zn var
1123 then
1124 ITU_COUNTDIR=0
1125 else
1126 (( ITU_COUNTDIR += 1 ))
1127 fi
1128
1129 unpack_media || return
1130 add_pkgs && add_patches
1131 if (( status=$? )) && [[ -s "$LOGFILE" ]]
1132 then
1133 echo;
1134 gettext "A package or patch installation has failed.\n"
1135 gettext "Messages from pkgadd and patchadd have been saved in $LOGFILE\n"
1136 return $status
1137 else
1138 rm -f "$LOGFILE"
1139 fi
1140 print
1141 repack_media || return
1142 create_bootable_iso "$MEDIA_ROOT"
1143 gettext "$MEDIA_ROOT has been successfully patched\n"
1144 }
1145
1146
1147 #
1148 # Starting point for makeiso subcommand:
1149 #
1150 # Create a bootable ISO image of a Solaris install image.
1151 #
1152 function makeiso # <arg> ..
1153 {
1154 process_options "$@" || return
1155 shift 'MYOPTIND - 1'
1156
1157 if (( $# == 0 ))
1158 then
1159 gettext "Please specify the Solaris media root.\n"
1160 usage_short
1161 return 1
1162 elif (( $# > 1 ))
1163 then
1164 gettext "Too many arguments supplied.\n"
1165 usage_short
1166 return 1
1167 fi
1168 MEDIA_ROOT=$1
1169 check_media_root || return
1170
1171 # ISO image must be specified.
1172 if [[ -z "$ISO" ]]
1173 then
1174 gettext "Please specify ISO image file (-o option).\n"
1175 usage_short
1176 return 1
1177 fi
1178 check_iso || return
1179
1180 # If user doesn't specify ISO label, use the Solaris_* directory name
1181 # under $MEDIA_ROOT.
1182 if [[ -z "$ISOLABEL" ]]
1183 then
1184 ISOLABEL=$(echo "$MEDIA_ROOT"/Solaris*)
1185 ISOLABEL=${ISOLABEL##*/}
1186 fi
1187
1188 check_prereqs || return
1189 create_bootable_iso "$MEDIA_ROOT"
1190 }
1191
1192
1193 #
1194 # Main
1195 #
1196 trap cleanup EXIT
1197
1198 # Numbered subdirectories under ITU directory $ITUDIR.
1199 typeset -Z3 ITU_COUNTDIR=0
1200
1201 # Where to unpack a miniroot.
1202 UNPACKED_ROOT=${TMP_DIR}/miniroot
1203
1204 # Reset arrays.
1205 unset PACKAGES PATCHES
1206
1207 DU_OUTDIR=
1208 FORCE=0
1209 ISO=
1210 ISOLABEL=
1211 MEDIA_ROOT=
1212 RELEASE=
1213 SUBCOMMAND=
1214 VERBOSE_LEVEL=0
1215 VERBOSE_OPTS=
1216
1217 if (( $# == 0 ))
1218 then
1219 usage_long
1220 return 1
1221 fi
1222 typeset -l SUBCOMMAND=$1 # ignore case
1223 shift
1224
1225 if [[ $SUBCOMMAND != @(makedu|patchmedia|makeiso) ]]
1226 then
1227 # Be nice: allow some subcommands that cry out "help".
1228 case "$SUBCOMMAND" in
1229 *(-)help|*(-)usage|-h|-\?)
1230 usage_long
1231 return 0
1232 ;;
1233 *)
1234 gettext "Invalid subcommand: $SUBCOMMAND.\n"
1235 usage_short
1236 return 1
1237 ;;
1238 esac
1239 fi
1240
1241 $MKDIR -p "$TMP_DIR" || return
1242 $RM -f "$LOGFILE"
1243
1244 # Run the subcommand.
1245 $SUBCOMMAND "$@"