smartmontools SVN Rev 5611
Utility to control and monitor storage systems with "S.M.A.R.T."
knowndrives.cpp
Go to the documentation of this file.
1/*
2 * knowndrives.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2003-11 Philip Williams, Bruce Allen
7 * Copyright (C) 2008-21 Christian Franke
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12#include "config.h"
13
14#include <stdio.h>
15#include "atacmds.h"
16#include "knowndrives.h"
17#include "utility.h"
18
19#ifdef HAVE_UNISTD_H
20#include <unistd.h>
21#endif
22#ifdef _WIN32
23#include <io.h> // access()
24#endif
25
26#include <stdexcept>
27
28const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 5376 2022-05-01 12:49:30Z chrfranke $"
30
31#define MODEL_STRING_LENGTH 40
32#define FIRMWARE_STRING_LENGTH 8
33#define TABLEPRINTWIDTH 19
34
35
36// Builtin table of known drives.
37// Used as a default if not read from
38// "/usr/{,/local}share/smartmontools/drivedb.h"
39// or any other file specified by '-B' option,
40// see read_default_drive_databases() below.
41// The drive_settings structure is described in drivedb.h.
43#include "drivedb.h"
44};
45
47 sizeof(builtin_knowndrives) / sizeof(builtin_knowndrives[0]);
48
49/// Drive database class. Stores custom entries read from file.
50/// Provides transparent access to concatenation of custom and
51/// default table.
53{
54public:
56
58
59 /// Get total number of entries.
60 unsigned size() const
61 { return m_custom_tab.size() + m_builtin_size; }
62
63 /// Get number of custom entries.
64 unsigned custom_size() const
65 { return m_custom_tab.size(); }
66
67 /// Array access.
68 const drive_settings & operator[](unsigned i);
69
70 /// Append new custom entry.
71 void push_back(const drive_settings & src);
72
73 /// Append builtin table.
74 void append(const drive_settings * builtin_tab, unsigned builtin_size)
75 { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; }
76
77private:
80
81 std::vector<drive_settings> m_custom_tab;
82 std::vector<char *> m_custom_strings;
83
84 const char * copy_string(const char * str);
85
88};
89
91: m_builtin_tab(0), m_builtin_size(0)
92{
93}
94
96{
97 for (unsigned i = 0; i < m_custom_strings.size(); i++)
98 delete [] m_custom_strings[i];
99}
100
102{
103 return (i < m_custom_tab.size() ? m_custom_tab[i]
104 : m_builtin_tab[i - m_custom_tab.size()] );
105}
106
108{
109 drive_settings dest;
114 dest.presets = copy_string(src.presets);
115 m_custom_tab.push_back(dest);
116}
117
118const char * drive_database::copy_string(const char * src)
119{
120 size_t len = strlen(src);
121 char * dest = new char[len+1];
122 memcpy(dest, src, len+1);
123 try {
124 m_custom_strings.push_back(dest);
125 }
126 catch (...) {
127 delete [] dest; throw;
128 }
129 return dest;
130}
131
132
133/// The drive database.
135
136
143
144// Return type of entry
145static dbentry_type get_modelfamily_type(const char * modelfamily)
146{
147 if (modelfamily[0] == 'V' && str_starts_with(modelfamily, "VERSION:"))
148 return DBENTRY_VERSION;
149 else if (modelfamily[0] == 'D' && !strcmp(modelfamily, "DEFAULT"))
150 return DBENTRY_ATA_DEFAULT;
151 else if (modelfamily[0] == 'U' && str_starts_with(modelfamily, "USB:"))
152 return DBENTRY_USB;
153 else
154 return DBENTRY_ATA;
155}
156
157static inline dbentry_type get_dbentry_type(const drive_settings * dbentry)
158{
159 return get_modelfamily_type(dbentry->modelfamily);
160}
161
162// Extract "BRANCH/REV" from "VERSION: ..." string.
163static void parse_version(std::string & dbversion, const char * verstr)
164{
165 static const regular_expression regex(
166 "^VERSION: ([0-9]+\\.[0-9]+)(/([0-9]+) | \\$[^0-9]* ([0-9]+) )"
167 // (1 )( (3 ) (4 ) )
168 );
169 const int nmatch = 1+4;
171 if (!regex.execute(verstr, nmatch, match))
172 return;
173 dbversion.assign(verstr + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
174 int i = (match[3].rm_so >= 0 ? 3 : 4); // "BRANCH/REV" or "BRANCH ... SVN-REV"
175 dbversion += '/';
176 dbversion.append(verstr + match[i].rm_so, match[i].rm_eo - match[i].rm_so);
177}
178
179// Compile regular expression, print message on failure.
180static bool compile(regular_expression & regex, const char *pattern)
181{
182 if (!regex.compile(pattern)) {
183 pout("Internal error: unable to compile regular expression \"%s\": %s\n"
184 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n",
185 pattern, regex.get_errmsg());
186 return false;
187 }
188 return true;
189}
190
191// Compile & match a regular expression.
192static bool match(const char * pattern, const char * str)
193{
194 regular_expression regex;
195 if (!compile(regex, pattern))
196 return false;
197 return regex.full_match(str);
198}
199
200// Searches knowndrives[] for a drive with the given model number and firmware
201// string. If either the drive's model or firmware strings are not set by the
202// manufacturer then values of NULL may be used. Returns the entry of the
203// first match in knowndrives[] or 0 if no match if found.
204static const drive_settings * lookup_drive(const char * model, const char * firmware,
205 std::string * dbversion = nullptr)
206{
207 if (!model)
208 model = "";
209 if (!firmware)
210 firmware = "";
211
212 for (unsigned i = 0; i < knowndrives.size(); i++) {
214 // Get version if requested
215 if (t == DBENTRY_VERSION) {
216 if (dbversion)
217 parse_version(*dbversion, knowndrives[i].modelfamily);
218 continue;
219 }
220
221 // Skip DEFAULT and USB entries
222 if (t != DBENTRY_ATA)
223 continue;
224
225 // Check whether model matches the regular expression in knowndrives[i].
226 if (!match(knowndrives[i].modelregexp, model))
227 continue;
228
229 // Model matches, now check firmware. "" matches always.
230 if (!( !*knowndrives[i].firmwareregexp
231 || match(knowndrives[i].firmwareregexp, firmware)))
232 continue;
233
234 // Found
235 return &knowndrives[i];
236 }
237
238 // Not found
239 return 0;
240}
241
242
243// Parse drive or USB options in preset string, return false on error.
244static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
245 firmwarebug_defs * firmwarebugs, std::string * type)
246{
247 for (int i = 0; ; ) {
248 i += strspn(presets+i, " \t");
249 if (!presets[i])
250 break;
251 char opt, arg[80+1+13]; int len = -1;
252 if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
253 return false;
254 if (opt == 'v' && defs) {
255 // Parse "-v N,format[,name[,HDD|SSD]]"
256 if (!parse_attribute_def(arg, *defs, (firmwarebugs ? PRIOR_DATABASE : PRIOR_DEFAULT)))
257 return false;
258 }
259 else if (opt == 'F' && firmwarebugs) {
261 if (!parse_firmwarebug_def(arg, bug))
262 return false;
263 // Don't set if user specified '-F none'.
264 if (!firmwarebugs->is_set(BUG_NONE))
265 firmwarebugs->set(bug);
266 }
267 else if (opt == 'd' && type) {
268 // TODO: Check valid types
269 *type = arg;
270 }
271 else
272 return false;
273
274 i += len;
275 }
276 return true;
277}
278
279// Parse '-v' options in default preset string, return false on error.
280static inline bool parse_default_presets(const char * presets,
282{
283 return parse_db_presets(presets, &defs, 0, 0);
284}
285
286// Parse '-v' and '-F' options in preset string, return false on error.
287static inline bool parse_presets(const char * presets,
289 firmwarebug_defs & firmwarebugs)
290{
291 return parse_db_presets(presets, &defs, &firmwarebugs, 0);
292}
293
294// Parse '-d' option in preset string, return false on error.
295static inline bool parse_usb_type(const char * presets, std::string & type)
296{
297 return parse_db_presets(presets, 0, 0, &type);
298}
299
300// Parse "USB: [DEVICE] ; [BRIDGE]" string
301static void parse_usb_names(const char * names, usb_dev_info & info)
302{
303 int n1 = -1, n2 = -1, n3 = -1;
304 sscanf(names, "USB: %n%*[^;]%n; %n", &n1, &n2, &n3);
305 if (0 < n1 && n1 < n2)
306 info.usb_device.assign(names+n1, n2-n1);
307 else
308 sscanf(names, "USB: ; %n", &n3);
309 if (0 < n3)
310 info.usb_bridge = names+n3;
311}
312
313// Search drivedb for USB device with vendor:product ID.
314int lookup_usb_device(int vendor_id, int product_id, int bcd_device,
315 usb_dev_info & info, usb_dev_info & info2)
316{
317 // Format strings to match
318 char usb_id_str[16], bcd_dev_str[16];
319 snprintf(usb_id_str, sizeof(usb_id_str), "0x%04x:0x%04x", vendor_id, product_id);
320 if (bcd_device >= 0)
321 snprintf(bcd_dev_str, sizeof(bcd_dev_str), "0x%04x", bcd_device);
322 else
323 bcd_dev_str[0] = 0;
324
325 int found = 0;
326 for (unsigned i = 0; i < knowndrives.size(); i++) {
327 const drive_settings & dbentry = knowndrives[i];
328
329 // Skip drive entries
330 if (get_dbentry_type(&dbentry) != DBENTRY_USB)
331 continue;
332
333 // Check whether USB vendor:product ID matches
334 if (!match(dbentry.modelregexp, usb_id_str))
335 continue;
336
337 // Parse '-d type'
338 usb_dev_info d;
339 if (!parse_usb_type(dbentry.presets, d.usb_type))
340 return 0; // Syntax error
341 parse_usb_names(dbentry.modelfamily, d);
342
343 // If two entries with same vendor:product ID have different
344 // types, use bcd_device (if provided by OS) to select entry.
345 if ( *dbentry.firmwareregexp && *bcd_dev_str
346 && match(dbentry.firmwareregexp, bcd_dev_str)) {
347 // Exact match including bcd_device
348 info = d; found = 1;
349 break;
350 }
351 else if (!found) {
352 // First match without bcd_device
353 info = d; found = 1;
354 }
355 else if (info.usb_type != d.usb_type) {
356 // Another possible match with different type
357 info2 = d; found = 2;
358 break;
359 }
360
361 // Stop search at first matching entry with empty bcd_device
362 if (!*dbentry.firmwareregexp)
363 break;
364 }
365
366 return found;
367}
368
369// Shows one entry of knowndrives[], returns #errors.
370static int showonepreset(const drive_settings * dbentry)
371{
372 // Basic error check
373 if (!( dbentry
374 && dbentry->modelfamily
375 && dbentry->modelregexp && *dbentry->modelregexp
376 && dbentry->firmwareregexp
377 && dbentry->warningmsg
378 && dbentry->presets )) {
379 pout("Invalid drive database entry. Please report\n"
380 "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
381 return 1;
382 }
383
384 dbentry_type type = get_dbentry_type(dbentry);
385 bool usb = (type == DBENTRY_USB);
386
387 // print and check model and firmware regular expressions
388 int errcnt = 0;
389 regular_expression regex;
390 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"),
391 dbentry->modelregexp);
392 if (!compile(regex, dbentry->modelregexp))
393 errcnt++;
394
395 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"),
396 *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
397 if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
398 errcnt++;
399
400 if (!usb) {
401 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
402
403 // if there are any presets, then show them
404 firmwarebug_defs firmwarebugs;
405 bool first_preset = true;
406 if (*dbentry->presets) {
408 if (type == DBENTRY_ATA_DEFAULT) {
409 if (!parse_default_presets(dbentry->presets, defs)) {
410 pout("Syntax error in DEFAULT option string \"%s\"\n", dbentry->presets);
411 errcnt++;
412 }
413 }
414 else {
415 if (!parse_presets(dbentry->presets, defs, firmwarebugs)) {
416 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
417 errcnt++;
418 }
419 }
420
421 for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
422 if (defs[i].priority != PRIOR_DEFAULT || !defs[i].name.empty()) {
423 std::string name = ata_get_smart_attr_name(i, defs);
424 // Use leading zeros instead of spaces so that everything lines up.
425 pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
426 i, name.c_str());
427 // Check max name length suitable for smartctl -A output
428 const unsigned maxlen = 23;
429 if (name.size() > maxlen) {
430 pout("%*s\n", TABLEPRINTWIDTH+6+maxlen, "Error: Attribute name too long ------^");
431 errcnt++;
432 }
433 first_preset = false;
434 }
435 }
436 }
437 if (first_preset)
438 pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
439
440 // describe firmwarefix
441 for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) {
442 if (!firmwarebugs.is_set((firmwarebug_t)b))
443 continue;
444 const char * fixdesc;
445 switch ((firmwarebug_t)b) {
446 case BUG_NOLOGDIR:
447 fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)";
448 break;
449 case BUG_SAMSUNG:
450 fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
451 break;
452 case BUG_SAMSUNG2:
453 fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
454 break;
455 case BUG_SAMSUNG3:
456 fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
457 break;
458 case BUG_XERRORLBA:
459 fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)";
460 break;
461 default:
462 fixdesc = "UNKNOWN"; errcnt++;
463 break;
464 }
465 pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
466 }
467 }
468 else {
469 // Print USB info
470 usb_dev_info info; parse_usb_names(dbentry->modelfamily, info);
471 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Device:",
472 (!info.usb_device.empty() ? info.usb_device.c_str() : "[unknown]"));
473 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Bridge:",
474 (!info.usb_bridge.empty() ? info.usb_bridge.c_str() : "[unknown]"));
475
476 if (*dbentry->presets && !parse_usb_type(dbentry->presets, info.usb_type)) {
477 pout("Syntax error in USB type string \"%s\"\n", dbentry->presets);
478 errcnt++;
479 }
480 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type",
481 (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]"));
482 }
483
484 // Print any special warnings
485 if (*dbentry->warningmsg)
486 pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
487 return errcnt;
488}
489
490// Shows all presets for drives in knowndrives[].
491// Returns #syntax errors.
493{
494 // loop over all entries in the knowndrives[] table, printing them
495 // out in a nice format
496 int errcnt = 0;
497 for (unsigned i = 0; i < knowndrives.size(); i++) {
498 errcnt += showonepreset(&knowndrives[i]);
499 pout("\n");
500 }
501
502 pout("Total number of entries :%5u\n"
503 "Entries read from file(s):%5u\n\n",
505
506 pout("For information about adding a drive to the database see the FAQ on the\n");
507 pout("smartmontools home page: " PACKAGE_URL "\n");
508
509 if (errcnt > 0)
510 pout("\nFound %d syntax error(s) in database.\n"
511 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt);
512 return errcnt;
513}
514
515// Shows all matching presets for a drive in knowndrives[].
516// Returns # matching entries.
517int showmatchingpresets(const char *model, const char *firmware)
518{
519 int cnt = 0;
520 const char * firmwaremsg = (firmware ? firmware : "(any)");
521
522 for (unsigned i = 0; i < knowndrives.size(); i++) {
523 if (!match(knowndrives[i].modelregexp, model))
524 continue;
525 if ( firmware && *knowndrives[i].firmwareregexp
526 && !match(knowndrives[i].firmwareregexp, firmware))
527 continue;
528 // Found
529 if (++cnt == 1)
530 pout("Drive found in smartmontools Database. Drive identity strings:\n"
531 "%-*s %s\n"
532 "%-*s %s\n"
533 "match smartmontools Drive Database entry:\n",
534 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
535 else if (cnt == 2)
536 pout("and match these additional entries:\n");
538 pout("\n");
539 }
540 if (cnt == 0)
541 pout("No presets are defined for this drive. Its identity strings:\n"
542 "MODEL: %s\n"
543 "FIRMWARE: %s\n"
544 "do not match any of the known regular expressions.\n",
545 model, firmwaremsg);
546 return cnt;
547}
548
549// Shows the presets (if any) that are available for the given drive.
551{
552 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
553
554 // get the drive's model/firmware strings
555 ata_format_id_string(model, drive->model, sizeof(model)-1);
556 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
557
558 // and search to see if they match values in the table
559 const drive_settings * dbentry = lookup_drive(model, firmware);
560 if (!dbentry) {
561 // no matches found
562 pout("No presets are defined for this drive. Its identity strings:\n"
563 "MODEL: %s\n"
564 "FIRMWARE: %s\n"
565 "do not match any of the known regular expressions.\n"
566 "Use -P showall to list all known regular expressions.\n",
567 model, firmware);
568 return;
569 }
570
571 // We found a matching drive. Print out all information about it.
572 pout("Drive found in smartmontools Database. Drive identity strings:\n"
573 "%-*s %s\n"
574 "%-*s %s\n"
575 "match smartmontools Drive Database entry:\n",
576 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
577 showonepreset(dbentry);
578}
579
580// Searches drive database and sets preset vendor attribute
581// options in defs and firmwarebugs.
582// Values that have already been set will not be changed.
583// Returns pointer to database entry or nullptr if none found
585 const ata_identify_device * drive, ata_vendor_attr_defs & defs,
586 firmwarebug_defs & firmwarebugs, std::string & dbversion)
587{
588 // get the drive's model/firmware strings
589 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
590 ata_format_id_string(model, drive->model, sizeof(model)-1);
591 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
592
593 // Look up the drive in knowndrives[].
594 const drive_settings * dbentry = lookup_drive(model, firmware, &dbversion);
595 if (!dbentry)
596 return 0;
597
598 if (*dbentry->presets) {
599 // Apply presets
600 if (!parse_presets(dbentry->presets, defs, firmwarebugs))
601 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
602 }
603 return dbentry;
604}
605
606
607/////////////////////////////////////////////////////////////////////////////
608// Parser for drive database files
609
610// Abstract pointer to read file input.
611// Operations supported: c = *p; c = p[1]; ++p;
613{
614public:
615 explicit stdin_iterator(FILE * f)
616 : m_f(f), m_next(0) { get(); get(); }
617
619 { get(); return *this; }
620
621 char operator*() const
622 { return m_c; }
623
624 char operator[](int i) const
625 {
626 if (i != 1)
627 fail();
628 return m_next;
629 }
630
631private:
632 FILE * m_f;
633 char m_c, m_next;
634 void get();
635 void fail() const;
636};
637
639{
640 m_c = m_next;
641 int ch = getc(m_f);
642 m_next = (ch != EOF ? ch : 0);
643}
644
646{
647 throw std::runtime_error("stdin_iterator: wrong usage");
648}
649
650
651// Use above as parser input 'pointer'. Can easily be changed later
652// to e.g. 'const char *' if above is too slow.
654
655// Skip whitespace and comments.
656static parse_ptr skip_white(parse_ptr src, const char * path, int & line)
657{
658 for ( ; ; ++src) switch (*src) {
659 case ' ': case '\t':
660 continue;
661
662 case '\n':
663 ++line;
664 continue;
665
666 case '/':
667 switch (src[1]) {
668 case '/':
669 // skip '// comment'
670 ++src; ++src;
671 while (*src && *src != '\n')
672 ++src;
673 if (*src)
674 ++line;
675 break;
676 case '*':
677 // skip '/* comment */'
678 ++src; ++src;
679 for (;;) {
680 if (!*src) {
681 pout("%s(%d): Missing '*/'\n", path, line);
682 return src;
683 }
684 char c = *src; ++src;
685 if (c == '\n')
686 ++line;
687 else if (c == '*' && *src == '/')
688 break;
689 }
690 break;
691 default:
692 return src;
693 }
694 continue;
695
696 default:
697 return src;
698 }
699}
700
701// Info about a token.
703{
704 char type;
705 int line;
706 std::string value;
707
708 token_info() : type(0), line(0) { }
709};
710
711// Get next token.
712static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line)
713{
714 src = skip_white(src, path, line);
715 switch (*src) {
716 case '{': case '}': case ',':
717 // Simple token
718 token.type = *src; token.line = line;
719 ++src;
720 break;
721
722 case '"':
723 // String constant
724 token.type = '"'; token.line = line;
725 token.value = "";
726 do {
727 for (++src; *src != '"'; ++src) {
728 char c = *src;
729 if (!c || c == '\n' || (c == '\\' && !src[1])) {
730 pout("%s(%d): Missing terminating '\"'\n", path, line);
731 token.type = '?'; token.line = line;
732 return src;
733 }
734 if (c == '\\') {
735 c = *++src;
736 switch (c) {
737 case 'n' : c = '\n'; break;
738 case '\n': ++line; break;
739 case '\\': case '"': break;
740 default:
741 pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c);
742 token.type = '?'; token.line = line;
743 continue;
744 }
745 }
746 token.value += c;
747 }
748 // Lookahead to detect string constant concatenation
749 src = skip_white(++src, path, line);
750 } while (*src == '"');
751 break;
752
753 case 0:
754 // EOF
755 token.type = 0; token.line = line;
756 break;
757
758 default:
759 pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src);
760 token.type = '?'; token.line = line;
761 while (*src && *src != '\n')
762 ++src;
763 break;
764 }
765
766 return src;
767}
768
769// Parse drive database from abstract input pointer.
770static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path)
771{
772 int state = 0, field = 0;
773 std::string values[5];
774 bool ok = true;
775
776 token_info token; int line = 1;
777 src = get_token(src, token, path, line);
778 for (;;) {
779 // EOF is ok after '}', trailing ',' is also allowed.
780 if (!token.type && (state == 0 || state == 4))
781 break;
782
783 // Check expected token
784 const char expect[] = "{\",},";
785 if (token.type != expect[state]) {
786 if (token.type != '?')
787 pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]);
788 ok = false;
789 // Skip to next entry
790 while (token.type && token.type != '{')
791 src = get_token(src, token, path, line);
792 state = 0;
793 if (token.type)
794 continue;
795 break;
796 }
797
798 // Interpret parser state
799 switch (state) {
800 case 0: // ... ^{...}
801 state = 1; field = 0;
802 break;
803 case 1: // {... ^"..." ...}
804 switch (field) {
805 case 1: case 2:
806 if (!token.value.empty()) {
807 regular_expression regex;
808 if (!regex.compile(token.value.c_str())) {
809 pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg());
810 ok = false;
811 }
812 }
813 else if (field == 1) {
814 pout("%s(%d): Missing regular expression for drive model\n", path, token.line);
815 ok = false;
816 }
817 break;
818 case 4:
819 if (!token.value.empty()) {
820 // Syntax check
821 switch (get_modelfamily_type(values[0].c_str())) {
822 case DBENTRY_ATA_DEFAULT: {
824 if (!parse_default_presets(token.value.c_str(), defs)) {
825 pout("%s(%d): Syntax error in DEFAULT option string\n", path, token.line);
826 ok = false;
827 }
828 } break;
829 default: { // DBENTRY_ATA
831 if (!parse_presets(token.value.c_str(), defs, fix)) {
832 pout("%s(%d): Syntax error in preset option string\n", path, token.line);
833 ok = false;
834 }
835 } break;
836 case DBENTRY_USB: {
837 std::string type;
838 if (!parse_usb_type(token.value.c_str(), type)) {
839 pout("%s(%d): Syntax error in USB type string\n", path, token.line);
840 ok = false;
841 }
842 } break;
843 }
844 }
845 break;
846 }
847 values[field] = token.value;
848 state = (++field < 5 ? 2 : 3);
849 break;
850 case 2: // {... "..."^, ...}
851 state = 1;
852 break;
853 case 3: // {...^}, ...
854 {
856 entry.modelfamily = values[0].c_str();
857 entry.modelregexp = values[1].c_str();
858 entry.firmwareregexp = values[2].c_str();
859 entry.warningmsg = values[3].c_str();
860 entry.presets = values[4].c_str();
861 db.push_back(entry);
862 }
863 state = 4;
864 break;
865 case 4: // {...}^, ...
866 state = 0;
867 break;
868 default:
869 pout("Bad state %d\n", state);
870 return false;
871 }
872 src = get_token(src, token, path, line);
873 }
874 return ok;
875}
876
877// Read drive database from file.
878bool read_drive_database(const char * path)
879{
880 stdio_file f(path, "r"
881#ifdef __CYGWIN__ // Allow files with '\r\n'.
882 "t"
883#endif
884 );
885 if (!f) {
886 pout("%s: cannot open drive database file\n", path);
887 return false;
888 }
889
891}
892
893// Get path for additional database file
895{
896#ifndef _WIN32
897 return SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h";
898#else
899 static std::string path = get_exe_dir() + "/drivedb-add.h";
900 return path.c_str();
901#endif
902}
903
904#ifdef SMARTMONTOOLS_DRIVEDBDIR
905
906// Get path for default database file
907const char * get_drivedb_path_default()
908{
909#ifndef _WIN32
910 return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
911#else
912 static std::string path = get_exe_dir() + "/drivedb.h";
913 return path.c_str();
914#endif
915}
916
917#endif
918
919// Read drive databases from standard places.
921{
922 // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
923 const char * db1 = get_drivedb_path_add();
924 if (!access(db1, 0)) {
925 if (!read_drive_database(db1))
926 return false;
927 }
928
929#ifdef SMARTMONTOOLS_DRIVEDBDIR
930 // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h
931 const char * db2 = get_drivedb_path_default();
932 if (!access(db2, 0)) {
933 if (!read_drive_database(db2))
934 return false;
935 }
936 else
937#endif
938 {
939 // Append builtin table.
941 }
942
943 return true;
944}
945
947
948// Initialize default_attr_defs.
950{
951 // Lookup default entry
952 const drive_settings * entry = 0;
953 for (unsigned i = 0; i < knowndrives.size(); i++) {
955 continue;
956 entry = &knowndrives[i];
957 break;
958 }
959
960 if (!entry) {
961 // Fall back to builtin database
962 for (unsigned i = 0; i < builtin_knowndrives_size; i++) {
964 continue;
966 break;
967 }
968
969 if (!entry)
970 throw std::logic_error("DEFAULT entry missing in builtin drive database");
971
972 pout("Warning: DEFAULT entry missing in drive database file(s)\n");
973 }
974
976 pout("Syntax error in DEFAULT drive database entry\n");
977 return false;
978 }
979
980 return true;
981}
982
983// Init default db entry and optionally read drive databases from standard places.
984bool init_drive_database(bool use_default_db)
985{
986 if (use_default_db && !read_default_drive_databases())
987 return false;
988
989 return init_default_attr_defs();
990}
991
992// Get vendor attribute options from default db entry.
994{
995 return default_attr_defs;
996}
bool parse_attribute_def(const char *opt, ata_vendor_attr_defs &defs, ata_vendor_def_prior priority)
Definition: atacmds.cpp:149
bool parse_firmwarebug_def(const char *opt, firmwarebug_defs &firmwarebugs)
Definition: atacmds.cpp:277
std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs &defs, int rpm)
Definition: atacmds.cpp:2127
void ata_format_id_string(char *out, const unsigned char *in, int n)
Definition: atacmds.cpp:762
#define MAX_ATTRIBUTE_NUM
Definition: atacmds.h:945
@ PRIOR_DATABASE
Definition: atacmds.h:643
@ PRIOR_DEFAULT
Definition: atacmds.h:642
firmwarebug_t
Definition: atacmds.h:712
@ BUG_SAMSUNG3
Definition: atacmds.h:717
@ BUG_NOLOGDIR
Definition: atacmds.h:714
@ BUG_XERRORLBA
Definition: atacmds.h:718
@ BUG_SAMSUNG
Definition: atacmds.h:715
@ BUG_NONE
Definition: atacmds.h:713
@ BUG_SAMSUNG2
Definition: atacmds.h:716
Drive database class.
Definition: knowndrives.cpp:53
const char * copy_string(const char *str)
unsigned size() const
Get total number of entries.
Definition: knowndrives.cpp:60
unsigned m_builtin_size
Definition: knowndrives.cpp:79
void push_back(const drive_settings &src)
Append new custom entry.
unsigned custom_size() const
Get number of custom entries.
Definition: knowndrives.cpp:64
const drive_settings * m_builtin_tab
Definition: knowndrives.cpp:78
std::vector< char * > m_custom_strings
Definition: knowndrives.cpp:82
void append(const drive_settings *builtin_tab, unsigned builtin_size)
Append builtin table.
Definition: knowndrives.cpp:74
drive_database(const drive_database &)
void operator=(const drive_database &)
std::vector< drive_settings > m_custom_tab
Definition: knowndrives.cpp:81
const drive_settings & operator[](unsigned i)
Array access.
void set(firmwarebug_t bug)
Definition: atacmds.h:731
bool is_set(firmwarebug_t bug) const
Definition: atacmds.h:728
Wrapper class for POSIX regex(3) or std::regex Supports copy & assignment and is compatible with STL ...
Definition: utility.h:222
bool full_match(const char *str) const
Return true if full string matches pattern.
Definition: utility.cpp:593
regmatch_t match_range
Definition: utility.h:262
const char * get_errmsg() const
Get error message from last compile().
Definition: utility.h:249
bool compile(const char *pattern)
Set and compile new pattern, return false on error.
Definition: utility.cpp:547
bool execute(const char *str, unsigned nmatch, match_range *pmatch) const
Return true if substring matches pattern, fill match_range array.
Definition: utility.cpp:604
char operator*() const
void fail() const
stdin_iterator & operator++()
stdin_iterator(FILE *f)
char operator[](int i) const
Wrapper class for FILE *.
Definition: utility.h:163
const drive_settings * lookup_drive_apply_presets(const ata_identify_device *drive, ata_vendor_attr_defs &defs, firmwarebug_defs &firmwarebugs, std::string &dbversion)
static bool parse_presets(const char *presets, ata_vendor_attr_defs &defs, firmwarebug_defs &firmwarebugs)
bool init_drive_database(bool use_default_db)
const drive_settings builtin_knowndrives[]
Definition: knowndrives.cpp:42
const char * get_drivedb_path_add()
static bool match(const char *pattern, const char *str)
dbentry_type
@ DBENTRY_ATA
@ DBENTRY_VERSION
@ DBENTRY_USB
@ DBENTRY_ATA_DEFAULT
static const drive_settings * lookup_drive(const char *model, const char *firmware, std::string *dbversion=nullptr)
static bool parse_db_presets(const char *presets, ata_vendor_attr_defs *defs, firmwarebug_defs *firmwarebugs, std::string *type)
static dbentry_type get_dbentry_type(const drive_settings *dbentry)
void show_presets(const ata_identify_device *drive)
bool read_drive_database(const char *path)
static bool parse_drive_database(parse_ptr src, drive_database &db, const char *path)
#define FIRMWARE_STRING_LENGTH
Definition: knowndrives.cpp:32
const char * knowndrives_cpp_cvsid
Definition: knowndrives.cpp:28
static parse_ptr get_token(parse_ptr src, token_info &token, const char *path, int &line)
const unsigned builtin_knowndrives_size
Definition: knowndrives.cpp:46
static int showonepreset(const drive_settings *dbentry)
static void parse_version(std::string &dbversion, const char *verstr)
stdin_iterator parse_ptr
static bool init_default_attr_defs()
static void parse_usb_names(const char *names, usb_dev_info &info)
static drive_database knowndrives
The drive database.
static bool parse_usb_type(const char *presets, std::string &type)
int lookup_usb_device(int vendor_id, int product_id, int bcd_device, usb_dev_info &info, usb_dev_info &info2)
static bool read_default_drive_databases()
static parse_ptr skip_white(parse_ptr src, const char *path, int &line)
const ata_vendor_attr_defs & get_default_attr_defs()
static bool compile(regular_expression &regex, const char *pattern)
static bool parse_default_presets(const char *presets, ata_vendor_attr_defs &defs)
int showmatchingpresets(const char *model, const char *firmware)
int showallpresets()
#define TABLEPRINTWIDTH
Definition: knowndrives.cpp:33
#define MODEL_STRING_LENGTH
Definition: knowndrives.cpp:31
static ata_vendor_attr_defs default_attr_defs
static dbentry_type get_modelfamily_type(const char *modelfamily)
#define KNOWNDRIVES_H_CVSID
Definition: knowndrives.h:15
u8 b[12]
Definition: megaraid.h:17
union @43 entry
std::string get_exe_dir()
Definition: os_win32.cpp:4839
void pout(const char *fmt,...)
Definition: smartd.cpp:1338
unsigned char model[40]
Definition: atacmds.h:120
unsigned char fw_rev[8]
Definition: atacmds.h:119
const char * modelregexp
Definition: knowndrives.h:20
const char * presets
Definition: knowndrives.h:23
const char * modelfamily
Definition: knowndrives.h:19
const char * warningmsg
Definition: knowndrives.h:22
const char * firmwareregexp
Definition: knowndrives.h:21
std::string value
std::string usb_device
Definition: knowndrives.h:29
std::string usb_type
Definition: knowndrives.h:31
std::string usb_bridge
Definition: knowndrives.h:30
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:52