forked from mirror/smartmontools
-
Notifications
You must be signed in to change notification settings - Fork 1
/
smartd.cpp
5560 lines (4946 loc) · 187 KB
/
smartd.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Home page of code is: http://www.smartmontools.org
*
* Copyright (C) 2002-11 Bruce Allen
* Copyright (C) 2008-17 Christian Franke
* Copyright (C) 2000 Michael Cornwell <[email protected]>
* Copyright (C) 2008 Oliver Bock <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* You should have received a copy of the GNU General Public License
* (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
*
* This code was originally developed as a Senior Thesis by Michael Cornwell
* at the Concurrent Systems Laboratory (now part of the Storage Systems
* Research Center), Jack Baskin School of Engineering, University of
* California, Santa Cruz. http://ssrc.soe.ucsc.edu/
*
*/
#include "config.h"
#include "int64.h"
// unconditionally included files
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h> // umask
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <getopt.h>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm> // std::replace()
// conditionally included files
#ifndef _WIN32
#include <sys/wait.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef _WIN32
#ifdef _MSC_VER
#pragma warning(disable:4761) // "conversion supplied"
typedef unsigned short mode_t;
typedef int pid_t;
#endif
#include <io.h> // umask()
#include <process.h> // getpid()
#endif // _WIN32
#ifdef __CYGWIN__
#include <io.h> // setmode()
#endif // __CYGWIN__
#ifdef HAVE_LIBCAP_NG
#include <cap-ng.h>
#endif // LIBCAP_NG
// locally included files
#include "atacmds.h"
#include "dev_interface.h"
#include "knowndrives.h"
#include "scsicmds.h"
#include "nvmecmds.h"
#include "utility.h"
// This is for solaris, where signal() resets the handler to SIG_DFL
// after the first signal is caught.
#ifdef HAVE_SIGSET
#define SIGNALFN sigset
#else
#define SIGNALFN signal
#endif
#ifdef _WIN32
// fork()/signal()/initd simulation for native Windows
#include "daemon_win32.h" // daemon_main/detach/signal()
#undef SIGNALFN
#define SIGNALFN daemon_signal
#define strsignal daemon_strsignal
#define sleep daemon_sleep
// SIGQUIT does not exist, CONTROL-Break signals SIGBREAK.
#define SIGQUIT SIGBREAK
#define SIGQUIT_KEYNAME "CONTROL-Break"
#else // _WIN32
#define SIGQUIT_KEYNAME "CONTROL-\\"
#endif // _WIN32
const char * smartd_cpp_cvsid = "$Id$"
CONFIG_H_CVSID;
using namespace smartmontools;
// smartd exit codes
#define EXIT_BADCMD 1 // command line did not parse
#define EXIT_BADCONF 2 // syntax error in config file
#define EXIT_STARTUP 3 // problem forking daemon
#define EXIT_PID 4 // problem creating pid file
#define EXIT_NOCONF 5 // config file does not exist
#define EXIT_READCONF 6 // config file exists but cannot be read
#define EXIT_NOMEM 8 // out of memory
#define EXIT_BADCODE 10 // internal error - should NEVER happen
#define EXIT_BADDEV 16 // we can't monitor this device
#define EXIT_NODEV 17 // no devices to monitor
#define EXIT_SIGNAL 254 // abort on signal
// command-line: 1=debug mode, 2=print presets
static unsigned char debugmode = 0;
// command-line: how long to sleep between checks
#define CHECKTIME 1800
static int checktime=CHECKTIME;
// command-line: name of PID file (empty for no pid file)
static std::string pid_file;
// command-line: path prefix of persistent state file, empty if no persistence.
static std::string state_path_prefix
#ifdef SMARTMONTOOLS_SAVESTATES
= SMARTMONTOOLS_SAVESTATES
#endif
;
// command-line: path prefix of attribute log file, empty if no logs.
static std::string attrlog_path_prefix
#ifdef SMARTMONTOOLS_ATTRIBUTELOG
= SMARTMONTOOLS_ATTRIBUTELOG
#endif
;
// configuration file name
static const char * configfile;
// configuration file "name" if read from stdin
static const char * const configfile_stdin = "<stdin>";
// path of alternate configuration file
static std::string configfile_alt;
// warning script file
static std::string warning_script;
// command-line: when should we exit?
enum quit_t {
QUIT_NODEV, QUIT_NODEVSTARTUP, QUIT_NEVER, QUIT_ONECHECK,
QUIT_SHOWTESTS, QUIT_ERRORS
};
static quit_t quit = QUIT_NODEV;
// command-line; this is the default syslog(3) log facility to use.
static int facility=LOG_DAEMON;
#ifndef _WIN32
// command-line: fork into background?
static bool do_fork=true;
#endif
#ifdef HAVE_LIBCAP_NG
// command-line: enable capabilities?
static bool enable_capabilities = false;
#endif
// TODO: This smartctl only variable is also used in os_win32.cpp
unsigned char failuretest_permissive = 0;
// set to one if we catch a USR1 (check devices now)
static volatile int caughtsigUSR1=0;
#ifdef _WIN32
// set to one if we catch a USR2 (toggle debug mode)
static volatile int caughtsigUSR2=0;
#endif
// set to one if we catch a HUP (reload config file). In debug mode,
// set to two, if we catch INT (also reload config file).
static volatile int caughtsigHUP=0;
// set to signal value if we catch INT, QUIT, or TERM
static volatile int caughtsigEXIT=0;
// This function prints either to stdout or to the syslog as needed.
static void PrintOut(int priority, const char *fmt, ...)
__attribute_format_printf(2, 3);
// Attribute monitoring flags.
// See monitor_attr_flags below.
enum {
MONITOR_IGN_FAILUSE = 0x01,
MONITOR_IGNORE = 0x02,
MONITOR_RAW_PRINT = 0x04,
MONITOR_RAW = 0x08,
MONITOR_AS_CRIT = 0x10,
MONITOR_RAW_AS_CRIT = 0x20,
};
// Array of flags for each attribute.
class attribute_flags
{
public:
attribute_flags()
{ memset(m_flags, 0, sizeof(m_flags)); }
bool is_set(int id, unsigned char flag) const
{ return (0 < id && id < (int)sizeof(m_flags) && (m_flags[id] & flag)); }
void set(int id, unsigned char flags)
{
if (0 < id && id < (int)sizeof(m_flags))
m_flags[id] |= flags;
}
private:
unsigned char m_flags[256];
};
/// Configuration data for a device. Read from smartd.conf.
/// Supports copy & assignment and is compatible with STL containers.
struct dev_config
{
int lineno; // Line number of entry in file
std::string name; // Device name (with optional extra info)
std::string dev_name; // Device name (plain, for SMARTD_DEVICE variable)
std::string dev_type; // Device type argument from -d directive, empty if none
std::string dev_idinfo; // Device identify info for warning emails
std::string state_file; // Path of the persistent state file, empty if none
std::string attrlog_file; // Path of the persistent attrlog file, empty if none
bool ignore; // Ignore this entry
bool id_is_unique; // True if dev_idinfo is unique (includes S/N or WWN)
bool smartcheck; // Check SMART status
bool usagefailed; // Check for failed Usage Attributes
bool prefail; // Track changes in Prefail Attributes
bool usage; // Track changes in Usage Attributes
bool selftest; // Monitor number of selftest errors
bool errorlog; // Monitor number of ATA errors
bool xerrorlog; // Monitor number of ATA errors (Extended Comprehensive error log)
bool offlinests; // Monitor changes in offline data collection status
bool offlinests_ns; // Disable auto standby if in progress
bool selfteststs; // Monitor changes in self-test execution status
bool selfteststs_ns; // Disable auto standby if in progress
bool permissive; // Ignore failed SMART commands
char autosave; // 1=disable, 2=enable Autosave Attributes
char autoofflinetest; // 1=disable, 2=enable Auto Offline Test
firmwarebug_defs firmwarebugs; // -F directives from drivedb or smartd.conf
bool ignorepresets; // Ignore database of -v options
bool showpresets; // Show database entry for this device
bool removable; // Device may disappear (not be present)
char powermode; // skip check, if disk in idle or standby mode
bool powerquiet; // skip powermode 'skipping checks' message
int powerskipmax; // how many times can be check skipped
unsigned char tempdiff; // Track Temperature changes >= this limit
unsigned char tempinfo, tempcrit; // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail
regular_expression test_regex; // Regex for scheduled testing
// Configuration of email warning messages
std::string emailcmdline; // script to execute, empty if no messages
std::string emailaddress; // email address, or empty
unsigned char emailfreq; // Emails once (1) daily (2) diminishing (3)
bool emailtest; // Send test email?
// ATA ONLY
int dev_rpm; // rotation rate, 0 = unknown, 1 = SSD, >1 = HDD
int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management
int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management
int set_lookahead; // disable(-1), enable(1) read look-ahead
int set_standby; // set(1..255->0..254) standby timer
bool set_security_freeze; // Freeze ATA security
int set_wcache; // disable(-1), enable(1) write cache
int set_dsn; // disable(0x2), enable(0x1) DSN
bool sct_erc_set; // set SCT ERC to:
unsigned short sct_erc_readtime; // ERC read time (deciseconds)
unsigned short sct_erc_writetime; // ERC write time (deciseconds)
unsigned char curr_pending_id; // ID of current pending sector count, 0 if none
unsigned char offl_pending_id; // ID of offline uncorrectable sector count, 0 if none
bool curr_pending_incr, offl_pending_incr; // True if current/offline pending values increase
bool curr_pending_set, offl_pending_set; // True if '-C', '-U' set in smartd.conf
attribute_flags monitor_attr_flags; // MONITOR_* flags for each attribute
ata_vendor_attr_defs attribute_defs; // -v options
dev_config();
};
dev_config::dev_config()
: lineno(0),
ignore(false),
id_is_unique(false),
smartcheck(false),
usagefailed(false),
prefail(false),
usage(false),
selftest(false),
errorlog(false),
xerrorlog(false),
offlinests(false), offlinests_ns(false),
selfteststs(false), selfteststs_ns(false),
permissive(false),
autosave(0),
autoofflinetest(0),
ignorepresets(false),
showpresets(false),
removable(false),
powermode(0),
powerquiet(false),
powerskipmax(0),
tempdiff(0),
tempinfo(0), tempcrit(0),
emailfreq(0),
emailtest(false),
dev_rpm(0),
set_aam(0), set_apm(0),
set_lookahead(0),
set_standby(0),
set_security_freeze(false),
set_wcache(0), set_dsn(0),
sct_erc_set(false),
sct_erc_readtime(0), sct_erc_writetime(0),
curr_pending_id(0), offl_pending_id(0),
curr_pending_incr(false), offl_pending_incr(false),
curr_pending_set(false), offl_pending_set(false)
{
}
// Number of allowed mail message types
static const int SMARTD_NMAIL = 13;
// Type for '-M test' mails (state not persistent)
static const int MAILTYPE_TEST = 0;
// TODO: Add const or enum for all mail types.
struct mailinfo {
int logged;// number of times an email has been sent
time_t firstsent;// time first email was sent, as defined by time(2)
time_t lastsent; // time last email was sent, as defined by time(2)
mailinfo()
: logged(0), firstsent(0), lastsent(0) { }
};
/// Persistent state data for a device.
struct persistent_dev_state
{
unsigned char tempmin, tempmax; // Min/Max Temperatures
unsigned char selflogcount; // total number of self-test errors
unsigned short selfloghour; // lifetime hours of last self-test error
time_t scheduled_test_next_check; // Time of next check for scheduled self-tests
uint64_t selective_test_last_start; // Start LBA of last scheduled selective self-test
uint64_t selective_test_last_end; // End LBA of last scheduled selective self-test
mailinfo maillog[SMARTD_NMAIL]; // log info on when mail sent
// ATA ONLY
int ataerrorcount; // Total number of ATA errors
// Persistent part of ata_smart_values:
struct ata_attribute {
unsigned char id;
unsigned char val;
unsigned char worst; // Byte needed for 'raw64' attribute only.
uint64_t raw;
unsigned char resvd;
ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { }
};
ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES];
// SCSI ONLY
struct scsi_error_counter_t {
struct scsiErrorCounter errCounter;
unsigned char found;
scsi_error_counter_t() : found(0)
{ memset(&errCounter, 0, sizeof(errCounter)); }
};
scsi_error_counter_t scsi_error_counters[3];
struct scsi_nonmedium_error_t {
struct scsiNonMediumError nme;
unsigned char found;
scsi_nonmedium_error_t() : found(0)
{ memset(&nme, 0, sizeof(nme)); }
};
scsi_nonmedium_error_t scsi_nonmedium_error;
// NVMe only
uint64_t nvme_err_log_entries;
persistent_dev_state();
};
persistent_dev_state::persistent_dev_state()
: tempmin(0), tempmax(0),
selflogcount(0),
selfloghour(0),
scheduled_test_next_check(0),
selective_test_last_start(0),
selective_test_last_end(0),
ataerrorcount(0),
nvme_err_log_entries(0)
{
}
/// Non-persistent state data for a device.
struct temp_dev_state
{
bool must_write; // true if persistent part should be written
bool not_cap_offline; // true == not capable of offline testing
bool not_cap_conveyance;
bool not_cap_short;
bool not_cap_long;
bool not_cap_selective;
unsigned char temperature; // last recorded Temperature (in Celsius)
time_t tempmin_delay; // time where Min Temperature tracking will start
bool removed; // true if open() failed for removable device
bool powermodefail; // true if power mode check failed
int powerskipcnt; // Number of checks skipped due to idle or standby mode
int lastpowermodeskipped; // the last power mode that was skipped
// SCSI ONLY
unsigned char SmartPageSupported; // has log sense IE page (0x2f)
unsigned char TempPageSupported; // has log sense temperature page (0xd)
unsigned char ReadECounterPageSupported;
unsigned char WriteECounterPageSupported;
unsigned char VerifyECounterPageSupported;
unsigned char NonMediumErrorPageSupported;
unsigned char SuppressReport; // minimize nuisance reports
unsigned char modese_len; // mode sense/select cmd len: 0 (don't
// know yet) 6 or 10
// ATA ONLY
uint64_t num_sectors; // Number of sectors
ata_smart_values smartval; // SMART data
ata_smart_thresholds_pvt smartthres; // SMART thresholds
bool offline_started; // true if offline data collection was started
bool selftest_started; // true if self-test was started
temp_dev_state();
};
temp_dev_state::temp_dev_state()
: must_write(false),
not_cap_offline(false),
not_cap_conveyance(false),
not_cap_short(false),
not_cap_long(false),
not_cap_selective(false),
temperature(0),
tempmin_delay(0),
removed(false),
powermodefail(false),
powerskipcnt(0),
lastpowermodeskipped(0),
SmartPageSupported(false),
TempPageSupported(false),
ReadECounterPageSupported(false),
WriteECounterPageSupported(false),
VerifyECounterPageSupported(false),
NonMediumErrorPageSupported(false),
SuppressReport(false),
modese_len(0),
num_sectors(0),
offline_started(false),
selftest_started(false)
{
memset(&smartval, 0, sizeof(smartval));
memset(&smartthres, 0, sizeof(smartthres));
}
/// Runtime state data for a device.
struct dev_state
: public persistent_dev_state,
public temp_dev_state
{
void update_persistent_state();
void update_temp_state();
};
/// Container for configuration info for each device.
typedef std::vector<dev_config> dev_config_vector;
/// Container for state info for each device.
typedef std::vector<dev_state> dev_state_vector;
// Copy ATA attributes to persistent state.
void dev_state::update_persistent_state()
{
for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
const ata_smart_attribute & ta = smartval.vendor_attributes[i];
ata_attribute & pa = ata_attributes[i];
pa.id = ta.id;
if (ta.id == 0) {
pa.val = pa.worst = 0; pa.raw = 0;
continue;
}
pa.val = ta.current;
pa.worst = ta.worst;
pa.raw = ta.raw[0]
| ( ta.raw[1] << 8)
| ( ta.raw[2] << 16)
| ((uint64_t)ta.raw[3] << 24)
| ((uint64_t)ta.raw[4] << 32)
| ((uint64_t)ta.raw[5] << 40);
pa.resvd = ta.reserv;
}
}
// Copy ATA from persistent to temp state.
void dev_state::update_temp_state()
{
for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
const ata_attribute & pa = ata_attributes[i];
ata_smart_attribute & ta = smartval.vendor_attributes[i];
ta.id = pa.id;
if (pa.id == 0) {
ta.current = ta.worst = 0;
memset(ta.raw, 0, sizeof(ta.raw));
continue;
}
ta.current = pa.val;
ta.worst = pa.worst;
ta.raw[0] = (unsigned char) pa.raw;
ta.raw[1] = (unsigned char)(pa.raw >> 8);
ta.raw[2] = (unsigned char)(pa.raw >> 16);
ta.raw[3] = (unsigned char)(pa.raw >> 24);
ta.raw[4] = (unsigned char)(pa.raw >> 32);
ta.raw[5] = (unsigned char)(pa.raw >> 40);
ta.reserv = pa.resvd;
}
}
// Parse a line from a state file.
static bool parse_dev_state_line(const char * line, persistent_dev_state & state)
{
static const regular_expression regex(
"^ *"
"((temperature-min)" // (1 (2)
"|(temperature-max)" // (3)
"|(self-test-errors)" // (4)
"|(self-test-last-err-hour)" // (5)
"|(scheduled-test-next-check)" // (6)
"|(selective-test-last-start)" // (7)
"|(selective-test-last-end)" // (8)
"|(ata-error-count)" // (9)
"|(mail\\.([0-9]+)\\." // (10 (11)
"((count)" // (12 (13)
"|(first-sent-time)" // (14)
"|(last-sent-time)" // (15)
")" // 12)
")" // 10)
"|(ata-smart-attribute\\.([0-9]+)\\." // (16 (17)
"((id)" // (18 (19)
"|(val)" // (20)
"|(worst)" // (21)
"|(raw)" // (22)
"|(resvd)" // (23)
")" // 18)
")" // 16)
"|(nvme-err-log-entries)" // (24)
")" // 1)
" *= *([0-9]+)[ \n]*$", // (25)
REG_EXTENDED
);
const int nmatch = 1+25;
regmatch_t match[nmatch];
if (!regex.execute(line, nmatch, match))
return false;
if (match[nmatch-1].rm_so < 0)
return false;
uint64_t val = strtoull(line + match[nmatch-1].rm_so, (char **)0, 10);
int m = 1;
if (match[++m].rm_so >= 0)
state.tempmin = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.tempmax = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.selflogcount = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.selfloghour = (unsigned short)val;
else if (match[++m].rm_so >= 0)
state.scheduled_test_next_check = (time_t)val;
else if (match[++m].rm_so >= 0)
state.selective_test_last_start = val;
else if (match[++m].rm_so >= 0)
state.selective_test_last_end = val;
else if (match[++m].rm_so >= 0)
state.ataerrorcount = (int)val;
else if (match[m+=2].rm_so >= 0) {
int i = atoi(line+match[m].rm_so);
if (!(0 <= i && i < SMARTD_NMAIL))
return false;
if (i == MAILTYPE_TEST) // Don't suppress test mails
return true;
if (match[m+=2].rm_so >= 0)
state.maillog[i].logged = (int)val;
else if (match[++m].rm_so >= 0)
state.maillog[i].firstsent = (time_t)val;
else if (match[++m].rm_so >= 0)
state.maillog[i].lastsent = (time_t)val;
else
return false;
}
else if (match[m+=5+1].rm_so >= 0) {
int i = atoi(line+match[m].rm_so);
if (!(0 <= i && i < NUMBER_ATA_SMART_ATTRIBUTES))
return false;
if (match[m+=2].rm_so >= 0)
state.ata_attributes[i].id = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.ata_attributes[i].val = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.ata_attributes[i].worst = (unsigned char)val;
else if (match[++m].rm_so >= 0)
state.ata_attributes[i].raw = val;
else if (match[++m].rm_so >= 0)
state.ata_attributes[i].resvd = (unsigned char)val;
else
return false;
}
else if (match[m+7].rm_so >= 0)
state.nvme_err_log_entries = val;
else
return false;
return true;
}
// Read a state file.
static bool read_dev_state(const char * path, persistent_dev_state & state)
{
stdio_file f(path, "r");
if (!f) {
if (errno != ENOENT)
pout("Cannot read state file \"%s\"\n", path);
return false;
}
#ifdef __CYGWIN__
setmode(fileno(f), O_TEXT); // Allow files with \r\n
#endif
persistent_dev_state new_state;
int good = 0, bad = 0;
char line[256];
while (fgets(line, sizeof(line), f)) {
const char * s = line + strspn(line, " \t");
if (!*s || *s == '#')
continue;
if (!parse_dev_state_line(line, new_state))
bad++;
else
good++;
}
if (bad) {
if (!good) {
pout("%s: format error\n", path);
return false;
}
pout("%s: %d invalid line(s) ignored\n", path, bad);
}
// This sets the values missing in the file to 0.
state = new_state;
return true;
}
static void write_dev_state_line(FILE * f, const char * name, uint64_t val)
{
if (val)
fprintf(f, "%s = %" PRIu64 "\n", name, val);
}
static void write_dev_state_line(FILE * f, const char * name1, int id, const char * name2, uint64_t val)
{
if (val)
fprintf(f, "%s.%d.%s = %" PRIu64 "\n", name1, id, name2, val);
}
// Write a state file
static bool write_dev_state(const char * path, const persistent_dev_state & state)
{
// Rename old "file" to "file~"
std::string pathbak = path; pathbak += '~';
unlink(pathbak.c_str());
rename(path, pathbak.c_str());
stdio_file f(path, "w");
if (!f) {
pout("Cannot create state file \"%s\"\n", path);
return false;
}
fprintf(f, "# smartd state file\n");
write_dev_state_line(f, "temperature-min", state.tempmin);
write_dev_state_line(f, "temperature-max", state.tempmax);
write_dev_state_line(f, "self-test-errors", state.selflogcount);
write_dev_state_line(f, "self-test-last-err-hour", state.selfloghour);
write_dev_state_line(f, "scheduled-test-next-check", state.scheduled_test_next_check);
write_dev_state_line(f, "selective-test-last-start", state.selective_test_last_start);
write_dev_state_line(f, "selective-test-last-end", state.selective_test_last_end);
int i;
for (i = 0; i < SMARTD_NMAIL; i++) {
if (i == MAILTYPE_TEST) // Don't suppress test mails
continue;
const mailinfo & mi = state.maillog[i];
if (!mi.logged)
continue;
write_dev_state_line(f, "mail", i, "count", mi.logged);
write_dev_state_line(f, "mail", i, "first-sent-time", mi.firstsent);
write_dev_state_line(f, "mail", i, "last-sent-time", mi.lastsent);
}
// ATA ONLY
write_dev_state_line(f, "ata-error-count", state.ataerrorcount);
for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
if (!pa.id)
continue;
write_dev_state_line(f, "ata-smart-attribute", i, "id", pa.id);
write_dev_state_line(f, "ata-smart-attribute", i, "val", pa.val);
write_dev_state_line(f, "ata-smart-attribute", i, "worst", pa.worst);
write_dev_state_line(f, "ata-smart-attribute", i, "raw", pa.raw);
write_dev_state_line(f, "ata-smart-attribute", i, "resvd", pa.resvd);
}
// NVMe only
write_dev_state_line(f, "nvme-err-log-entries", state.nvme_err_log_entries);
return true;
}
// Write to the attrlog file
static bool write_dev_attrlog(const char * path, const dev_state & state)
{
stdio_file f(path, "a");
if (!f) {
pout("Cannot create attribute log file \"%s\"\n", path);
return false;
}
time_t now = time(0);
struct tm * tms = gmtime(&now);
fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;",
1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday,
tms->tm_hour, tms->tm_min, tms->tm_sec);
// ATA ONLY
for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
if (!pa.id)
continue;
fprintf(f, "\t%d;%d;%" PRIu64 ";", pa.id, pa.val, pa.raw);
}
// SCSI ONLY
const struct scsiErrorCounter * ecp;
const char * pageNames[3] = {"read", "write", "verify"};
for (int k = 0; k < 3; ++k) {
if ( !state.scsi_error_counters[k].found ) continue;
ecp = &state.scsi_error_counters[k].errCounter;
fprintf(f, "\t%s-corr-by-ecc-fast;%" PRIu64 ";"
"\t%s-corr-by-ecc-delayed;%" PRIu64 ";"
"\t%s-corr-by-retry;%" PRIu64 ";"
"\t%s-total-err-corrected;%" PRIu64 ";"
"\t%s-corr-algorithm-invocations;%" PRIu64 ";"
"\t%s-gb-processed;%.3f;"
"\t%s-total-unc-errors;%" PRIu64 ";",
pageNames[k], ecp->counter[0],
pageNames[k], ecp->counter[1],
pageNames[k], ecp->counter[2],
pageNames[k], ecp->counter[3],
pageNames[k], ecp->counter[4],
pageNames[k], (ecp->counter[5] / 1000000000.0),
pageNames[k], ecp->counter[6]);
}
if(state.scsi_nonmedium_error.found && state.scsi_nonmedium_error.nme.gotPC0) {
fprintf(f, "\tnon-medium-errors;%" PRIu64 ";", state.scsi_nonmedium_error.nme.counterPC0);
}
// write SCSI current temperature if it is monitored
if (state.temperature)
fprintf(f, "\ttemperature;%d;", state.temperature);
// end of line
fprintf(f, "\n");
return true;
}
// Write all state files. If write_always is false, don't write
// unless must_write is set.
static void write_all_dev_states(const dev_config_vector & configs,
dev_state_vector & states,
bool write_always = true)
{
for (unsigned i = 0; i < states.size(); i++) {
const dev_config & cfg = configs.at(i);
if (cfg.state_file.empty())
continue;
dev_state & state = states[i];
if (!write_always && !state.must_write)
continue;
if (!write_dev_state(cfg.state_file.c_str(), state))
continue;
state.must_write = false;
if (write_always || debugmode)
PrintOut(LOG_INFO, "Device: %s, state written to %s\n",
cfg.name.c_str(), cfg.state_file.c_str());
}
}
// Write to all attrlog files
static void write_all_dev_attrlogs(const dev_config_vector & configs,
dev_state_vector & states)
{
for (unsigned i = 0; i < states.size(); i++) {
const dev_config & cfg = configs.at(i);
if (cfg.attrlog_file.empty())
continue;
dev_state & state = states[i];
write_dev_attrlog(cfg.attrlog_file.c_str(), state);
}
}
// remove the PID file
static void RemovePidFile()
{
if (!pid_file.empty()) {
if (unlink(pid_file.c_str()))
PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n",
pid_file.c_str(), strerror(errno));
pid_file.clear();
}
return;
}
extern "C" { // signal handlers require C-linkage
// Note if we catch a SIGUSR1
static void USR1handler(int sig)
{
if (SIGUSR1==sig)
caughtsigUSR1=1;
return;
}
#ifdef _WIN32
// Note if we catch a SIGUSR2
static void USR2handler(int sig)
{
if (SIGUSR2==sig)
caughtsigUSR2=1;
return;
}
#endif
// Note if we catch a HUP (or INT in debug mode)
static void HUPhandler(int sig)
{
if (sig==SIGHUP)
caughtsigHUP=1;
else
caughtsigHUP=2;
return;
}
// signal handler for TERM, QUIT, and INT (if not in debug mode)
static void sighandler(int sig)
{
if (!caughtsigEXIT)
caughtsigEXIT=sig;
return;
}
} // extern "C"
// Cleanup, print Goodbye message and remove pidfile
static int Goodbye(int status)
{
// delete PID file, if one was created
RemovePidFile();
// and this should be the final output from smartd before it exits
PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
return status;
}
// a replacement for setenv() which is not available on all platforms.
// Note that the string passed to putenv must not be freed or made
// invalid, since a pointer to it is kept by putenv(). This means that
// it must either be a static buffer or allocated off the heap. The
// string can be freed if the environment variable is redefined via
// another call to putenv(). There is no portable way to unset a variable
// with putenv(). So we manage the buffer in a static object.
// Using setenv() if available is not considered because some
// implementations may produce memory leaks.
class env_buffer
{
public:
env_buffer()
: m_buf((char *)0) { }
void set(const char * name, const char * value);
private:
char * m_buf;
env_buffer(const env_buffer &);
void operator=(const env_buffer &);
};
void env_buffer::set(const char * name, const char * value)
{
int size = strlen(name) + 1 + strlen(value) + 1;
char * newbuf = new char[size];
snprintf(newbuf, size, "%s=%s", name, value);
if (putenv(newbuf))
throw std::runtime_error("putenv() failed");
// This assumes that the same NAME is passed on each call
delete [] m_buf;
m_buf = newbuf;
}
#define EBUFLEN 1024
static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
__attribute_format_printf(4, 5);
// If either address or executable path is non-null then send and log
// a warning email, or execute executable
static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
{
static const char * const whichfail[] = {
"EmailTest", // 0
"Health", // 1
"Usage", // 2
"SelfTest", // 3
"ErrorCount", // 4
"FailedHealthCheck", // 5
"FailedReadSmartData", // 6
"FailedReadSmartErrorLog", // 7
"FailedReadSmartSelfTestLog", // 8
"FailedOpenDevice", // 9
"CurrentPendingSector", // 10
"OfflineUncorrectableSector", // 11
"Temperature" // 12
};
// See if user wants us to send mail
if (cfg.emailaddress.empty() && cfg.emailcmdline.empty())
return;
std::string address = cfg.emailaddress;
const char * executable = cfg.emailcmdline.c_str();
// which type of mail are we sending?
mailinfo * mail=(state.maillog)+which;
// checks for sanity
if (cfg.emailfreq<1 || cfg.emailfreq>3) {
PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg.mailwarn->emailfreq=%d\n",cfg.emailfreq);
return;
}
if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
which, (int)sizeof(whichfail));
return;
}
// Return if a single warning mail has been sent.
if ((cfg.emailfreq==1) && mail->logged)
return;