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#
73msg_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#
98msg() {
99    if [ "$WINDOWING" = "yes" ]; then
100	$WDWMSG "$*" "$TITLE" OK
101    elif [ "$silent" != "y" ]; then
102	echo "$*" > /dev/${MSGDEV}
103    fi
104}
105
106ok_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
115error_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#
127okcancel() {
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#
138yesno() {
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#
149error_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#
161get_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#
183find_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
225findmounts() {
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	}
256ENDOFAWKPGM
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#
264do_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
336do_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#
398do_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
429trap "" INT TERM QUIT TSTP ABRT
430
431PATH="/usr/bin:/usr/sbin"
432MODE="allocate"
433SILENT=n
434WDWMSG="/etc/security/lib/wdwmsg"
435VOLUME_ZONE_PATH="/"
436USAGE="Usage: disk_clean [-s|-f|-i|-I] devicename -[A|D] [username] [zonename] [zonepath]"
437EXIT_STATUS=0
438CLEAN_MOUNT=4
439MACH=`uname -p`
440FLAG=i
441#
442# Parse the command line arguments
443#
444while getopts ifsI c
445do
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
460done
461
462shift `expr $OPTIND - 1`
463
464DEVICE=$1
465MODE="deallocate"
466if [ "$2" = "-A" ]; then
467	MODE="allocate"
468elif [ "$2" = "-D" ]; then
469	MODE="deallocate"
470fi
471
472#get the device_maps information
473MAP=`/usr/sbin/list_devices -s -l $DEVICE`
474FILES=`echo $MAP | cut -f4 -d:`	# e.g., /dev/dsk/c0t6d0s0 /dev/dsk/c0t6d0s1 ...
475DEVFILE=`echo $FILES | cut -f1 -d" "` 		# e.g., "/dev/dsk/c0t6d0s0"
476
477# Set VOLUME_ variables that are inputs to rmmount
478
479VOLUME_DEVICE=`echo $FILES | cut -f2 -d" "` 	# e.g., "/dev/dsk/c0t6d0s1"
480MEDIATYPE=`echo $MAP | cut -f3 -d: | cut -f2 -d" "`
481					 	# e.g., "cdrom" or "floppy"
482if [ "$MEDIATYPE" = "sr" ]; then
483	VOLUME_MEDIATYPE="cdrom"
484elif [ "$MEDIATYPE" = "fd" ]; then
485	VOLUME_MEDIATYPE="floppy"
486elif [ "$MEDIATYPE" = "rmdisk" ]; then
487	VOLUME_MEDIATYPE="rmdisk"
488fi
489
490VOLUME_PATH=$DEVFILE				# e.g., "/dev/dsk/c0t6d0s0"
491if [ "$MACH" = "i386" ] && [ "$MEDIATYPE" = "rmdisk" ]; then
492	VOLUME_PATH=`echo $DEVFILE | sed -e 's/s0/p0/'`
493fi
494
495SYMDEV=`echo $DEVICE | sed -e 's/_//'`		# e.g., "cdrom" or "floppy"
496SYMNUM=`echo $SYMDEV | sed -e 's/[a-z]*//g'`
497SYMDEV=`echo $SYMDEV | sed -e 's/[0-9]*//g'`
498if [ "$SYMDEV" = "sr" ]; then
499	VOLUME_SYMDEV="cdrom"$SYMNUM
500elif [ "$SYMDEV" = "fd" ]; then
501	VOLUME_SYMDEV="floppy"$SYMNUM
502elif [ "$SYMDEV" = "rmdisk" ]; then
503	VOLUME_SYMDEV="rmdisk"$SYMNUM
504else
505	VOLUME_SYMDEV=$SYMDEV$SYMNUM
506fi
507
508VOLUME_ZONE_NAME=$4
509
510VOLUME_ZONE_PATH=$5
511
512if [ "$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
518else
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
531fi    
532
533VOLUME_NAME=unnamed_${VOLUME_MEDIATYPE}
534					# e.g., "joeuser-cdrom0/unnamed_cdrom"
535
536if [ "$VOLUME_MEDIATYPE" = "rmdisk" ]; then
537	VOLUME_PCFS_ID=1
538else
539	VOLUME_PCFS_ID=
540fi
541
542export VOLUME_ACTION VOLUME_DEVICE VOLUME_MEDIATYPE VOLUME_NAME VOLUME_PCFS_ID
543export VOLUME_PATH VOLUME_SYMDEV VOLUME_USER VOLUME_ZONE_NAME VOLUME_ZONE_PATH
544
545USERDIR=${VOLUME_USER}-${DEVICE}	# e.g., "joeusr-cdrom0"
546
547msg_init
548
549if [ "$MODE" = "allocate" ]; then
550	MSGDEV=tty
551  	do_allocate
552else
553    if [ "$FLAG" = "i" ] ; then
554	MSGDEV=console
555	do_init
556    else
557	MSGDEV=tty
558	do_deallocate
559    fi
560fi
561
562exit $EXIT_STATUS
563