Index: VERSION
===================================================================
RCS file: /home/cvsroot/scsidev/VERSION,v
retrieving revision 1.8.2.7
retrieving revision 1.8.2.9
diff -u -r1.8.2.7 -r1.8.2.9
--- VERSION	29 Jun 2003 20:43:05 -0000	1.8.2.7
+++ VERSION	8 Aug 2003 08:06:55 -0000	1.8.2.9
@@ -1 +1 @@
-2.29
+2.30
Index: scsidev.8
===================================================================
RCS file: /home/cvsroot/scsidev/scsidev.8,v
retrieving revision 1.11.2.4
retrieving revision 1.11.2.7
diff -u -r1.11.2.4 -r1.11.2.7
--- scsidev.8	4 Oct 2002 12:20:37 -0000	1.11.2.4
+++ scsidev.8	7 Aug 2003 13:46:10 -0000	1.11.2.7
@@ -1,10 +1,11 @@
 #
-# $Id: scsidev.8,v 1.11.2.4 2002/10/04 12:20:37 garloff Exp $
+# $Id: scsidev.8,v 1.11.2.7 2003/08/07 13:46:10 garloff Exp $
 #
 .\" -*- nroff -*-
-.TH SCSIDEV 8 "October 2002" "Version 2.28"
+.TH SCSIDEV 8 "June 2003" "Version 2.29"
 .SH NAME
-scsidev \- populate /dev/scsi with device names that are independent of reconfiguration.
+scsidev \- populate /dev/scsi with device names that are persistent
+against SCSI configuration changes.
 .SH SYNOPSIS
 .B scsidev
 [
@@ -38,6 +39,9 @@
 .B \-e
 ]
 [
+.B \-o
+]
+[
 .B \-s
 ]
 [
@@ -176,8 +180,16 @@
 unit) chraracters instead of hcil (host, channel, scsi Id, scsi Lun) to
 build the device name.
 .TP
+.I \-o
+Instructs 
+.B scsidev 
+to use scd instead of sr for the old names of CD-ROM devices 
+(relevant for symlink mode).
+.TP
 .I \-s
-Tells scsidev to print out the device serial numbers of all detected
+Tells 
+.B scsidev
+ to print out the device serial numbers of all detected
 devices on the system. This string can be useful for forming aliases.
 If supported, also the WWID is printed.
 .TP
@@ -274,7 +286,16 @@
 to match, so you may omit the version number.)
 .PP
 Note that the specifiers which take string arguments can be quoted
-if the string contains whitespace.
+if the string contains whitespace. 
+.PP
+For disks, aliases for all partitions will be created (unless partition=
+is specified). The names get a 
+.B -pN 
+suffix (N indicating the number of
+the partition. For tapes (st and osst type), the non-rewinding variant 
+with an 
+.B n
+prepended will be created automatically.
 .SH AUTHOR
 .nf
 .B scsidev
Index: scsidev.c
===================================================================
RCS file: /home/cvsroot/scsidev/scsidev.c,v
retrieving revision 1.28.2.26
retrieving revision 1.28.2.33
diff -u -r1.28.2.26 -r1.28.2.33
--- scsidev.c	30 Jun 2003 11:24:48 -0000	1.28.2.26
+++ scsidev.c	9 Aug 2003 14:57:18 -0000	1.28.2.33
@@ -107,7 +107,7 @@
  *    - Fix Medium Changer (and other device types with spaces) detection 
  *  -> 2.28
  * 
- *   * 2003-01-13: Kurt Garloff <garlof@suse.de>
+ *   * 2003-01-13: Kurt Garloff <garloff@suse.de>
  *     - Handle EOF return value from sscanf()
  *     - Handle empty names gracefully (for zfcp, from Bernd Kaindl)
  *   * 2003-03-07: Kurt Garloff <garloff@suse.de>
@@ -116,6 +116,36 @@
  *   * 2003-06-29: Kurt Garloff <garloff@suse.de>
  *     - Fix fd leak (/proc/partitions)
  *   -> 2.29
+ *
+ *   * 2003-07-25: Kurt Garloff <garloff@suse.de>
+ *     - Documentation: -pX and n automatically created for aliases
+ *       (Thx to Russel Shaw for suggesting it.)
+ *
+ *   * 2003-03-03: Evan Felix <evan.felix@pnl.gov>:
+ *     - Detect HP HSV controllers and grab OS Unit ID.
+ *     - Add Alias support for HSV OS Unit ID.
+ *     - Changed so we search for a matching HCTL pair 
+ *	 so we skip 'ghost' scsi disks which are common with 
+ *	 and HSV failover pair
+ *
+ *   * 2003-08-07: Kurt Garloff <garloff@suse.de>:
+ *     - Optionally support 'scd' instead of 'sr' as old name for
+ *	 CD-ROM devices. Option -o. Relevant in symlink mode.
+ *     - Add safe_strcmp() to avoid crashing in null pointers in
+ *	 name (model, ...) comparisons. Thanks to James Budworth
+ *	 for reporting the error.
+ *
+ *   * 2003-08-08: Kurt Garloff <garloff@suse.de>:
+ *     - Reorder list of modules in trigger_module_loads ():
+ *       In 2.6, we don't attach sg in case another driver can
+ *       be attached. Thus load sg as last module.
+ *     - Fix -o option
+ *     - Use sysfs to gather some information (short hostname,
+ *	 attached HL device -- unfortunately only block devs
+ *	 currently)
+ *     - Fix HSV OS ID gathering: Set to -1 if no HSV.
+ *     - Handle empty model name in get_hsv_os_id().
+ *   -> 2.30
  */
 
 #include <stdio.h>
@@ -136,8 +166,8 @@
 #include <ctype.h>
 
 
-static char rcsid[] ="$Id: scsidev.c,v 1.28.2.26 2003/06/30 11:24:48 garloff Exp $";
-static char *versid = "scsidev " VERSION " 2002-10-04";
+static char rcsid[] ="$Id: scsidev.c,v 1.28.2.33 2003/08/09 14:57:18 garloff Exp $";
+static char *versid = "scsidev " VERSION " 2003-08-04";
 static char *copyright = "Copyright: GNU GPL  (see file COPYING)\n" \
 " (w)  1994--1997 Eric Youngdale <eric@andante.org>\n"\
 "      2000--2003 Kurt Garloff   <garloff@suse.de>";
@@ -162,6 +192,7 @@
 #include <getopt.h>
 
 int use_symlink = 0;
+int use_scd = 0;
 int symlink_alias = 0;
 int filemode = 0600;
 int verbose = 0;
@@ -177,6 +208,7 @@
 int override_link_perm = 1;
 char * no_serial = "No serial number";
 unsigned long long no_wwid = 0;
+int no_hsv_os_id = -1;
 
 #define DEVSCSI "/dev/scsi"
 #define TESTDEV DEVSCSI "/testdev"
@@ -185,6 +217,7 @@
 
 enum devtype_t { NONE=0, SG, SD, SR, ST, OSST, SCH, };
 char* devtp_nm[] = { "", "Generic", "Disk", "Rom", "Tape", "OnStreamTape", "Changer", };
+char* devtp_nm26[] = { "", "sg", "sd", "sr", "st", "osst", "sch", };
 
 /*
  * This program builds entries in /dev/scsi that have names that are tied
@@ -211,10 +244,12 @@
     char * rev;
     char * serial;
     unsigned long long wwid;
+    int  hsv_os_id;
     enum devtype_t devtp;
     char inq_devtp;
     char rmvbl;
     char unsafe;
+    char partition;
     int  hostid;
     int  major;
     int  minor;
@@ -224,12 +259,11 @@
     int  chan;
     int  id;
     int  lun;
-    char partition;
     struct regnames * alias; // TBR
     struct regnames * related;
 } sname;
 
-/* The related pointer is new: It point for disks, tapes and CDRoms to
+/* The related pointer is new: For disks, tapes and CDRoms it points to
  * the corresponding generic sname; for generic entries, it points to
  * ZERO or to a disk/CDrom/tape */
 
@@ -237,6 +271,7 @@
 
 void build_special();
 int inquiry (int, sname *);
+int get_hsv_os_id(int, sname *);
 
 #ifndef OSST_MAJOR
 # define OSST_MAJOR 206
@@ -286,6 +321,14 @@
 	}
 }
 
+enum devtype_t nm26_to_devtp (char* nm)
+{
+	enum devtype_t i;
+	for (i = NONE; i <= SCH; ++i)
+	       if (!strcmp (devtp_nm26[i], nm))
+		       return i;
+	return 0;
+}
 
 /*
  * Used to maintain a list of the nodes that we have seen
@@ -294,9 +337,9 @@
 
 void dumpentry (sname * pnt)
 {
-	printf ("%s (%s): %s %s %s (%s) %Lx\n", pnt->name, pnt->oldname,
+	printf ("%s (%s): %s %s %s (%s) %Lx (%d)\n", pnt->name, pnt->oldname,
 		pnt->manufacturer, pnt->model, pnt->rev, pnt->serial,
-		pnt->wwid);
+		pnt->wwid, pnt->hsv_os_id);
 	printf ("  on %s (%d-%x) \"%s\":\n c%di%dl%d", pnt->shorthostname,
 		pnt->hostnum, pnt->hostid, pnt->hostname, pnt->chan, pnt->id, pnt->lun);
 	if (pnt->devtp == SD && pnt->partition != -1)
@@ -324,13 +367,24 @@
 	return spnt1;
 }
 
+int safe_strcmp (const char* str1, const char* str2)
+{
+	if (str1 == 0 && str2 == 0)
+		return 0;
+	else if (str1 == 0 && str2 != 0)
+		return -256;
+	else if (str1 != 0 && str2 == 0)
+		return 256;
+	else return strcmp (str1, str2);
+}
+
 /// compare two sname entries
 char sname_cmp (sname *sp1, sname *sp2)
 {
     // Host
     if (sp1->hostid != sp2->hostid || 
 	sp1->hostnum != sp2->hostnum) return 1;
-    if (strcmp (sp1->hostname, sp2->hostname)) return 1;
+    if (safe_strcmp (sp1->hostname, sp2->hostname)) return 1;
     // Channel, ID, LUN
     if (sp1->chan != sp2->chan) return 2;
     if (sp1->id != sp2->id) return 3;
@@ -339,11 +393,12 @@
     if (sp1->inq_devtp != sp2->inq_devtp ||
 	sp1->rmvbl != sp2->rmvbl) return 5;
     // FIXME: Don't compare devtp??
-    if (strcmp (sp1->manufacturer, sp2->manufacturer)) return 6;
-    if (strcmp (sp1->model, sp2->model)) return 7;
-    if (strcmp (sp1->rev, sp2->rev)) return 8;
-    if (strcmp (sp1->serial, sp2->serial)) return 9;
+    if (safe_strcmp (sp1->manufacturer, sp2->manufacturer)) return 6;
+    if (safe_strcmp (sp1->model, sp2->model)) return 7;
+    if (safe_strcmp (sp1->rev, sp2->rev)) return 8;
+    if (safe_strcmp (sp1->serial, sp2->serial)) return 9;
     if (sp1->wwid != sp2->wwid) return 10;
+    if (sp1->hsv_os_id != sp2->hsv_os_id) return 11;
     return 0;
 }
 
@@ -366,6 +421,7 @@
 	else
 	    spnt->oldname = strdup (oldname);
     }
+    spnt->partition = part;
     spnt->major = major;
     spnt->minor = minor;
     spnt->devtp = devtp;
@@ -374,7 +430,6 @@
     spnt->chan = chan;
     spnt->id = id;
     spnt->lun = lun;
-    spnt->partition = part;
     if (hostname) 
 	spnt->hostname = strdup (hostname); 
     else 
@@ -386,6 +441,7 @@
      */
     spnt->model = spnt->manufacturer = spnt->serial = spnt->rev = NULL;
     spnt->wwid = no_wwid;
+    spnt->hsv_os_id = no_hsv_os_id;
     spnt->next = reglist; reglist = spnt;
     return spnt;
 }
@@ -518,7 +574,11 @@
 	case SG:
 	    sprintf (genpart, "sg%d", spnt->minor); break;
 	case SR:
-	    sprintf (genpart, "sr%d", spnt->minor); break;
+	    if (use_scd)
+		sprintf (genpart, "scd%d", spnt->minor);
+	    else
+		sprintf (genpart, "sr%d", spnt->minor); 
+	    break;
 	case ST:
 	    if (spnt->minor & 0x80) 
 		sprintf (genpart, "nst%d", spnt->minor & 0x7f);
@@ -954,6 +1014,7 @@
 	return status;	
 
     status = inquiry (fd, spnt);
+    get_hsv_os_id(fd,spnt);
     scsiname (spnt);
     if (setidlun)
 	oldscsiname (spnt);
@@ -961,12 +1022,59 @@
 
 }
 
+void findscsidisk(sname * spnt, int no) {
+       int status,major,minor,i,fd,res;
+       int scsi_id,channel,lun,host;
+      int id[2];
+	printf("Findscsidisk: %d\n",no);
+/* only seach up 16 devices may be a bad assumption, but scanning the whole list could take a long time */
+       for (i=no;i<no+16;i++) {
+       		major = disknum_to_sd_major (i);
+       		minor = (i << 4) & 0xf0;
+                unlink (TESTDEV);
+                
+                status = mknod ( TESTDEV, 0600 | S_IFBLK,
+                                makedev (major, minor) );
+                fd = open (TESTDEV, O_RDONLY);
+
+        	res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, id);
+        	if (res < 0) {
+            		continue;
+        	}
+
+      		host = id[0] >> 24;
+      		channel = (id[0] >> 16) & 0xff;
+      		lun = (id[0] >> 8 ) & 0xff;
+      		scsi_id = id[0] & 0xff;
+ 
+                unlink (TESTDEV);
+               	if (verbose>=2) printf ("Scanning: %d==%d %d==%d %d==%d %d==%d \n",
+                       host , spnt->hostnum ,
+                       channel , spnt->chan ,
+                       scsi_id , spnt->id ,
+                       lun , spnt->lun ) ;
+               	if (host == spnt->hostnum &&
+                       channel == spnt->chan &&
+                       scsi_id == spnt->id &&
+                       lun == spnt->lun ) {
+                       spnt->major = major;
+                       spnt->minor = minor;
+                       return;
+               }               
+       }
+       /* If we did not find a match, return a good guess.. */ 
+       unlink (TESTDEV);       
+       spnt->major = disknum_to_sd_major (no);
+       spnt->minor = (no << 4) & 0xf0; 
+}
+
+
 int build_disk (sname * spnt, int no)
 {
     int minor; int fd; int status;
     sname * spnt1 = sname_dup (spnt);
-    spnt1->major = disknum_to_sd_major (no);
-    spnt1->minor = (no << 4) & 0xf0; spnt->partition = -1;
+    findscsidisk(spnt1,no);
+    spnt->partition = -1;
     spnt1->devtp = SD;
     scsiname (spnt1); oldscsiname (spnt1);
     spnt1->next = reglist; reglist = spnt1;
@@ -1538,16 +1646,45 @@
 	return 0;
 }
 
+char* sysfs_findhostname (sname *sdev)
+{
+	char buf[128]; FILE* f; char* ptr;
+	strcpy (buf, "/sys/class/scsi_host/");
+	sprintf (buf+strlen(buf), "host%d", sdev->hostnum);
+	ptr = buf+strlen(buf);
+	strcat (buf, "/unique_id");
+	f = fopen (buf, "r");
+	if (!f) {
+		fprintf (stderr, "Could not open \"%s\"!\n", buf);
+		return 0;
+	}
+	fscanf (f, "%i", &sdev->hostid);
+	fclose (f);
+	*ptr = 0;	
+	strcat (buf, "/device/name");
+	f = fopen (buf, "r");
+	if (!f) {
+		fprintf (stderr, "Could not open \"%s\"!\n", buf);
+		return 0;
+	}
+	fgets (buf, 127, f);
+	fclose (f);
+	if (buf[strlen(buf)-1] == '\n')
+		buf[strlen(buf)-1] = 0;
+	return strdup (buf);
+}
 
 void fill_in_proc (sname * spnt)
 {
 	int fd;
 	spnt->shorthostname = find_scsihostname (spnt->hostnum);
-	spnt->hostname = strdup (spnt->shorthostname);
+	if (!spnt->shorthostname) 
+		spnt->shorthostname = sysfs_findhostname (spnt);
 	if (!spnt->shorthostname) {
 		fprintf (stderr, "scsidev: warning: could not deduce hostname & hostid\n");
 		return;
 	}
+	spnt->hostname = strdup (spnt->shorthostname);
 	spnt->hostid = find_ioport (spnt->shorthostname);
 	
 	fd = mknod (TESTDEV, 0600 | (isblk(spnt->inq_devtp)? S_IFBLK: S_IFCHR),
@@ -1560,6 +1697,7 @@
 	fd = open (TESTDEV, O_RDWR | O_NONBLOCK);
 	unlink (TESTDEV);
 	inquiry (fd, spnt);
+	get_hsv_os_id(fd, spnt);
 	close (fd);
 	scsiname (spnt);
 }
@@ -1691,21 +1829,88 @@
 void trigger_module_loads ()
 {
 	unlink (TESTDEV);
-	/* sg */
-	trigger_one_mod (0, SCSI_GENERIC_MAJOR, 255);
 	/* sd */
 	trigger_one_mod (1, SCSI_DISK0_MAJOR, 255);
-	/* st */
-	trigger_one_mod (0, SCSI_TAPE_MAJOR, 255);
-	/* osst */
-	trigger_one_mod (0, OSST_MAJOR, 255);
 	/* sr */
 	trigger_one_mod (1, SCSI_CDROM_MAJOR, 255);
+	/* osst */
+	trigger_one_mod (0, OSST_MAJOR, 255);
+	/* st */
+	trigger_one_mod (0, SCSI_TAPE_MAJOR, 255);
 	/* sch */
 	trigger_one_mod (0, 86, 255);
+	/* sg */
+	trigger_one_mod (0, SCSI_GENERIC_MAJOR, 255);
+}
+
+struct sysfsdev {
+	int maj, min;
+	char nm[7];
+	char tp;
+};
+
+struct sysfsdev sysfsdevs[2];
+
+
+void sysfs_get_nm (char* nm, struct sysfsdev* sysfsdevptr)
+{
+	char buf[128];
+	char* ptr = strrchr (nm, '/');
+	int ln;
+	*ptr = 0;
+	ln = readlink (nm, buf, 128);
+	buf[ln] = 0;
+	ptr = strrchr (buf, '/');
+	strcpy (sysfsdevptr->nm, ptr+1);
+}
+
+int sysfs_getinfo (sname * spnt)
+{
+	char nm[128]; char *nmptr;
+	FILE* f; int dev = 0;
+	strcpy (nm, "/sys/class/scsi_device/");
+	sprintf (nm+strlen(nm), "%d:%d:%d:%d",
+		 spnt->hostnum, spnt->chan, spnt->id, spnt->lun);
+	strcat (nm, "/device/");
+	nmptr = nm+strlen(nm);
+	// Bad luck: linux-2.6-test1 does not display sg devices
+	strcat (nm, "char/dev");
+	f = fopen(nm, "r");
+	if (f) {
+		sysfsdevs[dev].tp = 0;
+		fscanf (f, "%02x%02x", &sysfsdevs[dev].maj, &sysfsdevs[dev].min);
+		sysfs_get_nm (nm, sysfsdevs+dev);
+		++dev;
+		fclose (f);
+	}
+	*nmptr = 0;
+	strcat (nm, "block/dev");
+	f = fopen(nm, "r");
+	if (f) {
+		sysfsdevs[dev].tp = 1;
+		fscanf (f, "%02x%02x", &sysfsdevs[dev].maj, &sysfsdevs[dev].min);
+		sysfs_get_nm (nm, sysfsdevs+dev);
+		++dev;
+		fclose (f);
+	}
+	return dev;
+}	
+
+void sysfs_parse (sname *spnt, int idx)
+{	
+	char nm[7];
+	char *p1 = sysfsdevs[idx].nm, *p2 = nm;
+	while (!isdigit(*p1) && *p1)
+		*p2++ = *p1++;
+	*p2 = 0;
+	spnt->oldname = strdup(sysfsdevs[idx].nm);
+	spnt->major = sysfsdevs[idx].maj;
+	spnt->minor = sysfsdevs[idx].min;
+	spnt->devtp = nm26_to_devtp(nm);
 }
+	
 
-/* Build device list by reading /proc/scsi/scsi */
+/* Build device list by reading /proc/scsi/scsi with extensions from scsi-many or sysfs */
 void build_sgdevlist_procscsi ()
 {
 	FILE* scsifile;
@@ -1747,6 +1952,8 @@
 		spnt = malloc (sizeof (sname));
 		memset (spnt, 0, sizeof (sname));
 		hl_per_dev = procscsi_parse (spnt);
+		if (!hl_per_dev || hl_per_dev == EOF) 
+			hl_per_dev = sysfs_getinfo (spnt);
 		if (!hl_per_dev || hl_per_dev == EOF) {
 			free (spnt);
 			fprintf (stderr, "Low level dev without HL driver?\n");
@@ -1760,6 +1967,8 @@
 			}
 			++hdevs; spnt->partition = -1;
 			procscsiext_parse (spnt, hl);
+			if (spnt->major == 0)
+				sysfs_parse (spnt, hl);
 			if (spnt->devtp == SG)
 				sgpnt = spnt;
 		}
@@ -1798,10 +2007,9 @@
 			rdevs, hdevs);
 		dumplist ();
 	}
-	
-	
 }
 	
+
 /* Test for availability of /proc/scsi/scsi extensions */
 int procscsi_ext_status ()
 {
@@ -1861,7 +2069,7 @@
 	if (!procscsi_ext_status())
 		return -1;
 	
-	build_sgdevlist_procscsi();
+	build_sgdevlist_procscsi(0);
 	
 	if (!se_status)
 		procscsi_ext_off();
@@ -1869,6 +2077,19 @@
 }
 
 
+
+/* Try sysfs */
+int find_sysfs ()
+{
+	DIR *sdev_dir = opendir("/sys/class/scsi_device");
+	if (!sdev_dir)
+		return -1;
+	closedir (sdev_dir);
+	build_sgdevlist_procscsi(1);
+	return 0;
+}
+
+
 void usage()
 {
     fprintf (stderr, "%s\n", versid);
@@ -1878,10 +2099,11 @@
     fprintf (stderr, " -d     : sanitize by Deleting undetected entries (def: .shadow. files\n");
     fprintf (stderr, " -l/-L  : create symLinks for device names / alias names\n");
     fprintf (stderr, " -m mode: permissions to create dev nodes with\n");
-    fprintf (stderr, " -s     : list Serial numbers /WWIDs of devices (if available)\n");
+    fprintf (stderr, " -s     : list Serial numbers /WWIDs /HSVs of devices (if available)\n");
     fprintf (stderr, " -c mxms: Continue scanning until mxms missing devs found\n");
     fprintf (stderr, " -r     : trust Removeable media (only safe after boot)\n");
     fprintf (stderr, " -e     : use dEvfs like naming  (cbtu chars)\n");
+    fprintf (stderr, " -o     : for the Old names use scd instead of sr\n");
     fprintf (stderr, " -M     : support Multipathing: First device is aliased\n");
     fprintf (stderr, " -v/-q  : Verbose/Quiet operation\n");
     fprintf (stderr, " -h     : print Help and exit.\n");
@@ -1903,7 +2125,7 @@
 	fprintf(stderr, DEVSCSI " either does not exist, or is not a directory\n");
 	exit(0);
     }
-    while ((c = getopt(argc, argv, "pflLvqshnderMm:c:")) != EOF) {
+    while ((c = getopt(argc, argv, "pflLvqshnderoMm:c:")) != EOF) {
 	switch (c) {
 	  case 'p':
 	    no_procscsi = 1; break;
@@ -1929,6 +2151,8 @@
 	    supp_multi = 1; break;
 	  case 'e':
 	    nm_cbtu = 1; break;
+	  case 'o':
+	    use_scd = 1; break;  
 	  case 'v':
 	    verbose++; break;
 	  case 'h':
@@ -1959,7 +2183,7 @@
     register_dev("/dev/scsi/rsth4-334c0i5l0", 9,128, ST, 6, 0x334, 0, 5, 0, -1, "debug", 0, NULL, NULL);
 #else
     if (no_procscsi || try_procscsi ()) {
-	if (!quiet) 
+	if (find_sysfs () && !quiet) 
 	    fprintf (stderr, "/proc/scsi/scsi extensions not found. Fall back to scanning.\n");
 	build_sgdevlist ();
     }
@@ -1982,6 +2206,8 @@
 		printf("Serial number of %s: \"%s\"\n", spnt->name, spnt->serial);
 	    if ( spnt->wwid != no_wwid )
 		printf (" WWID: %Lx\n", spnt->wwid);
+	    if ( spnt->hsv_os_id != no_hsv_os_id )
+		printf (" HSV OS Id: %d\n", spnt->hsv_os_id);
 	}
     }
 
@@ -2150,6 +2376,7 @@
  * MODEL="string"
  * SERIAL_NUMBER="string"	(for those devices that support this).
  * WWID=number			( "    "      "     "      "      " )
+ * HSVOSID=number		( "    "      "     "      "      " )
  * REV="string"
  * NAME="string" (alias)
  * DEVTYPE="disk", "tape", "osst", "generic", or "cdrom".
@@ -2166,6 +2393,7 @@
 
     int lun, chan, id, part, hostid, hostnum;
     int line;
+    int hsv_os_id;
     unsigned long long wwid;	/* host byte order ... */
     enum devtype_t devtype_i;
     char *manufacturer, *model, *serial_number, *name, *devtype, *rev, *host;
@@ -2205,6 +2433,7 @@
 	chan = -1;
 	hostid = -1; hostnum = -1;
 	part = -1; wwid = no_wwid;
+	hsv_os_id = -1;
 	host = NULL;
 	manufacturer = NULL; model = NULL;
 	serial_number = NULL; rev = NULL;
@@ -2255,6 +2484,8 @@
 		pnt = get_string(pnt1 + 1, &name);
 	    else if ( strncmp(pnt, "devt", 4) == 0 )
 		pnt = get_string(pnt1 + 1, &devtype);
+	    else if ( strcmp(pnt, "hsvosid") == 0 )
+		pnt = get_number(pnt1 + 1, &hsv_os_id);
 	    else {
 		fprintf(stderr,"Unrecognized specifier \"%s\" on line %i\n", pnt,
 			line);
@@ -2315,6 +2546,8 @@
 		continue;
 	    if( hostnum != -1 && hostnum != spnt->hostnum ) 
 		continue;
+	    if( hsv_os_id != -1 && hsv_os_id != spnt->hsv_os_id ) 
+		continue;
 	    if( spnt->devtp != devtype_i )
 		continue;
 	    if( part != spnt->partition )
@@ -2622,3 +2855,61 @@
     return 0;
 #endif
 }
+
+int get_hsv_os_id (int infile, sname * spnt)
+{
+  int status,i;
+  unsigned char *cmd;
+  unsigned char *pagestart;
+  unsigned char buffer[1024];
+  char path[64];
+  
+  spnt->hsv_os_id = no_hsv_os_id;
+
+  if (!spnt->model || strncmp (spnt->model, "HSV", 3) != 0)
+       return -1;
+  memset (buffer, 0, sizeof(buffer));
+  
+  *( (int *)  buffer )         = 0;    /* length of input data */
+  *( ((int *) buffer) + 1 )    = sizeof(buffer)-16;    /* length of output buffer */
+
+  cmd = (char *) ( ((int *) buffer) + 2 );
+  
+  cmd[0] = 0xa3;
+  cmd[1] = 0x05;
+  cmd[2] = 0x00;	//res          
+  cmd[3] = 0x00;	//res  
+  cmd[4] = 0x00;	//lun ignored
+  cmd[5] = 0x00;	//lun ignored
+  cmd[6] = 0x00;	//len
+  cmd[7] = 0x00;	//len
+  cmd[8] = 0x00;	//len
+  cmd[9] = 0xff;	//len
+  cmd[10] = 0x00;	//PRUCLU
+  cmd[11] = 0x00;
+  
+  if( infile == -1 ) 
+	return -1;
+
+  status = ioctl( infile,  SCSI_IOCTL_SEND_COMMAND , buffer );
+  pagestart = buffer + 8;
+
+  if (status) 
+	spnt->hsv_os_id = no_hsv_os_id;
+  else
+	spnt->hsv_os_id = (pagestart[4])<<8 | (pagestart[5]);
+
+  if (verbose == 1) 
+	printf ("HSV OS Unit ID for %s: %d\n", path, spnt->hsv_os_id);
+
+  if (verbose == 2) {
+	for (i = 0; i <= 16; i++) {
+		printf (" %02x", buffer[i]);
+		if (!((i+1)%16)) 
+			printf ("\n");
+	}
+	printf("\n");
+  }
+  return 0;
+}
+
