]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - knowndrives.cpp
Stop passing arguments to dh_installinit
[mirror_smartmontools-debian.git] / knowndrives.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * knowndrives.cpp
832b75ed 3 *
6b80b4d2 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
cfbba5b9 6 * Copyright (C) 2003-11 Philip Williams, Bruce Allen
6b80b4d2 7 * Copyright (C) 2008-16 Christian Franke
832b75ed
GG
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
ee38a438 15 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
832b75ed
GG
16 *
17 */
18
19#include "config.h"
20#include "int64.h"
21#include <stdio.h>
22#include "atacmds.h"
832b75ed 23#include "knowndrives.h"
2127e193 24#include "utility.h"
832b75ed 25
2127e193
GI
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
6b80b4d2 35const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 4208 2016-01-22 19:45:35Z chrfranke $"
a23d5117 36 KNOWNDRIVES_H_CVSID;
832b75ed
GG
37
38#define MODEL_STRING_LENGTH 40
39#define FIRMWARE_STRING_LENGTH 8
40#define TABLEPRINTWIDTH 19
41
832b75ed 42
a23d5117
GI
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.
49const drive_settings builtin_knowndrives[] = {
50#include "drivedb.h"
832b75ed
GG
51};
52
6b80b4d2
JD
53const unsigned builtin_knowndrives_size =
54 sizeof(builtin_knowndrives) / sizeof(builtin_knowndrives[0]);
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{
ee38a438
GI
127 size_t len = strlen(src);
128 char * dest = new char[len+1];
129 memcpy(dest, src, len+1);
2127e193
GI
130 try {
131 m_custom_strings.push_back(dest);
132 }
133 catch (...) {
134 delete [] dest; throw;
135 }
ee38a438 136 return dest;
2127e193
GI
137}
138
139
140/// The drive database.
141static drive_database knowndrives;
142
143
6b80b4d2
JD
144enum dbentry_type {
145 DBENTRY_ATA_DEFAULT,
146 DBENTRY_ATA,
147 DBENTRY_USB
148};
149
150// Return type of entry
151static dbentry_type get_modelfamily_type(const char * modelfamily)
e9583e0c 152{
6b80b4d2
JD
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;
e9583e0c
GI
159}
160
6b80b4d2 161static inline dbentry_type get_dbentry_type(const drive_settings * dbentry)
e9583e0c 162{
6b80b4d2 163 return get_modelfamily_type(dbentry->modelfamily);
e9583e0c
GI
164}
165
2127e193
GI
166// Compile regular expression, print message on failure.
167static 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.
179static 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
832b75ed
GG
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
2127e193
GI
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.
cfbba5b9 191static const drive_settings * lookup_drive(const char * model, const char * firmware)
832b75ed 192{
2127e193
GI
193 if (!model)
194 model = "";
195 if (!firmware)
196 firmware = "";
832b75ed 197
2127e193 198 for (unsigned i = 0; i < knowndrives.size(); i++) {
6b80b4d2
JD
199 // Skip DEFAULT and USB entries
200 if (get_dbentry_type(&knowndrives[i]) != DBENTRY_ATA)
e9583e0c
GI
201 continue;
202
2127e193
GI
203 // Check whether model matches the regular expression in knowndrives[i].
204 if (!match(knowndrives[i].modelregexp, model))
205 continue;
832b75ed 206
2127e193
GI
207 // Model matches, now check firmware. "" matches always.
208 if (!( !*knowndrives[i].firmwareregexp
209 || match(knowndrives[i].firmwareregexp, firmware)))
210 continue;
832b75ed 211
2127e193
GI
212 // Found
213 return &knowndrives[i];
832b75ed
GG
214 }
215
2127e193
GI
216 // Not found
217 return 0;
832b75ed
GG
218}
219
e9583e0c
GI
220
221// Parse drive or USB options in preset string, return false on error.
222static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
ee38a438 223 firmwarebug_defs * firmwarebugs, std::string * type)
2127e193
GI
224{
225 for (int i = 0; ; ) {
226 i += strspn(presets+i, " \t");
227 if (!presets[i])
228 break;
a7e8ffec
GI
229 char opt, arg[80+1+13]; int len = -1;
230 if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
2127e193 231 return false;
e9583e0c 232 if (opt == 'v' && defs) {
6b80b4d2
JD
233 // Parse "-v N,format[,name[,HDD|SSD]]"
234 if (!parse_attribute_def(arg, *defs, (firmwarebugs ? PRIOR_DATABASE : PRIOR_DEFAULT)))
2127e193 235 return false;
2127e193 236 }
ee38a438
GI
237 else if (opt == 'F' && firmwarebugs) {
238 firmwarebug_defs bug;
239 if (!parse_firmwarebug_def(arg, bug))
2127e193 240 return false;
ee38a438
GI
241 // Don't set if user specified '-F none'.
242 if (!firmwarebugs->is_set(BUG_NONE))
243 firmwarebugs->set(bug);
e9583e0c
GI
244 }
245 else if (opt == 'd' && type) {
246 // TODO: Check valid types
247 *type = arg;
2127e193
GI
248 }
249 else
250 return false;
832b75ed 251
2127e193
GI
252 i += len;
253 }
254 return true;
255}
256
6b80b4d2
JD
257// Parse '-v' options in default preset string, return false on error.
258static 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
e9583e0c
GI
264// Parse '-v' and '-F' options in preset string, return false on error.
265static inline bool parse_presets(const char * presets,
266 ata_vendor_attr_defs & defs,
ee38a438 267 firmwarebug_defs & firmwarebugs)
e9583e0c 268{
ee38a438 269 return parse_db_presets(presets, &defs, &firmwarebugs, 0);
e9583e0c
GI
270}
271
272// Parse '-d' option in preset string, return false on error.
273static 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
279static 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.
292int 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;
e9583e0c
GI
304 for (unsigned i = 0; i < knowndrives.size(); i++) {
305 const drive_settings & dbentry = knowndrives[i];
306
307 // Skip drive entries
6b80b4d2 308 if (get_dbentry_type(&dbentry) != DBENTRY_USB)
e9583e0c
GI
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.
cfbba5b9
GI
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
e9583e0c 331 info = d; found = 1;
e9583e0c 332 }
cfbba5b9
GI
333 else if (info.usb_type != d.usb_type) {
334 // Another possible match with different type
e9583e0c
GI
335 info2 = d; found = 2;
336 break;
337 }
cfbba5b9
GI
338
339 // Stop search at first matching entry with empty bcd_device
340 if (!*dbentry.firmwareregexp)
341 break;
e9583e0c
GI
342 }
343
344 return found;
345}
346
2127e193
GI
347// Shows one entry of knowndrives[], returns #errors.
348static int showonepreset(const drive_settings * dbentry)
349{
832b75ed 350 // Basic error check
2127e193
GI
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"
832b75ed 358 "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
2127e193 359 return 1;
832b75ed 360 }
e9583e0c 361
6b80b4d2
JD
362 dbentry_type type = get_dbentry_type(dbentry);
363 bool usb = (type == DBENTRY_USB);
e9583e0c 364
2127e193
GI
365 // print and check model and firmware regular expressions
366 int errcnt = 0;
367 regular_expression regex;
e9583e0c
GI
368 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"),
369 dbentry->modelregexp);
2127e193
GI
370 if (!compile(regex, dbentry->modelregexp))
371 errcnt++;
832b75ed 372
e9583e0c
GI
373 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"),
374 *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
2127e193
GI
375 if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
376 errcnt++;
377
e9583e0c
GI
378 if (!usb) {
379 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
380
381 // if there are any presets, then show them
ee38a438 382 firmwarebug_defs firmwarebugs;
e9583e0c
GI
383 bool first_preset = true;
384 if (*dbentry->presets) {
385 ata_vendor_attr_defs defs;
6b80b4d2
JD
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 }
e9583e0c 391 }
6b80b4d2
JD
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
e9583e0c 399 for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
6b80b4d2 400 if (defs[i].priority != PRIOR_DEFAULT || !defs[i].name.empty()) {
d008864d 401 std::string name = ata_get_smart_attr_name(i, defs);
e9583e0c
GI
402 // Use leading zeros instead of spaces so that everything lines up.
403 pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
d008864d
GI
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 }
e9583e0c
GI
411 first_preset = false;
412 }
413 }
2127e193 414 }
e9583e0c
GI
415 if (first_preset)
416 pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
417
418 // describe firmwarefix
ee38a438
GI
419 for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) {
420 if (!firmwarebugs.is_set((firmwarebug_t)b))
421 continue;
e9583e0c 422 const char * fixdesc;
ee38a438
GI
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:
e9583e0c
GI
428 fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
429 break;
ee38a438 430 case BUG_SAMSUNG2:
e9583e0c
GI
431 fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
432 break;
ee38a438 433 case BUG_SAMSUNG3:
e9583e0c
GI
434 fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
435 break;
ee38a438
GI
436 case BUG_XERRORLBA:
437 fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)";
438 break;
e9583e0c
GI
439 default:
440 fixdesc = "UNKNOWN"; errcnt++;
441 break;
2127e193 442 }
e9583e0c 443 pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
2127e193 444 }
832b75ed 445 }
e9583e0c
GI
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++;
2127e193 457 }
e9583e0c
GI
458 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type",
459 (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]"));
832b75ed 460 }
2127e193 461
832b75ed 462 // Print any special warnings
2127e193
GI
463 if (*dbentry->warningmsg)
464 pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
465 return errcnt;
832b75ed
GG
466}
467
468// Shows all presets for drives in knowndrives[].
2127e193
GI
469// Returns #syntax errors.
470int showallpresets()
471{
832b75ed
GG
472 // loop over all entries in the knowndrives[] table, printing them
473 // out in a nice format
2127e193
GI
474 int errcnt = 0;
475 for (unsigned i = 0; i < knowndrives.size(); i++) {
476 errcnt += showonepreset(&knowndrives[i]);
832b75ed
GG
477 pout("\n");
478 }
479
2127e193
GI
480 pout("Total number of entries :%5u\n"
481 "Entries read from file(s):%5u\n\n",
482 knowndrives.size(), knowndrives.custom_size());
483
832b75ed
GG
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");
2127e193
GI
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;
832b75ed
GG
491}
492
493// Shows all matching presets for a drive in knowndrives[].
494// Returns # matching entries.
2127e193
GI
495int showmatchingpresets(const char *model, const char *firmware)
496{
832b75ed
GG
497 int cnt = 0;
498 const char * firmwaremsg = (firmware ? firmware : "(any)");
832b75ed 499
2127e193
GI
500 for (unsigned i = 0; i < knowndrives.size(); i++) {
501 if (!match(knowndrives[i].modelregexp, model))
832b75ed 502 continue;
2127e193
GI
503 if ( firmware && *knowndrives[i].firmwareregexp
504 && !match(knowndrives[i].firmwareregexp, firmware))
832b75ed 505 continue;
2127e193 506 // Found
832b75ed
GG
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 }
832b75ed
GG
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.
cfbba5b9 528void show_presets(const ata_identify_device * drive)
2127e193 529{
832b75ed
GG
530 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
531
532 // get the drive's model/firmware strings
cfbba5b9
GI
533 ata_format_id_string(model, drive->model, sizeof(model)-1);
534 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
535
832b75ed 536 // and search to see if they match values in the table
2127e193
GI
537 const drive_settings * dbentry = lookup_drive(model, firmware);
538 if (!dbentry) {
832b75ed
GG
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);
2127e193 555 showonepreset(dbentry);
832b75ed
GG
556}
557
cfbba5b9 558// Searches drive database and sets preset vendor attribute
ee38a438 559// options in defs and firmwarebugs.
cfbba5b9
GI
560// Values that have already been set will not be changed.
561// Returns pointer to database entry or nullptr if none found
562const drive_settings * lookup_drive_apply_presets(
563 const ata_identify_device * drive, ata_vendor_attr_defs & defs,
ee38a438 564 firmwarebug_defs & firmwarebugs)
2127e193 565{
832b75ed 566 // get the drive's model/firmware strings
2127e193 567 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
cfbba5b9
GI
568 ata_format_id_string(model, drive->model, sizeof(model)-1);
569 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
570
832b75ed 571 // Look up the drive in knowndrives[].
2127e193
GI
572 const drive_settings * dbentry = lookup_drive(model, firmware);
573 if (!dbentry)
cfbba5b9 574 return 0;
2127e193
GI
575
576 if (*dbentry->presets) {
577 // Apply presets
ee38a438 578 if (!parse_presets(dbentry->presets, defs, firmwarebugs))
2127e193
GI
579 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
580 }
cfbba5b9 581 return dbentry;
2127e193
GI
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;
590class stdin_iterator
591{
592public:
593 explicit stdin_iterator(FILE * f)
6b80b4d2 594 : m_f(f), m_next(0) { get(); get(); }
2127e193
GI
595
596 stdin_iterator & operator++()
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
609private:
610 FILE * m_f;
611 char m_c, m_next;
612 void get();
613 void fail() const;
614};
615
616void stdin_iterator::get()
617{
618 m_c = m_next;
619 int ch = getc(m_f);
620 m_next = (ch != EOF ? ch : 0);
621}
622
623void stdin_iterator::fail() const
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.
631typedef stdin_iterator parse_ptr;
632
633// Skip whitespace and comments.
634static 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;
832b75ed 671 }
2127e193
GI
672 continue;
673
674 default:
675 return src;
676 }
677}
678
679// Info about a token.
680struct token_info
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.
690static 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.
748static 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;
832b75ed 774 }
2127e193
GI
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()) {
6b80b4d2
JD
798 // Syntax check
799 switch (get_modelfamily_type(values[0].c_str())) {
800 case DBENTRY_ATA_DEFAULT: {
801 ata_vendor_attr_defs defs;
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
808 ata_vendor_attr_defs defs; firmwarebug_defs fix;
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;
2127e193
GI
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);
832b75ed 851 }
2127e193
GI
852 return ok;
853}
854
855// Read drive database from file.
856bool read_drive_database(const char * path)
857{
a23d5117
GI
858 stdio_file f(path, "r"
859#ifdef __CYGWIN__ // Allow files with '\r\n'.
860 "t"
861#endif
862 );
2127e193
GI
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
e9583e0c
GI
871// Get path for additional database file
872const char * get_drivedb_path_add()
2127e193
GI
873{
874#ifndef _WIN32
e9583e0c
GI
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
885const char * get_drivedb_path_default()
886{
887#ifndef _WIN32
888 return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
2127e193 889#else
e9583e0c
GI
890 static std::string path = get_exe_dir() + "/drivedb.h";
891 return path.c_str();
892#endif
893}
894
2127e193 895#endif
e9583e0c
GI
896
897// Read drive databases from standard places.
6b80b4d2 898static bool read_default_drive_databases()
e9583e0c
GI
899{
900 // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
901 const char * db1 = get_drivedb_path_add();
2127e193
GI
902 if (!access(db1, 0)) {
903 if (!read_drive_database(db1))
904 return false;
905 }
906
907#ifdef SMARTMONTOOLS_DRIVEDBDIR
e9583e0c
GI
908 // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h
909 const char * db2 = get_drivedb_path_default();
2127e193
GI
910 if (!access(db2, 0)) {
911 if (!read_drive_database(db2))
912 return false;
913 }
914 else
915#endif
916 {
917 // Append builtin table.
6b80b4d2
JD
918 knowndrives.append(builtin_knowndrives, builtin_knowndrives_size);
919 }
920
921 return true;
922}
923
924static ata_vendor_attr_defs default_attr_defs;
925
926// Initialize default_attr_defs.
927static bool init_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;
2127e193
GI
956 }
957
958 return true;
832b75ed 959}
6b80b4d2
JD
960
961// Init default db entry and optionally read drive databases from standard places.
962bool 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.
971const ata_vendor_attr_defs & get_default_attr_defs()
972{
973 return default_attr_defs;
974}