]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * smartctl.cpp |
832b75ed GG |
3 | * |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
293b5ab8 JD |
6 | * Copyright (C) 2002-11 Bruce Allen |
7 | * Copyright (C) 2008-15 Christian Franke | |
832b75ed GG |
8 | * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
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 | |
ee38a438 | 16 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. |
832b75ed GG |
17 | * |
18 | * This code was originally developed as a Senior Thesis by Michael Cornwell | |
19 | * at the Concurrent Systems Laboratory (now part of the Storage Systems | |
20 | * Research Center), Jack Baskin School of Engineering, University of | |
21 | * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <errno.h> | |
26 | #include <stdio.h> | |
27 | #include <sys/types.h> | |
28 | #include <string.h> | |
d2e702cf | 29 | #include <stdlib.h> |
832b75ed | 30 | #include <stdarg.h> |
2127e193 GI |
31 | #include <stdexcept> |
32 | #include <getopt.h> | |
832b75ed GG |
33 | |
34 | #include "config.h" | |
2127e193 GI |
35 | |
36 | #ifdef HAVE_UNISTD_H | |
832b75ed GG |
37 | #include <unistd.h> |
38 | #endif | |
39 | ||
2127e193 GI |
40 | #if defined(__FreeBSD__) |
41 | #include <sys/param.h> | |
a37e7145 GG |
42 | #endif |
43 | ||
832b75ed GG |
44 | #include "int64.h" |
45 | #include "atacmds.h" | |
2127e193 | 46 | #include "dev_interface.h" |
832b75ed | 47 | #include "ataprint.h" |
832b75ed GG |
48 | #include "knowndrives.h" |
49 | #include "scsicmds.h" | |
50 | #include "scsiprint.h" | |
51 | #include "smartctl.h" | |
52 | #include "utility.h" | |
53 | ||
293b5ab8 | 54 | const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4080 2015-05-05 20:31:22Z chrfranke $" |
cfbba5b9 | 55 | CONFIG_H_CVSID SMARTCTL_H_CVSID; |
832b75ed | 56 | |
cfbba5b9 GI |
57 | // Globals to control printing |
58 | bool printing_is_switchable = false; | |
59 | bool printing_is_off = false; | |
832b75ed | 60 | |
2127e193 GI |
61 | static void printslogan() |
62 | { | |
63 | pout("%s\n", format_version_info("smartctl").c_str()); | |
832b75ed GG |
64 | } |
65 | ||
cfbba5b9 GI |
66 | static void UsageSummary() |
67 | { | |
832b75ed GG |
68 | pout("\nUse smartctl -h to get a usage summary\n\n"); |
69 | return; | |
70 | } | |
71 | ||
d008864d | 72 | static std::string getvalidarglist(int opt); |
2127e193 | 73 | |
832b75ed | 74 | /* void prints help information for command syntax */ |
cfbba5b9 GI |
75 | static void Usage() |
76 | { | |
832b75ed | 77 | printf("Usage: smartctl [options] device\n\n"); |
832b75ed | 78 | printf( |
2127e193 | 79 | "============================================ SHOW INFORMATION OPTIONS =====\n\n" |
832b75ed GG |
80 | " -h, --help, --usage\n" |
81 | " Display this help and exit\n\n" | |
82 | " -V, --version, --copyright, --license\n" | |
83 | " Print license, copyright, and version information and exit\n\n" | |
d008864d | 84 | " -i, --info\n" |
832b75ed | 85 | " Show identity information for device\n\n" |
ee38a438 GI |
86 | " --identify[=[w][nvb]]\n" |
87 | " Show words and bits from IDENTIFY DEVICE data (ATA)\n\n" | |
d008864d | 88 | " -g NAME, --get=NAME\n" |
3d17a85c | 89 | " Get device setting: all, aam, apm, lookahead, security, wcache, rcache, wcreorder\n\n" |
d008864d | 90 | " -a, --all\n" |
832b75ed | 91 | " Show all SMART information for device\n\n" |
2127e193 GI |
92 | " -x, --xall\n" |
93 | " Show all information for device\n\n" | |
e9583e0c GI |
94 | " --scan\n" |
95 | " Scan for devices\n\n" | |
96 | " --scan-open\n" | |
97 | " Scan for devices and try to open each device\n\n" | |
832b75ed | 98 | ); |
832b75ed | 99 | printf( |
2127e193 | 100 | "================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n" |
832b75ed | 101 | " -q TYPE, --quietmode=TYPE (ATA)\n" |
a37e7145 | 102 | " Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n" |
832b75ed | 103 | " -d TYPE, --device=TYPE\n" |
2127e193 | 104 | " Specify device type to one of: %s\n\n" |
832b75ed GG |
105 | " -T TYPE, --tolerance=TYPE (ATA)\n" |
106 | " Tolerance: normal, conservative, permissive, verypermissive\n\n" | |
107 | " -b TYPE, --badsum=TYPE (ATA)\n" | |
108 | " Set action on bad checksum to one of: warn, exit, ignore\n\n" | |
109 | " -r TYPE, --report=TYPE\n" | |
110 | " Report transactions (see man page)\n\n" | |
4d59bff9 | 111 | " -n MODE, --nocheck=MODE (ATA)\n" |
2127e193 | 112 | " No check if: never, sleep, standby, idle (see man page)\n\n", |
54965743 | 113 | getvalidarglist('d').c_str()); // TODO: Use this function also for other options ? |
832b75ed | 114 | printf( |
2127e193 | 115 | "============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n" |
832b75ed GG |
116 | " -s VALUE, --smart=VALUE\n" |
117 | " Enable/disable SMART on device (on/off)\n\n" | |
118 | " -o VALUE, --offlineauto=VALUE (ATA)\n" | |
119 | " Enable/disable automatic offline testing on device (on/off)\n\n" | |
120 | " -S VALUE, --saveauto=VALUE (ATA)\n" | |
121 | " Enable/disable Attribute autosave on device (on/off)\n\n" | |
d008864d GI |
122 | " -s NAME[,VALUE], --set=NAME[,VALUE]\n" |
123 | " Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n" | |
124 | " lookahead,[on|off], security-freeze, standby,[N|off|now],\n" | |
3d17a85c | 125 | " wcache,[on|off], rcache,[on|off], wcreorder,[on|off]\n\n" |
832b75ed | 126 | ); |
832b75ed | 127 | printf( |
2127e193 | 128 | "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n" |
832b75ed GG |
129 | " -H, --health\n" |
130 | " Show device SMART health status\n\n" | |
131 | " -c, --capabilities (ATA)\n" | |
132 | " Show device SMART capabilities\n\n" | |
a7e8ffec | 133 | " -A, --attributes\n" |
832b75ed | 134 | " Show device SMART vendor-specific Attributes and values\n\n" |
a7e8ffec | 135 | " -f FORMAT, --format=FORMAT (ATA)\n" |
e165493d | 136 | " Set output format for attributes: old, brief, hex[,id|val]\n\n" |
832b75ed | 137 | " -l TYPE, --log=TYPE\n" |
2127e193 | 138 | " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" |
d008864d | 139 | " xerror[,N][,error], xselftest[,N][,selftest],\n" |
2127e193 | 140 | " background, sasphy[,reset], sataphy[,reset],\n" |
d008864d GI |
141 | " scttemp[sts,hist], scttempint,N[,p],\n" |
142 | " scterc[,N,M], devstat[,N], ssd,\n" | |
143 | " gplog,N[,RANGE], smartlog,N[,RANGE]\n\n" | |
832b75ed GG |
144 | " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" |
145 | " Set display OPTION for vendor Attribute N (see man page)\n\n" | |
146 | " -F TYPE, --firmwarebug=TYPE (ATA)\n" | |
ee38a438 GI |
147 | " Use firmware bug workaround:\n" |
148 | " %s, swapid\n\n" | |
832b75ed GG |
149 | " -P TYPE, --presets=TYPE (ATA)\n" |
150 | " Drive-specific presets: use, ignore, show, showall\n\n" | |
2127e193 GI |
151 | " -B [+]FILE, --drivedb=[+]FILE (ATA)\n" |
152 | " Read and replace [add] drive database from FILE\n" | |
e9583e0c | 153 | " [default is +%s", |
ee38a438 | 154 | get_valid_firmwarebug_args(), |
e9583e0c GI |
155 | get_drivedb_path_add() |
156 | ); | |
2127e193 | 157 | #ifdef SMARTMONTOOLS_DRIVEDBDIR |
e9583e0c GI |
158 | printf( |
159 | "\n" | |
160 | " and then %s", | |
161 | get_drivedb_path_default() | |
2127e193 | 162 | ); |
e9583e0c | 163 | #endif |
832b75ed | 164 | printf( |
e9583e0c | 165 | "]\n\n" |
2127e193 | 166 | "============================================ DEVICE SELF-TEST OPTIONS =====\n\n" |
832b75ed | 167 | " -t TEST, --test=TEST\n" |
d008864d GI |
168 | " Run test. TEST: offline, short, long, conveyance, force, vendor,N,\n" |
169 | " select,M-N, pending,N, afterselect,[on|off]\n\n" | |
832b75ed GG |
170 | " -C, --captive\n" |
171 | " Do test in captive mode (along with -t)\n\n" | |
172 | " -X, --abort\n" | |
173 | " Abort any non-captive test on device\n\n" | |
174 | ); | |
54965743 GI |
175 | std::string examples = smi()->get_app_examples("smartctl"); |
176 | if (!examples.empty()) | |
177 | printf("%s\n", examples.c_str()); | |
832b75ed GG |
178 | } |
179 | ||
d008864d | 180 | // Values for --long only options, see parse_options() |
ee38a438 | 181 | enum { opt_identify = 1000, opt_scan, opt_scan_open, opt_set, opt_smart }; |
d008864d | 182 | |
54965743 GI |
183 | /* Returns a string containing a formatted list of the valid arguments |
184 | to the option opt or empty on failure. Note 'v' case different */ | |
d008864d | 185 | static std::string getvalidarglist(int opt) |
2127e193 | 186 | { |
832b75ed GG |
187 | switch (opt) { |
188 | case 'q': | |
a37e7145 | 189 | return "errorsonly, silent, noserial"; |
832b75ed | 190 | case 'd': |
cfbba5b9 | 191 | return smi()->get_valid_dev_types_str() + ", auto, test"; |
832b75ed GG |
192 | case 'T': |
193 | return "normal, conservative, permissive, verypermissive"; | |
194 | case 'b': | |
195 | return "warn, exit, ignore"; | |
196 | case 'r': | |
197 | return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; | |
d008864d | 198 | case opt_smart: |
832b75ed GG |
199 | case 'o': |
200 | case 'S': | |
201 | return "on, off"; | |
202 | case 'l': | |
d008864d GI |
203 | return "error, selftest, selective, directory[,g|s], " |
204 | "xerror[,N][,error], xselftest[,N][,selftest], " | |
205 | "background, sasphy[,reset], sataphy[,reset], " | |
206 | "scttemp[sts,hist], scttempint,N[,p], " | |
207 | "scterc[,N,M], devstat[,N], ssd, " | |
208 | "gplog,N[,RANGE], smartlog,N[,RANGE]"; | |
209 | ||
832b75ed GG |
210 | case 'P': |
211 | return "use, ignore, show, showall"; | |
212 | case 't': | |
d008864d GI |
213 | return "offline, short, long, conveyance, force, vendor,N, select,M-N, " |
214 | "pending,N, afterselect,[on|off]"; | |
832b75ed | 215 | case 'F': |
ee38a438 | 216 | return std::string(get_valid_firmwarebug_args()) + ", swapid"; |
4d59bff9 GG |
217 | case 'n': |
218 | return "never, sleep, standby, idle"; | |
a7e8ffec | 219 | case 'f': |
e165493d | 220 | return "old, brief, hex[,id|val]"; |
d008864d | 221 | case 'g': |
3d17a85c | 222 | return "aam, apm, lookahead, security, wcache, rcache, wcreorder"; |
d008864d GI |
223 | case opt_set: |
224 | return "aam,[N|off], apm,[N|off], lookahead,[on|off], security-freeze, " | |
3d17a85c | 225 | "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off]"; |
d008864d GI |
226 | case 's': |
227 | return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set); | |
ee38a438 GI |
228 | case opt_identify: |
229 | return "n, wn, w, v, wv, wb"; | |
832b75ed GG |
230 | case 'v': |
231 | default: | |
54965743 | 232 | return ""; |
832b75ed GG |
233 | } |
234 | } | |
235 | ||
236 | /* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where | |
237 | <LIST> is the list of valid arguments for option opt. */ | |
d008864d | 238 | static void printvalidarglistmessage(int opt) |
cfbba5b9 | 239 | { |
832b75ed | 240 | if (opt=='v'){ |
2127e193 GI |
241 | pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", |
242 | create_vendor_attribute_arg_list().c_str()); | |
832b75ed GG |
243 | } |
244 | else { | |
245 | // getvalidarglist() might produce a multiline or single line string. We | |
246 | // need to figure out which to get the formatting right. | |
54965743 GI |
247 | std::string s = getvalidarglist(opt); |
248 | char separator = strchr(s.c_str(), '\n') ? '\n' : ' '; | |
249 | pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator); | |
832b75ed GG |
250 | } |
251 | ||
252 | return; | |
253 | } | |
254 | ||
2127e193 GI |
255 | // Checksum error mode |
256 | enum checksum_err_mode_t { | |
257 | CHECKSUM_ERR_WARN, CHECKSUM_ERR_EXIT, CHECKSUM_ERR_IGNORE | |
258 | }; | |
259 | ||
260 | static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN; | |
261 | ||
cfbba5b9 | 262 | static void scan_devices(const char * type, bool with_open, char ** argv); |
e9583e0c | 263 | |
d008864d | 264 | |
832b75ed | 265 | /* Takes command options and sets features to be run */ |
cfbba5b9 | 266 | static const char * parse_options(int argc, char** argv, |
d008864d GI |
267 | ata_print_options & ataopts, scsi_print_options & scsiopts, |
268 | bool & print_type_only) | |
2127e193 | 269 | { |
832b75ed | 270 | // Please update getvalidarglist() if you edit shortopts |
d008864d | 271 | const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:"; |
832b75ed GG |
272 | // Please update getvalidarglist() if you edit longopts |
273 | struct option longopts[] = { | |
274 | { "help", no_argument, 0, 'h' }, | |
275 | { "usage", no_argument, 0, 'h' }, | |
276 | { "version", no_argument, 0, 'V' }, | |
277 | { "copyright", no_argument, 0, 'V' }, | |
278 | { "license", no_argument, 0, 'V' }, | |
279 | { "quietmode", required_argument, 0, 'q' }, | |
280 | { "device", required_argument, 0, 'd' }, | |
281 | { "tolerance", required_argument, 0, 'T' }, | |
282 | { "badsum", required_argument, 0, 'b' }, | |
283 | { "report", required_argument, 0, 'r' }, | |
d008864d | 284 | { "smart", required_argument, 0, opt_smart }, |
832b75ed GG |
285 | { "offlineauto", required_argument, 0, 'o' }, |
286 | { "saveauto", required_argument, 0, 'S' }, | |
287 | { "health", no_argument, 0, 'H' }, | |
288 | { "capabilities", no_argument, 0, 'c' }, | |
289 | { "attributes", no_argument, 0, 'A' }, | |
290 | { "log", required_argument, 0, 'l' }, | |
291 | { "info", no_argument, 0, 'i' }, | |
292 | { "all", no_argument, 0, 'a' }, | |
2127e193 | 293 | { "xall", no_argument, 0, 'x' }, |
832b75ed GG |
294 | { "vendorattribute", required_argument, 0, 'v' }, |
295 | { "presets", required_argument, 0, 'P' }, | |
296 | { "test", required_argument, 0, 't' }, | |
297 | { "captive", no_argument, 0, 'C' }, | |
298 | { "abort", no_argument, 0, 'X' }, | |
299 | { "firmwarebug", required_argument, 0, 'F' }, | |
4d59bff9 | 300 | { "nocheck", required_argument, 0, 'n' }, |
2127e193 | 301 | { "drivedb", required_argument, 0, 'B' }, |
a7e8ffec | 302 | { "format", required_argument, 0, 'f' }, |
d008864d | 303 | { "get", required_argument, 0, 'g' }, |
ee38a438 | 304 | { "identify", optional_argument, 0, opt_identify }, |
d008864d | 305 | { "set", required_argument, 0, opt_set }, |
e9583e0c GI |
306 | { "scan", no_argument, 0, opt_scan }, |
307 | { "scan-open", no_argument, 0, opt_scan_open }, | |
832b75ed GG |
308 | { 0, 0, 0, 0 } |
309 | }; | |
2127e193 GI |
310 | |
311 | char extraerror[256]; | |
832b75ed | 312 | memset(extraerror, 0, sizeof(extraerror)); |
832b75ed | 313 | opterr=optopt=0; |
2127e193 GI |
314 | |
315 | const char * type = 0; // set to -d optarg | |
316 | bool no_defaultdb = false; // set true on '-B FILE' | |
a7e8ffec | 317 | bool output_format_set = false; // set true on '-f FORMAT' |
e9583e0c | 318 | int scan = 0; // set by --scan, --scan-open |
2127e193 GI |
319 | bool badarg = false, captive = false; |
320 | int testcnt = 0; // number of self-tests requested | |
321 | ||
322 | int optchar; | |
323 | char *arg; | |
324 | ||
e9583e0c | 325 | while ((optchar = getopt_long(argc, argv, shortopts, longopts, 0)) != -1) { |
832b75ed GG |
326 | switch (optchar){ |
327 | case 'V': | |
cfbba5b9 | 328 | printing_is_off = false; |
2127e193 GI |
329 | pout("%s", format_version_info("smartctl", true /*full*/).c_str()); |
330 | EXIT(0); | |
832b75ed GG |
331 | break; |
332 | case 'q': | |
333 | if (!strcmp(optarg,"errorsonly")) { | |
cfbba5b9 GI |
334 | printing_is_switchable = true; |
335 | printing_is_off = false; | |
832b75ed | 336 | } else if (!strcmp(optarg,"silent")) { |
cfbba5b9 GI |
337 | printing_is_switchable = false; |
338 | printing_is_off = true; | |
a37e7145 | 339 | } else if (!strcmp(optarg,"noserial")) { |
cfbba5b9 | 340 | dont_print_serial_number = true; |
832b75ed | 341 | } else { |
2127e193 | 342 | badarg = true; |
832b75ed GG |
343 | } |
344 | break; | |
345 | case 'd': | |
d008864d GI |
346 | if (!strcmp(optarg, "test")) |
347 | print_type_only = true; | |
348 | else | |
349 | type = (strcmp(optarg, "auto") ? optarg : (char *)0); | |
832b75ed GG |
350 | break; |
351 | case 'T': | |
352 | if (!strcmp(optarg,"normal")) { | |
cfbba5b9 GI |
353 | failuretest_conservative = false; |
354 | failuretest_permissive = 0; | |
832b75ed | 355 | } else if (!strcmp(optarg,"conservative")) { |
cfbba5b9 | 356 | failuretest_conservative = true; |
832b75ed | 357 | } else if (!strcmp(optarg,"permissive")) { |
cfbba5b9 GI |
358 | if (failuretest_permissive < 0xff) |
359 | failuretest_permissive++; | |
832b75ed | 360 | } else if (!strcmp(optarg,"verypermissive")) { |
cfbba5b9 | 361 | failuretest_permissive = 0xff; |
832b75ed | 362 | } else { |
2127e193 | 363 | badarg = true; |
832b75ed GG |
364 | } |
365 | break; | |
366 | case 'b': | |
367 | if (!strcmp(optarg,"warn")) { | |
2127e193 | 368 | checksum_err_mode = CHECKSUM_ERR_WARN; |
832b75ed | 369 | } else if (!strcmp(optarg,"exit")) { |
2127e193 | 370 | checksum_err_mode = CHECKSUM_ERR_EXIT; |
832b75ed | 371 | } else if (!strcmp(optarg,"ignore")) { |
2127e193 | 372 | checksum_err_mode = CHECKSUM_ERR_IGNORE; |
832b75ed | 373 | } else { |
2127e193 | 374 | badarg = true; |
832b75ed GG |
375 | } |
376 | break; | |
377 | case 'r': | |
378 | { | |
379 | int i; | |
380 | char *s; | |
381 | ||
382 | // split_report_arg() may modify its first argument string, so use a | |
383 | // copy of optarg in case we want optarg for an error message. | |
384 | if (!(s = strdup(optarg))) { | |
2127e193 | 385 | throw std::bad_alloc(); |
832b75ed GG |
386 | } |
387 | if (split_report_arg(s, &i)) { | |
2127e193 | 388 | badarg = true; |
832b75ed | 389 | } else if (!strcmp(s,"ioctl")) { |
cfbba5b9 | 390 | ata_debugmode = scsi_debugmode = i; |
832b75ed | 391 | } else if (!strcmp(s,"ataioctl")) { |
cfbba5b9 | 392 | ata_debugmode = i; |
832b75ed | 393 | } else if (!strcmp(s,"scsiioctl")) { |
cfbba5b9 | 394 | scsi_debugmode = i; |
832b75ed | 395 | } else { |
2127e193 | 396 | badarg = true; |
832b75ed GG |
397 | } |
398 | free(s); | |
399 | } | |
400 | break; | |
d008864d | 401 | |
832b75ed | 402 | case 's': |
d008864d | 403 | case opt_smart: // --smart |
832b75ed | 404 | if (!strcmp(optarg,"on")) { |
2127e193 GI |
405 | ataopts.smart_enable = scsiopts.smart_enable = true; |
406 | ataopts.smart_disable = scsiopts.smart_disable = false; | |
832b75ed | 407 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
408 | ataopts.smart_disable = scsiopts.smart_disable = true; |
409 | ataopts.smart_enable = scsiopts.smart_enable = false; | |
d008864d GI |
410 | } else if (optchar == 's') { |
411 | goto case_s_continued; // --set, see below | |
832b75ed | 412 | } else { |
2127e193 | 413 | badarg = true; |
832b75ed GG |
414 | } |
415 | break; | |
d008864d | 416 | |
832b75ed GG |
417 | case 'o': |
418 | if (!strcmp(optarg,"on")) { | |
2127e193 GI |
419 | ataopts.smart_auto_offl_enable = true; |
420 | ataopts.smart_auto_offl_disable = false; | |
832b75ed | 421 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
422 | ataopts.smart_auto_offl_disable = true; |
423 | ataopts.smart_auto_offl_enable = false; | |
832b75ed | 424 | } else { |
2127e193 | 425 | badarg = true; |
832b75ed GG |
426 | } |
427 | break; | |
428 | case 'S': | |
429 | if (!strcmp(optarg,"on")) { | |
2127e193 GI |
430 | ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = true; |
431 | ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = false; | |
832b75ed | 432 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
433 | ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = true; |
434 | ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = false; | |
832b75ed | 435 | } else { |
2127e193 | 436 | badarg = true; |
832b75ed GG |
437 | } |
438 | break; | |
439 | case 'H': | |
2127e193 | 440 | ataopts.smart_check_status = scsiopts.smart_check_status = true; |
d008864d | 441 | scsiopts.smart_ss_media_log = true; |
832b75ed GG |
442 | break; |
443 | case 'F': | |
ee38a438 | 444 | if (!strcmp(optarg, "swapid")) |
2127e193 | 445 | ataopts.fix_swapped_id = true; |
ee38a438 | 446 | else if (!parse_firmwarebug_def(optarg, ataopts.firmwarebugs)) |
2127e193 | 447 | badarg = true; |
832b75ed GG |
448 | break; |
449 | case 'c': | |
2127e193 | 450 | ataopts.smart_general_values = true; |
832b75ed GG |
451 | break; |
452 | case 'A': | |
2127e193 | 453 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true; |
832b75ed GG |
454 | break; |
455 | case 'l': | |
456 | if (!strcmp(optarg,"error")) { | |
2127e193 | 457 | ataopts.smart_error_log = scsiopts.smart_error_log = true; |
832b75ed | 458 | } else if (!strcmp(optarg,"selftest")) { |
2127e193 | 459 | ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; |
832b75ed | 460 | } else if (!strcmp(optarg, "selective")) { |
2127e193 | 461 | ataopts.smart_selective_selftest_log = true; |
832b75ed | 462 | } else if (!strcmp(optarg,"directory")) { |
2127e193 GI |
463 | ataopts.smart_logdir = ataopts.gp_logdir = true; // SMART+GPL |
464 | } else if (!strcmp(optarg,"directory,s")) { | |
465 | ataopts.smart_logdir = true; // SMART | |
466 | } else if (!strcmp(optarg,"directory,g")) { | |
467 | ataopts.gp_logdir = true; // GPL | |
468 | } else if (!strcmp(optarg,"sasphy")) { | |
469 | scsiopts.sasphy = true; | |
470 | } else if (!strcmp(optarg,"sasphy,reset")) { | |
471 | scsiopts.sasphy = scsiopts.sasphy_reset = true; | |
472 | } else if (!strcmp(optarg,"sataphy")) { | |
473 | ataopts.sataphy = true; | |
474 | } else if (!strcmp(optarg,"sataphy,reset")) { | |
475 | ataopts.sataphy = ataopts.sataphy_reset = true; | |
4d59bff9 | 476 | } else if (!strcmp(optarg,"background")) { |
2127e193 | 477 | scsiopts.smart_background_log = true; |
d008864d GI |
478 | } else if (!strcmp(optarg,"ssd")) { |
479 | ataopts.devstat_ssd_page = true; | |
480 | scsiopts.smart_ss_media_log = true; | |
7f0798ef GI |
481 | } else if (!strcmp(optarg,"scterc")) { |
482 | ataopts.sct_erc_get = true; | |
a37e7145 | 483 | } else if (!strcmp(optarg,"scttemp")) { |
2127e193 | 484 | ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; |
a37e7145 | 485 | } else if (!strcmp(optarg,"scttempsts")) { |
2127e193 | 486 | ataopts.sct_temp_sts = true; |
a37e7145 | 487 | } else if (!strcmp(optarg,"scttemphist")) { |
2127e193 GI |
488 | ataopts.sct_temp_hist = true; |
489 | ||
d008864d GI |
490 | } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) { |
491 | unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg); | |
492 | if (!( sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1 | |
493 | && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) { | |
ee38a438 | 494 | snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n"); |
d008864d GI |
495 | badarg = true; |
496 | } | |
497 | ataopts.sct_temp_int = interval; | |
498 | ataopts.sct_temp_int_pers = (n2 == len); | |
499 | ||
500 | } else if (!strncmp(optarg, "devstat", sizeof("devstat")-1)) { | |
501 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
502 | unsigned val = ~0; | |
503 | sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2); | |
504 | if (n1 == len) | |
505 | ataopts.devstat_all_pages = true; | |
293b5ab8 JD |
506 | else { |
507 | if (n2 != len) // retry with hex | |
508 | sscanf(optarg, "devstat,0x%x%n", &val, &n2); | |
509 | if (n2 == len && val <= 0xff) | |
510 | ataopts.devstat_pages.push_back(val); | |
511 | else | |
512 | badarg = true; | |
513 | } | |
d008864d | 514 | |
2127e193 GI |
515 | } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) { |
516 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
517 | unsigned val = 8; | |
518 | sscanf(optarg, "xerror%n,error%n", &n1, &n2); | |
519 | if (!(n1 == len || n2 == len)) { | |
520 | n1 = n2 = -1; | |
521 | sscanf(optarg, "xerror,%u%n,error%n", &val, &n1, &n2); | |
522 | } | |
523 | if ((n1 == len || n2 == len) && val > 0) { | |
524 | ataopts.smart_ext_error_log = val; | |
525 | ataopts.retry_error_log = (n2 == len); | |
526 | } | |
527 | else | |
528 | badarg = true; | |
529 | ||
530 | } else if (!strncmp(optarg, "xselftest", sizeof("xselftest")-1)) { | |
531 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
532 | unsigned val = 25; | |
533 | sscanf(optarg, "xselftest%n,selftest%n", &n1, &n2); | |
534 | if (!(n1 == len || n2 == len)) { | |
535 | n1 = n2 = -1; | |
536 | sscanf(optarg, "xselftest,%u%n,selftest%n", &val, &n1, &n2); | |
537 | } | |
538 | if ((n1 == len || n2 == len) && val > 0) { | |
539 | ataopts.smart_ext_selftest_log = val; | |
540 | ataopts.retry_selftest_log = (n2 == len); | |
541 | } | |
542 | else | |
543 | badarg = true; | |
544 | ||
7f0798ef GI |
545 | } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { |
546 | unsigned rt = ~0, wt = ~0; int n = -1; | |
547 | sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); | |
548 | if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { | |
549 | ataopts.sct_erc_set = true; | |
550 | ataopts.sct_erc_readtime = rt; | |
551 | ataopts.sct_erc_writetime = wt; | |
552 | } | |
553 | else { | |
ee38a438 | 554 | snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); |
7f0798ef GI |
555 | badarg = true; |
556 | } | |
2127e193 GI |
557 | } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) |
558 | || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { | |
559 | unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; | |
560 | int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); | |
561 | sscanf(optarg, "%*[a-z],0x%x%n,%u%n%c%u%n", | |
562 | &logaddr, &n1, &page, &n2, &sign, &nsectors, &n3); | |
563 | if (len > n2 && n3 == -1 && !strcmp(optarg+n2, "-max")) { | |
564 | nsectors = ~0U; sign = '+'; n3 = len; | |
565 | } | |
566 | bool gpl = (optarg[0] == 'g'); | |
567 | const char * erropt = (gpl ? "gplog" : "smartlog"); | |
568 | if (!( n1 == len || n2 == len | |
569 | || (n3 == len && (sign == '+' || sign == '-')))) { | |
ee38a438 | 570 | snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt); |
2127e193 GI |
571 | badarg = true; |
572 | } | |
573 | else if (!( logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU) | |
574 | && 0 < nsectors | |
575 | && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U) | |
576 | && (sign != '-' || page <= nsectors) )) { | |
ee38a438 | 577 | snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt); |
2127e193 GI |
578 | badarg = true; |
579 | } | |
580 | else { | |
581 | ata_log_request req; | |
582 | req.gpl = gpl; req.logaddr = logaddr; req.page = page; | |
583 | req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors); | |
584 | ataopts.log_requests.push_back(req); | |
585 | } | |
832b75ed | 586 | } else { |
2127e193 | 587 | badarg = true; |
832b75ed GG |
588 | } |
589 | break; | |
590 | case 'i': | |
2127e193 GI |
591 | ataopts.drive_info = scsiopts.drive_info = true; |
592 | break; | |
ee38a438 GI |
593 | |
594 | case opt_identify: | |
595 | ataopts.identify_word_level = ataopts.identify_bit_level = 0; | |
596 | if (optarg) { | |
597 | for (int i = 0; optarg[i]; i++) { | |
598 | switch (optarg[i]) { | |
599 | case 'w': ataopts.identify_word_level = 1; break; | |
600 | case 'n': ataopts.identify_bit_level = -1; break; | |
601 | case 'v': ataopts.identify_bit_level = 1; break; | |
602 | case 'b': ataopts.identify_bit_level = 2; break; | |
603 | default: badarg = true; | |
604 | } | |
605 | } | |
606 | } | |
607 | break; | |
608 | ||
832b75ed | 609 | case 'a': |
2127e193 GI |
610 | ataopts.drive_info = scsiopts.drive_info = true; |
611 | ataopts.smart_check_status = scsiopts.smart_check_status = true; | |
612 | ataopts.smart_general_values = true; | |
613 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true; | |
614 | ataopts.smart_error_log = scsiopts.smart_error_log = true; | |
615 | ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; | |
616 | ataopts.smart_selective_selftest_log = true; | |
617 | /* scsiopts.smart_background_log = true; */ | |
d008864d | 618 | scsiopts.smart_ss_media_log = true; |
2127e193 GI |
619 | break; |
620 | case 'x': | |
621 | ataopts.drive_info = scsiopts.drive_info = true; | |
622 | ataopts.smart_check_status = scsiopts.smart_check_status = true; | |
623 | ataopts.smart_general_values = true; | |
624 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true; | |
625 | ataopts.smart_ext_error_log = 8; | |
626 | ataopts.retry_error_log = true; | |
627 | ataopts.smart_ext_selftest_log = 25; | |
628 | ataopts.retry_selftest_log = true; | |
629 | scsiopts.smart_error_log = scsiopts.smart_selftest_log = true; | |
630 | ataopts.smart_selective_selftest_log = true; | |
631 | ataopts.smart_logdir = ataopts.gp_logdir = true; | |
632 | ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; | |
7f0798ef | 633 | ataopts.sct_erc_get = true; |
3d17a85c | 634 | ataopts.sct_wcache_reorder_get = true; |
ee38a438 | 635 | ataopts.devstat_all_pages = true; |
2127e193 | 636 | ataopts.sataphy = true; |
d008864d GI |
637 | ataopts.get_set_used = true; |
638 | ataopts.get_aam = ataopts.get_apm = true; | |
639 | ataopts.get_security = true; | |
640 | ataopts.get_lookahead = ataopts.get_wcache = true; | |
ee38a438 | 641 | scsiopts.get_rcd = scsiopts.get_wce = true; |
2127e193 | 642 | scsiopts.smart_background_log = true; |
d008864d | 643 | scsiopts.smart_ss_media_log = true; |
2127e193 | 644 | scsiopts.sasphy = true; |
a7e8ffec | 645 | if (!output_format_set) |
e165493d | 646 | ataopts.output_format |= ata_print_options::FMT_BRIEF; |
832b75ed GG |
647 | break; |
648 | case 'v': | |
649 | // parse vendor-specific definitions of attributes | |
650 | if (!strcmp(optarg,"help")) { | |
cfbba5b9 | 651 | printing_is_off = false; |
832b75ed | 652 | printslogan(); |
2127e193 GI |
653 | pout("The valid arguments to -v are:\n\thelp\n%s\n", |
654 | create_vendor_attribute_arg_list().c_str()); | |
832b75ed GG |
655 | EXIT(0); |
656 | } | |
bed94269 | 657 | if (!parse_attribute_def(optarg, ataopts.attribute_defs, PRIOR_USER)) |
2127e193 | 658 | badarg = true; |
832b75ed GG |
659 | break; |
660 | case 'P': | |
661 | if (!strcmp(optarg, "use")) { | |
2127e193 | 662 | ataopts.ignore_presets = false; |
832b75ed | 663 | } else if (!strcmp(optarg, "ignore")) { |
2127e193 | 664 | ataopts.ignore_presets = true; |
832b75ed | 665 | } else if (!strcmp(optarg, "show")) { |
2127e193 | 666 | ataopts.show_presets = true; |
832b75ed | 667 | } else if (!strcmp(optarg, "showall")) { |
2127e193 GI |
668 | if (!no_defaultdb && !read_default_drive_databases()) |
669 | EXIT(FAILCMD); | |
832b75ed GG |
670 | if (optind < argc) { // -P showall MODEL [FIRMWARE] |
671 | int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL)); | |
672 | EXIT(cnt); // report #matches | |
673 | } | |
674 | if (showallpresets()) | |
675 | EXIT(FAILCMD); // report regexp syntax error | |
676 | EXIT(0); | |
677 | } else { | |
2127e193 | 678 | badarg = true; |
832b75ed GG |
679 | } |
680 | break; | |
681 | case 't': | |
682 | if (!strcmp(optarg,"offline")) { | |
2127e193 GI |
683 | testcnt++; |
684 | ataopts.smart_selftest_type = OFFLINE_FULL_SCAN; | |
685 | scsiopts.smart_default_selftest = true; | |
832b75ed | 686 | } else if (!strcmp(optarg,"short")) { |
2127e193 GI |
687 | testcnt++; |
688 | ataopts.smart_selftest_type = SHORT_SELF_TEST; | |
689 | scsiopts.smart_short_selftest = true; | |
832b75ed | 690 | } else if (!strcmp(optarg,"long")) { |
2127e193 GI |
691 | testcnt++; |
692 | ataopts.smart_selftest_type = EXTEND_SELF_TEST; | |
693 | scsiopts.smart_extend_selftest = true; | |
832b75ed | 694 | } else if (!strcmp(optarg,"conveyance")) { |
2127e193 GI |
695 | testcnt++; |
696 | ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST; | |
d008864d GI |
697 | } else if (!strcmp(optarg,"force")) { |
698 | ataopts.smart_selftest_force = true; | |
ee38a438 | 699 | scsiopts.smart_selftest_force = true; |
832b75ed | 700 | } else if (!strcmp(optarg,"afterselect,on")) { |
2127e193 GI |
701 | // scan remainder of disk after doing selected segment |
702 | ataopts.smart_selective_args.scan_after_select = 2; | |
832b75ed | 703 | } else if (!strcmp(optarg,"afterselect,off")) { |
2127e193 GI |
704 | // don't scan remainder of disk after doing selected segments |
705 | ataopts.smart_selective_args.scan_after_select = 1; | |
832b75ed GG |
706 | } else if (!strncmp(optarg,"pending,",strlen("pending,"))) { |
707 | // parse number of minutes that test should be pending | |
708 | int i; | |
709 | char *tailptr=NULL; | |
710 | errno=0; | |
711 | i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10); | |
712 | if (errno || *tailptr != '\0') { | |
ee38a438 | 713 | snprintf(extraerror, sizeof(extraerror), "Option -t pending,N requires N to be a non-negative integer\n"); |
2127e193 | 714 | badarg = true; |
832b75ed | 715 | } else if (i<0 || i>65535) { |
ee38a438 | 716 | snprintf(extraerror, sizeof(extraerror), "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); |
2127e193 | 717 | badarg = true; |
832b75ed | 718 | } else { |
2127e193 | 719 | ataopts.smart_selective_args.pending_time = i+1; |
832b75ed GG |
720 | } |
721 | } else if (!strncmp(optarg,"select",strlen("select"))) { | |
e9583e0c GI |
722 | if (ataopts.smart_selective_args.num_spans == 0) |
723 | testcnt++; | |
a37e7145 GG |
724 | // parse range of LBAs to test |
725 | uint64_t start, stop; int mode; | |
726 | if (split_selective_arg(optarg, &start, &stop, &mode)) { | |
ee38a438 | 727 | snprintf(extraerror, sizeof(extraerror), "Option -t select,M-N must have non-negative integer M and N\n"); |
2127e193 | 728 | badarg = true; |
832b75ed | 729 | } else { |
2127e193 | 730 | if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) { |
832b75ed | 731 | if (start > stop) { |
d2e702cf | 732 | snprintf(extraerror, sizeof(extraerror), "ERROR: Start LBA (%" PRIu64 ") > ending LBA (%" PRId64 ") in argument \"%s\"\n", |
832b75ed GG |
733 | start, stop, optarg); |
734 | } else { | |
ee38a438 | 735 | snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be" |
832b75ed GG |
736 | " defined\n"); |
737 | } | |
2127e193 | 738 | badarg = true; |
832b75ed | 739 | } |
2127e193 GI |
740 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].start = start; |
741 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].end = stop; | |
742 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].mode = mode; | |
743 | ataopts.smart_selective_args.num_spans++; | |
744 | ataopts.smart_selftest_type = SELECTIVE_SELF_TEST; | |
832b75ed | 745 | } |
d008864d | 746 | } else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) { |
ee38a438 | 747 | snprintf(extraerror, sizeof(extraerror), "-t scttempint is no longer supported, use -l scttempint instead\n"); |
d008864d | 748 | badarg = true; |
cfbba5b9 GI |
749 | } else if (!strncmp(optarg, "vendor,", sizeof("vendor,")-1)) { |
750 | unsigned subcmd = ~0U; int n = -1; | |
751 | if (!( sscanf(optarg, "%*[a-z],0x%x%n", &subcmd, &n) == 1 | |
752 | && subcmd <= 0xff && n == (int)strlen(optarg))) { | |
ee38a438 | 753 | snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n"); |
cfbba5b9 GI |
754 | badarg = true; |
755 | } | |
756 | else | |
757 | ataopts.smart_selftest_type = subcmd; | |
832b75ed | 758 | } else { |
2127e193 | 759 | badarg = true; |
832b75ed GG |
760 | } |
761 | break; | |
762 | case 'C': | |
2127e193 | 763 | captive = true; |
832b75ed GG |
764 | break; |
765 | case 'X': | |
2127e193 GI |
766 | testcnt++; |
767 | scsiopts.smart_selftest_abort = true; | |
768 | ataopts.smart_selftest_type = ABORT_SELF_TEST; | |
832b75ed | 769 | break; |
4d59bff9 GG |
770 | case 'n': |
771 | // skip disk check if in low-power mode | |
772 | if (!strcmp(optarg, "never")) | |
2127e193 | 773 | ataopts.powermode = 1; // do not skip, but print mode |
4d59bff9 | 774 | else if (!strcmp(optarg, "sleep")) |
2127e193 | 775 | ataopts.powermode = 2; |
4d59bff9 | 776 | else if (!strcmp(optarg, "standby")) |
2127e193 | 777 | ataopts.powermode = 3; |
4d59bff9 | 778 | else if (!strcmp(optarg, "idle")) |
2127e193 | 779 | ataopts.powermode = 4; |
4d59bff9 | 780 | else |
2127e193 GI |
781 | badarg = true; |
782 | break; | |
a7e8ffec | 783 | case 'f': |
e165493d GI |
784 | if (!strcmp(optarg, "old")) { |
785 | ataopts.output_format &= ~ata_print_options::FMT_BRIEF; | |
786 | output_format_set = true; | |
787 | } | |
788 | else if (!strcmp(optarg, "brief")) { | |
789 | ataopts.output_format |= ata_print_options::FMT_BRIEF; | |
790 | output_format_set = true; | |
a7e8ffec | 791 | } |
e165493d GI |
792 | else if (!strcmp(optarg, "hex")) |
793 | ataopts.output_format |= ata_print_options::FMT_HEX_ID | |
794 | | ata_print_options::FMT_HEX_VAL; | |
795 | else if (!strcmp(optarg, "hex,id")) | |
796 | ataopts.output_format |= ata_print_options::FMT_HEX_ID; | |
797 | else if (!strcmp(optarg, "hex,val")) | |
798 | ataopts.output_format |= ata_print_options::FMT_HEX_VAL; | |
799 | else | |
800 | badarg = true; | |
a7e8ffec | 801 | break; |
2127e193 GI |
802 | case 'B': |
803 | { | |
804 | const char * path = optarg; | |
805 | if (*path == '+' && path[1]) | |
806 | path++; | |
807 | else | |
808 | no_defaultdb = true; | |
809 | if (!read_drive_database(path)) | |
810 | EXIT(FAILCMD); | |
811 | } | |
4d59bff9 | 812 | break; |
832b75ed | 813 | case 'h': |
cfbba5b9 | 814 | printing_is_off = false; |
832b75ed GG |
815 | printslogan(); |
816 | Usage(); | |
817 | EXIT(0); | |
818 | break; | |
e9583e0c | 819 | |
d008864d GI |
820 | case 'g': |
821 | case_s_continued: // -s, see above | |
822 | case opt_set: // --set | |
823 | { | |
824 | ataopts.get_set_used = true; | |
825 | bool get = (optchar == 'g'); | |
826 | char name[16+1]; unsigned val; | |
827 | int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); | |
828 | if (sscanf(optarg, "%16[^,=]%n%*[,=]%n%u%n", name, &n1, &n2, &val, &n3) >= 1 | |
829 | && (n1 == len || (!get && n2 > 0))) { | |
830 | bool on = (n2 > 0 && !strcmp(optarg+n2, "on")); | |
831 | bool off = (n2 > 0 && !strcmp(optarg+n2, "off")); | |
832 | if (n3 != len) | |
833 | val = ~0U; | |
834 | ||
835 | if (get && !strcmp(name, "all")) { | |
836 | ataopts.get_aam = ataopts.get_apm = true; | |
837 | ataopts.get_security = true; | |
838 | ataopts.get_lookahead = ataopts.get_wcache = true; | |
ee38a438 | 839 | scsiopts.get_rcd = scsiopts.get_wce = true; |
d008864d GI |
840 | } |
841 | else if (!strcmp(name, "aam")) { | |
842 | if (get) | |
843 | ataopts.get_aam = true; | |
844 | else if (off) | |
845 | ataopts.set_aam = -1; | |
846 | else if (val <= 254) | |
847 | ataopts.set_aam = val + 1; | |
848 | else { | |
ee38a438 | 849 | snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n"); |
d008864d GI |
850 | badarg = true; |
851 | } | |
852 | } | |
853 | else if (!strcmp(name, "apm")) { | |
854 | if (get) | |
855 | ataopts.get_apm = true; | |
856 | else if (off) | |
857 | ataopts.set_apm = -1; | |
858 | else if (1 <= val && val <= 254) | |
859 | ataopts.set_apm = val + 1; | |
860 | else { | |
ee38a438 | 861 | snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n"); |
d008864d GI |
862 | badarg = true; |
863 | } | |
864 | } | |
865 | else if (!strcmp(name, "lookahead")) { | |
ee38a438 | 866 | if (get) { |
d008864d | 867 | ataopts.get_lookahead = true; |
ee38a438 | 868 | } |
d008864d GI |
869 | else if (off) |
870 | ataopts.set_lookahead = -1; | |
871 | else if (on) | |
872 | ataopts.set_lookahead = 1; | |
873 | else | |
874 | badarg = true; | |
875 | } | |
3d17a85c GI |
876 | else if (!strcmp(name, "wcreorder")) { |
877 | if (get) { | |
878 | ataopts.sct_wcache_reorder_get = true; | |
879 | } | |
880 | else if (off) | |
881 | ataopts.sct_wcache_reorder_set = -1; | |
882 | else if (on) | |
883 | ataopts.sct_wcache_reorder_set = 1; | |
884 | else | |
885 | badarg = true; | |
886 | } | |
ee38a438 GI |
887 | else if (!strcmp(name, "rcache")) { |
888 | if (get) | |
889 | scsiopts.get_rcd = true; | |
890 | else if (off) | |
891 | scsiopts.set_rcd = -1; | |
892 | else if (on) | |
893 | scsiopts.set_rcd = 1; | |
894 | else | |
895 | badarg = true; | |
896 | } | |
d008864d GI |
897 | else if (get && !strcmp(name, "security")) { |
898 | ataopts.get_security = true; | |
899 | } | |
900 | else if (!get && !strcmp(optarg, "security-freeze")) { | |
901 | ataopts.set_security_freeze = true; | |
902 | } | |
903 | else if (!get && !strcmp(optarg, "standby,now")) { | |
904 | ataopts.set_standby_now = true; | |
905 | } | |
906 | else if (!get && !strcmp(name, "standby")) { | |
907 | if (off) | |
908 | ataopts.set_standby = 0 + 1; | |
909 | else if (val <= 255) | |
910 | ataopts.set_standby = val + 1; | |
911 | else { | |
ee38a438 | 912 | snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n"); |
d008864d GI |
913 | badarg = true; |
914 | } | |
915 | } | |
916 | else if (!strcmp(name, "wcache")) { | |
ee38a438 | 917 | if (get) { |
d008864d | 918 | ataopts.get_wcache = true; |
ee38a438 GI |
919 | scsiopts.get_wce = true; |
920 | } | |
921 | else if (off) { | |
d008864d | 922 | ataopts.set_wcache = -1; |
ee38a438 GI |
923 | scsiopts.set_wce = -1; |
924 | } | |
925 | else if (on) { | |
d008864d | 926 | ataopts.set_wcache = 1; |
ee38a438 GI |
927 | scsiopts.set_wce = 1; |
928 | } | |
d008864d GI |
929 | else |
930 | badarg = true; | |
931 | } | |
932 | else | |
933 | badarg = true; | |
934 | } | |
935 | else | |
936 | badarg = true; | |
937 | } | |
938 | break; | |
939 | ||
e9583e0c GI |
940 | case opt_scan: |
941 | case opt_scan_open: | |
942 | scan = optchar; | |
943 | break; | |
944 | ||
832b75ed GG |
945 | case '?': |
946 | default: | |
cfbba5b9 | 947 | printing_is_off = false; |
832b75ed | 948 | printslogan(); |
832b75ed GG |
949 | // Point arg to the argument in which this option was found. |
950 | arg = argv[optind-1]; | |
951 | // Check whether the option is a long option that doesn't map to -h. | |
952 | if (arg[1] == '-' && optchar != 'h') { | |
953 | // Iff optopt holds a valid option then argument must be missing. | |
d008864d | 954 | if (optopt && (optopt >= opt_scan || strchr(shortopts, optopt))) { |
832b75ed GG |
955 | pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); |
956 | printvalidarglistmessage(optopt); | |
957 | } else | |
958 | pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2); | |
959 | if (extraerror[0]) | |
960 | pout("=======> %s", extraerror); | |
961 | UsageSummary(); | |
962 | EXIT(FAILCMD); | |
963 | } | |
d008864d | 964 | if (0 < optopt && optopt < '~') { |
832b75ed GG |
965 | // Iff optopt holds a valid option then argument must be |
966 | // missing. Note (BA) this logic seems to fail using Solaris | |
967 | // getopt! | |
968 | if (strchr(shortopts, optopt) != NULL) { | |
969 | pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt); | |
970 | printvalidarglistmessage(optopt); | |
971 | } else | |
972 | pout("=======> UNRECOGNIZED OPTION: %c\n",optopt); | |
973 | if (extraerror[0]) | |
974 | pout("=======> %s", extraerror); | |
975 | UsageSummary(); | |
976 | EXIT(FAILCMD); | |
977 | } | |
978 | Usage(); | |
979 | EXIT(0); | |
980 | } // closes switch statement to process command-line options | |
981 | ||
982 | // Check to see if option had an unrecognized or incorrect argument. | |
983 | if (badarg) { | |
984 | printslogan(); | |
985 | // It would be nice to print the actual option name given by the user | |
986 | // here, but we just print the short form. Please fix this if you know | |
987 | // a clean way to do it. | |
d008864d GI |
988 | char optstr[] = { (char)optchar, 0 }; |
989 | pout("=======> INVALID ARGUMENT TO -%s: %s\n", | |
ee38a438 GI |
990 | (optchar == opt_identify ? "-identify" : |
991 | optchar == opt_set ? "-set" : | |
d008864d | 992 | optchar == opt_smart ? "-smart" : optstr), optarg); |
832b75ed GG |
993 | printvalidarglistmessage(optchar); |
994 | if (extraerror[0]) | |
995 | pout("=======> %s", extraerror); | |
996 | UsageSummary(); | |
997 | EXIT(FAILCMD); | |
998 | } | |
999 | } | |
e9583e0c GI |
1000 | |
1001 | // Special handling of --scan, --scanopen | |
1002 | if (scan) { | |
cfbba5b9 GI |
1003 | // Read or init drive database to allow USB ID check. |
1004 | if (!no_defaultdb && !read_default_drive_databases()) | |
1005 | EXIT(FAILCMD); | |
1006 | scan_devices(type, (scan == opt_scan_open), argv + optind); | |
e9583e0c GI |
1007 | EXIT(0); |
1008 | } | |
1009 | ||
832b75ed GG |
1010 | // At this point we have processed all command-line options. If the |
1011 | // print output is switchable, then start with the print output | |
1012 | // turned off | |
cfbba5b9 GI |
1013 | if (printing_is_switchable) |
1014 | printing_is_off = true; | |
832b75ed GG |
1015 | |
1016 | // error message if user has asked for more than one test | |
2127e193 | 1017 | if (testcnt > 1) { |
cfbba5b9 | 1018 | printing_is_off = false; |
832b75ed GG |
1019 | printslogan(); |
1020 | pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); | |
1021 | UsageSummary(); | |
1022 | EXIT(FAILCMD); | |
1023 | } | |
1024 | ||
1025 | // error message if user has set selective self-test options without | |
1026 | // asking for a selective self-test | |
2127e193 GI |
1027 | if ( (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select) |
1028 | && !ataopts.smart_selective_args.num_spans) { | |
cfbba5b9 | 1029 | printing_is_off = false; |
832b75ed | 1030 | printslogan(); |
2127e193 | 1031 | if (ataopts.smart_selective_args.pending_time) |
832b75ed GG |
1032 | pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); |
1033 | else | |
1034 | pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n"); | |
1035 | UsageSummary(); | |
1036 | EXIT(FAILCMD); | |
1037 | } | |
1038 | ||
1039 | // If captive option was used, change test type if appropriate. | |
2127e193 GI |
1040 | if (captive) |
1041 | switch (ataopts.smart_selftest_type) { | |
1042 | case SHORT_SELF_TEST: | |
1043 | ataopts.smart_selftest_type = SHORT_CAPTIVE_SELF_TEST; | |
1044 | scsiopts.smart_short_selftest = false; | |
1045 | scsiopts.smart_short_cap_selftest = true; | |
1046 | break; | |
1047 | case EXTEND_SELF_TEST: | |
1048 | ataopts.smart_selftest_type = EXTEND_CAPTIVE_SELF_TEST; | |
1049 | scsiopts.smart_extend_selftest = false; | |
1050 | scsiopts.smart_extend_cap_selftest = true; | |
1051 | break; | |
1052 | case CONVEYANCE_SELF_TEST: | |
1053 | ataopts.smart_selftest_type = CONVEYANCE_CAPTIVE_SELF_TEST; | |
1054 | break; | |
1055 | case SELECTIVE_SELF_TEST: | |
1056 | ataopts.smart_selftest_type = SELECTIVE_CAPTIVE_SELF_TEST; | |
1057 | break; | |
1058 | } | |
1059 | ||
832b75ed GG |
1060 | // From here on, normal operations... |
1061 | printslogan(); | |
1062 | ||
1063 | // Warn if the user has provided no device name | |
1064 | if (argc-optind<1){ | |
1065 | pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); | |
1066 | UsageSummary(); | |
1067 | EXIT(FAILCMD); | |
1068 | } | |
1069 | ||
1070 | // Warn if the user has provided more than one device name | |
1071 | if (argc-optind>1){ | |
1072 | int i; | |
1073 | pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); | |
1074 | pout("You have provided %d device names:\n",argc-optind); | |
1075 | for (i=0; i<argc-optind; i++) | |
1076 | pout("%s\n",argv[optind+i]); | |
1077 | UsageSummary(); | |
1078 | EXIT(FAILCMD); | |
2127e193 GI |
1079 | } |
1080 | ||
1081 | // Read or init drive database | |
1082 | if (!no_defaultdb && !read_default_drive_databases()) | |
1083 | EXIT(FAILCMD); | |
1084 | ||
1085 | return type; | |
832b75ed GG |
1086 | } |
1087 | ||
cfbba5b9 | 1088 | // Printing function (controlled by global printing_is_off) |
832b75ed GG |
1089 | // [From GLIBC Manual: Since the prototype doesn't specify types for |
1090 | // optional arguments, in a call to a variadic function the default | |
1091 | // argument promotions are performed on the optional argument | |
1092 | // values. This means the objects of type char or short int (whether | |
1093 | // signed or not) are promoted to either int or unsigned int, as | |
1094 | // appropriate.] | |
4d59bff9 | 1095 | void pout(const char *fmt, ...){ |
832b75ed GG |
1096 | va_list ap; |
1097 | ||
1098 | // initialize variable argument list | |
1099 | va_start(ap,fmt); | |
cfbba5b9 | 1100 | if (printing_is_off) { |
832b75ed GG |
1101 | va_end(ap); |
1102 | return; | |
1103 | } | |
1104 | ||
1105 | // print out | |
1106 | vprintf(fmt,ap); | |
1107 | va_end(ap); | |
1108 | fflush(stdout); | |
1109 | return; | |
1110 | } | |
1111 | ||
cfbba5b9 GI |
1112 | // Globals to set failuretest() policy |
1113 | bool failuretest_conservative = false; | |
1114 | unsigned char failuretest_permissive = 0; | |
832b75ed | 1115 | |
cfbba5b9 GI |
1116 | // Compares failure type to policy in effect, and either exits or |
1117 | // simply returns to the calling routine. | |
1118 | // Used in ataprint.cpp and scsiprint.cpp. | |
1119 | void failuretest(failure_type type, int returnvalue) | |
1120 | { | |
1121 | // If this is an error in an "optional" SMART command | |
1122 | if (type == OPTIONAL_CMD) { | |
1123 | if (!failuretest_conservative) | |
1124 | return; | |
1125 | pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n"); | |
1126 | EXIT(returnvalue); | |
1127 | } | |
832b75ed | 1128 | |
cfbba5b9 GI |
1129 | // If this is an error in a "mandatory" SMART command |
1130 | if (type == MANDATORY_CMD) { | |
1131 | if (failuretest_permissive--) | |
1132 | return; | |
1133 | pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n"); | |
1134 | EXIT(returnvalue); | |
1135 | } | |
1136 | ||
1137 | throw std::logic_error("failuretest: Unknown type"); | |
832b75ed GG |
1138 | } |
1139 | ||
2127e193 GI |
1140 | // Used to warn users about invalid checksums. Called from atacmds.cpp. |
1141 | // Action to be taken may be altered by the user. | |
1142 | void checksumwarning(const char * string) | |
1143 | { | |
1144 | // user has asked us to ignore checksum errors | |
1145 | if (checksum_err_mode == CHECKSUM_ERR_IGNORE) | |
1146 | return; | |
832b75ed | 1147 | |
2127e193 | 1148 | pout("Warning! %s error: invalid SMART checksum.\n", string); |
832b75ed | 1149 | |
2127e193 GI |
1150 | // user has asked us to fail on checksum errors |
1151 | if (checksum_err_mode == CHECKSUM_ERR_EXIT) | |
1152 | EXIT(FAILSMART); | |
1153 | } | |
832b75ed | 1154 | |
2127e193 GI |
1155 | // Return info string about device protocol |
1156 | static const char * get_protocol_info(const smart_device * dev) | |
1157 | { | |
1158 | switch ((int)dev->is_ata() | ((int)dev->is_scsi() << 1)) { | |
1159 | case 0x1: return "ATA"; | |
1160 | case 0x2: return "SCSI"; | |
1161 | case 0x3: return "ATA+SCSI"; | |
1162 | default: return "Unknown"; | |
1163 | } | |
1164 | } | |
832b75ed | 1165 | |
e9583e0c | 1166 | // Device scan |
cfbba5b9 GI |
1167 | // smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...] |
1168 | void scan_devices(const char * type, bool with_open, char ** argv) | |
e9583e0c | 1169 | { |
cfbba5b9 GI |
1170 | bool dont_print = !(ata_debugmode || scsi_debugmode); |
1171 | ||
1172 | const char * pattern = 0; | |
1173 | int ai = 0; | |
1174 | if (argv[ai] && argv[ai][0] != '-') | |
1175 | pattern = argv[ai++]; | |
e9583e0c | 1176 | |
cfbba5b9 GI |
1177 | smart_device_list devlist; |
1178 | printing_is_off = dont_print; | |
e9583e0c | 1179 | bool ok = smi()->scan_smart_devices(devlist, type , pattern); |
cfbba5b9 | 1180 | printing_is_off = false; |
e9583e0c GI |
1181 | |
1182 | if (!ok) { | |
cfbba5b9 | 1183 | pout("# scan_smart_devices: %s\n", smi()->get_errmsg()); |
e9583e0c GI |
1184 | return; |
1185 | } | |
1186 | ||
1187 | for (unsigned i = 0; i < devlist.size(); i++) { | |
cfbba5b9 | 1188 | smart_device_auto_ptr dev( devlist.release(i) ); |
e9583e0c | 1189 | |
e9583e0c | 1190 | if (with_open) { |
cfbba5b9 GI |
1191 | printing_is_off = dont_print; |
1192 | dev.replace ( dev->autodetect_open() ); | |
1193 | printing_is_off = false; | |
e9583e0c | 1194 | |
cfbba5b9 GI |
1195 | if (!dev->is_open()) { |
1196 | pout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(), | |
1197 | dev->get_dev_type(), dev->get_info_name(), | |
1198 | get_protocol_info(dev.get()), dev->get_errmsg()); | |
1199 | continue; | |
1200 | } | |
1201 | } | |
1202 | ||
1203 | pout("%s -d %s", dev->get_dev_name(), dev->get_dev_type()); | |
1204 | if (!argv[ai]) | |
1205 | pout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get())); | |
1206 | else { | |
1207 | for (int j = ai; argv[j]; j++) | |
1208 | pout(" %s", argv[j]); | |
1209 | pout("\n"); | |
e9583e0c GI |
1210 | } |
1211 | ||
e9583e0c GI |
1212 | if (dev->is_open()) |
1213 | dev->close(); | |
1214 | } | |
1215 | } | |
1216 | ||
2127e193 | 1217 | // Main program without exception handling |
cfbba5b9 | 1218 | static int main_worker(int argc, char **argv) |
2127e193 | 1219 | { |
d2e702cf GI |
1220 | // Throw if runtime environment does not match compile time test. |
1221 | check_config(); | |
e9583e0c | 1222 | |
2127e193 GI |
1223 | // Initialize interface |
1224 | smart_interface::init(); | |
1225 | if (!smi()) | |
1226 | return 1; | |
1227 | ||
bed94269 GI |
1228 | // Parse input arguments |
1229 | ata_print_options ataopts; | |
1230 | scsi_print_options scsiopts; | |
d008864d GI |
1231 | bool print_type_only = false; |
1232 | const char * type = parse_options(argc, argv, ataopts, scsiopts, print_type_only); | |
bed94269 GI |
1233 | |
1234 | const char * name = argv[argc-1]; | |
1235 | ||
1236 | smart_device_auto_ptr dev; | |
1237 | if (!strcmp(name,"-")) { | |
1238 | // Parse "smartctl -r ataioctl,2 ..." output from stdin | |
1239 | if (type || print_type_only) { | |
ee38a438 | 1240 | pout("-d option is not allowed in conjunction with device name \"-\".\n"); |
a37e7145 GG |
1241 | UsageSummary(); |
1242 | return FAILCMD; | |
1243 | } | |
bed94269 GI |
1244 | dev = get_parsed_ata_device(smi(), name); |
1245 | } | |
1246 | else | |
1247 | // get device of appropriate type | |
1248 | dev = smi()->get_smart_device(name, type); | |
1249 | ||
1250 | if (!dev) { | |
1251 | pout("%s: %s\n", name, smi()->get_errmsg()); | |
1252 | if (type) | |
1253 | printvalidarglistmessage('d'); | |
1254 | else | |
ee38a438 | 1255 | pout("Please specify device type with the -d option.\n"); |
bed94269 GI |
1256 | UsageSummary(); |
1257 | return FAILCMD; | |
1258 | } | |
a37e7145 | 1259 | |
bed94269 GI |
1260 | if (print_type_only) |
1261 | // Report result of first autodetection | |
1262 | pout("%s: Device of type '%s' [%s] detected\n", | |
1263 | dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); | |
2127e193 | 1264 | |
bed94269 GI |
1265 | // Open device |
1266 | { | |
1267 | // Save old info | |
1268 | smart_device::device_info oldinfo = dev->get_info(); | |
2127e193 | 1269 | |
bed94269 GI |
1270 | // Open with autodetect support, may return 'better' device |
1271 | dev.replace( dev->autodetect_open() ); | |
2127e193 | 1272 | |
bed94269 GI |
1273 | // Report if type has changed |
1274 | if ((type || print_type_only) && oldinfo.dev_type != dev->get_dev_type()) | |
1275 | pout("%s: Device open changed type from '%s' to '%s'\n", | |
1276 | dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type()); | |
2127e193 | 1277 | } |
bed94269 GI |
1278 | if (!dev->is_open()) { |
1279 | pout("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg()); | |
1280 | return FAILDEV; | |
832b75ed | 1281 | } |
bed94269 GI |
1282 | |
1283 | // now call appropriate ATA or SCSI routine | |
1284 | int retval = 0; | |
1285 | if (print_type_only) | |
1286 | pout("%s: Device of type '%s' [%s] opened\n", | |
1287 | dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); | |
1288 | else if (dev->is_ata()) | |
1289 | retval = ataPrintMain(dev->to_ata(), ataopts); | |
1290 | else if (dev->is_scsi()) | |
1291 | retval = scsiPrintMain(dev->to_scsi(), scsiopts); | |
1292 | else | |
1293 | // we should never fall into this branch! | |
1294 | pout("%s: Neither ATA nor SCSI device\n", dev->get_info_name()); | |
1295 | ||
1296 | dev->close(); | |
2127e193 GI |
1297 | return retval; |
1298 | } | |
832b75ed | 1299 | |
2127e193 GI |
1300 | |
1301 | // Main program | |
1302 | int main(int argc, char **argv) | |
1303 | { | |
1304 | int status; | |
1305 | try { | |
1306 | // Do the real work ... | |
1307 | status = main_worker(argc, argv); | |
832b75ed | 1308 | } |
2127e193 GI |
1309 | catch (int ex) { |
1310 | // EXIT(status) arrives here | |
1311 | status = ex; | |
832b75ed | 1312 | } |
2127e193 GI |
1313 | catch (const std::bad_alloc & /*ex*/) { |
1314 | // Memory allocation failed (also thrown by std::operator new) | |
bed94269 | 1315 | printf("Smartctl: Out of memory\n"); |
2127e193 | 1316 | status = FAILCMD; |
832b75ed | 1317 | } |
2127e193 GI |
1318 | catch (const std::exception & ex) { |
1319 | // Other fatal errors | |
bed94269 | 1320 | printf("Smartctl: Exception: %s\n", ex.what()); |
2127e193 | 1321 | status = FAILCMD; |
832b75ed | 1322 | } |
2127e193 | 1323 | return status; |
832b75ed | 1324 | } |
2127e193 | 1325 |