Whamcloud - gitweb
Revert "LU-8384 scripts: Add scripts to systemd for EL7"
[fs/lustre-release.git] / lustre / scripts / lustre
1 #!/bin/bash
2 #
3 # lustre        This shell script takes care of starting and stopping
4 #              the lustre services.
5 #
6 # chkconfig: - 60 20
7 # description:  Part of the lustre file system.
8 # probe: true
9 # config: /etc/sysconfig/lustre
10
11 PATH=/sbin:/usr/sbin:/bin:/usr/bin
12
13 # Source function library.
14 . /etc/rc.d/init.d/functions
15
16 # Source networking configuration.
17 if [ ! -f /etc/sysconfig/network ]; then
18         exit 0
19 fi
20
21 . /etc/sysconfig/network
22
23 LDEV=${LDEV:-"/usr/sbin/ldev"}
24 ZPOOL_LAYOUT=/usr/bin/zpool_layout
25 UDEVADM=${UDEVADM:-/sbin/udevadm}
26
27 # Check that networking is up.
28 [ "${NETWORKING}" = "no" ] && exit 0
29
30 # Check for and source configuration file otherwise set defaults
31 [ -f /etc/sysconfig/lustre ] && . /etc/sysconfig/lustre
32 FSCK_ARGS=${FSCK_ARGS:-""}
33 MOUNT_OPTIONS=${MOUNT_OPTIONS:-""}
34 LOCAL_SRV=${LOCAL_SRV:-"`$LDEV -l 2>/dev/null`"}
35 FOREIGN_SRV=${FOREIGN_SRV:-"`$LDEV -f 2>/dev/null`"}
36 REQUIRE_MMP_FEATURE=${REQUIRE_MMP_FEATURE:-${FOREIGN_SRV:+"yes"}}
37 LOCAL_MOUNT_DIR=${LOCAL_MOUNT_DIR:-"/mnt/lustre/local"}
38 FOREIGN_MOUNT_DIR=${FOREIGN_MOUNT_DIR:-"/mnt/lustre/foreign"}
39 SETUP_DEVICES=${SETUP_DEVICES:-""}
40 ZPOOL_LAYOUT_BUSES=${ZPOOL_LAYOUT_BUSES:-""}
41 ZPOOL_LAYOUT_PORTS=${ZPOOL_LAYOUT_PORTS:-""}
42 ZPOOL_LAYOUT_MAP=${ZPOOL_LAYOUT_MAP:-""}
43 MOUNT_DELAY=${MOUNT_DELAY:-2}
44 LOAD_ZFS=${LOAD_ZFS:-""}
45
46 if [ -z "$TUNE2FS" ] ; then
47         TUNE2FS=`which tunefs.ldiskfs 2>/dev/null`
48         if [ -z "$TUNE2FS" ] ; then
49                 TUNE2FS=`which tune2fs 2>/dev/null`
50         fi
51 fi
52
53 if [ -z "$PFSCK" ] ; then
54         PFSCK=`which pfsck.ldiskfs 2>/dev/null`
55         if [ -z "$PFSCK" ] ; then
56                 PFSCK=`which fsck 2>/dev/null`
57         fi
58 fi
59
60 shopt -s nullglob
61
62 start_zfs_services ()
63 {
64         if [ -n "$ZPOOL_LAYOUT_BUSES" -a -n "$ZPOOL_LAYOUT_PORTS" ] ; then
65                 MAP_ARG=${ZPOOL_LAYOUT_MAP:+"-m $ZPOOL_LAYOUT_MAP"}
66                 $ZPOOL_LAYOUT -t -b "$ZPOOL_LAYOUT_BUSES" \
67                         -p "$ZPOOL_LAYOUT_PORTS" $MAP_ARG
68         fi
69         if [ "$LOAD_ZFS" = "yes" ] && ! modprobe zfs ; then
70                 echo "Failed to load zfs module.  Aborting."
71                 exit 1
72         fi
73 }
74
75 stop_devices ()
76 {
77         local labels=$*
78         local label devtype
79         for label in $labels; do
80                 devtype=`$LDEV -t $label`
81                 if [ "$devtype" = "zfs" ] ; then
82                         export_zpool $label
83                 elif [ "$devtype" = "md" ] ; then
84                         dev=`label_to_device $label`
85                         journal=`$LDEV -j $label`
86                         stop_md_device $dev
87                         stop_md_device $journal
88                 fi
89         done
90 }
91
92 import_zpool ()
93 {
94         local result=1
95         local label=$1
96         local pool=`$LDEV -z $label`
97         local args="-N $ZPOOL_IMPORT_ARGS"
98         local cache=`$LDEV -r $label`
99         # -c is incompatible with -d
100         if [ -n "$cache" ] ; then
101                 args="$args -c $cache"
102         elif [ -n "$ZPOOL_IMPORT_DIR" ] ; then
103                 args="$args -d $ZPOOL_IMPORT_DIR"
104         fi
105
106         if zpool status $pool >/dev/null 2>&1 ; then
107                 result=0
108         elif [ -n "$pool" ] ; then
109                 zpool import $pool $args 2>/dev/null
110                 result=$?
111         fi
112         if [ $result -ne 0 ] ; then
113                 echo "Unexpected return code from import of pool $pool: $result"
114         fi
115         return $result
116 }
117
118 export_zpool ()
119 {
120         local label=$1
121         local pool=`$LDEV -z $label`
122         zpool export $pool 2>/dev/null
123 }
124
125 # Trigger udev and wait for it to settle.
126 udev_trigger()
127 {
128         if [ -x ${UDEVADM} ]; then
129                 ${UDEVADM} trigger --action=change --subsystem-match=block
130                 ${UDEVADM} settle
131         else
132                 /sbin/udevtrigger
133                 /sbin/udevsettle
134         fi
135 }
136
137 # Usage: run_preexec_check [ start | restart | condrestart ]
138 # The single parameter will be passed to the PREEXEC_SCRIPT
139 run_preexec_check ()
140 {
141         if [ -n "$PREEXEC_CHECK" ] && ! $PREEXEC_CHECK ; then
142                 echo "Pre-exec check \"$PREEXEC_CHECK\" failed.  Aborting."
143                 exit 1
144         fi
145
146         if [ -n "$PREEXEC_SCRIPT" ] && ! "$PREEXEC_SCRIPT" "$1" ; then
147                 echo "Pre-exec script \"$PREEXEC_SCRIPT\" failed.  Aborting."
148                 exit 1
149         fi
150 }
151
152 # Usage: run_postexec_check [ start | restart | condrestart ]
153 # The single parameter will be passed to the PREEXEC_SCRIPT
154 run_postexec_check ()
155 {
156         if [ -n "$POSTEXEC_CHECK" ] && ! $POSTEXEC_CHECK ; then
157                 echo "Post-exec check \"$POSTEXEC_CHECK\" failed.  Aborting."
158                 exit 1
159         fi
160
161         if [ -n "$POSTEXEC_SCRIPT" ] && ! "$POSTEXEC_SCRIPT" "$1" ; then
162                 echo "Post-exec script \"$POSTEXEC_SCRIPT\" failed.  Aborting."
163                 exit 1
164         fi
165 }
166
167 # Usage: adjust_scsi_timeout <dev>
168 adjust_scsi_timeout ()
169 {
170         local dev=$1
171
172         if [ -n "$SCSI_DEVICE_TIMEOUT" ]; then
173                 # make sure that it is actually a SCSI (sd) device
174                 local name=`basename $dev`
175                 local proc=/sys/block/${name}/device/timeout
176                 local driver=`readlink /sys/block/${name}/device/driver`
177                 if [ -n "$driver" ] && [ "`basename $driver`" == "sd" ]; then
178                         if ! echo $SCSI_DEVICE_TIMEOUT >$proc; then
179                                 echo "FAILED: could not adjust ${dev} timeout"
180                                 return 1
181                         fi
182                 fi
183         fi
184         return 0
185 }
186
187 # Usage: fsck_test <dev> [ <dev> ... ]
188 # Checks all devices in parallel if FSCK_ARGS is set.
189 fsck_test ()
190 {
191         local devices="$*"
192
193         # Filter out non-absolute paths, which are probably ZFS datasets
194         devices=`echo $devices |xargs -n 1|grep '^/'|xargs`
195
196         if [ -n "${FSCK_ARGS}" -a -n "$devices" ]; then
197                 if [ -x $PFSCK ] ; then
198                         echo "$PFSCK $devices -- ${FSCK_ARGS}"
199                         $PFSCK $devices -- ${FSCK_ARGS}
200                         if [ $? -ne 0 -a $? -ne 1 ] ; then
201                                 echo "FAILED: $PFSCK -- ${FSCK_ARGS}: $?"
202                                 return 1
203                         fi
204                 else
205                         echo "$PFSCK not found"
206                         return 1
207                 fi
208         fi
209         return 0
210 }
211
212 # Usage: test_feature_flag <dev> <flag>
213 test_feature_flag()
214 {
215         local dev=$1
216         local flag=$2
217         local result=1
218         local feature
219
220         for feature in `$TUNE2FS -l $dev 2>/dev/null \
221                                 | grep features: | sed -e 's/^.*: //'`; do
222                 if [ "$feature" == "$flag" ]; then
223                         result=0
224                         break
225                 fi
226         done
227
228         return $result
229 }
230
231 # Usage: mmp_test <dev>
232 # Returns 0 if it is set or not required, 1 if unset and required or error.
233 mmp_test ()
234 {
235         local dev=$1
236         local result=0
237
238         if [ "$REQUIRE_MMP_FEATURE" == "yes" ]; then
239                 if [ -x $TUNE2FS ]; then
240                         if ! test_feature_flag $dev "mmp"; then
241                                 echo "mmp feature flag is not set on $dev"
242                                 result=1
243                         fi
244                 else
245                         echo "$TUNE2FS not found"
246                         result=1
247                 fi
248         fi
249
250         return $result
251 }
252
253 # Usage: label_to_mountpt <label>
254 # Prints mount point path, if label matches a local or foreign server.
255 label_to_mountpt ()
256 {
257         local label=$1
258         local serv
259
260         for serv in $LOCAL_SRV; do
261                 if [ "$serv" == "$label" ]; then
262                         echo "$LOCAL_MOUNT_DIR/$label"
263                         return
264                 fi
265         done
266         for serv in $FOREIGN_SRV; do
267                 if [ "$serv" == "$label" ]; then
268                         echo "$FOREIGN_MOUNT_DIR/$label"
269                         return
270                 fi
271         done
272 }
273
274 # Usage: label_to_device <label>
275 # Prints canonical device path.
276 label_to_device ()
277 {
278         local label=$1
279         local path=/dev/disk/by-label/$label
280
281         if [ -h $path ] ; then
282                 readlink --canonicalize $path
283         else
284                 $LDEV -d $label
285         fi
286 }
287
288 # helper for mountpt_is_active() and device_is_active()
289 declare -r awkprog='BEGIN {rc = 1;}
290                         { if ($field == path) {rc = 0;} }
291                     END { exit rc;}'
292
293 # Usage: mountpt_is_active <label>
294 # Return 1 (inactive) on invalid label.
295 mountpt_is_active ()
296 {
297         local dir=`label_to_mountpt $1`
298         local result=1
299
300         if [ -n "$dir" ]; then
301                 cat /proc/mounts | awk "$awkprog" field=2 path=$dir
302                 result=$?
303         fi
304         return $result
305 }
306
307 # Usage: device_is_active <label>
308 # Return 1 (inactive) on invalid label.
309 device_is_active ()
310 {
311         local dev=`label_to_device $1`
312         local result=1
313
314         if [ -n "$dev" ]; then
315                 cat /proc/mounts | awk "$awkprog" field=1 path=$dir
316                 result=$?
317         fi
318         return $result
319 }
320
321 # Usage: mount_one_device <label> <successflag> [devtype]
322 # Remove <successflag> on error (trick to detect errors after parallel runs).
323 mount_one_device ()
324 {
325         local label=$1
326         local successflag=$2
327         local devtype=$3
328         local dev=`label_to_device $label`
329         local dir=`label_to_mountpt $label`
330
331         # $dir and $dev have already been checked at ths point
332         if [ ! -d $dir ] && ! mkdir -p $dir; then
333                 rm -f $successflag
334                 return
335         fi
336         echo "Mounting $dev on $dir"
337         if ! mount -t lustre $MOUNT_OPTIONS $dev $dir; then
338                 rm -f $successflag
339                 return
340         fi
341 }
342
343 # Usage: assemble_md_device <device>
344 # Assemble the md device backing device.
345 # Return 0 if the array is assembled successfully or was already active,
346 # otherwise return error code from mdadm.
347 assemble_md_device ()
348 {
349         local dev=$1
350         local raidtab=$2
351         local args="-Aq"
352         local result=0
353
354         if [ -n "$raidtab" ] ; then
355                 args="$args -c $raidtab"
356         fi
357
358         if ! md_array_is_active $dev ; then
359                 mdadm $args $dev
360                 result=$?
361         fi
362
363         udev_trigger
364         return $result
365 }
366
367 # Usage: stop_md_device <device>
368 # Stop the md device backing device.
369 # Return 0 if the array is stopped successfully or was not active,
370 # otherwise return error code from mdadm.
371 stop_md_device ()
372 {
373         local dev=$1
374         local raidtab=$2
375         local args="-Sq"
376         local result=0
377
378         if [ -n "$raidtab" ] ; then
379                 args="$args -c $raidtab"
380         fi
381
382         if [ -e $dev ] && md_array_is_active $dev ; then
383                 mdadm $args $dev
384                 result=$?
385         fi
386
387         return $result
388 }
389
390 # Usage: md_array_is_active <device>
391 # return 0 if device is an active md RAID array, or 1 otherwise
392 md_array_is_active ()
393 {
394         local device=$1
395
396         [ -e "$device" ] || return 1
397
398         mdadm --detail -t $device > /dev/null 2>&1
399         if [ $? -eq 4 ] ; then
400                 return 1
401         fi
402         return 0
403 }
404
405 # Usage: start_services <label> [ <label> ... ]
406 # fsck and mount any devices listed as arguments (in parallel).
407 # Attempt to assemble software raid arrays or zfs pools backing
408 # Lustre devices.
409 start_services ()
410 {
411         local result=0
412         local devices=""
413         local dir dev label
414         local successflag
415         local labels
416
417         start_zfs_services
418         for label in $*; do
419                 dir=`label_to_mountpt $label`
420                 devtype=`$LDEV -t $label`
421                 dev=`label_to_device $label`
422                 journal=`$LDEV -j $label`
423                 raidtab=`$LDEV -r $label`
424
425                 if [ -z "$dir" ] || [ -z "$dev" ]; then
426                         echo "$label is not a valid lustre label on this node"
427                         result=2
428                         continue
429                 fi
430
431                 if [ "$devtype" = "md" ] ; then
432                         if ! assemble_md_device $dev $raidtab ; then
433                                 echo "failed to assemble array $dev backing $label"
434                                 result=2
435                                 continue
436                         fi
437                 elif [ "$devtype" = "zfs" ] ; then
438                         if ! import_zpool $label ; then
439                                 result=2
440                         fi
441                 fi
442
443                 # Journal device field in ldev.conf may be "-" or empty,
444                 # so only attempt to assemble if its an absolute path.
445                 # Ignore errors since the journal device may not be an
446                 # md device.
447                 if echo $journal | grep -q ^/ ; then
448                         assemble_md_device $journal $raidtab 2>/dev/null
449                 fi
450
451                 if mountpt_is_active $label || \
452                    device_is_active $label; then
453                         echo "$label is already mounted"
454                         # no error
455                         continue
456                 fi
457
458                 if [ "x$devtype" != "xzfs" ] ; then
459                         if ! mmp_test $dev; then
460                                 result=2
461                                 continue
462                         fi
463                         if ! adjust_scsi_timeout $dev; then
464                                 result=2
465                                 continue
466                         fi
467                 fi
468                 devices="$devices $dev"
469                 labels="$labels $label"
470         done
471         if [ $result == 0 ]; then
472                 fsck_test $devices || return 2
473
474                 # Fork to handle multiple mount_one_device()'s in parallel.
475                 # Errors occurred if $successflag comes up missing afterwards.
476                 successflag=`mktemp`
477                 [ -e $successflag ] || return 2
478                 for label in $labels; do
479                         mount_one_device $label $successflag `$LDEV -t $label` &
480                         # stagger to avoid module loading races
481                         if [[ -n $MOUNT_DELAY && $MOUNT_DELAY -gt 0 ]] ; then
482                                 sleep $MOUNT_DELAY
483                         fi
484                 done
485                 for label in $labels; do
486                         wait
487                 done
488                 [ -e $successflag ] || return 2
489                 rm -f $successflag
490         fi
491
492         return $result
493 }
494
495 # Usage: stop_services <label> [ <label> ... ]
496 # Unmount any devices listed as arguments (serially).
497 # Any devices which are not mounted or don't exist are skipped with no error.
498 stop_services ()
499 {
500         local labels=$*
501         local result=0
502         local pids=""
503         local dir dev label
504
505         for label in $labels; do
506                 dir=`label_to_mountpt $label`
507                 if [ -z "$dir" ]; then
508                         echo "$label is not a valid lustre label on this node"
509                         result=2
510                         continue
511                 fi
512                 if ! mountpt_is_active $label; then
513                         #echo "$label is not mounted"
514                         # no error
515                         continue
516                 fi
517
518                 echo "Unmounting $dir"
519                 umount $dir &
520
521                 if [ -z "$pids" ]; then
522                         pids="$!"
523                 else
524                         pids="$pids $!"
525                 fi
526         done
527
528         # wait for all umount processes to complete, report any errors
529         for pid in $pids; do
530                 wait $pid || result=2
531         done
532
533         # double check!
534         for label in $labels; do
535                 if mountpt_is_active $label; then
536                         dir=`label_to_mountpt $label`
537                         echo "Mount point $dir is still active"
538                         result=2
539                 fi
540                 if device_is_active $label; then
541                         dev=`label_to_device $label`
542                         echo "Device $dev is still active"
543                         result=2
544                 fi
545         done
546         stop_devices $labels
547
548         return $result
549 }
550
551 # Usage: start_lustre_services [local|foreign|all|<label>]
552 # If no parameter is specified, local devices will be started.
553 start_lustre_services ()
554 {
555         local labels=""
556
557         case "$1" in
558                 ""|local)
559                         labels=$LOCAL_SRV
560                         ;;
561                 foreign)
562                         labels=$FOREIGN_SRV
563                         ;;
564                 all)    labels="$LOCAL_SRV $FOREIGN_SRV"
565                         ;;
566                 *)      labels="$1"
567                         ;;
568         esac
569         # for use by heartbeat V1 resource agent:
570         # starting an already-started service must not be an error
571         start_services $labels || exit 2
572 }
573
574 # Usage: stop_lustre_services [local|foreign|all|<label>]
575 # If no parameter is specified all devices will be stopped.
576 stop_lustre_services ()
577 {
578         local labels=""
579
580         case "$1" in
581                 local) labels=$LOCAL_SRV
582                         ;;
583                 foreign)
584                         labels=$FOREIGN_SRV
585                         ;;
586                 ""|all) labels="$LOCAL_SRV $FOREIGN_SRV"
587                         ;;
588                 *)      labels="$1"
589                         ;;
590         esac
591         # for use by heartbeat V1 resource agent:
592         # stopping already-stopped service must not be an error
593         stop_services $labels || exit 2
594 }
595
596 # General lustre health check - not device specific.
597 health_check ()
598 {
599
600         old_nullglob="`shopt -p nullglob`"
601         shopt -u nullglob
602
603         STATE="stopped"
604         # LSB compliance - return 3 if service is not running
605         # Lustre-specific returns
606         # 150 - partial startup
607         # 151 - health_check unhealthy
608         # 152 - LBUG
609         RETVAL=3
610         egrep -q "libcfs|lvfs|portals" /proc/modules && STATE="loaded"
611
612         # check for any configured devices (may indicate partial startup)
613         VAR=$(lctl get_param version 2>&1)
614         if [ $? = 0 ] ; then
615                 VAR=$(lctl get_param -n devices 2>&1)
616                 if [ $? = 0 ] ; then
617                         STATE="partial"
618                         RETVAL=150
619                 fi
620
621                 # check for either a server or a client filesystem
622                 local MGT=""
623                 local MDT=""
624                 local OST=""
625                 local LLITE=""
626
627                 ! lctl get_param -n mgs.MGS.* >/dev/null 2>&1 || MGT="YES"
628
629                 VAR=$(lctl get_param -n mdt.*.recovery_status 2>&1 | grep '^status:'  )
630                 if [ $? = 0 ] ; then
631                         MDT=$VAR
632                 fi
633
634                 VAR=$(lctl get_param -n obdfilter.*.recovery_status 2>&1 | grep '^status:')
635                 if [ $? = 0 ] ; then
636                         OST=$VAR
637                 fi
638
639                 VAR=$(lctl get_param -n llite.fs* 2>&1)
640                 if [ $? = 0 ] ; then
641                         LLITE="YES"
642                 fi
643
644                 if [ "$MGT" -o "$MDT" -o "$OST" -o "$LLITE" ]; then
645                         STATE="running"
646                         RETVAL=0
647                 fi
648         else
649                 # check if this is a router
650                 if [[ "$(lctl get_param -n routes)" =~ "Routing enabled" ]]; then
651                         STATE="running"
652                         RETVAL=0
653                 fi
654         fi
655
656         # check for server disconnections
657         VAR=$(lctl get_param -n *c.*.*server_uuid 2>&1)
658         if [ $? = 0 ] ; then
659                 DISCON="$(echo $VAR | grep -v FULL)"
660                 if [ -n "$DISCON" ] ; then
661                         STATE="disconnected"
662                         RETVAL=0
663                 fi
664         fi
665
666         # check for servers in recovery
667         if [ -n "$MDT$OST" ] && echo $MDT $OST | grep -q RECOV ; then
668                 STATE="recovery"
669                 RETVAL=0
670         fi
671
672         # check for error in health_check
673         local health_check=$(lctl get_param -n health_check)
674         if [[ "$health_check" =~ "NOT HEALTHY" ]]; then
675                 STATE="unhealthy"
676                 RETVAL=1
677         fi
678
679         # check for LBUG
680         if [[ "$health_check" =~ "LBUG" ]]; then
681                 STATE="LBUG"
682                 RETVAL=152
683         fi
684
685         echo $STATE
686         eval $old_nullglob
687         return $RETVAL
688 }
689
690 # Usage: status [local|foreign|all|<label>]
691 # If no parameter is specified, general lustre health status will be reported.
692 status ()
693 {
694         local labels=""
695         local label dir
696         local valid_devs=0
697
698         case "$1" in
699                 local) labels=$LOCAL_SRV;
700                         ;;
701                 foreign)
702                         labels=$FOREIGN_SRV;
703                         ;;
704                 all)    labels="$LOCAL_SRV $FOREIGN_SRV"
705                         ;;
706                 "")     # ASSUMPTION: this is not the heartbeat res agent
707                         health_check
708                         exit $?
709                         ;;
710                 *)      labels=$1
711                         ;;
712         esac
713         # for use by heartbeat V1 resource agent:
714         # print "running" if *anything* is running.
715         for label in $labels; do
716                 dir=`label_to_device $label`
717                 if [ -z "$dir" ]; then
718                         echo "$label is not a valid lustre label on this node"
719                         # no error
720                         continue
721                 fi
722                 valid_devs=1
723                 if mountpt_is_active $label || device_is_active $label; then
724                         echo "running"
725                         exit 0
726                 fi
727         done
728         [ $valid_devs == 1 ] && echo "stopped"
729         exit 3
730 }
731
732 usage ()
733 {
734         cat <<EOF
735 Usage: lustre {start|stop|status|restart|reload|condrestart}
736
737        lustre start  [local|foreign|<label>]
738        lustre stop   [local|foreign|<label>]
739        lustre status [local|foreign|<label>]
740 EOF
741         exit 1
742 }
743
744 # See how we were called.
745 case "$1" in
746   start)
747         if [ $# -gt 2 ] ; then
748                 echo "ERROR: Too many arguments."
749                 usage
750         fi
751         run_preexec_check "start"
752         start_lustre_services $2
753         run_postexec_check "start"
754         ;;
755   stop)
756         if [ $# -gt 2 ] ; then
757                 echo "ERROR: Too many arguments."
758                 usage
759         fi
760         run_preexec_check "stop"
761         stop_lustre_services $2
762         run_postexec_check "stop"
763         ;;
764   status)
765         if [ $# -gt 2 ] ; then
766                 echo "ERROR: Too many arguments."
767                 usage
768         fi
769         status $2
770         ;;
771   restart)
772         $0 stop
773         $0 start
774         ;;
775   reload)
776         ;;
777   probe)
778         ;;
779   condrestart)
780         if grep lustre /proc/mounts ; then
781                 $0 stop
782                 $0 start
783         fi
784         ;;
785   *)
786         usage
787 esac
788
789 exit 0