]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * smartctl.cpp |
832b75ed | 3 | * |
a86ec89e | 4 | * Home page of code is: http://www.smartmontools.org |
832b75ed | 5 | * |
a86ec89e | 6 | * Copyright (C) 2002-11 Bruce Allen |
ff28b140 | 7 | * Copyright (C) 2008-18 Christian Franke |
832b75ed GG |
8 | * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
9 | * | |
ff28b140 | 10 | * SPDX-License-Identifier: GPL-2.0-or-later |
832b75ed GG |
11 | */ |
12 | ||
ff28b140 TL |
13 | #include "config.h" |
14 | #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ | |
15 | ||
832b75ed | 16 | #include <errno.h> |
ff28b140 | 17 | #include <inttypes.h> |
832b75ed GG |
18 | #include <stdio.h> |
19 | #include <sys/types.h> | |
20 | #include <string.h> | |
d2e702cf | 21 | #include <stdlib.h> |
832b75ed | 22 | #include <stdarg.h> |
2127e193 GI |
23 | #include <stdexcept> |
24 | #include <getopt.h> | |
832b75ed | 25 | |
2127e193 | 26 | #ifdef HAVE_UNISTD_H |
832b75ed GG |
27 | #include <unistd.h> |
28 | #endif | |
29 | ||
2127e193 GI |
30 | #if defined(__FreeBSD__) |
31 | #include <sys/param.h> | |
a37e7145 GG |
32 | #endif |
33 | ||
832b75ed | 34 | #include "atacmds.h" |
2127e193 | 35 | #include "dev_interface.h" |
832b75ed | 36 | #include "ataprint.h" |
832b75ed GG |
37 | #include "knowndrives.h" |
38 | #include "scsicmds.h" | |
39 | #include "scsiprint.h" | |
a86ec89e | 40 | #include "nvmeprint.h" |
832b75ed GG |
41 | #include "smartctl.h" |
42 | #include "utility.h" | |
ff28b140 | 43 | #include "svnversion.h" |
832b75ed | 44 | |
ff28b140 | 45 | const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4871 2018-12-27 20:03:12Z chrfranke $" |
cfbba5b9 | 46 | CONFIG_H_CVSID SMARTCTL_H_CVSID; |
832b75ed | 47 | |
cfbba5b9 GI |
48 | // Globals to control printing |
49 | bool printing_is_switchable = false; | |
50 | bool printing_is_off = false; | |
832b75ed | 51 | |
ff28b140 TL |
52 | // Control JSON output |
53 | json jglb; | |
54 | static bool print_as_json = false; | |
55 | static json::print_options print_as_json_options; | |
56 | static bool print_as_json_output = false; | |
57 | static bool print_as_json_impl = false; | |
58 | static bool print_as_json_unimpl = false; | |
59 | ||
2127e193 GI |
60 | static void printslogan() |
61 | { | |
ff28b140 | 62 | jout("%s\n", format_version_info("smartctl").c_str()); |
832b75ed GG |
63 | } |
64 | ||
cfbba5b9 GI |
65 | static void UsageSummary() |
66 | { | |
832b75ed GG |
67 | pout("\nUse smartctl -h to get a usage summary\n\n"); |
68 | return; | |
69 | } | |
70 | ||
ff28b140 TL |
71 | static void js_initialize(int argc, char **argv, bool verbose) |
72 | { | |
73 | if (jglb.is_enabled()) | |
74 | return; | |
75 | jglb.enable(); | |
76 | if (verbose) | |
77 | jglb.set_verbose(); | |
78 | ||
79 | // Major.minor version of JSON format | |
80 | jglb["json_format_version"][0] = 1; | |
81 | jglb["json_format_version"][1] = 0; | |
82 | ||
83 | // Smartctl version info | |
84 | json::ref jref = jglb["smartctl"]; | |
85 | int ver[3] = { 0, 0, 0 }; | |
86 | sscanf(PACKAGE_VERSION, "%d.%d.%d", ver, ver+1, ver+2); | |
87 | jref["version"][0] = ver[0]; | |
88 | jref["version"][1] = ver[1]; | |
89 | if (ver[2] > 0) | |
90 | jref["version"][2] = ver[2]; | |
91 | ||
92 | #ifdef SMARTMONTOOLS_SVN_REV | |
93 | jref["svn_revision"] = SMARTMONTOOLS_SVN_REV; | |
94 | #endif | |
95 | jref["platform_info"] = smi()->get_os_version_str(); | |
96 | #ifdef BUILD_INFO | |
97 | jref["build_info"] = BUILD_INFO; | |
98 | #endif | |
99 | ||
100 | jref["argv"][0] = "smartctl"; | |
101 | for (int i = 1; i < argc; i++) | |
102 | jref["argv"][i] = argv[i]; | |
103 | } | |
104 | ||
d008864d | 105 | static std::string getvalidarglist(int opt); |
2127e193 | 106 | |
832b75ed | 107 | /* void prints help information for command syntax */ |
cfbba5b9 GI |
108 | static void Usage() |
109 | { | |
ff28b140 TL |
110 | pout("Usage: smartctl [options] device\n\n"); |
111 | pout( | |
2127e193 | 112 | "============================================ SHOW INFORMATION OPTIONS =====\n\n" |
832b75ed GG |
113 | " -h, --help, --usage\n" |
114 | " Display this help and exit\n\n" | |
115 | " -V, --version, --copyright, --license\n" | |
116 | " Print license, copyright, and version information and exit\n\n" | |
d008864d | 117 | " -i, --info\n" |
832b75ed | 118 | " Show identity information for device\n\n" |
ee38a438 GI |
119 | " --identify[=[w][nvb]]\n" |
120 | " Show words and bits from IDENTIFY DEVICE data (ATA)\n\n" | |
d008864d | 121 | " -g NAME, --get=NAME\n" |
f9e10201 JD |
122 | " Get device setting: all, aam, apm, dsn, lookahead, security,\n" |
123 | " wcache, rcache, wcreorder, wcache-sct\n\n" | |
d008864d | 124 | " -a, --all\n" |
832b75ed | 125 | " Show all SMART information for device\n\n" |
2127e193 GI |
126 | " -x, --xall\n" |
127 | " Show all information for device\n\n" | |
e9583e0c GI |
128 | " --scan\n" |
129 | " Scan for devices\n\n" | |
130 | " --scan-open\n" | |
131 | " Scan for devices and try to open each device\n\n" | |
832b75ed | 132 | ); |
ff28b140 | 133 | pout( |
2127e193 | 134 | "================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n" |
ff28b140 TL |
135 | " -j, --json[=[cgiosuv]]\n" |
136 | " Print output in JSON format\n\n" | |
832b75ed | 137 | " -q TYPE, --quietmode=TYPE (ATA)\n" |
a37e7145 | 138 | " Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n" |
832b75ed | 139 | " -d TYPE, --device=TYPE\n" |
f9e10201 JD |
140 | " Specify device type to one of:\n" |
141 | " %s\n\n" // TODO: fold this string | |
832b75ed GG |
142 | " -T TYPE, --tolerance=TYPE (ATA)\n" |
143 | " Tolerance: normal, conservative, permissive, verypermissive\n\n" | |
144 | " -b TYPE, --badsum=TYPE (ATA)\n" | |
145 | " Set action on bad checksum to one of: warn, exit, ignore\n\n" | |
146 | " -r TYPE, --report=TYPE\n" | |
147 | " Report transactions (see man page)\n\n" | |
f9e10201 | 148 | " -n MODE[,STATUS], --nocheck=MODE[,STATUS] (ATA)\n" |
2127e193 | 149 | " No check if: never, sleep, standby, idle (see man page)\n\n", |
54965743 | 150 | getvalidarglist('d').c_str()); // TODO: Use this function also for other options ? |
ff28b140 | 151 | pout( |
2127e193 | 152 | "============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n" |
832b75ed GG |
153 | " -s VALUE, --smart=VALUE\n" |
154 | " Enable/disable SMART on device (on/off)\n\n" | |
155 | " -o VALUE, --offlineauto=VALUE (ATA)\n" | |
156 | " Enable/disable automatic offline testing on device (on/off)\n\n" | |
157 | " -S VALUE, --saveauto=VALUE (ATA)\n" | |
158 | " Enable/disable Attribute autosave on device (on/off)\n\n" | |
d008864d GI |
159 | " -s NAME[,VALUE], --set=NAME[,VALUE]\n" |
160 | " Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n" | |
f9e10201 JD |
161 | " dsn,[on|off], lookahead,[on|off], security-freeze,\n" |
162 | " standby,[N|off|now], wcache,[on|off], rcache,[on|off],\n" | |
163 | " wcreorder,[on|off[,p]], wcache-sct,[ata|on|off[,p]]\n\n" | |
832b75ed | 164 | ); |
ff28b140 | 165 | pout( |
2127e193 | 166 | "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n" |
832b75ed GG |
167 | " -H, --health\n" |
168 | " Show device SMART health status\n\n" | |
a86ec89e | 169 | " -c, --capabilities (ATA, NVMe)\n" |
832b75ed | 170 | " Show device SMART capabilities\n\n" |
a7e8ffec | 171 | " -A, --attributes\n" |
832b75ed | 172 | " Show device SMART vendor-specific Attributes and values\n\n" |
a7e8ffec | 173 | " -f FORMAT, --format=FORMAT (ATA)\n" |
e165493d | 174 | " Set output format for attributes: old, brief, hex[,id|val]\n\n" |
832b75ed | 175 | " -l TYPE, --log=TYPE\n" |
2127e193 | 176 | " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" |
f9e10201 JD |
177 | " xerror[,N][,error], xselftest[,N][,selftest], background,\n" |
178 | " sasphy[,reset], sataphy[,reset], scttemp[sts,hist],\n" | |
179 | " scttempint,N[,p], scterc[,N,M], devstat[,N], defects[,N], ssd,\n" | |
180 | " gplog,N[,RANGE], smartlog,N[,RANGE], nvmelog,N,SIZE\n\n" | |
832b75ed GG |
181 | " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" |
182 | " Set display OPTION for vendor Attribute N (see man page)\n\n" | |
183 | " -F TYPE, --firmwarebug=TYPE (ATA)\n" | |
ee38a438 GI |
184 | " Use firmware bug workaround:\n" |
185 | " %s, swapid\n\n" | |
832b75ed GG |
186 | " -P TYPE, --presets=TYPE (ATA)\n" |
187 | " Drive-specific presets: use, ignore, show, showall\n\n" | |
2127e193 GI |
188 | " -B [+]FILE, --drivedb=[+]FILE (ATA)\n" |
189 | " Read and replace [add] drive database from FILE\n" | |
e9583e0c | 190 | " [default is +%s", |
ee38a438 | 191 | get_valid_firmwarebug_args(), |
e9583e0c GI |
192 | get_drivedb_path_add() |
193 | ); | |
2127e193 | 194 | #ifdef SMARTMONTOOLS_DRIVEDBDIR |
ff28b140 | 195 | pout( |
e9583e0c GI |
196 | "\n" |
197 | " and then %s", | |
198 | get_drivedb_path_default() | |
2127e193 | 199 | ); |
e9583e0c | 200 | #endif |
ff28b140 | 201 | pout( |
e9583e0c | 202 | "]\n\n" |
2127e193 | 203 | "============================================ DEVICE SELF-TEST OPTIONS =====\n\n" |
832b75ed | 204 | " -t TEST, --test=TEST\n" |
d008864d GI |
205 | " Run test. TEST: offline, short, long, conveyance, force, vendor,N,\n" |
206 | " select,M-N, pending,N, afterselect,[on|off]\n\n" | |
832b75ed GG |
207 | " -C, --captive\n" |
208 | " Do test in captive mode (along with -t)\n\n" | |
209 | " -X, --abort\n" | |
210 | " Abort any non-captive test on device\n\n" | |
211 | ); | |
54965743 GI |
212 | std::string examples = smi()->get_app_examples("smartctl"); |
213 | if (!examples.empty()) | |
ff28b140 | 214 | pout("%s\n", examples.c_str()); |
832b75ed GG |
215 | } |
216 | ||
d008864d | 217 | // Values for --long only options, see parse_options() |
ee38a438 | 218 | enum { opt_identify = 1000, opt_scan, opt_scan_open, opt_set, opt_smart }; |
d008864d | 219 | |
54965743 GI |
220 | /* Returns a string containing a formatted list of the valid arguments |
221 | to the option opt or empty on failure. Note 'v' case different */ | |
d008864d | 222 | static std::string getvalidarglist(int opt) |
2127e193 | 223 | { |
832b75ed GG |
224 | switch (opt) { |
225 | case 'q': | |
a37e7145 | 226 | return "errorsonly, silent, noserial"; |
832b75ed | 227 | case 'd': |
cfbba5b9 | 228 | return smi()->get_valid_dev_types_str() + ", auto, test"; |
832b75ed GG |
229 | case 'T': |
230 | return "normal, conservative, permissive, verypermissive"; | |
231 | case 'b': | |
232 | return "warn, exit, ignore"; | |
f9e10201 JD |
233 | case 'B': |
234 | return "[+]<FILE_NAME>"; | |
832b75ed | 235 | case 'r': |
a86ec89e | 236 | return "ioctl[,N], ataioctl[,N], scsiioctl[,N], nvmeioctl[,N]"; |
d008864d | 237 | case opt_smart: |
832b75ed GG |
238 | case 'o': |
239 | case 'S': | |
240 | return "on, off"; | |
241 | case 'l': | |
d008864d GI |
242 | return "error, selftest, selective, directory[,g|s], " |
243 | "xerror[,N][,error], xselftest[,N][,selftest], " | |
244 | "background, sasphy[,reset], sataphy[,reset], " | |
245 | "scttemp[sts,hist], scttempint,N[,p], " | |
f9e10201 | 246 | "scterc[,N,M], devstat[,N], defects[,N], ssd, " |
a86ec89e GI |
247 | "gplog,N[,RANGE], smartlog,N[,RANGE], " |
248 | "nvmelog,N,SIZE"; | |
832b75ed GG |
249 | case 'P': |
250 | return "use, ignore, show, showall"; | |
251 | case 't': | |
d008864d GI |
252 | return "offline, short, long, conveyance, force, vendor,N, select,M-N, " |
253 | "pending,N, afterselect,[on|off]"; | |
832b75ed | 254 | case 'F': |
ee38a438 | 255 | return std::string(get_valid_firmwarebug_args()) + ", swapid"; |
4d59bff9 | 256 | case 'n': |
f9e10201 | 257 | return "never, sleep[,STATUS], standby[,STATUS], idle[,STATUS]"; |
a7e8ffec | 258 | case 'f': |
e165493d | 259 | return "old, brief, hex[,id|val]"; |
d008864d | 260 | case 'g': |
f9e10201 | 261 | return "aam, apm, dsn, lookahead, security, wcache, rcache, wcreorder, wcache-sct"; |
d008864d | 262 | case opt_set: |
f9e10201 JD |
263 | return "aam,[N|off], apm,[N|off], dsn,[on|off], lookahead,[on|off], security-freeze, " |
264 | "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off[,p]], " | |
265 | "wcache-sct,[ata|on|off[,p]]"; | |
d008864d GI |
266 | case 's': |
267 | return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set); | |
ff28b140 TL |
268 | case 'j': |
269 | return "c, g, i, o, s, u, v"; | |
ee38a438 GI |
270 | case opt_identify: |
271 | return "n, wn, w, v, wv, wb"; | |
832b75ed GG |
272 | case 'v': |
273 | default: | |
54965743 | 274 | return ""; |
832b75ed GG |
275 | } |
276 | } | |
277 | ||
278 | /* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where | |
279 | <LIST> is the list of valid arguments for option opt. */ | |
d008864d | 280 | static void printvalidarglistmessage(int opt) |
cfbba5b9 | 281 | { |
832b75ed | 282 | if (opt=='v'){ |
ff28b140 | 283 | jerr("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", |
2127e193 | 284 | create_vendor_attribute_arg_list().c_str()); |
832b75ed GG |
285 | } |
286 | else { | |
287 | // getvalidarglist() might produce a multiline or single line string. We | |
288 | // need to figure out which to get the formatting right. | |
54965743 GI |
289 | std::string s = getvalidarglist(opt); |
290 | char separator = strchr(s.c_str(), '\n') ? '\n' : ' '; | |
ff28b140 | 291 | jerr("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator); |
832b75ed GG |
292 | } |
293 | ||
294 | return; | |
295 | } | |
296 | ||
2127e193 GI |
297 | // Checksum error mode |
298 | enum checksum_err_mode_t { | |
299 | CHECKSUM_ERR_WARN, CHECKSUM_ERR_EXIT, CHECKSUM_ERR_IGNORE | |
300 | }; | |
301 | ||
302 | static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN; | |
303 | ||
a86ec89e | 304 | static void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv); |
e9583e0c | 305 | |
d008864d | 306 | |
832b75ed | 307 | /* Takes command options and sets features to be run */ |
ff28b140 | 308 | static int parse_options(int argc, char** argv, const char * & type, |
d008864d | 309 | ata_print_options & ataopts, scsi_print_options & scsiopts, |
a86ec89e | 310 | nvme_print_options & nvmeopts, bool & print_type_only) |
2127e193 | 311 | { |
832b75ed | 312 | // Please update getvalidarglist() if you edit shortopts |
ff28b140 | 313 | const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:j"; |
832b75ed GG |
314 | // Please update getvalidarglist() if you edit longopts |
315 | struct option longopts[] = { | |
316 | { "help", no_argument, 0, 'h' }, | |
317 | { "usage", no_argument, 0, 'h' }, | |
318 | { "version", no_argument, 0, 'V' }, | |
319 | { "copyright", no_argument, 0, 'V' }, | |
320 | { "license", no_argument, 0, 'V' }, | |
321 | { "quietmode", required_argument, 0, 'q' }, | |
322 | { "device", required_argument, 0, 'd' }, | |
323 | { "tolerance", required_argument, 0, 'T' }, | |
324 | { "badsum", required_argument, 0, 'b' }, | |
325 | { "report", required_argument, 0, 'r' }, | |
d008864d | 326 | { "smart", required_argument, 0, opt_smart }, |
832b75ed GG |
327 | { "offlineauto", required_argument, 0, 'o' }, |
328 | { "saveauto", required_argument, 0, 'S' }, | |
329 | { "health", no_argument, 0, 'H' }, | |
330 | { "capabilities", no_argument, 0, 'c' }, | |
331 | { "attributes", no_argument, 0, 'A' }, | |
332 | { "log", required_argument, 0, 'l' }, | |
333 | { "info", no_argument, 0, 'i' }, | |
334 | { "all", no_argument, 0, 'a' }, | |
2127e193 | 335 | { "xall", no_argument, 0, 'x' }, |
832b75ed GG |
336 | { "vendorattribute", required_argument, 0, 'v' }, |
337 | { "presets", required_argument, 0, 'P' }, | |
338 | { "test", required_argument, 0, 't' }, | |
339 | { "captive", no_argument, 0, 'C' }, | |
340 | { "abort", no_argument, 0, 'X' }, | |
341 | { "firmwarebug", required_argument, 0, 'F' }, | |
4d59bff9 | 342 | { "nocheck", required_argument, 0, 'n' }, |
2127e193 | 343 | { "drivedb", required_argument, 0, 'B' }, |
a7e8ffec | 344 | { "format", required_argument, 0, 'f' }, |
d008864d | 345 | { "get", required_argument, 0, 'g' }, |
ff28b140 | 346 | { "json", optional_argument, 0, 'j' }, |
ee38a438 | 347 | { "identify", optional_argument, 0, opt_identify }, |
d008864d | 348 | { "set", required_argument, 0, opt_set }, |
e9583e0c GI |
349 | { "scan", no_argument, 0, opt_scan }, |
350 | { "scan-open", no_argument, 0, opt_scan_open }, | |
832b75ed GG |
351 | { 0, 0, 0, 0 } |
352 | }; | |
2127e193 GI |
353 | |
354 | char extraerror[256]; | |
832b75ed | 355 | memset(extraerror, 0, sizeof(extraerror)); |
832b75ed | 356 | opterr=optopt=0; |
2127e193 | 357 | |
a86ec89e GI |
358 | smart_devtype_list scan_types; // multiple -d TYPE options for --scan |
359 | bool use_default_db = true; // set false on '-B FILE' | |
a7e8ffec | 360 | bool output_format_set = false; // set true on '-f FORMAT' |
e9583e0c | 361 | int scan = 0; // set by --scan, --scan-open |
2127e193 GI |
362 | bool badarg = false, captive = false; |
363 | int testcnt = 0; // number of self-tests requested | |
364 | ||
365 | int optchar; | |
366 | char *arg; | |
367 | ||
e9583e0c | 368 | while ((optchar = getopt_long(argc, argv, shortopts, longopts, 0)) != -1) { |
ff28b140 TL |
369 | |
370 | // Clang analyzer: Workaround for false positive messages | |
371 | // 'Dereference of null pointer' and 'Null pointer argument' | |
372 | bool optarg_is_set = !!optarg; | |
373 | #ifdef __clang_analyzer__ | |
374 | if (!optarg_is_set) optarg = (char *)""; | |
375 | #endif | |
376 | ||
832b75ed GG |
377 | switch (optchar){ |
378 | case 'V': | |
cfbba5b9 | 379 | printing_is_off = false; |
2127e193 | 380 | pout("%s", format_version_info("smartctl", true /*full*/).c_str()); |
ff28b140 | 381 | return 0; |
832b75ed GG |
382 | case 'q': |
383 | if (!strcmp(optarg,"errorsonly")) { | |
cfbba5b9 GI |
384 | printing_is_switchable = true; |
385 | printing_is_off = false; | |
832b75ed | 386 | } else if (!strcmp(optarg,"silent")) { |
cfbba5b9 GI |
387 | printing_is_switchable = false; |
388 | printing_is_off = true; | |
a37e7145 | 389 | } else if (!strcmp(optarg,"noserial")) { |
cfbba5b9 | 390 | dont_print_serial_number = true; |
832b75ed | 391 | } else { |
2127e193 | 392 | badarg = true; |
832b75ed GG |
393 | } |
394 | break; | |
395 | case 'd': | |
d008864d GI |
396 | if (!strcmp(optarg, "test")) |
397 | print_type_only = true; | |
a86ec89e GI |
398 | else if (!strcmp(optarg, "auto")) { |
399 | type = 0; | |
400 | scan_types.clear(); | |
401 | } | |
402 | else { | |
403 | type = optarg; | |
404 | scan_types.push_back(optarg); | |
405 | } | |
832b75ed GG |
406 | break; |
407 | case 'T': | |
408 | if (!strcmp(optarg,"normal")) { | |
cfbba5b9 GI |
409 | failuretest_conservative = false; |
410 | failuretest_permissive = 0; | |
832b75ed | 411 | } else if (!strcmp(optarg,"conservative")) { |
cfbba5b9 | 412 | failuretest_conservative = true; |
832b75ed | 413 | } else if (!strcmp(optarg,"permissive")) { |
cfbba5b9 GI |
414 | if (failuretest_permissive < 0xff) |
415 | failuretest_permissive++; | |
832b75ed | 416 | } else if (!strcmp(optarg,"verypermissive")) { |
cfbba5b9 | 417 | failuretest_permissive = 0xff; |
832b75ed | 418 | } else { |
2127e193 | 419 | badarg = true; |
832b75ed GG |
420 | } |
421 | break; | |
422 | case 'b': | |
423 | if (!strcmp(optarg,"warn")) { | |
2127e193 | 424 | checksum_err_mode = CHECKSUM_ERR_WARN; |
832b75ed | 425 | } else if (!strcmp(optarg,"exit")) { |
2127e193 | 426 | checksum_err_mode = CHECKSUM_ERR_EXIT; |
832b75ed | 427 | } else if (!strcmp(optarg,"ignore")) { |
2127e193 | 428 | checksum_err_mode = CHECKSUM_ERR_IGNORE; |
832b75ed | 429 | } else { |
2127e193 | 430 | badarg = true; |
832b75ed GG |
431 | } |
432 | break; | |
433 | case 'r': | |
434 | { | |
a86ec89e GI |
435 | int n1 = -1, n2 = -1, len = strlen(optarg); |
436 | char s[9+1]; unsigned i = 1; | |
437 | sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2); | |
438 | if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) { | |
2127e193 | 439 | badarg = true; |
832b75ed | 440 | } else if (!strcmp(s,"ioctl")) { |
a86ec89e | 441 | ata_debugmode = scsi_debugmode = nvme_debugmode = i; |
832b75ed | 442 | } else if (!strcmp(s,"ataioctl")) { |
cfbba5b9 | 443 | ata_debugmode = i; |
832b75ed | 444 | } else if (!strcmp(s,"scsiioctl")) { |
cfbba5b9 | 445 | scsi_debugmode = i; |
a86ec89e GI |
446 | } else if (!strcmp(s,"nvmeioctl")) { |
447 | nvme_debugmode = i; | |
832b75ed | 448 | } else { |
2127e193 | 449 | badarg = true; |
832b75ed | 450 | } |
832b75ed GG |
451 | } |
452 | break; | |
d008864d | 453 | |
832b75ed | 454 | case 's': |
d008864d | 455 | case opt_smart: // --smart |
832b75ed | 456 | if (!strcmp(optarg,"on")) { |
2127e193 GI |
457 | ataopts.smart_enable = scsiopts.smart_enable = true; |
458 | ataopts.smart_disable = scsiopts.smart_disable = false; | |
832b75ed | 459 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
460 | ataopts.smart_disable = scsiopts.smart_disable = true; |
461 | ataopts.smart_enable = scsiopts.smart_enable = false; | |
d008864d GI |
462 | } else if (optchar == 's') { |
463 | goto case_s_continued; // --set, see below | |
832b75ed | 464 | } else { |
2127e193 | 465 | badarg = true; |
832b75ed GG |
466 | } |
467 | break; | |
d008864d | 468 | |
832b75ed GG |
469 | case 'o': |
470 | if (!strcmp(optarg,"on")) { | |
2127e193 GI |
471 | ataopts.smart_auto_offl_enable = true; |
472 | ataopts.smart_auto_offl_disable = false; | |
832b75ed | 473 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
474 | ataopts.smart_auto_offl_disable = true; |
475 | ataopts.smart_auto_offl_enable = false; | |
832b75ed | 476 | } else { |
2127e193 | 477 | badarg = true; |
832b75ed GG |
478 | } |
479 | break; | |
480 | case 'S': | |
481 | if (!strcmp(optarg,"on")) { | |
2127e193 GI |
482 | ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = true; |
483 | ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = false; | |
832b75ed | 484 | } else if (!strcmp(optarg,"off")) { |
2127e193 GI |
485 | ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = true; |
486 | ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = false; | |
832b75ed | 487 | } else { |
2127e193 | 488 | badarg = true; |
832b75ed GG |
489 | } |
490 | break; | |
491 | case 'H': | |
a86ec89e | 492 | ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; |
d008864d | 493 | scsiopts.smart_ss_media_log = true; |
832b75ed GG |
494 | break; |
495 | case 'F': | |
ee38a438 | 496 | if (!strcmp(optarg, "swapid")) |
2127e193 | 497 | ataopts.fix_swapped_id = true; |
ee38a438 | 498 | else if (!parse_firmwarebug_def(optarg, ataopts.firmwarebugs)) |
2127e193 | 499 | badarg = true; |
832b75ed GG |
500 | break; |
501 | case 'c': | |
a86ec89e | 502 | ataopts.smart_general_values = nvmeopts.drive_capabilities = true; |
832b75ed GG |
503 | break; |
504 | case 'A': | |
a86ec89e | 505 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; |
832b75ed GG |
506 | break; |
507 | case 'l': | |
a86ec89e GI |
508 | if (str_starts_with(optarg, "error")) { |
509 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
510 | unsigned val = ~0; | |
511 | sscanf(optarg, "error%n,%u%n", &n1, &val, &n2); | |
2127e193 | 512 | ataopts.smart_error_log = scsiopts.smart_error_log = true; |
a86ec89e GI |
513 | if (n1 == len) |
514 | nvmeopts.error_log_entries = 16; | |
515 | else if (n2 == len && val > 0) | |
516 | nvmeopts.error_log_entries = val; | |
517 | else | |
518 | badarg = true; | |
832b75ed | 519 | } else if (!strcmp(optarg,"selftest")) { |
2127e193 | 520 | ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; |
832b75ed | 521 | } else if (!strcmp(optarg, "selective")) { |
2127e193 | 522 | ataopts.smart_selective_selftest_log = true; |
832b75ed | 523 | } else if (!strcmp(optarg,"directory")) { |
2127e193 GI |
524 | ataopts.smart_logdir = ataopts.gp_logdir = true; // SMART+GPL |
525 | } else if (!strcmp(optarg,"directory,s")) { | |
526 | ataopts.smart_logdir = true; // SMART | |
527 | } else if (!strcmp(optarg,"directory,g")) { | |
528 | ataopts.gp_logdir = true; // GPL | |
529 | } else if (!strcmp(optarg,"sasphy")) { | |
530 | scsiopts.sasphy = true; | |
531 | } else if (!strcmp(optarg,"sasphy,reset")) { | |
532 | scsiopts.sasphy = scsiopts.sasphy_reset = true; | |
533 | } else if (!strcmp(optarg,"sataphy")) { | |
534 | ataopts.sataphy = true; | |
535 | } else if (!strcmp(optarg,"sataphy,reset")) { | |
536 | ataopts.sataphy = ataopts.sataphy_reset = true; | |
4d59bff9 | 537 | } else if (!strcmp(optarg,"background")) { |
2127e193 | 538 | scsiopts.smart_background_log = true; |
d008864d GI |
539 | } else if (!strcmp(optarg,"ssd")) { |
540 | ataopts.devstat_ssd_page = true; | |
541 | scsiopts.smart_ss_media_log = true; | |
7f0798ef GI |
542 | } else if (!strcmp(optarg,"scterc")) { |
543 | ataopts.sct_erc_get = true; | |
a37e7145 | 544 | } else if (!strcmp(optarg,"scttemp")) { |
2127e193 | 545 | ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; |
a37e7145 | 546 | } else if (!strcmp(optarg,"scttempsts")) { |
2127e193 | 547 | ataopts.sct_temp_sts = true; |
a37e7145 | 548 | } else if (!strcmp(optarg,"scttemphist")) { |
2127e193 GI |
549 | ataopts.sct_temp_hist = true; |
550 | ||
d008864d GI |
551 | } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) { |
552 | unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg); | |
553 | if (!( sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1 | |
554 | && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) { | |
ee38a438 | 555 | snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n"); |
d008864d GI |
556 | badarg = true; |
557 | } | |
558 | ataopts.sct_temp_int = interval; | |
559 | ataopts.sct_temp_int_pers = (n2 == len); | |
560 | ||
561 | } else if (!strncmp(optarg, "devstat", sizeof("devstat")-1)) { | |
562 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
563 | unsigned val = ~0; | |
564 | sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2); | |
565 | if (n1 == len) | |
566 | ataopts.devstat_all_pages = true; | |
a86ec89e GI |
567 | else { |
568 | if (n2 != len) // retry with hex | |
569 | sscanf(optarg, "devstat,0x%x%n", &val, &n2); | |
570 | if (n2 == len && val <= 0xff) | |
571 | ataopts.devstat_pages.push_back(val); | |
572 | else | |
573 | badarg = true; | |
574 | } | |
d008864d | 575 | |
f9e10201 JD |
576 | } else if (str_starts_with(optarg, "defects")) { |
577 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
578 | unsigned val = ~0; | |
579 | sscanf(optarg, "defects%n,%u%n", &n1, &val, &n2); | |
580 | if (n1 == len) | |
581 | ataopts.pending_defects_log = 31; // Entries of first page | |
582 | else if (n2 == len && val <= 0xffff * 32 - 1) | |
583 | ataopts.pending_defects_log = val; | |
584 | else | |
585 | badarg = true; | |
586 | ||
2127e193 GI |
587 | } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) { |
588 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
589 | unsigned val = 8; | |
590 | sscanf(optarg, "xerror%n,error%n", &n1, &n2); | |
591 | if (!(n1 == len || n2 == len)) { | |
592 | n1 = n2 = -1; | |
593 | sscanf(optarg, "xerror,%u%n,error%n", &val, &n1, &n2); | |
594 | } | |
595 | if ((n1 == len || n2 == len) && val > 0) { | |
596 | ataopts.smart_ext_error_log = val; | |
597 | ataopts.retry_error_log = (n2 == len); | |
598 | } | |
599 | else | |
600 | badarg = true; | |
601 | ||
602 | } else if (!strncmp(optarg, "xselftest", sizeof("xselftest")-1)) { | |
603 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
604 | unsigned val = 25; | |
605 | sscanf(optarg, "xselftest%n,selftest%n", &n1, &n2); | |
606 | if (!(n1 == len || n2 == len)) { | |
607 | n1 = n2 = -1; | |
608 | sscanf(optarg, "xselftest,%u%n,selftest%n", &val, &n1, &n2); | |
609 | } | |
610 | if ((n1 == len || n2 == len) && val > 0) { | |
611 | ataopts.smart_ext_selftest_log = val; | |
612 | ataopts.retry_selftest_log = (n2 == len); | |
613 | } | |
614 | else | |
615 | badarg = true; | |
616 | ||
7f0798ef GI |
617 | } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { |
618 | unsigned rt = ~0, wt = ~0; int n = -1; | |
619 | sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); | |
620 | if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { | |
621 | ataopts.sct_erc_set = true; | |
622 | ataopts.sct_erc_readtime = rt; | |
623 | ataopts.sct_erc_writetime = wt; | |
624 | } | |
625 | else { | |
ee38a438 | 626 | snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); |
7f0798ef GI |
627 | badarg = true; |
628 | } | |
2127e193 GI |
629 | } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) |
630 | || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { | |
631 | unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; | |
632 | int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); | |
633 | sscanf(optarg, "%*[a-z],0x%x%n,%u%n%c%u%n", | |
634 | &logaddr, &n1, &page, &n2, &sign, &nsectors, &n3); | |
635 | if (len > n2 && n3 == -1 && !strcmp(optarg+n2, "-max")) { | |
636 | nsectors = ~0U; sign = '+'; n3 = len; | |
637 | } | |
638 | bool gpl = (optarg[0] == 'g'); | |
639 | const char * erropt = (gpl ? "gplog" : "smartlog"); | |
640 | if (!( n1 == len || n2 == len | |
641 | || (n3 == len && (sign == '+' || sign == '-')))) { | |
ee38a438 | 642 | snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt); |
2127e193 GI |
643 | badarg = true; |
644 | } | |
645 | else if (!( logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU) | |
646 | && 0 < nsectors | |
647 | && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U) | |
648 | && (sign != '-' || page <= nsectors) )) { | |
ee38a438 | 649 | snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt); |
2127e193 GI |
650 | badarg = true; |
651 | } | |
652 | else { | |
653 | ata_log_request req; | |
654 | req.gpl = gpl; req.logaddr = logaddr; req.page = page; | |
655 | req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors); | |
656 | ataopts.log_requests.push_back(req); | |
657 | } | |
a86ec89e GI |
658 | } |
659 | ||
660 | else if (str_starts_with(optarg, "nvmelog,")) { | |
661 | int n = -1, len = strlen(optarg); | |
662 | unsigned page = 0, size = 0; | |
663 | sscanf(optarg, "nvmelog,0x%x,0x%x%n", &page, &size, &n); | |
664 | if (n == len && page <= 0xff && 0 < size && size <= 0x4000) { | |
665 | nvmeopts.log_page = page; nvmeopts.log_page_size = size; | |
666 | } | |
667 | else | |
668 | badarg = true; | |
669 | } | |
670 | ||
671 | else { | |
2127e193 | 672 | badarg = true; |
832b75ed GG |
673 | } |
674 | break; | |
675 | case 'i': | |
a86ec89e | 676 | ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; |
2127e193 | 677 | break; |
ee38a438 GI |
678 | |
679 | case opt_identify: | |
680 | ataopts.identify_word_level = ataopts.identify_bit_level = 0; | |
ff28b140 | 681 | if (optarg_is_set) { |
ee38a438 GI |
682 | for (int i = 0; optarg[i]; i++) { |
683 | switch (optarg[i]) { | |
684 | case 'w': ataopts.identify_word_level = 1; break; | |
685 | case 'n': ataopts.identify_bit_level = -1; break; | |
686 | case 'v': ataopts.identify_bit_level = 1; break; | |
687 | case 'b': ataopts.identify_bit_level = 2; break; | |
688 | default: badarg = true; | |
689 | } | |
690 | } | |
691 | } | |
692 | break; | |
693 | ||
832b75ed | 694 | case 'a': |
a86ec89e GI |
695 | ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; |
696 | ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; | |
697 | ataopts.smart_general_values = nvmeopts.drive_capabilities = true; | |
698 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; | |
2127e193 | 699 | ataopts.smart_error_log = scsiopts.smart_error_log = true; |
a86ec89e | 700 | nvmeopts.error_log_entries = 16; |
2127e193 GI |
701 | ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; |
702 | ataopts.smart_selective_selftest_log = true; | |
703 | /* scsiopts.smart_background_log = true; */ | |
d008864d | 704 | scsiopts.smart_ss_media_log = true; |
2127e193 GI |
705 | break; |
706 | case 'x': | |
a86ec89e GI |
707 | ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; |
708 | ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; | |
709 | ataopts.smart_general_values = nvmeopts.drive_capabilities = true; | |
710 | ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; | |
2127e193 GI |
711 | ataopts.smart_ext_error_log = 8; |
712 | ataopts.retry_error_log = true; | |
a86ec89e | 713 | nvmeopts.error_log_entries = 16; |
2127e193 GI |
714 | ataopts.smart_ext_selftest_log = 25; |
715 | ataopts.retry_selftest_log = true; | |
716 | scsiopts.smart_error_log = scsiopts.smart_selftest_log = true; | |
717 | ataopts.smart_selective_selftest_log = true; | |
718 | ataopts.smart_logdir = ataopts.gp_logdir = true; | |
719 | ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; | |
7f0798ef | 720 | ataopts.sct_erc_get = true; |
3d17a85c | 721 | ataopts.sct_wcache_reorder_get = true; |
ee38a438 | 722 | ataopts.devstat_all_pages = true; |
ff28b140 | 723 | ataopts.pending_defects_log = 31; |
2127e193 | 724 | ataopts.sataphy = true; |
d008864d GI |
725 | ataopts.get_set_used = true; |
726 | ataopts.get_aam = ataopts.get_apm = true; | |
727 | ataopts.get_security = true; | |
728 | ataopts.get_lookahead = ataopts.get_wcache = true; | |
f9e10201 | 729 | ataopts.get_dsn = true; |
ee38a438 | 730 | scsiopts.get_rcd = scsiopts.get_wce = true; |
2127e193 | 731 | scsiopts.smart_background_log = true; |
d008864d | 732 | scsiopts.smart_ss_media_log = true; |
2127e193 | 733 | scsiopts.sasphy = true; |
a7e8ffec | 734 | if (!output_format_set) |
e165493d | 735 | ataopts.output_format |= ata_print_options::FMT_BRIEF; |
832b75ed GG |
736 | break; |
737 | case 'v': | |
738 | // parse vendor-specific definitions of attributes | |
739 | if (!strcmp(optarg,"help")) { | |
cfbba5b9 | 740 | printing_is_off = false; |
832b75ed | 741 | printslogan(); |
2127e193 GI |
742 | pout("The valid arguments to -v are:\n\thelp\n%s\n", |
743 | create_vendor_attribute_arg_list().c_str()); | |
ff28b140 | 744 | return 0; |
832b75ed | 745 | } |
bed94269 | 746 | if (!parse_attribute_def(optarg, ataopts.attribute_defs, PRIOR_USER)) |
2127e193 | 747 | badarg = true; |
832b75ed GG |
748 | break; |
749 | case 'P': | |
750 | if (!strcmp(optarg, "use")) { | |
2127e193 | 751 | ataopts.ignore_presets = false; |
832b75ed | 752 | } else if (!strcmp(optarg, "ignore")) { |
2127e193 | 753 | ataopts.ignore_presets = true; |
832b75ed | 754 | } else if (!strcmp(optarg, "show")) { |
2127e193 | 755 | ataopts.show_presets = true; |
832b75ed | 756 | } else if (!strcmp(optarg, "showall")) { |
a86ec89e | 757 | if (!init_drive_database(use_default_db)) |
ff28b140 | 758 | return FAILCMD; |
832b75ed GG |
759 | if (optind < argc) { // -P showall MODEL [FIRMWARE] |
760 | int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL)); | |
ff28b140 | 761 | return (cnt >= 0 ? cnt : 0); |
832b75ed GG |
762 | } |
763 | if (showallpresets()) | |
ff28b140 TL |
764 | return FAILCMD; // report regexp syntax error |
765 | return 0; | |
832b75ed | 766 | } else { |
2127e193 | 767 | badarg = true; |
832b75ed GG |
768 | } |
769 | break; | |
770 | case 't': | |
771 | if (!strcmp(optarg,"offline")) { | |
2127e193 GI |
772 | testcnt++; |
773 | ataopts.smart_selftest_type = OFFLINE_FULL_SCAN; | |
774 | scsiopts.smart_default_selftest = true; | |
832b75ed | 775 | } else if (!strcmp(optarg,"short")) { |
2127e193 GI |
776 | testcnt++; |
777 | ataopts.smart_selftest_type = SHORT_SELF_TEST; | |
778 | scsiopts.smart_short_selftest = true; | |
832b75ed | 779 | } else if (!strcmp(optarg,"long")) { |
2127e193 GI |
780 | testcnt++; |
781 | ataopts.smart_selftest_type = EXTEND_SELF_TEST; | |
782 | scsiopts.smart_extend_selftest = true; | |
832b75ed | 783 | } else if (!strcmp(optarg,"conveyance")) { |
2127e193 GI |
784 | testcnt++; |
785 | ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST; | |
d008864d GI |
786 | } else if (!strcmp(optarg,"force")) { |
787 | ataopts.smart_selftest_force = true; | |
ee38a438 | 788 | scsiopts.smart_selftest_force = true; |
832b75ed | 789 | } else if (!strcmp(optarg,"afterselect,on")) { |
2127e193 GI |
790 | // scan remainder of disk after doing selected segment |
791 | ataopts.smart_selective_args.scan_after_select = 2; | |
832b75ed | 792 | } else if (!strcmp(optarg,"afterselect,off")) { |
2127e193 GI |
793 | // don't scan remainder of disk after doing selected segments |
794 | ataopts.smart_selective_args.scan_after_select = 1; | |
832b75ed GG |
795 | } else if (!strncmp(optarg,"pending,",strlen("pending,"))) { |
796 | // parse number of minutes that test should be pending | |
797 | int i; | |
798 | char *tailptr=NULL; | |
799 | errno=0; | |
800 | i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10); | |
801 | if (errno || *tailptr != '\0') { | |
ee38a438 | 802 | snprintf(extraerror, sizeof(extraerror), "Option -t pending,N requires N to be a non-negative integer\n"); |
2127e193 | 803 | badarg = true; |
832b75ed | 804 | } else if (i<0 || i>65535) { |
ee38a438 | 805 | snprintf(extraerror, sizeof(extraerror), "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); |
2127e193 | 806 | badarg = true; |
832b75ed | 807 | } else { |
2127e193 | 808 | ataopts.smart_selective_args.pending_time = i+1; |
832b75ed GG |
809 | } |
810 | } else if (!strncmp(optarg,"select",strlen("select"))) { | |
e9583e0c GI |
811 | if (ataopts.smart_selective_args.num_spans == 0) |
812 | testcnt++; | |
a37e7145 GG |
813 | // parse range of LBAs to test |
814 | uint64_t start, stop; int mode; | |
815 | if (split_selective_arg(optarg, &start, &stop, &mode)) { | |
ee38a438 | 816 | snprintf(extraerror, sizeof(extraerror), "Option -t select,M-N must have non-negative integer M and N\n"); |
2127e193 | 817 | badarg = true; |
832b75ed | 818 | } else { |
2127e193 | 819 | if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) { |
832b75ed | 820 | if (start > stop) { |
d2e702cf | 821 | snprintf(extraerror, sizeof(extraerror), "ERROR: Start LBA (%" PRIu64 ") > ending LBA (%" PRId64 ") in argument \"%s\"\n", |
832b75ed GG |
822 | start, stop, optarg); |
823 | } else { | |
ee38a438 | 824 | snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be" |
832b75ed GG |
825 | " defined\n"); |
826 | } | |
2127e193 | 827 | badarg = true; |
832b75ed | 828 | } |
2127e193 GI |
829 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].start = start; |
830 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].end = stop; | |
831 | ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].mode = mode; | |
832 | ataopts.smart_selective_args.num_spans++; | |
833 | ataopts.smart_selftest_type = SELECTIVE_SELF_TEST; | |
832b75ed | 834 | } |
d008864d | 835 | } else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) { |
ee38a438 | 836 | snprintf(extraerror, sizeof(extraerror), "-t scttempint is no longer supported, use -l scttempint instead\n"); |
d008864d | 837 | badarg = true; |
cfbba5b9 GI |
838 | } else if (!strncmp(optarg, "vendor,", sizeof("vendor,")-1)) { |
839 | unsigned subcmd = ~0U; int n = -1; | |
840 | if (!( sscanf(optarg, "%*[a-z],0x%x%n", &subcmd, &n) == 1 | |
841 | && subcmd <= 0xff && n == (int)strlen(optarg))) { | |
ee38a438 | 842 | snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n"); |
cfbba5b9 GI |
843 | badarg = true; |
844 | } | |
845 | else | |
846 | ataopts.smart_selftest_type = subcmd; | |
832b75ed | 847 | } else { |
2127e193 | 848 | badarg = true; |
832b75ed GG |
849 | } |
850 | break; | |
851 | case 'C': | |
2127e193 | 852 | captive = true; |
832b75ed GG |
853 | break; |
854 | case 'X': | |
2127e193 GI |
855 | testcnt++; |
856 | scsiopts.smart_selftest_abort = true; | |
857 | ataopts.smart_selftest_type = ABORT_SELF_TEST; | |
832b75ed | 858 | break; |
4d59bff9 GG |
859 | case 'n': |
860 | // skip disk check if in low-power mode | |
f9e10201 | 861 | if (!strcmp(optarg, "never")) { |
2127e193 | 862 | ataopts.powermode = 1; // do not skip, but print mode |
f9e10201 JD |
863 | } |
864 | else { | |
865 | int n1 = -1, n2 = -1, len = strlen(optarg); | |
866 | char s[7+1]; unsigned i = FAILPOWER; | |
867 | sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2); | |
868 | if (!((n1 == len || n2 == len) && i <= 255)) | |
869 | badarg = true; | |
870 | else if (!strcmp(s, "sleep")) | |
871 | ataopts.powermode = 2; | |
872 | else if (!strcmp(s, "standby")) | |
873 | ataopts.powermode = 3; | |
874 | else if (!strcmp(s, "idle")) | |
875 | ataopts.powermode = 4; | |
876 | else | |
877 | badarg = true; | |
878 | ataopts.powerexit = i; | |
879 | } | |
2127e193 | 880 | break; |
a7e8ffec | 881 | case 'f': |
e165493d GI |
882 | if (!strcmp(optarg, "old")) { |
883 | ataopts.output_format &= ~ata_print_options::FMT_BRIEF; | |
884 | output_format_set = true; | |
885 | } | |
886 | else if (!strcmp(optarg, "brief")) { | |
887 | ataopts.output_format |= ata_print_options::FMT_BRIEF; | |
888 | output_format_set = true; | |
a7e8ffec | 889 | } |
e165493d GI |
890 | else if (!strcmp(optarg, "hex")) |
891 | ataopts.output_format |= ata_print_options::FMT_HEX_ID | |
892 | | ata_print_options::FMT_HEX_VAL; | |
893 | else if (!strcmp(optarg, "hex,id")) | |
894 | ataopts.output_format |= ata_print_options::FMT_HEX_ID; | |
895 | else if (!strcmp(optarg, "hex,val")) | |
896 | ataopts.output_format |= ata_print_options::FMT_HEX_VAL; | |
897 | else | |
898 | badarg = true; | |
a7e8ffec | 899 | break; |
2127e193 GI |
900 | case 'B': |
901 | { | |
902 | const char * path = optarg; | |
903 | if (*path == '+' && path[1]) | |
904 | path++; | |
905 | else | |
a86ec89e | 906 | use_default_db = false; |
2127e193 | 907 | if (!read_drive_database(path)) |
ff28b140 | 908 | return FAILCMD; |
2127e193 | 909 | } |
4d59bff9 | 910 | break; |
832b75ed | 911 | case 'h': |
cfbba5b9 | 912 | printing_is_off = false; |
832b75ed GG |
913 | printslogan(); |
914 | Usage(); | |
ff28b140 | 915 | return 0; |
e9583e0c | 916 | |
d008864d GI |
917 | case 'g': |
918 | case_s_continued: // -s, see above | |
919 | case opt_set: // --set | |
920 | { | |
921 | ataopts.get_set_used = true; | |
922 | bool get = (optchar == 'g'); | |
923 | char name[16+1]; unsigned val; | |
924 | int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); | |
925 | if (sscanf(optarg, "%16[^,=]%n%*[,=]%n%u%n", name, &n1, &n2, &val, &n3) >= 1 | |
926 | && (n1 == len || (!get && n2 > 0))) { | |
f9e10201 JD |
927 | bool on = false; |
928 | bool off = false; | |
929 | bool ata = false; | |
930 | bool persistent = false; | |
931 | ||
932 | if (n2 > 0) { | |
933 | int len2 = strlen(optarg + n2); | |
934 | char * tmp = strstr(optarg+n2, ",p"); | |
935 | // handle ",p" in persistent options like: wcache-sct,[ata|on|off],p | |
936 | if (tmp && (strlen(tmp) == 2)) { | |
937 | persistent = true; | |
938 | len2 = strlen(optarg+n2) - 2; | |
939 | ||
940 | // the ,p option only works for set of SCT Feature Control command | |
941 | if (strcmp(name, "wcache-sct") != 0 && | |
942 | strcmp(name, "wcreorder") != 0) | |
943 | badarg = true; | |
944 | } | |
945 | on = !strncmp(optarg+n2, "on", len2); | |
946 | off = !strncmp(optarg+n2, "off", len2); | |
947 | ata = !strncmp(optarg+n2, "ata", len2); | |
948 | } | |
d008864d GI |
949 | if (n3 != len) |
950 | val = ~0U; | |
951 | ||
952 | if (get && !strcmp(name, "all")) { | |
953 | ataopts.get_aam = ataopts.get_apm = true; | |
954 | ataopts.get_security = true; | |
955 | ataopts.get_lookahead = ataopts.get_wcache = true; | |
f9e10201 | 956 | ataopts.get_dsn = true; |
ee38a438 | 957 | scsiopts.get_rcd = scsiopts.get_wce = true; |
d008864d GI |
958 | } |
959 | else if (!strcmp(name, "aam")) { | |
960 | if (get) | |
961 | ataopts.get_aam = true; | |
962 | else if (off) | |
963 | ataopts.set_aam = -1; | |
964 | else if (val <= 254) | |
965 | ataopts.set_aam = val + 1; | |
966 | else { | |
ee38a438 | 967 | snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n"); |
d008864d GI |
968 | badarg = true; |
969 | } | |
970 | } | |
971 | else if (!strcmp(name, "apm")) { | |
972 | if (get) | |
973 | ataopts.get_apm = true; | |
974 | else if (off) | |
975 | ataopts.set_apm = -1; | |
976 | else if (1 <= val && val <= 254) | |
977 | ataopts.set_apm = val + 1; | |
978 | else { | |
ee38a438 | 979 | snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n"); |
d008864d GI |
980 | badarg = true; |
981 | } | |
982 | } | |
983 | else if (!strcmp(name, "lookahead")) { | |
ee38a438 | 984 | if (get) { |
d008864d | 985 | ataopts.get_lookahead = true; |
ee38a438 | 986 | } |
d008864d GI |
987 | else if (off) |
988 | ataopts.set_lookahead = -1; | |
989 | else if (on) | |
990 | ataopts.set_lookahead = 1; | |
991 | else | |
992 | badarg = true; | |
993 | } | |
3d17a85c | 994 | else if (!strcmp(name, "wcreorder")) { |
f9e10201 | 995 | ataopts.sct_wcache_reorder_set_pers = persistent; |
3d17a85c GI |
996 | if (get) { |
997 | ataopts.sct_wcache_reorder_get = true; | |
998 | } | |
999 | else if (off) | |
1000 | ataopts.sct_wcache_reorder_set = -1; | |
1001 | else if (on) | |
1002 | ataopts.sct_wcache_reorder_set = 1; | |
1003 | else | |
1004 | badarg = true; | |
1005 | } | |
f9e10201 JD |
1006 | else if (!strcmp(name, "wcache-sct")) { |
1007 | ataopts.sct_wcache_sct_set_pers = persistent; | |
1008 | if (get) { | |
1009 | ataopts.sct_wcache_sct_get = true; | |
1010 | } | |
1011 | else if (off) | |
1012 | ataopts.sct_wcache_sct_set = 3; | |
1013 | else if (on) | |
1014 | ataopts.sct_wcache_sct_set = 2; | |
1015 | else if (ata) | |
1016 | ataopts.sct_wcache_sct_set = 1; | |
1017 | else | |
1018 | badarg = true; | |
1019 | } | |
ee38a438 GI |
1020 | else if (!strcmp(name, "rcache")) { |
1021 | if (get) | |
1022 | scsiopts.get_rcd = true; | |
1023 | else if (off) | |
1024 | scsiopts.set_rcd = -1; | |
1025 | else if (on) | |
1026 | scsiopts.set_rcd = 1; | |
1027 | else | |
1028 | badarg = true; | |
1029 | } | |
d008864d GI |
1030 | else if (get && !strcmp(name, "security")) { |
1031 | ataopts.get_security = true; | |
1032 | } | |
1033 | else if (!get && !strcmp(optarg, "security-freeze")) { | |
1034 | ataopts.set_security_freeze = true; | |
1035 | } | |
1036 | else if (!get && !strcmp(optarg, "standby,now")) { | |
1037 | ataopts.set_standby_now = true; | |
1038 | } | |
1039 | else if (!get && !strcmp(name, "standby")) { | |
1040 | if (off) | |
1041 | ataopts.set_standby = 0 + 1; | |
1042 | else if (val <= 255) | |
1043 | ataopts.set_standby = val + 1; | |
1044 | else { | |
ee38a438 | 1045 | snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n"); |
d008864d GI |
1046 | badarg = true; |
1047 | } | |
1048 | } | |
1049 | else if (!strcmp(name, "wcache")) { | |
ee38a438 | 1050 | if (get) { |
d008864d | 1051 | ataopts.get_wcache = true; |
ee38a438 GI |
1052 | scsiopts.get_wce = true; |
1053 | } | |
1054 | else if (off) { | |
d008864d | 1055 | ataopts.set_wcache = -1; |
ee38a438 GI |
1056 | scsiopts.set_wce = -1; |
1057 | } | |
1058 | else if (on) { | |
d008864d | 1059 | ataopts.set_wcache = 1; |
ee38a438 GI |
1060 | scsiopts.set_wce = 1; |
1061 | } | |
d008864d GI |
1062 | else |
1063 | badarg = true; | |
1064 | } | |
f9e10201 JD |
1065 | else if (!strcmp(name, "dsn")) { |
1066 | if (get) { | |
1067 | ataopts.get_dsn = true; | |
1068 | } | |
1069 | else if (off) { | |
1070 | ataopts.set_dsn = -1; | |
1071 | } | |
1072 | else if (on) { | |
1073 | ataopts.set_dsn = 1; | |
1074 | } | |
1075 | else | |
1076 | badarg = true; | |
1077 | } | |
d008864d GI |
1078 | else |
1079 | badarg = true; | |
1080 | } | |
1081 | else | |
1082 | badarg = true; | |
1083 | } | |
1084 | break; | |
1085 | ||
e9583e0c GI |
1086 | case opt_scan: |
1087 | case opt_scan_open: | |
1088 | scan = optchar; | |
1089 | break; | |
1090 | ||
ff28b140 TL |
1091 | case 'j': |
1092 | { | |
1093 | print_as_json = true; | |
1094 | print_as_json_options.pretty = true; | |
1095 | print_as_json_options.sorted = false; | |
1096 | print_as_json_options.flat = false; | |
1097 | print_as_json_output = false; | |
1098 | print_as_json_impl = print_as_json_unimpl = false; | |
1099 | bool json_verbose = false; | |
1100 | if (optarg_is_set) { | |
1101 | for (int i = 0; optarg[i]; i++) { | |
1102 | switch (optarg[i]) { | |
1103 | case 'c': print_as_json_options.pretty = false; break; | |
1104 | case 'g': print_as_json_options.flat = true; break; | |
1105 | case 'i': print_as_json_impl = true; break; | |
1106 | case 'o': print_as_json_output = true; break; | |
1107 | case 's': print_as_json_options.sorted = true; break; | |
1108 | case 'u': print_as_json_unimpl = true; break; | |
1109 | case 'v': json_verbose = true; break; | |
1110 | default: badarg = true; | |
1111 | } | |
1112 | } | |
1113 | } | |
1114 | js_initialize(argc, argv, json_verbose); | |
1115 | } | |
1116 | break; | |
1117 | ||
832b75ed GG |
1118 | case '?': |
1119 | default: | |
cfbba5b9 | 1120 | printing_is_off = false; |
832b75ed | 1121 | printslogan(); |
832b75ed GG |
1122 | // Point arg to the argument in which this option was found. |
1123 | arg = argv[optind-1]; | |
1124 | // Check whether the option is a long option that doesn't map to -h. | |
1125 | if (arg[1] == '-' && optchar != 'h') { | |
1126 | // Iff optopt holds a valid option then argument must be missing. | |
d008864d | 1127 | if (optopt && (optopt >= opt_scan || strchr(shortopts, optopt))) { |
ff28b140 | 1128 | jerr("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); |
832b75ed GG |
1129 | printvalidarglistmessage(optopt); |
1130 | } else | |
ff28b140 | 1131 | jerr("=======> UNRECOGNIZED OPTION: %s\n",arg+2); |
832b75ed GG |
1132 | if (extraerror[0]) |
1133 | pout("=======> %s", extraerror); | |
1134 | UsageSummary(); | |
ff28b140 | 1135 | return FAILCMD; |
832b75ed | 1136 | } |
d008864d | 1137 | if (0 < optopt && optopt < '~') { |
832b75ed GG |
1138 | // Iff optopt holds a valid option then argument must be |
1139 | // missing. Note (BA) this logic seems to fail using Solaris | |
1140 | // getopt! | |
1141 | if (strchr(shortopts, optopt) != NULL) { | |
ff28b140 | 1142 | jerr("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt); |
832b75ed GG |
1143 | printvalidarglistmessage(optopt); |
1144 | } else | |
ff28b140 | 1145 | jerr("=======> UNRECOGNIZED OPTION: %c\n",optopt); |
832b75ed GG |
1146 | if (extraerror[0]) |
1147 | pout("=======> %s", extraerror); | |
1148 | UsageSummary(); | |
ff28b140 | 1149 | return FAILCMD; |
832b75ed GG |
1150 | } |
1151 | Usage(); | |
ff28b140 | 1152 | return 0; |
832b75ed GG |
1153 | } // closes switch statement to process command-line options |
1154 | ||
1155 | // Check to see if option had an unrecognized or incorrect argument. | |
1156 | if (badarg) { | |
1157 | printslogan(); | |
1158 | // It would be nice to print the actual option name given by the user | |
1159 | // here, but we just print the short form. Please fix this if you know | |
1160 | // a clean way to do it. | |
d008864d | 1161 | char optstr[] = { (char)optchar, 0 }; |
ff28b140 | 1162 | jerr("=======> INVALID ARGUMENT TO -%s: %s\n", |
ee38a438 GI |
1163 | (optchar == opt_identify ? "-identify" : |
1164 | optchar == opt_set ? "-set" : | |
ff28b140 TL |
1165 | optchar == opt_smart ? "-smart" : |
1166 | optchar == 'j' ? "-json" : optstr), optarg); | |
832b75ed GG |
1167 | printvalidarglistmessage(optchar); |
1168 | if (extraerror[0]) | |
1169 | pout("=======> %s", extraerror); | |
1170 | UsageSummary(); | |
ff28b140 | 1171 | return FAILCMD; |
832b75ed GG |
1172 | } |
1173 | } | |
e9583e0c GI |
1174 | |
1175 | // Special handling of --scan, --scanopen | |
1176 | if (scan) { | |
cfbba5b9 | 1177 | // Read or init drive database to allow USB ID check. |
a86ec89e | 1178 | if (!init_drive_database(use_default_db)) |
ff28b140 | 1179 | return FAILCMD; |
a86ec89e | 1180 | scan_devices(scan_types, (scan == opt_scan_open), argv + optind); |
ff28b140 | 1181 | return 0; |
e9583e0c GI |
1182 | } |
1183 | ||
832b75ed GG |
1184 | // At this point we have processed all command-line options. If the |
1185 | // print output is switchable, then start with the print output | |
1186 | // turned off | |
cfbba5b9 GI |
1187 | if (printing_is_switchable) |
1188 | printing_is_off = true; | |
832b75ed | 1189 | |
a86ec89e GI |
1190 | // Check for multiple -d TYPE options |
1191 | if (scan_types.size() > 1) { | |
1192 | printing_is_off = false; | |
1193 | printslogan(); | |
ff28b140 | 1194 | jerr("ERROR: multiple -d TYPE options are only allowed with --scan\n"); |
a86ec89e | 1195 | UsageSummary(); |
ff28b140 | 1196 | return FAILCMD; |
a86ec89e GI |
1197 | } |
1198 | ||
832b75ed | 1199 | // error message if user has asked for more than one test |
2127e193 | 1200 | if (testcnt > 1) { |
cfbba5b9 | 1201 | printing_is_off = false; |
832b75ed | 1202 | printslogan(); |
ff28b140 | 1203 | jerr("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); |
832b75ed | 1204 | UsageSummary(); |
ff28b140 | 1205 | return FAILCMD; |
832b75ed GG |
1206 | } |
1207 | ||
1208 | // error message if user has set selective self-test options without | |
1209 | // asking for a selective self-test | |
2127e193 GI |
1210 | if ( (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select) |
1211 | && !ataopts.smart_selective_args.num_spans) { | |
cfbba5b9 | 1212 | printing_is_off = false; |
832b75ed | 1213 | printslogan(); |
2127e193 | 1214 | if (ataopts.smart_selective_args.pending_time) |
ff28b140 | 1215 | jerr("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); |
832b75ed | 1216 | else |
ff28b140 | 1217 | jerr("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n"); |
832b75ed | 1218 | UsageSummary(); |
ff28b140 | 1219 | return FAILCMD; |
832b75ed GG |
1220 | } |
1221 | ||
1222 | // If captive option was used, change test type if appropriate. | |
2127e193 GI |
1223 | if (captive) |
1224 | switch (ataopts.smart_selftest_type) { | |
1225 | case SHORT_SELF_TEST: | |
1226 | ataopts.smart_selftest_type = SHORT_CAPTIVE_SELF_TEST; | |
1227 | scsiopts.smart_short_selftest = false; | |
1228 | scsiopts.smart_short_cap_selftest = true; | |
1229 | break; | |
1230 | case EXTEND_SELF_TEST: | |
1231 | ataopts.smart_selftest_type = EXTEND_CAPTIVE_SELF_TEST; | |
1232 | scsiopts.smart_extend_selftest = false; | |
1233 | scsiopts.smart_extend_cap_selftest = true; | |
1234 | break; | |
1235 | case CONVEYANCE_SELF_TEST: | |
1236 | ataopts.smart_selftest_type = CONVEYANCE_CAPTIVE_SELF_TEST; | |
1237 | break; | |
1238 | case SELECTIVE_SELF_TEST: | |
1239 | ataopts.smart_selftest_type = SELECTIVE_CAPTIVE_SELF_TEST; | |
1240 | break; | |
1241 | } | |
1242 | ||
832b75ed GG |
1243 | // From here on, normal operations... |
1244 | printslogan(); | |
1245 | ||
1246 | // Warn if the user has provided no device name | |
1247 | if (argc-optind<1){ | |
ff28b140 | 1248 | jerr("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); |
832b75ed | 1249 | UsageSummary(); |
ff28b140 | 1250 | return FAILCMD; |
832b75ed GG |
1251 | } |
1252 | ||
1253 | // Warn if the user has provided more than one device name | |
1254 | if (argc-optind>1){ | |
1255 | int i; | |
ff28b140 | 1256 | jerr("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); |
832b75ed GG |
1257 | pout("You have provided %d device names:\n",argc-optind); |
1258 | for (i=0; i<argc-optind; i++) | |
1259 | pout("%s\n",argv[optind+i]); | |
1260 | UsageSummary(); | |
ff28b140 | 1261 | return FAILCMD; |
2127e193 GI |
1262 | } |
1263 | ||
1264 | // Read or init drive database | |
a86ec89e | 1265 | if (!init_drive_database(use_default_db)) |
ff28b140 TL |
1266 | return FAILCMD; |
1267 | ||
1268 | // No error, continue in main_worker() | |
1269 | return -1; | |
1270 | } | |
1271 | ||
1272 | // Printing functions | |
1273 | ||
1274 | __attribute_format_printf(3, 0) | |
1275 | static void vjpout(bool is_js_impl, const char * msg_severity, | |
1276 | const char *fmt, va_list ap) | |
1277 | { | |
1278 | if (!print_as_json) { | |
1279 | // Print out directly | |
1280 | vprintf(fmt, ap); | |
1281 | fflush(stdout); | |
1282 | } | |
1283 | else { | |
1284 | // Add lines to JSON output | |
1285 | static char buf[1024]; | |
1286 | static char * bufnext = buf; | |
1287 | vsnprintf(bufnext, sizeof(buf) - (bufnext - buf), fmt, ap); | |
1288 | for (char * p = buf, *q; ; p = q) { | |
1289 | if (!(q = strchr(p, '\n'))) { | |
1290 | // Keep remaining line for next call | |
1291 | for (bufnext = buf; *p; bufnext++, p++) | |
1292 | *bufnext = *p; | |
1293 | break; | |
1294 | } | |
1295 | *q++ = 0; // '\n' -> '\0' | |
1296 | ||
1297 | static int lineno = 0; | |
1298 | lineno++; | |
1299 | if (print_as_json_output) { | |
1300 | // Collect full output in array | |
1301 | static int outindex = 0; | |
1302 | jglb["smartctl"]["output"][outindex++] = p; | |
1303 | } | |
1304 | if (!*p) | |
1305 | continue; // Skip empty line | |
1306 | ||
1307 | if (msg_severity) { | |
1308 | // Collect non-empty messages in array | |
1309 | static int errindex = 0; | |
1310 | json::ref jref = jglb["smartctl"]["messages"][errindex++]; | |
1311 | jref["string"] = p; | |
1312 | jref["severity"] = msg_severity; | |
1313 | } | |
1314 | ||
1315 | if ( ( is_js_impl && print_as_json_impl ) | |
1316 | || (!is_js_impl && print_as_json_unimpl)) { | |
1317 | // Add (un)implemented non-empty lines to global object | |
1318 | jglb[strprintf("smartctl_%04d_%c", lineno, | |
1319 | (is_js_impl ? 'i' : 'u')).c_str()] = p; | |
1320 | } | |
1321 | } | |
1322 | } | |
1323 | } | |
1324 | ||
1325 | // Default: print to stdout | |
1326 | // --json: ignore | |
1327 | // --json=o: append to "output" array | |
1328 | // --json=u: add "smartctl_NNNN_u" element(s) | |
1329 | void pout(const char *fmt, ...) | |
1330 | { | |
1331 | if (printing_is_off) | |
1332 | return; | |
1333 | if (print_as_json && !(print_as_json_output | |
1334 | || print_as_json_impl || print_as_json_unimpl)) | |
1335 | return; | |
2127e193 | 1336 | |
ff28b140 TL |
1337 | va_list ap; |
1338 | va_start(ap, fmt); | |
1339 | vjpout(false, 0, fmt, ap); | |
1340 | va_end(ap); | |
832b75ed GG |
1341 | } |
1342 | ||
ff28b140 TL |
1343 | // Default: Print to stdout |
1344 | // --json: ignore | |
1345 | // --json=o: append to "output" array | |
1346 | // --json=i: add "smartctl_NNNN_i" element(s) | |
1347 | void jout(const char *fmt, ...) | |
1348 | { | |
1349 | if (printing_is_off) | |
1350 | return; | |
1351 | if (print_as_json && !(print_as_json_output | |
1352 | || print_as_json_impl || print_as_json_unimpl)) | |
1353 | return; | |
1354 | ||
832b75ed | 1355 | va_list ap; |
ff28b140 TL |
1356 | va_start(ap, fmt); |
1357 | vjpout(true, 0, fmt, ap); | |
1358 | va_end(ap); | |
1359 | } | |
1360 | ||
1361 | // Default: print to stdout | |
1362 | // --json: append to "messages" | |
1363 | // --json=o: append to "output" array | |
1364 | // --json=i: add "smartctl_NNNN_i" element(s) | |
1365 | void jinf(const char *fmt, ...) | |
1366 | { | |
1367 | if (printing_is_off) | |
832b75ed | 1368 | return; |
832b75ed | 1369 | |
ff28b140 TL |
1370 | va_list ap; |
1371 | va_start(ap, fmt); | |
1372 | vjpout(true, "information", fmt, ap); | |
1373 | va_end(ap); | |
1374 | } | |
1375 | ||
1376 | void jwrn(const char *fmt, ...) | |
1377 | { | |
1378 | if (printing_is_off) | |
1379 | return; | |
1380 | ||
1381 | va_list ap; | |
1382 | va_start(ap, fmt); | |
1383 | vjpout(true, "warning", fmt, ap); | |
1384 | va_end(ap); | |
1385 | } | |
1386 | ||
1387 | void jerr(const char *fmt, ...) | |
1388 | { | |
1389 | if (printing_is_off) | |
1390 | return; | |
1391 | ||
1392 | va_list ap; | |
1393 | va_start(ap, fmt); | |
1394 | vjpout(true, "error", fmt, ap); | |
832b75ed | 1395 | va_end(ap); |
832b75ed GG |
1396 | } |
1397 | ||
cfbba5b9 GI |
1398 | // Globals to set failuretest() policy |
1399 | bool failuretest_conservative = false; | |
1400 | unsigned char failuretest_permissive = 0; | |
832b75ed | 1401 | |
cfbba5b9 GI |
1402 | // Compares failure type to policy in effect, and either exits or |
1403 | // simply returns to the calling routine. | |
1404 | // Used in ataprint.cpp and scsiprint.cpp. | |
1405 | void failuretest(failure_type type, int returnvalue) | |
1406 | { | |
1407 | // If this is an error in an "optional" SMART command | |
1408 | if (type == OPTIONAL_CMD) { | |
1409 | if (!failuretest_conservative) | |
1410 | return; | |
1411 | pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n"); | |
ff28b140 | 1412 | throw int(returnvalue); |
cfbba5b9 | 1413 | } |
832b75ed | 1414 | |
cfbba5b9 GI |
1415 | // If this is an error in a "mandatory" SMART command |
1416 | if (type == MANDATORY_CMD) { | |
1417 | if (failuretest_permissive--) | |
1418 | return; | |
1419 | pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n"); | |
ff28b140 | 1420 | throw int(returnvalue); |
cfbba5b9 GI |
1421 | } |
1422 | ||
1423 | throw std::logic_error("failuretest: Unknown type"); | |
832b75ed GG |
1424 | } |
1425 | ||
2127e193 GI |
1426 | // Used to warn users about invalid checksums. Called from atacmds.cpp. |
1427 | // Action to be taken may be altered by the user. | |
1428 | void checksumwarning(const char * string) | |
1429 | { | |
1430 | // user has asked us to ignore checksum errors | |
1431 | if (checksum_err_mode == CHECKSUM_ERR_IGNORE) | |
1432 | return; | |
832b75ed | 1433 | |
2127e193 | 1434 | pout("Warning! %s error: invalid SMART checksum.\n", string); |
832b75ed | 1435 | |
2127e193 GI |
1436 | // user has asked us to fail on checksum errors |
1437 | if (checksum_err_mode == CHECKSUM_ERR_EXIT) | |
ff28b140 | 1438 | throw int(FAILSMART); |
2127e193 | 1439 | } |
832b75ed | 1440 | |
2127e193 GI |
1441 | // Return info string about device protocol |
1442 | static const char * get_protocol_info(const smart_device * dev) | |
1443 | { | |
a86ec89e GI |
1444 | switch ( (int)dev->is_ata() |
1445 | | ((int)dev->is_scsi() << 1) | |
1446 | | ((int)dev->is_nvme() << 2)) { | |
2127e193 GI |
1447 | case 0x1: return "ATA"; |
1448 | case 0x2: return "SCSI"; | |
1449 | case 0x3: return "ATA+SCSI"; | |
a86ec89e | 1450 | case 0x4: return "NVMe"; |
2127e193 GI |
1451 | default: return "Unknown"; |
1452 | } | |
1453 | } | |
832b75ed | 1454 | |
ff28b140 TL |
1455 | // Add JSON device info |
1456 | static void js_device_info(const json::ref & jref, const smart_device * dev) | |
1457 | { | |
1458 | jref["name"] = dev->get_dev_name(); | |
1459 | jref["info_name"] = dev->get_info_name(); | |
1460 | jref["type"] = dev->get_dev_type(); | |
1461 | jref["protocol"] = get_protocol_info(dev); | |
1462 | } | |
1463 | ||
e9583e0c | 1464 | // Device scan |
cfbba5b9 | 1465 | // smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...] |
a86ec89e | 1466 | void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv) |
e9583e0c | 1467 | { |
a86ec89e | 1468 | bool dont_print = !(ata_debugmode || scsi_debugmode || nvme_debugmode); |
cfbba5b9 GI |
1469 | |
1470 | const char * pattern = 0; | |
1471 | int ai = 0; | |
1472 | if (argv[ai] && argv[ai][0] != '-') | |
1473 | pattern = argv[ai++]; | |
e9583e0c | 1474 | |
cfbba5b9 GI |
1475 | smart_device_list devlist; |
1476 | printing_is_off = dont_print; | |
a86ec89e | 1477 | bool ok = smi()->scan_smart_devices(devlist, types, pattern); |
cfbba5b9 | 1478 | printing_is_off = false; |
e9583e0c GI |
1479 | |
1480 | if (!ok) { | |
cfbba5b9 | 1481 | pout("# scan_smart_devices: %s\n", smi()->get_errmsg()); |
e9583e0c GI |
1482 | return; |
1483 | } | |
1484 | ||
1485 | for (unsigned i = 0; i < devlist.size(); i++) { | |
cfbba5b9 | 1486 | smart_device_auto_ptr dev( devlist.release(i) ); |
ff28b140 | 1487 | json::ref jref = jglb["devices"][i]; |
e9583e0c | 1488 | |
e9583e0c | 1489 | if (with_open) { |
cfbba5b9 GI |
1490 | printing_is_off = dont_print; |
1491 | dev.replace ( dev->autodetect_open() ); | |
1492 | printing_is_off = false; | |
ff28b140 | 1493 | } |
e9583e0c | 1494 | |
ff28b140 TL |
1495 | js_device_info(jref, dev.get()); |
1496 | ||
1497 | if (with_open && !dev->is_open()) { | |
1498 | jout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(), | |
1499 | dev->get_dev_type(), dev->get_info_name(), | |
1500 | get_protocol_info(dev.get()), dev->get_errmsg()); | |
1501 | jref["open_error"] = dev->get_errmsg(); | |
1502 | continue; | |
cfbba5b9 GI |
1503 | } |
1504 | ||
ff28b140 | 1505 | jout("%s -d %s", dev->get_dev_name(), dev->get_dev_type()); |
cfbba5b9 | 1506 | if (!argv[ai]) |
ff28b140 | 1507 | jout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get())); |
cfbba5b9 GI |
1508 | else { |
1509 | for (int j = ai; argv[j]; j++) | |
ff28b140 TL |
1510 | jout(" %s", argv[j]); |
1511 | jout("\n"); | |
e9583e0c GI |
1512 | } |
1513 | ||
e9583e0c GI |
1514 | if (dev->is_open()) |
1515 | dev->close(); | |
1516 | } | |
1517 | } | |
1518 | ||
2127e193 | 1519 | // Main program without exception handling |
cfbba5b9 | 1520 | static int main_worker(int argc, char **argv) |
2127e193 | 1521 | { |
d2e702cf GI |
1522 | // Throw if runtime environment does not match compile time test. |
1523 | check_config(); | |
e9583e0c | 1524 | |
2127e193 GI |
1525 | // Initialize interface |
1526 | smart_interface::init(); | |
1527 | if (!smi()) | |
1528 | return 1; | |
1529 | ||
bed94269 | 1530 | // Parse input arguments |
ff28b140 | 1531 | const char * type = 0; |
bed94269 GI |
1532 | ata_print_options ataopts; |
1533 | scsi_print_options scsiopts; | |
a86ec89e | 1534 | nvme_print_options nvmeopts; |
d008864d | 1535 | bool print_type_only = false; |
ff28b140 TL |
1536 | { |
1537 | int status = parse_options(argc, argv, type, ataopts, scsiopts, nvmeopts, print_type_only); | |
1538 | if (status >= 0) | |
1539 | return status; | |
1540 | } | |
bed94269 GI |
1541 | |
1542 | const char * name = argv[argc-1]; | |
1543 | ||
1544 | smart_device_auto_ptr dev; | |
1545 | if (!strcmp(name,"-")) { | |
1546 | // Parse "smartctl -r ataioctl,2 ..." output from stdin | |
1547 | if (type || print_type_only) { | |
ee38a438 | 1548 | pout("-d option is not allowed in conjunction with device name \"-\".\n"); |
a37e7145 GG |
1549 | UsageSummary(); |
1550 | return FAILCMD; | |
1551 | } | |
bed94269 GI |
1552 | dev = get_parsed_ata_device(smi(), name); |
1553 | } | |
1554 | else | |
1555 | // get device of appropriate type | |
1556 | dev = smi()->get_smart_device(name, type); | |
1557 | ||
1558 | if (!dev) { | |
ff28b140 | 1559 | jerr("%s: %s\n", name, smi()->get_errmsg()); |
bed94269 GI |
1560 | if (type) |
1561 | printvalidarglistmessage('d'); | |
1562 | else | |
ee38a438 | 1563 | pout("Please specify device type with the -d option.\n"); |
bed94269 GI |
1564 | UsageSummary(); |
1565 | return FAILCMD; | |
1566 | } | |
a37e7145 | 1567 | |
bed94269 GI |
1568 | if (print_type_only) |
1569 | // Report result of first autodetection | |
1570 | pout("%s: Device of type '%s' [%s] detected\n", | |
1571 | dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); | |
2127e193 | 1572 | |
a86ec89e | 1573 | if (dev->is_ata() && ataopts.powermode>=2 && dev->is_powered_down()) { |
ff28b140 | 1574 | jinf("Device is in STANDBY (OS) mode, exit(%d)\n", ataopts.powerexit); |
f9e10201 | 1575 | return ataopts.powerexit; |
a86ec89e GI |
1576 | } |
1577 | ||
bed94269 GI |
1578 | // Open device |
1579 | { | |
1580 | // Save old info | |
1581 | smart_device::device_info oldinfo = dev->get_info(); | |
2127e193 | 1582 | |
bed94269 GI |
1583 | // Open with autodetect support, may return 'better' device |
1584 | dev.replace( dev->autodetect_open() ); | |
2127e193 | 1585 | |
bed94269 | 1586 | // Report if type has changed |
a86ec89e GI |
1587 | if ( (ata_debugmode || scsi_debugmode || nvme_debugmode || print_type_only) |
1588 | && oldinfo.dev_type != dev->get_dev_type() ) | |
bed94269 GI |
1589 | pout("%s: Device open changed type from '%s' to '%s'\n", |
1590 | dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type()); | |
2127e193 | 1591 | } |
bed94269 | 1592 | if (!dev->is_open()) { |
ff28b140 | 1593 | jerr("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg()); |
bed94269 | 1594 | return FAILDEV; |
832b75ed | 1595 | } |
bed94269 | 1596 | |
ff28b140 TL |
1597 | // Add JSON info similar to --scan output |
1598 | js_device_info(jglb["device"], dev.get()); | |
1599 | ||
bed94269 GI |
1600 | // now call appropriate ATA or SCSI routine |
1601 | int retval = 0; | |
1602 | if (print_type_only) | |
ff28b140 | 1603 | jout("%s: Device of type '%s' [%s] opened\n", |
bed94269 GI |
1604 | dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); |
1605 | else if (dev->is_ata()) | |
1606 | retval = ataPrintMain(dev->to_ata(), ataopts); | |
1607 | else if (dev->is_scsi()) | |
1608 | retval = scsiPrintMain(dev->to_scsi(), scsiopts); | |
a86ec89e GI |
1609 | else if (dev->is_nvme()) |
1610 | retval = nvmePrintMain(dev->to_nvme(), nvmeopts); | |
bed94269 GI |
1611 | else |
1612 | // we should never fall into this branch! | |
a86ec89e | 1613 | pout("%s: Neither ATA, SCSI nor NVMe device\n", dev->get_info_name()); |
bed94269 GI |
1614 | |
1615 | dev->close(); | |
2127e193 GI |
1616 | return retval; |
1617 | } | |
832b75ed | 1618 | |
2127e193 GI |
1619 | |
1620 | // Main program | |
1621 | int main(int argc, char **argv) | |
1622 | { | |
1623 | int status; | |
a86ec89e GI |
1624 | bool badcode = false; |
1625 | ||
2127e193 | 1626 | try { |
ff28b140 TL |
1627 | try { |
1628 | // Do the real work ... | |
1629 | status = main_worker(argc, argv); | |
1630 | } | |
1631 | catch (int ex) { | |
1632 | // Exit status from checksumwarning() and failuretest() arrives here | |
1633 | status = ex; | |
1634 | } | |
1635 | // Print JSON if enabled | |
1636 | if (jglb.has_uint128_output()) | |
1637 | jglb["smartctl"]["uint128_precision_bits"] = uint128_to_str_precision_bits(); | |
1638 | jglb["smartctl"]["exit_status"] = status; | |
1639 | jglb.print(stdout, print_as_json_options); | |
832b75ed | 1640 | } |
2127e193 GI |
1641 | catch (const std::bad_alloc & /*ex*/) { |
1642 | // Memory allocation failed (also thrown by std::operator new) | |
bed94269 | 1643 | printf("Smartctl: Out of memory\n"); |
2127e193 | 1644 | status = FAILCMD; |
832b75ed | 1645 | } |
2127e193 GI |
1646 | catch (const std::exception & ex) { |
1647 | // Other fatal errors | |
bed94269 | 1648 | printf("Smartctl: Exception: %s\n", ex.what()); |
a86ec89e | 1649 | badcode = true; |
2127e193 | 1650 | status = FAILCMD; |
832b75ed | 1651 | } |
a86ec89e GI |
1652 | |
1653 | // Check for remaining device objects | |
1654 | if (smart_device::get_num_objects() != 0) { | |
1655 | printf("Smartctl: Internal Error: %d device object(s) left at exit.\n", | |
1656 | smart_device::get_num_objects()); | |
1657 | badcode = true; | |
1658 | status = FAILCMD; | |
1659 | } | |
1660 | ||
1661 | if (badcode) | |
1662 | printf("Please inform " PACKAGE_BUGREPORT ", including output of smartctl -V.\n"); | |
1663 | ||
2127e193 | 1664 | return status; |
832b75ed | 1665 | } |
2127e193 | 1666 |