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