1 #! /usr/bin/sh
   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 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23 # Use is subject to license terms.
  24 #
  25 #
  26 # This is a clean script for removable disks
  27 # 
  28 # Following is the syntax for calling the script:
  29 #       scriptname [-s|-f|-i|-I] devicename [-A|-D] username zonename zonepath
  30 #
  31 #       -s for standard cleanup by a user
  32 #       -f for forced cleanup by an administrator
  33 #       -i for boot-time initialization (when the system is booted with -r) 
  34 #       -I to suppress error/warning messages; the script is run in the '-i'
  35 #          mode
  36 #
  37 # $1:   devicename - device to be allocated/deallocated, e.g., sr0
  38 #
  39 # $2:   -A if cleanup is for allocation, or -D if cleanup is for deallocation.
  40 #
  41 # $3:   username - run the script as this user, rather than as the caller.
  42 #
  43 # $4:   zonename - zone in which device to be allocated/deallocated
  44 #
  45 # $5:   zonepath - root path of zonename
  46 #
  47 # A clean script for a removable media device should prompt the user to 
  48 # insert correctly labeled media at allocation time, and ensure that the
  49 # media is ejected at deallocation time.
  50 #
  51 # Unless the clean script is being called for boot-time
  52 # initialization, it may communicate with the user via stdin and
  53 # stdout.  To communicate with the user via CDE dialogs, create a
  54 # script or link with the same name, but with ".windowing" appended.
  55 # For example, if the clean script specified in device_allocate is
  56 # /etc/security/xyz_clean, that script must use stdin/stdout.  If a
  57 # script named /etc/security/xyz_clean.windowing exists, it must use
  58 # dialogs.  To present dialogs to the user, the dtksh script
  59 # /etc/security/lib/wdwmsg may be used.
  60 #
  61 # This particular script, disk_clean, will work using stdin/stdout, or
  62 # using dialogs.  A symbolic link disk_clean.windowing points to
  63 # disk_clean.
  64 #
  65  
  66 # ####################################################
  67 # ################  Local Functions  #################
  68 # ####################################################
  69  
  70 #
  71 # Set up for windowing and non-windowing messages
  72 #
  73 msg_init()
  74 {
  75     if [ `basename $0` != `basename $0 .windowing` ]; then
  76         WINDOWING="yes"
  77         case $VOLUME_MEDIATYPE in
  78           cdrom)   TITLE="CD-ROM";;
  79           rmdisk)  TITLE="Removable Disk";;
  80           floppy)  TITLE="Floppy";;
  81           *)       TITLE="Disk";;
  82         esac
  83         
  84         if [ "$MODE" = "allocate" ]; then
  85             TITLE="$TITLE Allocation"
  86         else
  87             TITLE="$TITLE Deallocation"
  88         fi
  89     else
  90         WINDOWING="no"
  91     fi
  92 }
  93 
  94 #
  95 # Display a message for the user.  For windowing, user must press OK button 
  96 # to continue. For non-windowing, no response is required.
  97 #
  98 msg() {
  99     if [ "$WINDOWING" = "yes" ]; then
 100         $WDWMSG "$*" "$TITLE" OK
 101     elif [ "$silent" != "y" ]; then
 102         echo "$*" > /dev/${MSGDEV}
 103     fi
 104 }
 105 
 106 ok_msg() {
 107         if [ "$WINDOWING" = "yes" ]; then
 108                 $WDWMSG "$*" "$TITLE" READY
 109         else
 110                 form=`gettext "Media in %s is ready. Please store safely."`
 111                 printf "${form}\n" $PROG $DEVICE > /dev/{MSGDEV}
 112         fi
 113 }
 114 
 115 error_msg() {
 116         if [ "$WINDOWING" = "yes" ]; then
 117                 $WDWMSG "$*" "$TITLE" ERROR
 118         else
 119                 form=`gettext "%s: Error cleaning up device %s."`
 120                 printf "${form}\n" $PROG $DEVICE > /dev/${MSGDEV}
 121         fi
 122 }
 123 
 124 #
 125 # Ask the user an OK/Cancel question.  Return 0 for OK, 1 for Cancel.
 126 #
 127 okcancel() {
 128     if [ "$WINDOWING" = "yes" ]; then
 129         $WDWMSG "$*" "$TITLE" OK Cancel
 130     elif [ "$silent" != "y" ]; then
 131         get_reply "$* (y to continue, n to cancel) \c" y n
 132     fi
 133 }
 134 
 135 #
 136 # Ask the user an Yes/No question.  Return 0 for Yes, 1 for No
 137 #
 138 yesno() {
 139     if [ "$WINDOWING" = "yes" ]; then
 140         $WDWMSG "$*" "$TITLE" Yes No
 141     elif [ "$silent" != "y" ]; then
 142         get_reply "$* (y/n) \c" y n
 143     fi
 144 }
 145 
 146 #
 147 # Display an error message, put the device in the error state, and exit.
 148 #
 149 error_exit() {
 150         if [ "$silent" != "y" ]; then
 151                 msg "$2" "$3" \
 152                     "\n\nDevice has been placed in allocation error state." \
 153                     "\nPlease inform system administrator."
 154         fi
 155         exit 1
 156 }
 157 
 158 #
 159 # get_reply prompt choice ...
 160 #
 161 get_reply() {
 162         prompt=$1; shift
 163         while true
 164         do
 165                 echo $prompt > /dev/tty
 166                 read reply
 167                 i=0
 168                 for choice in $*
 169                 do
 170                         if [ "$choice" = "$reply" ]
 171                         then
 172                                 return $i
 173                         else
 174                                 i=`expr $i + 1`
 175                         fi
 176                 done
 177         done
 178 }
 179 
 180 #
 181 # Find the first disk slice containing a file system
 182 #
 183 find_fs()
 184 {
 185         # The list of files in device_maps(4) is in an unspecified order.
 186         # To speed up the fstyp(1M) scanning below in most cases, perform
 187         # the search for filesystems as follows:
 188         # 1) Select only block device files of the form "/dev/dsk/*".
 189         # 2) Sort the list of files in an order more likely to yield
 190         #    matches: first the fdisk(1M) partitions ("/dev/dsk/cNtNdNpN")
 191         #    then the format(1M) slices ("/dev/dsk/cNtNdNsN"), in ascending
 192         #    numeric order within each group.
 193         DEVall="`echo $FILES | \
 194             /usr/bin/tr ' ' '\n' | \
 195             /usr/bin/sed '/^\/dev\/dsk\//!d; s/\([sp]\)\([0-9]*\)$/ \1 \2/;' | \
 196             /usr/bin/sort -t ' ' -k 2,2d -k 3,3n | \
 197             /usr/bin/tr -d ' '`"
 198         for DEVn in $DEVall ; do
 199                 fstyp_output="`/usr/sbin/fstyp -a $DEVn 2>&1`"
 200                 if [ $? = 0 ]; then
 201                         FSPATH=$DEVn
 202                         gen_volume_label="`echo "$fstyp_output" | \
 203                             sed -n '/^gen_volume_label: .\(.*\).$/s//\1/p'`"
 204                         if [ "$gen_volume_label" != "" ]; then
 205                                 FSNAME="`echo $gen_volume_label | \
 206                                     /usr/xpg4/bin/tr '[:upper:] ' '[:lower:]_'`"
 207                         fi
 208                         # For consistency, hsfs filesystems detected at
 209                         # /dev/dsk/*p0 are mounted as /dev/dsk/*s2
 210                         FSTYPE=`echo "$fstyp_output" | /usr/bin/head -1`
 211                         if [ "$FSTYPE" = hsfs -a \
 212                             `/usr/bin/expr $FSPATH : '.*p0'` -gt 0 ]; then
 213                                 FSPATH=`echo $FSPATH | /usr/bin/sed 's/p0$/s2/'`
 214                         fi
 215                         return
 216                 fi
 217         done
 218 }
 219 
 220 #
 221 # Find all mountpoints in use for a set of device special files.
 222 # Usage: findmounts devpath ...
 223 #
 224 
 225 findmounts() {
 226         nawk -f - -v vold_root="$VOLD_ROOT" -v devs="$*" /etc/mnttab <<\
 227             "ENDOFAWKPGM"
 228         BEGIN {
 229                 split(devs, devlist, " ");
 230                 for (devN in devlist) {
 231                         dev = devlist[devN];
 232                         realdevlist[dev] = 1;
 233                         sub(/.*\//, "", dev);
 234                         sub(/s[0-9]$/, "", dev);
 235                         if (vold_root != "") {
 236                                 vold_dir[vold_root "/dev/dsk/" dev] = 1;
 237                                 vold_dir[vold_root "/dev/rdsk/" dev] = 1;
 238                         }
 239                 }
 240         }
 241 
 242         {
 243                 for (dev in realdevlist) {
 244                         if ($1 == dev) {
 245                                 mountpoint = $2;
 246                                 print mountpoint;
 247                         }
 248                 }
 249                 for (dev in vold_dir) {
 250                         if (substr($1, 1, length(dev)) == dev) {
 251                                 mountpoint = $2;
 252                                 print mountpoint;
 253                         }
 254                 }
 255         }
 256 ENDOFAWKPGM
 257 }
 258 
 259 #
 260 # Allocate a device.
 261 # Ask the user to make sure the disk is properly labeled.
 262 # Ask if the disk should be mounted.
 263 #
 264 do_allocate()
 265 {
 266         if [ $VOLUME_MEDIATYPE = floppy ]; then
 267                 # Determine if media is in drive
 268                 eject_msg="`eject -q $DEVFILE 2>&1`"
 269                 eject_status="$?"
 270                 case $eject_status in
 271                 1) # Media is not in drive
 272                         okcancel "Insert disk in $DEVICE."
 273                         if [ $? != 0 ]; then
 274                                 exit 0
 275                         fi;;
 276                 3) # Error 
 277                         error_exit $DEVICE \
 278                             "Error checking for media in drive.";;
 279                 esac
 280         else
 281                 okcancel "Insert disk in $DEVICE."
 282                 if [ $? != 0 ]; then
 283                         exit 0
 284                 fi
 285         fi
 286     
 287         yesno "Do you want $DEVICE mounted?"
 288         if [ $? != 0 ]; then
 289                 exit 0
 290         fi
 291 
 292         if [ $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk ]; then
 293                 # Get the device path and volume name of a partition
 294                 find_fs
 295                 if [ "$FSPATH" != "" ]; then
 296                         VOLUME_PATH=$FSPATH     
 297                 fi
 298                 if [ "$FSNAME" != "" ]; then
 299                         VOLUME_NAME=$FSNAME
 300                 fi
 301         fi
 302         VOLUME_ACTION=insert
 303 
 304         # Give ourself write permission on device file so file system gets
 305         # mounted read/write if possible.
 306         # rmmount only cares about permissions not user...
 307         chown $VOLUME_USER $VOLUME_PATH
 308         chmod 700 $VOLUME_PATH
 309 
 310         # Do the actual mount.  VOLUME_* environment variables are inputs to
 311         # rmmount.
 312         rmmount_msg="`/usr/sbin/rmmount 2>&1`"
 313         rmmount_status="$?"
 314         if [ $rmmount_status -eq 0 ]; then
 315                 EXIT_STATUS=$CLEAN_MOUNT
 316         elif [ $rmmount_status -gt 0 -a $VOLUME_MEDIATYPE != cdrom ]; then
 317                 # Try again in readonly mode. cdrom is always mounted ro, so
 318                 # no need to try again.
 319                 echo "Read-write mount of $DEVICE failed. Mounting read-only."
 320                 VOLUME_ACTION=remount; export VOLUME_ACTION
 321                 VOLUME_MOUNT_MODE=ro; export VOLUME_MOUNT_MODE
 322                 `/usr/sbin/rmmount`
 323                 if [ $? -eq 0 ]; then
 324                         EXIT_STATUS=$CLEAN_MOUNT
 325                 fi
 326         fi
 327 
 328         # Set permissions on directory used by vold, sdtvolcheck, etc.
 329         if [ -d /tmp/.removable ]; then
 330                 chown root /tmp/.removable
 331                 chmod 777 /tmp/.removable
 332         fi
 333 }
 334 
 335 
 336 do_deallocate()
 337 {
 338         if [ $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk ]; then
 339                 if [ -h /$VOLUME_MEDIATYPE/$DEVICE ]; then
 340                         # Get the device path and volume name of a partition
 341                         VOLUME_PATH=`ls -l /$VOLUME_MEDIATYPE/$DEVICE|\
 342                             cut -d '>' -f2`
 343                         VOLUME_DEVICE=`mount -p|grep $VOLUME_PATH|\
 344                             cut -d ' ' -f1`
 345                 fi
 346         fi
 347 
 348         if [ -d "$VOLUME_PATH" ]; then
 349                 VOLUME_ACTION=eject
 350                 # Do the actual unmount.
 351                 # VOLUME_* environment variables are inputs to rmmount.
 352                 rmmount_msg="`/usr/sbin/rmmount 2>&1`"
 353                 rmmount_status="$?"
 354 
 355                 # Remove symbolic links to mount point
 356                 for name in /$VOLUME_MEDIATYPE/*; do
 357                         if [ -h $name ]; then
 358                                 target=`ls -l $name | awk '{ print $NF; }'`
 359                                 target_dir=`dirname $target`
 360                                 target_device=`echo $target_dir | \
 361                                     sed -e 's/^.*-\(.*\)$/\1/'`
 362                                 if [ "$target_device" = "$DEVICE" ]; then
 363                                         rm -f $name
 364                                 fi
 365                         fi
 366                 done
 367         else
 368                 rmmount_status=0
 369         fi
 370 
 371         case $rmmount_status in
 372         1) # still mounted
 373                 error_exit $DEVICE "Error unmounting $DEVICE" "$rmmount_msg";;
 374         0) # not mounted
 375                 # Eject the media
 376                 if [ "$FLAG" = "f" ] ; then
 377                         eject_msg="`eject -f $DEVICE 2>&1`"
 378                 else
 379                         eject_msg="`eject $DEVICE 2>&1`"
 380                 fi
 381                 eject_status="$?"
 382                 case $eject_status in
 383                 0|1|4) # Media has been ejected
 384                         case $VOLUME_MEDIATYPE in
 385                         floppy|cdrom|rmdisk)
 386                                 msg "Please remove the disk from $DEVICE.";;
 387                         esac;;
 388                 3) # Media didn't eject
 389                         msg $DEVICE "Error ejecting disk from $DEVICE" \
 390                             "$eject_msg";;
 391                 esac
 392         esac
 393 }
 394 
 395 #
 396 # Reclaim a device
 397 #
 398 do_init()
 399 {
 400         eject_msg="`eject -f $DEVICE 2>&1`"
 401         eject_status="$?"
 402 
 403         case $eject_status in
 404         0) # Media has been ejected 
 405                 if [ "$silent" != "y" ]; then
 406                         ok_msg
 407                 fi
 408                 exit 0;;
 409         1) # Media not ejected
 410                 if [ "$silent" != "y" ]; then
 411                         error_msg
 412                 fi
 413                 exit 0;;
 414         3) # Error 
 415                 if [ "$silent" != "y" ]; then
 416                         error_msg
 417                 fi
 418                 msg $DEVICE "Error ejecting disk from $DEVICE" \
 419                 "$eject_msg"
 420                 exit 2;;
 421         esac
 422 }
 423 
 424 
 425 # ####################################################
 426 # ################ Begin main program ################
 427 # ####################################################
 428 
 429 trap "" INT TERM QUIT TSTP ABRT
 430 
 431 PATH="/usr/bin:/usr/sbin"
 432 MODE="allocate"
 433 SILENT=n
 434 WDWMSG="/etc/security/lib/wdwmsg"
 435 VOLUME_ZONE_PATH="/"
 436 USAGE="Usage: disk_clean [-s|-f|-i|-I] devicename -[A|D] [username] [zonename] [zonepath]"
 437 EXIT_STATUS=0
 438 CLEAN_MOUNT=4
 439 MACH=`uname -p`
 440 FLAG=i
 441 #
 442 # Parse the command line arguments
 443 #
 444 while getopts ifsI c
 445 do
 446         case $c in
 447         i)
 448                 FLAG=$c;;
 449         f)
 450                 FLAG=$c;;
 451         s)
 452                 FLAG=$c;;
 453         I)
 454                 FLAG=i
 455                 silent=y;;
 456         \?)
 457                 echo $USAGE
 458                 exit 1;;
 459       esac
 460 done
 461 
 462 shift `expr $OPTIND - 1`
 463 
 464 DEVICE=$1
 465 MODE="deallocate"
 466 if [ "$2" = "-A" ]; then
 467         MODE="allocate"
 468 elif [ "$2" = "-D" ]; then
 469         MODE="deallocate"
 470 fi
 471 
 472 #get the device_maps information
 473 MAP=`/usr/sbin/list_devices -s -l $DEVICE`
 474 FILES=`echo $MAP | cut -f4 -d:` # e.g., /dev/dsk/c0t6d0s0 /dev/dsk/c0t6d0s1 ...
 475 DEVFILE=`echo $FILES | cut -f1 -d" "`           # e.g., "/dev/dsk/c0t6d0s0"
 476 
 477 # Set VOLUME_ variables that are inputs to rmmount
 478 
 479 VOLUME_DEVICE=`echo $FILES | cut -f2 -d" "`     # e.g., "/dev/dsk/c0t6d0s1"
 480 MEDIATYPE=`echo $MAP | cut -f3 -d: | cut -f2 -d" "`
 481                                                 # e.g., "cdrom" or "floppy"
 482 if [ "$MEDIATYPE" = "sr" ]; then
 483         VOLUME_MEDIATYPE="cdrom"
 484 elif [ "$MEDIATYPE" = "fd" ]; then
 485         VOLUME_MEDIATYPE="floppy"
 486 elif [ "$MEDIATYPE" = "rmdisk" ]; then
 487         VOLUME_MEDIATYPE="rmdisk"
 488 fi
 489 
 490 VOLUME_PATH=$DEVFILE                            # e.g., "/dev/dsk/c0t6d0s0"
 491 if [ "$MACH" = "i386" ] && [ "$MEDIATYPE" = "rmdisk" ]; then
 492         VOLUME_PATH=`echo $DEVFILE | sed -e 's/s0/p0/'`
 493 fi
 494 
 495 SYMDEV=`echo $DEVICE | sed -e 's/_//'`          # e.g., "cdrom" or "floppy"
 496 SYMNUM=`echo $SYMDEV | sed -e 's/[a-z]*//g'`
 497 SYMDEV=`echo $SYMDEV | sed -e 's/[0-9]*//g'`
 498 if [ "$SYMDEV" = "sr" ]; then
 499         VOLUME_SYMDEV="cdrom"$SYMNUM
 500 elif [ "$SYMDEV" = "fd" ]; then
 501         VOLUME_SYMDEV="floppy"$SYMNUM
 502 elif [ "$SYMDEV" = "rmdisk" ]; then
 503         VOLUME_SYMDEV="rmdisk"$SYMNUM
 504 else
 505         VOLUME_SYMDEV=$SYMDEV$SYMNUM
 506 fi
 507 
 508 VOLUME_ZONE_NAME=$4
 509 
 510 VOLUME_ZONE_PATH=$5
 511 
 512 if [ "$MODE" = "allocate" ]; then
 513         if [ -n "$3" ]; then                    # e.g., "joeuser"
 514                 VOLUME_USER=$3
 515         else
 516                 VOLUME_USER=`/usr/xpg4/bin/id -u -nr`
 517         fi
 518 else
 519         # If there's a directory for the device under /<mediatype>, get the 
 520         # user name from there, to use in cleaning up that directory. Otherwise,
 521         # the user name isn't actually used in deallocation.
 522         if [ -d ${VOLUME_ZONE_PATH}/${VOLUME_MEDIATYPE}/*-${DEVICE} ]; then
 523                 VOLUME_USER=`ls -ld ${VOLUME_ZONE_PATH}/${VOLUME_MEDIATYPE}/*-${DEVICE} | awk '/^d/{print $3}'`
 524         else
 525                 if [ -n "$3" ]; then
 526                         VOLUME_USER=$3
 527                 else
 528                         VOLUME_USER=`/usr/xpg4/bin/id -u -nr`
 529                 fi
 530         fi
 531 fi    
 532 
 533 VOLUME_NAME=unnamed_${VOLUME_MEDIATYPE}
 534                                         # e.g., "joeuser-cdrom0/unnamed_cdrom"
 535 
 536 if [ "$VOLUME_MEDIATYPE" = "rmdisk" ]; then
 537         VOLUME_PCFS_ID=1
 538 else
 539         VOLUME_PCFS_ID=
 540 fi
 541 
 542 export VOLUME_ACTION VOLUME_DEVICE VOLUME_MEDIATYPE VOLUME_NAME VOLUME_PCFS_ID
 543 export VOLUME_PATH VOLUME_SYMDEV VOLUME_USER VOLUME_ZONE_NAME VOLUME_ZONE_PATH
 544 
 545 USERDIR=${VOLUME_USER}-${DEVICE}        # e.g., "joeusr-cdrom0"
 546 
 547 msg_init
 548 
 549 if [ "$MODE" = "allocate" ]; then
 550         MSGDEV=tty
 551         do_allocate
 552 else
 553     if [ "$FLAG" = "i" ] ; then
 554         MSGDEV=console
 555         do_init
 556     else
 557         MSGDEV=tty
 558         do_deallocate
 559     fi
 560 fi
 561 
 562 exit $EXIT_STATUS