|
smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
|
00001 /* 00002 * knowndrives.cpp 00003 * 00004 * Home page of code is: http://smartmontools.sourceforge.net 00005 * Address of support mailing list: smartmontools-support@lists.sourceforge.net 00006 * 00007 * Copyright (C) 2003-11 Philip Williams, Bruce Allen 00008 * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net> 00009 * 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2, or (at your option) 00013 * any later version. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 */ 00019 00020 #include "config.h" 00021 #include "int64.h" 00022 #include <stdio.h> 00023 #include "atacmds.h" 00024 #include "knowndrives.h" 00025 #include "utility.h" 00026 00027 #ifdef HAVE_UNISTD_H 00028 #include <unistd.h> 00029 #endif 00030 #ifdef _WIN32 00031 #include <io.h> // access() 00032 #endif 00033 00034 #include <stdexcept> 00035 00036 const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $" 00037 KNOWNDRIVES_H_CVSID; 00038 00039 #define MODEL_STRING_LENGTH 40 00040 #define FIRMWARE_STRING_LENGTH 8 00041 #define TABLEPRINTWIDTH 19 00042 00043 00044 // Builtin table of known drives. 00045 // Used as a default if not read from 00046 // "/usr/{,/local}share/smartmontools/drivedb.h" 00047 // or any other file specified by '-B' option, 00048 // see read_default_drive_databases() below. 00049 // The drive_settings structure is described in drivedb.h. 00050 const drive_settings builtin_knowndrives[] = { 00051 #include "drivedb.h" 00052 }; 00053 00054 00055 /// Drive database class. Stores custom entries read from file. 00056 /// Provides transparent access to concatenation of custom and 00057 /// default table. 00058 class drive_database 00059 { 00060 public: 00061 drive_database(); 00062 00063 ~drive_database(); 00064 00065 /// Get total number of entries. 00066 unsigned size() const 00067 { return m_custom_tab.size() + m_builtin_size; } 00068 00069 /// Get number of custom entries. 00070 unsigned custom_size() const 00071 { return m_custom_tab.size(); } 00072 00073 /// Array access. 00074 const drive_settings & operator[](unsigned i); 00075 00076 /// Append new custom entry. 00077 void push_back(const drive_settings & src); 00078 00079 /// Append builtin table. 00080 void append(const drive_settings * builtin_tab, unsigned builtin_size) 00081 { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; } 00082 00083 private: 00084 const drive_settings * m_builtin_tab; 00085 unsigned m_builtin_size; 00086 00087 std::vector<drive_settings> m_custom_tab; 00088 std::vector<char *> m_custom_strings; 00089 00090 const char * copy_string(const char * str); 00091 00092 drive_database(const drive_database &); 00093 void operator=(const drive_database &); 00094 }; 00095 00096 drive_database::drive_database() 00097 : m_builtin_tab(0), m_builtin_size(0) 00098 { 00099 } 00100 00101 drive_database::~drive_database() 00102 { 00103 for (unsigned i = 0; i < m_custom_strings.size(); i++) 00104 delete [] m_custom_strings[i]; 00105 } 00106 00107 const drive_settings & drive_database::operator[](unsigned i) 00108 { 00109 return (i < m_custom_tab.size() ? m_custom_tab[i] 00110 : m_builtin_tab[i - m_custom_tab.size()] ); 00111 } 00112 00113 void drive_database::push_back(const drive_settings & src) 00114 { 00115 drive_settings dest; 00116 dest.modelfamily = copy_string(src.modelfamily); 00117 dest.modelregexp = copy_string(src.modelregexp); 00118 dest.firmwareregexp = copy_string(src.firmwareregexp); 00119 dest.warningmsg = copy_string(src.warningmsg); 00120 dest.presets = copy_string(src.presets); 00121 m_custom_tab.push_back(dest); 00122 } 00123 00124 const char * drive_database::copy_string(const char * src) 00125 { 00126 size_t len = strlen(src); 00127 char * dest = new char[len+1]; 00128 memcpy(dest, src, len+1); 00129 try { 00130 m_custom_strings.push_back(dest); 00131 } 00132 catch (...) { 00133 delete [] dest; throw; 00134 } 00135 return dest; 00136 } 00137 00138 00139 /// The drive database. 00140 static drive_database knowndrives; 00141 00142 00143 // Return true if modelfamily string describes entry for USB ID 00144 static bool is_usb_modelfamily(const char * modelfamily) 00145 { 00146 return !strncmp(modelfamily, "USB:", 4); 00147 } 00148 00149 // Return true if entry for USB ID 00150 static inline bool is_usb_entry(const drive_settings * dbentry) 00151 { 00152 return is_usb_modelfamily(dbentry->modelfamily); 00153 } 00154 00155 // Compile regular expression, print message on failure. 00156 static bool compile(regular_expression & regex, const char *pattern) 00157 { 00158 if (!regex.compile(pattern, REG_EXTENDED)) { 00159 pout("Internal error: unable to compile regular expression \"%s\": %s\n" 00160 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", 00161 pattern, regex.get_errmsg()); 00162 return false; 00163 } 00164 return true; 00165 } 00166 00167 // Compile & match a regular expression. 00168 static bool match(const char * pattern, const char * str) 00169 { 00170 regular_expression regex; 00171 if (!compile(regex, pattern)) 00172 return false; 00173 return regex.full_match(str); 00174 } 00175 00176 // Searches knowndrives[] for a drive with the given model number and firmware 00177 // string. If either the drive's model or firmware strings are not set by the 00178 // manufacturer then values of NULL may be used. Returns the entry of the 00179 // first match in knowndrives[] or 0 if no match if found. 00180 static const drive_settings * lookup_drive(const char * model, const char * firmware) 00181 { 00182 if (!model) 00183 model = ""; 00184 if (!firmware) 00185 firmware = ""; 00186 00187 for (unsigned i = 0; i < knowndrives.size(); i++) { 00188 // Skip USB entries 00189 if (is_usb_entry(&knowndrives[i])) 00190 continue; 00191 00192 // Check whether model matches the regular expression in knowndrives[i]. 00193 if (!match(knowndrives[i].modelregexp, model)) 00194 continue; 00195 00196 // Model matches, now check firmware. "" matches always. 00197 if (!( !*knowndrives[i].firmwareregexp 00198 || match(knowndrives[i].firmwareregexp, firmware))) 00199 continue; 00200 00201 // Found 00202 return &knowndrives[i]; 00203 } 00204 00205 // Not found 00206 return 0; 00207 } 00208 00209 00210 // Parse drive or USB options in preset string, return false on error. 00211 static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, 00212 firmwarebug_defs * firmwarebugs, std::string * type) 00213 { 00214 for (int i = 0; ; ) { 00215 i += strspn(presets+i, " \t"); 00216 if (!presets[i]) 00217 break; 00218 char opt, arg[80+1+13]; int len = -1; 00219 if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0)) 00220 return false; 00221 if (opt == 'v' && defs) { 00222 // Parse "-v N,format[,name]" 00223 if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE)) 00224 return false; 00225 } 00226 else if (opt == 'F' && firmwarebugs) { 00227 firmwarebug_defs bug; 00228 if (!parse_firmwarebug_def(arg, bug)) 00229 return false; 00230 // Don't set if user specified '-F none'. 00231 if (!firmwarebugs->is_set(BUG_NONE)) 00232 firmwarebugs->set(bug); 00233 } 00234 else if (opt == 'd' && type) { 00235 // TODO: Check valid types 00236 *type = arg; 00237 } 00238 else 00239 return false; 00240 00241 i += len; 00242 } 00243 return true; 00244 } 00245 00246 // Parse '-v' and '-F' options in preset string, return false on error. 00247 static inline bool parse_presets(const char * presets, 00248 ata_vendor_attr_defs & defs, 00249 firmwarebug_defs & firmwarebugs) 00250 { 00251 return parse_db_presets(presets, &defs, &firmwarebugs, 0); 00252 } 00253 00254 // Parse '-d' option in preset string, return false on error. 00255 static inline bool parse_usb_type(const char * presets, std::string & type) 00256 { 00257 return parse_db_presets(presets, 0, 0, &type); 00258 } 00259 00260 // Parse "USB: [DEVICE] ; [BRIDGE]" string 00261 static void parse_usb_names(const char * names, usb_dev_info & info) 00262 { 00263 int n1 = -1, n2 = -1, n3 = -1; 00264 sscanf(names, "USB: %n%*[^;]%n; %n", &n1, &n2, &n3); 00265 if (0 < n1 && n1 < n2) 00266 info.usb_device.assign(names+n1, n2-n1); 00267 else 00268 sscanf(names, "USB: ; %n", &n3); 00269 if (0 < n3) 00270 info.usb_bridge = names+n3; 00271 } 00272 00273 // Search drivedb for USB device with vendor:product ID. 00274 int lookup_usb_device(int vendor_id, int product_id, int bcd_device, 00275 usb_dev_info & info, usb_dev_info & info2) 00276 { 00277 // Format strings to match 00278 char usb_id_str[16], bcd_dev_str[16]; 00279 snprintf(usb_id_str, sizeof(usb_id_str), "0x%04x:0x%04x", vendor_id, product_id); 00280 if (bcd_device >= 0) 00281 snprintf(bcd_dev_str, sizeof(bcd_dev_str), "0x%04x", bcd_device); 00282 else 00283 bcd_dev_str[0] = 0; 00284 00285 int found = 0; 00286 for (unsigned i = 0; i < knowndrives.size(); i++) { 00287 const drive_settings & dbentry = knowndrives[i]; 00288 00289 // Skip drive entries 00290 if (!is_usb_entry(&dbentry)) 00291 continue; 00292 00293 // Check whether USB vendor:product ID matches 00294 if (!match(dbentry.modelregexp, usb_id_str)) 00295 continue; 00296 00297 // Parse '-d type' 00298 usb_dev_info d; 00299 if (!parse_usb_type(dbentry.presets, d.usb_type)) 00300 return 0; // Syntax error 00301 parse_usb_names(dbentry.modelfamily, d); 00302 00303 // If two entries with same vendor:product ID have different 00304 // types, use bcd_device (if provided by OS) to select entry. 00305 if ( *dbentry.firmwareregexp && *bcd_dev_str 00306 && match(dbentry.firmwareregexp, bcd_dev_str)) { 00307 // Exact match including bcd_device 00308 info = d; found = 1; 00309 break; 00310 } 00311 else if (!found) { 00312 // First match without bcd_device 00313 info = d; found = 1; 00314 } 00315 else if (info.usb_type != d.usb_type) { 00316 // Another possible match with different type 00317 info2 = d; found = 2; 00318 break; 00319 } 00320 00321 // Stop search at first matching entry with empty bcd_device 00322 if (!*dbentry.firmwareregexp) 00323 break; 00324 } 00325 00326 return found; 00327 } 00328 00329 // Shows one entry of knowndrives[], returns #errors. 00330 static int showonepreset(const drive_settings * dbentry) 00331 { 00332 // Basic error check 00333 if (!( dbentry 00334 && dbentry->modelfamily 00335 && dbentry->modelregexp && *dbentry->modelregexp 00336 && dbentry->firmwareregexp 00337 && dbentry->warningmsg 00338 && dbentry->presets )) { 00339 pout("Invalid drive database entry. Please report\n" 00340 "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n"); 00341 return 1; 00342 } 00343 00344 bool usb = is_usb_entry(dbentry); 00345 00346 // print and check model and firmware regular expressions 00347 int errcnt = 0; 00348 regular_expression regex; 00349 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"), 00350 dbentry->modelregexp); 00351 if (!compile(regex, dbentry->modelregexp)) 00352 errcnt++; 00353 00354 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"), 00355 *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change) 00356 if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp)) 00357 errcnt++; 00358 00359 if (!usb) { 00360 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily); 00361 00362 // if there are any presets, then show them 00363 firmwarebug_defs firmwarebugs; 00364 bool first_preset = true; 00365 if (*dbentry->presets) { 00366 ata_vendor_attr_defs defs; 00367 if (!parse_presets(dbentry->presets, defs, firmwarebugs)) { 00368 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); 00369 errcnt++; 00370 } 00371 for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) { 00372 if (defs[i].priority != PRIOR_DEFAULT) { 00373 std::string name = ata_get_smart_attr_name(i, defs); 00374 // Use leading zeros instead of spaces so that everything lines up. 00375 pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", 00376 i, name.c_str()); 00377 // Check max name length suitable for smartctl -A output 00378 const unsigned maxlen = 23; 00379 if (name.size() > maxlen) { 00380 pout("%*s\n", TABLEPRINTWIDTH+6+maxlen, "Error: Attribute name too long ------^"); 00381 errcnt++; 00382 } 00383 first_preset = false; 00384 } 00385 } 00386 } 00387 if (first_preset) 00388 pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); 00389 00390 // describe firmwarefix 00391 for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) { 00392 if (!firmwarebugs.is_set((firmwarebug_t)b)) 00393 continue; 00394 const char * fixdesc; 00395 switch ((firmwarebug_t)b) { 00396 case BUG_NOLOGDIR: 00397 fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)"; 00398 break; 00399 case BUG_SAMSUNG: 00400 fixdesc = "Fixes byte order in some SMART data (same as -F samsung)"; 00401 break; 00402 case BUG_SAMSUNG2: 00403 fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)"; 00404 break; 00405 case BUG_SAMSUNG3: 00406 fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)"; 00407 break; 00408 case BUG_XERRORLBA: 00409 fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)"; 00410 break; 00411 default: 00412 fixdesc = "UNKNOWN"; errcnt++; 00413 break; 00414 } 00415 pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc); 00416 } 00417 } 00418 else { 00419 // Print USB info 00420 usb_dev_info info; parse_usb_names(dbentry->modelfamily, info); 00421 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Device:", 00422 (!info.usb_device.empty() ? info.usb_device.c_str() : "[unknown]")); 00423 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Bridge:", 00424 (!info.usb_bridge.empty() ? info.usb_bridge.c_str() : "[unknown]")); 00425 00426 if (*dbentry->presets && !parse_usb_type(dbentry->presets, info.usb_type)) { 00427 pout("Syntax error in USB type string \"%s\"\n", dbentry->presets); 00428 errcnt++; 00429 } 00430 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type", 00431 (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]")); 00432 } 00433 00434 // Print any special warnings 00435 if (*dbentry->warningmsg) 00436 pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg); 00437 return errcnt; 00438 } 00439 00440 // Shows all presets for drives in knowndrives[]. 00441 // Returns #syntax errors. 00442 int showallpresets() 00443 { 00444 // loop over all entries in the knowndrives[] table, printing them 00445 // out in a nice format 00446 int errcnt = 0; 00447 for (unsigned i = 0; i < knowndrives.size(); i++) { 00448 errcnt += showonepreset(&knowndrives[i]); 00449 pout("\n"); 00450 } 00451 00452 pout("Total number of entries :%5u\n" 00453 "Entries read from file(s):%5u\n\n", 00454 knowndrives.size(), knowndrives.custom_size()); 00455 00456 pout("For information about adding a drive to the database see the FAQ on the\n"); 00457 pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n"); 00458 00459 if (errcnt > 0) 00460 pout("\nFound %d syntax error(s) in database.\n" 00461 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt); 00462 return errcnt; 00463 } 00464 00465 // Shows all matching presets for a drive in knowndrives[]. 00466 // Returns # matching entries. 00467 int showmatchingpresets(const char *model, const char *firmware) 00468 { 00469 int cnt = 0; 00470 const char * firmwaremsg = (firmware ? firmware : "(any)"); 00471 00472 for (unsigned i = 0; i < knowndrives.size(); i++) { 00473 if (!match(knowndrives[i].modelregexp, model)) 00474 continue; 00475 if ( firmware && *knowndrives[i].firmwareregexp 00476 && !match(knowndrives[i].firmwareregexp, firmware)) 00477 continue; 00478 // Found 00479 if (++cnt == 1) 00480 pout("Drive found in smartmontools Database. Drive identity strings:\n" 00481 "%-*s %s\n" 00482 "%-*s %s\n" 00483 "match smartmontools Drive Database entry:\n", 00484 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg); 00485 else if (cnt == 2) 00486 pout("and match these additional entries:\n"); 00487 showonepreset(&knowndrives[i]); 00488 pout("\n"); 00489 } 00490 if (cnt == 0) 00491 pout("No presets are defined for this drive. Its identity strings:\n" 00492 "MODEL: %s\n" 00493 "FIRMWARE: %s\n" 00494 "do not match any of the known regular expressions.\n", 00495 model, firmwaremsg); 00496 return cnt; 00497 } 00498 00499 // Shows the presets (if any) that are available for the given drive. 00500 void show_presets(const ata_identify_device * drive) 00501 { 00502 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; 00503 00504 // get the drive's model/firmware strings 00505 ata_format_id_string(model, drive->model, sizeof(model)-1); 00506 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); 00507 00508 // and search to see if they match values in the table 00509 const drive_settings * dbentry = lookup_drive(model, firmware); 00510 if (!dbentry) { 00511 // no matches found 00512 pout("No presets are defined for this drive. Its identity strings:\n" 00513 "MODEL: %s\n" 00514 "FIRMWARE: %s\n" 00515 "do not match any of the known regular expressions.\n" 00516 "Use -P showall to list all known regular expressions.\n", 00517 model, firmware); 00518 return; 00519 } 00520 00521 // We found a matching drive. Print out all information about it. 00522 pout("Drive found in smartmontools Database. Drive identity strings:\n" 00523 "%-*s %s\n" 00524 "%-*s %s\n" 00525 "match smartmontools Drive Database entry:\n", 00526 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware); 00527 showonepreset(dbentry); 00528 } 00529 00530 // Searches drive database and sets preset vendor attribute 00531 // options in defs and firmwarebugs. 00532 // Values that have already been set will not be changed. 00533 // Returns pointer to database entry or nullptr if none found 00534 const drive_settings * lookup_drive_apply_presets( 00535 const ata_identify_device * drive, ata_vendor_attr_defs & defs, 00536 firmwarebug_defs & firmwarebugs) 00537 { 00538 // get the drive's model/firmware strings 00539 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; 00540 ata_format_id_string(model, drive->model, sizeof(model)-1); 00541 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); 00542 00543 // Look up the drive in knowndrives[]. 00544 const drive_settings * dbentry = lookup_drive(model, firmware); 00545 if (!dbentry) 00546 return 0; 00547 00548 if (*dbentry->presets) { 00549 // Apply presets 00550 if (!parse_presets(dbentry->presets, defs, firmwarebugs)) 00551 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); 00552 } 00553 return dbentry; 00554 } 00555 00556 00557 ///////////////////////////////////////////////////////////////////////////// 00558 // Parser for drive database files 00559 00560 // Abstract pointer to read file input. 00561 // Operations supported: c = *p; c = p[1]; ++p; 00562 class stdin_iterator 00563 { 00564 public: 00565 explicit stdin_iterator(FILE * f) 00566 : m_f(f) { get(); get(); } 00567 00568 stdin_iterator & operator++() 00569 { get(); return *this; } 00570 00571 char operator*() const 00572 { return m_c; } 00573 00574 char operator[](int i) const 00575 { 00576 if (i != 1) 00577 fail(); 00578 return m_next; 00579 } 00580 00581 private: 00582 FILE * m_f; 00583 char m_c, m_next; 00584 void get(); 00585 void fail() const; 00586 }; 00587 00588 void stdin_iterator::get() 00589 { 00590 m_c = m_next; 00591 int ch = getc(m_f); 00592 m_next = (ch != EOF ? ch : 0); 00593 } 00594 00595 void stdin_iterator::fail() const 00596 { 00597 throw std::runtime_error("stdin_iterator: wrong usage"); 00598 } 00599 00600 00601 // Use above as parser input 'pointer'. Can easily be changed later 00602 // to e.g. 'const char *' if above is too slow. 00603 typedef stdin_iterator parse_ptr; 00604 00605 // Skip whitespace and comments. 00606 static parse_ptr skip_white(parse_ptr src, const char * path, int & line) 00607 { 00608 for ( ; ; ++src) switch (*src) { 00609 case ' ': case '\t': 00610 continue; 00611 00612 case '\n': 00613 ++line; 00614 continue; 00615 00616 case '/': 00617 switch (src[1]) { 00618 case '/': 00619 // skip '// comment' 00620 ++src; ++src; 00621 while (*src && *src != '\n') 00622 ++src; 00623 if (*src) 00624 ++line; 00625 break; 00626 case '*': 00627 // skip '/* comment */' 00628 ++src; ++src; 00629 for (;;) { 00630 if (!*src) { 00631 pout("%s(%d): Missing '*/'\n", path, line); 00632 return src; 00633 } 00634 char c = *src; ++src; 00635 if (c == '\n') 00636 ++line; 00637 else if (c == '*' && *src == '/') 00638 break; 00639 } 00640 break; 00641 default: 00642 return src; 00643 } 00644 continue; 00645 00646 default: 00647 return src; 00648 } 00649 } 00650 00651 // Info about a token. 00652 struct token_info 00653 { 00654 char type; 00655 int line; 00656 std::string value; 00657 00658 token_info() : type(0), line(0) { } 00659 }; 00660 00661 // Get next token. 00662 static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line) 00663 { 00664 src = skip_white(src, path, line); 00665 switch (*src) { 00666 case '{': case '}': case ',': 00667 // Simple token 00668 token.type = *src; token.line = line; 00669 ++src; 00670 break; 00671 00672 case '"': 00673 // String constant 00674 token.type = '"'; token.line = line; 00675 token.value = ""; 00676 do { 00677 for (++src; *src != '"'; ++src) { 00678 char c = *src; 00679 if (!c || c == '\n' || (c == '\\' && !src[1])) { 00680 pout("%s(%d): Missing terminating '\"'\n", path, line); 00681 token.type = '?'; token.line = line; 00682 return src; 00683 } 00684 if (c == '\\') { 00685 c = *++src; 00686 switch (c) { 00687 case 'n' : c = '\n'; break; 00688 case '\n': ++line; break; 00689 case '\\': case '"': break; 00690 default: 00691 pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c); 00692 token.type = '?'; token.line = line; 00693 continue; 00694 } 00695 } 00696 token.value += c; 00697 } 00698 // Lookahead to detect string constant concatentation 00699 src = skip_white(++src, path, line); 00700 } while (*src == '"'); 00701 break; 00702 00703 case 0: 00704 // EOF 00705 token.type = 0; token.line = line; 00706 break; 00707 00708 default: 00709 pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src); 00710 token.type = '?'; token.line = line; 00711 while (*src && *src != '\n') 00712 ++src; 00713 break; 00714 } 00715 00716 return src; 00717 } 00718 00719 // Parse drive database from abstract input pointer. 00720 static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path) 00721 { 00722 int state = 0, field = 0; 00723 std::string values[5]; 00724 bool ok = true; 00725 00726 token_info token; int line = 1; 00727 src = get_token(src, token, path, line); 00728 for (;;) { 00729 // EOF is ok after '}', trailing ',' is also allowed. 00730 if (!token.type && (state == 0 || state == 4)) 00731 break; 00732 00733 // Check expected token 00734 const char expect[] = "{\",},"; 00735 if (token.type != expect[state]) { 00736 if (token.type != '?') 00737 pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]); 00738 ok = false; 00739 // Skip to next entry 00740 while (token.type && token.type != '{') 00741 src = get_token(src, token, path, line); 00742 state = 0; 00743 if (token.type) 00744 continue; 00745 break; 00746 } 00747 00748 // Interpret parser state 00749 switch (state) { 00750 case 0: // ... ^{...} 00751 state = 1; field = 0; 00752 break; 00753 case 1: // {... ^"..." ...} 00754 switch (field) { 00755 case 1: case 2: 00756 if (!token.value.empty()) { 00757 regular_expression regex; 00758 if (!regex.compile(token.value.c_str(), REG_EXTENDED)) { 00759 pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg()); 00760 ok = false; 00761 } 00762 } 00763 else if (field == 1) { 00764 pout("%s(%d): Missing regular expression for drive model\n", path, token.line); 00765 ok = false; 00766 } 00767 break; 00768 case 4: 00769 if (!token.value.empty()) { 00770 if (!is_usb_modelfamily(values[0].c_str())) { 00771 ata_vendor_attr_defs defs; firmwarebug_defs fix; 00772 if (!parse_presets(token.value.c_str(), defs, fix)) { 00773 pout("%s(%d): Syntax error in preset option string\n", path, token.line); 00774 ok = false; 00775 } 00776 } 00777 else { 00778 std::string type; 00779 if (!parse_usb_type(token.value.c_str(), type)) { 00780 pout("%s(%d): Syntax error in USB type string\n", path, token.line); 00781 ok = false; 00782 } 00783 } 00784 } 00785 break; 00786 } 00787 values[field] = token.value; 00788 state = (++field < 5 ? 2 : 3); 00789 break; 00790 case 2: // {... "..."^, ...} 00791 state = 1; 00792 break; 00793 case 3: // {...^}, ... 00794 { 00795 drive_settings entry; 00796 entry.modelfamily = values[0].c_str(); 00797 entry.modelregexp = values[1].c_str(); 00798 entry.firmwareregexp = values[2].c_str(); 00799 entry.warningmsg = values[3].c_str(); 00800 entry.presets = values[4].c_str(); 00801 db.push_back(entry); 00802 } 00803 state = 4; 00804 break; 00805 case 4: // {...}^, ... 00806 state = 0; 00807 break; 00808 default: 00809 pout("Bad state %d\n", state); 00810 return false; 00811 } 00812 src = get_token(src, token, path, line); 00813 } 00814 return ok; 00815 } 00816 00817 // Read drive database from file. 00818 bool read_drive_database(const char * path) 00819 { 00820 stdio_file f(path, "r" 00821 #ifdef __CYGWIN__ // Allow files with '\r\n'. 00822 "t" 00823 #endif 00824 ); 00825 if (!f) { 00826 pout("%s: cannot open drive database file\n", path); 00827 return false; 00828 } 00829 00830 return parse_drive_database(parse_ptr(f), knowndrives, path); 00831 } 00832 00833 // Get path for additional database file 00834 const char * get_drivedb_path_add() 00835 { 00836 #ifndef _WIN32 00837 return SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h"; 00838 #else 00839 static std::string path = get_exe_dir() + "/drivedb-add.h"; 00840 return path.c_str(); 00841 #endif 00842 } 00843 00844 #ifdef SMARTMONTOOLS_DRIVEDBDIR 00845 00846 // Get path for default database file 00847 const char * get_drivedb_path_default() 00848 { 00849 #ifndef _WIN32 00850 return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h"; 00851 #else 00852 static std::string path = get_exe_dir() + "/drivedb.h"; 00853 return path.c_str(); 00854 #endif 00855 } 00856 00857 #endif 00858 00859 // Read drive databases from standard places. 00860 bool read_default_drive_databases() 00861 { 00862 // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h 00863 const char * db1 = get_drivedb_path_add(); 00864 if (!access(db1, 0)) { 00865 if (!read_drive_database(db1)) 00866 return false; 00867 } 00868 00869 #ifdef SMARTMONTOOLS_DRIVEDBDIR 00870 // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h 00871 const char * db2 = get_drivedb_path_default(); 00872 if (!access(db2, 0)) { 00873 if (!read_drive_database(db2)) 00874 return false; 00875 } 00876 else 00877 #endif 00878 { 00879 // Append builtin table. 00880 knowndrives.append(builtin_knowndrives, 00881 sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0])); 00882 } 00883 00884 return true; 00885 }
1.7.4