]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - knowndrives.cpp
Fixed example path in man pages
[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 *
2127e193
GI
7 * Copyright (C) 2003-9 Philip Williams, Bruce Allen
8 * Copyright (C) 2008-9 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
GG
25#include "extern.h"
26#include "knowndrives.h"
2127e193 27#include "utility.h"
832b75ed 28
2127e193
GI
29#ifdef HAVE_UNISTD_H
30#include <unistd.h>
31#endif
32#ifdef _WIN32
33#include <io.h> // access()
34#endif
35
36#include <stdexcept>
37
a23d5117
GI
38const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3004 2009-12-19 19:39:12Z chrfranke $"
39 KNOWNDRIVES_H_CVSID;
832b75ed
GG
40
41#define MODEL_STRING_LENGTH 40
42#define FIRMWARE_STRING_LENGTH 8
43#define TABLEPRINTWIDTH 19
44
832b75ed 45
a23d5117
GI
46// Builtin table of known drives.
47// Used as a default if not read from
48// "/usr/{,/local}share/smartmontools/drivedb.h"
49// or any other file specified by '-B' option,
50// see read_default_drive_databases() below.
51// The drive_settings structure is described in drivedb.h.
52const drive_settings builtin_knowndrives[] = {
53#include "drivedb.h"
832b75ed
GG
54};
55
2127e193
GI
56
57/// Drive database class. Stores custom entries read from file.
58/// Provides transparent access to concatenation of custom and
59/// default table.
60class drive_database
61{
62public:
63 drive_database();
64
65 ~drive_database();
66
67 /// Get total number of entries.
68 unsigned size() const
69 { return m_custom_tab.size() + m_builtin_size; }
70
71 /// Get number of custom entries.
72 unsigned custom_size() const
73 { return m_custom_tab.size(); }
74
75 /// Array access.
76 const drive_settings & operator[](unsigned i);
77
78 /// Append new custom entry.
79 void push_back(const drive_settings & src);
80
81 /// Append builtin table.
82 void append(const drive_settings * builtin_tab, unsigned builtin_size)
83 { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; }
84
85private:
86 const drive_settings * m_builtin_tab;
87 unsigned m_builtin_size;
88
89 std::vector<drive_settings> m_custom_tab;
90 std::vector<char *> m_custom_strings;
91
92 const char * copy_string(const char * str);
93
94 drive_database(const drive_database &);
95 void operator=(const drive_database &);
96};
97
98drive_database::drive_database()
99: m_builtin_tab(0), m_builtin_size(0)
100{
101}
102
103drive_database::~drive_database()
104{
105 for (unsigned i = 0; i < m_custom_strings.size(); i++)
106 delete [] m_custom_strings[i];
107}
108
109const drive_settings & drive_database::operator[](unsigned i)
110{
111 return (i < m_custom_tab.size() ? m_custom_tab[i]
112 : m_builtin_tab[i - m_custom_tab.size()] );
113}
114
115void drive_database::push_back(const drive_settings & src)
116{
117 drive_settings dest;
118 dest.modelfamily = copy_string(src.modelfamily);
119 dest.modelregexp = copy_string(src.modelregexp);
120 dest.firmwareregexp = copy_string(src.firmwareregexp);
121 dest.warningmsg = copy_string(src.warningmsg);
122 dest.presets = copy_string(src.presets);
123 m_custom_tab.push_back(dest);
124}
125
126const char * drive_database::copy_string(const char * src)
127{
128 char * dest = new char[strlen(src)+1];
129 try {
130 m_custom_strings.push_back(dest);
131 }
132 catch (...) {
133 delete [] dest; throw;
134 }
135 return strcpy(dest, src);
136}
137
138
139/// The drive database.
140static drive_database knowndrives;
141
142
143// Compile regular expression, print message on failure.
144static bool compile(regular_expression & regex, const char *pattern)
145{
146 if (!regex.compile(pattern, REG_EXTENDED)) {
147 pout("Internal error: unable to compile regular expression \"%s\": %s\n"
148 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n",
149 pattern, regex.get_errmsg());
150 return false;
151 }
152 return true;
153}
154
155// Compile & match a regular expression.
156static bool match(const char * pattern, const char * str)
157{
158 regular_expression regex;
159 if (!compile(regex, pattern))
160 return false;
161 return regex.full_match(str);
162}
163
832b75ed
GG
164// Searches knowndrives[] for a drive with the given model number and firmware
165// string. If either the drive's model or firmware strings are not set by the
2127e193
GI
166// manufacturer then values of NULL may be used. Returns the entry of the
167// first match in knowndrives[] or 0 if no match if found.
168const drive_settings * lookup_drive(const char * model, const char * firmware)
832b75ed 169{
2127e193
GI
170 if (!model)
171 model = "";
172 if (!firmware)
173 firmware = "";
832b75ed 174
2127e193
GI
175 for (unsigned i = 0; i < knowndrives.size(); i++) {
176 // Check whether model matches the regular expression in knowndrives[i].
177 if (!match(knowndrives[i].modelregexp, model))
178 continue;
832b75ed 179
2127e193
GI
180 // Model matches, now check firmware. "" matches always.
181 if (!( !*knowndrives[i].firmwareregexp
182 || match(knowndrives[i].firmwareregexp, firmware)))
183 continue;
832b75ed 184
2127e193
GI
185 // Found
186 return &knowndrives[i];
832b75ed
GG
187 }
188
2127e193
GI
189 // Not found
190 return 0;
832b75ed
GG
191}
192
2127e193 193// Parse '-v' and '-F' options in preset string, return false on error.
bed94269
GI
194static bool parse_presets(const char * presets, ata_vendor_attr_defs & defs,
195 unsigned char & fix_firmwarebug)
2127e193
GI
196{
197 for (int i = 0; ; ) {
198 i += strspn(presets+i, " \t");
199 if (!presets[i])
200 break;
201 char opt, arg[40+1+13]; int len = -1;
202 if (!(sscanf(presets+i, "-%c %40[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
203 return false;
204 if (opt == 'v') {
bed94269
GI
205 // Parse "-v N,format[,name]"
206 if (!parse_attribute_def(arg, defs, PRIOR_DATABASE))
2127e193 207 return false;
2127e193
GI
208 }
209 else if (opt == 'F') {
210 unsigned char fix;
211 if (!strcmp(arg, "samsung"))
212 fix = FIX_SAMSUNG;
213 else if (!strcmp(arg, "samsung2"))
214 fix = FIX_SAMSUNG2;
215 else if (!strcmp(arg, "samsung3"))
216 fix = FIX_SAMSUNG3;
217 else
218 return false;
219 // Set only if not set by user
220 if (fix_firmwarebug == FIX_NOTSPECIFIED)
221 fix_firmwarebug = fix;
222 }
223 else
224 return false;
832b75ed 225
2127e193
GI
226 i += len;
227 }
228 return true;
229}
230
231// Shows one entry of knowndrives[], returns #errors.
232static int showonepreset(const drive_settings * dbentry)
233{
832b75ed 234 // Basic error check
2127e193
GI
235 if (!( dbentry
236 && dbentry->modelfamily
237 && dbentry->modelregexp && *dbentry->modelregexp
238 && dbentry->firmwareregexp
239 && dbentry->warningmsg
240 && dbentry->presets )) {
241 pout("Invalid drive database entry. Please report\n"
832b75ed 242 "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
2127e193 243 return 1;
832b75ed
GG
244 }
245
2127e193
GI
246 // print and check model and firmware regular expressions
247 int errcnt = 0;
248 regular_expression regex;
249 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", dbentry->modelregexp);
250 if (!compile(regex, dbentry->modelregexp))
251 errcnt++;
832b75ed 252
2127e193
GI
253 pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", *dbentry->firmwareregexp ?
254 dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
255 if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
256 errcnt++;
257
258 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
259
260 // if there are any presets, then show them
261 unsigned char fix_firmwarebug = 0;
262 bool first_preset = true;
263 if (*dbentry->presets) {
bed94269
GI
264 ata_vendor_attr_defs defs;
265 if (!parse_presets(dbentry->presets, defs, fix_firmwarebug)) {
2127e193
GI
266 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
267 errcnt++;
268 }
269 for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
bed94269 270 if (defs[i].priority != PRIOR_DEFAULT) {
2127e193 271 // Use leading zeros instead of spaces so that everything lines up.
bed94269
GI
272 pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
273 i, ata_get_smart_attr_name(i, defs).c_str());
2127e193
GI
274 first_preset = false;
275 }
276 }
832b75ed 277 }
2127e193 278 if (first_preset)
832b75ed
GG
279 pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
280
2127e193
GI
281 // describe firmwarefix
282 if (fix_firmwarebug) {
283 const char * fixdesc;
284 switch (fix_firmwarebug) {
285 case FIX_SAMSUNG:
286 fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
287 break;
288 case FIX_SAMSUNG2:
289 fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
290 break;
291 case FIX_SAMSUNG3:
292 fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
293 break;
294 default:
295 fixdesc = "UNKNOWN"; errcnt++;
296 break;
297 }
298 pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
832b75ed 299 }
2127e193 300
832b75ed 301 // Print any special warnings
2127e193
GI
302 if (*dbentry->warningmsg)
303 pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
304 return errcnt;
832b75ed
GG
305}
306
307// Shows all presets for drives in knowndrives[].
2127e193
GI
308// Returns #syntax errors.
309int showallpresets()
310{
832b75ed
GG
311 // loop over all entries in the knowndrives[] table, printing them
312 // out in a nice format
2127e193
GI
313 int errcnt = 0;
314 for (unsigned i = 0; i < knowndrives.size(); i++) {
315 errcnt += showonepreset(&knowndrives[i]);
832b75ed
GG
316 pout("\n");
317 }
318
2127e193
GI
319 pout("Total number of entries :%5u\n"
320 "Entries read from file(s):%5u\n\n",
321 knowndrives.size(), knowndrives.custom_size());
322
832b75ed
GG
323 pout("For information about adding a drive to the database see the FAQ on the\n");
324 pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n");
2127e193
GI
325
326 if (errcnt > 0)
327 pout("\nFound %d syntax error(s) in database.\n"
328 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt);
329 return errcnt;
832b75ed
GG
330}
331
332// Shows all matching presets for a drive in knowndrives[].
333// Returns # matching entries.
2127e193
GI
334int showmatchingpresets(const char *model, const char *firmware)
335{
832b75ed
GG
336 int cnt = 0;
337 const char * firmwaremsg = (firmware ? firmware : "(any)");
832b75ed 338
2127e193
GI
339 for (unsigned i = 0; i < knowndrives.size(); i++) {
340 if (!match(knowndrives[i].modelregexp, model))
832b75ed 341 continue;
2127e193
GI
342 if ( firmware && *knowndrives[i].firmwareregexp
343 && !match(knowndrives[i].firmwareregexp, firmware))
832b75ed 344 continue;
2127e193 345 // Found
832b75ed
GG
346 if (++cnt == 1)
347 pout("Drive found in smartmontools Database. Drive identity strings:\n"
348 "%-*s %s\n"
349 "%-*s %s\n"
350 "match smartmontools Drive Database entry:\n",
351 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
352 else if (cnt == 2)
353 pout("and match these additional entries:\n");
354 showonepreset(&knowndrives[i]);
355 pout("\n");
356 }
832b75ed
GG
357 if (cnt == 0)
358 pout("No presets are defined for this drive. Its identity strings:\n"
359 "MODEL: %s\n"
360 "FIRMWARE: %s\n"
361 "do not match any of the known regular expressions.\n",
362 model, firmwaremsg);
363 return cnt;
364}
365
366// Shows the presets (if any) that are available for the given drive.
2127e193
GI
367void show_presets(const ata_identify_device * drive, bool fix_swapped_id)
368{
832b75ed
GG
369 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
370
371 // get the drive's model/firmware strings
2127e193
GI
372 format_ata_string(model, drive->model, MODEL_STRING_LENGTH, fix_swapped_id);
373 format_ata_string(firmware, drive->fw_rev, FIRMWARE_STRING_LENGTH, fix_swapped_id);
832b75ed
GG
374
375 // and search to see if they match values in the table
2127e193
GI
376 const drive_settings * dbentry = lookup_drive(model, firmware);
377 if (!dbentry) {
832b75ed
GG
378 // no matches found
379 pout("No presets are defined for this drive. Its identity strings:\n"
380 "MODEL: %s\n"
381 "FIRMWARE: %s\n"
382 "do not match any of the known regular expressions.\n"
383 "Use -P showall to list all known regular expressions.\n",
384 model, firmware);
385 return;
386 }
387
388 // We found a matching drive. Print out all information about it.
389 pout("Drive found in smartmontools Database. Drive identity strings:\n"
390 "%-*s %s\n"
391 "%-*s %s\n"
392 "match smartmontools Drive Database entry:\n",
393 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
2127e193 394 showonepreset(dbentry);
832b75ed
GG
395}
396
397// Sets preset vendor attribute options in opts by finding the entry
398// (if any) for the given drive in knowndrives[]. Values that have
2127e193
GI
399// already been set in opts will not be changed. Returns false if drive
400// not recognized.
bed94269 401bool apply_presets(const ata_identify_device *drive, ata_vendor_attr_defs & defs,
2127e193
GI
402 unsigned char & fix_firmwarebug, bool fix_swapped_id)
403{
832b75ed 404 // get the drive's model/firmware strings
2127e193
GI
405 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
406 format_ata_string(model, drive->model, MODEL_STRING_LENGTH, fix_swapped_id);
407 format_ata_string(firmware, drive->fw_rev, FIRMWARE_STRING_LENGTH, fix_swapped_id);
832b75ed
GG
408
409 // Look up the drive in knowndrives[].
2127e193
GI
410 const drive_settings * dbentry = lookup_drive(model, firmware);
411 if (!dbentry)
412 return false;
413
414 if (*dbentry->presets) {
415 // Apply presets
bed94269 416 if (!parse_presets(dbentry->presets, defs, fix_firmwarebug))
2127e193
GI
417 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
418 }
419 return true;
420}
421
422
423/////////////////////////////////////////////////////////////////////////////
424// Parser for drive database files
425
426// Abstract pointer to read file input.
427// Operations supported: c = *p; c = p[1]; ++p;
428class stdin_iterator
429{
430public:
431 explicit stdin_iterator(FILE * f)
432 : m_f(f) { get(); get(); }
433
434 stdin_iterator & operator++()
435 { get(); return *this; }
436
437 char operator*() const
438 { return m_c; }
439
440 char operator[](int i) const
441 {
442 if (i != 1)
443 fail();
444 return m_next;
445 }
446
447private:
448 FILE * m_f;
449 char m_c, m_next;
450 void get();
451 void fail() const;
452};
453
454void stdin_iterator::get()
455{
456 m_c = m_next;
457 int ch = getc(m_f);
458 m_next = (ch != EOF ? ch : 0);
459}
460
461void stdin_iterator::fail() const
462{
463 throw std::runtime_error("stdin_iterator: wrong usage");
464}
465
466
467// Use above as parser input 'pointer'. Can easily be changed later
468// to e.g. 'const char *' if above is too slow.
469typedef stdin_iterator parse_ptr;
470
471// Skip whitespace and comments.
472static parse_ptr skip_white(parse_ptr src, const char * path, int & line)
473{
474 for ( ; ; ++src) switch (*src) {
475 case ' ': case '\t':
476 continue;
477
478 case '\n':
479 ++line;
480 continue;
481
482 case '/':
483 switch (src[1]) {
484 case '/':
485 // skip '// comment'
486 ++src; ++src;
487 while (*src && *src != '\n')
488 ++src;
489 if (*src)
490 ++line;
491 break;
492 case '*':
493 // skip '/* comment */'
494 ++src; ++src;
495 for (;;) {
496 if (!*src) {
497 pout("%s(%d): Missing '*/'\n", path, line);
498 return src;
499 }
500 char c = *src; ++src;
501 if (c == '\n')
502 ++line;
503 else if (c == '*' && *src == '/')
504 break;
505 }
506 break;
507 default:
508 return src;
832b75ed 509 }
2127e193
GI
510 continue;
511
512 default:
513 return src;
514 }
515}
516
517// Info about a token.
518struct token_info
519{
520 char type;
521 int line;
522 std::string value;
523
524 token_info() : type(0), line(0) { }
525};
526
527// Get next token.
528static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line)
529{
530 src = skip_white(src, path, line);
531 switch (*src) {
532 case '{': case '}': case ',':
533 // Simple token
534 token.type = *src; token.line = line;
535 ++src;
536 break;
537
538 case '"':
539 // String constant
540 token.type = '"'; token.line = line;
541 token.value = "";
542 do {
543 for (++src; *src != '"'; ++src) {
544 char c = *src;
545 if (!c || c == '\n' || (c == '\\' && !src[1])) {
546 pout("%s(%d): Missing terminating '\"'\n", path, line);
547 token.type = '?'; token.line = line;
548 return src;
549 }
550 if (c == '\\') {
551 c = *++src;
552 switch (c) {
553 case 'n' : c = '\n'; break;
554 case '\n': ++line; break;
555 case '\\': case '"': break;
556 default:
557 pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c);
558 token.type = '?'; token.line = line;
559 continue;
560 }
561 }
562 token.value += c;
563 }
564 // Lookahead to detect string constant concatentation
565 src = skip_white(++src, path, line);
566 } while (*src == '"');
567 break;
568
569 case 0:
570 // EOF
571 token.type = 0; token.line = line;
572 break;
573
574 default:
575 pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src);
576 token.type = '?'; token.line = line;
577 while (*src && *src != '\n')
578 ++src;
579 break;
580 }
581
582 return src;
583}
584
585// Parse drive database from abstract input pointer.
586static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path)
587{
588 int state = 0, field = 0;
589 std::string values[5];
590 bool ok = true;
591
592 token_info token; int line = 1;
593 src = get_token(src, token, path, line);
594 for (;;) {
595 // EOF is ok after '}', trailing ',' is also allowed.
596 if (!token.type && (state == 0 || state == 4))
597 break;
598
599 // Check expected token
600 const char expect[] = "{\",},";
601 if (token.type != expect[state]) {
602 if (token.type != '?')
603 pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]);
604 ok = false;
605 // Skip to next entry
606 while (token.type && token.type != '{')
607 src = get_token(src, token, path, line);
608 state = 0;
609 if (token.type)
610 continue;
611 break;
832b75ed 612 }
2127e193
GI
613
614 // Interpret parser state
615 switch (state) {
616 case 0: // ... ^{...}
617 state = 1; field = 0;
618 break;
619 case 1: // {... ^"..." ...}
620 switch (field) {
621 case 1: case 2:
622 if (!token.value.empty()) {
623 regular_expression regex;
624 if (!regex.compile(token.value.c_str(), REG_EXTENDED)) {
625 pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg());
626 ok = false;
627 }
628 }
629 else if (field == 1) {
630 pout("%s(%d): Missing regular expression for drive model\n", path, token.line);
631 ok = false;
632 }
633 break;
634 case 4:
635 if (!token.value.empty()) {
bed94269
GI
636 ata_vendor_attr_defs defs; unsigned char fix = 0;
637 if (!parse_presets(token.value.c_str(), defs, fix)) {
2127e193
GI
638 pout("%s(%d): Syntax error in preset option string\n", path, token.line);
639 ok = false;
640 }
641 }
642 break;
643 }
644 values[field] = token.value;
645 state = (++field < 5 ? 2 : 3);
646 break;
647 case 2: // {... "..."^, ...}
648 state = 1;
649 break;
650 case 3: // {...^}, ...
651 {
652 drive_settings entry;
653 entry.modelfamily = values[0].c_str();
654 entry.modelregexp = values[1].c_str();
655 entry.firmwareregexp = values[2].c_str();
656 entry.warningmsg = values[3].c_str();
657 entry.presets = values[4].c_str();
658 db.push_back(entry);
659 }
660 state = 4;
661 break;
662 case 4: // {...}^, ...
663 state = 0;
664 break;
665 default:
666 pout("Bad state %d\n", state);
667 return false;
668 }
669 src = get_token(src, token, path, line);
832b75ed 670 }
2127e193
GI
671 return ok;
672}
673
674// Read drive database from file.
675bool read_drive_database(const char * path)
676{
a23d5117
GI
677 stdio_file f(path, "r"
678#ifdef __CYGWIN__ // Allow files with '\r\n'.
679 "t"
680#endif
681 );
2127e193
GI
682 if (!f) {
683 pout("%s: cannot open drive database file\n", path);
684 return false;
685 }
686
687 return parse_drive_database(parse_ptr(f), knowndrives, path);
688}
689
690// Read drive databases from standard places.
691bool read_default_drive_databases()
692{
693#ifndef _WIN32
694 // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
695 static const char db1[] = SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h";
696#else
697 static const char db1[] = "./smart_drivedb.h";
698#endif
699 if (!access(db1, 0)) {
700 if (!read_drive_database(db1))
701 return false;
702 }
703
704#ifdef SMARTMONTOOLS_DRIVEDBDIR
705 // Read file from package: // /usr/{,local/}share/smartmontools/drivedb.h
706 static const char db2[] = SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
707 if (!access(db2, 0)) {
708 if (!read_drive_database(db2))
709 return false;
710 }
711 else
712#endif
713 {
714 // Append builtin table.
715 knowndrives.append(builtin_knowndrives,
716 sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0]));
717 }
718
719 return true;
832b75ed 720}