]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - smartd.cpp
refresh
[mirror_smartmontools-debian.git] / smartd.cpp
CommitLineData
832b75ed
GG
1/*
2 * Home page of code is: http://smartmontools.sourceforge.net
3 *
4 * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
5 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * You should have received a copy of the GNU General Public License
13 * (for example COPYING); if not, write to the Free
14 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 *
16 * This code was originally developed as a Senior Thesis by Michael Cornwell
17 * at the Concurrent Systems Laboratory (now part of the Storage Systems
18 * Research Center), Jack Baskin School of Engineering, University of
19 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
20 *
21 */
22
ba59cff1 23#ifndef _GNU_SOURCE
832b75ed 24#define _GNU_SOURCE
ba59cff1
GG
25#endif
26
27// unconditionally included files
832b75ed
GG
28#include <stdio.h>
29#include <sys/types.h>
30#include <sys/stat.h> // umask
31#ifndef _WIN32
32#include <sys/wait.h>
33#include <unistd.h>
34#endif
35#include <signal.h>
36#include <fcntl.h>
37#include <string.h>
38#include <syslog.h>
39#include <stdarg.h>
40#include <stdlib.h>
41#include <errno.h>
42#include <time.h>
43#include <limits.h>
44
45#if SCSITIMEOUT
46#include <setjmp.h>
47#endif
48
49// see which system files to conditionally include
50#include "config.h"
51
52// conditionally included files
53#ifdef HAVE_GETOPT_LONG
54#include <getopt.h>
55#endif
56#ifdef HAVE_NETDB_H
57#include <netdb.h>
58#endif
59
60#ifdef _WIN32
61#ifdef _MSC_VER
62#pragma warning(disable:4761) // "conversion supplied"
63typedef unsigned short mode_t;
64typedef int pid_t;
65#endif
66#include <io.h> // umask()
67#include <process.h> // getpid()
68#endif // _WIN32
69
70#ifdef __CYGWIN__
71// From <windows.h>:
72// BOOL WINAPI FreeConsole(void);
4d59bff9 73extern "C" int __stdcall FreeConsole(void);
832b75ed
GG
74#include <io.h> // setmode()
75#endif // __CYGWIN__
76
77// locally included files
78#include "int64.h"
79#include "atacmds.h"
80#include "ataprint.h"
81#include "extern.h"
82#include "knowndrives.h"
83#include "scsicmds.h"
9ebc753d 84#include "scsiata.h"
832b75ed
GG
85#include "smartd.h"
86#include "utility.h"
87
88#ifdef _WIN32
89#include "hostname_win32.h" // gethost/domainname()
90#define HAVE_GETHOSTNAME 1
91#define HAVE_GETDOMAINNAME 1
92// fork()/signal()/initd simulation for native Windows
93#include "daemon_win32.h" // daemon_main/detach/signal()
94#undef SIGNALFN
95#define SIGNALFN daemon_signal
96#define strsignal daemon_strsignal
97#define sleep daemon_sleep
98#undef EXIT // see utility.h
99#define EXIT(x) { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); }
100// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK.
101#define SIGQUIT SIGBREAK
102#define SIGQUIT_KEYNAME "CONTROL-Break"
103#else // _WIN32
104#ifdef __CYGWIN__
105// 2x CONTROL-C simulates missing SIGQUIT via keyboard
106#define SIGQUIT_KEYNAME "2x CONTROL-C"
107#else // __CYGWIN__
108#define SIGQUIT_KEYNAME "CONTROL-\\"
109#endif // __CYGWIN__
110#endif // _WIN32
111
112#if defined (__SVR4) && defined (__sun)
4d59bff9 113extern "C" int getdomainname(char *, int); // no declaration in header files!
832b75ed
GG
114#endif
115
116#define ARGUSED(x) ((void)(x))
117
4d59bff9 118// These are CVS identification information for *.cpp and *.h files
832b75ed
GG
119extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid,
120 *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
121
9ebc753d 122static const char *filenameandversion="$Id: smartd.cpp,v 1.384 2006/11/12 04:49:09 dpgilbert Exp $";
832b75ed
GG
123#ifdef NEED_SOLARIS_ATA_CODE
124extern const char *os_solaris_ata_s_cvsid;
125#endif
126#ifdef _WIN32
127extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid;
128#endif
9ebc753d 129const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.384 2006/11/12 04:49:09 dpgilbert Exp $"
832b75ed
GG
130ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID
131#ifdef DAEMON_WIN32_H_CVSID
132DAEMON_WIN32_H_CVSID
133#endif
134EXTERN_H_CVSID INT64_H_CVSID
135#ifdef HOSTNAME_WIN32_H_CVSID
136HOSTNAME_WIN32_H_CVSID
137#endif
138KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID
139#ifdef SYSLOG_H_CVSID
140SYSLOG_H_CVSID
141#endif
142UTILITY_H_CVSID;
143
144extern const char *reportbug;
145
146// GNU copyleft statement. Needed for GPL purposes.
147const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n"
148 "free software, and you are welcome to redistribute it\n"
149 "under the terms of the GNU General Public License\n"
150 "Version 2. See http://www.gnu.org for further details.\n\n";
151
152extern unsigned char debugmode;
153
154// command-line: how long to sleep between checks
155static int checktime=CHECKTIME;
156
157// command-line: name of PID file (NULL for no pid file)
158static char* pid_file=NULL;
159
160// configuration file name
161#ifndef _WIN32
162static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
163#else
164static char* configfile = "./" CONFIGFILENAME ;
165#endif
166// configuration file "name" if read from stdin
167static /*const*/ char * const configfile_stdin = "<stdin>";
168// allocated memory for alternate configuration file name
169static char* configfile_alt = NULL;
170
171// command-line: when should we exit?
172static int quit=0;
173
174// command-line; this is the default syslog(3) log facility to use.
175static int facility=LOG_DAEMON;
176
177#ifdef __CYGWIN__
178// command-line: running as service, so don't fork()
179static int is_service=0;
180#endif
181
182// used for control of printing, passing arguments to atacmds.c
183smartmonctrl *con=NULL;
184
185// pointers to (real or simulated) entries in configuration file, and
186// maximum space currently allocated for these entries.
187cfgfile **cfgentries=NULL;
188int cfgentries_max=0;
189
190// pointers to ATA and SCSI devices being monitored, maximum and
191// actual numbers
192cfgfile **atadevlist=NULL, **scsidevlist=NULL;
193int atadevlist_max=0, scsidevlist_max=0;
194int numdevata=0, numdevscsi=0;
195
196// track memory usage
197extern int64_t bytes;
198
199// exit status
200extern int exitstatus;
201
202// set to one if we catch a USR1 (check devices now)
203volatile int caughtsigUSR1=0;
204
205#ifdef _WIN32
206// set to one if we catch a USR2 (toggle debug mode)
207volatile int caughtsigUSR2=0;
208#endif
209
210// set to one if we catch a HUP (reload config file). In debug mode,
211// set to two, if we catch INT (also reload config file).
212volatile int caughtsigHUP=0;
213
214// set to signal value if we catch INT, QUIT, or TERM
215volatile int caughtsigEXIT=0;
216
217#if SCSITIMEOUT
218// stack environment if we time out during SCSI access (USB devices)
219jmp_buf registerscsienv;
220#endif
221
222// tranlate cfg->pending into the correct Attribute numbers
223void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) {
224
225 unsigned char curr = CURR_PEND(pending);
226 unsigned char off = OFF_PEND(pending);
227
228 // look for special value of CUR_UNC_DEFAULT that means DONT
229 // monitor. 0 means DO test.
230 if (curr==CUR_UNC_DEFAULT)
231 curr=0;
232 else if (curr==0)
233 curr=CUR_UNC_DEFAULT;
234
235 // look for special value of OFF_UNC_DEFAULT that means DONT
236 // monitor. 0 means DO TEST.
237 if (off==OFF_UNC_DEFAULT)
238 off=0;
239 else if (off==0)
240 off=OFF_UNC_DEFAULT;
241
242 *current=curr;
243 *offline=off;
244
245 return;
246}
247
248
249// free all memory associated with selftest part of configfile entry. Return NULL
250testinfo* FreeTestData(testinfo *data){
251
252 // make sure we have something to do.
253 if (!data)
254 return NULL;
255
256 // free space for text pattern
257 data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion);
258
259 // free compiled expression
260 regfree(&(data->cregex));
261
262 // make sure that no sign of the compiled expression is left behind
263 // (just in case, to help detect bugs if we ever try and refer to
264 // that again).
265 memset(&(data->cregex), '0', sizeof(regex_t));
266
267 // free remaining memory space
268 data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion);
269
270 return NULL;
271}
272
273cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){
274 // for now keep BLOCKSIZE small to help detect coding problems.
275 // Perhaps increase in the future.
276 const int BLOCKSIZE=8;
277 int i;
4d59bff9
GG
278 int olds = *oldsize;
279 int news = olds + BLOCKSIZE;
280 cfgfile **newptr=(cfgfile **)realloc(oldarray, news*sizeof(cfgfile *));
832b75ed
GG
281
282 // did we get more space?
283 if (newptr) {
284
285 // clear remaining entries ala calloc()
4d59bff9 286 for (i=olds; i<news; i++)
832b75ed
GG
287 newptr[i]=NULL;
288
289 bytes += BLOCKSIZE*sizeof(cfgfile *);
290
4d59bff9 291 *oldsize=news;
832b75ed
GG
292
293#if 0
294 PrintOut(LOG_INFO, "allocating %d slots for %s\n", BLOCKSIZE, listname);
295#endif
296
297 return newptr;
298 }
299
300 PrintOut(LOG_CRIT, "out of memory for allocating %s list\n", listname);
301 EXIT(EXIT_NOMEM);
302}
303
304void PrintOneCVS(const char *a_cvs_id){
305 char out[CVSMAXLEN];
306 printone(out,a_cvs_id);
307 PrintOut(LOG_INFO,"%s",out);
308 return;
309}
310
311// prints CVS identity information for the executable
312void PrintCVS(void){
313 char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
314
315 PrintOut(LOG_INFO,(char *)copyleftstring);
316 PrintOut(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
317 PrintOneCVS(atacmdnames_c_cvsid);
318 PrintOneCVS(atacmds_c_cvsid);
319 PrintOneCVS(ataprint_c_cvsid);
320#ifdef _WIN32
321 PrintOneCVS(daemon_win32_c_cvsid);
322#endif
323#ifdef _WIN32
324 PrintOneCVS(hostname_win32_c_cvsid);
325#endif
326 PrintOneCVS(knowndrives_c_cvsid);
327 PrintOneCVS(os_XXXX_c_cvsid);
328#ifdef NEED_SOLARIS_ATA_CODE
329 PrintOneCVS( os_solaris_ata_s_cvsid);
330#endif
331 PrintOneCVS(scsicmds_c_cvsid);
332 PrintOneCVS(smartd_c_cvsid);
333#ifdef _WIN32
334 PrintOneCVS(syslog_win32_c_cvsid);
335#endif
336 PrintOneCVS(utility_c_cvsid);
337 PrintOut(LOG_INFO, "\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
338 PrintOut(LOG_INFO, "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
339 PrintOut(LOG_INFO, "smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
340 PrintOut(LOG_INFO, "smartd compile dated " __DATE__ " at "__TIME__ "\n");
341 PrintOut(LOG_INFO, "smartmontools configure arguments: %s\n", configargs);
342 return;
343}
344
345// Removes config file entry, freeing all memory
346void RmConfigEntry(cfgfile **anentry, int whatline){
347
348 cfgfile *cfg;
349
350 // pointer should never be null!
351 if (!anentry){
352 PrintOut(LOG_CRIT,"Internal error in RmConfigEntry() at line %d of file %s\n%s",
353 whatline, filenameandversion, reportbug);
354 EXIT(EXIT_BADCODE);
355 }
356
357 // only remove entries that exist!
358 if (!(cfg=*anentry))
359 return;
360
361 // entry exists -- free all of its memory
362 cfg->name = FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
363 cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
364 cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion);
365 cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
366 cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
367 if (cfg->mailwarn){
368 cfg->mailwarn->address = FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion);
369 cfg->mailwarn->emailcmdline = FreeNonZero(cfg->mailwarn->emailcmdline, -1,__LINE__,filenameandversion);
370 cfg->mailwarn = FreeNonZero(cfg->mailwarn, sizeof(maildata),__LINE__,filenameandversion);
371 }
372 cfg->testdata = FreeTestData(cfg->testdata);
373 *anentry = FreeNonZero(cfg, sizeof(cfgfile),__LINE__,filenameandversion);
374
375 return;
376}
377
378// deallocates all memory associated with cfgentries list
379void RmAllConfigEntries(){
380 int i;
381
382 for (i=0; i<cfgentries_max; i++)
383 RmConfigEntry(cfgentries+i, __LINE__);
384
385 cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion);
386 cfgentries_max=0;
387
388 return;
389}
390
391// deallocates all memory associated with ATA/SCSI device lists
392void RmAllDevEntries(){
393 int i;
394
395 for (i=0; i<atadevlist_max; i++)
396 RmConfigEntry(atadevlist+i, __LINE__);
397
398 atadevlist=FreeNonZero(atadevlist, sizeof(cfgfile *)*atadevlist_max, __LINE__, filenameandversion);
399 atadevlist_max=0;
400
401 for (i=0; i<scsidevlist_max; i++)
402 RmConfigEntry(scsidevlist+i, __LINE__);
403
404 scsidevlist=FreeNonZero(scsidevlist, sizeof(cfgfile *)*scsidevlist_max, __LINE__, filenameandversion);
405 scsidevlist_max=0;
406
407 return;
408}
409
410// remove the PID file
411void RemovePidFile(){
412 if (pid_file) {
413 if ( -1==unlink(pid_file) )
414 PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n",
415 pid_file, strerror(errno));
416 pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
417 }
418 return;
419}
420
421
422// Note if we catch a SIGUSR1
423void USR1handler(int sig){
424 if (SIGUSR1==sig)
425 caughtsigUSR1=1;
426 return;
427}
428
429#ifdef _WIN32
430// Note if we catch a SIGUSR2
431void USR2handler(int sig){
432 if (SIGUSR2==sig)
433 caughtsigUSR2=1;
434 return;
435}
436#endif
437
438// Note if we catch a HUP (or INT in debug mode)
439void HUPhandler(int sig){
440 if (sig==SIGHUP)
441 caughtsigHUP=1;
442 else
443 caughtsigHUP=2;
444 return;
445}
446
447// signal handler for TERM, QUIT, and INT (if not in debug mode)
448void sighandler(int sig){
449 if (!caughtsigEXIT)
450 caughtsigEXIT=sig;
451 return;
452}
453
454
455// signal handler that prints Goodbye message and removes pidfile
456void Goodbye(void){
457
458 // clean up memory -- useful for debugging
459 RmAllConfigEntries();
460 RmAllDevEntries();
461
462 // delete PID file, if one was created
463 RemovePidFile();
464
465 // remove alternate configfile name
466 configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion);
467
468 // useful for debugging -- have we managed memory correctly?
469 if (debugmode || (bytes && exitstatus!=EXIT_NOMEM))
470 PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes);
471
472 // if we are exiting because of a code bug, tell user
473 if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM))
474 PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
475
476 if (exitstatus==0 && bytes)
477 exitstatus=EXIT_BADCODE;
478
479 // and this should be the final output from smartd before it exits
480 PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus);
481
482 return;
483}
484
485#define ENVLENGTH 1024
486
487// a replacement for setenv() which is not available on all platforms.
488// Note that the string passed to putenv must not be freed or made
489// invalid, since a pointer to it is kept by putenv(). This means that
490// it must either be a static buffer or allocated off the heap. The
491// string can be freed if the environment variable is redefined or
492// deleted via another call to putenv(). So we keep these on the stack
493// as long as the popen() call is underway.
494int exportenv(char* stackspace, const char *name, const char *value){
495 snprintf(stackspace,ENVLENGTH, "%s=%s", name, value);
496 return putenv(stackspace);
497}
498
499char* dnsdomain(const char* hostname) {
500 char *p = NULL;
501#ifdef HAVE_GETHOSTBYNAME
502 struct hostent *hp;
503
504 if ((hp = gethostbyname(hostname))) {
505 // Does this work if gethostbyname() returns an IPv6 name in
506 // colon/dot notation? [BA]
507 if ((p = strchr(hp->h_name, '.')))
508 p++; // skip "."
509 }
510#else
511 ARGUSED(hostname);
512#endif
513 return p;
514}
515
516#define EBUFLEN 1024
517
518// If either address or executable path is non-null then send and log
519// a warning email, or execute executable
520void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
521 char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024];
522 char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN];
523 char environ_strings[11][ENVLENGTH];
524 time_t epoch;
525 va_list ap;
526 const int day=24*3600;
527 int days=0;
528 char *whichfail[]={
529 "EmailTest", // 0
530 "Health", // 1
531 "Usage", // 2
532 "SelfTest", // 3
533 "ErrorCount", // 4
534 "FailedHealthCheck", // 5
535 "FailedReadSmartData", // 6
536 "FailedReadSmartErrorLog", // 7
537 "FailedReadSmartSelfTestLog", // 8
538 "FailedOpenDevice", // 9
539 "CurrentPendingSector", // 10
4d59bff9
GG
540 "OfflineUncorrectableSector", // 11
541 "Temperature" // 12
832b75ed
GG
542 };
543
544 char *address, *executable;
545 mailinfo *mail;
546 maildata* data=cfg->mailwarn;
547#ifndef _WIN32
548 FILE *pfp=NULL;
549#else
550 char stdinbuf[1024]; int boxmsgoffs, boxtype;
551#endif
4d59bff9 552 const char *newadd=NULL, *newwarn=NULL;
832b75ed
GG
553 const char *unknown="[Unknown]";
554
555 // See if user wants us to send mail
556 if(!data)
557 return;
558
559 address=data->address;
560 executable=data->emailcmdline;
561
562 if (!address && !executable)
563 return;
564
565 // which type of mail are we sending?
566 mail=(data->maillog)+which;
567
568 // checks for sanity
569 if (data->emailfreq<1 || data->emailfreq>3) {
570 PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq);
571 return;
572 }
573 if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
574 PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
575 which, (int)sizeof(whichfail));
576 return;
577 }
578
579 // Return if a single warning mail has been sent.
580 if ((data->emailfreq==1) && mail->logged)
581 return;
582
583 // Return if this is an email test and one has already been sent.
584 if (which == 0 && mail->logged)
585 return;
586
587 // To decide if to send mail, we need to know what time it is.
588 epoch=time(NULL);
589
590 // Return if less than one day has gone by
591 if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
592 return;
593
594 // Return if less than 2^(logged-1) days have gone by
595 if (data->emailfreq==3 && mail->logged){
596 days=0x01<<(mail->logged-1);
597 days*=day;
598 if (epoch<(mail->lastsent+days))
599 return;
600 }
601
602 // record the time of this mail message, and the first mail message
603 if (!mail->logged)
604 mail->firstsent=epoch;
605 mail->lastsent=epoch;
606
607 // get system host & domain names (not null terminated if length=MAX)
608#ifdef HAVE_GETHOSTNAME
609 if (gethostname(hostname, 256))
610 strcpy(hostname, unknown);
611 else {
612 char *p=NULL;
613 hostname[255]='\0';
614 p = dnsdomain(hostname);
615 if (p && *p) {
616 strncpy(domainname, p, 255);
617 domainname[255]='\0';
618 } else
619 strcpy(domainname, unknown);
620 }
621#else
622 strcpy(hostname, unknown);
623 strcpy(domainname, unknown);
624#endif
625
626#ifdef HAVE_GETDOMAINNAME
627 if (getdomainname(nisdomain, 256))
628 strcpy(nisdomain, unknown);
629 else
630 nisdomain[255]='\0';
631#else
632 strcpy(nisdomain, unknown);
633#endif
634
635 // print warning string into message
636 va_start(ap, fmt);
637 vsnprintf(message, 256, fmt, ap);
638 va_end(ap);
639
640 // appropriate message about further information
641 additional[0]=original[0]=further[0]='\0';
642 if (which) {
643 sprintf(further,"You can also use the smartctl utility for further investigation.\n");
644
645 switch (data->emailfreq){
646 case 1:
647 sprintf(additional,"No additional email messages about this problem will be sent.\n");
648 break;
649 case 2:
650 sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n");
651 break;
652 case 3:
653 sprintf(additional,"Another email message will be sent in %d days if the problem persists\n",
654 (0x01)<<mail->logged);
655 break;
656 }
657 if (data->emailfreq>1 && mail->logged){
658 dateandtimezoneepoch(dates, mail->firstsent);
659 sprintf(original,"The original email about this issue was sent at %s\n", dates);
660 }
661 }
662
663 snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname);
664
665 // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx".
666 if (!executable)
667#ifdef DEFAULT_MAILER
668 executable = DEFAULT_MAILER ;
669#else
670#ifndef _WIN32
671 executable = "mail";
672#else
673 executable = "blat"; // http://blat.sourceforge.net/
674#endif
675#endif
676
677 // make a private copy of address with commas replaced by spaces
678 // to separate recipients
679 if (address) {
680 address=CustomStrDup(data->address, 1, __LINE__, filenameandversion);
681#ifndef _WIN32 // blat mailer needs comma
682 {
683 char *comma=address;
684 while ((comma=strchr(comma, ',')))
685 *comma=' ';
686 }
687#endif
688 }
689
690 // Export information in environment variables that will be useful
691 // for user scripts
692 exportenv(environ_strings[0], "SMARTD_MAILER", executable);
693 exportenv(environ_strings[1], "SMARTD_MESSAGE", message);
694 exportenv(environ_strings[2], "SMARTD_SUBJECT", subject);
695 dateandtimezoneepoch(dates, mail->firstsent);
696 exportenv(environ_strings[3], "SMARTD_TFIRST", dates);
697 snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
698 exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates);
699 exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]);
700 if (address)
701 exportenv(environ_strings[6], "SMARTD_ADDRESS", address);
702 exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name);
703
704 switch (cfg->controller_type) {
705 case CONTROLLER_3WARE_678K:
706 case CONTROLLER_3WARE_9000_CHAR:
707 case CONTROLLER_3WARE_678K_CHAR:
708 {
709 char *s,devicetype[16];
710 sprintf(devicetype, "3ware,%d", cfg->controller_port-1);
711 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
712 if ((s=strchr(cfg->name, ' ')))
713 *s='\0';
714 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
715 if (s)
716 *s=' ';
717 }
718 break;
ba59cff1
GG
719 case CONTROLLER_CCISS:
720 {
721 char *s,devicetype[16];
722 sprintf(devicetype, "cciss,%d", cfg->controller_port-1);
723 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
724 if ((s=strchr(cfg->name, ' ')))
725 *s='\0';
726 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
727 if (s)
728 *s=' ';
729 }
730 break;
832b75ed
GG
731 case CONTROLLER_ATA:
732 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata");
733 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
734 break;
735 case CONTROLLER_MARVELL_SATA:
736 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell");
737 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
738 break;
739 case CONTROLLER_SCSI:
740 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi");
741 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
ba59cff1 742 break;
4d59bff9
GG
743 case CONTROLLER_SAT:
744 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "sat");
745 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
ba59cff1 746 break;
4d59bff9
GG
747 case CONTROLLER_HPT:
748 {
749 char *s,devicetype[16];
750 sprintf(devicetype, "hpt,%d/%d/%d", cfg->hpt_data[0],
751 cfg->hpt_data[1], cfg->hpt_data[2]);
752 exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
753 if ((s=strchr(cfg->name, ' ')))
754 *s='\0';
755 exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
756 if (s)
757 *s=' ';
758 }
ba59cff1 759 break;
832b75ed
GG
760 }
761
762 snprintf(fullmessage, 1024,
763 "This email was generated by the smartd daemon running on:\n\n"
764 " host name: %s\n"
765 " DNS domain: %s\n"
766 " NIS domain: %s\n\n"
767 "The following warning/error was logged by the smartd daemon:\n\n"
768 "%s\n\n"
769 "For details see host's SYSLOG (default: /var/log/messages).\n\n"
770 "%s%s%s",
771 hostname, domainname, nisdomain, message, further, original, additional);
772 exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage);
773
774 // now construct a command to send this as EMAIL
775#ifndef _WIN32
776 if (address)
777 snprintf(command, 2048,
778 "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
779 "%sENDMAIL\n", subject, address, fullmessage);
780 else
781 snprintf(command, 2048, "%s 2>&1", executable);
782
783 // tell SYSLOG what we are about to do...
784 newadd=address?address:"<nomailer>";
785 newwarn=which?"Warning via":"Test of";
786
787 PrintOut(LOG_INFO,"%s %s to %s ...\n",
788 which?"Sending warning via":"Executing test of", executable, newadd);
789
790 // issue the command to send mail or to run the user's executable
791 errno=0;
792 if (!(pfp=popen(command, "r")))
793 // failed to popen() mail process
794 PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n",
795 newwarn, executable, newadd, errno?strerror(errno):"");
796 else {
797 // pipe suceeded!
798 int len, status;
799 char buffer[EBUFLEN];
800
801 // if unexpected output on stdout/stderr, null terminate, print, and flush
802 if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
803 int count=0;
804 int newlen = len<EBUFLEN ? len : EBUFLEN-1;
805 buffer[newlen]='\0';
806 PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n",
807 newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
808
809 // flush pipe if needed
810 while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
811 count++;
812
813 // tell user that pipe was flushed, or that something is really wrong
814 if (count && count<EBUFLEN)
815 PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n",
816 newwarn, executable, newadd);
817 else if (count)
818 PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n",
819 newwarn, executable, newadd);
820 }
821
822 // if something went wrong with mail process, print warning
823 errno=0;
824 if (-1==(status=pclose(pfp)))
825 PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
826 errno?strerror(errno):"");
827 else {
828 // mail process apparently succeeded. Check and report exit status
829 int status8;
830
831 if (WIFEXITED(status)) {
832 // exited 'normally' (but perhaps with nonzero status)
833 status8=WEXITSTATUS(status);
834
835 if (status8>128)
836 PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n",
837 newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
838 else if (status8)
839 PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n",
840 newwarn, executable, newadd, status, status8);
841 else
842 PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
843 }
844
845 if (WIFSIGNALED(status))
846 PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n",
847 newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
848
849 // this branch is probably not possible. If subprocess is
850 // stopped then pclose() should not return.
851 if (WIFSTOPPED(status))
852 PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
853 newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
854
855 }
856 }
857
858#else // _WIN32
859
860 // No "here-documents" on Windows, so must use separate commandline and stdin
861 command[0] = stdinbuf[0] = 0;
862 boxtype = -1; boxmsgoffs = 0;
863 newadd = "<nomailer>";
864 if (address) {
865 // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox
866 int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0);
867 if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) {
868 boxtype = (addroffs > 0 ? 1 : 0);
869 addroffs += 6;
870 if (address[addroffs])
871 addroffs++;
872 }
873 else
874 addroffs = 0;
875
876 if (address[addroffs]) {
877 // Use "blat" parameter syntax (TODO: configure via -M for other mailers)
878 snprintf(command, sizeof(command),
879 "%s - -q -subject \"%s\" -to \"%s\"",
880 executable, subject, address+addroffs);
881 newadd = address+addroffs;
882 }
883 // Message for mail [0...] and messagebox [boxmsgoffs...]
884 snprintf(stdinbuf, sizeof(stdinbuf),
885 "This email was generated by the smartd daemon running on:\n\n"
886 " host name: %s\n"
887 " DNS domain: %s\n"
888// " NIS domain: %s\n"
889 "\n%n"
890 "The following warning/error was logged by the smartd daemon:\n\n"
891 "%s\n\n"
892 "For details see the event log or log file of smartd.\n\n"
893 "%s%s%s"
894 "\n",
895 hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional);
896 }
897 else
898 snprintf(command, sizeof(command), "%s", executable);
899
900 newwarn=which?"Warning via":"Test of";
901 if (boxtype >= 0) {
902 // show message box
903 daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
904 PrintOut(LOG_INFO,"%s message box\n", newwarn);
905 }
906 if (command[0]) {
907 char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
908 int rc;
909 // run command
910 PrintOut(LOG_INFO,"%s %s to %s ...\n",
911 (which?"Sending warning via":"Executing test of"), executable, newadd);
912 rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf));
913 if (rc >= 0 && stdoutbuf[0])
914 PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
915 newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf);
916 if (rc != 0)
917 PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
918 newwarn, executable, newadd, rc);
919 else
920 PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
921 }
922
923#endif // _WIN32
924
925 // increment mail sent counter
926 mail->logged++;
927
928 // free copy of address (without commas)
929 address=FreeNonZero(address, -1, __LINE__, filenameandversion);
930
931 return;
932}
933
934// Printing function for watching ataprint commands, or losing them
935// [From GLIBC Manual: Since the prototype doesn't specify types for
936// optional arguments, in a call to a variadic function the default
937// argument promotions are performed on the optional argument
938// values. This means the objects of type char or short int (whether
939// signed or not) are promoted to either int or unsigned int, as
940// appropriate.]
4d59bff9 941void pout(const char *fmt, ...){
832b75ed
GG
942 va_list ap;
943
944 // get the correct time in syslog()
945 FixGlibcTimeZoneBug();
946 // initialize variable argument list
947 va_start(ap,fmt);
948 // in debug==1 mode we will print the output from the ataprint.o functions!
949 if (debugmode && debugmode!=2)
950#ifdef _WIN32
951 if (facility == LOG_LOCAL1) // logging to stdout
952 vfprintf(stderr,fmt,ap);
953 else
954#endif
955 vprintf(fmt,ap);
956 // in debug==2 mode we print output from knowndrives.o functions
957 else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) {
958 openlog("smartd", LOG_PID, facility);
959 vsyslog(LOG_INFO, fmt, ap);
960 closelog();
961 }
962 va_end(ap);
963 fflush(NULL);
964 return;
965}
966
967// This function prints either to stdout or to the syslog as needed.
4d59bff9
GG
968// This function is also used by utility.cpp to report LOG_CRIT errors.
969void PrintOut(int priority, const char *fmt, ...){
832b75ed
GG
970 va_list ap;
971
972 // get the correct time in syslog()
973 FixGlibcTimeZoneBug();
974 // initialize variable argument list
975 va_start(ap,fmt);
976 if (debugmode)
977#ifdef _WIN32
978 if (facility == LOG_LOCAL1) // logging to stdout
979 vfprintf(stderr,fmt,ap);
980 else
981#endif
982 vprintf(fmt,ap);
983 else {
984 openlog("smartd", LOG_PID, facility);
985 vsyslog(priority,fmt,ap);
986 closelog();
987 }
988 va_end(ap);
989 return;
990}
991
992// Forks new process, closes ALL file descriptors, redirects stdin,
993// stdout, and stderr. Not quite daemon(). See
994// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html
995// for a good description of why we do things this way.
996void DaemonInit(){
997#ifndef _WIN32
998 pid_t pid;
999 int i;
1000
1001 // flush all buffered streams. Else we might get two copies of open
1002 // streams since both parent and child get copies of the buffers.
1003 fflush(NULL);
1004
1005 if ((pid=fork()) < 0) {
1006 // unable to fork!
1007 PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
1008 EXIT(EXIT_STARTUP);
1009 }
1010 else if (pid)
1011 // we are the parent process -- exit cleanly
1012 EXIT(0);
1013
1014 // from here on, we are the child process.
1015 setsid();
1016
1017 // Fork one more time to avoid any possibility of having terminals
1018 if ((pid=fork()) < 0) {
1019 // unable to fork!
1020 PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
1021 EXIT(EXIT_STARTUP);
1022 }
1023 else if (pid)
1024 // we are the parent process -- exit cleanly
1025 EXIT(0);
1026
1027 // Now we are the child's child...
1028
1029 // close any open file descriptors
1030 for (i=getdtablesize();i>=0;--i)
1031 close(i);
1032
1033#ifdef __CYGWIN__
1034 // Cygwin's setsid() does not detach the process from Windows console
1035 FreeConsole();
1036#endif // __CYGWIN__
1037
1038 // redirect any IO attempts to /dev/null for stdin
1039 i=open("/dev/null",O_RDWR);
1040 // stdout
1041 dup(i);
1042 // stderr
1043 dup(i);
1044 umask(0);
1045 chdir("/");
1046
1047 PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
1048
1049#else // _WIN32
1050
1051 // No fork() on native Win32
1052 // Detach this process from console
1053 fflush(NULL);
1054 if (daemon_detach("smartd")) {
1055 PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
1056 EXIT(EXIT_STARTUP);
1057 }
1058 // stdin/out/err now closed if not redirected
1059
1060#endif // _WIN32
1061 return;
1062}
1063
1064// create a PID file containing the current process id
1065void WritePidFile() {
1066 if (pid_file) {
1067 int error = 0;
1068 pid_t pid = getpid();
1069 mode_t old_umask;
1070 FILE* fp;
1071
1072#ifndef __CYGWIN__
1073 old_umask = umask(0077); // rwx------
1074#else
1075 // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
1076 old_umask = umask(0033); // rwxr--r--
1077#endif
1078 fp = fopen(pid_file, "w");
1079 umask(old_umask);
1080 if (fp == NULL) {
1081 error = 1;
1082 } else if (fprintf(fp, "%d\n", (int)pid) <= 0) {
1083 error = 1;
1084 } else if (fclose(fp) != 0) {
1085 error = 1;
1086 }
1087 if (error) {
1088 PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file);
1089 EXIT(EXIT_PID);
1090 }
1091 PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid);
1092 }
1093 return;
1094}
1095
1096// Prints header identifying version of code and home
1097void PrintHead(){
1098#ifdef HAVE_GET_OS_VERSION_STR
1099 const char * ver = get_os_version_str();
1100#else
1101 const char * ver = SMARTMONTOOLS_BUILD_HOST;
1102#endif
1103 PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
1104 PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n");
1105 return;
1106}
1107
1108// prints help info for configuration file Directives
1109void Directives() {
1110 PrintOut(LOG_INFO,
1111 "Configuration file (%s) Directives (after device name):\n"
ba59cff1 1112 " -d TYPE Set the device type: ata, scsi, marvell, removable, sat, 3ware,N, hpt,L/M/N, cciss,N\n"
832b75ed
GG
1113 " -T TYPE Set the tolerance to one of: normal, permissive\n"
1114 " -o VAL Enable/disable automatic offline tests (on/off)\n"
1115 " -S VAL Enable/disable attribute autosave (on/off)\n"
1116 " -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n"
1117 " -H Monitor SMART Health Status, report if failed\n"
1118 " -s REG Do Self-Test at time(s) given by regular expression REG\n"
1119 " -l TYPE Monitor SMART log. Type is one of: error, selftest\n"
1120 " -f Monitor 'Usage' Attributes, report failures\n"
1121 " -m ADD Send email warning to address ADD\n"
1122 " -M TYPE Modify email warning behavior (see man page)\n"
1123 " -p Report changes in 'Prefailure' Attributes\n"
1124 " -u Report changes in 'Usage' Attributes\n"
1125 " -t Equivalent to -p and -u Directives\n"
1126 " -r ID Also report Raw values of Attribute ID with -p, -u or -t\n"
1127 " -R ID Track changes in Attribute ID Raw value with -p, -u or -t\n"
1128 " -i ID Ignore Attribute ID for -f Directive\n"
1129 " -I ID Ignore Attribute ID for -p, -u or -t Directive\n"
1130 " -C ID Monitor Current Pending Sectors in Attribute ID\n"
1131 " -U ID Monitor Offline Uncorrectable Sectors in Attribute ID\n"
4d59bff9 1132 " -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\n"
832b75ed
GG
1133 " -v N,ST Modifies labeling of Attribute N (see man page) \n"
1134 " -P TYPE Drive-specific presets: use, ignore, show, showall\n"
1135 " -a Default: -H -f -t -l error -l selftest -C 197 -U 198\n"
1136 " -F TYPE Firmware bug workaround: none, samsung, samsung2\n"
1137 " # Comment: text after a hash sign is ignored\n"
1138 " \\ Line continuation character\n"
1139 "Attribute ID is a decimal integer 1 <= ID <= 255\n"
1140 "Use ID = 0 to turn off -C and/or -U Directives\n"
1141 "Example: /dev/hda -a\n",
1142 configfile);
1143 return;
1144}
1145
1146/* Returns a pointer to a static string containing a formatted list of the valid
1147 arguments to the option opt or NULL on failure. */
1148const char *GetValidArgList(char opt) {
1149 switch (opt) {
1150 case 'c':
1151 return "<FILE_NAME>, -";
1152 case 's':
1153 return "valid_regular_expression";
1154 case 'l':
1155 return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
1156 case 'q':
1157 return "nodev, errors, nodevstartup, never, onecheck, showtests";
1158 case 'r':
1159 return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
1160 case 'p':
1161 return "<FILE_NAME>";
1162 case 'i':
1163 return "<INTEGER_SECONDS>";
1164 default:
1165 return NULL;
1166 }
1167}
1168
1169/* prints help information for command syntax */
1170void Usage (void){
1171 PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
1172#ifdef HAVE_GETOPT_LONG
1173 PrintOut(LOG_INFO," -c NAME|-, --configfile=NAME|-\n");
1174 PrintOut(LOG_INFO," Read configuration file NAME or stdin [default is %s]\n\n", configfile);
1175 PrintOut(LOG_INFO," -d, --debug\n");
1176 PrintOut(LOG_INFO," Start smartd in debug mode\n\n");
1177 PrintOut(LOG_INFO," -D, --showdirectives\n");
1178 PrintOut(LOG_INFO," Print the configuration file Directives and exit\n\n");
1179 PrintOut(LOG_INFO," -h, --help, --usage\n");
1180 PrintOut(LOG_INFO," Display this help and exit\n\n");
1181 PrintOut(LOG_INFO," -i N, --interval=N\n");
1182 PrintOut(LOG_INFO," Set interval between disk checks to N seconds, where N >= 10\n\n");
1183 PrintOut(LOG_INFO," -l local[0-7], --logfacility=local[0-7]\n");
1184#ifndef _WIN32
1185 PrintOut(LOG_INFO," Use syslog facility local0 - local7 or daemon [default]\n\n");
1186#else
1187 PrintOut(LOG_INFO," Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
1188#endif
1189 PrintOut(LOG_INFO," -p NAME, --pidfile=NAME\n");
1190 PrintOut(LOG_INFO," Write PID file NAME\n\n");
1191 PrintOut(LOG_INFO," -q WHEN, --quit=WHEN\n");
1192 PrintOut(LOG_INFO," Quit on one of: %s\n\n", GetValidArgList('q'));
1193 PrintOut(LOG_INFO," -r, --report=TYPE\n");
1194 PrintOut(LOG_INFO," Report transactions for one of: %s\n\n", GetValidArgList('r'));
1195#if defined(_WIN32) || defined(__CYGWIN__)
1196 PrintOut(LOG_INFO," --service\n");
1197 PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n");
1198#ifdef _WIN32
1199 PrintOut(LOG_INFO," smartd install [options]\n");
1200 PrintOut(LOG_INFO," Remove service with:\n");
1201 PrintOut(LOG_INFO," smartd remove\n\n");
1202#else
1203 PrintOut(LOG_INFO," /etc/rc.d/init.d/smartd install [options]\n");
1204 PrintOut(LOG_INFO," Remove service with:\n");
1205 PrintOut(LOG_INFO," /etc/rc.d/init.d/smartd remove\n\n");
1206#endif
1207#endif // _WIN32 || __CYGWIN__
1208 PrintOut(LOG_INFO," -V, --version, --license, --copyright\n");
1209 PrintOut(LOG_INFO," Print License, Copyright, and version information\n");
1210#else
1211 PrintOut(LOG_INFO," -c NAME|- Read configuration file NAME or stdin [default is %s]\n", configfile);
1212 PrintOut(LOG_INFO," -d Start smartd in debug mode\n");
1213 PrintOut(LOG_INFO," -D Print the configuration file Directives and exit\n");
1214 PrintOut(LOG_INFO," -h Display this help and exit\n");
1215 PrintOut(LOG_INFO," -i N Set interval between disk checks to N seconds, where N >= 10\n");
1216 PrintOut(LOG_INFO," -l local? Use syslog facility local0 - local7, or daemon\n");
1217 PrintOut(LOG_INFO," -p NAME Write PID file NAME\n");
1218 PrintOut(LOG_INFO," -q WHEN Quit on one of: %s\n", GetValidArgList('q'));
1219 PrintOut(LOG_INFO," -r TYPE Report transactions for one of: %s\n", GetValidArgList('r'));
1220 PrintOut(LOG_INFO," -V Print License, Copyright, and version information\n");
1221#endif
1222}
1223
1224// returns negative if problem, else fd>=0
1225static int OpenDevice(char *device, char *mode, int scanning) {
1226 int fd;
1227 char *s=device;
1228
1229 // If there is an ASCII "space" character in the device name,
4d59bff9 1230 // terminate string there. This is for 3ware and highpoint devices only.
832b75ed
GG
1231 if ((s=strchr(device,' ')))
1232 *s='\0';
1233
1234 // open the device
1235 fd = deviceopen(device, mode);
1236
1237 // if we removed a space, put it back in please
1238 if (s)
1239 *s=' ';
1240
1241 // if we failed to open the device, complain!
1242 if (fd < 0) {
1243
1244 // For linux+devfs, a nonexistent device gives a strange error
1245 // message. This makes the error message a bit more sensible.
1246 // If no debug and scanning - don't print errors
1247 if (debugmode || !scanning) {
1248 if (errno==ENOENT || errno==ENOTDIR)
4d59bff9
GG
1249 errno=ENODEV;
1250
832b75ed
GG
1251 PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n",
1252 device, strerror(errno));
1253 }
1254 return -1;
1255 }
1256 // device opened sucessfully
1257 return fd;
1258}
1259
1260int CloseDevice(int fd, char *name){
1261 if (deviceclose(fd)){
1262 PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd);
1263 return 1;
1264 }
1265 // device sucessfully closed
1266 return 0;
1267}
1268
1269// returns <0 on failure
1270int ATAErrorCount(int fd, char *name){
1271 struct ata_smart_errorlog log;
1272
1273 if (-1==ataReadErrorLog(fd,&log)){
1274 PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
1275 return -1;
1276 }
1277
1278 // return current number of ATA errors
1279 return log.error_log_pointer?log.ata_error_count:0;
1280}
1281
1282// returns <0 if problem. Otherwise, bottom 8 bits are the self test
1283// error count, and top bits are the power-on hours of the last error.
1284int SelfTestErrorCount(int fd, char *name){
1285 struct ata_smart_selftestlog log;
1286
1287 if (-1==ataReadSelfTestLog(fd,&log)){
1288 PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
1289 return -1;
1290 }
1291
1292 // return current number of self-test errors
1293 return ataPrintSmartSelfTestlog(&log,0);
1294}
1295
1296// scan to see what ata devices there are, and if they support SMART
1297int ATADeviceScan(cfgfile *cfg, int scanning){
1298 int fd, supported=0;
1299 struct ata_identify_device drive;
1300 char *name=cfg->name;
1301 int retainsmartdata=0;
1302 int retid;
1303 char *mode;
1304
1305 // should we try to register this as an ATA device?
1306 switch (cfg->controller_type) {
1307 case CONTROLLER_ATA:
1308 case CONTROLLER_3WARE_678K:
1309 case CONTROLLER_MARVELL_SATA:
4d59bff9 1310 case CONTROLLER_HPT:
832b75ed
GG
1311 case CONTROLLER_UNKNOWN:
1312 mode="ATA";
1313 break;
1314 case CONTROLLER_3WARE_678K_CHAR:
1315 mode="ATA_3WARE_678K";
1316 break;
1317 case CONTROLLER_3WARE_9000_CHAR:
1318 mode="ATA_3WARE_9000";
1319 break;
4d59bff9
GG
1320 case CONTROLLER_SAT:
1321 mode="SCSI";
1322 break;
832b75ed
GG
1323 default:
1324 // not a recognized ATA or SATA device. We should never enter
1325 // this branch.
1326 return 1;
1327 }
1328
1329 // open the device
1330 if ((fd=OpenDevice(name, mode, scanning))<0)
1331 // device open failed
1332 return 1;
1333 PrintOut(LOG_INFO,"Device: %s, opened\n", name);
1334
1335 // pass user settings on to low-level ATA commands
1336 con->controller_port=cfg->controller_port;
4d59bff9
GG
1337 con->hpt_data[0]=cfg->hpt_data[0];
1338 con->hpt_data[1]=cfg->hpt_data[1];
1339 con->hpt_data[2]=cfg->hpt_data[2];
832b75ed 1340 con->controller_type=cfg->controller_type;
4d59bff9 1341 con->controller_explicit=cfg->controller_explicit;
832b75ed 1342 con->fixfirmwarebug = cfg->fixfirmwarebug;
4d59bff9 1343 con->satpassthrulen = cfg->satpassthrulen;
832b75ed
GG
1344
1345 // Get drive identity structure
1346 if ((retid=ataReadHDIdentity (fd,&drive))){
1347 if (retid<0)
1348 // Unable to read Identity structure
1349 PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
1350 else
1351 PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
1352 name, packetdevicetype(retid-1));
1353 CloseDevice(fd, name);
1354 return 2;
1355 }
1356
1357 // Show if device in database, and use preset vendor attribute
1358 // options unless user has requested otherwise.
1359 if (cfg->ignorepresets)
1360 PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
1361 else {
1362 // do whatever applypresets decides to do. Will allocate memory if
1363 // cfg->attributedefs is needed.
1364 if (applypresets(&drive, &cfg->attributedefs, con)<0)
1365 PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
1366 else
1367 PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name);
1368
1369 // then save the correct state of the flag (applypresets may have changed it)
1370 cfg->fixfirmwarebug = con->fixfirmwarebug;
1371 }
1372
1373 // If requested, show which presets would be used for this drive
1374 if (cfg->showpresets) {
1375 int savedebugmode=debugmode;
1376 PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
1377 if (!debugmode)
1378 debugmode=2;
1379 showpresets(&drive);
1380 debugmode=savedebugmode;
1381 }
1382
1383 // see if drive supports SMART
1384 supported=ataSmartSupport(&drive);
1385 if (supported!=1) {
1386 if (supported==0)
1387 // drive does NOT support SMART
1388 PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
1389 else
1390 // can't tell if drive supports SMART
1391 PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
1392
1393 // should we proceed anyway?
1394 if (cfg->permissive){
1395 PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
1396 }
1397 else {
1398 PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
1399 CloseDevice(fd, name);
1400 return 2;
1401 }
1402 }
1403
1404 if (ataEnableSmart(fd)){
1405 // Enable SMART command has failed
1406 PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
1407 CloseDevice(fd, name);
1408 return 2;
1409 }
1410
1411 // disable device attribute autosave...
1412 if (cfg->autosave==1){
1413 if (ataDisableAutoSave(fd))
1414 PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
1415 else
1416 PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
1417 }
1418
1419 // or enable device attribute autosave
1420 if (cfg->autosave==2){
1421 if (ataEnableAutoSave(fd))
1422 PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
1423 else
1424 PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
1425 }
1426
1427 // capability check: SMART status
1428 if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
1429 PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
1430 cfg->smartcheck=0;
1431 }
1432
1433 // capability check: Read smart values and thresholds. Note that
1434 // smart values are ALSO needed even if we ONLY want to know if the
1435 // device is self-test log or error-log capable! After ATA-5, this
1436 // information was ALSO reproduced in the IDENTIFY DEVICE response,
1437 // but sadly not for ATA-5. Sigh.
1438
1439 // do we need to retain SMART data after returning from this routine?
4d59bff9 1440 retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage || cfg->tempdiff || cfg->tempinfo || cfg->tempcrit;
832b75ed
GG
1441
1442 // do we need to get SMART data?
1443 if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) {
1444
1445 unsigned char currentpending, offlinepending;
1446
1447 cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values));
1448 cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt));
1449
1450 if (!cfg->smartval || !cfg->smartthres){
1451 PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n");
1452 EXIT(EXIT_NOMEM);
1453 }
1454
1455 if (ataReadSmartValues(fd,cfg->smartval) ||
1456 ataReadSmartThresholds (fd,cfg->smartthres)){
1457 PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
1458 retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0;
4d59bff9 1459 cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
832b75ed
GG
1460 cfg->pending=DONT_MONITOR_UNC;
1461 }
1462
1463 // see if the necessary Attribute is there to monitor offline or
4d59bff9 1464 // current pending sectors or temperature
832b75ed
GG
1465 TranslatePending(cfg->pending, &currentpending, &offlinepending);
1466
1467 if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) {
1468 PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n",
1469 name, (int)currentpending);
1470 cfg->pending &= 0xff00;
1471 cfg->pending |= CUR_UNC_DEFAULT;
1472 }
1473
1474 if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) {
1475 PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count - no Attribute %d\n",
1476 name, (int)offlinepending);
1477 cfg->pending &= 0x00ff;
1478 cfg->pending |= OFF_UNC_DEFAULT<<8;
1479 }
4d59bff9
GG
1480
1481 if ( (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
1482 && !ATAReturnTemperatureValue(cfg->smartval, cfg->attributedefs)) {
1483 PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name);
1484 cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
1485 }
832b75ed
GG
1486 }
1487
1488 // enable/disable automatic on-line testing
1489 if (cfg->autoofflinetest){
1490 // is this an enable or disable request?
4d59bff9 1491 const char *what=(cfg->autoofflinetest==1)?"disable":"enable";
832b75ed
GG
1492 if (!cfg->smartval)
1493 PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
1494 else {
1495 // if command appears unsupported, issue a warning...
1496 if (!isSupportAutomaticTimer(cfg->smartval))
1497 PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
1498 // ... but then try anyway
1499 if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
1500 PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
1501 else
1502 PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
1503 }
1504 }
1505
1506 // capability check: self-test-log
1507 if (cfg->selftest){
1508 int retval;
1509
1510 // start with service disabled, and re-enable it if all works OK
1511 cfg->selftest=0;
1512 cfg->selflogcount=0;
1513 cfg->selfloghour=0;
1514
1515 if (!cfg->smartval)
1516 PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name);
1517 else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive))
1518 PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name);
1519 else if ((retval=SelfTestErrorCount(fd, name))<0)
1520 PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name);
1521 else {
1522 cfg->selftest=1;
1523 cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
1524 cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
1525 }
1526 }
1527
1528 // capability check: ATA error log
1529 if (cfg->errorlog){
1530 int val;
1531
1532 // start with service disabled, and re-enable it if all works OK
1533 cfg->errorlog=0;
1534 cfg->ataerrorcount=0;
1535
1536 if (!cfg->smartval)
1537 PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name);
1538 else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive))
1539 PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name);
1540 else if ((val=ATAErrorCount(fd, name))<0)
1541 PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name);
1542 else {
1543 cfg->errorlog=1;
1544 cfg->ataerrorcount=val;
1545 }
1546 }
1547
1548 // If we don't need to save SMART data, get rid of it now
1549 if (!retainsmartdata) {
1550 if (cfg->smartval) {
1551 cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion);
1552 bytes-=sizeof(struct ata_smart_values);
1553 }
1554 if (cfg->smartthres) {
1555 cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion);
1556 bytes-=sizeof(struct ata_smart_thresholds_pvt);
1557 }
1558 }
1559
1560 // capabilities check -- does it support powermode?
1561 if (cfg->powermode) {
1562 int powermode=ataCheckPowerMode(fd);
1563
1564 if (-1 == powermode) {
1565 PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
1566 cfg->powermode=0;
1567 }
1568 else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
1569 PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
1570 name, powermode);
1571 cfg->powermode=0;
1572 }
1573 }
1574
1575 // If no tests available or selected, return
4d59bff9
GG
1576 if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck ||
1577 cfg->usagefailed || cfg->prefail || cfg->usage ||
1578 cfg->tempdiff || cfg->tempinfo || cfg->tempcrit )) {
832b75ed
GG
1579 CloseDevice(fd, name);
1580 return 3;
1581 }
1582
1583 // Do we still have entries available?
1584 while (numdevata>=atadevlist_max)
1585 atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
1586
1587 // register device
1588 PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
1589
1590 // record number of device, type of device, increment device count
1591 if (cfg->controller_type == CONTROLLER_UNKNOWN)
1592 cfg->controller_type=CONTROLLER_ATA;
1593
1594 // close file descriptor
1595 CloseDevice(fd, name);
1596 return 0;
1597}
1598
9ebc753d
GG
1599// Returns 0 if normal SCSI device. Returns -1 if INQUIRY fails.
1600// Returns 2 if ATA device detected behind SAT layer.
1601// Returns 1 if other device detected that we don't want to treat
1602// as a normal SCSI device.
832b75ed
GG
1603static int SCSIFilterKnown(int fd, char * device)
1604{
1605 char req_buff[256];
832b75ed
GG
1606 int req_len, avail_len, len;
1607
1608 memset(req_buff, 0, 96);
1609 req_len = 36;
1610 if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
1611 /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
1612 /* watch this spot ... other devices could lock up here */
1613 req_len = 64;
1614 if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
1615 PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device);
1616 // device doesn't like INQUIRY commands
9ebc753d 1617 return -1;
832b75ed
GG
1618 }
1619 }
1620 avail_len = req_buff[4] + 5;
1621 len = (avail_len < req_len) ? avail_len : req_len;
1622 if (len >= 36) {
1623 if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) {
1624 PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device);
1625 PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device);
1626 return 1;
1627 } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
1628 PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device);
1629 return 1;
9ebc753d
GG
1630 } else if ((avail_len >= 36) &&
1631 (0 == strncmp(req_buff + 8, "ATA ", 8)) &&
1632 has_sat_pass_through(fd, 0 /* non-packet dev */)) {
1633
1634 PrintOut(LOG_INFO, "Device %s: ATA disk detected behind SAT layer\n",
1635 device);
1636 PrintOut(LOG_INFO, " Try adding '-d sat' to the device line in the "
1637 "smartd.conf file.\n");
1638 PrintOut(LOG_INFO, " For example: '%s -a -d sat'\n", device);
1639 return 2;
832b75ed
GG
1640 }
1641 }
1642 return 0;
1643}
1644
1645// on success, return 0. On failure, return >0. Never return <0,
1646// please.
1647static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
1648 int k, fd, err;
1649 char *device = cfg->name;
1650 struct scsi_iec_mode_page iec;
1651 UINT8 tBuf[64];
ba59cff1 1652 char *mode=NULL;
832b75ed
GG
1653
1654 // should we try to register this as a SCSI device?
1655 switch (cfg->controller_type) {
1656 case CONTROLLER_SCSI:
1657 case CONTROLLER_UNKNOWN:
ba59cff1
GG
1658 mode="SCSI";
1659 break;
1660 case CONTROLLER_CCISS:
1661 mode="CCISS";
832b75ed
GG
1662 break;
1663 default:
1664 return 1;
1665 }
ba59cff1
GG
1666 // pass user settings on to low-level SCSI commands
1667 con->controller_port=cfg->controller_port;
1668 con->controller_type=cfg->controller_type;
832b75ed
GG
1669
1670 // open the device
ba59cff1 1671 if ((fd = OpenDevice(device, mode, scanning)) < 0)
832b75ed
GG
1672 return 1;
1673 PrintOut(LOG_INFO,"Device: %s, opened\n", device);
1674
1675 // early skip if device known and needs to be handled by some other
1676 // device type (e.g. '-d 3ware,<n>')
1677 if (SCSIFilterKnown(fd, device)) {
1678 CloseDevice(fd, device);
1679 return 2;
1680 }
1681
1682 // check that device is ready for commands. IE stores its stuff on
1683 // the media.
1684 if ((err = scsiTestUnitReady(fd))) {
1685 if (SIMPLE_ERR_NOT_READY == err)
1686 PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
1687 else if (SIMPLE_ERR_NO_MEDIUM == err)
1688 PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
1689 else if (SIMPLE_ERR_BECOMING_READY == err)
1690 PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
1691 else
1692 PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
1693 CloseDevice(fd, device);
1694 return 2;
1695 }
1696
1697 // Badly-conforming USB storage devices may fail this check.
1698 // The response to the following IE mode page fetch (current and
1699 // changeable values) is carefully examined. It has been found
1700 // that various USB devices that malform the response will lock up
1701 // if asked for a log page (e.g. temperature) so it is best to
1702 // bail out now.
1703 if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len)))
1704 cfg->modese_len = iec.modese_len;
1705 else if (SIMPLE_ERR_BAD_FIELD == err)
1706 ; /* continue since it is reasonable not to support IE mpage */
1707 else { /* any other error (including malformed response) unreasonable */
1708 PrintOut(LOG_INFO,
1709 "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n",
1710 device, err);
1711 CloseDevice(fd, device);
1712 return 3;
1713 }
1714
1715 // N.B. The following is passive (i.e. it doesn't attempt to turn on
1716 // smart if it is off). This may change to be the same as the ATA side.
1717 if (!scsi_IsExceptionControlEnabled(&iec)) {
1718 PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
1719 "Try 'smartctl -s on %s' to turn on SMART features\n",
1720 device, device);
1721 CloseDevice(fd, device);
1722 return 3;
1723 }
1724
1725 // Device exists, and does SMART. Add to list (allocating more space if needed)
1726 while (numdevscsi >= scsidevlist_max)
1727 scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
1728
1729 // Flag that certain log pages are supported (information may be
1730 // available from other sources).
4d59bff9 1731 if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
832b75ed
GG
1732 for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
1733 switch (tBuf[k]) {
1734 case TEMPERATURE_LPAGE:
1735 cfg->TempPageSupported = 1;
1736 break;
1737 case IE_LPAGE:
1738 cfg->SmartPageSupported = 1;
1739 break;
1740 default:
1741 break;
1742 }
1743 }
1744 }
1745
1746 // record type of device
ba59cff1
GG
1747 if (cfg->controller_type == CONTROLLER_UNKNOWN)
1748 cfg->controller_type = CONTROLLER_SCSI;
832b75ed
GG
1749
1750 // get rid of allocated memory only needed for ATA devices. These
1751 // might have been allocated if the user specified Ignore options or
1752 // other ATA-only Attribute-specific options on the DEVICESCAN line.
1753 cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
1754 cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
1755 cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion);
1756 cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
1757
1758 // Check if scsiCheckIE() is going to work
1759 {
1760 UINT8 asc = 0;
1761 UINT8 ascq = 0;
1762 UINT8 currenttemp = 0;
1763 UINT8 triptemp = 0;
1764
1765 if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
1766 &asc, &ascq, &currenttemp, &triptemp)) {
1767 PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
1768 cfg->SuppressReport = 1;
4d59bff9
GG
1769 if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit) {
1770 PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", device);
1771 cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
1772 }
832b75ed
GG
1773 }
1774 }
1775
1776 // capability check: self-test-log
1777 if (cfg->selftest){
1778 int retval=scsiCountFailedSelfTests(fd, 0);
1779 if (retval<0) {
1780 // no self-test log, turn off monitoring
1781 PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
1782 cfg->selftest=0;
1783 cfg->selflogcount=0;
1784 cfg->selfloghour=0;
1785 }
1786 else {
1787 // register starting values to watch for changes
1788 cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
1789 cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
1790 }
1791 }
1792
1793 // disable autosave (set GLTSD bit)
1794 if (cfg->autosave==1){
1795 if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
1796 PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
1797 else
1798 PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
1799 }
1800
1801 // or enable autosave (clear GLTSD bit)
1802 if (cfg->autosave==2){
1803 if (scsiSetControlGLTSD(fd, 0, cfg->modese_len))
1804 PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
1805 else
1806 PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
1807 }
1808
1809 // tell user we are registering device
1810 PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
1811
1812 // close file descriptor
1813 CloseDevice(fd, device);
1814 return 0;
1815}
1816
1817// We compare old and new values of the n'th attribute. Note that n
1818// is NOT the attribute ID number.. If (Normalized & Raw) equal,
1819// then return 0, else nonzero.
1820int ATACompareValues(changedattribute_t *delta,
4d59bff9
GG
1821 struct ata_smart_values *newv,
1822 struct ata_smart_values *oldv,
832b75ed
GG
1823 struct ata_smart_thresholds_pvt *thresholds,
1824 int n, char *name){
1825 struct ata_smart_attribute *now,*was;
1826 struct ata_smart_threshold_entry *thre;
1827 unsigned char oldval,newval;
1828 int sameraw;
1829
1830 // check that attribute number in range, and no null pointers
4d59bff9 1831 if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !newv || !oldv || !thresholds)
832b75ed
GG
1832 return 0;
1833
1834 // pointers to disk's values and vendor's thresholds
4d59bff9
GG
1835 now=newv->vendor_attributes+n;
1836 was=oldv->vendor_attributes+n;
832b75ed
GG
1837 thre=thresholds->thres_entries+n;
1838
1839 // consider only valid attributes
1840 if (!now->id || !was->id || !thre->id)
1841 return 0;
1842
1843
1844 // issue warning if they don't have the same ID in all structures:
1845 if ( (now->id != was->id) || (now->id != thre->id) ){
1846 PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n",
1847 name, (int)now->id, (int)was->id, (int)thre->id);
1848 return 0;
1849 }
1850
1851 // new and old values of Normalized Attributes
1852 newval=now->current;
1853 oldval=was->current;
1854
1855 // See if the RAW values are unchanged (ie, the same)
1856 if (memcmp(now->raw, was->raw, 6))
1857 sameraw=0;
1858 else
1859 sameraw=1;
1860
1861 // if any values out of the allowed range, or if the values haven't
1862 // changed, return 0
1863 if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
1864 return 0;
1865
1866 // values have changed. Construct output and return
1867 delta->newval=newval;
1868 delta->oldval=oldval;
1869 delta->id=now->id;
1870 delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags);
1871 delta->sameraw=sameraw;
1872
1873 return 1;
1874}
1875
1876// This looks to see if the corresponding bit of the 32 bytes is set.
1877// This wastes a few bytes of storage but eliminates all searching and
1878// sorting functions! Entry is ZERO <==> the attribute ON. Calling
1879// with set=0 tells you if the attribute is being tracked or not.
1880// Calling with set=1 turns the attribute OFF.
1881int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){
1882 unsigned char *data;
1883 int loc=attr>>3;
1884 int bit=attr & 0x07;
1885 unsigned char mask=0x01<<bit;
1886
1887 if (which>=NMONITOR || which < 0){
1888 PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s",
1889 whatline, filenameandversion, which, reportbug);
1890 EXIT(EXIT_BADCODE);
1891 }
1892
1893 if (*datap == NULL){
1894 // NULL data implies Attributes are ON...
1895 if (!set)
1896 return 0;
1897
1898 // we are writing
1899 if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){
1900 PrintOut(LOG_CRIT,"No memory to create monattflags\n");
1901 EXIT(EXIT_NOMEM);
1902 }
1903 }
1904
1905 // pointer to the 256 bits that we need
1906 data=*datap+which*32;
1907
1908 // attribute zero is always OFF
1909 if (!attr)
1910 return 1;
1911
1912 if (!set)
1913 return (data[loc] & mask);
1914
1915 data[loc]|=mask;
1916
1917 // return value when setting has no sense
1918 return 0;
1919}
1920
1921// If the self-test log has got more self-test errors (or more recent
1922// self-test errors) recorded, then notify user.
4d59bff9 1923void CheckSelfTestLogs(cfgfile *cfg, int newi){
832b75ed
GG
1924 char *name=cfg->name;
1925
4d59bff9 1926 if (newi<0)
832b75ed
GG
1927 // command failed
1928 MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
1929 else {
1930 // old and new error counts
1931 int oldc=cfg->selflogcount;
4d59bff9 1932 int newc=SELFTEST_ERRORCOUNT(newi);
832b75ed
GG
1933
1934 // old and new error timestamps in hours
1935 int oldh=cfg->selfloghour;
4d59bff9 1936 int newh=SELFTEST_ERRORHOURS(newi);
832b75ed
GG
1937
1938 if (oldc<newc) {
1939 // increase in error count
1940 PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
1941 name, oldc, newc);
1942 MailWarning(cfg, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
1943 name, oldc, newc);
1944 } else if (oldh!=newh) {
1945 // more recent error
1946 // a 'more recent' error might actually be a smaller hour number,
1947 // if the hour number has wrapped.
1948 // There's still a bug here. You might just happen to run a new test
1949 // exactly 32768 hours after the previous failure, and have run exactly
1950 // 20 tests between the two, in which case smartd will miss the
1951 // new failure.
1952 PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
1953 name, newh);
1954 MailWarning(cfg, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
1955 name, newh);
1956 }
1957
1958 // Needed since self-test error count may DECREASE. Hour might
1959 // also have changed.
1960 cfg->selflogcount= newc;
1961 cfg->selfloghour = newh;
1962 }
1963 return;
1964}
1965
1966// returns 1 if time to do test of type testtype, 0 if not time to do
1967// test, < 0 if error
1968int DoTestNow(cfgfile *cfg, char testtype, time_t testtime) {
1969 // start by finding out the time:
1970 struct tm *timenow;
1971 time_t epochnow;
1972 char matchpattern[16];
1973 regmatch_t substring;
1974 int weekday, length;
1975 unsigned short hours;
1976 testinfo *dat=cfg->testdata;
1977
1978 // check that self-testing has been requested
1979 if (!dat)
1980 return 0;
1981
1982 // since we are about to call localtime(), be sure glibc is informed
1983 // of any timezone changes we make.
1984 if (!testtime)
1985 FixGlibcTimeZoneBug();
1986
1987 // construct pattern containing the month, day of month, day of
1988 // week, and hour
1989 epochnow = (!testtime ? time(NULL) : testtime);
1990 timenow=localtime(&epochnow);
1991
1992 // tm_wday is 0 (Sunday) to 6 (Saturday). We use 1 (Monday) to 7
1993 // (Sunday).
1994 weekday=timenow->tm_wday?timenow->tm_wday:7;
1995 sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1,
1996 timenow->tm_mday, weekday, timenow->tm_hour);
1997
1998 // if no match, we are done
1999 if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0))
2000 return 0;
2001
2002 // must match the ENTIRE type/date/time string
2003 length=strlen(matchpattern);
2004 if (substring.rm_so!=0 || substring.rm_eo!=length)
2005 return 0;
2006
2007 // never do a second test in the same hour as another test (the % 7 ensures
2008 // that the RHS will never be greater than 65535 and so will always fit into
2009 // an unsigned short)
2010 hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7));
2011 if (hours==dat->hour) {
2012 if (!testtime && testtype!=dat->testtype)
2013 PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n",
2014 cfg->name, dat->testtype, testtype);
2015 return 0;
2016 }
2017
2018 // save time and type of the current test; we are ready to do a test
2019 dat->hour=hours;
2020 dat->testtype=testtype;
2021 return 1;
2022}
2023
2024// Print a list of future tests.
2025void PrintTestSchedule(cfgfile **atadevices, cfgfile **scsidevices){
2026 int i, t;
2027 cfgfile * cfg;
2028 char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
2029 time_t now; long seconds;
2030 int numdev = numdevata+numdevscsi;
2031 typedef int cnt_t[4];
2032 cnt_t * testcnts; // testcnts[numdev][4]
2033 if (numdev <= 0)
2034 return;
4d59bff9 2035 testcnts = (cnt_t *)calloc(numdev, sizeof(testcnts[0]));
832b75ed
GG
2036 if (!testcnts)
2037 return;
2038
2039 PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
2040
2041 // FixGlibcTimeZoneBug(); // done in PrintOut()
2042 now=time(NULL);
2043 dateandtimezoneepoch(datenow, now);
2044 for (seconds=0; seconds<3600L*24*90; seconds+=checktime) {
2045 // Check for each device whether a test will be run
2046 time_t testtime = now + seconds;
2047 for (i=0; i<numdev; i++) {
2048 cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
2049 for (t=0; t<(i<numdevata?4:2); t++) {
2050 char testtype = "LSCO"[t];
2051 if (DoTestNow(cfg, testtype, testtime)) {
2052 // Report at most 5 tests of each type
2053 if (++testcnts[i][t] <= 5) {
2054 dateandtimezoneepoch(date, testtime);
2055 PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg->name,
2056 testcnts[i][t], testtype, date);
2057 }
2058 }
2059 }
2060 }
2061 }
2062
2063 // Report totals
2064 dateandtimezoneepoch(date, now+seconds);
2065 PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
2066 for (i=0; i<numdev; i++) {
2067 cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
2068 for (t=0; t<(i<numdevata?4:2); t++) {
2069 PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg->name, testcnts[i][t],
2070 (testcnts[i][t]==1?"":"s"), "LSCO"[t]);
2071 }
2072 }
2073
2074 free(testcnts);
2075}
2076
2077// Return zero on success, nonzero on failure. Perform offline (background)
2078// short or long (extended) self test on given scsi device.
2079int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
2080 int retval = 0;
2081 char *testname = NULL;
2082 char *name = cfg->name;
2083 int inProgress;
2084
2085 if (scsiSelfTestInProgress(fd, &inProgress)) {
2086 PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
2087 cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
2088 return 1;
2089 }
2090
2091 if (1 == inProgress) {
2092 PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
2093 "progress.\n", name);
2094 return 1;
2095 }
2096
2097 switch (testtype) {
2098 case 'S':
2099 testname = "Short Self";
2100 retval = scsiSmartShortSelfTest(fd);
2101 break;
2102 case 'L':
2103 testname = "Long Self";
2104 retval = scsiSmartExtendSelfTest(fd);
2105 break;
2106 }
2107 // If we can't do the test, exit
2108 if (NULL == testname) {
2109 PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name,
2110 testtype);
2111 return 1;
2112 }
2113 if (retval) {
2114 if ((SIMPLE_ERR_BAD_OPCODE == retval) ||
2115 (SIMPLE_ERR_BAD_FIELD == retval)) {
2116 PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name,
2117 testname);
2118 if ('L'==testtype)
2119 cfg->testdata->not_cap_long=1;
2120 else
2121 cfg->testdata->not_cap_short=1;
2122
2123 return 1;
2124 }
2125 PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name,
2126 testname, retval);
2127 return 1;
2128 }
2129
2130 PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
2131
2132 return 0;
2133}
2134
2135// Do an offline immediate or self-test. Return zero on success,
2136// nonzero on failure.
2137int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
2138
2139 struct ata_smart_values data;
2140 char *testname=NULL;
2141 int retval, dotest=-1;
2142 char *name=cfg->name;
2143
2144 // Read current smart data and check status/capability
2145 if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
2146 PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
2147 return 1;
2148 }
2149
2150 // Check for capability to do the test
2151 switch (testtype) {
2152 case 'O':
2153 testname="Offline Immediate ";
2154 if (isSupportExecuteOfflineImmediate(&data))
2155 dotest=OFFLINE_FULL_SCAN;
2156 else
2157 cfg->testdata->not_cap_offline=1;
2158 break;
2159 case 'C':
2160 testname="Conveyance Self-";
2161 if (isSupportConveyanceSelfTest(&data))
2162 dotest=CONVEYANCE_SELF_TEST;
2163 else
2164 cfg->testdata->not_cap_conveyance=1;
2165 break;
2166 case 'S':
2167 testname="Short Self-";
2168 if (isSupportSelfTest(&data))
2169 dotest=SHORT_SELF_TEST;
2170 else
2171 cfg->testdata->not_cap_short=1;
2172 break;
2173 case 'L':
2174 testname="Long Self-";
2175 if (isSupportSelfTest(&data))
2176 dotest=EXTEND_SELF_TEST;
2177 else
2178 cfg->testdata->not_cap_long=1;
2179 break;
2180 }
2181
2182 // If we can't do the test, exit
2183 if (dotest<0) {
2184 PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
2185 return 1;
2186 }
2187
2188 // If currently running a self-test, do not interrupt it to start another.
2189 if (15==(data.self_test_exec_status >> 4)) {
2190 PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
2191 name, testname, (int)(data.self_test_exec_status & 0x0f));
2192 return 1;
2193 }
2194
2195 // else execute the test, and return status
2196 if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
2197 PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
2198 else
2199 PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
2200
2201 return retval;
2202}
2203
4d59bff9
GG
2204// Check Temperature limits
2205static void CheckTemperature(cfgfile * cfg, unsigned char currtemp, unsigned char triptemp)
2206{
2207 const char *minchg = "", *maxchg = "";
2208 if (!(0 < currtemp && currtemp < 255)) {
2209 PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg->name);
2210 return;
2211 }
2212
2213 if (!cfg->temperature) {
2214 PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius\n",
2215 cfg->name, (int)currtemp);
2216 if (triptemp)
2217 PrintOut(LOG_INFO, " [trip Temperature is %d Celsius]\n", (int)triptemp);
2218 cfg->temperature = cfg->tempmin = cfg->tempmax = currtemp;
2219 }
2220 else {
2221 // Update [min,max]
2222 if (currtemp < cfg->tempmin) {
2223 cfg->tempmin = currtemp; minchg = "!";
2224 cfg->tempmininc = 0;
2225 }
2226 else if (cfg->tempmininc) {
2227 // increase min Temperature during first 30 minutes
2228 cfg->tempmin = currtemp;
2229 cfg->tempmininc--;
2230 }
2231 if (currtemp > cfg->tempmax) {
2232 cfg->tempmax = currtemp; maxchg = "!";
2233 }
2234
2235 // Track changes
2236 if (cfg->tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)cfg->temperature) >= cfg->tempdiff)) {
2237 PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %u%s/%u%s)\n",
2238 cfg->name, (int)currtemp-(int)cfg->temperature, currtemp, cfg->tempmin, minchg, cfg->tempmax, maxchg);
2239 cfg->temperature = currtemp;
2240 }
2241 }
2242
2243 // Check limits
2244 if (cfg->tempcrit && currtemp >= cfg->tempcrit) {
2245 PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %u%s/%u%s)\n",
2246 cfg->name, currtemp, cfg->tempcrit, cfg->tempmin, minchg, cfg->tempmax, maxchg);
2247 MailWarning(cfg, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %u%s/%u%s)\n",
2248 cfg->name, currtemp, cfg->tempcrit, cfg->tempmin, minchg, cfg->tempmax, maxchg);
2249 }
2250 else if (cfg->tempinfo && currtemp >= cfg->tempinfo) {
2251 PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %u%s/%u%s)\n",
2252 cfg->name, currtemp, cfg->tempinfo, cfg->tempmin, minchg, cfg->tempmax, maxchg);
2253 }
2254}
832b75ed
GG
2255
2256int ATACheckDevice(cfgfile *cfg){
2257 int fd,i;
2258 char *name=cfg->name;
2259 char *mode="ATA";
4d59bff9 2260 char testtype=0;
832b75ed
GG
2261
2262 // fix firmware bug if requested
2263 con->fixfirmwarebug=cfg->fixfirmwarebug;
2264 con->controller_port=cfg->controller_port;
2265 con->controller_type=cfg->controller_type;
4d59bff9 2266 con->controller_explicit=cfg->controller_explicit;
832b75ed
GG
2267
2268 // If user has asked, test the email warning system
2269 if (cfg->mailwarn && cfg->mailwarn->emailtest)
2270 MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
2271
2272 if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR)
2273 mode="ATA_3WARE_9000";
2274
2275 if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR)
2276 mode="ATA_3WARE_678K";
2277
2278 // if we can't open device, fail gracefully rather than hard --
2279 // perhaps the next time around we'll be able to open it. ATAPI
2280 // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
2281 // given (see linux cdrom driver).
2282 if ((fd=OpenDevice(name, mode, 0))<0){
2283 MailWarning(cfg, 9, "Device: %s, unable to open device", name);
2284 return 1;
2285 }
2286
4d59bff9
GG
2287 // if the user has asked, and device is capable (or we're not yet
2288 // sure) check whether a self test should be done now.
2289 // This check is done before powermode check to avoid missing self
2290 // tests on idle or sleeping disks.
2291 if (cfg->testdata) {
2292 // long test
2293 if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
2294 testtype = 'L';
2295 // short test
2296 else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
2297 testtype = 'S';
2298 // conveyance test
2299 else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C', 0)>0)
2300 testtype = 'C';
2301 // offline immediate
2302 else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O', 0)>0)
2303 testtype = 'O';
2304 }
2305
832b75ed
GG
2306 // user may have requested (with the -n Directive) to leave the disk
2307 // alone if it is in idle or sleeping mode. In this case check the
2308 // power mode and exit without check if needed
2309 if (cfg->powermode){
2310 int dontcheck=0, powermode=ataCheckPowerMode(fd);
2311 char *mode=NULL;
4d59bff9
GG
2312 if (0 <= powermode && powermode < 0xff) {
2313 // wait for possible spin up and check again
2314 int powermode2;
2315 sleep(5);
2316 powermode2 = ataCheckPowerMode(fd);
832b75ed
GG
2317 if (powermode2 > powermode)
2318 PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
2319 powermode = powermode2;
2320 }
2321
2322 switch (powermode){
2323 case -1:
2324 // SLEEP
2325 mode="SLEEP";
2326 if (cfg->powermode>=1)
4d59bff9 2327 dontcheck=1;
832b75ed
GG
2328 break;
2329 case 0:
2330 // STANDBY
2331 mode="STANDBY";
2332 if (cfg->powermode>=2)
4d59bff9 2333 dontcheck=1;
832b75ed
GG
2334 break;
2335 case 0x80:
2336 // IDLE
2337 mode="IDLE";
2338 if (cfg->powermode>=3)
4d59bff9 2339 dontcheck=1;
832b75ed
GG
2340 break;
2341 case 0xff:
2342 // ACTIVE/IDLE
4d59bff9 2343 mode="ACTIVE or IDLE";
832b75ed
GG
2344 break;
2345 default:
2346 // UNKNOWN
2347 PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
4d59bff9 2348 name, powermode);
832b75ed
GG
2349 cfg->powermode=0;
2350 break;
2351 }
2352
2353 // if we are going to skip a check, return now
2354 if (dontcheck){
4d59bff9
GG
2355 // but ignore powermode on scheduled selftest
2356 if (!testtype) {
2357 CloseDevice(fd, name);
2358 if (!cfg->powerskipcnt && !cfg->powerquiet) // report first only and avoid waking up system disk
2359 PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
2360 cfg->powerskipcnt++;
2361 return 0;
2362 }
2363 PrintOut(LOG_INFO, "Device: %s, %s mode ignored due to scheduled self test (%d check%s skipped)\n",
2364 name, mode, cfg->powerskipcnt, (cfg->powerskipcnt==1?"":"s"));
2365 cfg->powerskipcnt = 0;
2366 }
2367 else if (cfg->powerskipcnt) {
2368 PrintOut(LOG_INFO, "Device: %s, is back in %s mode, resuming checks (%d check%s skipped)\n",
2369 name, mode, cfg->powerskipcnt, (cfg->powerskipcnt==1?"":"s"));
2370 cfg->powerskipcnt = 0;
2371 }
832b75ed
GG
2372 }
2373
2374 // check smart status
2375 if (cfg->smartcheck){
2376 int status=ataSmartStatus2(fd);
2377 if (status==-1){
2378 PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
2379 MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name);
2380 }
2381 else if (status==1){
2382 PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
2383 MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
2384 }
2385 }
2386
2387 // Check everything that depends upon SMART Data (eg, Attribute values)
4d59bff9
GG
2388 if ( cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC
2389 || cfg->tempdiff || cfg->tempinfo || cfg->tempcrit ){
832b75ed
GG
2390 struct ata_smart_values curval;
2391 struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
2392
2393 // Read current attribute values. *drive contains old values and thresholds
2394 if (ataReadSmartValues(fd,&curval)){
2395 PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
2396 MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name);
2397 }
2398 else {
2399 // look for current or offline pending sectors
2400 if (cfg->pending != DONT_MONITOR_UNC) {
2401 int64_t rawval;
2402 unsigned char currentpending, offlinepending;
2403
2404 TranslatePending(cfg->pending, &currentpending, &offlinepending);
2405
2406 if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) {
2407 // Unreadable pending sectors!!
2408 PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval);
2409 MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval);
2410 }
2411
2412 if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) {
2413 // Unreadable offline sectors!!
2414 PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval);
2415 MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval);
2416 }
2417 }
2418
4d59bff9
GG
2419 // check temperature limits
2420 if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
2421 CheckTemperature(cfg, ATAReturnTemperatureValue(&curval, cfg->attributedefs), 0);
2422
832b75ed
GG
2423 if (cfg->usagefailed || cfg->prefail || cfg->usage) {
2424
2425 // look for failed usage attributes, or track usage or prefail attributes
2426 for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
2427 int att;
2428 changedattribute_t delta;
2429
2430 // This block looks for usage attributes that have failed.
2431 // Prefail attributes that have failed are returned with a
2432 // positive sign. No failure returns 0. Usage attributes<0.
2433 if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
2434
2435 // are we ignoring failures of this attribute?
2436 att *= -1;
2437 if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){
2438 char attname[64], *loc=attname;
2439
2440 // get attribute name & skip white space
2441 ataPrintSmartAttribName(loc, att, cfg->attributedefs);
2442 while (*loc && *loc==' ') loc++;
2443
2444 // warning message
2445 PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc);
2446 MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc);
2447 }
2448 }
2449
2450 // This block tracks usage or prefailure attributes to see if
2451 // they are changing. It also looks for changes in RAW values
2452 // if this has been requested by user.
2453 if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){
2454 unsigned char id=delta.id;
2455
2456 // if the only change is the raw value, and we're not
2457 // tracking raw value, then continue loop over attributes
2458 if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__))
2459 continue;
2460
2461 // are we tracking this attribute?
2462 if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){
2463 char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
2464
2465 // get attribute name, skip spaces
2466 ataPrintSmartAttribName(loc, id, cfg->attributedefs);
2467 while (*loc && *loc==' ') loc++;
2468
2469 // has the user asked for us to print raw values?
2470 if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) {
2471 // get raw values (as a string) and add to printout
2472 char rawstring[64];
2473 ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs);
2474 sprintf(newrawstring, " [Raw %s]", rawstring);
2475 ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs);
2476 sprintf(oldrawstring, " [Raw %s]", rawstring);
2477 }
2478 else
2479 newrawstring[0]=oldrawstring[0]='\0';
2480
2481 // prefailure attribute
2482 if (cfg->prefail && delta.prefail)
2483 PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
2484 name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
2485
2486 // usage attribute
2487 if (cfg->usage && !delta.prefail)
2488 PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
2489 name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
2490 }
2491 } // endof block tracking usage or prefailure
2492 } // end of loop over attributes
2493
2494 // Save the new values into *drive for the next time around
2495 *(cfg->smartval)=curval;
2496 }
2497 }
2498 }
2499
2500 // check if number of selftest errors has increased (note: may also DECREASE)
2501 if (cfg->selftest)
2502 CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
2503
2504 // check if number of ATA errors has increased
2505 if (cfg->errorlog){
2506
4d59bff9 2507 int newc,oldc=cfg->ataerrorcount;
832b75ed
GG
2508
2509 // new number of errors
4d59bff9 2510 newc=ATAErrorCount(fd, name);
832b75ed
GG
2511
2512 // did command fail?
4d59bff9 2513 if (newc<0)
832b75ed
GG
2514 // lack of PrintOut here is INTENTIONAL
2515 MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name);
2516
2517 // has error count increased?
4d59bff9 2518 if (newc>oldc){
832b75ed 2519 PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
4d59bff9 2520 name, oldc, newc);
832b75ed 2521 MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d",
4d59bff9 2522 name, oldc, newc);
832b75ed
GG
2523 }
2524
2525 // this last line is probably not needed, count always increases
4d59bff9
GG
2526 if (newc>=0)
2527 cfg->ataerrorcount=newc;
832b75ed
GG
2528 }
2529
4d59bff9
GG
2530 // carry out scheduled self-test
2531 if (testtype)
2532 DoATASelfTest(fd, cfg, testtype);
832b75ed
GG
2533
2534 // Don't leave device open -- the OS/user may want to access it
2535 // before the next smartd cycle!
2536 CloseDevice(fd, name);
2537 return 0;
2538}
2539
832b75ed
GG
2540int SCSICheckDevice(cfgfile *cfg)
2541{
2542 UINT8 asc, ascq;
2543 UINT8 currenttemp;
2544 UINT8 triptemp;
2545 int fd;
2546 char *name=cfg->name;
2547 const char *cp;
ba59cff1
GG
2548 char *mode=NULL;
2549
2550 // should we try to register this as a SCSI device?
2551 switch (cfg->controller_type) {
2552 case CONTROLLER_CCISS:
2553 mode="CCISS";
2554 break;
2555 case CONTROLLER_SCSI:
2556 case CONTROLLER_UNKNOWN:
2557 mode="SCSI";
2558 break;
2559 default:
2560 return 1;
2561 }
2562
2563 // pass user settings on to low-level SCSI commands
2564 con->controller_port=cfg->controller_port;
2565 con->controller_type=cfg->controller_type;
832b75ed
GG
2566
2567 // If the user has asked for it, test the email warning system
2568 if (cfg->mailwarn && cfg->mailwarn->emailtest)
2569 MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
2570
2571 // if we can't open device, fail gracefully rather than hard --
2572 // perhaps the next time around we'll be able to open it
ba59cff1 2573 if ((fd=OpenDevice(name, mode, 0))<0) {
832b75ed
GG
2574 // Lack of PrintOut() here is intentional!
2575 MailWarning(cfg, 9, "Device: %s, unable to open device", name);
2576 return 1;
ba59cff1
GG
2577 } else if (debugmode)
2578 PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name);
832b75ed
GG
2579 currenttemp = 0;
2580 asc = 0;
2581 ascq = 0;
2582 if (! cfg->SuppressReport) {
2583 if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
2584 &asc, &ascq, &currenttemp, &triptemp)) {
2585 PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
2586 name);
2587 MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name);
2588 cfg->SuppressReport = 1;
2589 }
2590 }
2591 if (asc > 0) {
2592 cp = scsiGetIEString(asc, ascq);
2593 if (cp) {
2594 PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
2595 MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp);
ba59cff1
GG
2596 } else if (debugmode)
2597 PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n",
2598 name, (int)asc, (int)ascq);
832b75ed 2599 } else if (debugmode)
ba59cff1 2600 PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name);
4d59bff9
GG
2601
2602 // check temperature limits
2603 if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
2604 CheckTemperature(cfg, currenttemp, triptemp);
2605
832b75ed
GG
2606 // check if number of selftest errors has increased (note: may also DECREASE)
2607 if (cfg->selftest)
2608 CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
2609
2610 if (cfg->testdata) {
2611 // long (extended) background test
2612 if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
2613 DoSCSISelfTest(fd, cfg, 'L');
2614 // short background test
2615 else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
2616 DoSCSISelfTest(fd, cfg, 'S');
2617 }
2618 CloseDevice(fd, name);
2619 return 0;
2620}
2621
2622// Checks the SMART status of all ATA and SCSI devices
2623void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){
2624 int i;
2625
2626 for (i=0; i<numdevata; i++)
2627 ATACheckDevice(atadevices[i]);
2628
2629 for (i=0; i<numdevscsi; i++)
2630 SCSICheckDevice(scsidevices[i]);
2631
2632 return;
2633}
2634
2635#if SCSITIMEOUT
2636// This alarm means that a SCSI USB device was hanging
2637void AlarmHandler(int signal) {
2638 longjmp(registerscsienv, 1);
2639}
2640#endif
2641
2642// Does initialization right after fork to daemon mode
2643void Initialize(time_t *wakeuptime){
2644
2645 // install goobye message and remove pidfile handler
2646 atexit(Goodbye);
2647
2648 // write PID file only after installing exit handler
2649 if (!debugmode)
2650 WritePidFile();
2651
2652 // install signal handlers. On Solaris, can't use signal() because
2653 // it resets the handler to SIG_DFL after each call. So use sigset()
2654 // instead. So SIGNALFN()==signal() or SIGNALFN()==sigset().
2655
2656 // normal and abnormal exit
2657 if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
2658 SIGNALFN(SIGTERM, SIG_IGN);
2659 if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
2660 SIGNALFN(SIGQUIT, SIG_IGN);
2661
2662 // in debug mode, <CONTROL-C> ==> HUP
2663 if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
2664 SIGNALFN(SIGINT, SIG_IGN);
2665
2666 // Catch HUP and USR1
2667 if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
2668 SIGNALFN(SIGHUP, SIG_IGN);
2669 if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
2670 SIGNALFN(SIGUSR1, SIG_IGN);
2671#ifdef _WIN32
2672 if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
2673 SIGNALFN(SIGUSR2, SIG_IGN);
2674#endif
2675
2676 // initialize wakeup time to CURRENT time
2677 *wakeuptime=time(NULL);
2678
2679 return;
2680}
2681
2682#ifdef _WIN32
2683// Toggle debug mode implemented for native windows only
2684// (there is no easy way to reopen tty on *nix)
2685static void ToggleDebugMode()
2686{
2687 if (!debugmode) {
2688 PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
2689 if (!daemon_enable_console("smartd [Debug]")) {
2690 debugmode = 1;
2691 daemon_signal(SIGINT, HUPhandler);
2692 PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
2693 }
2694 else
2695 PrintOut(LOG_INFO,"enable console failed\n");
2696 }
2697 else if (debugmode == 1) {
2698 daemon_disable_console();
2699 debugmode = 0;
2700 daemon_signal(SIGINT, sighandler);
2701 PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
2702 }
2703 else
2704 PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
2705}
2706#endif
2707
2708time_t dosleep(time_t wakeuptime){
2709 time_t timenow=0;
2710
2711 // If past wake-up-time, compute next wake-up-time
2712 timenow=time(NULL);
2713 while (wakeuptime<=timenow){
2714 int intervals=1+(timenow-wakeuptime)/checktime;
2715 wakeuptime+=intervals*checktime;
2716 }
2717
2718 // sleep until we catch SIGUSR1 or have completed sleeping
2719 while (timenow<wakeuptime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT){
2720
2721 // protect user again system clock being adjusted backwards
2722 if (wakeuptime>timenow+checktime){
2723 PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
2724 wakeuptime=timenow+checktime;
2725 }
2726
2727 // Exit sleep when time interval has expired or a signal is received
2728 sleep(wakeuptime-timenow);
2729
2730#ifdef _WIN32
2731 // toggle debug mode?
2732 if (caughtsigUSR2) {
2733 ToggleDebugMode();
2734 caughtsigUSR2 = 0;
2735 }
2736#endif
2737
2738 timenow=time(NULL);
2739 }
2740
2741 // if we caught a SIGUSR1 then print message and clear signal
2742 if (caughtsigUSR1){
2743 PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
2744 wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
2745 caughtsigUSR1=0;
2746 }
2747
2748 // return adjusted wakeuptime
2749 return wakeuptime;
2750}
2751
2752// Print out a list of valid arguments for the Directive d
2753void printoutvaliddirectiveargs(int priority, char d) {
2754 char *s=NULL;
2755
2756 switch (d) {
2757 case 'n':
2758 PrintOut(priority, "never[,q], sleep[,q], standby[,q], idle[,q]");
2759 break;
2760 case 's':
2761 PrintOut(priority, "valid_regular_expression");
2762 break;
2763 case 'd':
4d59bff9 2764 PrintOut(priority, "ata, scsi, marvell, removable, sat, 3ware,N, hpt,L/M/N");
832b75ed
GG
2765 break;
2766 case 'T':
2767 PrintOut(priority, "normal, permissive");
2768 break;
2769 case 'o':
2770 case 'S':
2771 PrintOut(priority, "on, off");
2772 break;
2773 case 'l':
2774 PrintOut(priority, "error, selftest");
2775 break;
2776 case 'M':
2777 PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
2778 break;
2779 case 'v':
2780 if (!(s = create_vendor_attribute_arg_list())) {
2781 PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n");
2782 EXIT(EXIT_NOMEM);
2783 }
2784 PrintOut(priority, "\n%s\n", s);
2785 s=CheckFree(s, __LINE__,filenameandversion);
2786 break;
2787 case 'P':
2788 PrintOut(priority, "use, ignore, show, showall");
2789 break;
2790 case 'F':
2791 PrintOut(priority, "none, samsung, samsung2");
2792 break;
2793 }
2794}
2795
2796// exits with an error message, or returns integer value of token
2797int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){
2798 char *endptr;
2799 int val;
2800
2801 // check input range
2802 if (min<0){
2803 PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min);
2804 return -1;
2805 }
2806
2807 // make sure argument is there
2808 if (!arg) {
2809 PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
2810 configfile, lineno, name, token, min, max);
2811 return -1;
2812 }
2813
2814 // get argument value (base 10), check that it's integer, and in-range
2815 val=strtol(arg,&endptr,10);
2816 if (*endptr!='\0' || val<min || val>max ) {
2817 PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
2818 configfile, lineno, name, token, arg, min, max);
2819 return -1;
2820 }
2821
2822 // all is well; return value
2823 return val;
2824}
2825
4d59bff9
GG
2826
2827// Get 1-3 small integer(s) for '-W' directive
2828int Get3Integers(const char *arg, const char *name, const char *token, int lineno, const char *configfile,
2829 unsigned char * val1, unsigned char * val2, unsigned char * val3){
2830 unsigned v1 = 0, v2 = 0, v3 = 0;
2831 int n1 = -1, n2 = -1, n3 = -1, len;
2832 if (!arg) {
2833 PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes 1-3 integer argument(s) from 0 to 255.\n",
2834 configfile, lineno, name, token);
2835 return -1;
2836 }
2837
2838 len = strlen(arg);
2839 if (!( sscanf(arg, "%u%n,%u%n,%u%n", &v1, &n1, &v2, &n2, &v3, &n3) >= 1
2840 && (n1 == len || n2 == len || n3 == len) && v1 <= 255 && v2 <= 255 && v3 <= 255)) {
2841 PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs 1-3 integer(s) from 0 to 255.\n",
2842 configfile, lineno, name, token, arg);
2843 return -1;
2844 }
2845 *val1 = (unsigned char)v1; *val2 = (unsigned char)v2; *val3 = (unsigned char)v3;
2846 return 0;
2847}
2848
2849
832b75ed
GG
2850// This function returns 1 if it has correctly parsed one token (and
2851// any arguments), else zero if no tokens remain. It returns -1 if an
2852// error was encountered.
2853int ParseToken(char *token,cfgfile *cfg){
2854 char sym;
2855 char *name=cfg->name;
2856 int lineno=cfg->lineno;
2857 char *delim = " \n\t";
2858 int badarg = 0;
2859 int missingarg = 0;
2860 char *arg = NULL;
2861 int makemail=0;
2862 maildata *mdat=NULL, tempmail;
2863
2864 // is the rest of the line a comment
2865 if (*token=='#')
2866 return 1;
2867
2868 // is the token not recognized?
2869 if (*token!='-' || strlen(token)!=2) {
2870 PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
2871 configfile, lineno, name, token);
2872 PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
2873 return -1;
2874 }
2875
2876 // token we will be parsing:
2877 sym=token[1];
2878
2879 // create temporary maildata structure. This means we can postpone
2880 // allocating space in the data segment until we are sure there are
2881 // no errors.
2882 if ('m'==sym || 'M'==sym){
2883 if (!cfg->mailwarn){
2884 memset(&tempmail, 0, sizeof(maildata));
2885 mdat=&tempmail;
2886 makemail=1;
2887 }
2888 else
2889 mdat=cfg->mailwarn;
2890 }
2891
2892 // parse the token and swallow its argument
2893 switch (sym) {
2894 int val;
2895
2896 case 'C':
2897 // monitor current pending sector count (default 197)
2898 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
2899 return -1;
2900 if (val==CUR_UNC_DEFAULT)
2901 val=0;
2902 else if (val==0)
2903 val=CUR_UNC_DEFAULT;
2904 // set bottom 8 bits to correct value
2905 cfg->pending &= 0xff00;
2906 cfg->pending |= val;
2907 break;
2908 case 'U':
2909 // monitor offline uncorrectable sectors (default 198)
2910 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
2911 return -1;
2912 if (val==OFF_UNC_DEFAULT)
2913 val=0;
2914 else if (val==0)
2915 val=OFF_UNC_DEFAULT;
2916 // turn off top 8 bits, then set to correct value
2917 cfg->pending &= 0xff;
2918 cfg->pending |= (val<<8);
2919 break;
2920 case 'T':
2921 // Set tolerance level for SMART command failures
2922 if ((arg = strtok(NULL, delim)) == NULL) {
2923 missingarg = 1;
2924 } else if (!strcmp(arg, "normal")) {
2925 // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
2926 // not on failure of an optional S.M.A.R.T. command.
2927 // This is the default so we don't need to actually do anything here.
2928 cfg->permissive=0;
2929 } else if (!strcmp(arg, "permissive")) {
2930 // Permissive mode; ignore errors from Mandatory SMART commands
2931 cfg->permissive=1;
2932 } else {
2933 badarg = 1;
2934 }
2935 break;
2936 case 'd':
2937 // specify the device type
4d59bff9 2938 cfg->controller_explicit = 1;
832b75ed
GG
2939 if ((arg = strtok(NULL, delim)) == NULL) {
2940 missingarg = 1;
2941 } else if (!strcmp(arg, "ata")) {
2942 cfg->controller_port = 0;
2943 cfg->controller_type = CONTROLLER_ATA;
2944 } else if (!strcmp(arg, "scsi")) {
2945 cfg->controller_port =0;
2946 cfg->controller_type = CONTROLLER_SCSI;
2947 } else if (!strcmp(arg, "marvell")) {
2948 cfg->controller_port =0;
2949 cfg->controller_type = CONTROLLER_MARVELL_SATA;
4d59bff9
GG
2950 } else if (!strncmp(arg, "sat", 3)) {
2951 cfg->controller_type = CONTROLLER_SAT;
2952 cfg->controller_port = 0;
2953 cfg->satpassthrulen = 0;
2954 if (strlen(arg) > 3) {
2955 int k;
2956 char * cp;
2957
2958 cp = strchr(arg, ',');
2959 if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
2960 ((0 == k) || (12 == k) || (16 == k)))
2961 cfg->satpassthrulen = k;
2962 else {
2963 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
2964 "'-d sat,<n>' requires <n> to be 0, 12 or 16\n",
2965 configfile, lineno, name);
2966 badarg = 1;
2967 }
2968 }
2969 } else if (!strncmp(arg, "hpt", 3)){
2970 unsigned char i, slash = 0;
2971 cfg->hpt_data[0] = 0;
2972 cfg->hpt_data[1] = 0;
2973 cfg->hpt_data[2] = 0;
2974 cfg->controller_type = CONTROLLER_HPT;
2975 for (i=4; i < strlen(arg); i++) {
2976 if(arg[i] == '/') {
2977 slash++;
2978 if(slash == 3) {
2979 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
2980 "'-d hpt,L/M/N' supports 2-3 items\n",
2981 configfile, lineno, name);
2982 badarg = TRUE;
2983 break;
2984 }
2985 }
2986 else if ((arg[i])>='0' && (arg[i])<='9') {
2987 if (cfg->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
2988 badarg = TRUE;
2989 break;
2990 }
2991 cfg->hpt_data[slash] = cfg->hpt_data[slash]*10 + arg[i] - '0';
2992 }
2993 else {
2994 badarg = TRUE;
2995 break;
2996 }
2997 }
2998 if ( slash == 0 ) {
2999 badarg = TRUE;
3000 } else if (badarg != TRUE) {
3001 if (cfg->hpt_data[0]==0 || cfg->hpt_data[0]>8){
3002 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
3003 "'-d hpt,L/M/N' no/invalid controller id L supplied\n",
3004 configfile, lineno, name);
3005 badarg = TRUE;
3006 }
3007 if (cfg->hpt_data[1]==0 || cfg->hpt_data[1]>8){
3008 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
3009 "'-d hpt,L/M/N' no/invalid channel number M supplied\n",
3010 configfile, lineno, name);
3011 badarg = TRUE;
3012 }
3013 if (slash==2){
3014 if (cfg->hpt_data[2]==0 || cfg->hpt_data[2]>15){
3015 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
3016 "'-d hpt,L/M/N' no/invalid pmport number N supplied\n",
3017 configfile, lineno, name);
3018 badarg = TRUE;
3019 }
3020 } else { /* no pmport device */
3021 cfg->hpt_data[2]=1;
3022 }
3023 }
832b75ed
GG
3024 } else if (!strcmp(arg, "removable")) {
3025 cfg->removable = 1;
3026 } else {
3027 // look 3ware,N RAID device
3028 int i;
3029 char *s;
3030
3031 // make a copy of the string to mess with
3032 if (!(s = strdup(arg))) {
3033 PrintOut(LOG_CRIT,
3034 "No memory to copy argument to -d option - exiting\n");
3035 EXIT(EXIT_NOMEM);
ba59cff1
GG
3036 } else if (!strncmp(s,"3ware,",6)) {
3037 if (split_report_arg2(s, &i)){
3038 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n",
3039 configfile, lineno, name);
3040 badarg=1;
3041 } else if ( i<0 || i>15) {
3042 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n",
3043 configfile, lineno, name, i);
3044 badarg=1;
3045 } else {
3046 // determine type of escalade device from name of device
3047 cfg->controller_type = guess_device_type(name);
3048 if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR)
3049 cfg->controller_type=CONTROLLER_3WARE_678K;
832b75ed 3050
ba59cff1
GG
3051 // NOTE: controller_port == disk number + 1
3052 cfg->controller_port = i+1;
3053 }
3054 } else if (!strncmp(s,"cciss,",6)) {
3055 if (split_report_arg2(s, &i)){
3056 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N requires N integer\n",
3057 configfile, lineno, name);
3058 badarg=1;
3059 } else if ( i<0 || i>15) {
3060 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N (N=%d) must have 0 <= N <= 15\n",
3061 configfile, lineno, name, i);
3062 badarg=1;
3063 } else {
3064 // NOTE: controller_port == disk number + 1
3065 cfg->controller_type = CONTROLLER_CCISS;
3066 cfg->controller_port = i+1;
3067 }
3068 } else {
3069 badarg=1;
832b75ed 3070 }
ba59cff1 3071 s=CheckFree(s, __LINE__,filenameandversion);
832b75ed
GG
3072 }
3073 break;
3074 case 'F':
3075 // fix firmware bug
3076 if ((arg = strtok(NULL, delim)) == NULL) {
3077 missingarg = 1;
3078 } else if (!strcmp(arg, "none")) {
3079 cfg->fixfirmwarebug = FIX_NONE;
3080 } else if (!strcmp(arg, "samsung")) {
3081 cfg->fixfirmwarebug = FIX_SAMSUNG;
3082 } else if (!strcmp(arg, "samsung2")) {
3083 cfg->fixfirmwarebug = FIX_SAMSUNG2;
3084 } else {
3085 badarg = 1;
3086 }
3087 break;
3088 case 'H':
3089 // check SMART status
3090 cfg->smartcheck=1;
3091 break;
3092 case 'f':
3093 // check for failure of usage attributes
3094 cfg->usagefailed=1;
3095 break;
3096 case 't':
3097 // track changes in all vendor attributes
3098 cfg->prefail=1;
3099 cfg->usage=1;
3100 break;
3101 case 'p':
3102 // track changes in prefail vendor attributes
3103 cfg->prefail=1;
3104 break;
3105 case 'u':
3106 // track changes in usage vendor attributes
3107 cfg->usage=1;
3108 break;
3109 case 'l':
3110 // track changes in SMART logs
3111 if ((arg = strtok(NULL, delim)) == NULL) {
3112 missingarg = 1;
3113 } else if (!strcmp(arg, "selftest")) {
3114 // track changes in self-test log
3115 cfg->selftest=1;
3116 } else if (!strcmp(arg, "error")) {
3117 // track changes in ATA error log
3118 cfg->errorlog=1;
3119 } else {
3120 badarg = 1;
3121 }
3122 break;
3123 case 'a':
3124 // monitor everything
3125 cfg->smartcheck=1;
3126 cfg->prefail=1;
3127 cfg->usagefailed=1;
3128 cfg->usage=1;
3129 cfg->selftest=1;
3130 cfg->errorlog=1;
3131 break;
3132 case 'o':
3133 // automatic offline testing enable/disable
3134 if ((arg = strtok(NULL, delim)) == NULL) {
3135 missingarg = 1;
3136 } else if (!strcmp(arg, "on")) {
3137 cfg->autoofflinetest = 2;
3138 } else if (!strcmp(arg, "off")) {
3139 cfg->autoofflinetest = 1;
3140 } else {
3141 badarg = 1;
3142 }
3143 break;
3144 case 'n':
3145 // skip disk check if in idle or standby mode
3146 if (!(arg = strtok(NULL, delim)))
3147 missingarg = 1;
3148 else if (!strcmp(arg, "never") || !strcmp(arg, "never,q"))
3149 cfg->powermode = 0;
3150 else if (!strcmp(arg, "sleep") || !strcmp(arg, "sleep,q"))
3151 cfg->powermode = 1;
3152 else if (!strcmp(arg, "standby") || !strcmp(arg, "standby,q"))
3153 cfg->powermode = 2;
3154 else if (!strcmp(arg, "idle") || !strcmp(arg, "idle,q"))
3155 cfg->powermode = 3;
3156 else
3157 badarg = 1;
3158 cfg->powerquiet = !!strchr(arg, ',');
3159 break;
3160 case 'S':
3161 // automatic attribute autosave enable/disable
3162 if ((arg = strtok(NULL, delim)) == NULL) {
3163 missingarg = 1;
3164 } else if (!strcmp(arg, "on")) {
3165 cfg->autosave = 2;
3166 } else if (!strcmp(arg, "off")) {
3167 cfg->autosave = 1;
3168 } else {
3169 badarg = 1;
3170 }
3171 break;
3172 case 's':
3173 // warn user, and delete any previously given -s REGEXP Directives
3174 if (cfg->testdata){
3175 PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
3176 configfile, lineno, name, cfg->testdata->regex);
3177 cfg->testdata=FreeTestData(cfg->testdata);
3178 }
3179 // check for missing argument
3180 if (!(arg = strtok(NULL, delim))) {
3181 missingarg = 1;
3182 }
3183 // allocate space for structure and string
3184 else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) {
3185 PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n",
3186 configfile, lineno, name, arg);
3187 EXIT(EXIT_NOMEM);
3188 }
3189 else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) {
3190 char errormsg[512];
3191 // not a valid regular expression!
3192 regerror(val, &(cfg->testdata->cregex), errormsg, 512);
3193 PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
3194 configfile, lineno, name, arg, errormsg);
3195 cfg->testdata=FreeTestData(cfg->testdata);
3196 return -1;
3197 }
3198 // Do a bit of sanity checking and warn user if we think that
3199 // their regexp is "strange". User probably confused about shell
3200 // glob(3) syntax versus regular expression syntax regexp(7).
3201 if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO")))
3202 PrintOut(LOG_INFO, "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
3203 configfile, lineno, name, val+1, arg[val], arg);
3204 break;
3205 case 'm':
3206 // send email to address that follows
3207 if (!(arg = strtok(NULL,delim)))
3208 missingarg = 1;
3209 else {
3210 if (mdat->address) {
3211 PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
3212 configfile, lineno, name, mdat->address);
3213 mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion);
3214 }
3215 mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion);
3216 }
3217 break;
3218 case 'M':
3219 // email warning options
3220 if (!(arg = strtok(NULL, delim)))
3221 missingarg = 1;
3222 else if (!strcmp(arg, "once"))
3223 mdat->emailfreq = 1;
3224 else if (!strcmp(arg, "daily"))
3225 mdat->emailfreq = 2;
3226 else if (!strcmp(arg, "diminishing"))
3227 mdat->emailfreq = 3;
3228 else if (!strcmp(arg, "test"))
3229 mdat->emailtest = 1;
3230 else if (!strcmp(arg, "exec")) {
3231 // Get the next argument (the command line)
3232 if (!(arg = strtok(NULL, delim))) {
3233 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
3234 configfile, lineno, name, token);
3235 return -1;
3236 }
3237 // Free the last cmd line given if any, and copy new one
3238 if (mdat->emailcmdline) {
3239 PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
3240 configfile, lineno, name, mdat->emailcmdline);
3241 mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion);
3242 }
3243 mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion);
3244 }
3245 else
3246 badarg = 1;
3247 break;
3248 case 'i':
3249 // ignore failure of usage attribute
3250 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
3251 return -1;
3252 IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__);
3253 break;
3254 case 'I':
3255 // ignore attribute for tracking purposes
3256 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
3257 return -1;
3258 IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__);
3259 break;
3260 case 'r':
3261 // print raw value when tracking
3262 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
3263 return -1;
3264 IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
3265 break;
3266 case 'R':
3267 // track changes in raw value (forces printing of raw value)
3268 if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
3269 return -1;
3270 IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
3271 IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__);
3272 break;
4d59bff9
GG
3273 case 'W':
3274 // track Temperature
3275 if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
3276 &cfg->tempdiff, &cfg->tempinfo, &cfg->tempcrit))<0)
3277 return -1;
3278 // increase min Temperature during first 30 minutes
3279 if (!(cfg->tempmininc = (unsigned char)(CHECKTIME / checktime)))
3280 cfg->tempmininc = 1;
3281 break;
832b75ed
GG
3282 case 'v':
3283 // non-default vendor-specific attribute meaning
3284 if (!(arg=strtok(NULL,delim))) {
3285 missingarg = 1;
3286 } else if (parse_attribute_def(arg, &cfg->attributedefs)){
3287 badarg = 1;
3288 }
3289 break;
3290 case 'P':
3291 // Define use of drive-specific presets.
3292 if (!(arg = strtok(NULL, delim))) {
3293 missingarg = 1;
3294 } else if (!strcmp(arg, "use")) {
3295 cfg->ignorepresets = FALSE;
3296 } else if (!strcmp(arg, "ignore")) {
3297 cfg->ignorepresets = TRUE;
3298 } else if (!strcmp(arg, "show")) {
3299 cfg->showpresets = TRUE;
3300 } else if (!strcmp(arg, "showall")) {
3301 showallpresets();
3302 } else {
3303 badarg = 1;
3304 }
3305 break;
3306 default:
3307 // Directive not recognized
3308 PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
3309 configfile, lineno, name, token);
3310 Directives();
3311 return -1;
3312 }
3313 if (missingarg) {
3314 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
3315 configfile, lineno, name, token);
3316 }
3317 if (badarg) {
3318 PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
3319 configfile, lineno, name, token, arg);
3320 }
3321 if (missingarg || badarg) {
3322 PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
3323 printoutvaliddirectiveargs(LOG_CRIT, sym);
3324 PrintOut(LOG_CRIT, "\n");
3325 return -1;
3326 }
3327
3328 // If this did something to fill the mail structure, and that didn't
3329 // already exist, create it and copy.
3330 if (makemail) {
3331 if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) {
3332 PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n",
3333 configfile, lineno, name);
3334 EXIT(EXIT_NOMEM);
3335 }
3336 memcpy(cfg->mailwarn, mdat, sizeof(maildata));
3337 }
3338
3339 return 1;
3340}
3341
3342// Allocate storage for a new cfgfile entry. If original!=NULL, it's
3343// a copy of the original, but with private data storage. Else all is
3344// zeroed. Returns address, and fails if non memory available.
3345
3346cfgfile *CreateConfigEntry(cfgfile *original){
3347 cfgfile *add;
3348
3349 // allocate memory for new structure
3350 if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile))))
3351 goto badexit;
3352
3353 // if old structure was pointed to, copy it
3354 if (original)
3355 memcpy(add, original, sizeof(cfgfile));
3356
3357 // make private copies of data items ONLY if they are in use (non
3358 // NULL)
3359 add->name = CustomStrDup(add->name, 0, __LINE__,filenameandversion);
3360
3361 if (add->testdata) {
3362 int val;
3363 if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo))))
3364 goto badexit;
3365 memcpy(add->testdata, original->testdata, sizeof(testinfo));
3366 add->testdata->regex = CustomStrDup(add->testdata->regex, 1, __LINE__,filenameandversion);
3367 // only POSIX-portable way to make fresh copy of compiled regex is
3368 // to recompile it completely. There is no POSIX
3369 // compiled-regex-copy command.
3370 if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) {
3371 char errormsg[512];
3372 regerror(val, &(add->testdata->cregex), errormsg, 512);
3373 PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg);
3374 goto badexit;
3375 }
3376 }
3377
3378 if (add->mailwarn) {
3379 if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata))))
3380 goto badexit;
3381 memcpy(add->mailwarn, original->mailwarn, sizeof(maildata));
3382 add->mailwarn->address = CustomStrDup(add->mailwarn->address, 0, __LINE__,filenameandversion);
3383 add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion);
3384 }
3385
3386 if (add->attributedefs) {
3387 if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1)))
3388 goto badexit;
3389 memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM);
3390 }
3391
3392 if (add->monitorattflags) {
3393 if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1)))
3394 goto badexit;
3395 memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32);
3396 }
3397
3398 if (add->smartval) {
3399 if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values))))
3400 goto badexit;
3401 }
3402
3403 if (add->smartthres) {
3404 if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt))))
3405 goto badexit;
3406 }
3407
3408 return add;
3409
3410 badexit:
3411 PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n");
3412 EXIT(EXIT_NOMEM);
3413}
3414
3415
3416
3417// This is the routine that adds things to the cfgentries list. To
3418// prevent memory leaks when re-reading the configuration file many
3419// times, this routine MUST deallocate any memory other than that
3420// pointed to within cfg-> before it returns.
3421//
3422// Return values are:
3423// 1: parsed a normal line
3424// 0: found comment or blank line
3425// -1: found SCANDIRECTIVE line
3426// -2: found an error
3427//
3428// Note: this routine modifies *line from the caller!
3429int ParseConfigLine(int entry, int lineno,char *line){
3430 char *token=NULL;
3431 char *name=NULL;
3432 char *delim = " \n\t";
3433 cfgfile *cfg=NULL;
3434 int devscan=0;
3435
3436 // get first token: device name. If a comment, skip line
3437 if (!(name=strtok(line,delim)) || *name=='#') {
3438 return 0;
3439 }
3440
3441 // Have we detected the SCANDIRECTIVE directive?
3442 if (!strcmp(SCANDIRECTIVE,name)){
3443 devscan=1;
3444 if (entry) {
3445 PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile);
3446 return -2;
3447 }
3448 }
3449
3450 // Is there space for another entry? If not, allocate more
3451 while (entry>=cfgentries_max)
3452 cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device");
3453
3454 // We've got a legit entry, make space to store it
3455 cfg=cfgentries[entry]=CreateConfigEntry(NULL);
3456 cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion);
3457
3458 // Store line number, and by default check for both device types.
3459 cfg->lineno=lineno;
3460
3461 // Try and recognize if a IDE or SCSI device. These can be
3462 // overwritten by configuration file directives.
3463 if (cfg->controller_type==CONTROLLER_UNKNOWN)
3464 cfg->controller_type = guess_device_type(cfg->name);
3465
3466 // parse tokens one at a time from the file.
3467 while ((token=strtok(NULL,delim))){
3468 int retval=ParseToken(token,cfg);
3469
3470 if (retval==0)
3471 // No tokens left:
3472 break;
3473
3474 if (retval>0) {
3475 // Parsed token
3476#if (0)
3477 PrintOut(LOG_INFO,"Parsed token %s\n",token);
3478#endif
3479 continue;
3480 }
3481
3482 if (retval<0) {
3483 // error found on the line
3484 return -2;
3485 }
3486 }
3487
ba59cff1
GG
3488 // If we found 3ware/cciss controller, then modify device name by adding a SPACE
3489 if (cfg->controller_port) {
832b75ed
GG
3490 int len=17+strlen(cfg->name);
3491 char *newname;
3492
3493 if (devscan){
ba59cff1 3494 PrintOut(LOG_CRIT, "smartd: can not scan for 3ware/cciss devices (line %d of file %s)\n",
832b75ed
GG
3495 lineno, configfile);
3496 return -2;
3497 }
3498
3499 if (!(newname=(char *)calloc(len,1))) {
3500 PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
3501 EXIT(EXIT_NOMEM);
3502 }
3503
3504 // Make new device name by adding a space then RAID disk number
ba59cff1
GG
3505 snprintf(newname, len, "%s [%s_disk_%02d]", cfg->name, (cfg->controller_type == CONTROLLER_CCISS) ? "cciss" : "3ware",
3506 cfg->controller_port-1);
832b75ed
GG
3507 cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
3508 cfg->name=newname;
3509 bytes+=16;
3510 }
3511
4d59bff9
GG
3512 if (cfg->hpt_data[0]) {
3513 int len=17+strlen(cfg->name);
3514 char *newname;
3515
3516 if (devscan){
3517 PrintOut(LOG_CRIT, "smartd: can not scan for highpoint devices (line %d of file %s)\n",
3518 lineno, configfile);
3519 return -2;
3520 }
3521
3522 if (!(newname=(char *)calloc(len,1))) {
3523 PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
3524 EXIT(EXIT_NOMEM);
3525 }
3526
3527 // Make new device name by adding a space then RAID disk number
3528 snprintf(newname, len, "%s [hpt_%d/%d/%d]", cfg->name, cfg->hpt_data[0],
3529 cfg->hpt_data[1], cfg->hpt_data[2]);
3530 cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
3531 cfg->name=newname;
3532 bytes+=16;
3533 }
3534
832b75ed
GG
3535 // If NO monitoring directives are set, then set all of them.
3536 if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail ||
4d59bff9
GG
3537 cfg->usage || cfg->selftest || cfg->errorlog ||
3538 cfg->tempdiff || cfg->tempinfo || cfg->tempcrit )) {
832b75ed
GG
3539
3540 PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
3541 cfg->name, cfg->lineno, configfile);
3542
3543 cfg->smartcheck=1;
3544 cfg->usagefailed=1;
3545 cfg->prefail=1;
3546 cfg->usage=1;
3547 cfg->selftest=1;
3548 cfg->errorlog=1;
3549 }
3550
3551 // additional sanity check. Has user set -M options without -m?
3552 if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){
3553 PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
3554 cfg->name, cfg->lineno, configfile);
3555 return -2;
3556 }
3557
3558 // has the user has set <nomailer>?
3559 if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){
3560 // check that -M exec is also set
3561 if (!cfg->mailwarn->emailcmdline){
3562 PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
3563 cfg->name, cfg->lineno, configfile);
3564 return -2;
3565 }
3566 // now free memory. From here on the sign of <nomailer> is
3567 // address==NULL and cfg->emailcmdline!=NULL
3568 cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion);
3569 }
3570
3571 // set cfg->emailfreq to 1 (once) if user hasn't set it
3572 if (cfg->mailwarn && !cfg->mailwarn->emailfreq)
3573 cfg->mailwarn->emailfreq = 1;
3574
3575 entry++;
3576
3577 if (devscan)
3578 return -1;
3579 else
3580 return 1;
3581}
3582
3583// clean up utility for ParseConfigFile()
3584void cleanup(FILE **fpp, int is_stdin){
3585 if (*fpp){
3586 // (*fpp != stdin) does not work here if stdin has been closed & reopened
3587 if (!is_stdin)
3588 fclose(*fpp);
3589 *fpp=NULL;
3590 }
3591
3592 return;
3593}
3594
3595
3596// Parses a configuration file. Return values are:
3597// N=>0: found N entries
3598// -1: syntax error in config file
3599// -2: config file does not exist
3600// -3: config file exists but cannot be read
3601//
3602// In the case where the return value is 0, there are three
3603// possiblities:
3604// Empty configuration file ==> cfgentries==NULL
3605// No configuration file ==> cfgentries[0]->lineno == 0
3606// SCANDIRECTIVE found ==> cfgentries[0]->lineno != 0
3607int ParseConfigFile(){
3608 FILE *fp=NULL;
3609 int entry=0,lineno=1,cont=0,contlineno=0;
3610 char line[MAXLINELEN+2];
3611 char fullline[MAXCONTLINE+1];
3612
3613 int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here
3614
3615 // Open config file, if it exists and is not <stdin>
3616 if (!is_stdin) {
3617 fp=fopen(configfile,"r");
3618 if (fp==NULL && (errno!=ENOENT || configfile_alt)) {
3619 // file exists but we can't read it or it should exist due to '-c' option
3620 int ret = (errno!=ENOENT ? -3 : -2);
3621 PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
3622 strerror(errno),configfile);
3623 return ret;
3624 }
3625 }
3626 else // read from stdin ('-c -' option)
3627 fp = stdin;
3628
3629 // No configuration file found -- use fake one
3630 if (fp==NULL) {
3631 int len=strlen(SCANDIRECTIVE)+4;
3632 char *fakeconfig=(char *)calloc(len,1);
3633
3634 if (!fakeconfig ||
3635 (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) ||
3636 -1 != ParseConfigLine(entry, 0, fakeconfig)
3637 ) {
3638 PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s",
3639 __LINE__, filenameandversion, reportbug);
3640 EXIT(EXIT_BADCODE);
3641 }
3642 fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion);
3643 return 0;
3644 }
3645
3646#ifdef __CYGWIN__
3647 setmode(fileno(fp), O_TEXT); // Allow files with \r\n
3648#endif
3649
3650 // configuration file exists
3651 PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
3652
3653 // parse config file line by line
3654 while (1) {
3655 int len=0,scandevice;
3656 char *lastslash;
3657 char *comment;
3658 char *code;
3659
3660 // make debugging simpler
3661 memset(line,0,sizeof(line));
3662
3663 // get a line
3664 code=fgets(line,MAXLINELEN+2,fp);
3665
3666 // are we at the end of the file?
3667 if (!code){
3668 if (cont) {
3669 scandevice=ParseConfigLine(entry,contlineno,fullline);
3670 // See if we found a SCANDIRECTIVE directive
3671 if (scandevice==-1) {
3672 cleanup(&fp, is_stdin);
3673 return 0;
3674 }
3675 // did we find a syntax error
3676 if (scandevice==-2) {
3677 cleanup(&fp, is_stdin);
3678 return -1;
3679 }
3680 // the final line is part of a continuation line
3681 cont=0;
3682 entry+=scandevice;
3683 }
3684 break;
3685 }
3686
3687 // input file line number
3688 contlineno++;
3689
3690 // See if line is too long
3691 len=strlen(line);
3692 if (len>MAXLINELEN){
3693 char *warn;
3694 if (line[len-1]=='\n')
3695 warn="(including newline!) ";
3696 else
3697 warn="";
3698 PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
3699 (int)contlineno,configfile,warn,(int)MAXLINELEN);
3700 cleanup(&fp, is_stdin);
3701 return -1;
3702 }
3703
3704 // Ignore anything after comment symbol
3705 if ((comment=strchr(line,'#'))){
3706 *comment='\0';
3707 len=strlen(line);
3708 }
3709
3710 // is the total line (made of all continuation lines) too long?
3711 if (cont+len>MAXCONTLINE){
3712 PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
3713 lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
3714 cleanup(&fp, is_stdin);
3715 return -1;
3716 }
3717
3718 // copy string so far into fullline, and increment length
3719 strcpy(fullline+cont,line);
3720 cont+=len;
3721
3722 // is this a continuation line. If so, replace \ by space and look at next line
3723 if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
3724 *(fullline+(cont-len)+(lastslash-line))=' ';
3725 continue;
3726 }
3727
3728 // Not a continuation line. Parse it
3729 scandevice=ParseConfigLine(entry,contlineno,fullline);
3730
3731 // did we find a scandevice directive?
3732 if (scandevice==-1) {
3733 cleanup(&fp, is_stdin);
3734 return 0;
3735 }
3736 // did we find a syntax error
3737 if (scandevice==-2) {
3738 cleanup(&fp, is_stdin);
3739 return -1;
3740 }
3741
3742 entry+=scandevice;
3743 lineno++;
3744 cont=0;
3745 }
3746 cleanup(&fp, is_stdin);
3747
3748 // note -- may be zero if syntax of file OK, but no valid entries!
3749 return entry;
3750}
3751
3752
3753// Prints copyright, license and version information
3754void PrintCopyleft(void){
3755 debugmode=1;
3756 PrintHead();
3757 PrintCVS();
3758 return;
3759}
3760
3761/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> <=======\n", where
3762 <LIST> is the list of valid arguments for option opt. */
3763void PrintValidArgs(char opt) {
3764 const char *s;
3765
3766 PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
3767 if (!(s = GetValidArgList(opt)))
3768 PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
3769 else
3770 PrintOut(LOG_CRIT, (char *)s);
3771 PrintOut(LOG_CRIT, " <=======\n");
3772}
3773
3774// Parses input line, prints usage message and
3775// version/license/copyright messages
3776void ParseOpts(int argc, char **argv){
3777 extern char *optarg;
3778 extern int optopt, optind, opterr;
3779 int optchar;
3780 int badarg;
3781 char *tailptr;
3782 long lchecktime;
3783 // Please update GetValidArgList() if you edit shortopts
3784 const char *shortopts = "c:l:q:dDi:p:r:Vh?";
3785#ifdef HAVE_GETOPT_LONG
3786 char *arg;
3787 // Please update GetValidArgList() if you edit longopts
3788 struct option longopts[] = {
3789 { "configfile", required_argument, 0, 'c' },
3790 { "logfacility", required_argument, 0, 'l' },
3791 { "quit", required_argument, 0, 'q' },
3792 { "debug", no_argument, 0, 'd' },
3793 { "showdirectives", no_argument, 0, 'D' },
3794 { "interval", required_argument, 0, 'i' },
3795 { "pidfile", required_argument, 0, 'p' },
3796 { "report", required_argument, 0, 'r' },
3797#if defined(_WIN32) || defined(__CYGWIN__)
3798 { "service", no_argument, 0, 'S' },
3799#endif
3800 { "version", no_argument, 0, 'V' },
3801 { "license", no_argument, 0, 'V' },
3802 { "copyright", no_argument, 0, 'V' },
3803 { "help", no_argument, 0, 'h' },
3804 { "usage", no_argument, 0, 'h' },
3805 { 0, 0, 0, 0 }
3806 };
3807#endif
3808
3809 opterr=optopt=0;
3810 badarg=FALSE;
3811
3812 // Parse input options. This horrible construction is so that emacs
3813 // indents properly. Sorry.
3814 while (-1 != (optchar =
3815#ifdef HAVE_GETOPT_LONG
3816 getopt_long(argc, argv, shortopts, longopts, NULL)
3817#else
3818 getopt(argc, argv, shortopts)
3819#endif
3820 )) {
3821
3822 switch(optchar) {
3823 case 'q':
3824 // when to quit
3825 if (!(strcmp(optarg,"nodev"))) {
3826 quit=0;
3827 } else if (!(strcmp(optarg,"nodevstartup"))) {
3828 quit=1;
3829 } else if (!(strcmp(optarg,"never"))) {
3830 quit=2;
3831 } else if (!(strcmp(optarg,"onecheck"))) {
3832 quit=3;
3833 debugmode=1;
3834 } else if (!(strcmp(optarg,"showtests"))) {
3835 quit=4;
3836 debugmode=1;
3837 } else if (!(strcmp(optarg,"errors"))) {
3838 quit=5;
3839 } else {
3840 badarg = TRUE;
3841 }
3842 break;
3843 case 'l':
3844 // set the log facility level
3845 if (!strcmp(optarg, "daemon"))
3846 facility=LOG_DAEMON;
3847 else if (!strcmp(optarg, "local0"))
3848 facility=LOG_LOCAL0;
3849 else if (!strcmp(optarg, "local1"))
3850 facility=LOG_LOCAL1;
3851 else if (!strcmp(optarg, "local2"))
3852 facility=LOG_LOCAL2;
3853 else if (!strcmp(optarg, "local3"))
3854 facility=LOG_LOCAL3;
3855 else if (!strcmp(optarg, "local4"))
3856 facility=LOG_LOCAL4;
3857 else if (!strcmp(optarg, "local5"))
3858 facility=LOG_LOCAL5;
3859 else if (!strcmp(optarg, "local6"))
3860 facility=LOG_LOCAL6;
3861 else if (!strcmp(optarg, "local7"))
3862 facility=LOG_LOCAL7;
3863 else
3864 badarg = TRUE;
3865 break;
3866 case 'd':
3867 // enable debug mode
3868 debugmode = TRUE;
3869 break;
3870 case 'D':
3871 // print summary of all valid directives
3872 debugmode = TRUE;
3873 Directives();
3874 EXIT(0);
3875 break;
3876 case 'i':
3877 // Period (time interval) for checking
3878 // strtol will set errno in the event of overflow, so we'll check it.
3879 errno = 0;
3880 lchecktime = strtol(optarg, &tailptr, 10);
3881 if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
3882 debugmode=1;
3883 PrintHead();
3884 PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
3885 PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
3886 PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
3887 EXIT(EXIT_BADCMD);
3888 }
3889 checktime = (int)lchecktime;
3890 break;
3891 case 'r':
3892 // report IOCTL transactions
3893 {
3894 int i;
3895 char *s;
3896
3897 // split_report_arg() may modify its first argument string, so use a
3898 // copy of optarg in case we want optarg for an error message.
3899 if (!(s = strdup(optarg))) {
3900 PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
3901 EXIT(EXIT_NOMEM);
3902 }
3903 if (split_report_arg(s, &i)) {
3904 badarg = TRUE;
3905 } else if (i<1 || i>3) {
3906 debugmode=1;
3907 PrintHead();
3908 PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
3909 PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
3910 EXIT(EXIT_BADCMD);
3911 } else if (!strcmp(s,"ioctl")) {
3912 con->reportataioctl = con->reportscsiioctl = i;
3913 } else if (!strcmp(s,"ataioctl")) {
3914 con->reportataioctl = i;
3915 } else if (!strcmp(s,"scsiioctl")) {
3916 con->reportscsiioctl = i;
3917 } else {
3918 badarg = TRUE;
3919 }
3920 s=CheckFree(s, __LINE__,filenameandversion);
3921 }
3922 break;
3923 case 'c':
3924 // alternate configuration file
3925 if (strcmp(optarg,"-"))
3926 configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
3927 else // read from stdin
3928 configfile=configfile_stdin;
3929 break;
3930 case 'p':
3931 // output file with PID number
3932 pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
3933 break;
3934#if defined(_WIN32) || defined(__CYGWIN__)
3935 case 'S':
3936 // running as service
3937#ifdef __CYGWIN__ // On Windows, option is already handled by daemon_main(), so ignore it
3938 is_service = 1;
3939#endif
3940 break;
3941#endif // _WIN32 || __CYGWIN__
3942 case 'V':
3943 // print version and CVS info
3944 PrintCopyleft();
3945 EXIT(0);
3946 break;
3947 case 'h':
3948 // help: print summary of command-line options
3949 debugmode=1;
3950 PrintHead();
3951 Usage();
3952 EXIT(0);
3953 break;
3954 case '?':
3955 default:
3956 // unrecognized option
3957 debugmode=1;
3958 PrintHead();
3959#ifdef HAVE_GETOPT_LONG
3960 // Point arg to the argument in which this option was found.
3961 arg = argv[optind-1];
3962 // Check whether the option is a long option that doesn't map to -h.
3963 if (arg[1] == '-' && optchar != 'h') {
3964 // Iff optopt holds a valid option then argument must be missing.
3965 if (optopt && (strchr(shortopts, optopt) != NULL)) {
3966 PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
3967 PrintValidArgs(optopt);
3968 } else {
3969 PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
3970 }
3971 PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
3972 EXIT(EXIT_BADCMD);
3973 }
3974#endif
3975 if (optopt) {
3976 // Iff optopt holds a valid option then argument must be missing.
3977 if (strchr(shortopts, optopt) != NULL){
3978 PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
3979 PrintValidArgs(optopt);
3980 } else {
3981 PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
3982 }
3983 PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
3984 EXIT(EXIT_BADCMD);
3985 }
3986 Usage();
3987 EXIT(0);
3988 }
3989
3990 // Check to see if option had an unrecognized or incorrect argument.
3991 if (badarg) {
3992 debugmode=1;
3993 PrintHead();
3994 // It would be nice to print the actual option name given by the user
3995 // here, but we just print the short form. Please fix this if you know
3996 // a clean way to do it.
3997 PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
3998 PrintValidArgs(optchar);
3999 PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
4000 EXIT(EXIT_BADCMD);
4001 }
4002 }
4003
4004 // non-option arguments are not allowed
4005 if (argc > optind) {
4006 debugmode=1;
4007 PrintHead();
4008 PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
4009 PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
4010 EXIT(EXIT_BADCMD);
4011 }
4012
4013 // no pidfile in debug mode
4014 if (debugmode && pid_file) {
4015 debugmode=1;
4016 PrintHead();
4017 PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
4018 PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file);
4019 pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
4020 EXIT(EXIT_BADCMD);
4021 }
4022
4023 // print header
4024 PrintHead();
4025
4026 return;
4027}
4028
4029// Function we call if no configuration file was found or if the
4030// SCANDIRECTIVE Directive was found. It makes entries for device
4031// names returned by make_device_names() in os_OSNAME.c
4032int MakeConfigEntries(const char *type, int start){
4033 int i;
4034 int num;
4035 char** devlist = NULL;
4036 cfgfile *first=cfgentries[0],*cfg=first;
4037
9ebc753d
GG
4038 // Hack! This is to make DEVICESCAN work on ATA devices behind
4039 // a SCSI to ATA Translation (SAT) Layer.
ba59cff1
GG
4040 // This will work on a general OS if the way that SAT devices are
4041 // named is the same as SCSI devices.
4042 // The BETTER solution is to modify make_device_names to recognize
4043 // the additional type "SAT". This requires changing os_*.cpp.
4044
4045 const char *basetype = type;
4046 if (!strcmp(type,"SAT") )
4047 basetype = "SCSI";
4048
832b75ed 4049 // make list of devices
ba59cff1 4050 if ((num=make_device_names(&devlist,basetype))<0)
832b75ed
GG
4051 PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
4052
4053 // if no devices, or error constructing list, return
4054 if (num<=0)
4055 return 0;
4056
4057 // loop over entries to create
4058 for (i=0; i<num; i++){
4059
4060 // make storage and copy for all but first entry
4061 if (start+i) {
4062 // allocate more storage if needed
4063 while (cfgentries_max<=start+i)
4064 cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "simulated configuration file device");
4065 cfg=cfgentries[start+i]=CreateConfigEntry(first);
4066 }
4067
4068 // ATA or SCSI?
4069 if (!strcmp(type,"ATA") )
4070 cfg->controller_type = CONTROLLER_ATA;
4071 if (!strcmp(type,"SCSI") )
4072 cfg->controller_type = CONTROLLER_SCSI;
ba59cff1
GG
4073 if (!strcmp(type,"SAT") )
4074 cfg->controller_type = CONTROLLER_SAT;
832b75ed
GG
4075
4076 // remove device name, if it's there, and put in correct one
4077 cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
4078 // save pointer to the device name created within
4079 // make_device_names
4080 cfg->name=devlist[i];
4081 }
4082
4083 // If needed, free memory used for devlist: pointers now in
4084 // cfgentries[]->names. If num==0 we never get to this point, but
4085 // that's OK. If we realloc()d the array length in
4086 // make_device_names() that was ALREADY equivalent to calling
4087 // free().
4088 devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion);
4089
4090 return num;
4091}
4092
4093void CanNotRegister(char *name, char *type, int line, int scandirective){
4094 if( !debugmode && scandirective == 1 ) { return; }
4095 if (line)
4096 PrintOut(scandirective?LOG_INFO:LOG_CRIT,
4097 "Unable to register %s device %s at line %d of file %s\n",
4098 type, name, line, configfile);
4099 else
4100 PrintOut(LOG_INFO,"Unable to register %s device %s\n",
4101 type, name);
4102 return;
4103}
4104
4105// Returns negative value (see ParseConfigFile()) if config file
4106// had errors, else number of entries which may be zero or positive.
4107// If we found no configuration file, or it contained SCANDIRECTIVE,
4108// then *scanning is set to 1, else 0.
4109int ReadOrMakeConfigEntries(int *scanning){
4110 int entries;
4111
4112 // deallocate any cfgfile data structures in memory
4113 RmAllConfigEntries();
4114
4115 // parse configuration file configfile (normally /etc/smartd.conf)
4116 if ((entries=ParseConfigFile())<0) {
4117
4118 // There was an error reading the configuration file.
4119 RmAllConfigEntries();
4120 if (entries == -1)
4121 PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
4122 return entries;
4123 }
4124
4125 // did we find entries or scan?
4126 *scanning=0;
4127
4128 // no error parsing config file.
4129 if (entries) {
4130 // we did not find a SCANDIRECTIVE and did find valid entries
4131 PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
4132 }
4133 else if (cfgentries && cfgentries[0]) {
4134 // we found a SCANDIRECTIVE or there was no configuration file so
4135 // scan. Configuration file's first entry contains all options
4136 // that were set
4137 cfgfile *first=cfgentries[0];
ba59cff1
GG
4138
4139 // By default scan for ATA, SCSI and SAT devices
4140 int doata=1, doscsi=1, dosat=1;
4141
4142 if (first->controller_type==CONTROLLER_SCSI) {
4143 doata = 0;
4144 dosat = 0;
4145 } else if (first->controller_type==CONTROLLER_ATA) {
4146 doscsi = 0;
4147 dosat = 0;
4148 } else if (first->controller_type==CONTROLLER_SAT) {
4149 doata = 0;
4150 doscsi = 0;
4151 }
4152
832b75ed
GG
4153 *scanning=1;
4154
4155 if (first->lineno)
4156 PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
4157 else
4158 PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
4159
4160 // make config list of ATA devices to search for
4161 if (doata)
4162 entries+=MakeConfigEntries("ATA", entries);
4163 // make config list of SCSI devices to search for
4164 if (doscsi)
4165 entries+=MakeConfigEntries("SCSI", entries);
ba59cff1
GG
4166 if (dosat)
4167 entries+=MakeConfigEntries("SAT", entries);
832b75ed
GG
4168
4169 // warn user if scan table found no devices
4170 if (!entries) {
4171 PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
4172 // get rid of fake entry with SCANDIRECTIVE as name
4173 RmConfigEntry(cfgentries, __LINE__);
4174 }
4175 }
4176 else
4177 PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
4178
4179 return entries;
4180}
4181
4182
4183// This function tries devices from cfgentries. Each one that can be
4184// registered is moved onto the [ata|scsi]devices lists and removed
4185// from the cfgentries list, else it's memory is deallocated.
4186void RegisterDevices(int scanning){
4187 int i;
4188
4189 // start by clearing lists/memory of ALL existing devices
4190 RmAllDevEntries();
4191 numdevata=numdevscsi=0;
4192
4193 // Register entries
4194 for (i=0; i<cfgentries_max ; i++){
4195
4196 cfgfile *ent=cfgentries[i];
4197
4198 // skip any NULL entries (holes)
4199 if (!ent)
4200 continue;
4201
4202 // register ATA devices
ba59cff1 4203 if (ent->controller_type!=CONTROLLER_SCSI && ent->controller_type!=CONTROLLER_CCISS){
832b75ed
GG
4204 if (ATADeviceScan(ent, scanning))
4205 CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
4206 else {
4207 // move onto the list of ata devices
4208 cfgentries[i]=NULL;
4209 while (numdevata>=atadevlist_max)
4210 atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
4211 atadevlist[numdevata++]=ent;
4212 }
4213 }
4214
4215 // then register SCSI devices
ba59cff1
GG
4216 if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_CCISS ||
4217 ent->controller_type==CONTROLLER_UNKNOWN){
832b75ed
GG
4218 int retscsi=0;
4219
4220#if SCSITIMEOUT
4221 struct sigaction alarmAction, defaultaction;
4222
4223 // Set up an alarm handler to catch USB devices that hang on
4224 // SCSI scanning...
4225 alarmAction.sa_handler= AlarmHandler;
4226 alarmAction.sa_flags = SA_RESTART;
4227 if (sigaction(SIGALRM, &alarmAction, &defaultaction)) {
4228 // if we can't set timeout, just scan device
4229 PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n");
4230 retscsi=SCSIDeviceScan(ent, scanning);
4231 }
4232 else {
4233 // prepare return point in case of bad SCSI device
4234 if (setjmp(registerscsienv))
4235 // SCSI device timed out!
4236 retscsi=-1;
4237 else {
4238 // Set alarm, make SCSI call, reset alarm
4239 alarm(SCSITIMEOUT);
4240 retscsi=SCSIDeviceScan(ent, scanning);
4241 alarm(0);
4242 }
4243 if (sigaction(SIGALRM, &defaultaction, NULL)){
4244 PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n");
4245 }
4246 }
4247#else
4248 retscsi=SCSIDeviceScan(ent, scanning);
4249#endif
4250
4251 // Now scan SCSI device...
4252 if (retscsi){
4253 if (retscsi<0)
4254 PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name);
4255 CanNotRegister(ent->name, "SCSI", ent->lineno, scanning);
4256 }
4257 else {
4258 // move onto the list of scsi devices
4259 cfgentries[i]=NULL;
4260 while (numdevscsi>=scsidevlist_max)
4261 scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
4262 scsidevlist[numdevscsi++]=ent;
4263 }
4264 }
4265
4266 // if device is explictly listed and we can't register it, then
4267 // exit unless the user has specified that the device is removable
4268 if (cfgentries[i] && !scanning){
4269 if (ent->removable || quit==2)
4270 PrintOut(LOG_INFO, "Device %s not available\n", ent->name);
4271 else {
4272 PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name);
4273 EXIT(EXIT_BADDEV);
4274 }
4275 }
4276
4277 // free up memory if device could not be registered
4278 RmConfigEntry(cfgentries+i, __LINE__);
4279 }
4280
4281 return;
4282}
4283
4284
4285#ifndef _WIN32
4286// Main function
4287int main(int argc, char **argv)
4288#else
4289// Windows: internal main function started direct or by service control manager
4290static int smartd_main(int argc, char **argv)
4291#endif
4292{
4293 // external control variables for ATA disks
4294 smartmonctrl control;
4295
4296 // is it our first pass through?
4297 int firstpass=1;
4298
4299 // next time to wake up
4300 time_t wakeuptime;
4301
4302 // for simplicity, null all global communications variables/lists
4303 con=&control;
4304 memset(con, 0,sizeof(control));
4305
4306 // parse input and print header and usage info if needed
4307 ParseOpts(argc,argv);
4308
4309 // do we mute printing from ataprint commands?
4310 con->printing_switchable=0;
4311 con->dont_print=debugmode?0:1;
4312
4313 // don't exit on bad checksums
4314 con->checksumfail=0;
4315
4316 // the main loop of the code
4317 while (1){
4318
4319 // are we exiting from a signal?
4320 if (caughtsigEXIT) {
4321 // are we exiting with SIGTERM?
4322 int isterm=(caughtsigEXIT==SIGTERM);
4323 int isquit=(caughtsigEXIT==SIGQUIT);
4324 int isok=debugmode?isterm || isquit:isterm;
4325
4326 PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
4327 caughtsigEXIT, strsignal(caughtsigEXIT));
4328
4329 EXIT(isok?0:EXIT_SIGNAL);
4330 }
4331
4332 // Should we (re)read the config file?
4333 if (firstpass || caughtsigHUP){
4334 int entries, scanning=0;
4335
4336 if (!firstpass) {
4337#ifdef __CYGWIN__
4338 // Workaround for missing SIGQUIT via keyboard on Cygwin
4339 if (caughtsigHUP==2) {
4340 // Simulate SIGQUIT if another SIGINT arrives soon
4341 caughtsigHUP=0;
4342 sleep(1);
4343 if (caughtsigHUP==2) {
4344 caughtsigEXIT=SIGQUIT;
4345 continue;
4346 }
4347 caughtsigHUP=2;
4348 }
4349#endif
4350 PrintOut(LOG_INFO,
4351 caughtsigHUP==1?
4352 "Signal HUP - rereading configuration file %s\n":
4353 "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n",
4354 configfile);
4355 }
4356
4357 // clears cfgentries, (re)reads config file, makes >=0 entries
4358 entries=ReadOrMakeConfigEntries(&scanning);
4359
4360 if (entries>=0) {
4361 // checks devices, then moves onto ata/scsi list or deallocates.
4362 RegisterDevices(scanning);
4363 }
4364 else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
4365 // user has asked to continue on error in configuration file
4366 if (!firstpass)
4367 PrintOut(LOG_INFO,"Reusing previous configuration\n");
4368 }
4369 else {
4370 // exit with configuration file error status
4371 int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
4372 EXIT(status);
4373 }
4374
4375 // Log number of devices we are monitoring...
4376 if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass))
4377 PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
4378 numdevata, numdevscsi);
4379 else {
4380 PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
4381 EXIT(EXIT_NODEV);
4382 }
4383
4384 if (quit==4) {
4385 // user has asked to print test schedule
4386 PrintTestSchedule(atadevlist, scsidevlist);
4387 EXIT(0);
4388 }
4389
4390 // reset signal
4391 caughtsigHUP=0;
4392 }
4393
4394 // check all devices once
4395 CheckDevicesOnce(atadevlist, scsidevlist);
4396
4397 // user has asked us to exit after first check
4398 if (quit==3) {
4399 PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
4400 "smartd is exiting (exit status 0)\n");
4401 EXIT(0);
4402 }
4403
4404 // fork into background if needed
4405 if (firstpass && !debugmode) {
4406#ifdef __CYGWIN__
4407 if (!is_service) // don't fork() if running as service via cygrunsrv
4408#endif
4409 DaemonInit();
4410 }
4411
4412 // set exit and signal handlers, write PID file, set wake-up time
4413 if (firstpass){
4414 Initialize(&wakeuptime);
4415 firstpass=0;
4416 }
4417
4418 // sleep until next check time, or a signal arrives
4419 wakeuptime=dosleep(wakeuptime);
4420 }
4421}
4422
4423
4424#ifdef _WIN32
4425// Main function for Windows
4426int main(int argc, char **argv){
4427 // Options for smartd windows service
4428 static const daemon_winsvc_options svc_opts = {
4429 "--service", // cmd_opt
4430 "smartd", "SmartD Service", // servicename, displayname
4431 // description
4432 "Controls and monitors storage devices using the Self-Monitoring, "
4433 "Analysis and Reporting Technology System (S.M.A.R.T.) "
4434 "built into ATA and SCSI Hard Drives. "
4435 PACKAGE_HOMEPAGE
4436 };
4437 // daemon_main() handles daemon and service specific commands
4438 // and starts smartd_main() direct, from a new process,
4439 // or via service control manager
4440 return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
4441}
4442#endif