Index: CHANGES =================================================================== RCS file: /home/cvsroot/scsidev/CHANGES,v retrieving revision 1.1 retrieving revision 1.1.4.2 diff -u -r1.1 -r1.1.4.2 --- CHANGES 2000/01/16 23:28:49 1.1 +++ CHANGES 2002/07/29 23:56:28 1.1.4.2 @@ -1,2 +1,93 @@ CHANGES to scsidev: See comments on top of scsidev.c. + +/* + * Program to assign static device names to SCSI devices as opposed to the + * dynamic numbering of the Linux-1.0 -- 2.4 kernel. + * Program written by Eric Youngdale + * Reworked 1/2000 Kurt Garloff + * + * Copyright: GNU GPL. + * Please read the file COPYING. + * + * Changes: + * * 2000/01/16: Kurt Garloff : + * - SCSI_DISK_MAJOR + * - Fixed lots of warnings + * - use tmp file in /dev/scsi + * Fix /etc/scsi-alias functionality + * - Parsing: Respect EOL \0 + * - inquire: Request takes a few bytes, response buffer is somewhat smaller + * use getstr() to get and truncate strings + * - comparison: Empty Strings of failed inquiries do not fit + * -> Released Version: 1.6 + * + * * 2000/01/17: Kurt Garloff : + * Major changes: + * - Now scan completely relying on sg. Does inquiry for every device + * immediately (it's not that expensive), and does interpret the + * periph dev qualifier field to build the other high-level SCSI devs. + * -> removable devices will work, this way. + * For harddisks, the partitions will be scanned, for tapes the n version + * be created. + * Minor changes: + * - new inquiry () function replacing old inquire and get_version_no + * - added support for more than 16 SCSI disks + * - quiet flag -q + * - Link alias names flag -L + * - -c maxmiss option: Tolerate maxmiss missing sg devs. + * - rev and hostname fields + * - avoid unnecessary recreation of alias names + * - new options -n osanitize and -d elete undetected + * -> Released Version: 2.0 + * + * * 2000/01/18: Kurt Garloff + * In principal, there is a chance to fool scsidev: Remove a device with + * a low SCSI ID and reinsert another device. Then the assumptions of + * scsidev about the dev no. ordering of the kernel are wrong. + * - Add a check for this + * - infrastructure changes: scsiname () and oldscsiname () + * - struct regnames (sname) now has generic pointer to allow + * relationships between devs. name is the full name, now. + * + * * 2000/07/15: Kurt Garloff + * - Open device nodes (but sg) with O_NONBLOCK; unfortunately this is not + * honoured by most of Linux' SCSI high-level drivers :-( + * - Add support for OnStream tapes (osst) + * -> Version 2.20 + * + * * 2000/09/04: Kurt Garloff + * - Bugfix for long hostnames from Doug Gilbert (=> 2.21) + * - Fix parsing of scsi.alias file: Broke on missing LF at the end + * -> 2.22 + * + * * 2002/07/26: Kurt Garloff + * - optional (-e) cbtu naming scheme + * - bugfix WRT alias handling of subdevices (parititons, non-rew. tap) + * - Support for large SCSI IDs and LUNs. (from MGE) + * - Support WWID (INQ EVPD 0x83) report + aliases + * - Fix hex number parser + * -> 2.23 + * + * * 2002/07/29: Kurt Garloff + * - More sane way of storing permissions + * - Less relying on major numbers + * - Support many SDs (only up to 255 that is) + * - Support for SCSI changers + * -> 2.24 + * + * * 2002/07/30: Kurt Garloff + * - Support for /proc/scsi/scsi extensions + * - Get other info from proc as well (hostname, ioport, partitions) + * as needed + * - Alternatively match short hostname + * - Support for really large no of disks + * - Strip leading and trailing spaces for SCSI INQUIRY stuff, esp. + * the serial number. + * - We now have a sane way of handling permission. Major code cleanup. + * Permissions are stored in /dev/scsi/.shadow.XXX files, now. + * - If working in symlink mode, we do adjust the permissions of the + * underlying real dev node now. + * -> 2.25 + * + */ Index: Makefile.in =================================================================== RCS file: /home/cvsroot/scsidev/Makefile.in,v retrieving revision 1.9.2.2 retrieving revision 1.9.2.3 diff -u -r1.9.2.2 -r1.9.2.3 --- Makefile.in 2002/07/27 00:17:23 1.9.2.2 +++ Makefile.in 2002/07/29 23:56:28 1.9.2.3 @@ -1,5 +1,5 @@ # -# $Id: Makefile.in,v 1.9.2.2 2002/07/27 00:17:23 garloff Exp $ +# $Id: Makefile.in,v 1.9.2.3 2002/07/29 23:56:28 garloff Exp $ # ## Makefile for scsidev. @@ -48,12 +48,14 @@ cd $(srcdir) && autoconf install: scsidev - install -o root -g root -m 755 -s scsidev $(bindir) - install -o root -g root -m 644 scsidev.8 $(mandir) + install -d $(bindir) + install -m 755 -s scsidev $(bindir) + install -d $(mandir) + install -m 644 scsidev.8 $(mandir) gzip -9f $(mandir)/scsidev.8 - if [ ! -d $(DESTDIR)/dev/scsi ]; then mkdir $(DESTDIR)/dev/scsi; fi + if [ ! -d $(DESTDIR)/dev/scsi ]; then mkdir -p $(DESTDIR)/dev/scsi; fi #install -d $(docdir)/scsidev - #install COPYING boot.diff README scsi.alias $(docdir)/scsidev/ + #install COPYING boot.diff README scsi.alias CHANGES $(docdir)/scsidev/ dist: clean rm Makefile Index: VERSION =================================================================== RCS file: /home/cvsroot/scsidev/VERSION,v retrieving revision 1.8.2.2 retrieving revision 1.8.2.3 diff -u -r1.8.2.2 -r1.8.2.3 --- VERSION 2002/07/29 01:12:16 1.8.2.2 +++ VERSION 2002/07/29 15:48:00 1.8.2.3 @@ -1 +1 @@ -2.24 +2.25 Index: scsidev.8 =================================================================== RCS file: /home/cvsroot/scsidev/scsidev.8,v retrieving revision 1.11.2.1 retrieving revision 1.11.2.2 diff -u -r1.11.2.1 -r1.11.2.2 --- scsidev.8 2002/07/26 23:28:54 1.11.2.1 +++ scsidev.8 2002/07/30 08:21:53 1.11.2.2 @@ -1,8 +1,8 @@ # -# $Id: scsidev.8,v 1.11.2.1 2002/07/26 23:28:54 garloff Exp $ +# $Id: scsidev.8,v 1.11.2.2 2002/07/30 08:21:53 garloff Exp $ # .\" -*- nroff -*- -.TH SCSIDEV 8 "July 2002" "Version 2.23" +.TH SCSIDEV 8 "July 2002" "Version 2.25" .SH NAME scsidev \- populate /dev/scsi with device names that are independent of reconfiguration. .SH SYNOPSIS @@ -71,13 +71,12 @@ .B scsidev will scan all of the entries in the /dev/scsi directory, and see if any of them are for devices nodes which were added for devices that -are not active. These devices are modified to have a minor number of -255 as a security precaution, since these might have permissions that -would allow people to access devices that they should not be able to -access. This is the default behaviour and is considered ideal for most -cases, as it preserves the ownership and permissions of the files and is -secure (unless you have partitions or devices that need to be protected and -have minor == 255, such as sdp15). +are not active. The permissions of inactive devices are stored in +a .shadow. file and the device node is removed as a security precaution, +since these might have permissions that would allow people to access devices +that they should not be able to access. This is the default behaviour and +is considered ideal for most cases, as it preserves the ownership and +permissions of the files and is secure. .PP The so called sanitizing can be influenced by the options .B \-f \-d \-n. @@ -111,12 +110,13 @@ .TP .I \-f Flush everything from /dev/scsi prior to scanning the detected devices. -All non-default ownership/permissions that may have been set will be lost. +This means that new device nodes will be created even if the old ones +were OK. .TP .I \-d -Sanitize by deletion. Device nodes of non-available SCSI devices will be -deleted instead of setting minor to 255. All non-default -ownership/permissions that may have been set will be lost. +Sanitize by deletion. The .shadow. backup files will not be +created, so you loose all non-default ownership/permissions that may +have been set. .TP .I \-n Don't touch device nodes for non-existing SCSI devices. @@ -124,14 +124,14 @@ .TP .I \-l Symbolic link mode. Instead of creating nodes, symbolic links are created -which point to the older /dev/sda1 types of device nodes. The usage -of this switch is not recommended. +which point to the older /dev/sda1 types of device nodes. When using this +option, the permissions of the /dev/XXX device nodes will be changed to +match the ones stored in /dev/scsi/YYY file, if present. .TP .I \-L Use symbolic names for the aliases assigned through the .B /etc/scsi.alias settings (see below). -The usage of this switch is not recommended. .TP .I \-m mode Specifies the mode (permissions) for new entries that need to be created. @@ -141,6 +141,8 @@ .B scsidev fails to open a generic scsi device, it finishes its scan for devices. With this option, it goes on until maxmiss missing devices were found. +This is only used, if you don't have the /proc/scsi/scsi extensions +for large disks. .TP .I \-r .B scsidev @@ -153,6 +155,8 @@ .B scsidev will do the right guesses. So using \-r in bootup scripts is safe. After you removed devices from your SCSI config, it isn't safe any longer. +This is only needed, if you don't have the /proc/scsi/scsi extensions +for large disks. .TP .I \-e Instructs Index: scsidev.c =================================================================== RCS file: /home/cvsroot/scsidev/scsidev.c,v retrieving revision 1.28.2.9 retrieving revision 1.28.2.17 diff -u -r1.28.2.9 -r1.28.2.17 --- scsidev.c 2002/07/29 08:28:36 1.28.2.9 +++ scsidev.c 2002/07/29 23:56:28 1.28.2.17 @@ -70,16 +70,25 @@ * - Fix hex number parser * -> 2.23 * - * * 2002/07/28: Kurt Garloff + * * 2002/07/29: Kurt Garloff * - More sane way of storing permissions - * - Agnostic of major numbers + * - Less relying on major numbers * - Support many SDs (only up to 255 that is) * - Support for SCSI changers * -> 2.24 * - * * 2002/07/29: Kurt Garloff + * * 2002/07/30: Kurt Garloff * - Support for /proc/scsi/scsi extensions + * - Get other info from proc as well (hostname, ioport, partitions) + * as needed + * - Alternatively match short hostname * - Support for really large no of disks + * - Strip leading and trailing spaces for SCSI INQUIRY stuff, esp. + * the serial number. + * - We now have a sane way of handling permission. Major code cleanup. + * Permissions are stored in /dev/scsi/.shadow.XXX files, now. + * - If working in symlink mode, we do adjust the permissions of the + * underlying real dev node now. * -> 2.25 * */ @@ -98,9 +107,11 @@ #include #include #include +#include +#include -static char rcsid[] ="$Id: scsidev.c,v 1.28.2.9 2002/07/29 08:28:36 garloff Exp $"; +static char rcsid[] ="$Id: scsidev.c,v 1.28.2.17 2002/07/29 23:56:28 garloff Exp $"; static char *versid = "scsidev " VERSION " 2000/01/17"; static char *copyright = "Copyright: GNU GPL (see file COPYING)\n" \ " (w) 1994--1997 Eric Youngdale \n"\ @@ -136,15 +147,17 @@ int no_san = 0; int nm_cbtu = 0; int supp_rmvbl = 0; +int override_link_perm = 1; char * no_serial = "No serial number"; unsigned long long no_wwid = 0; #define DEVSCSI "/dev/scsi" #define TESTDEV DEVSCSI "/testdev" #define PROCSCSI "/proc/scsi/scsi" -#define SHADOW ".shadow" +#define SHADOW ".shadow." enum devtype_t { NONE=0, SG, SD, SR, ST, OSST, SCH, }; +char* devtp_nm[] = { "", "Generic", "Disk", "Rom", "Tape", "OnStreamTape", "Changer", }; /* * This program builds entries in /dev/scsi that have names that are tied @@ -165,6 +178,7 @@ { struct regnames * next; char * name; + char * oldname; char * manufacturer; char * model; char * rev; @@ -178,6 +192,7 @@ int major; int minor; char * hostname; + char * shorthostname; int hostnum; int chan; int id; @@ -250,33 +265,39 @@ * which we know to be OK and active. */ -void dumplist () +void dumpentry (sname * pnt) { - sname * pnt; - for (pnt = reglist; pnt; pnt = pnt->next) { - printf ("%s: %s %s %s (%s) %Lx\n", pnt->name, + printf ("%s (%s): %s %s %s (%s) %Lx\n", pnt->name, pnt->oldname, pnt->manufacturer, pnt->model, pnt->rev, pnt->serial, pnt->wwid); - printf (" on %s (%d-%x): c%di%dl%d", pnt->hostname, - pnt->hostnum, pnt->hostid, pnt->chan, pnt->id, pnt->lun); - if (pnt->partition != -1) - printf ("p%d", pnt->partition); - printf (" %c %x-%x\n", (isblk(pnt->devtp)? 'b': 'c'), - pnt->major, pnt->minor); - } + 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) + printf ("p%d", pnt->partition); + printf (" %c %x-%x ", (isblk(pnt->devtp)? 'b': 'c'), + pnt->major, pnt->minor); + printf (" SCSI %s\n", devtp_nm[(int)pnt->devtp]); +} + + +void dumplist () +{ + sname * spnt; + for (spnt = reglist; spnt; spnt = spnt->next) + dumpentry (spnt); } -// Creates a dup scsi device registration, and a link from new to old +/// Creates a dup scsi device registration, and a link from new to old sname * sname_dup (sname * spnt) { - sname * spnt1 = malloc (sizeof (sname)); - memcpy (spnt1, spnt, sizeof (sname)); - spnt1->related = spnt; //spnt->related = spnt1; - //spnt1->next = reglist; reglist = spnt1; - return spnt1; + sname * spnt1 = malloc (sizeof (sname)); + memcpy (spnt1, spnt, sizeof (sname)); + spnt1->related = spnt; //spnt->related = spnt1; + //spnt1->next = reglist; reglist = spnt1; + return spnt1; } -// compare two sname entries +/// compare two sname entries char sname_cmp (sname *sp1, sname *sp2) { // Host @@ -299,10 +320,11 @@ return 0; } +/// Used for alias registration sname * register_dev (char * name, int major, int minor, int hnum, int hid, int chan, int id, int lun, int part, char * hostname, - sname * alias) + char* oldname, sname * alias, sname * rel) { sname * spnt; //char * pnt; @@ -311,6 +333,12 @@ //pnt = strrchr (name, '/'); //spnt->name = strdup (pnt + 1); spnt->name = strdup (name); + if (oldname) { + if (!memcmp (oldname, "/dev/", 5)) + spnt->oldname = strdup (oldname + 5); + else + spnt->oldname = strdup (oldname); + } spnt->major = major; spnt->minor = minor; spnt->hostnum = hnum; @@ -319,10 +347,12 @@ spnt->id = id; spnt->lun = lun; spnt->partition = part; - if (hostname) spnt->hostname = strdup (hostname); - else spnt->hostname = 0; + if (hostname) + spnt->hostname = strdup (hostname); + else + spnt->hostname = 0; spnt->alias = alias; - spnt->related = 0; + spnt->related = rel; /* * Initialize this - they may be needed later. */ @@ -332,97 +362,6 @@ return spnt; } -/* - * We need to "fix" any device nodes that are currently not used because - * it is a sercurity risk to leave these lying about. These are fixed - * by storing a shadow file. If these become active again, - * we will be able to use them again because the minor number will be - * set back again, and we are preserving the ownership and permissions. - */ -void sanitize_sdev () -{ - struct dirent * de; - char filename[64]; - DIR * sdir; - sname * spnt; - struct stat statbuf; - int status; - int i; - - /* - * Next, delete all of the existing entries in the /dev/scsi directory. - * The idea is that if we have added/removed devices, the numbers might have - * changed. - */ - sdir = opendir (DEVSCSI); - while (1) { - de = readdir (sdir); - if (de == NULL) - break; - /* If it's a .shadow name, leave it alone */ - i = strlen (de->d_name) - strlen (SHADOW); - if (i > 0 && !strcmp (de->d_name+i, SHADOW)) - break; - /* - * OK, we have the name. See whether this is something - * we know about already. - */ - for( spnt = reglist; spnt; spnt = spnt->next ) { - if( strcmp(de->d_name, strrchr (spnt->name, '/') + 1) == 0 ) - break; - } - /* Didn't we find it? */ - if (spnt == NULL) { - strcpy (filename, DEVSCSI); strcat (filename, "/"); - strcat (filename, de->d_name); - status = stat (filename, &statbuf); - if ( status == 0 - && (S_ISCHR (statbuf.st_mode) || S_ISBLK (statbuf.st_mode)) ) { - /* - * OK, this one is something new that we have to do something - * with. No big deal, stat it so we get the particulars, then - * create a new one with a safe minor number. - */ - unlink (filename); - if (!san_del) { - strcat (filename, SHADOW); - status = open (filename, O_RDWR | O_CREAT | O_EXCL); - status = chmod (filename, statbuf.st_mode); - chown (filename, statbuf.st_uid, statbuf.st_gid); - } - } - } - } - closedir (sdir); - -} - - -/* - * Next, delete all of the existing entries in the /dev/scsi directory. - * The idea is that if we have added/removed devices, the numbers might have - * changed. - */ -void flush_sdev () -{ - struct dirent * de; - char filename[60]; - DIR * sdir; - sdir = opendir (DEVSCSI); - while (1) { - de = readdir (sdir); - if (de == NULL) break; - //if (de->d_name[0] != 's' && de->d_name[0] != 'n') continue; - strcpy (filename, DEVSCSI); strcat (filename, "/"); - strcat (filename, de->d_name); - unlink (filename); - } - closedir (sdir); - if (!quiet) - printf ("Flushed old " DEVSCSI " entries...\n"); - -} - // Creates a /dev/scsi name from the info in sname char * scsiname (sname *spnt) { @@ -454,7 +393,7 @@ case SD: dnm = "sd"; if (spnt->minor & 0x0f) - sprintf (app, "p%d", spnt->minor % 0x10); + sprintf (app, "p%d", spnt->minor & 0x0f); break; case SCH: dnm = "sch"; @@ -541,14 +480,12 @@ // Creates an old /dev/s? name from the info in sname -char * oldscsiname (sname *spnt) +void oldscsiname (sname *spnt) { - char nm[64]; char *genpart; + char genpart[64]; int diskno; enum devtype_t tp = spnt->devtp; - strcpy (nm, "/dev/"); genpart = nm + strlen (nm); - /* FIXME */ switch (tp) { case SG: sprintf (genpart, "sg%d", spnt->minor); break; @@ -572,9 +509,8 @@ case SD: diskno = sd_major_to_disknum (spnt->major, spnt->minor); sd_devname (diskno, genpart); - genpart += strlen (genpart); - if (spnt->minor & 0xf) - sprintf (genpart, "%d", (spnt->minor & 0xf)); + if (spnt->minor & 0x0f) + sprintf (genpart+strlen(genpart), "%d", (spnt->minor & 0x0f)); break; default: fprintf (stderr, "scsidev: PANIC: Illegal device type major 0x%02x!\n", @@ -582,161 +518,348 @@ abort (); } //spnt->name = strdup (nm); - return strdup (nm); + spnt->oldname = strdup (genpart); } -/* +/*************************** PERMISSIONS ****************************/ + +/** Helper to copy perms */ +inline void cp_perm (struct stat *to, const struct stat *from) +{ + to->st_uid = from->st_uid; + to->st_gid = from->st_gid; + to->st_mode = from->st_mode & ~S_IFMT; +} + +/** Helper to apply perms */ +inline void apply_perm (const char* nm, const struct stat *st, int fmode) +{ + chown (nm, st->st_uid, st->st_gid); + chmod (nm, st->st_mode | fmode); +} + +/** Helper to compare perms */ +inline int cmp_perm (const struct stat *p1, const struct stat *p2) +{ + return (p1->st_uid != p2->st_uid || + p1->st_gid != p2->st_gid || + (p1->st_mode & ~S_IFMT) != (p2->st_mode & ~S_IFMT)); +} + +/* Construct .shadow. name */ +void mk_shadow_nm (char* buf, const char* nm) +{ + const char *ptr = strrchr (nm, '/'); + *buf = 0; + if (ptr) { + memcpy (buf, nm, ptr-nm+1); + buf[ptr-nm+1] = 0; + } else + ptr = nm; + strcat (buf, SHADOW); + strcat (buf, ++ptr); +} + + +/** Make .shadow. file for backing up permissions */ +void backup_shadow (const char* nm, struct stat *stbuf) +{ + struct stat statbuf; + char shadow[64]; + int status; + mk_shadow_nm (shadow, nm); + + status = stat (shadow, &statbuf); + if (!status && !cmp_perm (&statbuf, stbuf)) + return; + + if (status) { + int fd = open (shadow, O_RDWR | O_CREAT | O_EXCL); + close (fd); + } + apply_perm (shadow, stbuf, 0); +} + +/** Remove the shadow file */ +void rm_shadow (const char *nm) +{ + char shadow[64]; + int fd; + mk_shadow_nm (shadow, nm); + fd = open (shadow, O_RDONLY); + if (fd > 0) { + close (fd); + unlink (shadow); + } +} + +/** Get permissions + * Permissions: + * (a) old permissions of nm (if it's not a symlink) + * (b) permissions of shadow file + * (c) permissions of file pointed to + * (d) (new) filemode + */ +void get_perm (const char *nm, const char *linkto, struct stat * stbuf, int cdrom) +{ + int status; + struct stat statbuf; + char shadow[64]; + + status = lstat (nm, &statbuf); + if (!status && !S_ISLNK (statbuf.st_mode)) { + cp_perm (stbuf, &statbuf); + return; + } + + mk_shadow_nm (shadow, nm); + //printf ("%s\n", shadow); + status = stat (shadow, &statbuf); + + if (!status) { + cp_perm (stbuf, &statbuf); + return; + } + + if (linkto) { + status = stat (linkto, &statbuf); + if (!status) { + cp_perm (stbuf, &statbuf); + return; + } + } + + stbuf->st_uid = 0; + stbuf->st_gid = 0; + stbuf->st_mode = filemode; + if (cdrom) + stbuf->st_mode &= ~0222; +} + + +/** * Check to see if a given entry exists. If not, create it, * if it does make sure the major and minor numbers are correct - * and save permissions and ownerships if this is the case. + * and save permissions and ownerships (in the .shadow. file) + * if this is the case. */ -void update_device (char * path, int fmode, int major, int minor) +void update_device (char* linkto, char * path, int fmode, int major, int minor) { - struct stat statbuf; - int recreate; - int newmode; - int uid, gid; - int status; - char shadow[64]; - strcpy (shadow, path); - - /* FIXME */ - newmode = fmode | - (major != SCSI_CDROM_MAJOR ? filemode : (filemode & ~0222)); + struct stat statbuf, statbuf2; + int recreate; + int newmode; + int status; - recreate = 1; - uid = gid = -1; - status = lstat (path, &statbuf); - /* If dev node does not exist, try to look up .shadow file */ - if (status) { - strcat (shadow, SHADOW); - status = stat (shadow, &statbuf); - } - if (status == 0) { recreate = 0; - uid = statbuf.st_uid; - gid = statbuf.st_gid; - /* - * We are NOT in symlink mode, when getting here. - */ - if ( S_ISLNK (statbuf.st_mode) ) { - recreate = 1; - if (verbose >= 2) - printf("is symbolic link ...\n"); - /* Try to get permissions from somewhere */ - strcat (shadow, SHADOW); - status = stat (shadow, &statbuf); - if (status) - status = stat (path, &statbuf); - uid = statbuf.st_uid; - gid = statbuf.st_gid; - newmode = fmode | (statbuf.st_mode & ~S_IFLNK); - unlink (path); + get_perm (path, linkto, &statbuf2, (major == SCSI_CDROM_MAJOR)); + + newmode = fmode | statbuf2.st_mode; + + status = lstat (path, &statbuf); + if (status || S_ISLNK (statbuf.st_mode)) + ++recreate; + else if (statbuf.st_rdev != makedev (major, minor)) + ++recreate; + /* Don't test permissions here, just set them later */ + if (recreate) { + if (!status) + unlink (path); + status = mknod (path, newmode, makedev (major, minor)); + //printf("Recreate maj %i min %i\n", major, minor); + if( status == -1 ) { + fprintf (stderr, "mknod (%s) failed\n", path); + exit (1); + } + apply_perm (path, &statbuf2, fmode); + } else + if (cmp_perm (&statbuf, &statbuf2)) + apply_perm (path, &statbuf2, fmode); + rm_shadow (path); +} + +/** Create a symlink to the real dev */ +void create_symlink (const char *linkto, const char* nm, int fmode, int major, int minor) +{ + struct stat statbuf; + struct stat statbuf2; + int status; + int recreate = 0; + + get_perm (nm, linkto, &statbuf2, (major == SCSI_CDROM_MAJOR)); + + status = lstat (nm, &statbuf); + if (status || !S_ISLNK (statbuf.st_mode)) + ++recreate; + else { + char realnm[64]; + int n = readlink (nm, realnm, 63); + if (n != -1) { + realnm[n] = 0; + if (strcmp (linkto, realnm)) + ++recreate; + } else + ++recreate; } - /* - * Make sure we have the correct device type too. - */ - if ( (statbuf.st_mode & S_IFMT) != fmode ) { - recreate = 1; - newmode = fmode | (statbuf.st_mode & ~S_IFLNK); - if (verbose >= 2) - printf ("mode: %08x vs. %08x\n", - statbuf.st_mode & S_IFMT, fmode); + + if (recreate) { + if (!status) + unlink (nm); + symlink (linkto, nm); } + /* - * Compare the device number. If something changed, then - * unlink it so that we can recreate it. Save the mode of - * the thing so that we can keep the same permissions. + * Now make sure that the device the symlink points to + * actually exists. If not, then create that device. */ - if ( statbuf.st_rdev != makedev (major, minor) ) { - recreate = 1; - newmode = fmode | (statbuf.st_mode & ~S_IFLNK); - if (verbose >= 2) - printf ("r_dev: %08x vs. %08x\n", - (unsigned int)statbuf.st_rdev, makedev (major, minor)); + + status = stat (linkto, &statbuf); + if (status) { + int newmode = statbuf2.st_mode | fmode; + status = mknod (linkto, newmode, + makedev (major, minor)); + fprintf (stderr, "Creating %s\n", linkto); + apply_perm (linkto, &statbuf2, fmode); + } else { + if (statbuf.st_rdev != makedev (major, minor)) { + fprintf (stderr, "scsidev: Inconsistency %s == %02x:%02x != %02x:%02x\n", + linkto, major(statbuf.st_rdev), minor(statbuf.st_rdev), + major, minor); + abort (); + } + if (cmp_perm (&statbuf, &statbuf2) && override_link_perm) + apply_perm (linkto, &statbuf2, fmode); } + if (0 && override_link_perm) + rm_shadow (nm); + else + backup_shadow (nm, &statbuf2); +} - if (verbose == 2 && recreate) - printf ("mismatch: Recreate %s (%i-%i)\n", path, major, minor); - } +/** Create device node by making symlink or calling update_device() */ +void create_dev (sname *spnt, char symlink) +{ + char linkto[64]; + int devtype = isblk (spnt->devtp)? S_IFBLK: S_IFCHR;; + strcpy (linkto, "/dev/"); + strcat (linkto, spnt->oldname); + + if (symlink) + create_symlink (linkto, spnt->name, devtype, spnt->major, spnt->minor); + else + update_device (linkto, spnt->name, devtype, spnt->major, spnt->minor); +} + +/* + * We need to "fix" any device nodes that are currently not used because + * it is a sercurity risk to leave these lying about. These are fixed + * by storing a shadow file. If these become active again, + * we will be able to use them again because the minor number will be + * set back again, and we are preserving the ownership and permissions. + */ +void sanitize_sdev () +{ + struct dirent * de; + char filename[64]; + DIR * sdir; + sname * spnt; + int status; + /* - * If we need to recreate the device, then do it. + * Next, delete all of the existing entries in the /dev/scsi directory. + * The idea is that if we have added/removed devices, the numbers might have + * changed. */ - if( recreate ) { - if (status == 0) - unlink (shadow); - status = mknod (path, newmode, makedev (major, minor)); - //printf("Recreate maj %i min %i\n", major, minor); - if( status == -1 ) { - fprintf (stderr, "mknod (%s) failed\n", path); - exit (1); - } - - if( uid != -1 ) - status = chown (path, uid, gid); + sdir = opendir (DEVSCSI); + while (1) { + de = readdir (sdir); + if (de == NULL) + break; + if (*de->d_name == '.') + continue; + /* If it's a .shadow. name, leave it alone */ + //if (strlen (de->d_name) >= strlen (SHADOW) && !strcmp (de->d_name, SHADOW)) + // continue; /* - * The mknod system call will not always use - * the right permissions because of umask. - * Fix it so that it is really correct. + * OK, we have the name. See whether this is something + * we know about already. */ - status = chmod (path, newmode); - if( status == -1 ) { - fprintf (stderr, "chmod (%s) failed\n", path); - exit (1); + for( spnt = reglist; spnt; spnt = spnt->next ) { + if( strcmp(de->d_name, strrchr (spnt->name, '/') + 1) == 0 ) + break; + } + /* Didn't we find it? */ + if (spnt == NULL) { + struct stat statbuf; + strcpy (filename, DEVSCSI); strcat (filename, "/"); + strcat (filename, de->d_name); + status = stat (filename, &statbuf); + if ( status == 0 && (S_ISLNK (statbuf.st_mode) || + S_ISCHR (statbuf.st_mode) || + S_ISBLK (statbuf.st_mode)) ) { + /* + * OK, this one is something new that we have to do something + * with. No big deal, stat it so we get the particulars, then + * create a new one with a safe minor number. + */ + unlink (filename); + if (!san_del) + backup_shadow (filename, &statbuf); + } } - - } + } + closedir (sdir); } -void create_dev (sname *spnt) + +/* + * Next, delete all of the existing entries in the /dev/scsi directory. + * The idea is that if we have added/removed devices, the numbers might have + * changed. + */ +void flush_sdev () { - char *linkto; - int status; - int newmode; - struct stat statbuf; - int devtype = isblk (spnt->devtp)? S_IFBLK: S_IFCHR;; + struct dirent * de; + char filename[60]; + struct stat stbuf; + DIR * sdir; - if (use_symlink) { - char shadow[64]; - strcpy (shadow, spnt->name); - strcat (shadow, SHADOW); - - linkto = oldscsiname (spnt); - unlink (spnt->name); - symlink (linkto, spnt->name); - - /* - * Now make sure that the device the symlink points to - * actually exists. If not, then create that device. - */ - status = stat (spnt->name, &statbuf); - if (status == 0) { - if (statbuf.st_rdev != makedev(spnt->major, spnt->minor)) - fprintf (stderr, - "Warning - device %s does not point to expected device\n", linkto); - } else { - newmode = devtype | filemode; - /* - * Unable to stat the file. Assume this is - * because it did not exist, so we create it. - */ - status = mknod (linkto, newmode, - makedev (spnt->major, spnt->minor)); - fprintf (stderr, "Creating %s\n", linkto); - } - /* If a shadow file exists, get perms from there */ - if (!stat (shadow, &statbuf)) { - chown (linkto, statbuf.st_uid, statbuf.st_gid); - chmod (linkto, devtype | statbuf.st_mode); + sdir = opendir (DEVSCSI); + while (1) { + de = readdir (sdir); + if (de == NULL) + break; + if (de->d_name[0] == '.') + continue; + //if (strlen (de->d_name >= strlen(SHADOW) && !strcmp (de->d_name+i, SHADOW)) + // continue; + //if (de->d_name[0] != 's' && de->d_name[0] != 'n') continue; + strcpy (filename, DEVSCSI); strcat (filename, "/"); + strcat (filename, de->d_name); + stat (filename, &stbuf); + unlink (filename); + backup_shadow (filename, &stbuf); } - free (linkto); - } - else /* ! use_symlink */ - update_device (spnt->name, devtype, spnt->major, spnt->minor); + closedir (sdir); + if (!quiet) + printf ("Flushed old " DEVSCSI " entries...\n"); + +} + + +/** Remove trailing whitespace */ +int inline rmv_trail_ws (char* str) +{ + int i = strlen (str); + int n = 0; + while (--i >= 0 && (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')) + n++; + str[i+1] = 0; + return n; } +/** Use ioctl to get hostnum, channel, id, lun tuple and hostid (ioport) */ int getidlun (int fd, sname *spnt, int setidlun) { int status; @@ -769,6 +892,7 @@ } +/** Get long scsi host adapter name by ioctl */ int getscsihostname (int fd, sname *spnt) { int status; @@ -785,9 +909,11 @@ return -1; }; spnt->hostname = strdup (hostname); + rmv_trail_ws (spnt->hostname); return status; } +/** Do hostname, idlun lookup, do inquiry and make name */ int getscsiinfo (int fd, sname *spnt, int setidlun) { int status; @@ -799,12 +925,14 @@ status = inquiry (fd, spnt); scsiname (spnt); + if (setidlun) + oldscsiname (spnt); return status; #if 0 - spnt = register_dev (scsidev, major, minor, - h_id, id[1], chan, scsi_id, lun, -1, hostname, NULL); - create_dev (spnt); + spnt = register_dev (scsidev, major, minor, + h_id, id[1], chan, scsi_id, lun, -1, hostname, 0, NULL, NULL); + create_dev (spnt, use_symlink); if (!quiet) printf ("Found %s (Type %02x) %c on %s \n", scsidev, spnt->inq_devtp, (spnt->rmvbl? 'R' : ' '), hostname); @@ -818,9 +946,9 @@ spnt1->major = disknum_to_sd_major (no); spnt1->minor = (no << 4) & 0xf0; spnt->partition = -1; spnt1->devtp = SD; - scsiname (spnt1); + scsiname (spnt1); oldscsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); spnt->related = spnt1; /* Check if device is there (i.e. medium inside) */ fd = open (spnt1->name, O_RDONLY | O_NONBLOCK); @@ -877,7 +1005,7 @@ spnt1->minor = minor; scsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); } return 0; } @@ -889,9 +1017,9 @@ spnt1->major = SCSI_TAPE_MAJOR; spnt1->minor = no; spnt1->devtp = ST; - scsiname (spnt1); + scsiname (spnt1); oldscsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); /* Check if device is there (i.e. medium inside) */ fd = open (spnt1->name, O_RDONLY | O_NONBLOCK); if (fd < 0) { @@ -924,7 +1052,7 @@ spnt1->minor |= 0x80; scsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); return 0; } @@ -936,9 +1064,9 @@ spnt1->major = OSST_MAJOR; spnt1->minor = no; spnt1->devtp = OSST; - scsiname (spnt1); + scsiname (spnt1); oldscsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); /* Check if device is there (i.e. medium inside) */ fd = open (spnt1->name, O_RDONLY | O_NONBLOCK); if (fd < 0) { @@ -976,7 +1104,7 @@ spnt1->minor |= 0x80; scsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); return 0; } @@ -988,9 +1116,9 @@ spnt1->major = SCSI_CDROM_MAJOR; spnt1->minor = no; spnt1->devtp = SR; - scsiname (spnt1); + scsiname (spnt1); oldscsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); fd = open (spnt1->name, O_RDONLY | O_NONBLOCK); /* No access to medium / part. table */ if (fd < 0) { @@ -1032,9 +1160,9 @@ spnt1->major = 86; /* SCSI_CHANGER_MAJOR; */ spnt1->minor = no; spnt1->devtp = SCH; - scsiname (spnt1); + scsiname (spnt1); oldscsiname (spnt1); spnt1->next = reglist; reglist = spnt1; - create_dev (spnt1); + create_dev (spnt1, use_symlink); fd = open (spnt1->name, O_RDONLY | O_NONBLOCK); /* No access to medium / part. table */ if (fd < 0) { @@ -1119,6 +1247,7 @@ } } spnt = (sname*) malloc (sizeof (sname)); + memset (spnt, 0, sizeof (sname)); spnt->major = major; spnt->minor = minor; spnt->devtp = SG; spnt->name = TESTDEV; spnt->partition = -1; @@ -1136,7 +1265,7 @@ //scsiname (spnt); spnt->next = reglist; reglist = spnt; - create_dev (spnt); + create_dev (spnt, use_symlink); devtp = inq_devtp_to_devtp (spnt->inq_devtp, spnt); @@ -1186,145 +1315,532 @@ //unlink (TESTDEV); } -#if 0 +char fourlnbuf[4][128]; +/* Fill fourlnbuf with the next record from /proc/scsi/scsi */ +int procscsi_readrecord (FILE* f) +{ + int c; char *ptr; + fourlnbuf[0][0] = 0; fourlnbuf[1][0] = 0; + fourlnbuf[2][0] = 0; fourlnbuf[3][0] = 0; + do { + ptr = fgets (fourlnbuf[0], 128, f); + if (!ptr || feof (f)) + return -1; + } while (memcmp (fourlnbuf[0], "Host:", 5)); + ptr = fgets (fourlnbuf[1], 128, f); + ptr = fgets (fourlnbuf[2], 128, f); + /* Test for extensions ... */ + c = fgetc (f); + if (c == EOF) + return 0; + else { + ungetc (c, f); + if (c != 'H') { + ptr = fgets (fourlnbuf[3], 128, f); + c = fgetc (f); + if (c != EOF) + ungetc (c, f); + } + } + //printf ("%s", fourlnbuf[0]); printf ("%s", fourlnbuf[1]); + //printf ("%s", fourlnbuf[2]); printf ("%s", fourlnbuf[3]); + //printf ("%i\n", fourlnbuf[3][0]); + return 0; +} -/* Test for availability of /proc/scsi/scsi extensions */ -int scsi_ext_status () +#ifndef TYPE_PRINTER +# define TYPE_PRINTER 0x02 +#endif +#ifndef TYPE_COMM +# define TYPE_COMM 0x09 +#endif + +const char *const scsi_device_types[] = { + "Direct-Access", + "Sequential-Access", + "Printer", + "Processor", + "WORM", + "CD-ROM", + "Scanner", + "Optical Device", + "Medium Changer", + "Communications", + "Unknown", + "Unknown", + "Unknown", + "Enclosure", +}; + + +char linux_to_devtp (const char* tp) +{ + int idx = 0; + if (!strcmp (tp, "Unknown")) + return 0x1f; + for (; idx < 14; ++idx) + if (!strcmp (tp, scsi_device_types[idx])) + return idx; + fprintf (stderr, "Linux kernel reports new device type \"%s\". Mail author!\n", + tp); + return 0x1f; +} + +/* Parse contents of fourlnbuf */ +int procscsi_parse (sname *spnt) +{ + char vendor[9]; + char product[17]; + char rev[5]; + char devtype[21]; + char hldev0[16], hldev1[16], hldev2[16], hldev3[16]; + int ansi; + + sscanf (fourlnbuf[0], "Host: scsi%i Channel: %d Id: %d Lun: %d", + &spnt->hostnum, &spnt->chan, &spnt->id, &spnt->lun); + sscanf (fourlnbuf[1], " Vendor: %8c Model: %16c Rev: %4c", + vendor, product, rev); + vendor[8] = 0; product[16] = 0; rev[4] = 0; + rmv_trail_ws (vendor); rmv_trail_ws (product); rmv_trail_ws (rev); + spnt->manufacturer = strdup (vendor); + spnt->model = strdup (product); + spnt->rev = strdup (rev); + sscanf (fourlnbuf[2], " Type: %20s ANSI SCSI revision: %d", + devtype, &ansi); + spnt->inq_devtp = linux_to_devtp (devtype); + if (!fourlnbuf[3][0]) + return 0; + return sscanf (fourlnbuf[3], " Attached drivers: %16s %16s %16s %16s", + hldev0, hldev1, hldev2, hldev3); } -/* Try to switch on extensions */ -void scsi_ext_on () +int procscsiext_parse (sname *spnt, int idx) { + char hldev[4][16]; + char* hdev; char* devptr; + char tp; + int n = sscanf (fourlnbuf[3], " Attached drivers: %16s %16s %16s %16s", + hldev[0], hldev[1], hldev[2], hldev[3]); + if (idx >= n) + return -1; + else + hdev = hldev[idx]; + + for (devptr = hdev; *devptr != '(' && *devptr != 0; ++devptr); + *devptr++ = 0; + spnt->oldname = strdup (hdev); + + sscanf (devptr, "%c:%x:%x)", &tp, &spnt->major, &spnt->minor); + + // We don't use tp (which can be 'b' or 'c') + + if (!memcmp (hdev, "sg", 2)) + spnt->devtp = SG; + else + spnt->devtp = inq_devtp_to_devtp (spnt->inq_devtp, spnt); + + return 0; } -/* Try to switch on extensions */ -void scsi_ext_off () +char* find_scsihostname (int hnum) { + struct dirent *sde; + DIR * sdir; + char path[64]; + char num[8]; + char* const phost = path + strlen("/proc/scsi/"); + + strcpy (path, "/proc/scsi/"); + sprintf (num, "/%d", hnum); + + sdir = opendir ("/proc/scsi"); + if (!sdir) { + fprintf (stderr, "can't read /proc/scsi/: %s\n", + strerror (errno)); + abort (); + } + while (1) { + int fd; + sde = readdir (sdir); + if (!sde) + break; + if (!strcmp (sde->d_name, "scsi") || *sde->d_name == '.') + continue; + strcpy (phost, sde->d_name); + strcat (phost, num); + fd = open (path, O_RDONLY); + if (fd > 0) { + char * nm = strdup (sde->d_name); + close (fd); + closedir (sdir); + return nm; + } + } + closedir (sdir); + return 0; } -void build_sgdevlist_procscsi () +unsigned int find_ioport (const char* nm) { - int fd; - struct stat statbuf; - int status; - sname * spnt; - int miss = 0; - int minor = 0; int major = SCSI_GENERIC_MAJOR; - char devchar = 'g'; int mode = O_RDWR; - int devtype = (SCSI_BLK_MAJOR(major)? S_IFBLK: S_IFCHR); - char* buf; int buflen; - FILE* mapfile; + unsigned char lnbuf[128]; + char nm2[64]; char *nmptr; + char * buf; + FILE * iop = fopen ("/proc/ioports", "r"); + if (!iop) + return 0; + /* nm2 = to_lower (nm); */ + strcpy (nm2, nm); + for (nmptr = nm2; *nmptr; ++nmptr) + *nmptr = tolower (*nmptr); + + while (!feof (iop)) { + unsigned int io1, io2; + char name[64]; + buf = fgets (lnbuf, 128, iop); + if (!buf) + break; + sscanf (lnbuf, " %x-%x : %s", &io1, &io2, name); + if (!strcmp (name, "PCI")) + continue; + /* name = to_lower (name); */ + for (nmptr = name; *nmptr; ++nmptr) + *nmptr = tolower (*nmptr); + if (!strcmp (nm2, name)) { + fclose (iop); + return io1; + } + } + fclose (iop); + return 0; +} - status = stat (DEVSCSI, &statbuf); - if (status == -1) - return; - status = stat (TESTDEV, &statbuf); - if (status == 0) +void fill_in_proc (sname * spnt) +{ + int fd; + spnt->shorthostname = find_scsihostname (spnt->hostnum); + spnt->hostname = strdup (spnt->shorthostname); + if (!spnt->shorthostname) { + fprintf (stderr, "scsidev: warning: could not deduce hostname & hostid\n"); + return; + } + spnt->hostid = find_ioport (spnt->shorthostname); + + fd = mknod (TESTDEV, 0600 | (isblk(spnt->inq_devtp)? S_IFBLK: S_IFCHR), + makedev (spnt->major, spnt->minor)); + if (fd) { + fprintf (stderr, "scsidev: Can't mknod " TESTDEV ": %s\n", + strerror (errno)); + return; + } + fd = open (TESTDEV, O_RDWR | O_NONBLOCK); unlink (TESTDEV); - - if (verbose >= 1) - fprintf (stderr, "Building list for s%c (%s dev major %i)\n", - devchar, (SCSI_BLK_MAJOR(major)? "block": "char"), major); - - mapfile = fopen (PROCSCSI, "r"); - if (!mapfile) { - fprintf (stderr, "scsidev: could not open " PROCSCSI ": %s\n", - strerror (errno)); - return; - } - - buf = malloc(128); buflen = 128; + inquiry (fd, spnt); + close (fd); + scsiname (spnt); +} + - - while (getline (&buf, &buflen, mapfile) != -1) - { - int h,c,i,l; - int inqtp, onl; - char *nm, *dev, *rest; - if (buf[0] == '#') - continue; - +void fill_in_sg (sname * spnt) +{ + int status; int fd; errno = 0; - status = mknod ( TESTDEV, 0600 | devtype , - makedev (major, minor) ); - if (status) { perror ("scsidev: mknod"); exit (3); } - fd = open (TESTDEV, mode); - unlink (TESTDEV); - if (fd == -1) { - if (verbose == 2) - { - fprintf (stderr, "open(%x/%x) returned %d (%d)\n", - major, minor, fd, errno); - } - miss++; - if (miss > maxmiss) break; - else { minor++; continue; } + status = mknod (TESTDEV, 0600 | S_IFCHR, + makedev (spnt->major, spnt->minor)); + if (status) { + perror ("scsidev: mknod"); + exit (3); } - spnt = (sname*) malloc (sizeof (sname)); - spnt->major = major; spnt->minor = minor; - spnt->name = TESTDEV; spnt->partition = -1; - status = getscsiinfo (fd, spnt); + fd = open (TESTDEV, O_RDWR); + unlink (TESTDEV); + getscsiinfo (fd, spnt, 0); close (fd); + spnt->shorthostname = find_scsihostname (spnt->hostnum); + if (spnt->hostid == 0 && spnt->shorthostname) + spnt->hostid = find_ioport (spnt->shorthostname); +} - if (status) { free (spnt); return; }; - scsiname (spnt); - spnt->next = reglist; reglist = spnt; - create_dev (spnt); +/* Create non-rew. alternative for a tape */ +void create_ntape (sname * spnt) +{ + sname * spnt1 = sname_dup (spnt); + spnt1->minor |= 0x80; + scsiname (spnt1); oldscsiname (spnt1); + create_dev (spnt1, use_symlink); + spnt1->next = reglist; reglist = spnt1; +} - if (!quiet) printf ("Found %s (Type %02x) %c on %s \n", spnt->name, - spnt->inq_devtp, (spnt->rmvbl? 'R' : ' '), - spnt->hostname); +/* Use /proc/partitions to scan for partitions */ +void create_partitions (sname * spnt) +{ + char pline[128]; char *ptr; + FILE *pf = fopen ("/proc/partitions", "r"); + if (!pf) { + fprintf (stderr, "scsidev: Couldn't read /proc/partitions: %s\n", + strerror (errno)); + return; + } + while (!feof (pf)) { + unsigned int maj, min, blk; + char nm[8], nm2[8]; + ptr = fgets (pline, 128, pf); + if (!ptr) + break; + blk = sscanf (pline, " %d %d %d %8s", &maj, &min, &blk, nm); + if (blk < 4) + continue; + + /* Strip part */ + strcpy (nm2, nm); + if (isdigit (nm2[strlen(nm2)-1])) { + for (ptr = nm2+strlen(nm2)-1; isdigit(*ptr); --ptr); + *(++ptr) = 0; + } + ptr = nm + strlen(nm2); + if (!strcmp (nm2, spnt->oldname)) { + /* Found it ! */ + if (maj != spnt->major || (min & 0xf0) != (spnt->minor)) { + fprintf (stderr, "scsidev: Inconsistency found: /proc/partitions reports " + " %s as %02x:%02x\n whereas we have %02x:%02x\n", + nm2, maj, min & 0xf0, spnt->major, spnt->minor); + dumpentry (spnt); + abort (); + } + if (isdigit (*ptr)) { + int part = atoi (ptr); + sname * spnt1 = sname_dup (spnt); + spnt1->minor |= part; spnt1->partition = part; + scsiname (spnt1); + spnt1->oldname = strdup (nm); + create_dev (spnt1, use_symlink); + spnt1->next = reglist; reglist = spnt1; + } + } + } +} + - /* Now register cdroms, tapes, and disks as well */ - switch (spnt->inq_devtp) { - case TYPE_DISK: - case TYPE_MOD: - if (!build_disk (spnt, disks)) disks++; - else if (!build_disk (spnt, disks+1)) disks += 2; - break; - case TYPE_TAPE: - if (OSST_SUPPORTS(spnt)) - { - if (!build_os_tape (spnt, tapes)) tapes++; - else if (!build_os_tape (spnt, tapes+1)) tapes += 2; - } - else - { - if (!build_tape (spnt, tapes)) tapes++; - else if (!build_tape (spnt, tapes+1)) tapes += 2; - } - break; - case TYPE_ROM: - case TYPE_WORM: - if (!build_cdrom (spnt, cdroms)) cdroms++; - else if (!build_cdrom (spnt, cdroms+1)) cdroms += 2; - break; - default: - /* nothing to be done */ +/* Create device nodes etc. */ +void dev_specific_setup (sname * spnt) +{ + if (verbose >= 2) { + printf ("dev_specific_setup () for %s\n", + spnt->name); + dumpentry (spnt); + } + create_dev (spnt, use_symlink); + switch (spnt->devtp) { + case SG: + case SR: + case SCH: + break; + case ST: + case OSST: + create_ntape (spnt); + break; + case SD: + spnt->partition = -1; + create_partitions (spnt); + break; + default: + fprintf (stderr, "scsidev: Unset dev type! Oops!\n"); + dumpentry (spnt); + abort (); } - minor += 1; - } - //unlink (TESTDEV); - free(buf); } +/* access a dev that's unlikely to exist to trigger module loads + * with a minimum of side effects */ +void trigger_one_mod (char blk, int major, int minor) +{ + int fd; + fd = mknod (TESTDEV, blk? S_IFBLK: S_IFCHR, makedev (major, minor)); + if (fd) + return; + fd = open (TESTDEV, O_RDWR | O_NONBLOCK); + if (fd > 0) + close (fd); + unlink (TESTDEV); +} + +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); + /* sch */ + trigger_one_mod (0, 86, 255); +} + +/* Build device list by reading /proc/scsi/scsi */ +void build_sgdevlist_procscsi () +{ + FILE* scsifile; + sname * spnt; + int status; + struct stat statbuf; + int rdevs = 0, hdevs = 0; + + status = stat (DEVSCSI, &statbuf); + if (status == -1) + return; + + status = stat (TESTDEV, &statbuf); + if (status == 0) + unlink (TESTDEV); + + if (verbose >= 1) + fprintf (stderr, "Building device list using " PROCSCSI "\n"); + + scsifile = fopen (PROCSCSI, "r"); + if (!scsifile) { + fprintf (stderr, "scsidev: could not open " PROCSCSI ": %s\n", + strerror (errno)); + return; + } + fclose (scsifile); + + /* Now, we need to make sure all high-level modules are loaded */ + trigger_module_loads (); + + scsifile = fopen (PROCSCSI, "r"); + /* parse /proc/scsi */ + while (!feof (scsifile)) { + int hl_per_dev; int hl; + sname *sgpnt = 0; + if (procscsi_readrecord (scsifile)) + break; + ++rdevs; + spnt = malloc (sizeof (sname)); + memset (spnt, 0, sizeof (sname)); + hl_per_dev = procscsi_parse (spnt); + if (!hl_per_dev) { + free (spnt); + fprintf (stderr, "Low level dev without HL driver?\n"); + continue; + } + spnt->next = reglist; reglist = spnt; + for (hl = 0; hl < hl_per_dev; ++hl) { + if (hl) { + spnt = sname_dup (spnt); + spnt->next = reglist; reglist = spnt; + } + ++hdevs; spnt->partition = -1; + procscsiext_parse (spnt, hl); + if (spnt->devtp == SG) + sgpnt = spnt; + } + /* Fill in missing information (inquiry, host adapter name ...) */ + if (sgpnt) + fill_in_sg (sgpnt); + else + fill_in_proc (spnt); + if (!sgpnt) + sgpnt = spnt; + /* Copy info to the colleagues + * and do special stuff depending on dev types. Such as the non-rew. + * variant for tapes or the partitions on disks + */ + for (hl = 0; hl < hl_per_dev; ++hl, spnt = spnt->next) { + if (spnt != sgpnt) { + spnt->serial = strdup (sgpnt->serial); + spnt->wwid = sgpnt->wwid; + spnt->rmvbl = sgpnt->rmvbl; + //spnt->unsafe = sgpnt->unsafe; + spnt->hostid = sgpnt->hostid; + spnt->hostname = strdup (sgpnt->hostname); + spnt->shorthostname = strdup (sgpnt->shorthostname); + spnt->related = sgpnt; + } + /* This does the handling of the dev nodes */ + scsiname (spnt); + dev_specific_setup (spnt); + } + } + if (verbose >= 1) { + printf ("%i real SCSI devices found, %i high level devs attached\n", + rdevs, hdevs); + dumplist (); + } + + +} + +/* Test for availability of /proc/scsi/scsi extensions */ +int procscsi_ext_status () +{ + int n; + FILE *f = fopen (PROCSCSI, "r"); + if (!f) { + fprintf (stderr, "scsidev: " PROCSCSI " does not exist?\n"); + return 0; + } + n = procscsi_readrecord (f); + fclose (f); + /* We don't know ... */ + if (n < 0) + return 1; + if (fourlnbuf[3][0]) + return 1; + else + return 0; +} + +/* Try to switch on extensions */ +int procscsi_ext_on () +{ + FILE *f = fopen (PROCSCSI, "w"); + if (!f) { + fprintf (stderr, "scsidev: " PROCSCSI ": %s\n", + strerror (errno)); + return -1; + } + fprintf (f, "scsi report-devs 1\n"); + fclose (f); + //fprintf (stderr, "Switched on " PROCSCSI " extensions\n"); + return 0; +} + +/* Try to switch on extensions */ +int procscsi_ext_off () +{ + FILE *f = fopen (PROCSCSI, "w"); + if (!f) { + fprintf (stderr, "scsidev: " PROCSCSI ": %s\n", + strerror (errno)); + return -1; + } + fprintf (f, "scsi report-devs 0\n"); + fclose (f); + //fprintf (stderr, "Switched off " PROCSCSI " extensions\n"); + return 0; +} + + int try_procscsi () { - int se_status = scsi_ext_status(); + int se_status = procscsi_ext_status(); if (!se_status) - scsi_ext_on(); - if (!scsi_ext_status()) + procscsi_ext_on(); + if (!procscsi_ext_status()) return -1; build_sgdevlist_procscsi(); if (!se_status) - scsi_ext_off(); + procscsi_ext_off(); + return 0; } -#endif void usage() @@ -1400,18 +1916,22 @@ flush_sdev (); #ifdef DEBUG - register_dev("/dev/scsi/sdh4-334c0i0l0", 8, 0, 6, 0x334, 0, 0, 0, -1, "debug", NULL); - register_dev("/dev/scsi/sdh4-334c0i0l0p1",8, 1, 6, 0x334, 0, 0, 0, 1, "debug", NULL); - register_dev("/dev/scsi/sdh4-334c0i0l0p2",8, 2, 6, 0x334, 0, 0, 0, 2, "debug", NULL); - register_dev("/dev/scsi/sdh4-334c0i0l0p3",8, 3, 6, 0x334, 0, 0, 0, 3, "debug", NULL); - register_dev("/dev/scsi/sgh4-334c0i0l0", 21, 0, 6, 0x334, 0, 0, 0, -1, "debug", NULL); - register_dev("/dev/scsi/sgh4-334c0i2l0", 21, 1, 6, 0x334, 0, 2, 0, -1, "debug", NULL); - register_dev("/dev/scsi/sgh4-334c0i5l0", 21, 2, 6, 0x334, 0, 5, 0, -1, "debug", NULL); - register_dev("/dev/scsi/srh4-334c0i2l0", 11, 0, 6, 0x334, 0, 2, 0, -1, "debug", NULL); - register_dev("/dev/scsi/sth4-334c0i5l0", 9, 0, 6, 0x334, 0, 5, 0, -1, "debug", NULL); - register_dev("/dev/scsi/rsth4-334c0i5l0", 9,128, 6, 0x334, 0, 5, 0, -1, "debug", NULL); + register_dev("/dev/scsi/sdh4-334c0i0l0", 8, 0, 6, 0x334, 0, 0, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sdh4-334c0i0l0p1",8, 1, 6, 0x334, 0, 0, 0, 1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sdh4-334c0i0l0p2",8, 2, 6, 0x334, 0, 0, 0, 2, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sdh4-334c0i0l0p3",8, 3, 6, 0x334, 0, 0, 0, 3, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sgh4-334c0i0l0", 21, 0, 6, 0x334, 0, 0, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sgh4-334c0i2l0", 21, 1, 6, 0x334, 0, 2, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sgh4-334c0i5l0", 21, 2, 6, 0x334, 0, 5, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/srh4-334c0i2l0", 11, 0, 6, 0x334, 0, 2, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/sth4-334c0i5l0", 9, 0, 6, 0x334, 0, 5, 0, -1, "debug", 0, NULL, NULL); + register_dev("/dev/scsi/rsth4-334c0i5l0", 9,128, 6, 0x334, 0, 5, 0, -1, "debug", 0, NULL, NULL); #else - build_sgdevlist (); + if (try_procscsi ()) { + if (!quiet) + fprintf (stderr, "/proc/scsi/scsi extensions not found. Fall back to scanning.\n"); + build_sgdevlist (); + } #endif if( show_serial ) { @@ -1434,6 +1954,7 @@ } } + //dumplist (); /* * Now, read the configuration file and see whether there @@ -1609,7 +2130,7 @@ char buffer[256]; char * pnt; char * pnt1; - sname * spnt, *match; + sname * spnt, * spnt1, *match; char scsidev[64]; int type; @@ -1747,6 +2268,7 @@ */ match = NULL; for (spnt = reglist; spnt; spnt = spnt->next) { + /* Don't alias aliases */ if( spnt->alias != NULL ) continue; /* @@ -1793,9 +2315,12 @@ strcmp(spnt->rev, rev) != 0 )) continue; - if( host != NULL && (spnt->hostname == NULL || - strncmp(spnt->hostname, host, strlen(host)) != 0 )) - continue; + if( host != NULL + && (spnt->hostname == NULL || + strncmp(spnt->hostname, host, strlen(host)) != 0) + && (spnt->shorthostname == NULL || + strncmp(spnt->shorthostname, host, strlen(host)) != 0) ) + continue; /* * We have a match. Record it and keep looking just in @@ -1826,13 +2351,13 @@ * as requested. */ if (!quiet) { - fprintf (stderr, "Alias device %s: %s=%s ", name, + fprintf (stderr, "Alias device %s: %s (%s)", name, strrchr (match->name, '/') + 1, - strrchr (oldscsiname (match), '/') + 1); + match->oldname); if (match->related) - fprintf (stderr, "(%s=%s)\n", + fprintf (stderr, " -> (%s, %s)\n", strrchr (match->related->name, '/') + 1, - strrchr (oldscsiname (match->related), '/') + 1); + match->related->oldname); else fprintf (stderr, "\n"); } @@ -1844,28 +2369,24 @@ * Just create it. */ sprintf (scsidev, DEVSCSI "/%s", name); - register_dev (scsidev, match->major, match->minor, - 0, 0, 0, 0, 0, 0, 0, match); - if (symlink_alias) { - unlink (scsidev); - symlink (match->name, scsidev); - } - else - update_device (scsidev, type, - match->major, match->minor); + spnt1 = register_dev (scsidev, match->major, match->minor, + match->hostnum, match->hostid, + match->chan, match->id, match->lun, 0, + match->hostname, match->name, match, 0); + create_dev (spnt1, symlink_alias); if( devtype_i == ST || devtype_i == OSST ) { + char nm2[64]; char * ptr; + ptr = strrchr (match->name, '/'); + strcpy (nm2, "scsi/n"); + strcat (nm2, ptr? ptr+1: match->name); sprintf (scsidev, DEVSCSI "/n%s", name); - register_dev (scsidev, match->major, match->minor | 0x80, - 0, 0, 0, 0, 0, 0, 0, match); - if (symlink_alias) { - char nm2[128]; strcpy (nm2, "n"); strcat (nm2, match->name); - unlink (scsidev); - symlink (nm2, scsidev); - } - else - update_device (scsidev, S_IFCHR, - match->major, (match->minor | 0x80) ); + + spnt1 = register_dev (scsidev, match->major, match->minor | 0x80, + match->hostnum, match->hostid, + match->chan, match->id, match->lun, 0, + match->hostname, nm2, match, spnt1); + create_dev (spnt1, symlink_alias); } if ( devtype_i == SD @@ -1878,38 +2399,37 @@ * ones we actually need to create. */ for( spnt = reglist; spnt; spnt = spnt->next ) { + sname * spnt2; if( spnt->alias != NULL ) continue; if( spnt->partition == -1 ) continue; - if( spnt->major != devtype_i ) continue; + if( spnt->devtp != devtype_i ) continue; if( spnt->id != match->id ) continue; if( spnt->lun != match->lun ) continue; if( spnt->chan != match->chan ) continue; if( spnt->hostnum != match->hostnum ) continue; - if (spnt->hostid != match->hostid ) continue; + if( spnt->hostid != match->hostid ) continue; sprintf(scsidev, DEVSCSI "/%s-p%d", name, spnt->partition); - register_dev (scsidev, match->major, spnt->minor, - 0, 0, 0, 0, 0, 0, 0, spnt); - if (symlink_alias) { - unlink (scsidev); - symlink (match->name, scsidev); - } else - update_device (scsidev, S_IFBLK, - match->major, spnt->minor); + spnt2 = register_dev (scsidev, match->major, spnt->minor, + match->hostnum, match->hostid, + match->chan, match->id, match->lun, spnt->partition, + match->hostname, spnt->name, spnt, spnt1); + create_dev (spnt2, symlink_alias); } } - } - else - { - if (!quiet) fprintf (stderr, "Unable to match device for line %d (alias %s)\n", - line, name); + } else { + if (!quiet) + fprintf (stderr, "Unable to match device for line %d (alias %s)\n", + line, name); } } fclose (configfile); } +/****************************** INQUIRY ***************************/ + void dumppage (unsigned char* page) { int ln = 4 + page[4]; @@ -1925,12 +2445,23 @@ char* getstr (char* page, int start, int stop) { - int ln = stop-start+1; int i; - char *str = (char*) malloc (ln+1); - memcpy (str, page+start, ln); - str[ln] = 0; - for (i = ln-1; i >= 0 && str[i] == ' '; i--) str[i] = 0; - return str; + int ln; + char* str; + /* Remove leading spaces */ + while (*(page+start) == ' ' && start < stop) + ++start; + /* Remove trailing spaces */ + while (*(page+stop) == ' ' && stop > start) + --stop; + /* empty */ + if (stop == start) + return 0; + /* otherwise copy */ + ln = stop-start+1; + str = (char*) malloc (ln+1); + memcpy (str, page+start, ln); + str[ln] = 0; + return str; } unsigned long long extract_wwid (unsigned char* page)