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