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