diff options
-rw-r--r-- | usr/src/Makefile.lint | 1 | ||||
-rw-r--r-- | usr/src/cmd/stmsboot/Makefile | 7 | ||||
-rw-r--r-- | usr/src/cmd/stmsboot/mpxio-upgrade | 183 | ||||
-rw-r--r-- | usr/src/cmd/stmsboot/stmsboot.sh | 207 | ||||
-rw-r--r-- | usr/src/cmd/stmsboot/stmsboot_util.c | 2806 |
5 files changed, 1361 insertions, 1843 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index f799555dd1..38d919e271 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -254,6 +254,7 @@ COMMON_SUBDIRS = \ cmd/split \ cmd/ssh \ cmd/stat \ + cmd/stmsboot \ cmd/streams/strcmd \ cmd/strings \ cmd/su \ diff --git a/usr/src/cmd/stmsboot/Makefile b/usr/src/cmd/stmsboot/Makefile index c3df2f1374..38dcc809fc 100644 --- a/usr/src/cmd/stmsboot/Makefile +++ b/usr/src/cmd/stmsboot/Makefile @@ -19,9 +19,7 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -41,8 +39,7 @@ CLOBBERFILES = $(SHFILES) $(STMSBOOT_UTIL) $(POFILE) $(OBJS) ROOTFSLIB_MPXIO = $(ROOT)/lib/mpxio ROOTMANIFESTDIR = $(ROOTSVCSYSTEMDEVICE) - -LDLIBS += -ldevinfo -lgen -ldevid +LDLIBS += -ldevinfo -ldevid -lnvpair -lumem .KEEP_STATE: diff --git a/usr/src/cmd/stmsboot/mpxio-upgrade b/usr/src/cmd/stmsboot/mpxio-upgrade index 4c672c4770..ef14445d04 100644 --- a/usr/src/cmd/stmsboot/mpxio-upgrade +++ b/usr/src/cmd/stmsboot/mpxio-upgrade @@ -20,10 +20,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/fs_include.sh . /lib/svc/share/net_include.sh @@ -34,25 +33,23 @@ STMSBOOTUTIL=/lib/mpxio/stmsboot_util SAVEDIR=/etc/mpxio RECOVERFILE=$SAVEDIR/recover_instructions DEVFSADM=/usr/sbin/devfsadm +DUMPADM=/usr/sbin/dumpadm METADEVADM=/usr/sbin/metadevadm usrmounted=0 MACH=`/usr/bin/uname -p` +CP=/usr/bin/cp +DF=/usr/bin/df +LS=/usr/bin/ls +RM=/usr/bin/rm EGREP=/usr/bin/egrep SED=/usr/bin/sed - -# map $special and $fsckdev -mpxio_mapdev() -{ - new_special=`$STMSBOOTUTIL -m $special` - if [ $? -eq 0 ]; then - special=$new_special - fi - - new_fsckdev=`$STMSBOOTUTIL -m $fsckdev` - if [ $? -eq 0 ]; then - fsckdev=$new_fsckdev - fi -} +ZPOOL=/usr/sbin/zpool +AWK=/usr/bin/awk +MOUNT=/sbin/mount +UMOUNT=/sbin/mount +EEPROM=/usr/sbin/eeprom +BOOTADM=/usr/sbin/bootadm +SVCADM=/usr/sbin/svcadm mpxio_error() { @@ -72,14 +69,56 @@ mpxio_error() # mpxio_mount_root() { + HASZFSROOT=`$DF -g / |grep zfs` + + # In single-user maintenance mode, we don't have a writable + # root partition, so we _cannot_ use devlinks. Therefore we + # have to do some dancing - first mount the physical path + # read-write, then re-run $STMSBOOTUTIL to get the real + # devlink mapping, and then re-mount the root slice. Of course, + # if we all used ZFS this wouldn't be such a pain! exec < $vfstab; readvfstab / - mpxio_mapdev + # ZFS root environments should _not_ have an entry for / + # in their /etc/vfstab. + if [ "x$special" != "x" ]; then + # sanity check for ZFSRoot _and_ / in /etc/vfstab + if [ "x$HASZFSROOT" != "x" ]; then + # ERROR - this would cause a failure later + # so let root know about it now and provide + # a chance to handle it before filesystem/usr + cecho "stmsboot: System has ZFS Root *and* an entry for / in /etc/vfstab\nstmsboot: Please remove the / entry from /etc/vfstab and then run\n# svcadm clear mpxio-upgrade" + exit 1 + fi + ISPHYS=`echo $special |$AWK '/^\/dev\/dsk/ {print}'`; + if [ "x$ISPHYS" != "x" ]; then + new_special=`$STMSBOOTUTIL -m $special` + if [ "x$new_special" = "xNOT_MAPPED" ]; then + # this is a bad state to be in, exit + cecho "Error: Your root device is not mapped." + exit 1 + fi + checkopt "llock" $mntopts + mntopts='remount' + [ -n "$otherops" ] && mntopts="${mntopts},${otherops}" + $MOUNT -m -F $fstype -o $mntopts $new_special \ + $mountp >/dev/msglog 2>&1 - checkopt "llock" $mntopts - mntopts='remount' - [ -n "$otherops" ] && mntopts="${mntopts},${otherops}" - /sbin/mount -m -F $fstype -o $mntopts $special $mountp \ ->/dev/msglog 2>&1 + # now re-run $STMSBOOTUTIL to get the real mapping + new_special=`$STMSBOOTUTIL -m $special` + else + # a metadevice, either /dev/md or /dev/vx + new_special=$special + fi + # mount root for real + $MOUNT -o remount,rw $new_special / >/dev/msglog 2>&1 + else + if [ "x$HASZFSROOT" = "x" ]; then + cecho "stmsboot: Error: your root slice is invalid" + exit 1 + else + cecho "stmsboot: Root is on ZFS" + fi + fi } # @@ -90,7 +129,8 @@ mpxio_mount_usr() exec < $vfstab; readvfstab "/usr" ret_val=0 if [ -n "$mountp" ]; then - mpxio_mapdev + new_special=`$STMSBOOTUTIL -m $special` + if [ "$fstype" = cachefs ]; then # Mount read-only without the cache. case "$mntopts" in @@ -106,7 +146,7 @@ mpxio_mount_usr() ;; esac # see the comment below for /dev/null - /sbin/mount -m -F $cfsbacktype -o ro $special $mountp \ + $MOUNT -m -F $cfsbacktype -o ro $new_special $mountp \ >/dev/null 2>&1 ret_val=$? else @@ -128,24 +168,19 @@ mpxio_mount_usr() mntopts="ro,$mntopts" fi - # # Requesting logging on a read-only mount # causes errors to be displayed, so remove # "logging" from the list of options. - # - checkopt logging $mntopts if [ "x$option" = xlogging ]; then mntopts="$otherops" fi fi - # # In case of a manual restart of the service, mount # will emit messages if /usr is already mounted. # So redirect the output to /dev/null. - # - /sbin/mount -m -F $fstype -o $mntopts $special /usr \ + $MOUNT -m -F $fstype -o $mntopts $new_special /usr \ >/dev/null 2>&1 ret_val=$? fi @@ -160,29 +195,35 @@ mpxio_mount_usr() # update system dump configuration update_dumpconf() { - # # Disable device-in-use checking (done in libdiskmgt). # Without disabling this check, the configuration of dump device # would fail as the device-in-use code incorrectly concludes that # the device is in use and hence prevents configuration of the dump # device. - # NOINUSE_CHECK=1 export NOINUSE_CHECK - set -- `dumpadm -u 2>&1 | $EGREP 'cannot use /dev.* as dump device'` - if [ "x$4" != x ]; then - newname=`$STMSBOOTUTIL -M $4` - if [ $? -eq 0 ]; then - if /usr/sbin/dumpadm -d $newname > /dev/msglog 2> /dev/console; then - cecho "stmsboot: dump configuration has been \ -updated." - else - mpxio_error "failed to configure $newname as \ -the dump device.\nold dump device name: $4" - return 1 + DUMPISZFS=`$AWK -F"=" '/DUMPADM_DEVICE/ {print $2}' /etc/dumpadm.conf|$EGREP zvol` + if [ "x$DUMPISZFS" = "x" ]; then + set -- `$DUMPADM -u 2>&1 | $EGREP 'cannot use /dev.* as dump device'` + if [ "x$4" != x ]; then + newname=`$STMSBOOTUTIL -m $4` + if [ $? -eq 0 ]; then + if $DUMPADM -d $newname > /dev/msglog 2> /dev/console; then + cecho "stmsboot: dump configuration \ + has been updated." + else + mpxio_error "failed to configure \ + the dump device.\nold \ + dump device name: $4" + return 1 + fi fi fi + else + # make sure we can get to it, force zfs to load fully + $LS $DUMPISZFS >>/dev/null 2>&1 + cecho "stmsboot: dump on ZFS, no dumpadm update required" fi return 0 } @@ -190,31 +231,35 @@ the dump device.\nold dump device name: $4" # Update bootpath for x86 here when we are enabling mpxio on root update_bootpath() { - cur_bootpath=`/usr/sbin/eeprom bootpath | $SED 's/bootpath=[ ]*//g' | $SED 's/[ ].*//'` - # only take care of phci-based path here - PHCIOPT=`$STMSBOOTUTIL -D mpt -n` - echo $cur_bootpath | $EGREP -s "/fp@.*/disk|$PHCIOPT" - - if [ $? -eq 0 ]; then - new_bootpath=`$STMSBOOTUTIL -m /devices$cur_bootpath` - if [ $? -eq 0 -a "x$new_bootpath" != "x" ]; then - # stmsboot_util -m phci-based path got mapped path back means - # mpxio is enabled on bootpath - new_bootpath=`echo $new_bootpath|$SED "s/.*\/devices//"` - /usr/sbin/eeprom bootpath=$new_bootpath - cecho "stmsboot: bootpath has been updated" + cur_bootpath=`$STMSBOOTUTIL -b` + if [ $? != 0 ]; then + cecho "stmsboot: ERROR! Unable to retrieve bootpath property\n" + exit 1 + fi - /sbin/bootadm update-archive + NEWBOOTPATH="" + for path in $cur_bootpath; do + mapped=`$STMSBOOTUTIL -p $path` + if [ "$mapped" != "NOT_MAPPED" ]; then + if [ "x$mapped" != "x$path" ]; then + NEWBOOTPATH=`echo "$path " | \ + $SED -e"s|$path|$mapped|"`" $NEWBOOTPATH" + else + NEWBOOTPATH="$NEWBOOTPATH $path" + fi fi - fi + done + # now strip off leading and trailing space chars + new_bootpath=`echo $NEWBOOTPATH` + $EEPROM bootpath="$new_bootpath" + cecho "stmsboot: bootpath has been updated, now regenerating boot archive" + $BOOTADM update-archive + cecho "" } -# -# do the actual work -# +# Now do the actual work mpxio_main() { - # # NOTE: If the first attempt to run the service has failed due to an # expected error, users should be able to manually rerun the service. # @@ -240,7 +285,13 @@ mpxio_main() $DEVFSADM # update /etc/vfstab to reflect device name changes - if $STMSBOOTUTIL -u >/dev/msglog 2>&1; then + $STMSBOOTUTIL -u >/dev/msglog 2>&1 + if [ $? -eq 0 ]; then + $CP /etc/vfstab /etc/vfstab.old + $CP $SAVEDIR/vfstab.new /etc/vfstab + $RM $SAVEDIR/vfstab.new + cecho "" + cecho "stmsboot: vfstab has been updated" if update_dumpconf; then # update svm configuration to reflect new names if [ -s /kernel/drv/md.conf ] && \ @@ -256,13 +307,11 @@ mpxio_main() mpxio_error "failed to update /etc/vfstab." fi - /usr/sbin/svcadm disable system/device/mpxio-upgrade - - /usr/sbin/reboot + $SVCADM disable system/device/mpxio-upgrade else mpxio_error "failed to mount the root filesystem." if [ $usrmounted -eq 1 ]; then - /sbin/umount /usr + $UMOUNT /usr fi fi } diff --git a/usr/src/cmd/stmsboot/stmsboot.sh b/usr/src/cmd/stmsboot/stmsboot.sh index 31b06328c7..2fcf42c483 100644 --- a/usr/src/cmd/stmsboot/stmsboot.sh +++ b/usr/src/cmd/stmsboot/stmsboot.sh @@ -1,4 +1,4 @@ -#!/sbin/sh +#!/sbin/sh -p # # CDDL HEADER START # @@ -20,12 +20,10 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" # - PATH=/usr/bin:/usr/sbin:$PATH; export PATH STMSBOOTUTIL=/lib/mpxio/stmsboot_util STMSMETHODSCRIPT=/lib/svc/method/mpxio-upgrade @@ -43,31 +41,36 @@ SUPPORTED_DRIVERS="fp|mpt" USAGE=`gettext "Usage: stmsboot [-D $SUPPORTED_DRIVERS] -e | -d | -u | -L | -l controller_number"` TEXTDOMAIN=SUNW_OST_OSCMD export TEXTDOMAIN -STMSINSTANCE=system/device/mpxio-upgrade:default +STMSINSTANCE=svc:system/device/mpxio-upgrade:default STMSBOOT=/usr/sbin/stmsboot BOOTADM=/sbin/bootadm MOUNT=/usr/sbin/mount +EEPROM=/usr/sbin/eeprom EGREP=/usr/bin/egrep GREP=/usr/bin/grep AWK=/usr/bin/awk +CP=/usr/bin/cp +LS=/usr/bin/ls +MV=/usr/bin/mv +RM=/usr/bin/rm SORT=/usr/bin/sort UNIQ=/usr/bin/uniq EXPR=/usr/bin/expr +MKDIR=/usr/bin/mkdir +REBOOT=/usr/sbin/reboot SED=/usr/bin/sed SVCPROP=/usr/bin/svcprop SVCCFG=/usr/sbin/svccfg SVCS=/usr/bin/svcs SVCADM=/usr/sbin/svcadm +NOW=`/usr/bin/date +%G%m%d_%H%M` MACH=`/usr/bin/uname -p` -BOOTENV_FILE=/boot/solaris/bootenv.rc - -CLIENT_TYPE_VHCI="/scsi_vhci.*/ssd@|/scsi_vhci.*/disk@" -# The phci client type egrep string will change based on the -# drivers which we are operating on, and the cpu architecture -# and we call stmsboot_util -n -D $drv to get that string -CLIENT_TYPE_PHCI= +BOOTENV_FILE=bootenv.rc reboot_needed=0 +new_bootpath="" +CLIENT_TYPE_PHCI="" +CLIENT_TYPE_VHCI="/scsi_vhci" # # Copy all entries (including comments) from source driver.conf @@ -82,7 +85,9 @@ reboot_needed=0 # delete_mpxio_disable_entries() { - sed ' + # be careful here, we've got embedded \t characters + # in sed's pattern space. + $SED ' /^[ ]*#/{ p d } @@ -117,10 +122,8 @@ backup_lastsaved() { for file in $* do - file=`basename $file` - if [ -f $SAVEDIR/$file ]; then - mv $SAVEDIR/$file $SAVEDIR/${file}.old - fi + newfile=`basename $file` + $CP $file $SAVEDIR/$newfile.$cmd.$NOW done } @@ -142,11 +145,11 @@ build_recover() gettext "\tUndo the modifications you made to STMS configuration.\n\tFor example undo any changes you made to " >> $RECOVERFILE echo "/mnt$KDRVCONF." >> $RECOVERFILE else - echo "\tcp /mnt${SAVEDIR}/$DRVCONF /mnt$KDRVCONF" >> $RECOVERFILE + echo "\tcp /mnt${SAVEDIR}/$DRVCONF.$cmd.$NOW /mnt$KDRVCONF" >> $RECOVERFILE fi if [ $1 -eq 1 ]; then - echo "\tcp /mnt${SAVEDIR}/vfstab /mnt$VFSTAB" >> $RECOVERFILE + echo "\tcp /mnt${SAVEDIR}/vfstab.$cmd.$NOW /mnt$VFSTAB" >> $RECOVERFILE echo "repository /mnt/etc/svc/repository.db" > $SVCCFG_RECOVERY echo "select $STMSINSTANCE" >> $SVCCFG_RECOVERY @@ -156,7 +159,7 @@ build_recover() echo "\t$SVCCFG -f /mnt$SVCCFG_RECOVERY" >> $RECOVERFILE if [ "x$MACH" = "xi386" -a "x$new_bootpath" != "x" ]; then - echo "\tcp /mnt${SAVEDIR}/bootenv.rc /mnt$BOOTENV_FILE" >> $RECOVERFILE + echo "\tcp /mnt${SAVEDIR}/bootenv.rc.$cmd.$NOW /mnt/boot/solaris/$BOOTENV_FILE" >> $RECOVERFILE fi fi @@ -165,6 +168,7 @@ build_recover() gettext "was your root device,\nbut it could be named differently after you boot net.\n" >> $RECOVERFILE } + # # Arrange for /etc/vfstab and dump configuration to be updated # during the next reboot. If the cmd is "enable" or "disable", copy @@ -175,14 +179,15 @@ build_recover() update_sysfiles() { - gettext "WARNING: This operation will require a reboot.\nDo you want to continue ? [y/n] (default: y) " + gettext "WARNING: This operation will require a reboot.\n" + gettext "Do you want to continue ? [y/n] (default: y) " read response if [ "x$response" != x -a "x$response" != xy -a \ "x$response" != xY ]; then for d in $DRVLIST; do TMPDRVCONF=/var/run/tmp.$d.conf.$$ - rm -f $TMPDRVCONF > /dev/null 2>&1 + $RM -f $TMPDRVCONF > /dev/null 2>&1 done; return 0; fi @@ -198,10 +203,10 @@ update_sysfiles() KDRVCONF=/kernel/drv/$d.conf TMPDRVCONF=/var/run/tmp.$d.conf.$$ - cp $KDRVCONF $SAVEDIR + $CP $KDRVCONF $SAVEDIR/`basename $KDRVCONF`.$cmd.$NOW if [ -f $TMPDRVCONF ]; then - cp $TMPDRVCONF $KDRVCONF - rm -f $TMPDRVCONF + $CP $TMPDRVCONF $KDRVCONF + $RM -f $TMPDRVCONF else # if $TMPDRVCONF doesn't exist, then we # haven't made any changes to it @@ -222,17 +227,17 @@ update_sysfiles() # depends upon the pathname of the parent node in the # device tree, which can be different on x86/x64 and sparc. - CLIENT_TYPE_PHCI=`$STMSBOOTUTIL -D $d -n`; + CLIENT_TYPE_PHCI=`$STMSBOOTUTIL -D $d -N`; if [ "x$CLIENT_TYPE_PHCI" = "x" ]; then continue; fi if [ "x$cmd" = "xenable" ]; then - ls -l /dev/dsk/*s2 2> /dev/null | \ + $LS -l /dev/dsk/*s2 2> /dev/null | \ $EGREP -s "$CLIENT_TYPE_PHCI" else - ls -l /dev/dsk/*s2 2> /dev/null | \ + $LS -l /dev/dsk/*s2 2> /dev/null | \ $EGREP -s "$CLIENT_TYPE_VHCI" fi @@ -246,19 +251,12 @@ update_sysfiles() need_bootscript=1 if [ "x$MACH" = "xi386" -a "x$new_bootpath" != "x" ]; then #only update bootpath for x86. - cp $BOOTENV_FILE $SAVEDIR - /usr/sbin/eeprom bootpath=$new_bootpath + $CP /boot/solaris/$BOOTENV_FILE $SAVEDIR/$BOOTENV_FILE.$cmd.$NOW + $EEPROM bootpath="$new_bootpath" fi - # - # Enable the mpxio-upgrade service, but don't run it now. - # The service will run during the next reboot and will do - # the actual job of modifying the system files. - # + # Enable the mpxio-upgrade service for the reboot $SVCADM disable -t $STMSINSTANCE - $SVCCFG -f - << EOF -select $STMSINSTANCE -setprop general/enabled = true -EOF + $SVCCFG -s $STMSINSTANCE "setprop general/enabled=true" else need_bootscript=0 fi @@ -274,12 +272,13 @@ EOF if [ "x$response" = x -o "x$response" = xy -o \ "x$response" = xY ]; then - /usr/sbin/reboot + $REBOOT fi return 0 } + # # Enable or disable mpxio as specified by the cmd. # Returns 0 on success, 1 on failure. @@ -291,6 +290,8 @@ EOF # configure_mpxio() { + # be careful here, we've got embedded \t characters + # in sed's pattern space. mpxiodisableno='mpxio-disable[ ]*=[ ]*"no"[ ]*;' mpxiodisableyes='mpxio-disable[ ]*=[ ]*"yes"[ ]*;' @@ -317,7 +318,7 @@ configure_mpxio() if [ $? -ne 0 ]; then # if all mpxiodisable entries are no/yes for # enable/disable mpxio, notify the user - rm -f $TMPDRVCONF $TMPDRVCONF_MPXIO_ENTRY > /dev/null 2>&1 + $RM -f $TMPDRVCONF $TMPDRVCONF_MPXIO_ENTRY > /dev/null 2>&1 continue; else reboot_needed=`$EXPR $reboot_needed + 1` @@ -326,7 +327,7 @@ configure_mpxio() # If mpxiodisable entries do not exist, always continue update fi else - rm -f $TMPDRVCONF $TMPDRVCONF_MPXIO_ENTRY > /dev/null 2>&1 + $RM -f $TMPDRVCONF $TMPDRVCONF_MPXIO_ENTRY > /dev/null 2>&1 gettext "failed to update " 1>&2 echo "$KDRVCONF." 1>&2 gettext "No changes were made to your STMS configuration.\n" 1>&2 @@ -349,9 +350,10 @@ setcmd() } # -#Need to update bootpath on x86 if boot system from FC disk -#Only update bootpath here when mpxio is enabled -#If mpxio is disabled currently, will update bootpath in mpxio-upgrade +# Need to update bootpath on x86 if boot system from FC disk +# Only update bootpath here when mpxio is enabled +# If mpxio is currently disabled, then we'll update bootpath in the +# mpxio-upgrade service method on reboot. # get_newbootpath_for_stmsdev() { @@ -359,34 +361,37 @@ get_newbootpath_for_stmsdev() { return 0 fi - cur_bootpath=`/usr/sbin/eeprom bootpath | \ - $SED 's/bootpath=[ ]*//g' | $SED 's/[ ]*$//'` - if [ "x$cur_bootpath" = "x" ]; then - gettext "failed to get bootpath by eeprom\n" 1>&2 + cur_bootpath=`$STMSBOOTUTIL -b` + if [ $? != 0 ]; then return 1 fi - #only update bootpath for STMS path - echo $cur_bootpath|$EGREP $CLIENT_TYPE_VHCI > /dev/null 2>&1 - if [ $? -eq 1 ]; then - return 0 - fi + # Since on x64 platforms the eeprom command doesn't update the + # kernel, the file /boot/solaris/bootenv.rc and the kernel's + # bootpath variable have a good chance of differing. We do some + # extra handwaving to get the correct bootpath variable setting. - new_bootpath=`$STMSBOOTUTIL -p /devices$cur_bootpath` - if [ $? -ne 0 ]; then - new_bootpath="" - return 1 + ONDISKVER=`$AWK '/bootpath/ {print $3}' /boot/solaris/bootenv.rc|\ + $SED -e"s,',,g"` + if [ "x$ONDISKVER" != "x$cur_bootpath" ]; then + cur_bootpath="$ONDISKVER" fi - # we replace "sd" with "disk" if we need to work on the eeprom - # bootpath setting, since fibre-channel devices will report as - # being attached via "disk" and not "sd". One day we'll have a - # truly unified and architecture-independent view of the device - # tree, and this block will be redundant - fp_bootpath=`echo $new_bootpath|grep fp.*sd` - if [ "x$fp_bootpath" != "x" ]; then - new_bootpath=`echo $fp_bootpath |sed -e"s,sd,disk,g"` - fi + NEWBOOTPATH="" + for path in $cur_bootpath; do + mapped=`$STMSBOOTUTIL -p $path` + if [ "$mapped" != "NOT_MAPPED" ]; then + if [ "$mapped" != "$path" ]; then + NEWBOOTPATH=`echo "$path " | \ + $SED -e"s|$path|$mapped|"`" $NEWBOOTPATH" + else + NEWBOOTPATH="$NEWBOOTPATH $path" + fi + fi + done + # now strip off leading and trailing space chars + new_bootpath=`echo $NEWBOOTPATH` + return 0 } # @@ -408,9 +413,7 @@ emit_driver_warning_msg() { gettext " detected in a host. In your system, these controllers are\n\n" for WARNDRV in `echo $SUPPORTED_DRIVERS| $SED -e"s,|, ,g"`; do - for i in `$STMSBOOTUTIL -D $WARNDRV -n | $SED -e"s,|, ,g"`; do - $GREP "$i.*$WARNDRV.$" /etc/path_to_inst | $AWK -F"\"" '{print "/devices"$2}' - done; + $STMSBOOTUTIL -D $WARNDRV -n done; echo "" @@ -426,11 +429,15 @@ emit_driver_warning_msg() { "x$response" != "xy" ]; then exit fi - } -cmd=none +# +# +# main starts here +# + +cmd=none # process options while getopts D:geduLl: c do @@ -498,13 +505,22 @@ if [ $? -ne 0 ]; then fi fi + +# make sure we can stash our data somewhere private +if [ ! -d $SAVEDIR ]; then + $MKDIR -p $SAVEDIR +fi +# prime the cache +$STMSBOOTUTIL -i + + if [ "x$cmd" = xenable -o "x$cmd" = xdisable -o "x$cmd" = xupdate ]; then # # The bootup script doesn't work on cache-only-clients as the script # is executed before the plumbing for cachefs mounting of root is done. # if $MOUNT -v | $EGREP -s " on / type (nfs|cachefs) "; then - gettext "This command option is not supported on systems with nfs or cachefs mounted root filesystem.\n" 1>&2 + gettext "This command option is not supported on systems with an nfs or cachefs mounted root filesystem.\n" 1>&2 exit 1 fi @@ -514,8 +530,8 @@ if [ "x$cmd" = xenable -o "x$cmd" = xdisable -o "x$cmd" = xupdate ]; then # is in a sane state before allowing any further invocations, so # try to get the system admin to do so - ISARMED=`$SVCS -l $STMSINSTANCE |$GREP "enabled.*temporary"` - if [ $? -eq 0 ]; then + ISARMED=`$SVCS -l $STMSINSTANCE|$GREP "enabled.*false.*temporary"` + if [ ! $? ]; then echo "" gettext "You need to reboot the system in order to complete\n" gettext "the previous invocation of stmsboot.\n" @@ -525,7 +541,7 @@ if [ "x$cmd" = xenable -o "x$cmd" = xdisable -o "x$cmd" = xupdate ]; then if [ "x$response" = "x" -o "x$response" = "xY" -o \ "x$response" = "xy" ]; then - /usr/sbin/reboot + $REBOOT else echo "" gettext "Please reboot this system before continuing\n" @@ -534,27 +550,21 @@ if [ "x$cmd" = xenable -o "x$cmd" = xdisable -o "x$cmd" = xupdate ]; then fi fi - if [ -d $SAVEDIR ]; then - # - # keep a copy of the last saved files, useful for manual - # recovery in case of a problem. - # - for d in $DRVLIST; do - DRVCONF=$d.conf - KDRVCONF=/kernel/drv/$d.conf - TMPDRVCONF=/var/run/tmp.$d.conf.$$ - TMPDRVCONF_MPXIO_ENTRY=/var/run/tmp.$d.conf.mpxioentry.$$; - - if [ "x$MACH" = "xsparc" ]; then - backup_lastsaved $KDRVCONF $VFSTAB - else - backup_lastsaved $KDRVCONF $VFSTAB $BOOTENV_FILE - fi - done - else - mkdir $SAVEDIR - fi - + # + # keep a copy of the last saved files, useful for manual + # recovery in case of a problem. + # + for d in $DRVLIST; do + DRVCONF=$d.conf + KDRVCONF=/kernel/drv/$d.conf + TMPDRVCONF=/var/run/tmp.$d.conf.$$ + TMPDRVCONF_MPXIO_ENTRY=/var/run/tmp.$d.conf.mpxioentry.$$; + if [ "x$MACH" = "xsparc" ]; then + backup_lastsaved $KDRVCONF $VFSTAB + else + backup_lastsaved $KDRVCONF $VFSTAB /boot/solaris/$BOOTENV_FILE + fi + done fi if [ "x$cmd" = xenable -o "x$cmd" = xdisable ]; then @@ -568,7 +578,6 @@ if [ "x$cmd" = xenable -o "x$cmd" = xdisable ]; then done if [ $reboot_needed -ne 0 ]; then - # Need to update bootpath on x86 if our boot device is # now accessed through mpxio. # Only update bootpath before reboot when mpxio is enabled @@ -578,7 +587,7 @@ if [ "x$cmd" = xenable -o "x$cmd" = xdisable ]; then if [ "x$MACH" = "xi386" -a "x$cmd" = "xdisable" ]; then get_newbootpath_for_stmsdev if [ $? -ne 0 ]; then - rm -f $TMPDRVCONF > /dev/null 2>&1 + $RM -f $TMPDRVCONF > /dev/null 2>&1 gettext "failed to update bootpath.\n" 1>&2 gettext "No changes were made to your STMS configuration.\n" 1>&2 return 1 diff --git a/usr/src/cmd/stmsboot/stmsboot_util.c b/usr/src/cmd/stmsboot/stmsboot_util.c index 5fa8e9701b..06af0ca964 100644 --- a/usr/src/cmd/stmsboot/stmsboot_util.c +++ b/usr/src/cmd/stmsboot/stmsboot_util.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -36,1921 +34,1385 @@ #include <unistd.h> #include <stropts.h> #include <strings.h> -#include <dirent.h> #include <sys/param.h> -#include <sys/scsi/adapters/scsi_vhci.h> #include <libdevinfo.h> -#include <libgen.h> -#include <dlfcn.h> -#include <link.h> #include <locale.h> #include <libintl.h> -#include <sys/syscall.h> -#include <sys/mnttab.h> -#include <sys/vfstab.h> -#include <sys/mount.h> #include <devid.h> -#include <sys/libdevid.h> - -#define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" -#define SLASH_DEVICES "/devices/" - -#ifdef sparc -#define DISK_NODE_NAME "ssd" -#define DISK_DRV_NAME "ssd" -#else /* sparc */ -#define DISK_NODE_NAME "disk" -#define DISK_DRV_NAME "sd" -#endif - -#define DISK_AT_G "disk@g" -#define SLASH_FP_AT "/fp@" -#define SLASH_SCSI_VHCI "/scsi_vhci" -#define DEV_DSK "/dev/dsk/" -#define DEV_RDSK "/dev/rdsk/" -#define SYS_FILENAME_LEN 256 +#include <sys/modctl.h> /* for MAXMODCONFNAME */ +#include <sys/scsi/adapters/scsi_vhci.h> /* - * Save directory is the directory in which system files are saved. - * Save directory must be under the root filesystem, as this program is + * SAVE_DIR is the directory in which system files are saved. + * SAVE_DIR must be under the root filesystem, as this program is * typically run before any other filesystems are mounted. */ #define SAVE_DIR "/etc/mpxio" +#define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" + +/* nvlist property names, these are ALL string types */ +#define NVL_DEVID "nvl-devid" +#define NVL_PATH "nvl-path" +#define NVL_PHYSPATH "nvl-physpath" +#define NVL_MPXPATH "nvl-mpxiopath" +#define NVL_MPXEN "nvl-mpxioenabled" + +#define MPX_LIST 0x01 +#define MPX_MAP 0x02 +#define MPX_CAPABLE_CTRL 0x04 +#define MPX_INIT 0x08 +#define MPX_PHYSICAL 0x10 +#define MPX_BOOTPATH 0x20 +#define MPX_UPDATEVFSTAB 0x40 +#define MPX_USAGE 0x80 +#define MSG_INFO 0x01 +#define MSG_ERROR 0x02 +#define MSG_PANIC 0x04 + +#define BOOT 0x01 +#define NONBOOT 0x00 -/* fcp driver publishes this property */ -#define NODE_WWN_PROP "node-wwn" +static di_node_t devinfo_root = DI_NODE_NIL; +static char *ondiskname = "/etc/mpxio/devid_path.cache"; /* - * For SAS, we look for "sas-$drivername", eg sas-mpt, but - * we strncat the driver name later once we've parsed the - * args passed in from the shell. + * We use devid-keyed nvlists to keep track of the guid, traditional and + * MPxIO-enabled /dev/rdsk paths. Each of these nvlists is eventually + * added to our global nvlist and our on-disk nvlist. */ -#define SASPROP "sas-" +static nvlist_t *mapnvl; +static int mpxenabled = 0; +static int limctrl = -1; +static int guid = 0; +static char *drvlimit; +static int globarg = 0; +static int debugflag = 0; +static char *devicep; +static int readonlyroot = 0; +static int cap_N_option = 0; + +static void print_mpx_capable(di_node_t curnode); +static int popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, + char *strdevid); +static int mpxio_nvl_boilerplate(di_node_t curnode); +static int validate_devnvl(); +static void report_map(char *argdev, int physpath); +static void list_devs(int listguids, int ctrl); +static void logmsg(int level, const char *msg, ...); +static char *find_link(di_node_t cnode); +static void usage(); +static void parse_args(int argc, char *argv[]); +static void get_devid(di_node_t node, ddi_devid_t *thisdevid); +static int print_bootpath(); +static void vhci_to_phci(char *devpath, char *physpath); +static int update_vfstab(); +int +main(int argc, char **argv) +{ + struct stat cachestat; + int mapfd = 0; + int rv = 0; + char *ondiskbuf; + size_t newsz = 0; -typedef enum { - CLIENT_TYPE_UNKNOWN, - CLIENT_TYPE_PHCI, - CLIENT_TYPE_VHCI -} client_type_t; + parse_args(argc, argv); + errno = 0; + devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE); + logmsg(MSG_INFO, "errno = %d after " + "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno); + if (devinfo_root == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to take device tree snapshot " + "(%s: %d)\n"), strerror(errno), errno); + return (-1); + } + logmsg(MSG_INFO, "opened root di_node\n"); -struct devlink_cbarg { - char *devlink; - size_t len; -}; + if (globarg == MPX_CAPABLE_CTRL) { + /* we just want to find MPxIO-capable controllers and exit */ + if (drvlimit != NULL) { + print_mpx_capable(di_drv_first_node(drvlimit, + devinfo_root)); + } else { + print_mpx_capable(di_drv_first_node("fp", + devinfo_root)); + print_mpx_capable(di_drv_first_node("mpt", + devinfo_root)); + } + di_fini(devinfo_root); + return (0); + } -static di_node_t devinfo_root = DI_NODE_NIL; -static di_devlink_handle_t devlink_hdl = NULL; -static int vhci_fd = -1; -static int patch_vfstab, cap_m_option, debug; -static int list_option, list_guid_mappings, list_controllernum = -1; -static char *mapdev = ""; -static char *map_vhciname = ""; -static char *stmsboot = "stmsboot"; - -char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */ -/* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */ -char *drvprop = (char *)NULL; -static int parent = 0; /* for "-n" usage */ - -static int make_temp(char *, char *, char *, size_t); -static void commit_change(char *, char *, char *, int); -static int map_devname(char *, char *, size_t, int); -static int update_vfstab(char *, char *); -static int list_mappings(int, int); -static int canopen(char *); -static client_type_t client_by_props(char *path); -static void list_nodes(char *drivername); -static int canread(char *, char *); - -static void logerr(char *, ...); -static void logdmsg(char *, ...); -static void *s_malloc(const size_t); -static char *s_strdup(const char *); -static void s_strlcpy(char *, const char *, size_t); -static int map_openable_vhciname(char *, char *, size_t); -/* - * Using an exit function not marked __NORETURN causes a warning with gcc. - * To suppress the warning, use __NORETURN attribute. - */ -static void clean_exit(int)__NORETURN; + mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR); + if (mapfd < 0) { + /* we could be in single-user, so try for RO */ + if ((mapfd = open(ondiskname, O_RDONLY)) < 0) { + logmsg(MSG_ERROR, + gettext("Unable to open or create %s:%s\n"), + ondiskname, strerror(errno)); + return (errno); + } + readonlyroot = 1; + } -/* - * Print usage and exit. - */ -static void -usage(char *argv0) -{ - char *progname; + if (stat(ondiskname, &cachestat) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to stat() %s: %s\n"), + ondiskname, strerror(errno)); + return (errno); + } + ondiskbuf = calloc(1, cachestat.st_size); + if (ondiskbuf == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate memory for the devid " + "cache file: %s\n"), strerror(errno)); + return (errno); + } + rv = read(mapfd, ondiskbuf, cachestat.st_size); + if (rv != cachestat.st_size) { + logmsg(MSG_ERROR, + gettext("Unable to read all of devid cache file (got %d " + "from expected %d bytes): %s\n"), + rv, cachestat.st_size, strerror(errno)); + return (errno); + } + errno = 0; + rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0); + if (rv) { + logmsg(MSG_INFO, + "Unable to unpack devid cache file %s: %s (%d)\n", + ondiskname, strerror(rv), rv); + if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to allocate root property" + "list\n")); + return (errno); + } + } + free(ondiskbuf); - progname = strrchr(argv0, '/'); - if (progname != NULL) - progname++; - else - progname = argv0; + if (validate_devnvl() < 0) { + logmsg(MSG_ERROR, + gettext("unable to validate kernel with on-disk devid " + "cache file\n")); + return (errno); + } /* - * -u update /etc/vfstab - * -m devname - * if devname is phci based name and not open-able, map it to - * vhci based /devices name. - * if devname is vhci based name and not open-able, map it to - * phci based /devices name. - * -M devname - * same as -m except that /dev link is printed instead of - * /devices name. - * -l controller - * list non-STMS to STMS device name mappings for the specific - * controller - * -L list non-STMS to STMS device name mappings for all controllers - * -p devname - * if devname is vhci based name and open-able, get the first - * onlined phci based name without /devices prefix. - * Used in stmsboot to update the phci based bootpath. - * -D drvname - * if supplied, indicates that we're going to operate on - * devices attached to this driver - * -n - * if supplied, returns name of the node containing "fp" or - * "sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only - * be used if -D drv is specified as well + * If we're in single-user mode or maintenance mode, we won't + * necessarily have a writable root device (ZFSroot; ufs root is + * different in that we _do_ have a writable root device. + * This causes problems for the devlink calls (see + * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to + * write out the devnvl if root is readonly. */ - (void) fprintf(stderr, gettext("usage: %s -u | -m devname | " - "-M devname | -l controller | -L | \n" - "\t\t-p devname | -D { fp | mpt } | -n\n"), progname); + if (!readonlyroot) { + rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to determine size of packed " + "on-disk devid cache file %s: %s (%d).\n"), + ondiskname, strerror(rv), rv); + logmsg(MSG_ERROR, gettext("Terminating\n")); + nvlist_free(mapnvl); + (void) close(mapfd); + return (rv); + } + + if ((ondiskbuf = calloc(1, newsz)) == NULL) { + logmsg(MSG_ERROR, + "Unable to allocate space for writing out new " + "on-disk devid cache file: %s\n", strerror(errno)); + (void) close(mapfd); + nvlist_free(mapnvl); + return (errno); + } + + rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz, + NV_ENCODE_NATIVE, 0); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to pack on-disk devid cache " + "file: %s (%d)\n"), strerror(rv), rv); + (void) close(mapfd); + free(ondiskbuf); + nvlist_free(mapnvl); + return (rv); + } + + rv = lseek(mapfd, 0, 0); + if (rv == -1) { + logmsg(MSG_ERROR, + gettext("Unable to seek to start of devid cache " + "file: %s (%d)\n"), strerror(errno), errno); + (void) close(mapfd); + free(ondiskbuf); + nvlist_free(mapnvl); + return (-1); + } + + if (write(mapfd, ondiskbuf, newsz) != newsz) { + logmsg(MSG_ERROR, + gettext("Unable to completely write out " + "on-disk devid cache file: %s\n"), strerror(errno)); + (void) close(mapfd); + nvlist_free(mapnvl); + free(ondiskbuf); + return (errno); + } + } /* !readonlyroot */ + + /* Now we can process the command line args */ + if (globarg == MPX_PHYSICAL) { + report_map(devicep, BOOT); + } else if (globarg == MPX_BOOTPATH) { + rv = print_bootpath(); + di_fini(devinfo_root); + return (rv); + } else if (globarg == MPX_UPDATEVFSTAB) { + rv = update_vfstab(); + di_fini(devinfo_root); + return (rv); + } else if (globarg != MPX_INIT) { + if (globarg & MPX_LIST) + list_devs(guid, limctrl); + + if (globarg == MPX_MAP) + report_map(devicep, NONBOOT); + } else { + logmsg(MSG_INFO, "\nprivate devid cache file initialised\n"); + } + + nvlist_free(mapnvl); + di_fini(devinfo_root); + return (0); +} + +static void +usage() +{ + (void) fprintf(stderr, + gettext("usage: stmsboot_util -b | -m devname | " + "-l <ctrl> | -L | [-g] | -n | -N | -i | -p devname\n")); + (void) fprintf(stderr, "\n\n"); + (void) fprintf(stderr, gettext("\t-h\tprint this usage message\n")); + (void) fprintf(stderr, gettext("\t-b\tretrieve the system's bootpath " + "setting\n")); + (void) fprintf(stderr, gettext("\t-m devname\n")); + (void) fprintf(stderr, gettext("\t\tReports the current mapping for " + "devname\n")); + (void) fprintf(stderr, gettext("\t-g\tprint the GUID for MPxIO-capable " + "devices. This\n")); + (void) fprintf(stderr, gettext("\t\toption is only valid with the -L " + "or -l options\n")); + (void) fprintf(stderr, gettext("\t-L | -l <ctrl>\n")); + (void) fprintf(stderr, gettext("\t\tList the 'native' to 'MPxIO' " + "device mappings. If <ctrl>\n")); + (void) fprintf(stderr, gettext("\t\tis specified, only print mappings " + "for those devices\n")); + (void) fprintf(stderr, gettext("\t\tattached via the specified " + "controller.\n")); + (void) fprintf(stderr, gettext("\t-i\tinitialise the private devid " + "cache file and exit\n")); + (void) fprintf(stderr, gettext("\t\tThis option excludes all " + "others.\n")); + (void) fprintf(stderr, gettext("\t-n\tprint the devfs paths for " + "multipath-capable\n")); + (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); + (void) fprintf(stderr, gettext("\t-N\tprint the device aliases of " + "multipath-capable\n")); + (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); + (void) fprintf(stderr, gettext("\t-p\tdevname\n")); + (void) fprintf(stderr, gettext("\t\tThis option provides the physical " + "devfs path for\n")); + (void) fprintf(stderr, gettext("\t\ta specific device (devname). Used " + "to set the bootpath\n")); + (void) fprintf(stderr, gettext("\t\tvariable on x86/x64 systems\n")); + (void) fprintf(stderr, gettext("\t-u\ttranslates device mappings in " + "/etc/vfstab as \n")); + (void) fprintf(stderr, gettext("\t\trequired. The output is written " + "to /etc/mpxio/vfstab.new\n\n")); exit(2); } -/* - * Parse command line arguments. - */ static void parse_args(int argc, char *argv[]) { char opt; - int n = 0; - if (argc == 1) { - usage(argv[0]); - /*NOTREACHED*/ - } + if (argc == 1) + usage(); - while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) { + /* + * -b prints the bootpath property + * -d turns on debug mode for this utility (copious output!) + * -D drvname + * if supplied, indicates that we're going to operate on + * devices attached to this driver. + * -g if (-l or -L), prints guids for devices rather than paths + * -h prints the usage() help text. + * -i initialises the cache file and exits. + * -l controller + * list non-STMS to STMS device name mappings for the specific + * controller, when MPxIO is enabled only. + * -L list non-STMS to STMS device name mappings for all controllers + * when MPxIO is enabled only. + * -m devname + * prints the device path (/dev/rdsk) that devname maps to + * in the currently-running system. + * -n + * if supplied, returns name of STMS-capable controller nodes. + * If the -D drvname option is specified as well, we only report + * nodes attached with drvname. + * -N + * same as the -n option, except that we only print the + * node-name (dev_info :: devi_node_name). Multiple instances + * through the libdevinfo snapshot are uniqified and separated + * by the "|" character for direct use by egrep(1). + * -p devname + * prints the physical devfs path for devname. Only used to + * determine the bootpath. + * -u + * remaps devices in /etc/vfstab, saving the newly generated + * file to /etc/mpxio/vfstab.new. If we have any remapped + * devices, exit with status 0, otherwise -1 for error. + */ + while ((opt = getopt(argc, argv, "bdD:ghil:Lm:nNp:u")) != EOF) { switch (opt) { - case 'u': - patch_vfstab = 1; - n++; + case 'b': + globarg = MPX_BOOTPATH; break; - case 'd': - debug = 1; + debugflag = 1; break; - - case 'm': - mapdev = s_strdup(optarg); - n++; + case 'D': + if ((drvlimit = calloc(1, MAXMODCONFNAME)) == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate memory for a " + "driver name: %s\n"), strerror(errno)); + exit(errno); + } + bcopy(optarg, drvlimit, strlen(optarg)); + /* update this if adding support for a new driver */ + if ((strncmp(drvlimit, "fp", 2) == NULL) && + (strncmp(drvlimit, "mpt", 3) == NULL)) { + logmsg(MSG_ERROR, + gettext("invalid parent driver (%s) " + "specified"), drvlimit); + usage(); + } break; - - case 'M': - mapdev = s_strdup(optarg); - cap_m_option = 1; - n++; + case 'h': + /* Just drop out and print the usage() output */ + globarg = MPX_USAGE; break; - - case 'L': - list_option = 1; - n++; + case 'i': + globarg = MPX_INIT; break; - case 'l': - list_option = 1; - list_controllernum = (int)atol(optarg); - if (list_controllernum < 0) { - logerr(gettext("controller number %d is " - "invalid\n"), list_controllernum); - clean_exit(1); + globarg |= MPX_LIST; + limctrl = (int)atol(optarg); + if (limctrl < 0) { + logmsg(MSG_INFO, + gettext("invalid controller number " + "(%d), checking all controllers\n"), + limctrl); } - n++; break; - - case 'g': - /* - * private option to display non-STMS device name - * to GUID mappings. - */ - list_guid_mappings = 1; + case 'L': + globarg |= MPX_LIST; break; - - case 'p': - /* - * map openable vhci based name to phci base name - */ - map_vhciname = s_strdup(optarg); - n++; + case 'g': + guid = 1; break; - - case 'D': - /* - * Grab the driver name we need to look for. Each - * time we add support for a new SAS or FC driver - * to this utility, make sure that its driver name - * is checked here. - */ - drvname = s_malloc(sizeof (optarg) + 1); - drvname = s_strdup(optarg); - if (strcmp(drvname, "fp") == 0) { - drvprop = s_malloc(sizeof (NODE_WWN_PROP)); - (void) snprintf(drvprop, sizeof (NODE_WWN_PROP), - NODE_WWN_PROP); - } else if (strcmp(drvname, "mpt") == 0) { - drvprop = s_malloc(sizeof (SASPROP) + - sizeof (drvname) + 1); - (void) snprintf(drvprop, sizeof (SASPROP) + - sizeof (drvname), "%s%s", - SASPROP, drvname); - } else { - logerr(gettext("Driver %s is not supported\n"), - drvname); - clean_exit(1); + case 'm': + globarg = MPX_MAP; + if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate space for a " + "device name\n")); + exit(errno); } - + devicep = strdup(optarg); + break; + case 'N': + cap_N_option = 1; + globarg = MPX_CAPABLE_CTRL; break; - case 'n': - ++parent; - n++; + globarg = MPX_CAPABLE_CTRL; + break; + case 'p': + globarg = MPX_PHYSICAL; + if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate space for a " + "device name\n")); + exit(errno); + } + devicep = strdup(optarg); + break; + case 'u': + globarg = MPX_UPDATEVFSTAB; break; - default: - usage(argv[0]); - /*NOTREACHED*/ - } - } - - if (n != 1) { - usage(argv[0]); - /*NOTREACHED*/ - } -} - -int -main(int argc, char *argv[]) -{ - char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN]; - int vfstab_updated; - - (void) setlocale(LC_ALL, ""); - (void) textdomain(TEXT_DOMAIN); - - if (getuid() != 0) { - logerr(gettext("must be super-user to run this program\n")); - clean_exit(1); - } - - parse_args(argc, argv); - (void) umask(022); - - /* - * NOTE: The mpxio boot-up script executes this program with the - * mapping (-m) option before the /usr is even mounted and when the - * root filesystem is still mounted read-only. - */ - if (*mapdev != '\0') { - char newname[MAXPATHLEN]; - - if (map_devname(mapdev, newname, sizeof (newname), - cap_m_option) == 0) { - (void) printf("%s\n", newname); - clean_exit(0); - } - clean_exit(1); - } - if (*map_vhciname != '\0') { - char newname[MAXPATHLEN]; - - if (map_openable_vhciname(map_vhciname, newname, - sizeof (newname)) == 0) { - (void) printf("%s\n", newname); - clean_exit(0); + logmsg(MSG_ERROR, + gettext("Invalid command line option (%c)\n"), + opt); + usage(); } - clean_exit(1); } - if (list_option || list_guid_mappings) { - if (list_mappings(list_controllernum, list_guid_mappings) == 0) - clean_exit(0); - clean_exit(1); - } - - if (parent > 0) { - if (strcmp(drvname, "") == 0) { - usage(argv[0]); - clean_exit(1); - } else { - list_nodes(drvname); - clean_exit(0); - } - } - - /* create a directory where a copy of the system files are saved */ - if (patch_vfstab) { - if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) { - logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"), - SAVE_DIR, strerror(errno)); - clean_exit(1); - } - - if (make_temp(VFSTAB, save_vfstab, tmp_vfstab, - SYS_FILENAME_LEN) != 0) - clean_exit(1); - - /* build new vfstab without modifying the existing one */ - if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab)) - == -1) { - logerr(gettext("failed to update %s\n"), VFSTAB); - clean_exit(1); - } + if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST))) + usage(); - commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated); - } - - clean_exit(0); - /*NOTREACHED*/ + if ((drvlimit != NULL) && + ((globarg != MPX_LIST) && + (globarg != MPX_CAPABLE_CTRL))) + usage(); } -/* - * Make saved and temporary filenames in SAVE_DIR. - * - * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename - * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively. - * - * Returns 0 on success, -1 on failure. - */ -static int -make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len) +static void +logmsg(int level, const char *msg, ...) { - char *ptr; + va_list ap; - if ((ptr = strrchr(filename, '/')) == NULL) { - logdmsg("invalid file %s\n", filename); - return (-1); + if ((level >= MSG_ERROR) || + ((debugflag > 0) && (level >= MSG_INFO))) { + (void) fprintf(stdout, "stmsboot: "); + va_start(ap, msg); + (void) vfprintf(stdout, msg, ap); + va_end(ap); } - (void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr); - (void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr); - logdmsg("make_temp: %s: save = %s, temp = %s\n", filename, - save_filename, tmp_filename); - return (0); } /* - * Commit the changes made to the system file + * It's up to the caller to do any sorting or pretty-printing of the device + * mappings we report. Since we're storing the device links as just the cXtYdZ + * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain + * compatibility with previous versions of this tool. There's a little bit + * of footwork involved to make sure that we show all the paths to a device + * rather than just the first one we stashed away. */ static void -commit_change(char *filename, char *save_filename, char *tmp_filename, - int updated) +list_devs(int listguids, int ctrl) { - int x; + nvlist_t *thisdevnvl; + nvpair_t *pair; + char *diskpath, *livepath, *key, *querydev; + char *matchctrl = NULL; + char checkctrl[MAXPATHLEN]; + int rv; - if (updated) { - /* save the original */ - if ((x = rename(filename, save_filename)) != 0) { - logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), - filename, save_filename, strerror(errno)); - } + if (!mpxenabled) { + logmsg(MSG_ERROR, gettext("MPxIO is not enabled\n")); + return; + } - /* now rename the new file to the actual file */ - if (rename(tmp_filename, filename) != 0) { - logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), - tmp_filename, filename, strerror(errno)); - - /* restore the original */ - if (x == 0 && rename(save_filename, filename) != 0) { - logerr( - gettext("rename %1$s to %2$s failed: %3$s\n" - "%4$s is a copy of the original %5$s file" - "\n"), - save_filename, filename, strerror(errno), - save_filename, filename); - } - } else - (void) printf(gettext("%1$s: %2$s has been updated.\n"), - stmsboot, filename); + if (listguids) { + (void) printf(gettext("non-STMS device name\t\t\tGUID\n" + "------------------------------------------" + "------------------------\n")); } else { - /* remove the temp file */ - (void) unlink(tmp_filename); - (void) printf(gettext("%1$s: %2$s was not modified as no " - "changes were needed.\n"), stmsboot, filename); + (void) printf(gettext("non-STMS device name\t\t\t" + "STMS device name\n" + "------------------------------------------" + "------------------------\n")); } -} - -/* - * Get the GUID of the device. - * - * physpath /devices name without the /devices prefix and minor name - * component. - * guid caller supplied buffer where the GUID will be placed on return - * guid_len length of the caller supplied guid buffer. - * no_delay_flag if set open the device with O_NDELAY - * node di_node corresponding to physpath if already available, - * otherwise pass DI_NODE_NIL. - * - * Returns 0 on success, -1 on failure. - */ -static int -get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag, - di_node_t node) -{ - int fd; - ddi_devid_t devid; - int rv = -1; - char *i_guid = NULL; - char physpath_raw[MAXPATHLEN]; - uchar_t *wwnp; - int i, n, snapshot_taken = 0; - - logdmsg("get_guid: physpath = %s\n", physpath); - -#ifdef sparc - (void) snprintf(physpath_raw, MAXPATHLEN, - "/devices%s:a,raw", physpath); -#else - (void) snprintf(physpath_raw, MAXPATHLEN, - "/devices%s:c,raw", physpath); -#endif - - *guid = '\0'; - if (no_delay_flag) - no_delay_flag = O_NDELAY; + bzero(checkctrl, MAXPATHLEN); + pair = NULL; + while ((pair = nvlist_next_nvpair(mapnvl, pair)) + != NULL) { + boolean_t livescsivhcip = B_FALSE; + + if ((((rv = nvpair_value_string(pair, &querydev)) < 0) || + ((key = nvpair_name(pair)) == NULL)) || + ((strstr(key, "/pci") != NULL) || + (strstr(key, "/sbus") != NULL) || + (strstr(key, "/scsi_vhci") != NULL) || + (strncmp(key, "id1", 3) == 0))) { + logmsg(MSG_INFO, + "list_devs: rv = %d; (%s) is not a devlink, " + "continuing.\n", rv, + (key != NULL) ? key : "null"); + querydev = NULL; + continue; + } - /* - * Open the raw device - * Without the O_DELAY flag, the open will fail on standby paths of - * T3 if its mp_support mode is "mpxio". - */ - if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) { - logdmsg("get_guid: failed to open %s: %s\n", physpath_raw, - strerror(errno)); - return (-1); - } + (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl); + (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN, + &livescsivhcip); + (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH, + &livepath); - if (devid_get(fd, &devid) == 0) { - i_guid = devid_to_guid(devid); - devid_free(devid); + if ((!livescsivhcip) || + (livescsivhcip && + (strncmp(key, livepath, strlen(key)) == 0))) + continue; - if (i_guid != NULL) { - s_strlcpy(guid, i_guid, guid_len); - devid_free_guid(i_guid); - rv = 0; - goto out; - } else { - logdmsg("get_guid: devid_to_guid() failed\n"); - logdmsg("Unable to get a GUID for device " - "%s\n", physpath_raw); - } + (void) nvlist_lookup_string(thisdevnvl, NVL_PATH, + &diskpath); - } else - logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno)); + logmsg(MSG_INFO, + "list_devs: %s :: %s ::%s :: MPXEN (%s)\n", + key, diskpath, livepath, + ((livescsivhcip) ? "TRUE" : "FALSE")); - /* - * Unless we're looking at an fp-attached device, we now - * fallback to node name as the guid as this is what the - * fcp driver does. A sas-attached device will have the - * client-guid property set. - */ - if (node == DI_NODE_NIL) { - if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE)) - == DI_NODE_NIL) { - logdmsg("get_guid: di_init on %s failed: %s\n", - physpath, strerror(errno)); - goto out; + if (ctrl > -1) { + (void) sprintf(checkctrl, "c%dt", ctrl); + matchctrl = strstr(key, checkctrl); + if (matchctrl == NULL) + continue; } - snapshot_taken = 1; - } + if (listguids != 0) { + char *tempguid; + ddi_devid_t curdevid; + int rv; + + rv = devid_str_decode(querydev, &curdevid, NULL); + if (rv == -1) { + logmsg(MSG_INFO, "Unable to decode devid %s\n", + key); + continue; + } + tempguid = devid_to_guid(curdevid); + if (tempguid != NULL) + (void) printf("/dev/rdsk/%s\t%s\n", + diskpath, tempguid); - /* non-fp fallout */ - if (strstr(physpath, "fp") == (char *)NULL) { - if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, - "client-guid", &guid) < 0) { - logdmsg("get_guid: non-fp-attached device, " - "bailing out\n"); - goto out; + devid_free_guid(tempguid); + devid_free(curdevid); + continue; } - } - if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP, - &wwnp)) == -1) { - logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup " - "%s: %s\n", NODE_WWN_PROP, strerror(errno)); - goto out; + (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n", + (strstr(key, diskpath) == NULL) ? key : diskpath, + livepath); } - - if (guid_len >= ((n * 2) + 1)) { - for (i = 0; i < n; i++) { - (void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp)); - wwnp++; - } - rv = 0; - } else - logerr(gettext("insufficient buffer size: need %1$d " - "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len); - -out: - if (snapshot_taken) - di_fini(node); - - (void) close(fd); - logdmsg("get_guid: GUID = %s\n", guid); - return (rv); } /* - * Given client_name return whether it is a phci or vhci based name. - * client_name is /devices name of a client without the /devices prefix. + * We get passed a device name which we search the mapnvl for. If we find + * it, we print the mapping as it is found. It is up to the caller of this + * utility to do any pretty-printing of the results. If a device listed on + * the command line does not exist in the mapnvl, then we print NOT_MAPPED. + * Otherwise we print the command-line device name as it maps to what is + * stashed in the mapnvl - even if that's a "no change" device mapping. * - * client_name Return value - * on sparc: - * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI (fc) - * .../LSILogic,sas@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) - * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI (fc) - * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI (sas) - * other CLIENT_TYPE_UNKNOWN - * on x86: - * .../fp@xxx/disk@yyy CLIENT_TYPE_PHCI (fc) - * .../pci1000,????@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) - * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI - * other CLIENT_TYPE_UNKNOWN - */ -static client_type_t -client_name_type(char *client_name) -{ - client_type_t client_type = CLIENT_TYPE_UNKNOWN; - char *p1; - char *client_path; - - client_path = s_strdup(client_name); - logdmsg("client_name_type: client is %s\n", client_path); - - if (*client_name != '/') - return (CLIENT_TYPE_UNKNOWN); - - if ((p1 = strrchr(client_name, '/')) == NULL || - ((strncmp(p1, "/ssd@", sizeof ("/ssd@") - 1) != 0) && - (strncmp(p1, "/sd@", sizeof ("/sd@") - 1) != 0) && - (strncmp(p1, "/disk@", sizeof ("/disk@") - 1) != 0))) { - logdmsg("client_name_type: p1 = %s\n", p1); - return (CLIENT_TYPE_UNKNOWN); - } - - *p1 = '\0'; - - /* - * Courtesy of the if (..) block above, we know that any - * device path we have now is either PHCI or VHCI - */ - client_type = client_by_props(client_path); - - logdmsg("client_name_type: client_type = %d\n", client_type); - - *p1 = '/'; - return (client_type); -} - -/* - * client_by_props() is called to determine what the client type - * is, based on properties in the device tree: + * Example output (-p maps to physpath=BOOT) + * # /lib/mpxio/stmsboot_util -p \ + * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a + * /scsi_vhci/disk@g500000e011e17720:a + * + * Or the reverse: + * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a + * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a * - * drivername property type - * ------------------------------------- - * fp node-wwn CLIENT_TYPE_PHCI - * mpt sas-mpt CLIENT_TYPE_PHCI - * mpt client-guid CLIENT_TYPE_PHCI (corner case) + * For the -m option, used when we're trying to find the root device mapping: * - * Normally, the "client-guid" property only shows up for a node - * if we've enumerated that node under scsi_vhci. During testing - * of this function, one particular corner case was found which - * requires an exception handler. + * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2 + * /dev/dsk/c3t500000E011637CF0d0s2 */ - -static client_type_t -client_by_props(char *path) { - - di_node_t clientnode = DI_NODE_NIL; - di_node_t parentnode = DI_NODE_NIL; - unsigned int rval = CLIENT_TYPE_UNKNOWN; - uchar_t *byteprop[32]; - char *charprop = NULL; - char *physpath; - char *parentpath; - - physpath = s_malloc(MAXPATHLEN); - bzero(physpath, MAXPATHLEN); - - physpath = s_strdup(path); - - logdmsg("client_by_props: physpath = (%s)\n", physpath); - - /* easy short-circuits */ - if (strstr(physpath, "scsi_vhci") != (char *)NULL) { - logdmsg("client_by_props: found " - "'scsi_vhci' on path (%s)\n", physpath); - rval = CLIENT_TYPE_VHCI; - goto out; - } else if ((strstr(physpath, "ide") != (char *)NULL) || - (strstr(physpath, "storage") != (char *)NULL)) { - logdmsg("client_by_props: ignoring this device\n"); - goto out; +static void +report_map(char *argdev, int physpath) +{ + nvlist_t *thisdev; + int rv = 0; + char *thisdevid; + char *mpxpath = NULL; + char *prefixt = NULL; + char *prefixp = NULL; + char *stripdev = NULL; + char *slice = NULL; + boolean_t mpxenp; + uint_t slicelen = 0; + + mpxenp = B_FALSE; + + if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) { + logmsg(MSG_INFO, "Unable to allocate memory\n"); + (void) printf("NOT_MAPPED\n"); + return; } - parentpath = s_malloc(MAXPATHLEN); - bzero(parentpath, MAXPATHLEN); + (void) strlcpy(prefixt, argdev, strlen(argdev) + 1); - (void) strncpy(parentpath, physpath, strlen(physpath) - - strlen(strrchr(physpath, '/'))); + slice = strrchr(argdev, (physpath == BOOT) ? ':' : 's'); + if (slice != NULL) { + slicelen = strlen(slice); + if (slicelen > 3) + /* invalid size - max is 3 chars */ + slicelen = 0; + } - if ((parentnode = di_init(parentpath, DINFOCPYALL | - DINFOFORCE)) == DI_NODE_NIL) { - logdmsg("client_by_props: unable to di_init(%s)\n", - parentpath); - goto out; + if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) { + logmsg(MSG_INFO, "Unable to allocate memory\n"); + (void) printf("NOT_MAPPED\n"); + free(prefixt); + return; } - if (strstr(physpath, "fp") != (char *)NULL) { - if (drvprop == (char *)NULL) { - drvprop = s_malloc(strlen(NODE_WWN_PROP) + 1); - } - logdmsg("NODE_WWN_PROP\n"); - (void) snprintf(drvprop, strlen(NODE_WWN_PROP) + 1, - NODE_WWN_PROP); + if ((strstr(prefixt, "/scsi_vhci") == NULL) && + (strstr(prefixt, "/pci") == NULL) && + (strstr(prefixt, "/sbus") == NULL)) { + prefixp = strrchr(prefixt, '/'); + (void) strlcpy(stripdev, + (prefixp == NULL) ? prefixt : prefixp + 1, + (prefixp == NULL) ? + strlen(prefixt) + 1: strlen(prefixp) + 1); + if (prefixp != NULL) + prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0'; } else { - if (drvname == (char *)NULL) { - drvname = di_driver_name(parentnode); - logdmsg("client_by_props: drvname = %s\n", drvname); + if (physpath != BOOT) { + logmsg(MSG_INFO, "Invalid device path provided\n"); + (void) printf("NOT_MAPPED\n"); + free(stripdev); + free(prefixt); + return; } + (void) strlcpy(stripdev, argdev, strlen(argdev) + 1); + } - if (drvprop == (char *)NULL) { - drvprop = s_malloc(sizeof (SASPROP) + - sizeof (drvname) + 1); + logmsg(MSG_INFO, + "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n", + (stripdev == NULL) ? "null" : stripdev, + (prefixt == NULL) ? "null" : prefixt, + (prefixp == NULL) ? "null" : prefixp, + (slice == NULL) ? "null" : slice); + + if (slicelen > 0) + stripdev[strlen(stripdev) - slicelen] = '\0'; + + /* search for the shortened version */ + rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid); + if (rv) { + if (physpath != BOOT) { + logmsg(MSG_INFO, + "searched mapnvl for '%s', got %s (%d)\n", + stripdev, strerror(rv), rv); + (void) printf("NOT_MAPPED\n"); + free(stripdev); + free(prefixt); + return; } - (void) snprintf(drvprop, sizeof (SASPROP) + - sizeof (drvname), "%s%s", SASPROP, drvname); - - logdmsg("parentpath: %s\nphyspath: %s\n" - "length %d, strrchr: %d\n", - parentpath, physpath, strlen(physpath), - strlen(strrchr(physpath, '/'))); } - logdmsg("client_by_props: searching for property '%s'\n", drvprop); - - if ((clientnode = di_init(physpath, DINFOCPYALL | DINFOFORCE)) == - DI_NODE_NIL) { - logdmsg("client_by_props: unable to di_init(%s)\n", - physpath); + logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid); - /* - * On x86/x64 systems, we won't be able to di_init() the - * node we want in the device tree, however the parent - * node will still have 'mpxio-disable' set, so we can - * check for that property and make our decision on type - */ + if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) { + logmsg(MSG_INFO, "device (%s) in mapnvl but " + "not mapped!\n", thisdevid); + (void) printf("NOT_MAPPED\n"); + free(stripdev); + free(prefixt); + return; + } - if (di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode, - "mpxio-disable", &charprop) > -1) { - rval = CLIENT_TYPE_PHCI; - di_fini(parentnode); - logdmsg("client_by_props: device %s is PHCI\n", - physpath); - } - goto out; + /* quick exit */ + if (!mpxenabled && (strstr(argdev, "/pci") != NULL || + strstr(argdev, "/sbus") != NULL)) { + (void) printf("%s\n", argdev); + free(stripdev); + free(prefixt); + return; } - if (di_prop_lookup_bytes(DDI_DEV_T_ANY, - clientnode, drvprop, byteprop) > -1) { - logdmsg("client_by_props: found prop %s on " - "path %s\n", drvprop, physpath); - rval = CLIENT_TYPE_PHCI; - } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, - clientnode, "client-guid", &charprop) > -1) { + (void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp); + + if (physpath == BOOT) { + (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath); + if ((strstr(argdev, "/scsi_vhci") != NULL) && + (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) { + /* Need to translate vhci to phci */ + char *realpath; + + if ((realpath = calloc(1, MAXPATHLEN + 1)) == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate " + "memory for a path element\n")); + free(stripdev); + free(prefixt); + return; + } + vhci_to_phci(stripdev, realpath); + (void) printf("%s%s\n", realpath, + ((slicelen > 0) && slice != NULL) ? slice : ""); + free(realpath); + } else { + (void) printf("%s%s\n", mpxpath, + ((slicelen > 0) && slice != NULL) ? slice : ""); + } + } else { + (void) nvlist_lookup_string(thisdev, + ((readonlyroot) ? NVL_PHYSPATH : + ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)), + &mpxpath); + logmsg(MSG_INFO, "mpxpath = %s\n", + (mpxpath == NULL) ? "null" : mpxpath); + if (readonlyroot || + (strstr(mpxpath, "/scsi_vhci") != NULL) || + (strstr(mpxpath, "/pci") != NULL) || + (strstr(mpxpath, "/sbus") != NULL)) { /* - * A corner case was seen during testing where - * scsi_vhci was loaded, but not all applicable - * devices were enumerated under it. That left - * the phci mapping along with the "client-guid" - * property. + * If we see a physical path here it means that + * devlinks aren't fully initialised yet, so we + * are still in maintenance/single-user mode. */ - logdmsg("client_by_props: weird... \n"); - rval = CLIENT_TYPE_PHCI; - } else { - logdmsg("client_by_props: unable to find " - "property 'client-guid', 'mpxio-disable' " - "or '%s' anywhere on path (%s)\n", - drvprop, physpath); - logdmsg("client_by_props: this node is unknown\n"); + (void) printf("/devices%s:%c\n", mpxpath, + slice[1] + '1'); + } else { + (void) printf("%s%s%s\n", + (prefixt[0] == '/') ? prefixt : "", + mpxpath, + ((slicelen > 0) && slice != NULL) ? slice : ""); + } } - - di_fini(parentnode); - di_fini(clientnode); -out: - free(physpath); - return (rval); + free(prefixt); + free(stripdev); } - /* - * Given a phci or vhci devname which is either a /dev link or /devices name - * get the corresponding physical node path (without the /devices prefix) - * and minor name. - * - * Returns 0 on success, -1 on failure. + * Validate the in-kernel and on-disk forms of our devid cache, + * returns -1 for unfixable error and 0 for success. */ static int -get_physname_minor(char *devname, char *physname, int physname_len, - char *minorname, int minorname_len) +validate_devnvl() { - int linksize; - char buf[MAXPATHLEN]; - char *p, *m; - - if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 || - strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) { - if ((linksize = readlink(devname, buf, MAXPATHLEN)) - > 0 && linksize <= (MAXPATHLEN - 1)) { - buf[linksize] = '\0'; - } else - return (-1); - } else - s_strlcpy(buf, devname, MAXPATHLEN); - - if ((p = strstr(buf, SLASH_DEVICES)) == NULL) - return (-1); - - /* point to '/' after /devices */ - p += sizeof (SLASH_DEVICES) - 2; + di_node_t curnode; + int rv1 = -1; + int rv2 = -1; - if ((m = strrchr(p, ':')) == NULL) { - logdmsg("get_physname_minor: no minor name component in %s\n", - buf); - return (-1); - } + /* + * Method: we walk through the kernel's concept of the device tree + * looking for "ssd" then "sd" nodes. + * We check to see whether the device's devid is already in our nvlist + * (on disk) nvlist cache file. If it is, we check that it's components + * match what we've got already and fill any missing fields. + * If the devid isn't in our on-disk nvlist already then we add it + * and populate the property nvpairs. + * + * At the end of this function we should have this program's concept + * of the devid-keyed nvlist matching what is in the ondisk form which + * is ready to be written out. + * If we can't do this, then we return -1. + */ + curnode = di_drv_first_node("ssd", devinfo_root); + if (curnode != DI_NODE_NIL) + rv1 = mpxio_nvl_boilerplate(curnode); - *m = '\0'; - m++; + curnode = di_drv_first_node("sd", devinfo_root); + if (curnode != DI_NODE_NIL) + rv2 = mpxio_nvl_boilerplate(curnode); - if (client_name_type(p) == CLIENT_TYPE_UNKNOWN) + if (rv1 + rv2 == -2) return (-1); - s_strlcpy(physname, p, physname_len); - s_strlcpy(minorname, m, minorname_len); - logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n", - devname, physname, minorname); return (0); } - -/* - * Map phci based client name to vhci based client name. - * - * phci_name - * phci based client /devices name without the /devices prefix and - * minor name component. - * ex: - * - * (FC) - * for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 - * for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 - * - * (SAS) - * for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0 - * for x86: /pci1000,3060@3/sd@0,0 - * - * vhci_name - * Caller supplied buffer where vhci /devices name will be placed on - * return (without the /devices prefix and minor name component). - * ex: - * - * (FC) - * for sparc: /scsi_vhci/ssd@g2000002037cd9f72 - * for x86: /scsi_vhci/disk@g2000002037cd9f72 - * - * (SAS) - * both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac - * - * vhci_name_len - * Length of the caller supplied vhci_name buffer. - * - * Returns 0 on success, -1 on failure. - */ static int -phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len) +mpxio_nvl_boilerplate(di_node_t curnode) { - sv_iocdata_t ioc; - char *slash, *at; - char vhci_name_buf[MAXPATHLEN]; - char phci_name_buf[MAXPATHLEN]; - char addr_buf[MAXNAMELEN]; - - logdmsg("phci_to_vhci: client = %s\n", phci_name); - - s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN); - - if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI || - (slash = strrchr(phci_name_buf, '/')) == NULL || - ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) && - (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) && - (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) { - logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n", - phci_name); - return (-1); - } + int rv; + char *strdevid; + ddi_devid_t curdevid; + nvlist_t *newnvl; - if (vhci_fd < 0) { - if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) - return (-1); - } + for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { + errno = 0; - *slash = '\0'; + curdevid = NULL; + get_devid(curnode, &curdevid); + if (curdevid == NULL) + /* + * There's no devid registered for this device + * so it's not cool enough to play with us + */ + continue; - at = strchr(slash + 1, '@'); - s_strlcpy(addr_buf, at + 1, MAXNAMELEN); + strdevid = devid_str_encode(curdevid, NULL); + /* does this exist in the on-disk cache? */ + rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl); + if (rv == ENOENT) { + logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid); + /* no, so alloc a new nvl to store it */ + if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to allocate space for " + "a devid property list: %s\n"), + strerror(errno)); + return (-1); + } + } else { + if ((rv != ENOTSUP) && (rv != EINVAL)) + logmsg(MSG_INFO, + "%s exists in ondisknvl, verifying\n", + strdevid); + } - bzero(&ioc, sizeof (sv_iocdata_t)); - ioc.client = vhci_name_buf; - ioc.phci = phci_name_buf; - ioc.addr = addr_buf; + if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to populate devid nvpair " + "for device with devid %s\n"), + strdevid); + devid_str_free(strdevid); + nvlist_free(newnvl); + return (-1); + } - if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) { - logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s " - "failed: %s\n", phci_name, strerror(errno)); - return (-1); + /* Now add newnvl into our cache. */ + errno = 0; + rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to add device (devid %s) " + "to in-kernel nvl: %s (%d)\n"), + strdevid, strerror(rv), rv); + devid_str_free(strdevid); + nvlist_free(newnvl); + return (-1); + } + logmsg(MSG_INFO, + gettext("added device (devid %s) to mapnvl\n\n"), + strdevid); + devid_str_free(strdevid); } - - s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len); - logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name); return (0); } /* - * Map vhci based client name to phci based client name. - * If the client has multiple paths, only one of the paths with which client - * can be accessed is returned. This function does not use SCSI_VHCI ioctls - * as it is called on mpxio disabled paths. + * Operates on a single di_node_t, collecting all the device properties + * that we need. devnvl is allocated by the caller, and we add our nvpairs + * to it if they don't already exist. * - * vhci_name - * vhci based client /devices name without the /devices prefix and - * minor name component. - * ex: - * sparc: /scsi_vhci/ssd@g2000002037cd9f72 - * x86: /scsi_vhci/disk@g2000002037cd9f72 - * - * phci_name - * Caller supplied buffer where phci /devices name will be placed on - * return (without the /devices prefix and minor name component). - * ex: - * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 - * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 - * - * phci_name_len - * Length of the caller supplied phci_name buffer. - * - * minor - * The slice of the disk of interest. - * - * Returns 0 on success, -1 on failure. + * We are _only_ interested in devices which have a devid. We pull in + * devices even when they're excluded via stmsboot -D (driver), because + * we don't want to miss out on any devid data that might be handy later. */ static int -vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len, - char *minor) +popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid) { - di_node_t node = DI_NODE_NIL; - char *vhci_guid, *devfspath; - char phci_guid[MAXPATHLEN]; - char root_guid[MAXPATHLEN]; - char root_phys[MAXPATHLEN]; - char root_minor[MAXPATHLEN]; - char root_path[MAXPATHLEN]; - char *node_name; - FILE *mntfp; - struct mnttab mntpref, rootmnt; - - logdmsg("vhci_to_phci: client = %s\n", vhci_name); - - bzero(&mntpref, sizeof (mntpref)); - mntpref.mnt_mountp = "/"; - - if (!(mntfp = fopen(MNTTAB, "r"))) { - logdmsg("vhci_to_phci: can't open %s\n", MNTTAB); + char *path = NULL; + char *curpath = NULL; + char *devfspath = NULL; + int scsivhciparent = 0; + int rv = 0; + boolean_t mpxenp = B_FALSE; + + errno = 0; + devfspath = di_devfs_path(thisnode); + if (devfspath == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to determine devfs path for node: %s\n"), + strerror(errno)); return (-1); } - if (getmntany(mntfp, &rootmnt, &mntpref)) { - logdmsg("vhci_to_phci: can't find / in %s\n", MNTTAB); + /* Add a convenient devfspath to devid inverse map */ + if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to add device path %s with devid " + "%s to mapnvl\n"), devfspath, strdevid); return (-1); } + if (strncmp(di_driver_name(di_parent_node(thisnode)), + "scsi_vhci", 9) == 0) { + scsivhciparent = 1; + if (!mpxenabled) + mpxenabled++; + + rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp); + if (rv || (mpxenp == B_FALSE)) { + rv = nvlist_add_boolean_value(devnvl, + NVL_MPXEN, B_TRUE); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to add property %s " + "(set to B_TRUE) for device %s: " + "%s (%d)\n"), + NVL_MPXEN, devfspath, + strerror(rv), rv); + return (-1); + } + logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n"); + } + } else { + /* turn _off_ the flag if it was enabled */ + rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to add property %s " + "(set to B_FALSE) for device %s: %s (%d)\n"), + NVL_MPXEN, devfspath, + strerror(rv), rv); + return (-1); + } + logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n"); + } - (void) fclose(mntfp); - - if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) { - logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n", - vhci_name); + rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath); + if (rv) { + logmsg(MSG_ERROR, + gettext("Unable to add physical device path (%s) " + "property to nvl\n")); return (-1); } - - if ((vhci_guid = strrchr(vhci_name, '@')) == NULL || - *(++vhci_guid) != 'g') { - logerr(gettext("couldn't get guid from %s\n"), vhci_name); + if ((curpath = calloc(1, MAXPATHLEN)) == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate space for current path\n")); return (-1); } - - /* point to guid */ - ++vhci_guid; - - /* - * Get devinfo snapshot and walk all ssd nodes whose parent is fp. - * For each node get the guid and match it with vhci_guid. - */ - if (devinfo_root == DI_NODE_NIL) { - logdmsg("vhci_to_phci: taking devinfo snapshot\n"); - if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE)) - == DI_NODE_NIL) { - logerr(gettext("di_init failed: %s\n"), - strerror(errno)); - return (-1); + curpath = find_link(thisnode); + if (curpath == NULL) { + if (readonlyroot) { + return (0); } - logdmsg("vhci_to_phci: done taking devinfo snapshot\n"); + logmsg(MSG_ERROR, + gettext("Unable to determine device path for node %s\n"), + devfspath); + return (-1); } - if (strncmp(rootmnt.mnt_special, SLASH_DEVICES, - sizeof (SLASH_DEVICES)-1)) - (void) snprintf(root_path, sizeof (root_path), "/devices%s", - rootmnt.mnt_special); - else - (void) strcpy(root_path, rootmnt.mnt_special); + rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path); - /* - * remove the /devices and minor components to call get_guid() - * if we can't get the guid, drop through to the regular processing. - */ - if ((get_physname_minor(root_path, root_phys, sizeof (root_phys), - root_minor, sizeof (root_minor)) || - (get_guid(root_phys, root_guid, sizeof (root_guid), 0, - node) != 0))) { - logdmsg("vhci_to_phci: can't get_guid for / (%s)\n", - rootmnt.mnt_special); - (void) strcpy(root_guid, ""); - } + if (path == NULL && scsivhciparent) + (void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath); - /* - * We check the guid of the root device against the vhci guid so we - * can return a preferred path. - */ - if ((strcmp(root_guid, vhci_guid) == 0) && - (canread(root_phys, minor))) { - s_strlcpy(phci_name, root_phys, phci_name_len); - logdmsg("vhci_to_phci: %s maps to %s preferred path\n", - vhci_name, phci_name); - return (0); + if (!scsivhciparent) { + (void) nvlist_add_string(devnvl, NVL_PATH, curpath); + path = curpath; } /* - * When we finally get a unified "sd" driver for all - * architectures that Solaris runs on, we can remove this - * first loop around for "ssd" + * This next block provides the path to devid inverse mapping + * that other functions require */ - for (node = di_drv_first_node("ssd", devinfo_root); - node != DI_NODE_NIL; node = di_drv_next_node(node)) { - - if ((node_name = di_node_name(node)) == NULL) - continue; - - if ((strcmp(node_name, "disk") != 0) && - (strcmp(node_name, "sd") != 0) && - (strcmp(node_name, "ssd") != 0)) - continue; - - if (di_parent_node(node) == DI_NODE_NIL) - continue; - - if ((devfspath = di_devfs_path(node)) == NULL) - continue; - - /* - * Don't set no_delay_flag to have get_guid() fail on - * standby paths of T3. So we'll find the preferred paths. - */ - if (get_guid(devfspath, phci_guid, - sizeof (phci_guid), 0, node) != 0) - continue; - - /* - * If the GUID's match, and we can read data from the path of - * interest, we conclude we have the correct path to use. - */ - if ((strcmp(phci_guid, vhci_guid) == 0) && - (canread(devfspath, minor))) { - s_strlcpy(phci_name, devfspath, phci_name_len); - di_devfs_path_free(devfspath); - logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, - phci_name); - return (0); + if (path != NULL) { + if (nvlist_add_string(mapnvl, path, strdevid) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to add device %s with devid " + "%s to mapnvl\n"), path, strdevid); + return (-1); } - - di_devfs_path_free(devfspath); + logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n", + path, strdevid); + if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to add device %s with devid " + "%s to mapnvl: %s\n"), + curpath, strdevid, strerror(errno)); + return (-1); + } + logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n", + curpath, strdevid); } - - for (node = di_drv_first_node("sd", devinfo_root); - node != DI_NODE_NIL; node = di_drv_next_node(node)) { - - if ((node_name = di_node_name(node)) == NULL) - continue; - - if ((strcmp(node_name, "disk") != 0) && - (strcmp(node_name, "sd") != 0) && - (strcmp(node_name, "ssd") != 0)) - continue; - - if (di_parent_node(node) == DI_NODE_NIL) - continue; - - if ((devfspath = di_devfs_path(node)) == NULL) - continue; - - /* - * Don't set no_delay_flag to have get_guid() fail on - * standby paths of T3. So we'll find the preferred paths. - */ - if (get_guid(devfspath, phci_guid, - sizeof (phci_guid), 0, node) != 0) - continue; - - /* - * If the GUID's match, and we can read data from the path of - * interest, we conclude we have the correct path to use. - */ - if ((strcmp(phci_guid, vhci_guid) == 0) && - (canread(devfspath, minor))) { - s_strlcpy(phci_name, devfspath, phci_name_len); - di_devfs_path_free(devfspath); - logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, - phci_name); - return (0); + if (scsivhciparent) { + if (nvlist_add_string(devnvl, NVL_MPXPATH, curpath) != 0) { + logmsg(MSG_ERROR, + gettext("Unable to add property %s for device " + "%s: %s\n"), + NVL_MPXPATH, devfspath, strerror(errno)); + return (-1); + } else { + logmsg(MSG_INFO, "added curpath (%s) as NVL_MPXPATH " + "to devnvl for devid %s\n", curpath, strdevid); } + } + return (0); +} - di_devfs_path_free(devfspath); +static void +print_mpx_capable(di_node_t curnode) +{ + char *prop; + char *path; + char *aliases = NULL; + + if (cap_N_option) { + aliases = calloc(1, MAXPATHLEN + 1); + if (aliases == NULL) { + logmsg(MSG_ERROR, + gettext("Unable to allocate memory for a device " + "alias list\n")); + return; + } } - logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name); - return (-1); + for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { + if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode, + "initiator-port", &prop) >= 0) { + if ((path = di_devfs_path(curnode)) == NULL) { + logmsg(MSG_INFO, + "Unable to find devfs path for device " + "%s: %s\n", &curnode, strerror(errno)); + continue; + } + if (cap_N_option) { + char *nodename = di_node_name(curnode); + /* nodename is never going to be null */ + if (strstr(aliases, nodename) == NULL) + /* haven't seen this nodename before */ + (void) snprintf(aliases, + MAXPATHLEN + 1, "%s|%s", + ((aliases != NULL) ? aliases : ""), + nodename); + } else + (void) printf("%s\n", path); + } + } + if (cap_N_option) + (void) printf("%s\n", aliases); } -/* - * Map vhci based client name to phci based client name. - * If the client has multiple paths, only one of the paths with which client - * can be accessed is returned. - * This function uses SCSI_VHCI ioctls to get the phci paths - * - * vhci_name - * vhci based client /devices name without the /devices prefix and - * minor name component. - * ex: - * sparc: /scsi_vhci/ssd@g2000002037cd9f72 - * x86: /scsi_vhci/disk@g2000002037cd9f72 - * - * phci_name - * Caller supplied buffer where phci /devices name will be placed on - * return (without the /devices prefix and minor name component). - * ex: - * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 - * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 - * - * phci_name_len - * Length of the caller supplied phci_name buffer. - * - * Returns 0 on success, -1 on failure. - */ - static int -vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len) +link_cb(di_devlink_t devlink, void *arg) { - sv_iocdata_t ioc; - uint_t npaths; - char *node_name, *at; - char vhci_name_buf[MAXPATHLEN]; - int ret; - sv_path_info_t *pi; - - logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name); + const char *result; - if (vhci_fd < 0) { - if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) - return (-1); + result = di_devlink_path(devlink); + if (result == NULL) { + arg = (void *)"(null)"; + } else { + (void) strlcpy(arg, result, strlen(result)); } + logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n", + ((result != NULL) ? result : "(null)")); + return (DI_WALK_CONTINUE); +} - (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN); - - /* first get the number paths */ - bzero(&ioc, sizeof (sv_iocdata_t)); - ioc.client = vhci_name_buf; - ioc.ret_elem = &npaths; - if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, - &ioc)) != 0 || npaths == 0) { - logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " - "failed: %s\n", vhci_name, - ret?strerror(errno):"got 0 paths"); - return (-1); +static char * +find_link(di_node_t cnode) +{ + di_minor_t devminor = DI_MINOR_NIL; + di_devlink_handle_t hdl; + char *devfspath = NULL; + char *minorpath = NULL; + char *linkname = NULL; + char *cbresult = NULL; + + devfspath = di_devfs_path(cnode); + if (cnode == DI_NODE_NIL) { + logmsg(MSG_ERROR, + gettext("find_ctrl must be called with non-null " + "di_node_t\n")); + return (NULL); } + logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath); - /* now allocate memory for the path information and get all paths */ - bzero(&ioc, sizeof (sv_iocdata_t)); - ioc.client = vhci_name_buf; - ioc.buf_elem = npaths; - ioc.ret_elem = &npaths; - if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths, - sizeof (sv_path_info_t))) == NULL) - return (-1); - if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, - &ioc)) != 0 || npaths == 0) { - logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " - "failed: %s\n", vhci_name, - ret?strerror(errno):"got 0 paths"); - goto out; + if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) || + ((minorpath = calloc(1, MAXPATHLEN)) == NULL) || + ((linkname = calloc(1, MAXPATHLEN)) == NULL)) { + logmsg(MSG_ERROR, "unable to allocate space for dev link\n"); + return (NULL); } - if (ioc.buf_elem < npaths) - npaths = ioc.buf_elem; - if ((node_name = strrchr(vhci_name_buf, '/')) == NULL || - (at = strchr(node_name, '@')) == NULL) - goto out; - - node_name++; - *at = '\0'; + devminor = di_minor_next(cnode, devminor); + hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK); + if (hdl == NULL) { + logmsg((readonlyroot ? MSG_INFO : MSG_ERROR), + gettext("unable to take devlink snapshot: %s\n"), + strerror(errno)); + return (NULL); + } - logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name); -#ifndef sparc - /* - * We need to use a libdevinfo call to get this info - * in an architecturally-neutral fashion. Phase-II for sure! - */ - node_name = "sd"; -#endif + linkname = "^dsk/"; + (void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath); - /* - * return the first online paths as non-online paths may - * not be accessible in the target environment. - */ - pi = (sv_path_info_t *)ioc.ret_buf; - while (npaths--) { - if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) { - (void) snprintf(phci_name, phci_name_len, "%s/%s@%s", - pi->device.ret_phci, node_name, - pi->ret_addr); - logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n", - vhci_name, phci_name); - free(ioc.ret_buf); - return (0); - } - pi++; + errno = 0; + if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK, + (void *)cbresult, link_cb) < 0) { + logmsg(MSG_ERROR, + gettext("Unable to walk devlink snapshot for %s: %s\n"), + minorpath, strerror(errno)); + return (NULL); } -out: - logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n", - vhci_name); - free(ioc.ret_buf); - return (-1); - + if (di_devlink_fini(&hdl) < 0) { + logmsg(MSG_ERROR, + gettext("Unable to close devlink snapshot: %s\n"), + strerror(errno)); + } + if (strstr(cbresult, "dsk/") == NULL) + return (devfspath); + + bzero(minorpath, MAXPATHLEN); + /* strip off the trailing "s2" */ + bcopy(cbresult, minorpath, strlen(cbresult) - 1); + /* Now strip off the /dev/dsk/ prefix for output flexibility */ + linkname = strrchr(minorpath, '/'); + return (++linkname); } /* - * Map physname from phci name space to vhci name space or vice-versa - * - * physname - * phci or vhci based client /devices name without the /devices prefix and - * minor name component. - * - * new_physname - * Caller supplied buffer where the mapped physical name is stored on - * return (without the /devices prefix and minor name component). - * - * len - * Length of the caller supplied new_physname buffer. - * - * minor - * The slice of the disk of interest. - * - * Returns 0 on success, -1 on failure. + * handle case where device has been probed but its target driver is not + * attached so enumeration has not quite finished. Opening the /devices + * pathname will force the kernel to finish the enumeration process and + * let us get the data we need. */ -static int -map_physname(char *physname, char *new_physname, size_t len, char *minor) -{ - int type; - int rv; - - type = client_name_type(physname); - logdmsg("map_physname: type (%d) physname = %s\n", - type, physname); - - if (type == CLIENT_TYPE_VHCI) - rv = vhci_to_phci(physname, new_physname, len, minor); - else if (type == CLIENT_TYPE_PHCI) - rv = phci_to_vhci(physname, new_physname, len); - else - rv = -1; - - logdmsg("map_physname: returning %d\n", rv); - return (rv); -} - -static int -devlink_callback(di_devlink_t devlink, void *argptr) +static void +get_devid(di_node_t node, ddi_devid_t *thisdevid) { - const char *link; - struct devlink_cbarg *argp = argptr; + int fd; + char realpath[MAXPATHLEN]; + char *openpath = di_devfs_path(node); + + errno = 0; + bzero(realpath, MAXPATHLEN); + if (strstr(openpath, "/devices") == NULL) { + (void) snprintf(realpath, MAXPATHLEN, + "/devices%s:c,raw", openpath); + fd = open(realpath, O_RDONLY|O_NDELAY); + } else { + fd = open(openpath, O_RDONLY|O_NDELAY); + } - if ((link = di_devlink_path(devlink)) != NULL) { - s_strlcpy(argp->devlink, link, argp->len); - return (DI_WALK_TERMINATE); + if (fd < 0) { + logmsg(MSG_INFO, "Unable to open path %s: %s\n", + openpath, strerror(errno)); + return; } - return (DI_WALK_CONTINUE); + if (devid_get(fd, thisdevid) != 0) { + logmsg(MSG_INFO, + "'%s' node (%s) without a devid registered\n", + di_driver_name(node), di_devfs_path(node)); + } + (void) close(fd); } -/* - * Lookup the /dev link corresponding to physname and minorname. - * - * physname client /devices path without the /devices prefix and minor - * name component. - * minorname client minor name. - * devlink caller supplied buffer where the /dev link is placed on return. - * len caller supplied devlink buffer length - * - * Returns 0 on success, -1 on failure. - */ static int -lookup_devlink(char *physname, char *minorname, char *devlink, size_t len) +print_bootpath() { - char buf[MAXPATHLEN]; - struct devlink_cbarg arg; - - if (devlink_hdl == NULL) { - logdmsg("lookup_devlink: taking devlink snapshot\n"); - if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { - logerr(gettext("di_devlink_init failed: %s\n"), - strerror(errno)); - clean_exit(1); - } - } - - *devlink = '\0'; - (void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname); - arg.devlink = devlink; - arg.len = len; - if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg, - devlink_callback) != 0) { - logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n", - buf, strerror(errno)); - return (-1); - } + char *bootprop = NULL; - if (*devlink == '\0') { - logdmsg("lookup_devlink: failed to lookup devlink for %s\n", - buf); - return (-1); + if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, + "bootpath", &bootprop) >= 0) { + (void) printf("%s\n", bootprop); + return (0); + } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, + "boot-path", &bootprop) >= 0) { + (void) printf("%s\n", bootprop); + return (0); + } else { + (void) printf("ERROR: no bootpath/boot-path property found\n"); + return (ENOENT); } - - logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname, - minorname, devlink); - return (0); } /* - * open infile for reading and return its file pointer in *fp_in. - * open outfile for writing and return its file pointer in *fp_out. - * - * Returns 0 on success, -1 on failure. + * We only call this routine if we have a scsi_vhci node and must + * determine the actual physical path of its first online client + * path. */ -static int -open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out) +static void +vhci_to_phci(char *devpath, char *physpath) { - FILE *fin = NULL; - FILE *fout = NULL; - struct stat sbuf; - - if ((fin = fopen(infile, "r")) == NULL) { - logerr(gettext("failed to fopen %1$s: %2$s\n"), - infile, strerror(errno)); - goto out; - } + sv_iocdata_t ioc; + sv_path_info_t *pi; + int vhci_fd; + int rv; + uint_t npaths = 0; - if (fstat(fileno(fin), &sbuf) != 0) { - logerr(gettext("fstat failed on %1$s: %2$s\n"), - infile, strerror(errno)); - goto out; - } + vhci_fd = open(VHCI_CTL_NODE, O_RDWR); + if (vhci_fd < 0) + goto failure; - if ((fout = fopen(outfile, "w")) == NULL) { - logerr(gettext("failed to fopen %1$s: %2$s\n"), - outfile, strerror(errno)); - goto out; - } - - if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) { - logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"), - outfile, sbuf.st_mode & 0777, strerror(errno)); - goto out; + bzero(&ioc, sizeof (sv_iocdata_t)); + ioc.client = devpath; + ioc.ret_elem = &npaths; + rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); + if (rv || npaths == 0) { + logmsg(MSG_INFO, + "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, " + "%s (%d)\n", strerror(rv), rv); + goto failure; } - if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) { - logerr(gettext("failed to fchown %1$s to uid %2$d and " - "gid %3$d: %4$s\n"), - outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno)); - goto out; + bzero(&ioc, sizeof (sv_iocdata_t)); + ioc.client = devpath; + ioc.buf_elem = npaths; + ioc.ret_elem = &npaths; + if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t))) + == NULL) + goto failure; + rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); + if (rv || npaths == 0) { + logmsg(MSG_INFO, + "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) " + "failed, %s (%d)\n", strerror(rv), rv); + goto failure; } - *fp_in = fin; - *fp_out = fout; - return (0); + if (ioc.buf_elem < npaths) + npaths = ioc.buf_elem; -out: - if (fin != NULL) - (void) fclose(fin); - if (fout != NULL) - (void) fclose(fout); - return (-1); -} + pi = (sv_path_info_t *)ioc.ret_buf; + while (npaths--) { + if (pi->ret_state == MDI_PATHINFO_STATE_ONLINE) { + char nodename[4]; -/* - * If the devname is a phci based name and not open-able, map it to vhci - * based name. If the devname is a vhci based name and not open-able, map it - * to phci based name. - * - * devname either a /dev link or /devices name to client device - * new_devname caller supplied buffer where the mapped device name is - * placed on return. - * len caller supplied new_devname buffer length - * devlink_flag pass 1 if requesting the /dev link to the mapped device. - * pass 0 if requesting the /devices name of the mapped device. - * - * Returns 0 on success, -1 on failure. - */ -static int -map_devname(char *devname, char *new_devname, size_t len, int devlink_flag) -{ - char physname[MAXPATHLEN]; - char minor[MAXNAMELEN]; - char new_physname[MAXPATHLEN]; - - logdmsg("map_devname: checking devname %s\n", devname); - if ((get_physname_minor(devname, physname, sizeof (physname), - minor, sizeof (minor)) == 0) && - (canopen(devname) == 0) && - (map_physname(physname, new_physname, - sizeof (new_physname), minor) == 0)) { - - logdmsg("map_devname: now looking up devlink\n"); - - if (devlink_flag) { - if (lookup_devlink(new_physname, minor, new_devname, - len) == 0) - return (0); - } else { - (void) snprintf(new_devname, len, "/devices%s:%s", - new_physname, minor); - return (0); + bzero(nodename, 4); + /* A hack, but nicer than a platform-specific ifdef */ + if (strstr(devpath, "ssd") != NULL) { + (void) snprintf(nodename, 4, "ssd"); + } else { + (void) snprintf(nodename, 4, "sd"); + } + (void) snprintf(physpath, MAXPATHLEN, "%s/%s@%s", + pi->device.ret_phci, nodename, pi->ret_addr); + free(ioc.ret_buf); + return; } + pi++; } - logdmsg("map_devname: failed to find mapping for %s\n", devname); - return (-1); +failure: + (void) snprintf(physpath, MAXPATHLEN, "NOT_MAPPED"); } /* - * If the devname is a vhci based name and open-able, map it to phci - * based name. - * - * devname either a /dev link or /devices name to client device - * new_devname caller supplied buffer where the mapped device name without - * /devices prefix is placed on return. - * len caller supplied new_devname buffer length - */ -static int -map_openable_vhciname(char *devname, char *new_devname, size_t len) -{ - char physname[MAXPATHLEN]; - char minor[MAXNAMELEN]; - char new_physname[MAXPATHLEN]; - - if (get_physname_minor(devname, physname, sizeof (physname), - minor, sizeof (minor)) == 0 && - canopen(devname) == 1 && - client_name_type(physname) == CLIENT_TYPE_VHCI && - vhci_to_phci_by_ioctl(physname, new_physname, - sizeof (new_physname)) == 0) { - (void) snprintf(new_devname, len, "%s:%s", - new_physname, minor); - return (0); - } - - return (-1); -} -/* - * Make a new /etc/vfstab: - * Read vfstab_in, convert the device name entries to appropriate vhci or phci - * based names, and write to vfstab_out. Only device names whose physical - * paths are either phci or vhci based names and not open-able are considered - * for conversion. Open-able device name entries are not converted as it - * means that the device is already accessible; hence no need to convert. + * Write /etc/vfstab to /etc/vfstab.new, with any remapped device + * names substituted. * * Returns: - * 0 successful but vfstab_out contents are the same as vfstab_in - * 1 successful and vfstab_out changed from vfstab_in + * 0 successful operation * -1 failed */ static int -update_vfstab(char *vfstab_in, char *vfstab_out) +update_vfstab() { - FILE *fp_in, *fp_out; + FILE *fdin, *fdout; char *buf, *tmpbuf; - char *vfs_cache[2]; - int idx = 0, count = 0; - int rv = -1; - int vfstab_updated = 0; - int i; + char fname[MAXPATHLEN]; + int rv = -1, rval = -1; char cdev[MAXPATHLEN]; char bdev[MAXPATHLEN]; char mntpt[MAXPATHLEN]; char fstype[512]; char fsckpass[512]; char mntboot[512]; - char mntopt[MAX_MNTOPT_STR]; - char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN]; - char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN]; - char new_physname[MAXPATHLEN]; - char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN]; + char mntopt[MAXPATHLEN]; char fmt[80]; - - if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0) + char *prefixt = NULL; + char *curdev = NULL; + char *thisdevid = NULL; + char *slice = NULL; + nvlist_t *thisdev; + boolean_t devmpx = B_FALSE; + + buf = calloc(1, MAXPATHLEN); + tmpbuf = calloc(1, MAXPATHLEN); + if (buf == NULL || tmpbuf == NULL) return (-1); - /* - * Read one line at time from vfstab_in. If no conversion is needed - * for the line simply write the line to vfstab_out. If conversion is - * needed, first write the existing line as a comment to vfstab_out - * and then write the converted line. - * - * To avoid commented entries piling up in vfstab in case if the - * user runs stmsboot multiple times to switch on and off from mpxio, - * add the commented line only if not already there. To do this - * cache the last two vfstab lines processed and add the commented - * entry only if it is not found in the cache. We only need to cache - * the last two lines because a device can have at most two names - - * one mpxio and one non-mpxio name. Therefore for any device name - * entry we at most add two comments - one with mpxio name and one - * with non-mpxio name - no matter how many times stmsboot is run. - */ - buf = (char *)s_malloc(VFS_LINE_MAX); - tmpbuf = (char *)s_malloc(VFS_LINE_MAX); - vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX); - vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX); + (void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new"); + + fdin = fopen("/etc/vfstab", "r"); + fdout = fopen(fname, "w+"); + if (fdin == NULL || fdout == NULL) { + logmsg(MSG_INFO, "Unable to open vfstab or create a backup " + "vfstab %s\n"); + return (-1); + } (void) snprintf(fmt, sizeof (fmt), "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); - while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) { - if (strlen(buf) == (VFS_LINE_MAX - 1) && - buf[VFS_LINE_MAX-2] != '\n') { - logerr(gettext("%1$s line size too long, " + while (fgets(buf, MAXPATHLEN, fdin) != NULL) { + if (strlen(buf) == (MAXPATHLEN - 1) && + buf[MAXPATHLEN-2] != '\n') { + logmsg(MSG_ERROR, + gettext("/etc/vfstab line length too long, " "exceeded %2$d: \"%3$s\"\n"), - VFSTAB, VFS_LINE_MAX - 2, buf); + MAXPATHLEN - 2, buf); goto out; } - /* LINTED - format specifier */ - if ((sscanf(buf, fmt, bdev, cdev, mntpt, - fstype, fsckpass, mntboot, mntopt) != 7) || - (bdev[0] == '#') || - (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev), - bdev_minor, sizeof (bdev_minor)) != 0) || - - (strcmp(fstype, "swap") != 0 && - ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev), - cdev_minor, sizeof (cdev_minor)) != 0) || - (strcmp(phys_bdev, phys_cdev) != 0))) || - - canopen(bdev) || - (map_physname(phys_bdev, new_physname, - sizeof (new_physname), bdev_minor) != 0) || - (lookup_devlink(new_physname, bdev_minor, new_bdevlink, - sizeof (new_bdevlink)) != 0) || - - (strcmp(fstype, "swap") != 0 && - (lookup_devlink(new_physname, cdev_minor, new_cdevlink, - sizeof (new_cdevlink)) != 0))) { - - /* cache the last two entries */ - (void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX); - idx = (idx == 0) ? 1 : 0; - if (count < 2) - count++; - - if (fputs(buf, fp_out) == EOF) { - logerr(gettext("fputs \"%1$s\" to %2$s " - "failed: %3$s\n"), - buf, vfstab_out, strerror(errno)); - goto out; - } - - } else { - /* - * comment the entry in vfstab only if it is not - * already in the cache. - */ - if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI) - (void) snprintf(tmpbuf, VFS_LINE_MAX, - "# mpxio: %s", buf); - else - (void) snprintf(tmpbuf, VFS_LINE_MAX, - "# non-mpxio: %s", buf); - - for (i = 0; i < count; i++) { - if (strcmp(vfs_cache[i], tmpbuf) == 0) - break; - } - - if (i == count) { - if (fputs(tmpbuf, fp_out) == EOF) { - logerr(gettext("fputs \"%1$s\" to %2$s " - "failed: %3$s\n"), tmpbuf, - vfstab_out, strerror(errno)); - goto out; - } - } - - count = 0; - idx = 0; - - if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", - new_bdevlink, - (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev, - mntpt, fstype, fsckpass, mntboot, mntopt) < 0) { - logerr(gettext("fprintf failed to write to " - "%1$s: %2$s\n"), - vfstab_out, strerror(errno)); - goto out; - } - vfstab_updated = 1; - } - } - - rv = vfstab_updated; -out: - (void) fclose(fp_in); - (void) fclose(fp_out); - free(buf); - free(tmpbuf); - free(vfs_cache[0]); - free(vfs_cache[1]); - return (rv); -} - -/* - * if guidmap is 0, list non-STMS to STMS device name mappings for the - * specified controller. - * if guidmap is 1, list non-STMS to GUID mappings for the specified controller. - * If controller is -1 list mappings for all controllers. - * - * Returns 0 on success, -1 on failure. - */ -static int -list_mappings(int controller, int guidmap) -{ - int cnum, len, mapped; - int header = 1; - char *p1, *p2; - DIR *dirp; - struct dirent *direntry; - char devname[MAXPATHLEN]; - char physname[MAXPATHLEN]; - char new_devname[MAXPATHLEN]; - char new_physname[MAXPATHLEN]; - char guid[MAXPATHLEN]; - char minor[MAXNAMELEN]; - - if ((dirp = opendir("/dev/rdsk")) == NULL) - return (-1); - - while ((direntry = readdir(dirp)) != NULL) { - if (strcmp(direntry->d_name, ".") == 0 || - strcmp(direntry->d_name, "..") == 0 || - (len = strlen(direntry->d_name)) < 2 || - strcmp(direntry->d_name + len - 2, "s0") != 0 || - sscanf(direntry->d_name, "c%dt", &cnum) != 1 || - (controller != -1 && controller != cnum)) - continue; - - (void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s", - direntry->d_name); + prefixt = NULL; + curdev = NULL; + slice = NULL; + thisdevid = NULL; + thisdev = NULL; - if (get_physname_minor(devname, physname, sizeof (physname), - minor, sizeof (minor)) != 0 || - client_name_type(physname) != CLIENT_TYPE_PHCI) { - logdmsg("list_mappings: continuing\n"); - continue; - } + /* LINTED - variable format specifier */ + rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass, + mntboot, mntopt); /* - * First try phci_to_vhci() mapping. It will work if the - * device is under MPxIO control. If the device is not under - * MPxIO, phci_to_vhci() will fail in which case try to lookup - * if an old mapping exists using guid lookup. + * Walk through the lines in the input file (/etc/vfstab), + * skipping anything which is _not_ a COGD (common or garden + * disk), ie all the /devices, /system, /dev/md, /dev/vx and + * /dev/zvol and so forth. */ - mapped = 1; - if (phci_to_vhci(physname, new_physname, - sizeof (new_physname)) != 0) { - if (get_guid(physname, guid, sizeof (guid), 1, - DI_NODE_NIL) == 0) - (void) snprintf(new_physname, MAXPATHLEN, - "/scsi_vhci/%s%s", DISK_AT_G, guid); - else - mapped = 0; - } - - if (mapped == 0) - continue; - - /* strip the slice number part */ - devname[strlen(devname) - 2] = '\0'; - - if (guidmap == 0) { - if (lookup_devlink(new_physname, minor, - new_devname, sizeof (new_devname)) != 0) + if ((rv == 7) && (bdev[0] == '/') && + (strstr(bdev, "/dev/dsk"))) { + slice = strrchr(bdev, 's'); + /* take a copy, strip off /dev/dsk/ */ + prefixt = strrchr(bdev, 'c'); + prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0'; + slice++; /* advance past the s */ + rval = nvlist_lookup_string(mapnvl, prefixt, + &thisdevid); + if (rval) { + /* Whoa, where did this device go?! */ + logmsg(MSG_INFO, + "error looking up device %s\n", prefixt); + /* Comment-out this line in the new version */ + (void) snprintf(tmpbuf, MAXPATHLEN, + "# DEVICE NOT FOUND %s", buf); + (void) fprintf(fdout, "%s", tmpbuf); continue; - - /* strip the slice number part */ - new_devname[strlen(new_devname) - 2] = '\0'; - - if (header) { - (void) printf( - gettext("non-STMS device name\t\t\t" - "STMS device name\n" - "------------------------------------------" - "------------------------\n")); - header = 0; - } - (void) printf("%s\t\t%s\n", devname, new_devname); - } else { - /* extract guid part */ - /* we should be using a getguid() call instead */ - if ((p1 = strstr(new_physname, "@")) - == NULL) { - logdmsg("invalid vhci: %s\n", new_physname); - continue; - } - - logdmsg("\tp1 = %s\n", p1); - - p1 += 2; /* "@" + [nwg] */ - if ((p2 = strrchr(p1, ':')) != NULL) - *p2 = '\0'; - - if (header) { - (void) printf( - gettext("non-STMS device name\t\t\tGUID\n" - "------------------------------------------" - "------------------------\n")); - header = 0; + } else { + /* The device exists in our mapnvl */ + (void) nvlist_lookup_nvlist(mapnvl, thisdevid, + &thisdev); + (void) nvlist_lookup_boolean_value(thisdev, + NVL_MPXEN, &devmpx); + (void) nvlist_lookup_string(thisdev, + ((devmpx == B_TRUE) + ? NVL_MPXPATH : NVL_PATH), + &curdev); } - (void) printf("%s\t\t%s\n", devname, p1); - } - } - - (void) closedir(dirp); - return (0); -} - -/* - * Check if the file can be opened. - * - * Return 1 if the file can be opened, 0 otherwise. - */ -static int -canopen(char *filename) -{ - int fd; - - if ((fd = open(filename, O_RDONLY)) == -1) - return (0); - - logdmsg("canopen: was able to open %s\n", filename); - (void) close(fd); - return (1); -} - - -/* - * This function traverses the device tree looking for nodes - * which have "drivername" as a property. We return a list of - * these nodes, without duplicate entries. - * Since there can be many different pci/pcie devices that all - * share the same driver but which have different pci vid/did - * combinations, we have to be smart about returning only those - * pci vid/dids which have the "sas-*" property unless the - * drivername is "fp", in which case we're searching for "node-wwn" - */ -static void -list_nodes(char *drivername) -{ - di_node_t devroot = DI_NODE_NIL; - di_node_t thisnode = DI_NODE_NIL; - char *aliaslist; - char *iitype = NULL; /* the "initiator-interconnect-type" property */ - int *intprop = NULL; - int i = 1; /* fencepost */ - int irval = 0; - int crval = 0; - - /* - * Since the "fp" driver enumerates with its own name, - * we can special-case its handling. - */ - if (strcmp(drvname, "fp") == 0) { - (void) fprintf(stdout, "fp\n"); - } else { - - if ((devroot = di_init("/", DINFOCPYALL | DINFOFORCE)) - == DI_NODE_NIL) { - logerr(gettext("list_nodes: di_init failed: " - "%s\n"), strerror(errno)); } - if ((thisnode = di_drv_first_node(drivername, devroot)) - != NULL) { - logdmsg("list_nodes: searching for property " - "%s\n", drvprop); - - aliaslist = s_malloc(1024 * sizeof (char)); - bzero(aliaslist, 1024); - while (thisnode != DI_NODE_NIL) { - logdmsg("devfs-name %s driver-name %s " - "node-name %s\n", - di_devfs_path(thisnode), - di_driver_name(thisnode), - di_node_name(thisnode)); - - /* We check the child node for drvprop */ - irval = di_prop_lookup_ints(DDI_DEV_T_ANY, - di_child_node(thisnode), drvprop, &intprop); - /* and this node for the correct initiator type */ - crval = di_prop_lookup_strings(DDI_DEV_T_ANY, - thisnode, "initiator-interconnect-type", &iitype); - - /* - * examine the return codes from di_prop_lookup*() - * functions to guard against library errors - */ - if ((irval > -1) || ((crval > -1) && - (strncmp(iitype, "SATA", 4) == 0))) { - - if (strstr(aliaslist, - di_node_name(thisnode)) == (char *)NULL) { - char *nname; - - nname = di_node_name(thisnode); - - if (i) { - (void) snprintf(aliaslist, - strlen(nname) + 1, "%s", nname); - --i; - } else { - if (strstr(aliaslist, - di_node_name(thisnode)) == - (char *)NULL) { - /* add 2 for the n-1 + "|" */ - (void) snprintf(aliaslist, - strlen(nname) + 2 + - strlen(aliaslist), - "%s|%s", aliaslist, - nname); - } - } - } + if ((prefixt != NULL) && (curdev != NULL) && + (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) { + /* Mapping change for this device */ + if (strcmp(fstype, "swap") == 0) { + (void) snprintf(tmpbuf, MAXPATHLEN, + "/dev/dsk/%ss%s\t-\t-\tswap\t" + "%s\t%s\t%s\n", + curdev, slice, fsckpass, mntboot, mntopt); } else { - logdmsg("unable to lookup property %s " - "for node %s. Error %d: %s\n", - drvprop, di_devfs_path(thisnode), - errno, strerror(errno)); + (void) snprintf(tmpbuf, MAXPATHLEN, + "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t" + "%s\t%s\t%s\t%s\t%s\n", + curdev, slice, curdev, slice, + mntpt, fstype, fsckpass, mntboot, mntopt); } - thisnode = di_drv_next_node(thisnode); - } - (void) fprintf(stdout, "%s\n", aliaslist); + errno = 0; + (void) fprintf(fdout, "%s", tmpbuf); + } else { + (void) fprintf(fdout, "%s", buf); } - di_fini(devroot); - } -} - -static void -logerr(char *msg, ...) -{ - va_list ap; - - (void) fprintf(stderr, "%s: ", stmsboot); - va_start(ap, msg); - /* LINTED - format specifier */ - (void) vfprintf(stderr, msg, ap); - va_end(ap); -} - -/* log debug message */ -static void -logdmsg(char *msg, ...) -{ - va_list ap; - - if (debug) { - va_start(ap, msg); - /* LINTED - format specifier */ - (void) vprintf(msg, ap); - va_end(ap); - } -} - -static void * -s_malloc(const size_t size) -{ - void *rp; - - if ((rp = malloc(size)) == NULL) { - logerr(gettext("malloc failed to allocate %d bytes\n"), size); - clean_exit(1); - } - return (rp); -} - -static char * -s_strdup(const char *ptr) -{ - void *rp; - - if ((rp = strdup(ptr)) == NULL) { - logerr(gettext("strdup failed to dup %s\n"), ptr); - clean_exit(1); - } - return (rp); -} - -static void -s_strlcpy(char *dst, const char *src, size_t dstsize) -{ - int n; - - if ((n = strlcpy(dst, src, dstsize)) >= dstsize) { - logerr(gettext("strlcpy: destination buffer size is %1$d " - "bytes, need to at least %2$d bytes\n"), dstsize, n + 1); - clean_exit(1); - } -} - -static void -clean_exit(int status) -{ - if (devinfo_root != DI_NODE_NIL) - di_fini(devinfo_root); - - if (devlink_hdl != NULL) - (void) di_devlink_fini(&devlink_hdl); - - if (vhci_fd != -1) - (void) close(vhci_fd); - - exit(status); -} - -/* - * Attempt to read some data from the specified slice from the device. - */ -static int -canread(char *physname, char *minor) -{ - char devname[MAXPATHLEN]; - int fd, rv = 0; - char tbuf[512]; - - (void) snprintf(devname, MAXPATHLEN, "/devices%s:%s", physname, minor); - if ((fd = open(devname, O_RDONLY)) == -1) { - logdmsg("canread: failed to open %s: %s\n", devname, - strerror(errno)); - return (rv); + errno = 0; + if (fflush(fdout) != 0) { + logmsg(MSG_ERROR, + gettext("fprintf failed to write to %s: %s (%d)\n"), + fname, strerror(errno), errno); + goto out; + } } - - if (read(fd, tbuf, sizeof (tbuf)) < 0) - logdmsg("canread: failed to read %s: %s\n", devname, - strerror(errno)); - else - rv = 1; - - (void) close(fd); - return (rv); +out: + (void) fclose(fdin); + (void) fclose(fdout); + free(buf); + free(tmpbuf); + return (errno); } |