1 #!/bin/ksh93 -p
   2 #
   3 #
   4 # CDDL HEADER START
   5 #
   6 # The contents of this file are subject to the terms of the
   7 # Common Development and Distribution License (the "License").
   8 # You may not use this file except in compliance with the License.
   9 #
  10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  11 # or http://www.opensolaris.org/os/licensing.
  12 # See the License for the specific language governing permissions
  13 # and limitations under the License.
  14 #
  15 # When distributing Covered Code, include this CDDL HEADER in each
  16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  17 # If applicable, add the following below this CDDL HEADER, with the
  18 # fields enclosed by brackets "[]" replaced with your own identifying
  19 # information: Portions Copyright [yyyy] [name of copyright owner]
  20 #
  21 # CDDL HEADER END
  22 #
  23 #
  24 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  25 # Use is subject to license terms.
  26 #
  27 
  28 #
  29 # pkg2du - convert driver packages to Driver Update (DU) format
  30 #
  31 
  32 readonly PROG=$0
  33 readonly ORIGPWD=$PWD
  34 readonly TMP_DIR=${TMPDIR:-/tmp}/${PROG##*/}.$$
  35 
  36 # Must-have utilities
  37 readonly CPIO=/usr/bin/cpio
  38 readonly GZIP=/usr/bin/gzip
  39 readonly MKISOFS=/usr/bin/mkisofs
  40 readonly PATCHADD=/usr/sbin/patchadd
  41 readonly PKGTRANS=/usr/bin/pkgtrans
  42 readonly ROOT_ARCHIVE=/usr/sbin/root_archive
  43 readonly LOFIADM=/usr/sbin/lofiadm
  44 readonly MKDIR=/usr/bin/mkdir
  45 readonly RM=/usr/bin/rm
  46 readonly CP=/usr/bin/cp
  47 readonly HEAD=/usr/bin/head
  48 readonly SORT=/usr/bin/sort
  49 readonly MKBOOTMEDIA=/usr/bin/mkbootmedia
  50 readonly PKG2DU=/usr/bin/pkg2du
  51 readonly TOUCH=/usr/bin/touch
  52 readonly NAWK=/usr/bin/nawk
  53 readonly CHMOD=/usr/bin/chmod
  54 readonly GREP=/usr/bin/grep
  55 readonly LS=/usr/bin/ls
  56 readonly LN=/usr/bin/ln
  57 readonly SED=/usr/bin/sed
  58 readonly CAT=/usr/bin/cat
  59 readonly FIND=/usr/bin/find
  60 readonly UNAME=/usr/bin/uname
  61 readonly MACH=`$UNAME -p`
  62 
  63 # for gettext
  64 TEXTDOMAIN=SUNW_OST_OSCMD
  65 export TEXTDOMAIN
  66 
  67 
  68 function usage
  69 {
  70         gettext "Usage:\n${PROG##*/} -r <release> [-f] [-v] [-d <dir>] [-o <iso>] [-l <label>]\n        <pkg> [<pkg> ...]\n"
  71         gettext "Options:\n  -d <dir>\n        Directory where the Driver Update directory should be created.\n"
  72         gettext "  -o <iso>\n        Create a Solaris ISO image of the Driver Update directory.\n"
  73         gettext "  -f\n        If <dir>/DU or <iso> exists, remove it without asking first.\n"
  74         gettext "  -l <label>\n        Label/volume name of the ISO image (if -o option is specified).\n"
  75         gettext "  -r <release>\n        Solaris release number to use.  It takes the form of 5.10.\n        This option must be specified.\n"
  76         gettext "  -v\n        Verbose.  Multiple -v options increase verbosity.\n"
  77         echo;
  78 }
  79 
  80 
  81 function check_prereqs
  82 {
  83         typeset f
  84 
  85         # We must have these utilities.
  86         for f in $GZIP ${ISO:+$MKISOFS} $PKGTRANS
  87         do 
  88                 if [[ ! -x "$f" ]]
  89                 then
  90                         gettext "Cannot find required utilty $f"
  91                         exit 1
  92                 fi
  93         done
  94 }
  95 
  96 
  97 function cleanup
  98 {
  99         $RM -rf "$TMP_DIR"
 100 }
 101 
 102 
 103 function is_overwrite
 104 {
 105         typeset arg=$1
 106         typeset -l ans
 107 
 108         [[ $FORCE == yes ]] && return 0
 109 
 110         while true
 111         do
 112                 gettext "$arg already exists. Overwrite it? (y/n) "
 113                 read ans
 114                 case $ans in
 115                 y*|Y*) return 0 ;;              # go ahead overwrite
 116                 n*|N*) return 1 ;;              # don't overwrite
 117                 esac
 118         done
 119         echo;
 120 }
 121 
 122 
 123 function collect_objs # <pkg> ...
 124 {
 125         typeset obj fail=0
 126 
 127         for obj
 128         do
 129                 if [[ -f "$obj"/pkginfo ]]
 130                 then
 131                         PACKAGES[ ${#PACKAGES[*]} ]=$obj
 132                 else
 133                         gettext "$obj is not in package format\n"
 134                         (( fail += 1 ))
 135                 fi
 136         done
 137         (( fail )) && return 1
 138         return 0
 139 }
 140 
 141 
 142 function mkdu
 143 {
 144         typeset distdir tmpdudir pkgs obj statusfile
 145 
 146         trap '/bin/rm -rf $statusfile $tmpdudir' EXIT
 147 
 148         # Create DU directory first.
 149         distdir=$ROOTDIR/DU/sol_$VERSION/$MACH
 150         $MKDIR -p "$distdir/Tools" "$distdir/Product"
 151 
 152         # to the other UltraSPARC architecture
 153         if [[ "$MACH" != "i386" ]]
 154         then
 155                 cd $ROOTDIR/DU/sol_$VERSION
 156                 $LN -s sparc sun4v
 157                 $LN -s sparc sun4u
 158         else
 159                 cd $ROOTDIR/DU/sol_$VERSION
 160                 $LN -s i386 i86pc
 161         fi      
 162 
 163         # Unfortunately pkgtrans insists that all packages must be in
 164         # <device1> (see pkgtrans(1)).  The packages can't have any path
 165         # components.  So we'll create a temporary directory first and then
 166         # symlinks to the specified packages.  Then run pkgtrans with
 167         # the temporary directory as <device1>.
 168         tmpdudir=$TMP_DIR/mkdu
 169         $MKDIR -p "$tmpdudir"
 170 
 171         for obj in "${PACKAGES[@]}"
 172         do
 173                 # Get rid of trailing /'s, if any.
 174                 [[ "$obj" == */ ]] && obj=${obj%%+(/)}
 175 
 176                 # Make sure it's a full pathname.
 177                 [[ "$obj" != /* ]] && obj=$ORIGPWD/$obj
 178 
 179                 $LN -s "$obj" "$tmpdudir" || exit 1
 180 
 181                 # Remember just the file component.
 182                 pkgs[ ${#pkgs[*]} ]=${obj##*/}
 183         done
 184 
 185         # Package up packages as compressed data stream.
 186         statusfile=$TMP_DIR/.pkgtrans.status
 187         {
 188                 $PKGTRANS -s "$tmpdudir" /dev/stdout "${pkgs[@]}"
 189                 echo $? > $statusfile
 190                 $TOUCH $statusfile      # make sure file is created
 191         } | $GZIP -9 > "$distdir/Product/pkgs.gz"
 192 
 193         [[ -s $statusfile && $(<$statusfile) != 0 ]] && return 1
 194 
 195         # Create admin file for pkgadd
 196         $CAT > "$distdir/Tools/admin" <<"EOF"
 197 mail=
 198 instance=unique
 199 partial=nocheck
 200 runlevel=nocheck
 201 idepend=nocheck
 202 rdepend=nocheck
 203 space=nocheck
 204 setuid=nocheck
 205 conflict=nocheck
 206 action=nocheck
 207 EOF
 208 
 209         # Create install.sh
 210         $CAT > "$distdir/Tools/install.sh" <<"EOF"
 211 #!/sbin/sh
 212 # install.sh -R <basedir> - install packages to basedir
 213 basedir=/
 214 toolsdir=`dirname $0`
 215 tmpfile=/tmp/`basename $0`.$$
 216 while getopts "R:" arg
 217 do
 218         case "$arg" in
 219                 R) basedir=$OPTARG;;
 220         esac
 221 done
 222 /usr/bin/gzip -c -d "$toolsdir/../Product/pkgs.gz" > $tmpfile &&
 223         /usr/sbin/pkgadd -R "$basedir" -d "$tmpfile" -a "$toolsdir/admin" all
 224 status=$?
 225 /bin/rm -f "$tmpfile"
 226 exit $status
 227 EOF
 228         $CHMOD a+rx "$distdir/Tools/install.sh"
 229 }
 230 
 231 
 232 function mkiso
 233 {
 234         typeset vflag
 235 
 236         # Skip if no ISO image was specified.
 237         [[ -z "$ISO" ]] && return 0
 238 
 239         # Determine mkisofs' verbose flag depending on $VERBOSE_LEVEL.
 240         case $VERBOSE_LEVEL in
 241         0)      vflag=-quiet
 242                 ;;
 243         1)      vflag=                          # mkisofs' default verboseness
 244                 ;;
 245         *)      vflag=
 246                 i=$VERBOSE_LEVEL
 247                 while ((i > 0))
 248                 do
 249                         vflag="-v $vflag"
 250                         (( i -= 1 ))
 251                 done
 252                 ;;
 253         esac
 254 
 255         (( VERBOSE_LEVEL )) && gettext "Creating ISO image ..."
 256 
 257         # Note: the "-log-file >(cat -u >&2)" and "2>/dev/null" below is a
 258         #       trick to filter out mkisofs's warning message about being
 259         #       non-conforming to ISO-9660.
 260         # We do some funky architecture-specific stuff here so that we can
 261         # actually create a bootable media image for UltraSPARC systems
 262         
 263         ISOARGS_sparc="-B ... -joliet-long -R -U"
 264         ISOARGS_i386="-d -N -r -relaxed-filenames"      
 265         if [[ "$MACH" = "i386" ]]
 266         then
 267                 ISOARGS=$ISOARGS_i386
 268         else
 269                 ISOARGS=$ISOARGS_sparc
 270         fi
 271 
 272         $MKISOFS -o "$ISO" \
 273                 -allow-leading-dots \
 274                 $ISOARGS \
 275                 -ldots -full-iso9660-filenames \
 276                 -R -J \
 277                 -V "$ISOLABEL" \
 278                 $vflag \
 279                 -log-file >($CAT -u >&2) \
 280                 "$ROOTDIR" 2>/dev/null
 281 }
 282 
 283 
 284 #
 285 # Main
 286 #
 287 trap cleanup EXIT
 288 
 289 FORCE=
 290 ISO=
 291 LABEL=
 292 RELEASE=
 293 ROOTDIR=
 294 VERBOSE_LEVEL=0
 295 
 296 while getopts ':d:fo:l:r:v' opt
 297 do
 298         case $opt in
 299         d)      ROOTDIR=$OPTARG
 300                 ;;
 301         f)      FORCE=yes
 302                 ;;
 303         o)      ISO=$OPTARG
 304                 if [ ! -z `echo $ISO | $GREP "^/tmp"` ]; then
 305                         gettext "ISO images will not be created on /tmp.\n"
 306                         gettext "Please choose a different output location.\n"
 307                         exit 3
 308                 fi
 309                 ;;
 310         l)      LABEL=$OPTARG
 311                 ;;
 312         r)      RELEASE=$OPTARG
 313                 ;;
 314         v)      (( VERBOSE_LEVEL += 1 ))
 315                 ;;
 316         :)      gettext "Option -$OPTARG missing argument."
 317                 usage
 318                 exit 1
 319                 ;;
 320         *)      gettext "Option -$OPTARG invalid.\n"
 321                 usage
 322                 exit 2
 323                 ;;
 324         esac
 325 done
 326 shift 'OPTIND - 1'
 327 
 328 # Release number must be specified.
 329 if [[ -z "$RELEASE" ]]
 330 then
 331         gettext "Solaris release number must be specified (-r option).\n"
 332         usage
 333         exit 1
 334 fi
 335 
 336 # Verify release number.  Allow major.minor or major.minor.micro format.
 337 if [[ $RELEASE != +([0-9]).+([0-9])?(.+([0-9])) ]]
 338 then
 339         gettext "Invalid release number: $RELEASE\n"
 340         exit 1
 341 fi
 342 VERSION=$(echo $RELEASE | $SED 's/5\./2/')
 343 
 344 # Either or both of -d or -o option must be specified.
 345 if [[ -z "$ROOTDIR" && -z "$ISO" ]]
 346 then
 347         gettext "Either -d or -o option (or both) must be specified.\n"
 348         usage
 349         exit 1
 350 fi
 351 
 352 # There must be at least one package.
 353 if (( $# == 0 ))
 354 then
 355         gettext "No package was specified.\n"
 356         usage
 357         exit 1
 358 fi
 359 
 360 # Check and collect packages
 361 unset PACKAGES
 362 collect_objs "$@" || exit 1
 363 
 364 # Default label for ISO image
 365 LABEL=${LABEL:-DU sol_$VERSION}
 366 
 367 check_prereqs           # must be called after $ISO is possibly set
 368 
 369 # If an ISO image was specified, check its parent directory and get its
 370 # full pathname.
 371 unset ISODIR
 372 if [[ -n "$ISO" ]]
 373 then
 374         if [[ "$ISO" = */* ]]
 375         then
 376                 ISODIR=$(cd "${ISO%/*}" 2>/dev/null && pwd -P)
 377                 if (( $? ))
 378                 then
 379                         gettext "Can't access parent directory of ISO image\n"
 380                         exit 1
 381                 fi
 382         else
 383                 ISODIR=$(pwd -P)
 384         fi
 385 fi
 386 
 387 # If user specified a media root directory, verify it exists, else use
 388 # a temporary directory.
 389 if [[ -n "$ROOTDIR" ]]
 390 then
 391         $MKDIR -p "$ROOTDIR";
 392         if [ $? != 0 ]; then
 393                 gettext "$ROOTDIR is not a directory.\n"
 394                 exit 1
 395         elif [[ ! -w "$ROOTDIR" ]] then
 396                 gettext "Directory $ROOTDIR is not writable.\n"
 397                 exit 1
 398         fi
 399         # If an ISO image path is also specified, make sure it's not under
 400         # $ROOTDIR since we're going to take the ISO image of $ROOTDIR.
 401         if [[ -n "$ISODIR" ]]
 402         then
 403                 realroot=$(cd "$ROOTDIR" 2>/dev/null && pwd -P)
 404                 if [[ "$ISODIR" = "$realroot"?(/*) ]]
 405                 then
 406                         gettext "ISO image must not be under Driver Update's parent directory ($realroot)\n"
 407                         exit 1
 408                 fi
 409         fi
 410 else
 411         ROOTDIR=$TMP_DIR/root
 412 fi
 413 
 414 # If DU directory already exists, ask user permission to remove it unless -f
 415 # option was specified.
 416 if [[ -d "$ROOTDIR/DU" ]]
 417 then
 418         is_overwrite "$ROOTDIR/DU" || exit 0
 419         $RM -rf "$ROOTDIR/DU"
 420 fi
 421 
 422 # If ISO image already exists, ask user permission to remove it unless -f
 423 # option was specified.
 424 if [[ -f "$ISO" ]]
 425 then
 426         is_overwrite "$ISO" || exit 0
 427         $RM -f "$ISO"
 428 fi
 429 
 430 # Create DU directory and the ISO image (if requested).
 431 mkdu && mkiso
 432 if (( $? ))
 433 then
 434         $RM -rf "$ROOTDIR/DU"
 435         [[ -n "$ISO" ]] && $RM -f "$ISO"
 436         exit 1
 437 fi
 438 exit 0