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 + * * 2003-01-13: Kurt Garloff * - Handle EOF return value from sscanf() * - Handle empty names gracefully (for zfcp, from Bernd Kaindl) * * 2003-03-07: Kurt Garloff @@ -116,6 +116,36 @@ * * 2003-06-29: Kurt Garloff * - Fix fd leak (/proc/partitions) * -> 2.29 + * + * * 2003-07-25: Kurt Garloff + * - Documentation: -pX and n automatically created for aliases + * (Thx to Russel Shaw for suggesting it.) + * + * * 2003-03-03: Evan Felix : + * - 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 : + * - 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 : + * - 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 @@ -136,8 +166,8 @@ #include -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 \n"\ " 2000--2003 Kurt Garloff "; @@ -162,6 +192,7 @@ #include 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> 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; +} +