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