📄 smartd.cpp
字号:
return;}// deallocates all memory associated with ATA/SCSI device listsvoid RmAllDevEntries(){ int i; for (i=0; i<ATAandSCSIdevlist_max; i++) RmConfigEntry(ATAandSCSIdevlist+i, __LINE__); ATAandSCSIdevlist=FreeNonZero(ATAandSCSIdevlist, sizeof(cfgfile *)*ATAandSCSIdevlist_max, __LINE__, filenameandversion); ATAandSCSIdevlist_max=0; return;}// remove the PID filevoid RemovePidFile(){ if (pid_file) { if ( -1==unlink(pid_file) ) PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", pid_file, strerror(errno)); pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion); } return;}// Note if we catch a SIGUSR1void USR1handler(int sig){ if (SIGUSR1==sig) caughtsigUSR1=1; return;}#ifdef _WIN32// Note if we catch a SIGUSR2void USR2handler(int sig){ if (SIGUSR2==sig) caughtsigUSR2=1; return;}#endif// Note if we catch a HUP (or INT in debug mode)void HUPhandler(int sig){ if (sig==SIGHUP) caughtsigHUP=1; else caughtsigHUP=2; return;}// signal handler for TERM, QUIT, and INT (if not in debug mode)void sighandler(int sig){ if (!caughtsigEXIT) caughtsigEXIT=sig; return;}// signal handler that prints Goodbye message and removes pidfilevoid Goodbye(void){ // clean up memory -- useful for debugging RmAllConfigEntries(); RmAllDevEntries(); // delete PID file, if one was created RemovePidFile(); // remove alternate configfile name configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion); // useful for debugging -- have we managed memory correctly? if (debugmode || (bytes && exitstatus!=EXIT_NOMEM)) PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes); // if we are exiting because of a code bug, tell user if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM)) PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n"); if (exitstatus==0 && bytes) exitstatus=EXIT_BADCODE; // and this should be the final output from smartd before it exits PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus); return;}#define ENVLENGTH 1024// a replacement for setenv() which is not available on all platforms.// Note that the string passed to putenv must not be freed or made// invalid, since a pointer to it is kept by putenv(). This means that// it must either be a static buffer or allocated off the heap. The// string can be freed if the environment variable is redefined or// deleted via another call to putenv(). So we keep these on the stack// as long as the popen() call is underway.int exportenv(char* stackspace, const char *name, const char *value){ snprintf(stackspace,ENVLENGTH, "%s=%s", name, value); return putenv(stackspace);}char* dnsdomain(const char* hostname) { char *p = NULL;#ifdef HAVE_GETHOSTBYNAME struct hostent *hp; if ((hp = gethostbyname(hostname))) { // Does this work if gethostbyname() returns an IPv6 name in // colon/dot notation? [BA] if ((p = strchr(hp->h_name, '.'))) p++; // skip "." }#else ARGUSED(hostname);#endif return p;}#define EBUFLEN 1024// If either address or executable path is non-null then send and log// a warning email, or execute executablevoid MailWarning(cfgfile *cfg, int which, char *fmt, ...){ char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024]; char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN]; char environ_strings[11][ENVLENGTH]; time_t epoch; va_list ap; const int day=24*3600; int days=0; char *whichfail[]={ "EmailTest", // 0 "Health", // 1 "Usage", // 2 "SelfTest", // 3 "ErrorCount", // 4 "FailedHealthCheck", // 5 "FailedReadSmartData", // 6 "FailedReadSmartErrorLog", // 7 "FailedReadSmartSelfTestLog", // 8 "FailedOpenDevice", // 9 "CurrentPendingSector", // 10 "OfflineUncorrectableSector", // 11 "Temperature" // 12 }; char *address, *executable; mailinfo *mail; maildata* data=cfg->mailwarn;#ifndef _WIN32 FILE *pfp=NULL;#else char stdinbuf[1024]; int boxmsgoffs, boxtype;#endif const char *newadd=NULL, *newwarn=NULL; const char *unknown="[Unknown]"; // See if user wants us to send mail if(!data) return; address=data->address; executable=data->emailcmdline; if (!address && !executable) return; // which type of mail are we sending? mail=(data->maillog)+which; // checks for sanity if (data->emailfreq<1 || data->emailfreq>3) { PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq); return; } if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) { PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n", which, (int)sizeof(whichfail)); return; } // Return if a single warning mail has been sent. if ((data->emailfreq==1) && mail->logged) return; // Return if this is an email test and one has already been sent. if (which == 0 && mail->logged) return; // To decide if to send mail, we need to know what time it is. epoch=time(NULL); // Return if less than one day has gone by if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) return; // Return if less than 2^(logged-1) days have gone by if (data->emailfreq==3 && mail->logged){ days=0x01<<(mail->logged-1); days*=day; if (epoch<(mail->lastsent+days)) return; } // record the time of this mail message, and the first mail message if (!mail->logged) mail->firstsent=epoch; mail->lastsent=epoch; // get system host & domain names (not null terminated if length=MAX) #ifdef HAVE_GETHOSTNAME if (gethostname(hostname, 256)) strcpy(hostname, unknown); else { char *p=NULL; hostname[255]='\0'; p = dnsdomain(hostname); if (p && *p) { strncpy(domainname, p, 255); domainname[255]='\0'; } else strcpy(domainname, unknown); }#else strcpy(hostname, unknown); strcpy(domainname, unknown);#endif #ifdef HAVE_GETDOMAINNAME if (getdomainname(nisdomain, 256)) strcpy(nisdomain, unknown); else nisdomain[255]='\0';#else strcpy(nisdomain, unknown);#endif // print warning string into message va_start(ap, fmt); vsnprintf(message, 256, fmt, ap); va_end(ap); // appropriate message about further information additional[0]=original[0]=further[0]='\0'; if (which) { sprintf(further,"You can also use the smartctl utility for further investigation.\n"); switch (data->emailfreq){ case 1: sprintf(additional,"No additional email messages about this problem will be sent.\n"); break; case 2: sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n"); break; case 3: sprintf(additional,"Another email message will be sent in %d days if the problem persists\n", (0x01)<<mail->logged); break; } if (data->emailfreq>1 && mail->logged){ dateandtimezoneepoch(dates, mail->firstsent); sprintf(original,"The original email about this issue was sent at %s\n", dates); } } snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname); // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx". if (!executable)#ifdef DEFAULT_MAILER executable = DEFAULT_MAILER ;#else#ifndef _WIN32 executable = "mail";#else executable = "blat"; // http://blat.sourceforge.net/#endif#endif // make a private copy of address with commas replaced by spaces // to separate recipients if (address) { address=CustomStrDup(data->address, 1, __LINE__, filenameandversion);#ifndef _WIN32 // blat mailer needs comma { char *comma=address; while ((comma=strchr(comma, ','))) *comma=' '; }#endif } // Export information in environment variables that will be useful // for user scripts exportenv(environ_strings[0], "SMARTD_MAILER", executable); exportenv(environ_strings[1], "SMARTD_MESSAGE", message); exportenv(environ_strings[2], "SMARTD_SUBJECT", subject); dateandtimezoneepoch(dates, mail->firstsent); exportenv(environ_strings[3], "SMARTD_TFIRST", dates); snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates); exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]); if (address) exportenv(environ_strings[6], "SMARTD_ADDRESS", address); exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name); switch (cfg->controller_type) { case CONTROLLER_3WARE_678K: case CONTROLLER_3WARE_9000_CHAR: case CONTROLLER_3WARE_678K_CHAR: { char *s,devicetype[16]; sprintf(devicetype, "3ware,%d", cfg->controller_port-1); exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype); if ((s=strchr(cfg->name, ' '))) *s='\0'; exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); if (s) *s=' '; } break; case CONTROLLER_CCISS: { char *s,devicetype[16]; sprintf(devicetype, "cciss,%d", cfg->controller_port-1); exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype); if ((s=strchr(cfg->name, ' '))) *s='\0'; exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); if (s) *s=' '; } break; case CONTROLLER_ATA: exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata"); exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); break; case CONTROLLER_MARVELL_SATA: exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell"); exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); break; case CONTROLLER_SCSI: exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi"); exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); break; case CONTROLLER_SAT: exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "sat"); exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); break; case CONTROLLER_HPT: { char *s,devicetype[16]; sprintf(devicetype, "hpt,%d/%d/%d", cfg->hpt_data[0], cfg->hpt_data[1], cfg->hpt_data[2]); exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype); if ((s=strchr(cfg->name, ' '))) *s='\0'; exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); if (s) *s=' '; } break; } snprintf(fullmessage, 1024, "This email was generated by the smartd daemon running on:\n\n" " host name: %s\n" " DNS domain: %s\n" " NIS domain: %s\n\n" "The following warning/error was logged by the smartd daemon:\n\n" "%s\n\n" "For details see host's SYSLOG (default: /var/log/messages).\n\n" "%s%s%s", hostname, domainname, nisdomain, message, further, original, additional); exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage); // now construct a command to send this as EMAIL#ifndef _WIN32 if (address) snprintf(command, 2048, "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n" "%sENDMAIL\n", subject, address, fullmessage); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -