From 28ab08b3a7c9344709a8e5c32df1a2288bb5b641 Mon Sep 17 00:00:00 2001 From: ballen4705 <ballen4705@4ea69e1a-61f1-4043-bf83-b5c94c648137> Date: Fri, 5 Dec 2003 13:14:07 +0000 Subject: [PATCH] smartd: reduce per-device memory footprint by making mail-warning info dynamically allocated. Also remove potential memory leak if use has -m Directive twice and keeps reloading the config file (highly unlikely this would ever be noticed!) git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@1339 4ea69e1a-61f1-4043-bf83-b5c94c648137 --- sm5/CHANGELOG | 10 ++- sm5/smartd.c | 210 ++++++++++++++++++++++++++++-------------------- sm5/smartd.cpp | 210 ++++++++++++++++++++++++++++-------------------- sm5/smartd.h | 41 +++++----- sm5/utility.c | 14 +++- sm5/utility.cpp | 14 +++- sm5/utility.h | 5 +- 7 files changed, 309 insertions(+), 195 deletions(-) diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG index 5b613fa6f..c727dec1d 100644 --- a/sm5/CHANGELOG +++ b/sm5/CHANGELOG @@ -1,6 +1,6 @@ CHANGELOG for smartmontools -$Id: CHANGELOG,v 1.278 2003/12/02 04:58:42 ballen4705 Exp $ +$Id: CHANGELOG,v 1.279 2003/12/05 13:14:06 ballen4705 Exp $ The most recent version of this file is: http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?sortby=date&view=markup @@ -23,7 +23,13 @@ Maintainers / Developers Key: NOTES FOR FUTURE RELEASES: see TODO file. <ADDITIONS TO THE CHANGE LOG SHOULD BE ADDED JUST BELOW HERE, PLEASE> - + + [BA] smartd: reduce per-device memory footprint by making + mail-warning info dynamically allocated. Also remove + potential memory leak if use has -m Directive twice and + keeps reloading the config file (highly unlikely this would + ever be noticed!) + [DG] smartd: added SCSI scheduled self-tests (Background short or extended. diff --git a/sm5/smartd.c b/sm5/smartd.c index 6f49bb624..e7afa07d3 100644 --- a/sm5/smartd.c +++ b/sm5/smartd.c @@ -69,7 +69,7 @@ extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; -const char *smartd_c_cvsid="$Id: smartd.c,v 1.256 2003/12/02 13:48:37 ballen4705 Exp $" +const char *smartd_c_cvsid="$Id: smartd.c,v 1.257 2003/12/05 13:14:06 ballen4705 Exp $" ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; @@ -183,13 +183,16 @@ void RmConfigEntry(cfgfile **anentry, int whatline){ // entry exists -- free all of its memory cfg->name = FreeNonZero(cfg->name, -1,__LINE__,__FILE__); - cfg->address = FreeNonZero(cfg->address, -1,__LINE__,__FILE__); - cfg->emailcmdline = FreeNonZero(cfg->emailcmdline, -1,__LINE__,__FILE__); cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds),__LINE__,__FILE__); cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,__FILE__); cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,__FILE__); cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,__FILE__); cfg->testregexp = FreeNonZero(cfg->testregexp, -1,__LINE__, __FILE__); + if (cfg->mailwarn){ + cfg->mailwarn->address = FreeNonZero(cfg->mailwarn->address, -1,__LINE__,__FILE__); + cfg->mailwarn->emailcmdline = FreeNonZero(cfg->mailwarn->emailcmdline, -1,__LINE__,__FILE__); + cfg->mailwarn = FreeNonZero(cfg->mailwarn, sizeof(maildata),__LINE__,__FILE__); + } *anentry = FreeNonZero(cfg, sizeof(cfgfile),__LINE__,__FILE__); return; @@ -269,12 +272,15 @@ void Goodbye(){ RemovePidFile(); // useful for debugging -- have we managed memory correctly? - if (debugmode || bytes!=0) + if (debugmode || bytes) PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %lld bytes.\n", bytes); - // if we are exiting because of a code bug, print CVS info + // if we are exiting because of a code bug, tell user if (exitstatus==EXIT_BADCODE || bytes) - PrintCVS(); + 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); @@ -320,26 +326,36 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ "FAILEDopendevice" // 9 }; - char *address=cfg->address; - char *executable=cfg->emailcmdline; - mailinfo *mail=cfg->maildata+which; + char *address, *executable; + mailinfo *mail; + maildata* data=cfg->mailwarn; // See if user wants us to send mail + if (!(data=cfg->mailwarn)) + 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 (cfg->emailfreq<1 || cfg->emailfreq>3) { - PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->emailfreq=%d\n",cfg->emailfreq); + 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>9) { - PrintOut(LOG_CRIT,"internal error in MailWarning(): which=%d\n",which); + 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, sizeof(whichfail)); return; } // Return if a single warning mail has been sent. - if ((cfg->emailfreq==1) && mail->logged) + if ((data->emailfreq==1) && mail->logged) return; // Return if this is an email test and one has already been sent. @@ -350,11 +366,11 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ epoch=time(NULL); // Return if less than one day has gone by - if (cfg->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) + if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) return; // Return if less than 2^(logged-1) days have gone by - if (cfg->emailfreq==3 && mail->logged){ + if (data->emailfreq==3 && mail->logged){ days=0x01<<(mail->logged-1); days*=day; if (epoch<(mail->lastsent+days)) @@ -391,7 +407,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ if (which) { sprintf(further,"You can also use the smartctl utility for further investigation.\n"); - switch (cfg->emailfreq){ + switch (data->emailfreq){ case 1: sprintf(additional,"No additional email messages about this problem will be sent.\n"); break; @@ -403,7 +419,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ (0x01)<<mail->logged); break; } - if (cfg->emailfreq>1 && mail->logged){ + if (data->emailfreq>1 && mail->logged){ dateandtimezoneepoch(dates, mail->firstsent); sprintf(original,"The original email about this issue was sent at %s\n", dates); } @@ -876,15 +892,9 @@ int ATADeviceScan(cfgfile *cfg){ // do we need to get SMART data? if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog) { - cfg->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)); - cfg->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)); - - if (cfg->smartval) - bytes+=sizeof(struct ata_smart_values); + cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)); + cfg->smartthres=(struct ata_smart_thresholds *)Calloc(1,sizeof(struct ata_smart_thresholds)); - if (cfg->smartthres) - bytes+=sizeof(struct ata_smart_thresholds); - if (!cfg->smartval || !cfg->smartthres){ PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n"); EXIT(EXIT_NOMEM); @@ -1228,12 +1238,10 @@ int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which return 0; // we are writing - if (!(*datap=calloc(NMONITOR*32, 1))){ + if (!(*datap=Calloc(NMONITOR*32, 1))){ PrintOut(LOG_CRIT,"No memory to create monattflags\n"); EXIT(EXIT_NOMEM); } - - bytes+=NMONITOR*32; } // pointer to the 256 bits that we need @@ -1472,7 +1480,7 @@ int ATACheckDevice(cfgfile *cfg){ con->escalade=cfg->escalade; // If user has asked, test the email warning system - if (cfg->emailtest) + if (cfg->mailwarn && cfg->mailwarn->emailtest) MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); // if we can't open device, fail gracefully rather than hard -- @@ -1646,7 +1654,7 @@ int SCSICheckDevice(cfgfile *cfg) const char *cp; // If the user has asked for it, test the email warning system - if (cfg->emailtest) + if (cfg->mailwarn && cfg->mailwarn->emailtest) MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); // if we can't open device, fail gracefully rather than hard -- @@ -1889,6 +1897,8 @@ int ParseToken(char *token,cfgfile *cfg){ int badarg = 0; int missingarg = 0; char *arg = NULL; + int makemail=0; + maildata *mdat=NULL, tempmail; // is the rest of the line a comment if (*token=='#') @@ -1902,8 +1912,24 @@ int ParseToken(char *token,cfgfile *cfg){ return -1; } + // token we will be parsing: + sym=token[1]; + + // create temporary maildata structure. This means we can postpone + // allocating space in the data segment until we are sure there are + // no errors. + if ('m'==sym || 'M'==sym){ + if (!cfg->mailwarn){ + memset(&tempmail, 0, sizeof(maildata)); + mdat=&tempmail; + makemail=1; + } + else + mdat=cfg->mailwarn; + } + // parse the token and swallow its argument - switch (sym=token[1]) { + switch (sym) { int val; case 'T': @@ -2067,7 +2093,7 @@ int ParseToken(char *token,cfgfile *cfg){ // Free the last pattern given if any if (cfg->testregexp) { - PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -s Self-Test pattern %s\n", + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -s Test Directive \"%s\"\n", configfile, lineno, name, cfg->testregexp); cfg->testregexp=FreeNonZero(cfg->testregexp, -1,__LINE__,__FILE__); } @@ -2075,36 +2101,48 @@ int ParseToken(char *token,cfgfile *cfg){ cfg->testregexp=CustomStrDup(arg, 1, __LINE__,__FILE__); } break; + case 'm': + // send email to address that follows + if (!(arg = strtok(NULL,delim))) + missingarg = 1; + else { + if (mdat->address) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -m Address Directive \"%s\"\n", + configfile, lineno, name, mdat->address); + mdat->address=FreeNonZero(mdat->address, -1,__LINE__,__FILE__); + } + mdat->address=CustomStrDup(arg, 1, __LINE__,__FILE__); + } + break; case 'M': - // email warning option - if ((arg = strtok(NULL, delim)) == NULL) { + // email warning options + if (!(arg = strtok(NULL, delim))) missingarg = 1; - } else if (!strcmp(arg, "once")) { - cfg->emailfreq = 1; - } else if (!strcmp(arg, "daily")) { - cfg->emailfreq = 2; - } else if (!strcmp(arg, "diminishing")) { - cfg->emailfreq = 3; - } else if (!strcmp(arg, "test")) { - cfg->emailtest = 1; - } else if (!strcmp(arg, "exec")) { + else if (!strcmp(arg, "once")) + mdat->emailfreq = 1; + else if (!strcmp(arg, "daily")) + mdat->emailfreq = 2; + else if (!strcmp(arg, "diminishing")) + mdat->emailfreq = 3; + else if (!strcmp(arg, "test")) + mdat->emailtest = 1; + else if (!strcmp(arg, "exec")) { // Get the next argument (the command line) if (!(arg = strtok(NULL, delim))) { - PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", - configfile, lineno, name, token); + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", + configfile, lineno, name, token); return -1; } - // Free the last cmd line given if any - if (cfg->emailcmdline) { - PrintOut(LOG_INFO, "File %s line %d (drive %s): found multiple -M exec Directives on line - ignoring all but the last\n", - configfile, lineno, name); - cfg->emailcmdline=FreeNonZero(cfg->emailcmdline, -1,__LINE__,__FILE__); + // Free the last cmd line given if any, and copy new one + if (mdat->emailcmdline) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -M exec Directive \"%s\"\n", + configfile, lineno, name, mdat->emailcmdline); + mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,__FILE__); } - // Attempt to copy the argument - cfg->emailcmdline=CustomStrDup(arg, 1, __LINE__,__FILE__); - } else { + mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,__FILE__); + } + else badarg = 1; - } break; case 'i': // ignore failure of usage attribute @@ -2131,15 +2169,6 @@ int ParseToken(char *token,cfgfile *cfg){ IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__); break; - case 'm': - // send email to address that follows - if ((arg = strtok(NULL,delim)) == NULL) { - PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s needs email address(es)\n", - configfile, lineno, name, token); - return -1; - } - cfg->address=CustomStrDup(arg, 1, __LINE__,__FILE__); - break; case 'v': // non-default vendor-specific attribute meaning if (!(arg=strtok(NULL,delim))) { @@ -2186,6 +2215,17 @@ int ParseToken(char *token,cfgfile *cfg){ return -1; } + // If this did something to fill the mail structure, and that didn't + // already exist, create it and copy. + if (makemail) { + if (!(cfg->mailwarn=Calloc(1, sizeof(maildata)))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n", + configfile, lineno, name); + EXIT(EXIT_NOMEM); + } + memcpy(cfg->mailwarn, mdat, sizeof(maildata)); + } + return 1; } @@ -2197,10 +2237,8 @@ cfgfile *CreateConfigEntry(cfgfile *original){ cfgfile *add; // allocate memory for new structure - if (!(add=(cfgfile *)calloc(1,sizeof(cfgfile)))) + if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile)))) goto badexit; - - bytes+=sizeof(cfgfile); // if old structure was pointed to, copy it if (original) @@ -2209,36 +2247,36 @@ cfgfile *CreateConfigEntry(cfgfile *original){ // make private copies of data items ONLY if they are in use (non // NULL) add->name = CustomStrDup(add->name, 0, __LINE__,__FILE__); - add->emailcmdline = CustomStrDup(add->emailcmdline, 0, __LINE__,__FILE__); - add->address = CustomStrDup(add->address, 0, __LINE__,__FILE__); add->testregexp = CustomStrDup(add->testregexp, 0, __LINE__,__FILE__); + if (add->mailwarn) { + if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata)))) + goto badexit; + memcpy(add->mailwarn, original->mailwarn, sizeof(maildata)); + add->mailwarn->address = CustomStrDup(add->mailwarn->address, 0, __LINE__,__FILE__); + add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,__FILE__); + } + if (add->attributedefs) { - if (!(add->attributedefs=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))) + if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1))) goto badexit; - bytes+=MAX_ATTRIBUTE_NUM; memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM); } if (add->monitorattflags) { - if (!(add->monitorattflags=(unsigned char *)calloc(NMONITOR*32, 1))) + if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1))) goto badexit; - bytes+=NMONITOR*32; memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32); } if (add->smartval) { - if (!(add->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)))) + if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)))) goto badexit; - else - bytes+=sizeof(struct ata_smart_values); } if (add->smartthres) { - if (!(add->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)))) + if (!(add->smartthres=(struct ata_smart_thresholds *)Calloc(1,sizeof(struct ata_smart_thresholds)))) goto badexit; - else - bytes+=sizeof(struct ata_smart_thresholds); } return add; @@ -2367,28 +2405,28 @@ int ParseConfigLine(int entry, int lineno,char *line){ } // additional sanity check. Has user set -M options without -m? - if (!cfg->address && (cfg->emailcmdline || cfg->emailfreq || cfg->emailtest)){ + if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){ PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n", cfg->name, cfg->lineno, configfile); return -2; } // has the user has set <nomailer>? - if (cfg->address && !strcmp(cfg->address,"<nomailer>")){ + if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){ // check that -M exec is also set - if (!cfg->emailcmdline){ + if (!cfg->mailwarn->emailcmdline){ PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n", cfg->name, cfg->lineno, configfile); return -2; } // now free memory. From here on the sign of <nomailer> is // address==NULL and cfg->emailcmdline!=NULL - cfg->address=FreeNonZero(cfg->address, -1,__LINE__,__FILE__); + cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,__FILE__); } // set cfg->emailfreq to 1 (once) if user hasn't set it - if (!cfg->emailfreq) - cfg->emailfreq = 1; + if (cfg->mailwarn && !cfg->mailwarn->emailfreq) + cfg->mailwarn->emailfreq = 1; entry++; diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp index 54e48c291..03a0aef4e 100644 --- a/sm5/smartd.cpp +++ b/sm5/smartd.cpp @@ -69,7 +69,7 @@ extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; -const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.256 2003/12/02 13:48:37 ballen4705 Exp $" +const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.257 2003/12/05 13:14:06 ballen4705 Exp $" ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; @@ -183,13 +183,16 @@ void RmConfigEntry(cfgfile **anentry, int whatline){ // entry exists -- free all of its memory cfg->name = FreeNonZero(cfg->name, -1,__LINE__,__FILE__); - cfg->address = FreeNonZero(cfg->address, -1,__LINE__,__FILE__); - cfg->emailcmdline = FreeNonZero(cfg->emailcmdline, -1,__LINE__,__FILE__); cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds),__LINE__,__FILE__); cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,__FILE__); cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,__FILE__); cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,__FILE__); cfg->testregexp = FreeNonZero(cfg->testregexp, -1,__LINE__, __FILE__); + if (cfg->mailwarn){ + cfg->mailwarn->address = FreeNonZero(cfg->mailwarn->address, -1,__LINE__,__FILE__); + cfg->mailwarn->emailcmdline = FreeNonZero(cfg->mailwarn->emailcmdline, -1,__LINE__,__FILE__); + cfg->mailwarn = FreeNonZero(cfg->mailwarn, sizeof(maildata),__LINE__,__FILE__); + } *anentry = FreeNonZero(cfg, sizeof(cfgfile),__LINE__,__FILE__); return; @@ -269,12 +272,15 @@ void Goodbye(){ RemovePidFile(); // useful for debugging -- have we managed memory correctly? - if (debugmode || bytes!=0) + if (debugmode || bytes) PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %lld bytes.\n", bytes); - // if we are exiting because of a code bug, print CVS info + // if we are exiting because of a code bug, tell user if (exitstatus==EXIT_BADCODE || bytes) - PrintCVS(); + 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); @@ -320,26 +326,36 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ "FAILEDopendevice" // 9 }; - char *address=cfg->address; - char *executable=cfg->emailcmdline; - mailinfo *mail=cfg->maildata+which; + char *address, *executable; + mailinfo *mail; + maildata* data=cfg->mailwarn; // See if user wants us to send mail + if (!(data=cfg->mailwarn)) + 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 (cfg->emailfreq<1 || cfg->emailfreq>3) { - PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->emailfreq=%d\n",cfg->emailfreq); + 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>9) { - PrintOut(LOG_CRIT,"internal error in MailWarning(): which=%d\n",which); + 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, sizeof(whichfail)); return; } // Return if a single warning mail has been sent. - if ((cfg->emailfreq==1) && mail->logged) + if ((data->emailfreq==1) && mail->logged) return; // Return if this is an email test and one has already been sent. @@ -350,11 +366,11 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ epoch=time(NULL); // Return if less than one day has gone by - if (cfg->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) + if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) return; // Return if less than 2^(logged-1) days have gone by - if (cfg->emailfreq==3 && mail->logged){ + if (data->emailfreq==3 && mail->logged){ days=0x01<<(mail->logged-1); days*=day; if (epoch<(mail->lastsent+days)) @@ -391,7 +407,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ if (which) { sprintf(further,"You can also use the smartctl utility for further investigation.\n"); - switch (cfg->emailfreq){ + switch (data->emailfreq){ case 1: sprintf(additional,"No additional email messages about this problem will be sent.\n"); break; @@ -403,7 +419,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ (0x01)<<mail->logged); break; } - if (cfg->emailfreq>1 && mail->logged){ + if (data->emailfreq>1 && mail->logged){ dateandtimezoneepoch(dates, mail->firstsent); sprintf(original,"The original email about this issue was sent at %s\n", dates); } @@ -876,15 +892,9 @@ int ATADeviceScan(cfgfile *cfg){ // do we need to get SMART data? if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog) { - cfg->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)); - cfg->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)); - - if (cfg->smartval) - bytes+=sizeof(struct ata_smart_values); + cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)); + cfg->smartthres=(struct ata_smart_thresholds *)Calloc(1,sizeof(struct ata_smart_thresholds)); - if (cfg->smartthres) - bytes+=sizeof(struct ata_smart_thresholds); - if (!cfg->smartval || !cfg->smartthres){ PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n"); EXIT(EXIT_NOMEM); @@ -1228,12 +1238,10 @@ int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which return 0; // we are writing - if (!(*datap=calloc(NMONITOR*32, 1))){ + if (!(*datap=Calloc(NMONITOR*32, 1))){ PrintOut(LOG_CRIT,"No memory to create monattflags\n"); EXIT(EXIT_NOMEM); } - - bytes+=NMONITOR*32; } // pointer to the 256 bits that we need @@ -1472,7 +1480,7 @@ int ATACheckDevice(cfgfile *cfg){ con->escalade=cfg->escalade; // If user has asked, test the email warning system - if (cfg->emailtest) + if (cfg->mailwarn && cfg->mailwarn->emailtest) MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); // if we can't open device, fail gracefully rather than hard -- @@ -1646,7 +1654,7 @@ int SCSICheckDevice(cfgfile *cfg) const char *cp; // If the user has asked for it, test the email warning system - if (cfg->emailtest) + if (cfg->mailwarn && cfg->mailwarn->emailtest) MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); // if we can't open device, fail gracefully rather than hard -- @@ -1889,6 +1897,8 @@ int ParseToken(char *token,cfgfile *cfg){ int badarg = 0; int missingarg = 0; char *arg = NULL; + int makemail=0; + maildata *mdat=NULL, tempmail; // is the rest of the line a comment if (*token=='#') @@ -1902,8 +1912,24 @@ int ParseToken(char *token,cfgfile *cfg){ return -1; } + // token we will be parsing: + sym=token[1]; + + // create temporary maildata structure. This means we can postpone + // allocating space in the data segment until we are sure there are + // no errors. + if ('m'==sym || 'M'==sym){ + if (!cfg->mailwarn){ + memset(&tempmail, 0, sizeof(maildata)); + mdat=&tempmail; + makemail=1; + } + else + mdat=cfg->mailwarn; + } + // parse the token and swallow its argument - switch (sym=token[1]) { + switch (sym) { int val; case 'T': @@ -2067,7 +2093,7 @@ int ParseToken(char *token,cfgfile *cfg){ // Free the last pattern given if any if (cfg->testregexp) { - PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -s Self-Test pattern %s\n", + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -s Test Directive \"%s\"\n", configfile, lineno, name, cfg->testregexp); cfg->testregexp=FreeNonZero(cfg->testregexp, -1,__LINE__,__FILE__); } @@ -2075,36 +2101,48 @@ int ParseToken(char *token,cfgfile *cfg){ cfg->testregexp=CustomStrDup(arg, 1, __LINE__,__FILE__); } break; + case 'm': + // send email to address that follows + if (!(arg = strtok(NULL,delim))) + missingarg = 1; + else { + if (mdat->address) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -m Address Directive \"%s\"\n", + configfile, lineno, name, mdat->address); + mdat->address=FreeNonZero(mdat->address, -1,__LINE__,__FILE__); + } + mdat->address=CustomStrDup(arg, 1, __LINE__,__FILE__); + } + break; case 'M': - // email warning option - if ((arg = strtok(NULL, delim)) == NULL) { + // email warning options + if (!(arg = strtok(NULL, delim))) missingarg = 1; - } else if (!strcmp(arg, "once")) { - cfg->emailfreq = 1; - } else if (!strcmp(arg, "daily")) { - cfg->emailfreq = 2; - } else if (!strcmp(arg, "diminishing")) { - cfg->emailfreq = 3; - } else if (!strcmp(arg, "test")) { - cfg->emailtest = 1; - } else if (!strcmp(arg, "exec")) { + else if (!strcmp(arg, "once")) + mdat->emailfreq = 1; + else if (!strcmp(arg, "daily")) + mdat->emailfreq = 2; + else if (!strcmp(arg, "diminishing")) + mdat->emailfreq = 3; + else if (!strcmp(arg, "test")) + mdat->emailtest = 1; + else if (!strcmp(arg, "exec")) { // Get the next argument (the command line) if (!(arg = strtok(NULL, delim))) { - PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", - configfile, lineno, name, token); + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", + configfile, lineno, name, token); return -1; } - // Free the last cmd line given if any - if (cfg->emailcmdline) { - PrintOut(LOG_INFO, "File %s line %d (drive %s): found multiple -M exec Directives on line - ignoring all but the last\n", - configfile, lineno, name); - cfg->emailcmdline=FreeNonZero(cfg->emailcmdline, -1,__LINE__,__FILE__); + // Free the last cmd line given if any, and copy new one + if (mdat->emailcmdline) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous -M exec Directive \"%s\"\n", + configfile, lineno, name, mdat->emailcmdline); + mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,__FILE__); } - // Attempt to copy the argument - cfg->emailcmdline=CustomStrDup(arg, 1, __LINE__,__FILE__); - } else { + mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,__FILE__); + } + else badarg = 1; - } break; case 'i': // ignore failure of usage attribute @@ -2131,15 +2169,6 @@ int ParseToken(char *token,cfgfile *cfg){ IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__); break; - case 'm': - // send email to address that follows - if ((arg = strtok(NULL,delim)) == NULL) { - PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s needs email address(es)\n", - configfile, lineno, name, token); - return -1; - } - cfg->address=CustomStrDup(arg, 1, __LINE__,__FILE__); - break; case 'v': // non-default vendor-specific attribute meaning if (!(arg=strtok(NULL,delim))) { @@ -2186,6 +2215,17 @@ int ParseToken(char *token,cfgfile *cfg){ return -1; } + // If this did something to fill the mail structure, and that didn't + // already exist, create it and copy. + if (makemail) { + if (!(cfg->mailwarn=Calloc(1, sizeof(maildata)))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n", + configfile, lineno, name); + EXIT(EXIT_NOMEM); + } + memcpy(cfg->mailwarn, mdat, sizeof(maildata)); + } + return 1; } @@ -2197,10 +2237,8 @@ cfgfile *CreateConfigEntry(cfgfile *original){ cfgfile *add; // allocate memory for new structure - if (!(add=(cfgfile *)calloc(1,sizeof(cfgfile)))) + if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile)))) goto badexit; - - bytes+=sizeof(cfgfile); // if old structure was pointed to, copy it if (original) @@ -2209,36 +2247,36 @@ cfgfile *CreateConfigEntry(cfgfile *original){ // make private copies of data items ONLY if they are in use (non // NULL) add->name = CustomStrDup(add->name, 0, __LINE__,__FILE__); - add->emailcmdline = CustomStrDup(add->emailcmdline, 0, __LINE__,__FILE__); - add->address = CustomStrDup(add->address, 0, __LINE__,__FILE__); add->testregexp = CustomStrDup(add->testregexp, 0, __LINE__,__FILE__); + if (add->mailwarn) { + if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata)))) + goto badexit; + memcpy(add->mailwarn, original->mailwarn, sizeof(maildata)); + add->mailwarn->address = CustomStrDup(add->mailwarn->address, 0, __LINE__,__FILE__); + add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,__FILE__); + } + if (add->attributedefs) { - if (!(add->attributedefs=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))) + if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1))) goto badexit; - bytes+=MAX_ATTRIBUTE_NUM; memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM); } if (add->monitorattflags) { - if (!(add->monitorattflags=(unsigned char *)calloc(NMONITOR*32, 1))) + if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1))) goto badexit; - bytes+=NMONITOR*32; memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32); } if (add->smartval) { - if (!(add->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)))) + if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)))) goto badexit; - else - bytes+=sizeof(struct ata_smart_values); } if (add->smartthres) { - if (!(add->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)))) + if (!(add->smartthres=(struct ata_smart_thresholds *)Calloc(1,sizeof(struct ata_smart_thresholds)))) goto badexit; - else - bytes+=sizeof(struct ata_smart_thresholds); } return add; @@ -2367,28 +2405,28 @@ int ParseConfigLine(int entry, int lineno,char *line){ } // additional sanity check. Has user set -M options without -m? - if (!cfg->address && (cfg->emailcmdline || cfg->emailfreq || cfg->emailtest)){ + if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){ PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n", cfg->name, cfg->lineno, configfile); return -2; } // has the user has set <nomailer>? - if (cfg->address && !strcmp(cfg->address,"<nomailer>")){ + if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){ // check that -M exec is also set - if (!cfg->emailcmdline){ + if (!cfg->mailwarn->emailcmdline){ PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n", cfg->name, cfg->lineno, configfile); return -2; } // now free memory. From here on the sign of <nomailer> is // address==NULL and cfg->emailcmdline!=NULL - cfg->address=FreeNonZero(cfg->address, -1,__LINE__,__FILE__); + cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,__FILE__); } // set cfg->emailfreq to 1 (once) if user hasn't set it - if (!cfg->emailfreq) - cfg->emailfreq = 1; + if (cfg->mailwarn && !cfg->mailwarn->emailfreq) + cfg->mailwarn->emailfreq = 1; entry++; diff --git a/sm5/smartd.h b/sm5/smartd.h index 6a34bcad0..c690c6185 100644 --- a/sm5/smartd.h +++ b/sm5/smartd.h @@ -27,7 +27,7 @@ #ifndef SMARTD_H_CVSID -#define SMARTD_H_CVSID "$Id: smartd.h,v 1.62 2003/12/01 21:53:43 ballen4705 Exp $\n" +#define SMARTD_H_CVSID "$Id: smartd.h,v 1.63 2003/12/05 13:14:07 ballen4705 Exp $\n" #endif // Configuration file @@ -67,17 +67,26 @@ #define MONITOR_RAWPRINT 2 #define MONITOR_RAW 3 -// If user has requested email warning messages, then this structure -// stores the information about them. -typedef struct mailinfo { - // number of times an email has been sent - int logged; - // time last email was sent, as defined by man 2 time - time_t lastsent; - // time problem initially logged - time_t firstsent; + +// Number of allowed mail message types +#define SMARTD_NMAIL 10 + +typedef struct mailinfo_s { + int logged;// number of times an email has been sent + time_t firstsent;// time first email was sent, as defined by time(2) + time_t lastsent; // time last email was sent, as defined by time(2) } mailinfo; +// If user has requested email warning messages, then this structure +// stores the information about them, and track type/date of email +// messages. +typedef struct maildata_s { + mailinfo maillog[10]; // log info on when mail sent + char *emailcmdline; // script to execute + char *address; // email address, or null + unsigned char emailfreq; // Emails once (1) daily (2) diminishing (3) + unsigned char emailtest; // Send test email? +} maildata; // cfgfile is the main data structure of smartd. It is used in two // ways. First, to store a list of devices/options given in the @@ -159,24 +168,20 @@ typedef struct configfile_s { char permissive; // Ignore failed SMART commands char autosave; // 1=disable, 2=enable Autosave Attributes char autoofflinetest; // 1=disable, 2=enable Auto Offline Test - unsigned char emailfreq; // Emails once (1) daily (2) diminishing (3) - unsigned char emailtest; // Send test email? unsigned char fixfirmwarebug; // Fix firmware bug char ignorepresets; // Ignore database of -v options char showpresets; // Show database entry for this device char removable; // Device may disappear (not be present) unsigned char selflogcount; // total number of self-test errors - unsigned char notused1[1]; // for packing alignment + unsigned char notused1[2]; // for packing alignment unsigned short selftesthour; // 1+hour of year when last scheduled self-test done unsigned short selfloghour; // lifetime hours of last self-test error - char *emailcmdline; // Program for sending mail (or NULL) - char *address; // Email addresses (or NULL) char *testregexp; // Pointer to regexps pattern for selftest types/times. Not stored in // compiled form because POSIX has no 'copy' compiled regex command! - - // THE NEXT SET OF ENTRIES TRACK DEVICE STATE AND ARE DYNAMIC - mailinfo maildata[10]; // Tracks type/date of email messages sent + // THE NEXT SET OF ENTRIES ALSO TRACK DEVICE STATE AND ARE DYNAMIC + maildata *mailwarn; // non-NULL: info about sending mail or executing script + // SCSI ONLY unsigned char SmartPageSupported; // has log sense IE page (0x2f) unsigned char TempPageSupported; // has log sense temperature page (0xd) diff --git a/sm5/utility.c b/sm5/utility.c index 260a63781..3298b484a 100644 --- a/sm5/utility.c +++ b/sm5/utility.c @@ -39,7 +39,7 @@ #include "config.h" // Any local header files should be represented by a CVSIDX just below. -const char* utility_c_cvsid="$Id: utility.c,v 1.28 2003/11/09 20:22:21 ballen4705 Exp $" CONFIG_H_CVSID UTILITY_H_CVSID; +const char* utility_c_cvsid="$Id: utility.c,v 1.29 2003/12/05 13:14:07 ballen4705 Exp $" CONFIG_H_CVSID UTILITY_H_CVSID; const char * packet_types[] = { "Direct-access (disk)", @@ -364,6 +364,18 @@ void *CheckFree(void *address, int whatline,char* file){ EXIT(EXIT_BADCODE); } +// A custom version of calloc() that tracks memory use +void *Calloc(size_t nmemb, size_t size) { + void *ptr=calloc(nmemb, size); + + if (ptr) + bytes+=nmemb*size; + + return ptr; +} + + + // A custom version of strdup() that keeps track of how much memory is // being allocated. If mustexist is set, it also throws an error if we diff --git a/sm5/utility.cpp b/sm5/utility.cpp index b267ba3f1..e6092efbc 100644 --- a/sm5/utility.cpp +++ b/sm5/utility.cpp @@ -39,7 +39,7 @@ #include "config.h" // Any local header files should be represented by a CVSIDX just below. -const char* utility_c_cvsid="$Id: utility.cpp,v 1.28 2003/11/09 20:22:21 ballen4705 Exp $" CONFIG_H_CVSID UTILITY_H_CVSID; +const char* utility_c_cvsid="$Id: utility.cpp,v 1.29 2003/12/05 13:14:07 ballen4705 Exp $" CONFIG_H_CVSID UTILITY_H_CVSID; const char * packet_types[] = { "Direct-access (disk)", @@ -364,6 +364,18 @@ void *CheckFree(void *address, int whatline,char* file){ EXIT(EXIT_BADCODE); } +// A custom version of calloc() that tracks memory use +void *Calloc(size_t nmemb, size_t size) { + void *ptr=calloc(nmemb, size); + + if (ptr) + bytes+=nmemb*size; + + return ptr; +} + + + // A custom version of strdup() that keeps track of how much memory is // being allocated. If mustexist is set, it also throws an error if we diff --git a/sm5/utility.h b/sm5/utility.h index 3e4c0240a..260548f4d 100644 --- a/sm5/utility.h +++ b/sm5/utility.h @@ -25,7 +25,7 @@ #ifndef UTILITY_H_ #define UTILITY_H_ -#define UTILITY_H_CVSID "$Id: utility.h,v 1.20 2003/10/21 01:45:50 arvoreen Exp $\n" +#define UTILITY_H_CVSID "$Id: utility.h,v 1.21 2003/12/05 13:14:07 ballen4705 Exp $\n" #include <time.h> #include <regex.h> @@ -82,6 +82,9 @@ int make_device_names (char ***devlist, const char* name); #define EXIT(x) { exitstatus = (x); exit((x)); } +// replacement for calloc() that tracks memory usage +void *Calloc(size_t nmemb, size_t size); + // Utility function to free memory void *FreeNonZero(void* address, int size, int whatline, char* file); -- GitLab