#! /usr/bin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # This is a clean script for removable disks # # Following is the syntax for calling the script: # scriptname [-s|-f|-i|-I] devicename [-A|-D] username zonename zonepath # # -s for standard cleanup by a user # -f for forced cleanup by an administrator # -i for boot-time initialization (when the system is booted with -r) # -I to suppress error/warning messages; the script is run in the '-i' # mode # # $1: devicename - device to be allocated/deallocated, e.g., sr0 # # $2: -A if cleanup is for allocation, or -D if cleanup is for deallocation. # # $3: username - run the script as this user, rather than as the caller. # # $4: zonename - zone in which device to be allocated/deallocated # # $5: zonepath - root path of zonename # # A clean script for a removable media device should prompt the user to # insert correctly labeled media at allocation time, and ensure that the # media is ejected at deallocation time. # # Unless the clean script is being called for boot-time # initialization, it may communicate with the user via stdin and # stdout. To communicate with the user via CDE dialogs, create a # script or link with the same name, but with ".windowing" appended. # For example, if the clean script specified in device_allocate is # /etc/security/xyz_clean, that script must use stdin/stdout. If a # script named /etc/security/xyz_clean.windowing exists, it must use # dialogs. To present dialogs to the user, the dtksh script # /etc/security/lib/wdwmsg may be used. # # This particular script, disk_clean, will work using stdin/stdout, or # using dialogs. A symbolic link disk_clean.windowing points to # disk_clean. # # #################################################### # ################ Local Functions ################# # #################################################### # # Set up for windowing and non-windowing messages # proc msg_init { if test $(basename $0) != $(basename $0 .windowing) { setvar WINDOWING = ""yes"" match $VOLUME_MEDIATYPE { with cdrom setvar TITLE = ""CD-ROM"" with rmdisk setvar TITLE = ""Removable Disk"" with floppy setvar TITLE = ""Floppy"" with * setvar TITLE = ""Disk"" } if test $MODE = "allocate" { setvar TITLE = ""$TITLE Allocation"" } else { setvar TITLE = ""$TITLE Deallocation"" } } else { setvar WINDOWING = ""no"" } } # # Display a message for the user. For windowing, user must press OK button # to continue. For non-windowing, no response is required. # proc msg { if test $WINDOWING = "yes" { $WDWMSG "$ifsjoin(ARGV)" $TITLE OK } elif test $silent != "y" { echo "$ifsjoin(ARGV)" > /dev/${MSGDEV} } } proc ok_msg { if test $WINDOWING = "yes" { $WDWMSG "$ifsjoin(ARGV)" $TITLE READY } else { setvar form = $(gettext "Media in %s is ready. Please store safely.) printf "${form}\n" $PROG $DEVICE > /dev/{MSGDEV} } } proc error_msg { if test $WINDOWING = "yes" { $WDWMSG "$ifsjoin(ARGV)" $TITLE ERROR } else { setvar form = $(gettext "%s: Error cleaning up device %s.) printf "${form}\n" $PROG $DEVICE > /dev/${MSGDEV} } } # # Ask the user an OK/Cancel question. Return 0 for OK, 1 for Cancel. # proc okcancel { if test $WINDOWING = "yes" { $WDWMSG "$ifsjoin(ARGV)" $TITLE OK Cancel } elif test $silent != "y" { get_reply "$ifsjoin(ARGV) (y to continue, n to cancel) \c" y n } } # # Ask the user an Yes/No question. Return 0 for Yes, 1 for No # proc yesno { if test $WINDOWING = "yes" { $WDWMSG "$ifsjoin(ARGV)" $TITLE Yes No } elif test $silent != "y" { get_reply "$ifsjoin(ARGV) (y/n) \c" y n } } # # Display an error message, put the device in the error state, and exit. # proc error_exit { if test $silent != "y" { msg $2 $3 \ "\n\nDevice has been placed in allocation error state." \ "\nPlease inform system administrator." } exit 1 } # # get_reply prompt choice ... # proc get_reply { setvar prompt = "$1"; shift while true { echo $prompt > /dev/tty read reply setvar i = '0' for choice in [$ifsjoin(ARGV)] { if test $choice = $reply { return $i } else { setvar i = $(expr $i + 1) } } } } # # Find the first disk slice containing a file system # proc find_fs { # The list of files in device_maps(4) is in an unspecified order. # To speed up the fstyp(1M) scanning below in most cases, perform # the search for filesystems as follows: # 1) Select only block device files of the form "/dev/dsk/*". # 2) Sort the list of files in an order more likely to yield # matches: first the fdisk(1M) partitions ("/dev/dsk/cNtNdNpN") # then the format(1M) slices ("/dev/dsk/cNtNdNsN"), in ascending # numeric order within each group. setvar DEVall = "$(echo $FILES | \ /usr/bin/tr ' ' '\n' | \ /usr/bin/sed '/^\/dev\/dsk\//!d; s/\([sp]\)\([0-9]*\)$/ \1 \2/;' | \ /usr/bin/sort -t ' ' -k 2,2d -k 3,3n | \ /usr/bin/tr -d ' )" for DEVn in [$DEVall] { setvar fstyp_output = "$(/usr/sbin/fstyp -a $DEVn )" if test $Status = 0 { setvar FSPATH = "$DEVn" setvar gen_volume_label = "$(echo $fstyp_output | \ sed -n '/^gen_volume_label: .\(.*\).$/s//\1/p)" if test $gen_volume_label != "" { setvar FSNAME = "$(echo $gen_volume_label | \ /usr/xpg4/bin/tr '[:upper:] ' '[:lower:]_)" } # For consistency, hsfs filesystems detected at # /dev/dsk/*p0 are mounted as /dev/dsk/*s2 setvar FSTYPE = $(echo $fstyp_output | /usr/bin/head -1) if test $FSTYPE = hsfs -a \ $(/usr/bin/expr $FSPATH : '.*p0) -gt 0 { setvar FSPATH = $(echo $FSPATH | /usr/bin/sed 's/p0$/s2/) } return } } } # # Find all mountpoints in use for a set of device special files. # Usage: findmounts devpath ... # proc findmounts { nawk -f - -v vold_root="$VOLD_ROOT" -v devs="$ifsjoin(ARGV)" /etc/mnttab <<< ''' BEGIN { split(devs, devlist, " "); for (devN in devlist) { dev = devlist[devN]; realdevlist[dev] = 1; sub(/.*\//, "", dev); sub(/s[0-9]$/, "", dev); if (vold_root != "") { vold_dir[vold_root "/dev/dsk/" dev] = 1; vold_dir[vold_root "/dev/rdsk/" dev] = 1; } } } { for (dev in realdevlist) { if ($1 == dev) { mountpoint = $2; print mountpoint; } } for (dev in vold_dir) { if (substr($1, 1, length(dev)) == dev) { mountpoint = $2; print mountpoint; } } } ''' } # # Allocate a device. # Ask the user to make sure the disk is properly labeled. # Ask if the disk should be mounted. # proc do_allocate { if test $VOLUME_MEDIATYPE = floppy { # Determine if media is in drive setvar eject_msg = "$(eject -q $DEVFILE )" setvar eject_status = ""$Status"" match $eject_status { with 1 # Media is not in drive okcancel "Insert disk in $DEVICE." if test $Status != 0 { exit 0 } with 3 # Error error_exit $DEVICE \ "Error checking for media in drive." } } else { okcancel "Insert disk in $DEVICE." if test $Status != 0 { exit 0 } } yesno "Do you want $DEVICE mounted?" if test $Status != 0 { exit 0 } if test $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk { # Get the device path and volume name of a partition find_fs if test $FSPATH != "" { setvar VOLUME_PATH = "$FSPATH" } if test $FSNAME != "" { setvar VOLUME_NAME = "$FSNAME" } } setvar VOLUME_ACTION = 'insert' # Give ourself write permission on device file so file system gets # mounted read/write if possible. # rmmount only cares about permissions not user... chown $VOLUME_USER $VOLUME_PATH chmod 700 $VOLUME_PATH # Do the actual mount. VOLUME_* environment variables are inputs to # rmmount. setvar rmmount_msg = "$(/usr/sbin/rmmount )" setvar rmmount_status = ""$Status"" if test $rmmount_status -eq 0 { setvar EXIT_STATUS = "$CLEAN_MOUNT" } elif test $rmmount_status -gt 0 -a $VOLUME_MEDIATYPE != cdrom { # Try again in readonly mode. cdrom is always mounted ro, so # no need to try again. echo "Read-write mount of $DEVICE failed. Mounting read-only." setvar VOLUME_ACTION = 'remount'; export VOLUME_ACTION setvar VOLUME_MOUNT_MODE = 'ro'; export VOLUME_MOUNT_MODE $(/usr/sbin/rmmount) if test $Status -eq 0 { setvar EXIT_STATUS = "$CLEAN_MOUNT" } } # Set permissions on directory used by vold, sdtvolcheck, etc. if test -d /tmp/.removable { chown root /tmp/.removable chmod 777 /tmp/.removable } } proc do_deallocate { if test $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk { if test -h /$VOLUME_MEDIATYPE/$DEVICE { # Get the device path and volume name of a partition setvar VOLUME_PATH = $(ls -l /$VOLUME_MEDIATYPE/$DEVICE|\ cut -d '>' -f2) setvar VOLUME_DEVICE = $(mount -p|grep $VOLUME_PATH|\ cut -d ' ' -f1) } } if test -d $VOLUME_PATH { setvar VOLUME_ACTION = 'eject' # Do the actual unmount. # VOLUME_* environment variables are inputs to rmmount. setvar rmmount_msg = "$(/usr/sbin/rmmount )" setvar rmmount_status = ""$Status"" # Remove symbolic links to mount point for name in [/$VOLUME_MEDIATYPE/*] { if test -h $name { setvar target = $(ls -l $name | awk '{ print $NF; }) setvar target_dir = $(dirname $target) setvar target_device = $(echo $target_dir | \ sed -e 's/^.*-\(.*\)$/\1/) if test $target_device = $DEVICE { rm -f $name } } } } else { setvar rmmount_status = '0' } match $rmmount_status { with 1 # still mounted error_exit $DEVICE "Error unmounting $DEVICE" $rmmount_msg with 0 # not mounted # Eject the media if test $FLAG = "f" { setvar eject_msg = "$(eject -f $DEVICE )" } else { setvar eject_msg = "$(eject $DEVICE )" } setvar eject_status = ""$Status"" match $eject_status { with 0|1|4 # Media has been ejected match $VOLUME_MEDIATYPE { with floppy|cdrom|rmdisk msg "Please remove the disk from $DEVICE." } with 3 # Media didn't eject msg $DEVICE "Error ejecting disk from $DEVICE" \ $eject_msg } } } # # Reclaim a device # proc do_init { setvar eject_msg = "$(eject -f $DEVICE )" setvar eject_status = ""$Status"" match $eject_status { with 0 # Media has been ejected if test $silent != "y" { ok_msg } exit 0 with 1 # Media not ejected if test $silent != "y" { error_msg } exit 0 with 3 # Error if test $silent != "y" { error_msg } msg $DEVICE "Error ejecting disk from $DEVICE" \ $eject_msg exit 2 } } # #################################################### # ################ Begin main program ################ # #################################################### trap "" INT TERM QUIT TSTP ABRT setvar PATH = ""/usr/bin:/usr/sbin"" setvar MODE = ""allocate"" setvar SILENT = 'n' setvar WDWMSG = ""/etc/security/lib/wdwmsg"" setvar VOLUME_ZONE_PATH = ""/"" setvar USAGE = ""Usage: disk_clean [-s|-f|-i|-I] devicename -[A|D] [username] [zonename] [zonepath]"" setvar EXIT_STATUS = '0' setvar CLEAN_MOUNT = '4' setvar MACH = $(uname -p) setvar FLAG = 'i' # # Parse the command line arguments # while getopts ifsI c { match $c { with i setvar FLAG = "$c" with f setvar FLAG = "$c" with s setvar FLAG = "$c" with I setvar FLAG = 'i' setvar silent = 'y' with \? echo $USAGE exit 1 } } shift $(expr $OPTIND - 1) setvar DEVICE = "$1" setvar MODE = ""deallocate"" if test $2 = "-A" { setvar MODE = ""allocate"" } elif test $2 = "-D" { setvar MODE = ""deallocate"" } #get the device_maps information setvar MAP = $(/usr/sbin/list_devices -s -l $DEVICE) setvar FILES = $(echo $MAP | cut -f4 -d:) # e.g., /dev/dsk/c0t6d0s0 /dev/dsk/c0t6d0s1 ... setvar DEVFILE = $(echo $FILES | cut -f1 -d" ) # e.g., "/dev/dsk/c0t6d0s0" # Set VOLUME_ variables that are inputs to rmmount setvar VOLUME_DEVICE = $(echo $FILES | cut -f2 -d" ) # e.g., "/dev/dsk/c0t6d0s1" setvar MEDIATYPE = $(echo $MAP | cut -f3 -d: | cut -f2 -d" ) # e.g., "cdrom" or "floppy" if test $MEDIATYPE = "sr" { setvar VOLUME_MEDIATYPE = ""cdrom"" } elif test $MEDIATYPE = "fd" { setvar VOLUME_MEDIATYPE = ""floppy"" } elif test $MEDIATYPE = "rmdisk" { setvar VOLUME_MEDIATYPE = ""rmdisk"" } setvar VOLUME_PATH = "$DEVFILE" # e.g., "/dev/dsk/c0t6d0s0" if test $MACH = "i386" && test $MEDIATYPE = "rmdisk" { setvar VOLUME_PATH = $(echo $DEVFILE | sed -e 's/s0/p0/) } setvar SYMDEV = $(echo $DEVICE | sed -e 's/_//) # e.g., "cdrom" or "floppy" setvar SYMNUM = $(echo $SYMDEV | sed -e 's/[a-z]*//g) setvar SYMDEV = $(echo $SYMDEV | sed -e 's/[0-9]*//g) if test $SYMDEV = "sr" { setvar VOLUME_SYMDEV = ""cdrom"$SYMNUM" } elif test $SYMDEV = "fd" { setvar VOLUME_SYMDEV = ""floppy"$SYMNUM" } elif test $SYMDEV = "rmdisk" { setvar VOLUME_SYMDEV = ""rmdisk"$SYMNUM" } else { setvar VOLUME_SYMDEV = "$SYMDEV$SYMNUM" } setvar VOLUME_ZONE_NAME = "$4" setvar VOLUME_ZONE_PATH = "$5" if test $MODE = "allocate" { if test -n $3 { # e.g., "joeuser" setvar VOLUME_USER = "$3" } else { setvar VOLUME_USER = $(/usr/xpg4/bin/id -u -nr) } } else { # If there's a directory for the device under /, get the # user name from there, to use in cleaning up that directory. Otherwise, # the user name isn't actually used in deallocation. if test -d ${VOLUME_ZONE_PATH}/${VOLUME_MEDIATYPE}/*-${DEVICE} { setvar VOLUME_USER = $(ls -ld ${VOLUME_ZONE_PATH}/${VOLUME_MEDIATYPE}/*-${DEVICE} | awk '/^d/{print $3}) } else { if test -n $3 { setvar VOLUME_USER = "$3" } else { setvar VOLUME_USER = $(/usr/xpg4/bin/id -u -nr) } } } setvar VOLUME_NAME = "unnamed_${VOLUME_MEDIATYPE}" # e.g., "joeuser-cdrom0/unnamed_cdrom" if test $VOLUME_MEDIATYPE = "rmdisk" { setvar VOLUME_PCFS_ID = '1' } else { setvar VOLUME_PCFS_ID = '' } export VOLUME_ACTION VOLUME_DEVICE VOLUME_MEDIATYPE VOLUME_NAME VOLUME_PCFS_ID export VOLUME_PATH VOLUME_SYMDEV VOLUME_USER VOLUME_ZONE_NAME VOLUME_ZONE_PATH setvar USERDIR = "${VOLUME_USER}-${DEVICE}" # e.g., "joeusr-cdrom0" msg_init if test $MODE = "allocate" { setvar MSGDEV = 'tty' do_allocate } else { if test $FLAG = "i" { setvar MSGDEV = 'console' do_init } else { setvar MSGDEV = 'tty' do_deallocate } } exit $EXIT_STATUS