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