diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp index 258e81d55235cbddfc8a01671d5d40b61ac39a25..28a189f9e0f4b2c090656b4eabad822a65d79409 100644 --- a/sm5/smartd.cpp +++ b/sm5/smartd.cpp @@ -50,7 +50,7 @@ #include "utility.h" extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; -const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.170 2003/06/13 18:01:17 ballen4705 Exp $" +const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.170.2.1 2004/08/13 00:04:38 likewise Exp $" ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; // Forward declaration @@ -234,7 +234,10 @@ void printandmail(cfgfile *cfg, int which, int priority, char *fmt, ...){ // for user scripts setenv("SMARTD_MAILER", executable, 1); setenv("SMARTD_DEVICE", cfg->name, 1); - setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1); + if (cfg->trymvsata) + setenv("SMARTD_DEVICETYPE", "mvsata", 1); + else + setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1); setenv("SMARTD_MESSAGE", message, 1); setenv("SMARTD_SUBJECT", subject, 1); dateandtimezoneepoch(dates, mail->firstsent); @@ -435,7 +438,7 @@ void printhead(){ // prints help info for configuration file Directives void Directives() { printout(LOG_INFO,"Configuration file (/etc/smartd.conf) Directives (after device name):\n"); - printout(LOG_INFO," -d TYPE Set the device type: ata, scsi, removable\n"); + printout(LOG_INFO," -d TYPE Set the device type: ata, scsi, mvsata, removable\n"); printout(LOG_INFO," -T TYPE Set the tolerance to one of: normal, permissive\n"); printout(LOG_INFO," -o VAL Enable/disable automatic offline tests (on/off)\n"); printout(LOG_INFO," -S VAL Enable/disable attribute autosave (on/off)\n"); @@ -563,7 +566,8 @@ int atadevicescan2(atadevices_t *devices, cfgfile *cfg){ // device open failed return 1; printout(LOG_INFO,"Device: %s, opened\n", device); - + //set the global MVSATA flag for further ioctl() usage + con->ismvsata = cfg->trymvsata; // Get drive identity structure if (ataReadHDIdentity (fd,&drive)){ // Unable to read Identity structure @@ -759,6 +763,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) int k, fd, err; char *device = cfg->name; struct scsi_iec_mode_page iec; + int peri_dt = 0, len = 0; UINT8 tBuf[64]; // should we try to register this as a SCSI device? @@ -774,7 +779,38 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) #endif } printout(LOG_INFO,"Device: %s, opened\n", device); - + + //Check whether SCSI controller is MVSATA + err = scsiStdInquiry(fd, tBuf, sizeof(tBuf)); + if (err) + { + if (1 == err) + printout(LOG_ERR, "Device: %s, failed Inquiry [err=%d]\n", + device, err); + close(fd); + + } + len = tBuf[4] + 5; + peri_dt = tBuf[0] & 0x1f; + if (peri_dt == 0) /*TYPE_DISK*/ + { + if (len >= 42) + { + if (!strcmp(&tBuf[36], "MVSATA")) + { + /*MVSATA controller detected*/ + cfg->tryata = 1; + cfg->tryscsi = 0; + cfg->trymvsata = 1; + cfg->scsidevicenum = numscsidevices; + cfg->atadevicenum = -1; + closedevice(fd, device); + return 3; + } + } + } + //MVSATA + // check that it's ready for commands. IE stores its stuff on the media. if ((err = scsiTestUnitReady(fd))) { if (1 == err) @@ -853,6 +889,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) // record number of device, type of device, increment device count cfg->tryata = 0; cfg->tryscsi = 1; + cfg->trymvsata = 0; cfg->scsidevicenum = numscsidevices; cfg->atadevicenum = -1; ++numscsidevices; @@ -862,6 +899,218 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) return 0; } +int mvsatadevicescan2(scsidevices_t *devices, cfgfile *cfg) +{ + int fd; + struct hd_driveid drive; + char *device=cfg->name; + + // should we try to register this as an ATA device? + if (!(cfg->trymvsata)) + return 1; + + // open the device + if ((fd=opendevice(device, O_RDONLY | O_NONBLOCK))<0) + // device open failed + return 1; + + /*set the global MVSATA flag for further ioctl()*/ + con->ismvsata = cfg->trymvsata; + if (cfg->trymvsata) + { + /*Reset ignored values*/ + devices->SmartPageSupported = 0; + devices->Temperature = 0; + devices->TempPageSupported = 0; + } + printout(LOG_INFO,"Device: %s, opened\n", device); + // Get drive identity structure + if (ataReadHDIdentity (fd,&drive)){ + // Unable to read Identity structure + printout(LOG_INFO,"Device: %s, unable to read Device Identity Structure\n",device); + close(fd); + return 2; + } + + // pass user setings on to low-level ATA commands + con->fixfirmwarebug = cfg->fixfirmwarebug; + + // Show if device in database, and use preset vendor attribute + // options unless user has requested otherwise. + if (!cfg->ignorepresets){ + + // do whatever applypresets decides to do + if (applypresets(&drive, cfg->attributedefs, con)<0) + printout(LOG_INFO, "Device: %s, not found in smartd database.\n", device); + else + printout(LOG_INFO, "Device: %s, found in smartd database.\n", device); + + // then save the correct state of the flag (applypresets may have changed it) + cfg->fixfirmwarebug = con->fixfirmwarebug; + } + else + printout(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", device); + + // If requested, show which presets would be used for this drive + if (cfg->showpresets) { + int savedebugmode=debugmode; + printout(LOG_INFO, "Device %s: presets are:\n", device); + if (!debugmode) + debugmode=2; + showpresets(&drive); + debugmode=savedebugmode; + } + + if (!cfg->permissive && !ataSmartSupport(&drive)){ + // SMART not supported + printout(LOG_INFO,"Device: %s, appears to lack SMART, use '-T permissive' Directive to try anyway.\n",device); + close(fd); + return 2; + } + + if (ataEnableSmart(fd)){ + // Enable SMART command has failed + printout(LOG_INFO,"Device: %s, could not enable SMART capability\n",device); + close(fd); + return 2; + } + + // disable device attribute autosave... + if (cfg->autosave==1){ + if (ataDisableAutoSave(fd)) + printout(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",device); + else + printout(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",device); + } + + // or enable device attribute autosave + if (cfg->autosave==2){ + if (ataEnableAutoSave(fd)) + printout(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",device); + else + printout(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",device); + } + + // capability check: SMART status + if (cfg->smartcheck && ataSmartStatus2(fd)==-1){ + printout(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",device); + cfg->smartcheck=0; + } + + // capability check: Read smart values and thresholds + if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->autoofflinetest) { + devices->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)); + devices->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)); + + if (!devices->smartval || !devices->smartthres){ + printout(LOG_CRIT,"Not enough memory to obtain SMART data\n"); + exit(EXIT_NOMEM); + } + + if (ataReadSmartValues(fd,devices->smartval) || + ataReadSmartThresholds (fd,devices->smartthres)){ + printout(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",device); + free(devices->smartval); + free(devices->smartthres); + + // make it easy to recognize that we've deallocated + devices->smartval=NULL; + devices->smartthres=NULL; + cfg->usagefailed=cfg->prefail=cfg->usage=0; + } + } + + // enable/disable automatic on-line testing + if (cfg->autoofflinetest){ + // is this an enable or disable request? + char *what=(cfg->autoofflinetest==1)?"disable":"enable"; + if (!devices->smartval) + printout(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",device, what); + else { + // if command appears unsupported, issue a warning... + if (!isSupportAutomaticTimer(devices->smartval)) + printout(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",device); + // ... but then try anyway + if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd)) + printout(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", device, what); + else + printout(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", device, what); + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int val; + + // see if device supports Self-test logging. Note that the + // following line is not a typo: Device supports self-test log if + // and only if it also supports error log. + if (!isSmartErrorLogCapable(devices->smartval)){ + printout(LOG_INFO, "Device: %s, does not support SMART Self-test Log.\n", device); + cfg->selftest=0; + cfg->selflogcount=0; + } + else { + // get number of Self-test errors logged + val=selftesterrorcount(fd, device); + if (val>=0) + cfg->selflogcount=val; + else + cfg->selftest=0; + } + } + + // capability check: ATA error log + if (cfg->errorlog){ + int val; + + // see if device supports error logging + if (!isSmartErrorLogCapable(devices->smartval)){ + printout(LOG_INFO, "Device: %s, does not support SMART Error Log.\n", device); + cfg->errorlog=0; + cfg->ataerrorcount=0; + } + else { + // get number of ATA errors logged + val=ataerrorcount(fd, device); + if (val>=0) + cfg->ataerrorcount=val; + else + cfg->errorlog=0; + } + } + + // If no tests available or selected, return + if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || + cfg->usagefailed || cfg->prefail || cfg->usage)) { + close(fd); + return 3; + } + + // Do we still have entries available? + if (numatadevices>=MAXATADEVICES){ + printout(LOG_CRIT,"smartd has found more than MAXATADEVICES=%d ATA devices.\n" + "Recompile code from " PROJECTHOME " with larger MAXATADEVICES\n",(int)numatadevices); + exit(EXIT_CCONST); + } + + // register device + printout(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device); + + // we were called from a routine that has global storage for the name. Keep pointer. + devices->devicename=device; + devices->cfg=cfg; + cfg->tryscsi=0; + cfg->tryata=1; + cfg->trymvsata=1; + cfg->atadevicenum = -1; + cfg->scsidevicenum = numscsidevices; + numscsidevices++; + // close file descriptor + closedevice(fd, device); + return 0; +} + // We compare old and new values of the n'th attribute. Note that n // is NOT the attribute ID number.. If (Normalized & Raw) equal, // then return 0, else nonzero. @@ -965,7 +1214,8 @@ int ataCheckDevice(atadevices_t *drive){ printandmail(cfg, 9, LOG_CRIT, "Device: %s, unable to open device", name); return 1; } - + //set MVSATA global flag for further ioctl() + con->ismvsata = cfg->trymvsata; // check smart status if (cfg->smartcheck){ int status=ataSmartStatus2(fd); @@ -1122,6 +1372,16 @@ int ataCheckDevice(atadevices_t *drive){ return 0; } +/* Calls ataCheckDevice instead of scsi */ +int mvsataCheckDevice(scsidevices_t *drive) +{ + atadevices_t atadrive; + atadrive.devicename = drive->devicename; + atadrive.cfg = drive->cfg; + atadrive.smartthres = drive->smartthres; + atadrive.smartval = drive->smartval; + return ataCheckDevice(&atadrive); +} int scsiCheckDevice(scsidevices_t *drive) { @@ -1191,7 +1451,12 @@ void CheckDevices(atadevices_t *atadevices, scsidevices_t *scsidevices){ ataCheckDevice(atadevices+i); for (i=0; i<numscsidevices; i++) - scsiCheckDevice(scsidevices+i); + { + if (scsidevices[i].cfg->trymvsata) + mvsataCheckDevice(scsidevices+i); + else + scsiCheckDevice(scsidevices+i); + } // This option is primarily for distribution developers who want // an automated procedure for seeing if smartd works correctly. @@ -1372,12 +1637,21 @@ int parsetoken(char *token,cfgfile *cfg){ } else if (!strcmp(arg, "ata")) { cfg->tryata = 1; cfg->tryscsi = 0; + cfg->trymvsata = 0; } else if (!strcmp(arg, "scsi")) { cfg->tryscsi = 1; cfg->tryata = 0; + cfg->trymvsata = 0; } else if (!strcmp(arg, "removable")) { cfg->removable = 1; - } else { + } + //set the MVSATA flag if device type is "mvsata" + else if (!strcmp(arg, "mvsata")) { + cfg->tryscsi = 0; + cfg->tryata = 1; + cfg->trymvsata = 1; + } + else { badarg = 1; } break; @@ -1638,7 +1912,8 @@ int parseconfigline(int entry, int lineno,char *line){ cfg->lineno=lineno; cfg->tryscsi=1; cfg->tryata=1; - + //reset the MVSATA flag: will be detected automatically if present + cfg->trymvsata=0; // Try and recognize if a IDE or SCSI device. These can be // overwritten by configuration file directives. if (GUESS_DEVTYPE_ATA == guess_linux_device_type(name)) @@ -2087,7 +2362,8 @@ int makeconfigentries(int num, char *name, int isata, int start, int scandirecti // select if it's a SCSI or ATA device cfg->tryata=isata; cfg->tryscsi=!isata; - + //reset trymvsata flag + cfg->trymvsata = 0; // put in the device name cfg->name=strdup(name); if (!cfg->name) { @@ -2118,6 +2394,7 @@ int main (int argc, char **argv){ atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices; scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices; int i, entries, scandirective=0, scanning=0; + int res = 0; smartmonctrl control; // initialize global communications variables @@ -2160,8 +2437,7 @@ int main (int argc, char **argv){ // initialize total number of entries to seach for entries=0; doata=config->tryata; - doscsi=config->tryscsi; - + doscsi=config->tryscsi || config->trymvsata; // make list of ATA devices to search for if (doata) entries+=makeconfigentries(MAXATADEVICES, "/dev/hda", 1, entries, scandirective); @@ -2175,17 +2451,41 @@ int main (int argc, char **argv){ int notregistered=1; // register ATA devices - if (config[i].tryata){ - if (atadevicescan2(atadevicesptr+numatadevices, config+i)) - cantregister(config[i].name, "ATA", config[i].lineno, scandirective); + if (config[i].trymvsata) + { + /*scan device connected to MVSATA*/ + if (mvsatadevicescan2(scsidevicesptr+numscsidevices, config+i)) + cantregister(config[i].name, "MVSATA", config[i].lineno, scandirective); else - notregistered=0; + notregistered=0; + } + else + { + if (config[i].tryata) + { + if (atadevicescan2(atadevicesptr+numatadevices, config+i)) + cantregister(config[i].name, "ATA", config[i].lineno, scandirective); + else + notregistered=0; + } } // then register SCSI devices - if (config[i].tryscsi){ - if (scsidevicescan(scsidevicesptr+numscsidevices, config+i)) - cantregister(config[i].name, "SCSI", config[i].lineno, scandirective); + if (config[i].tryscsi) + { + config[i].trymvsata = 0; + res = scsidevicescan(scsidevicesptr+numscsidevices, config+i); + if (res) + { + //if MVSATA controller detected rescan with ATA options + if (config[i].trymvsata) + { + i--; + continue; + } + else + cantregister(config[i].name, "SCSI", config[i].lineno, scandirective); + } else notregistered=0; }