Ticket #1079: scsiprint.cpp

File scsiprint.cpp, 86.9 KB (added by Rick Chen, 3 years ago)
Line 
1/*
2 * scsiprint.cpp
3 *
4 * Home page of code is: http://www.smartmontools.org
5 *
6 * Copyright (C) 2002-11 Bruce Allen
7 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
8 * Copyright (C) 2003-18 Douglas Gilbert <dgilbert@interlog.com>
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
13
14#include "config.h"
15#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
16
17#include <inttypes.h>
18#include <stdio.h>
19#include <string.h>
20#include <fcntl.h>
21#include <errno.h>
22
23#include "scsicmds.h"
24#include "atacmds.h" // smart_command_set
25#include "dev_interface.h"
26#include "scsiprint.h"
27#include "smartctl.h"
28#include "utility.h"
29#include "sg_unaligned.h"
30
31#define GBUF_SIZE 65532
32
33const char * scsiprint_c_cvsid = "$Id$"
34                                 SCSIPRINT_H_CVSID;
35
36
37uint8_t gBuf[GBUF_SIZE];
38#define LOG_RESP_LEN 252
39#define LOG_RESP_LONG_LEN ((62 * 256) + 252)
40#define LOG_RESP_TAPE_ALERT_LEN 0x144
41
42/* Log pages supported */
43static bool gSmartLPage = false;     /* Informational Exceptions log page */
44static bool gTempLPage = false;
45static bool gSelfTestLPage = false;
46static bool gStartStopLPage = false;
47static bool gReadECounterLPage = false;
48static bool gWriteECounterLPage = false;
49static bool gVerifyECounterLPage = false;
50static bool gNonMediumELPage = false;
51static bool gLastNErrorEvLPage = false;
52static bool gBackgroundResultsLPage = false;
53static bool gProtocolSpecificLPage = false;
54static bool gTapeAlertsLPage = false;
55static bool gSSMediaLPage = false;
56static bool gFormatStatusLPage = false;
57static bool gEnviroReportingLPage = false;
58static bool gEnviroLimitsLPage = false;
59static bool gUtilizationLPage = false;
60static bool gPendDefectsLPage = false;
61static bool gBackgroundOpLPage = false;
62static bool gLPSMisalignLPage = false;
63
64/* Vendor specific log pages */
65static bool gSeagateCacheLPage = false;
66static bool gSeagateFactoryLPage = false;
67
68/* Mode pages supported */
69static bool gIecMPage = true;    /* N.B. assume it until we know otherwise */
70
71/* Remember last successful mode sense/select command */
72static int modese_len = 0;
73
74/* Remember this value from the most recent INQUIRY */
75static int scsi_version;
76#define SCSI_VERSION_SPC_4 0x6
77#define SCSI_VERSION_SPC_5 0x7
78#define SCSI_VERSION_HIGHEST SCSI_VERSION_SPC_5
79
80/* T10 vendor identification. Should match entry in last Annex of SPC
81 * drafts and standards (e.g. SPC-4). */
82static char scsi_vendor[8+1];
83#define T10_VENDOR_SEAGATE "SEAGATE"
84#define T10_VENDOR_HITACHI_1 "HITACHI"
85#define T10_VENDOR_HITACHI_2 "HL-DT-ST"
86#define T10_VENDOR_HITACHI_3 "HGST"
87
88static const char * logSenStr = "Log Sense";
89static const char * logSenRspStr = "Log Sense response";
90
91
92static bool
93seagate_or_hitachi(void)
94{
95    return ((0 == memcmp(scsi_vendor, T10_VENDOR_SEAGATE,
96                         strlen(T10_VENDOR_SEAGATE))) ||
97            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_1,
98                         strlen(T10_VENDOR_HITACHI_1))) ||
99            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_2,
100                         strlen(T10_VENDOR_HITACHI_2))) ||
101            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_3,
102                         strlen(T10_VENDOR_HITACHI_3))));
103}
104
105static bool
106all_ffs(const uint8_t * bp, int b_len)
107{
108    if ((NULL == bp) || (b_len <= 0))
109        return false;
110    for (--b_len; b_len >= 0; --b_len) {
111        if (0xff != bp[b_len])
112            return false;
113    }
114    return true;
115}
116
117static void
118scsiGetSupportedLogPages(scsi_device * device)
119{
120    bool got_subpages = false;
121    int k, bump, err, payload_len, num_unreported, num_unreported_spg;
122    const uint8_t * up;
123    uint8_t sup_lpgs[LOG_RESP_LEN];
124
125    memset(gBuf, 0, LOG_RESP_LEN);
126    if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
127                            LOG_RESP_LEN, 0))) {
128        if (scsi_debugmode > 0)
129            pout("%s for supported pages failed [%s]\n", logSenStr,
130                 scsiErrString(err));
131        /* try one more time with defined length, workaround for the bug #678
132        found with ST8000NM0075/E001 */
133        err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
134                            LOG_RESP_LEN, 68); /* 64 max pages + 4b header */
135        if (scsi_debugmode > 0)
136            pout("%s for supported pages failed (second attempt) [%s]\n",
137                 logSenStr, scsiErrString(err));
138        if (err)
139            return;
140        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
141    } else if ((scsi_version >= SCSI_VERSION_SPC_4) &&
142               (scsi_version <= SCSI_VERSION_HIGHEST)) {
143        /* unclear what code T10 will choose for SPC-6 */
144        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
145        if ((err = scsiLogSense(device, SUPPORTED_LPAGES, SUPP_SPAGE_L_SPAGE,
146                                gBuf, LOG_RESP_LONG_LEN,
147                                -1 /* just single not double fetch */))) {
148            if (scsi_debugmode > 0)
149                pout("%s for supported pages and subpages failed [%s]\n",
150                     logSenStr, scsiErrString(err));
151        } else {
152            if (0 == memcmp(gBuf, sup_lpgs, LOG_RESP_LEN)) {
153                if (scsi_debugmode > 0)
154                    pout("%s: %s ignored subpage field, bad\n",
155                         __func__, logSenRspStr);
156            } else if (! ((0x40 & gBuf[0]) &&
157                          (SUPP_SPAGE_L_SPAGE == gBuf[1]))) {
158                if (scsi_debugmode > 0)
159                    pout("%s supported subpages is bad SPF=%u SUBPG=%u\n",
160                         logSenRspStr, !! (0x40 & gBuf[0]), gBuf[2]);
161            } else
162                got_subpages = true;
163        }
164    } else
165        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
166
167    if (got_subpages) {
168         payload_len = sg_get_unaligned_be16(gBuf + 2);
169         bump = 2;
170         up = gBuf + LOGPAGEHDRSIZE;
171    } else {
172        payload_len = sup_lpgs[3];
173        bump = 1;
174        up = sup_lpgs + LOGPAGEHDRSIZE;
175    }
176
177    num_unreported_spg = 0;
178    for (num_unreported = 0, k = 0; k < payload_len; k += bump, up += bump) {
179        uint8_t pg_num = 0x3f & up[0];
180        uint8_t sub_pg_num = (0x40 & up[0]) ? up[1] : 0;
181
182        switch (pg_num)
183        {
184            case SUPPORTED_LPAGES:
185                if (! ((NO_SUBPAGE_L_SPAGE == sub_pg_num) ||
186                       (SUPP_SPAGE_L_SPAGE == sub_pg_num))) {
187                    if (scsi_debugmode > 1)
188                        pout("%s: Strange Log page number: 0x0,0x%x\n",
189                             __func__, sub_pg_num);
190                }
191                break;
192            case READ_ERROR_COUNTER_LPAGE:
193                gReadECounterLPage = true;
194                break;
195            case WRITE_ERROR_COUNTER_LPAGE:
196                gWriteECounterLPage = true;
197                break;
198            case VERIFY_ERROR_COUNTER_LPAGE:
199                gVerifyECounterLPage = true;
200                break;
201            case LAST_N_ERROR_EVENTS_LPAGE:
202                gLastNErrorEvLPage = true;
203                break;
204            case NON_MEDIUM_ERROR_LPAGE:
205                gNonMediumELPage = true;
206                break;
207            case TEMPERATURE_LPAGE:
208                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
209                    gTempLPage = true;
210                else if (ENVIRO_REP_L_SPAGE == sub_pg_num)
211                    gEnviroReportingLPage = true;
212                else if (ENVIRO_LIMITS_L_SPAGE == sub_pg_num)
213                    gEnviroLimitsLPage = true;
214                else {
215                    ++num_unreported;
216                    ++num_unreported_spg;
217                }
218                break;
219            case STARTSTOP_CYCLE_COUNTER_LPAGE:
220                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
221                    gStartStopLPage = true;
222                else if (UTILIZATION_L_SPAGE == sub_pg_num)
223                    gUtilizationLPage = true;
224                else {
225                    ++num_unreported;
226                    ++num_unreported_spg;
227                }
228                break;
229            case SELFTEST_RESULTS_LPAGE:
230                gSelfTestLPage = true;
231                break;
232            case IE_LPAGE:
233                gSmartLPage = true;
234                break;
235            case BACKGROUND_RESULTS_LPAGE:
236                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
237                    gBackgroundResultsLPage = true;
238                else if (PEND_DEFECTS_L_SPAGE == sub_pg_num)
239                    gPendDefectsLPage = true;
240                else if (BACKGROUND_OP_L_SPAGE == sub_pg_num)
241                    gBackgroundOpLPage = true;
242                else if (LPS_MISALIGN_L_SPAGE == sub_pg_num)
243                    gLPSMisalignLPage = true;
244                else {
245                    ++num_unreported;
246                    ++num_unreported_spg;
247                }
248                break;
249            case PROTOCOL_SPECIFIC_LPAGE:
250                gProtocolSpecificLPage = true;
251                break;
252            case TAPE_ALERTS_LPAGE:
253                gTapeAlertsLPage = true;
254                break;
255            case SS_MEDIA_LPAGE:
256                gSSMediaLPage = true;
257                break;
258            case FORMAT_STATUS_LPAGE:
259                gFormatStatusLPage = true;
260                break;
261            case SEAGATE_CACHE_LPAGE:
262                if (failuretest_permissive) {
263                    gSeagateCacheLPage = true;
264                    break;
265                }
266                if (seagate_or_hitachi())
267                    gSeagateCacheLPage = true;
268                break;
269            case SEAGATE_FACTORY_LPAGE:
270                if (failuretest_permissive) {
271                    gSeagateFactoryLPage = true;
272                    break;
273                }
274                if (seagate_or_hitachi())
275                    gSeagateFactoryLPage = true;
276                break;
277            default:
278                if (pg_num < 0x30) {     /* don't count VS pages */
279                    ++num_unreported;
280                    if (sub_pg_num > 0)
281                        ++num_unreported_spg;
282                }
283                break;
284        }
285    }
286    if (scsi_debugmode > 1)
287        pout("%s: number of unreported (standard) log pages: %d (sub-pages: "
288             "%d)\n", __func__, num_unreported, num_unreported_spg);
289}
290
291/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
292   (or at least something to report). */
293static int
294scsiGetSmartData(scsi_device * device, bool attribs)
295{
296    uint8_t asc;
297    uint8_t ascq;
298    uint8_t currenttemp = 255;
299    uint8_t triptemp = 255;
300    const char * cp;
301    int err = 0;
302    print_on();
303    if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
304                    &currenttemp, &triptemp)) {
305        /* error message already announced */
306        print_off();
307        return -1;
308    }
309    print_off();
310    cp = scsiGetIEString(asc, ascq);
311    if (cp) {
312        err = -2;
313        print_on();
314        jout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
315        print_off();
316        jglb["smart_status"]["passed"] = false;
317        jglb["smart_status"]["scsi"]["asc"] = asc;
318        jglb["smart_status"]["scsi"]["ascq"] = ascq;
319        jglb["smart_status"]["scsi"]["ie_string"] = cp;
320    }
321    else if (gIecMPage) {
322        jout("SMART Health Status: OK\n");
323        jglb["smart_status"]["passed"] = true;
324    }
325
326    if (attribs && !gTempLPage) {
327        if (255 == currenttemp)
328            pout("Current Drive Temperature:     <not available>\n");
329        else {
330            jout("Current Drive Temperature:     %d C\n", currenttemp);
331            jglb["temperature"]["current"] = currenttemp;
332        }
333        if (255 == triptemp)
334            pout("Drive Trip Temperature:        <not available>\n");
335        else
336        {
337            pout("Drive Trip Temperature:        %d C\n", triptemp);
338            jglb["temperature"]["drive_trip"] = triptemp;
339        }
340    }
341    pout("\n");
342    return err;
343}
344
345
346// Returns number of logged errors or zero if none or -1 if fetching
347// TapeAlerts fails
348static const char * const severities = "CWI";
349
350static int
351scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
352{
353    unsigned short pagelength;
354    unsigned short parametercode;
355    int i, err;
356    const char *s;
357    const char *ts;
358    int failures = 0;
359
360    print_on();
361    if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
362                        LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
363        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
364        print_off();
365        return -1;
366    }
367    if (gBuf[0] != 0x2e) {
368        pout("TapeAlerts %s Failed\n", logSenStr);
369        print_off();
370        return -1;
371    }
372    pagelength = sg_get_unaligned_be16(gBuf + 2);
373
374    for (s=severities; *s; s++) {
375        for (i = 4; i < pagelength; i += 5) {
376            parametercode = sg_get_unaligned_be16(gBuf + i);
377
378            if (gBuf[i + 4]) {
379                ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
380                    scsiTapeAlertsChangerDevice(parametercode) :
381                    scsiTapeAlertsTapeDevice(parametercode);
382                if (*ts == *s) {
383                    if (!failures)
384                        pout("TapeAlert Errors (C=Critical, W=Warning, "
385                             "I=Informational):\n");
386                    pout("[0x%02x] %s\n", parametercode, ts);
387                    failures += 1;
388                }
389            }
390        }
391    }
392    print_off();
393
394    if (! failures)
395        pout("TapeAlert: OK\n");
396
397    return failures;
398}
399
400static void
401scsiGetStartStopData(scsi_device * device)
402{
403    int err, len, k, extra;
404    unsigned char * ucp;
405
406    if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
407                            LOG_RESP_LEN, 0))) {
408        print_on();
409        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
410        print_off();
411        return;
412    }
413    if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
414        print_on();
415        pout("StartStop %s Failed, page mismatch\n", logSenStr);
416        print_off();
417        return;
418    }
419    len = sg_get_unaligned_be16(gBuf + 2);
420    ucp = gBuf + 4;
421    for (k = len; k > 0; k -= extra, ucp += extra) {
422        if (k < 3) {
423            print_on();
424            pout("StartStop %s: short\n", logSenRspStr);
425            print_off();
426            return;
427        }
428        extra = ucp[3] + 4;
429        int pc = sg_get_unaligned_be16(ucp + 0);
430        uint32_t u = (extra > 7) ? sg_get_unaligned_be32(ucp + 4) : 0;
431        bool is_all_ffs = (extra > 7) ? all_ffs(ucp + 4, 4) : false;
432        switch (pc) {
433        case 1:
434            if (10 == extra)
435                pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
436                     ucp + 4);
437            break;
438        case 2:
439            /* ignore Accounting date */
440            break;
441        case 3:
442            if ((extra > 7) && (! is_all_ffs))
443                pout("Specified cycle count over device lifetime:  %u\n", u);
444            break;
445        case 4:
446            if ((extra > 7) && (! is_all_ffs))
447                pout("Accumulated start-stop cycles:  %u\n", u);
448            break;
449        case 5:
450            if ((extra > 7) && (! is_all_ffs))
451                pout("Specified load-unload count over device lifetime:  "
452                     "%u\n", u);
453            break;
454        case 6:
455            if ((extra > 7) && (! is_all_ffs))
456                pout("Accumulated load-unload cycles:  %u\n", u);
457            break;
458        default:
459            /* ignore */
460            break;
461        }
462    }
463}
464/* PENDING_DEFECTS_SUBPG [0x15,0x1]  introduced: SBC-4 */
465static void
466scsiPrintPendingDefectsLPage(scsi_device * device)
467{
468    int num, pl, pc, err;
469    uint32_t count;
470    const uint8_t * bp;
471    static const char * pDefStr = "Pending Defects";
472    static const char * jname = "pending_defects";
473
474    if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE,
475                            PEND_DEFECTS_L_SPAGE, gBuf, LOG_RESP_LONG_LEN,
476                            0))) {
477        print_on();
478        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
479        print_off();
480        return;
481    }
482    if (((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) &&
483        (gBuf[1] != PEND_DEFECTS_L_SPAGE)) {
484        print_on();
485        pout("%s %s, page mismatch\n", pDefStr, logSenRspStr);
486        print_off();
487        return;
488    }
489    num = sg_get_unaligned_be16(gBuf + 2);
490    if (num > LOG_RESP_LONG_LEN) {
491        print_on();
492        pout("%s %s too long\n", pDefStr, logSenRspStr);
493        print_off();
494        return;
495    }
496    bp = gBuf + 4;
497    while (num > 3) {
498        pc = sg_get_unaligned_be16(bp + 0);
499        pl = bp[3] + 4;
500        switch (pc) {
501        case 0x0:
502            printf("  Pending defect count:");
503            if ((pl < 8) || (num < 8)) {
504                print_on();
505                pout("%s truncated descriptor\n", pDefStr);
506                print_off();
507                return;
508            }
509            count = sg_get_unaligned_be32(bp + 4);
510            jglb[jname]["count"] = count;
511            if (0 == count)
512                jout("0 %s\n", pDefStr);
513            else if (1 == count)
514                jout("1 Pending Defect, LBA and accumulated_power_on_hours "
515                     "follow\n");
516            else
517                jout("%u %s: index, LBA and accumulated_power_on_hours "
518                     "follow\n", count, pDefStr);
519            break;
520        default:
521            if ((pl < 16) || (num < 16)) {
522                print_on();
523                pout("%s truncated descriptor\n", pDefStr);
524                print_off();
525                return;
526            }
527            jout("  %4d:  0x%-16" PRIx64 ",  %5u\n", pc,
528                 sg_get_unaligned_be64(bp + 8), sg_get_unaligned_be32(bp + 4));
529            jglb[jname][pc]["LBA"] = sg_get_unaligned_be64(bp + 8);
530            jglb[jname][pc]["accum_power_on_hours"] =
531                   sg_get_unaligned_be32(bp + 4);
532            break;
533        }
534        num -= pl;
535        bp += pl;
536    }
537}
538
539static void
540scsiPrintGrownDefectListLen(scsi_device * device)
541{
542    bool got_rd12;
543    int err, dl_format;
544    unsigned int dl_len, div;
545    static const char * hname = "Read defect list";
546
547    memset(gBuf, 0, 8);
548    if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
549                                4 /* format: bytes from index */,
550                                0 /* addr desc index */, gBuf, 8))) {
551        if (2 == err) { /* command not supported */
552            err = scsiReadDefect10(device, 0 /* req_plist */,
553                                   1 /* req_glist */,
554                                   4 /* format: bytes from index */, gBuf, 4);
555            if (err) {
556                if (scsi_debugmode > 0) {
557                    print_on();
558                    pout("%s (10) Failed: %s\n", hname, scsiErrString(err));
559                    print_off();
560                }
561                return;
562            } else
563                got_rd12 = 0;
564        } else if (101 == err)    /* Defect list not found, leave quietly */
565            return;
566        else {
567            if (scsi_debugmode > 0) {
568                print_on();
569                pout("%s (12) Failed: %s\n", hname, scsiErrString(err));
570                print_off();
571            }
572            return;
573        }
574    } else
575        got_rd12 = true;
576
577    if (got_rd12) {
578        int generation = sg_get_unaligned_be16(gBuf + 2);
579        if ((generation > 1) && (scsi_debugmode > 0)) {
580            print_on();
581            pout("%s (12): generation=%d\n", hname, generation);
582            print_off();
583        }
584        dl_len = sg_get_unaligned_be32(gBuf + 4);
585    } else
586        dl_len = sg_get_unaligned_be16(gBuf + 2);
587    if (0x8 != (gBuf[1] & 0x18)) {
588        print_on();
589        pout("%s: asked for grown list but didn't get it\n", hname);
590        print_off();
591        return;
592    }
593    div = 0;
594    dl_format = (gBuf[1] & 0x7);
595    switch (dl_format) {
596        case 0:     /* short block */
597            div = 4;
598            break;
599        case 1:     /* extended bytes from index */
600        case 2:     /* extended physical sector */
601            /* extended = 1; # might use in future */
602            div = 8;
603            break;
604        case 3:     /* long block */
605        case 4:     /* bytes from index */
606        case 5:     /* physical sector */
607            div = 8;
608            break;
609        default:
610            print_on();
611            pout("defect list format %d unknown\n", dl_format);
612            print_off();
613            break;
614    }
615    if (0 == dl_len)
616    {
617        pout("Elements in grown defect list: 0\n\n");
618        jglb["elements_grown_list"] = 0;
619    }
620    else {
621        if (0 == div)
622            pout("Grown defect list length=%u bytes [unknown "
623                 "number of elements]\n\n", dl_len);
624        else
625        {
626            pout("Elements in grown defect list: %u\n\n", dl_len / div);
627            jglb["elements_grown_list"] = dl_len;
628        }
629    }
630}
631
632static void
633scsiPrintSeagateCacheLPage(scsi_device * device)
634{
635    int num, pl, pc, err, len;
636    unsigned char * ucp;
637    uint64_t ull;
638    static const char * seaCacStr = "Seagate Cache";
639
640    if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
641                            LOG_RESP_LEN, 0))) {
642        if (scsi_debugmode > 0) {
643            print_on();
644            pout("%s %s Failed: %s\n", seaCacStr, logSenStr,
645                 scsiErrString(err));
646            print_off();
647        }
648        return;
649    }
650    if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
651        if (scsi_debugmode > 0) {
652            print_on();
653            pout("%s %s, page mismatch\n", seaCacStr, logSenRspStr);
654            print_off();
655        }
656        return;
657    }
658    len = sg_get_unaligned_be16(gBuf + 2) + 4;
659    num = len - 4;
660    ucp = &gBuf[0] + 4;
661    while (num > 3) {
662        pc = sg_get_unaligned_be16(ucp + 0);
663        pl = ucp[3] + 4;
664        switch (pc) {
665        case 0: case 1: case 2: case 3: case 4:
666            break;
667        default:
668            if (scsi_debugmode > 0) {
669                print_on();
670                pout("Vendor (%s) lpage has unexpected parameter, skip\n",
671                     seaCacStr);
672                print_off();
673            }
674            return;
675        }
676        num -= pl;
677        ucp += pl;
678    }
679    pout("Vendor (%s) information\n", seaCacStr);
680    num = len - 4;
681    ucp = &gBuf[0] + 4;
682    while (num > 3) {
683        pc = sg_get_unaligned_be16(ucp + 0);
684        pl = ucp[3] + 4;
685        switch (pc) {
686        case 0: pout("  Blocks sent to initiator"); break;
687        case 1: pout("  Blocks received from initiator"); break;
688        case 2: pout("  Blocks read from cache and sent to initiator"); break;
689        case 3: pout("  Number of read and write commands whose size "
690                       "<= segment size"); break;
691        case 4: pout("  Number of read and write commands whose size "
692                       "> segment size"); break;
693        default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
694        }
695        int k = pl - 4;
696        const int sz_ull = (int)sizeof(ull);
697        unsigned char * xp = ucp + 4;
698        if (k > sz_ull) {
699            xp += (k - sz_ull);
700            k = sz_ull;
701        }
702        ull = sg_get_unaligned_be(k, xp + 0);
703        pout(" = %" PRIu64 "\n", ull);
704        num -= pl;
705        ucp += pl;
706    }
707    pout("\n");
708}
709
710static void
711scsiPrintSeagateFactoryLPage(scsi_device * device)
712{
713    int num, pl, pc, len, err, good, bad;
714    unsigned char * ucp;
715    uint64_t ull;
716    static const char * jname = "format_status";
717
718    if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
719                            LOG_RESP_LEN, 0))) {
720        if (scsi_debugmode > 0) {
721            print_on();
722            pout("%s Failed [%s]\n", __func__, scsiErrString(err));
723            print_off();
724        }
725        return;
726    }
727    if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
728        if (scsi_debugmode > 0) {
729            print_on();
730            pout("Seagate/Hitachi Factory %s, page mismatch\n", logSenRspStr);
731            print_off();
732        }
733        return;
734    }
735    len = sg_get_unaligned_be16(gBuf + 2) + 4;
736    num = len - 4;
737    ucp = &gBuf[0] + 4;
738    good = 0;
739    bad = 0;
740    while (num > 3) {
741        pc = sg_get_unaligned_be16(ucp + 0);
742        pl = ucp[3] + 4;
743        switch (pc) {
744        case 0: case 8:
745            ++good;
746            break;
747        default:
748            ++bad;
749            break;
750        }
751        num -= pl;
752        ucp += pl;
753    }
754    if ((good < 2) || (bad > 4)) {  /* heuristic */
755        if (scsi_debugmode > 0) {
756            print_on();
757            pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
758                 "unexpected parameters, skip\n");
759            print_off();
760        }
761        return;
762    }
763    pout("Vendor (Seagate/Hitachi) factory information\n");
764    num = len - 4;
765    ucp = &gBuf[0] + 4;
766    while (num > 3) {
767        pc = sg_get_unaligned_be16(ucp + 0);
768        pl = ucp[3] + 4;
769        good = 0;
770        switch (pc) {
771        case 0: pout("  number of hours powered up");
772            jname = "hours_powered_up";
773            good = 1;
774            break;
775        case 8: pout("  number of minutes until next internal SMART test");
776            jname = "minutes_next_smart_test";
777            good = 1;
778            break;
779        default:
780            if (scsi_debugmode > 0) {
781                print_on();
782                pout("Vendor (Seagate/Hitachi) factory lpage: "
783                     "unknown parameter code [0x%x]\n", pc);
784                print_off();
785            }
786            break;
787        }
788        if (good) {
789            int k = pl - 4;
790            unsigned char * xp = ucp + 4;
791            if (k > (int)sizeof(ull)) {
792                xp += (k - (int)sizeof(ull));
793                k = (int)sizeof(ull);
794            }
795            ull = sg_get_unaligned_be(k, xp + 0);
796            if (0 == pc)
797            {
798                pout(" = %.2f\n", ull / 60.0 );
799                jglb[jname] = strprintf("%.2f", ull / 60.0);
800            }
801            else
802            {
803                pout(" = %" PRIu64 "\n", ull);
804                jglb[jname] = ull;
805            }
806        }
807        num -= pl;
808        ucp += pl;
809    }
810    pout("\n");
811}
812
813static void
814scsiPrintErrorCounterLog(scsi_device * device)
815{
816    struct scsiErrorCounter errCounterArr[3];
817    struct scsiErrorCounter * ecp;
818    int found[3] = {0, 0, 0};
819
820    if (gReadECounterLPage && (0 == scsiLogSense(device,
821                READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
822        scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
823        found[0] = 1;
824    }
825    if (gWriteECounterLPage && (0 == scsiLogSense(device,
826                WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
827        scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
828        found[1] = 1;
829    }
830    if (gVerifyECounterLPage && (0 == scsiLogSense(device,
831                VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
832        scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
833        ecp = &errCounterArr[2];
834        for (int k = 0; k < 7; ++k) {
835            if (ecp->gotPC[k] && ecp->counter[k]) {
836                found[2] = 1;
837                break;
838            }
839        }
840    }
841    if (found[0] || found[1] || found[2]) {
842        pout("Error counter log:\n");
843        pout("           Errors Corrected by           Total   "
844             "Correction     Gigabytes    Total\n");
845        pout("               ECC          rereads/    errors   "
846             "algorithm      processed    uncorrected\n");
847        pout("           fast | delayed   rewrites  corrected  "
848             "invocations   [10^9 bytes]  errors\n");
849
850        for (int k = 0; k < 3; ++k) {
851            if (! found[k])
852                continue;
853            ecp = &errCounterArr[k];
854            static const char * const pageNames[3] =
855                                 {"read:   ", "write:  ", "verify: "};
856            static const char * jpageNames[3] =
857                                 {"read", "write", "verify"};
858            pout("%s%8" PRIu64 " %8" PRIu64 "  %8" PRIu64 "  %8" PRIu64
859                 "   %8" PRIu64, pageNames[k], ecp->counter[0],
860                 ecp->counter[1], ecp->counter[2], ecp->counter[3],
861                 ecp->counter[4]);
862            double processed_gb = ecp->counter[5] / 1000000000.0;
863            pout("   %12.3f    %8" PRIu64 "\n", processed_gb,
864                 ecp->counter[6]);
865            // Error counter log info
866            jglb["error_counter_log"][jpageNames[k]]["errors_corrected_by_eccfast"] = ecp->counter[0];
867            jglb["error_counter_log"][jpageNames[k]]["errors_corrected_by_eccdelayed"] = ecp->counter[1];
868            jglb["error_counter_log"][jpageNames[k]]["errors_corrected_by_rereads_rewrites"] = ecp->counter[2];
869            jglb["error_counter_log"][jpageNames[k]]["total_errors_corrected"] = ecp->counter[3];
870            jglb["error_counter_log"][jpageNames[k]]["correction_algorithm_invocations"] = ecp->counter[4];
871            jglb["error_counter_log"][jpageNames[k]]["gigabytes_processed"] = strprintf("%.3f", processed_gb);
872            jglb["error_counter_log"][jpageNames[k]]["total_uncorrected_errors"] = ecp->counter[6];
873        }
874    }
875    else
876        pout("Error Counter logging not supported\n");
877    if (gNonMediumELPage && (0 == scsiLogSense(device,
878                NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
879        struct scsiNonMediumError nme;
880        scsiDecodeNonMediumErrPage(gBuf, &nme);
881        if (nme.gotPC0)
882            pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0);
883        if (nme.gotTFE_H)
884            pout("Track following error count [Hitachi]: %8" PRIu64 "\n",
885                 nme.counterTFE_H);
886        if (nme.gotPE_H)
887            pout("Positioning error count [Hitachi]: %8" PRIu64 "\n",
888                 nme.counterPE_H);
889    }
890    if (gLastNErrorEvLPage &&
891        (0 == scsiLogSense(device, LAST_N_ERROR_EVENTS_LPAGE, 0, gBuf,
892                           LOG_RESP_LONG_LEN, 0))) {
893        int num = sg_get_unaligned_be16(gBuf + 2) + 4;
894        int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
895        if (truncated)
896            num = LOG_RESP_LONG_LEN;
897        unsigned char * ucp = gBuf + 4;
898        num -= 4;
899        if (num < 4)
900            pout("\nNo error events logged\n");
901        else {
902            pout("\nLast n error events log page\n");
903            for (int k = num, pl; k > 0; k -= pl, ucp += pl) {
904                if (k < 3) {
905                    pout("  <<short Last n error events log page>>\n");
906                    break;
907                }
908                pl = ucp[3] + 4;
909                int pc = sg_get_unaligned_be16(ucp + 0);
910                if (pl > 4) {
911                    if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
912                        pout("  Error event %d:\n", pc);
913                        pout("    [binary]:\n");
914                        dStrHex((const uint8_t *)ucp + 4, pl - 4, 1);
915                    } else if (ucp[2] & 0x1) {
916                        pout("  Error event %d:\n", pc);
917                        pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
918                    } else {
919                        if (scsi_debugmode > 0) {
920                            pout("  Error event %d:\n", pc);
921                            pout("    [data counter??]:\n");
922                            dStrHex((const uint8_t *)ucp + 4, pl - 4, 1);
923                        }
924                    }
925                }
926            }
927            if (truncated)
928                pout(" >>>> log truncated, fetched %d of %d available "
929                     "bytes\n", LOG_RESP_LONG_LEN, truncated);
930        }
931    }
932    pout("\n");
933}
934
935static const char * self_test_code[] = {
936        "Default         ",
937        "Background short",
938        "Background long ",
939        "Reserved(3)     ",
940        "Abort background",
941        "Foreground short",
942        "Foreground long ",
943        "Reserved(7)     "
944};
945
946static const char * self_test_result[] = {
947        "Completed                ",
948        "Aborted (by user command)",
949        "Aborted (device reset ?) ",
950        "Unknown error, incomplete",
951        "Completed, segment failed",
952        "Failed in first segment  ",
953        "Failed in second segment ",
954        "Failed in segment -->    ",
955        "Reserved(8)              ",
956        "Reserved(9)              ",
957        "Reserved(10)             ",
958        "Reserved(11)             ",
959        "Reserved(12)             ",
960        "Reserved(13)             ",
961        "Reserved(14)             ",
962        "Self test in progress ..."
963};
964
965// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
966// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
967// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
968// FAILSMART is returned.
969static int
970scsiPrintSelfTest(scsi_device * device)
971{
972    int num, k, err, durationSec;
973    int noheader = 1;
974    int retval = 0;
975    uint8_t * ucp;
976    uint64_t ull;
977    struct scsi_sense_disect sense_info;
978    static const char * hname = "Self-test";
979
980    // check if test is running
981    if (!scsiRequestSense(device, &sense_info) &&
982                        (sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
983                        sense_info.progress != -1)) {
984        pout("%s execution status:\t\t%d%% of test remaining\n", hname,
985             100 - ((sense_info.progress * 100) / 65535));
986    }
987
988    if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
989                            LOG_RESP_SELF_TEST_LEN, 0))) {
990        print_on();
991        pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
992        print_off();
993        return FAILSMART;
994    }
995    if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
996        print_on();
997        pout("%s %s, page mismatch\n", hname, logSenRspStr);
998        print_off();
999        return FAILSMART;
1000    }
1001    // compute page length
1002    num = sg_get_unaligned_be16(gBuf + 2);
1003    // Log sense page length 0x190 bytes
1004    if (num != 0x190) {
1005        print_on();
1006        pout("%s %s length is 0x%x not 0x190 bytes\n", hname, logSenStr, num);
1007        print_off();
1008        return FAILSMART;
1009    }
1010    // loop through the twenty possible entries
1011    for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
1012        // timestamp in power-on hours (or zero if test in progress)
1013        int n = sg_get_unaligned_be16(ucp + 6);
1014
1015        // The spec says "all 20 bytes will be zero if no test" but
1016        // DG has found otherwise.  So this is a heuristic.
1017        if ((0 == n) && (0 == ucp[4]))
1018            break;
1019
1020        // only print header if needed
1021        if (noheader) {
1022            pout("SMART %s log\n", hname);
1023            pout("Num  Test              Status                 segment  "
1024                   "LifeTime  LBA_first_err [SK ASC ASQ]\n");
1025            pout("     Description                              number   "
1026                   "(hours)\n");
1027            noheader=0;
1028        }
1029
1030        // print parameter code (test number) & self-test code text
1031        pout("#%2d  %s", sg_get_unaligned_be16(ucp + 0),
1032            self_test_code[(ucp[4] >> 5) & 0x7]);
1033
1034        // check the self-test result nibble, using the self-test results
1035        // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
1036        int res;
1037        switch ((res = ucp[4] & 0xf)) {
1038        case 0x3:
1039            // an unknown error occurred while the device server
1040            // was processing the self-test and the device server
1041            // was unable to complete the self-test
1042            retval|=FAILSMART;
1043            break;
1044        case 0x4:
1045            // the self-test completed with a failure in a test
1046            // segment, and the test segment that failed is not
1047            // known
1048            retval|=FAILLOG;
1049            break;
1050        case 0x5:
1051            // the first segment of the self-test failed
1052            retval|=FAILLOG;
1053            break;
1054        case 0x6:
1055            // the second segment of the self-test failed
1056            retval|=FAILLOG;
1057            break;
1058        case 0x7:
1059            // another segment of the self-test failed and which
1060            // test is indicated by the contents of the SELF-TEST
1061            // NUMBER field
1062            retval|=FAILLOG;
1063            break;
1064        default:
1065            break;
1066        }
1067        pout("  %s", self_test_result[res]);
1068
1069        // self-test number identifies test that failed and consists
1070        // of either the number of the segment that failed during
1071        // the test, or the number of the test that failed and the
1072        // number of the segment in which the test was run, using a
1073        // vendor-specific method of putting both numbers into a
1074        // single byte.
1075        if (ucp[5])
1076            pout(" %3d",  (int)ucp[5]);
1077        else
1078            pout("   -");
1079
1080        // print time that the self-test was completed
1081        if (n==0 && res==0xf)
1082        // self-test in progress
1083            pout("     NOW");
1084        else
1085            pout("   %5d", n);
1086
1087        // construct 8-byte integer address of first failure
1088        ull = sg_get_unaligned_be64(ucp + 8);
1089        bool is_all_ffs = all_ffs(ucp + 8, 8);
1090        // print Address of First Failure, if sensible
1091        if ((! is_all_ffs) && (res > 0) && (res < 0xf)) {
1092            char buff[32];
1093
1094            // was hex but change to decimal to conform with ATA
1095            snprintf(buff, sizeof(buff), "%" PRIu64, ull);
1096            // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull);
1097            pout("%18s", buff);
1098        } else
1099            pout("                 -");
1100
1101        // if sense key nonzero, then print it, along with
1102        // additional sense code and additional sense code qualifier
1103        if (ucp[16] & 0xf)
1104            pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
1105        else
1106            pout(" [-   -    -]\n");
1107    }
1108
1109    // if header never printed, then there was no output
1110    if (noheader)
1111        pout("No %ss have been logged\n", hname);
1112    else
1113    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
1114                        modese_len)) && (durationSec > 0)) {
1115        pout("\nLong (extended) %s duration: %d seconds "
1116             "[%.1f minutes]\n", hname, durationSec, durationSec / 60.0);
1117    }
1118    pout("\n");
1119    return retval;
1120}
1121
1122static const char * bms_status[] = {
1123    "no scans active",
1124    "scan is active",
1125    "pre-scan is active",
1126    "halted due to fatal error",
1127    "halted due to a vendor specific pattern of error",
1128    "halted due to medium formatted without P-List",
1129    "halted - vendor specific cause",
1130    "halted due to temperature out of range",
1131    "waiting until BMS interval timer expires", /* 8 */
1132};
1133
1134static const char * reassign_status[] = {
1135    "Reserved [0x0]",
1136    "Require Write or Reassign Blocks command",
1137    "Successfully reassigned",
1138    "Reserved [0x3]",
1139    "Reassignment by disk failed",
1140    "Recovered via rewrite in-place",
1141    "Reassigned by app, has valid data",
1142    "Reassigned by app, has no valid data",
1143    "Unsuccessfully reassigned by app", /* 8 */
1144};
1145
1146// See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
1147// Returns 0 if ok else FAIL* bitmask. Note can have a status entry
1148// and up to 2048 events (although would hope to have less). May set
1149// FAILLOG if serious errors detected (in the future).
1150static int
1151scsiPrintBackgroundResults(scsi_device * device)
1152{
1153    int num, j, m, err, truncated;
1154    int noheader = 1;
1155    int firstresult = 1;
1156    int retval = 0;
1157    uint8_t * ucp;
1158    static const char * hname = "Background scan results";
1159
1160    if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
1161                            LOG_RESP_LONG_LEN, 0))) {
1162        print_on();
1163        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
1164        print_off();
1165        return FAILSMART;
1166    }
1167    if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
1168        print_on();
1169        pout("%s %s, page mismatch\n", hname, logSenRspStr);
1170        print_off();
1171        return FAILSMART;
1172    }
1173    // compute page length
1174    num = sg_get_unaligned_be16(gBuf + 2) + 4;
1175    if (num < 20) {
1176        print_on();
1177        pout("%s %s length is %d, no scan status\n", hname, logSenStr, num);
1178        print_off();
1179        return FAILSMART;
1180    }
1181    truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
1182    if (truncated)
1183        num = LOG_RESP_LONG_LEN;
1184    ucp = gBuf + 4;
1185    num -= 4;
1186    while (num > 3) {
1187        int pc = sg_get_unaligned_be16(ucp + 0);
1188        // pcb = ucp[2];
1189        int pl = ucp[3] + 4;
1190        switch (pc) {
1191        case 0:
1192            if (noheader) {
1193                noheader = 0;
1194                pout("%s log\n", hname);
1195            }
1196            pout("  Status: ");
1197            if ((pl < 16) || (num < 16)) {
1198                pout("\n");
1199                break;
1200            }
1201            j = ucp[9];
1202            if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
1203                pout("%s\n", bms_status[j]);
1204            else
1205                pout("unknown [0x%x] background scan status value\n", j);
1206            j = sg_get_unaligned_be32(ucp + 4);
1207            pout("    Accumulated power on time, hours:minutes %d:%02d "
1208                 "[%d minutes]\n", (j / 60), (j % 60), j);
1209            jglb["accumulated_power_on_time_minutes"] = j;
1210            pout("    Number of background scans performed: %d,  ",
1211                 sg_get_unaligned_be16(ucp + 10));
1212            jglb["number_sacns_performed"] = sg_get_unaligned_be16(ucp + 10);
1213            pout("scan progress: %.2f%%\n",
1214                 (double)sg_get_unaligned_be16(ucp + 12) * 100.0 / 65536.0);
1215            pout("    Number of background medium scans performed: %d\n",
1216                 sg_get_unaligned_be16(ucp + 14));
1217            jglb["number_medium_scans_performed"] = sg_get_unaligned_be16(ucp + 14);
1218            break;
1219        default:
1220            if (noheader) {
1221                noheader = 0;
1222                pout("\n%s log\n", hname);
1223            }
1224            if (firstresult) {
1225                firstresult = 0;
1226                pout("\n   #  when        lba(hex)    [sk,asc,ascq]    "
1227                     "reassign_status\n");
1228            }
1229            pout(" %3d ", pc);
1230            if ((pl < 24) || (num < 24)) {
1231                if (pl < 24)
1232                    pout("parameter length >= 24 expected, got %d\n", pl);
1233                break;
1234            }
1235            j = sg_get_unaligned_be32(ucp + 4);
1236            pout("%4d:%02d  ", (j / 60), (j % 60));
1237            for (m = 0; m < 8; ++m)
1238                pout("%02x", ucp[16 + m]);
1239            pout("  [%x,%x,%x]   ", ucp[8] & 0xf, ucp[9], ucp[10]);
1240            j = (ucp[8] >> 4) & 0xf;
1241            if (j <
1242                (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
1243                pout("%s\n", reassign_status[j]);
1244            else
1245                pout("Reassign status: reserved [0x%x]\n", j);
1246            break;
1247        }
1248        num -= pl;
1249        ucp += pl;
1250    }
1251    if (truncated)
1252        pout(" >>>> log truncated, fetched %d of %d available "
1253             "bytes\n", LOG_RESP_LONG_LEN, truncated);
1254    pout("\n");
1255    return retval;
1256}
1257
1258// See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 .
1259// Returns 0 if ok else FAIL* bitmask. Note can have a status entry
1260// and up to 2048 events (although would hope to have less). May set
1261// FAILLOG if serious errors detected (in the future).
1262static int
1263scsiPrintSSMedia(scsi_device * device)
1264{
1265    int num, err, truncated;
1266    int retval = 0;
1267    uint8_t * ucp;
1268    static const char * hname = "Solid state media";
1269
1270    if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
1271                            LOG_RESP_LONG_LEN, 0))) {
1272        print_on();
1273        pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
1274        print_off();
1275        return FAILSMART;
1276    }
1277    if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
1278        print_on();
1279        pout("%s %s, page mismatch\n", hname, logSenStr);
1280        print_off();
1281        return FAILSMART;
1282    }
1283    // compute page length
1284    num = sg_get_unaligned_be16(gBuf + 2) + 4;
1285    if (num < 12) {
1286        print_on();
1287        pout("%s %s length is %d, too short\n", hname, logSenStr, num);
1288        print_off();
1289        return FAILSMART;
1290    }
1291    truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
1292    if (truncated)
1293        num = LOG_RESP_LONG_LEN;
1294    ucp = gBuf + 4;
1295    num -= 4;
1296    while (num > 3) {
1297        int pc = sg_get_unaligned_be16(ucp + 0);
1298        // pcb = ucp[2];
1299        int pl = ucp[3] + 4;
1300        switch (pc) {
1301        case 1:
1302            if (pl < 8) {
1303                print_on();
1304                pout("%s Percentage used endurance indicator parameter "
1305                     "too short (pl=%d)\n", hname, pl);
1306                print_off();
1307                return FAILSMART;
1308            }
1309            pout("Percentage used endurance indicator: %d%%\n", ucp[7]);
1310            jglb["percentage_used_endurance_indicator"] = ucp[7];
1311        default:        /* ignore other parameter codes */
1312            break;
1313        }
1314        num -= pl;
1315        ucp += pl;
1316    }
1317    return retval;
1318}
1319
1320static int
1321scsiPrintFormatStatus(scsi_device * device)
1322{
1323    bool is_count;
1324    int k, num, err, truncated;
1325    int retval = 0;
1326    uint64_t ull;
1327    uint8_t * ucp;
1328    uint8_t * xp;
1329    const char * jout_str;
1330    const char * jglb_str;
1331    static const char * hname = "Format Status";
1332    static const char * jname = "format_status";
1333
1334    if ((err = scsiLogSense(device, FORMAT_STATUS_LPAGE, 0, gBuf,
1335                            LOG_RESP_LONG_LEN, 0))) {
1336        print_on();
1337        jout("%s: Failed [%s]\n", __func__, scsiErrString(err));
1338        print_off();
1339        return FAILSMART;
1340    }
1341    if ((gBuf[0] & 0x3f) != FORMAT_STATUS_LPAGE) {
1342        print_on();
1343        jout("%s %s, page mismatch\n", hname, logSenRspStr);
1344        print_off();
1345        return FAILSMART;
1346    }
1347    // compute page length
1348    num = sg_get_unaligned_be16(gBuf + 2) + 4;
1349    if (num < 12) {
1350        print_on();
1351        jout("%s %s length is %d, too short\n", hname, logSenStr, num);
1352        print_off();
1353        return FAILSMART;
1354    }
1355    truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
1356    if (truncated)
1357        num = LOG_RESP_LONG_LEN;
1358    ucp = gBuf + 4;
1359    num -= 4;
1360    while (num > 3) {
1361        int pc = sg_get_unaligned_be16(ucp + 0);
1362        // pcb = ucp[2];
1363        int pl = ucp[3] + 4;
1364
1365        is_count = true;
1366        jout_str = "";
1367        jglb_str = "x";
1368        switch (pc) {
1369        case 0:
1370            if (scsi_debugmode > 1) {
1371                if (pl < 5)
1372                    jout("Format data out: <empty>\n");
1373                else {
1374                    if (all_ffs(ucp + 4, pl - 4))
1375                        jout("Format data out: <not available>\n");
1376                    else {
1377                        jout("Format data out:\n");
1378                        dStrHex((const uint8_t *)ucp + 4, pl - 4, 0);
1379                    }
1380                }
1381            }
1382            is_count = false;
1383            break;
1384        case 1:
1385            jout_str = "Grown defects during certification";
1386            jglb_str = "grown_defects_during_cert";
1387            break;
1388        case 2:
1389            jout_str = "Total blocks reassigned during format";
1390            jglb_str = "blocks_reassigned_during_format";
1391            break;
1392        case 3:
1393            jout_str = "Total new blocks reassigned";
1394            jglb_str = "total_new_block_since_format";
1395            break;
1396        case 4:
1397            jout_str = "Power on minutes since format";
1398            jglb_str = "power_on_minutes_since_format";
1399            break;
1400        default:
1401            if (scsi_debugmode > 3) {
1402                pout("  Unknown Format parameter code = 0x%x\n", pc);
1403                dStrHex((const uint8_t *)ucp, pl, 0);
1404            }
1405            is_count = false;
1406            break;
1407        }
1408        if (is_count) {
1409            k = pl - 4;
1410            xp = ucp + 4;
1411            if (all_ffs(xp, k)) {
1412                jout("%s <not available>\n", jout_str);
1413                jglb[jname][jglb_str] = "not_available";
1414            } else {
1415                if (k > (int)sizeof(ull)) {
1416                    xp += (k - sizeof(ull));
1417                    k = sizeof(ull);
1418                }
1419                ull = sg_get_unaligned_be(k, xp);
1420                jout("%s = %" PRIu64 "\n", jout_str, ull);
1421                jglb[jname][jglb_str] = ull;
1422            }
1423        } else
1424        num -= pl;
1425        ucp += pl;
1426    }
1427    return retval;
1428
1429}
1430
1431static void
1432show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
1433{
1434    unsigned int u;
1435
1436    switch (peis) {
1437    case 0:
1438        pout("     No event\n");
1439        break;
1440    case 0x1:
1441        pout("     Invalid word count: %u\n", val);
1442        break;
1443    case 0x2:
1444        pout("     Running disparity error count: %u\n", val);
1445        break;
1446    case 0x3:
1447        pout("     Loss of dword synchronization count: %u\n", val);
1448        break;
1449    case 0x4:
1450        pout("     Phy reset problem count: %u\n", val);
1451        break;
1452    case 0x5:
1453        pout("     Elasticity buffer overflow count: %u\n", val);
1454        break;
1455    case 0x6:
1456        pout("     Received ERROR  count: %u\n", val);
1457        break;
1458    case 0x20:
1459        pout("     Received address frame error count: %u\n", val);
1460        break;
1461    case 0x21:
1462        pout("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
1463        break;
1464    case 0x22:
1465        pout("     Received abandon-class OPEN_REJECT count: %u\n", val);
1466        break;
1467    case 0x23:
1468        pout("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
1469        break;
1470    case 0x24:
1471        pout("     Received retry-class OPEN_REJECT count: %u\n", val);
1472        break;
1473    case 0x25:
1474        pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
1475        break;
1476    case 0x26:
1477        pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
1478        break;
1479    case 0x27:
1480        pout("     Transmitted BREAK count: %u\n", val);
1481        break;
1482    case 0x28:
1483        pout("     Received BREAK count: %u\n", val);
1484        break;
1485    case 0x29:
1486        pout("     Break timeout count: %u\n", val);
1487        break;
1488    case 0x2a:
1489        pout("     Connection count: %u\n", val);
1490        break;
1491    case 0x2b:
1492        pout("     Peak transmitted pathway blocked count: %u\n",
1493               val & 0xff);
1494        pout("         Peak value detector threshold: %u\n",
1495               thresh_val & 0xff);
1496        break;
1497    case 0x2c:
1498        u = val & 0xffff;
1499        if (u < 0x8000)
1500            pout("     Peak transmitted arbitration wait time (us): "
1501                   "%u\n", u);
1502        else
1503            pout("     Peak transmitted arbitration wait time (ms): "
1504                   "%u\n", 33 + (u - 0x8000));
1505        u = thresh_val & 0xffff;
1506        if (u < 0x8000)
1507            pout("         Peak value detector threshold (us): %u\n",
1508                   u);
1509        else
1510            pout("         Peak value detector threshold (ms): %u\n",
1511                   33 + (u - 0x8000));
1512        break;
1513    case 0x2d:
1514        pout("     Peak arbitration time (us): %u\n", val);
1515        pout("         Peak value detector threshold: %u\n", thresh_val);
1516        break;
1517    case 0x2e:
1518        pout("     Peak connection time (us): %u\n", val);
1519        pout("         Peak value detector threshold: %u\n", thresh_val);
1520        break;
1521    case 0x40:
1522        pout("     Transmitted SSP frame count: %u\n", val);
1523        break;
1524    case 0x41:
1525        pout("     Received SSP frame count: %u\n", val);
1526        break;
1527    case 0x42:
1528        pout("     Transmitted SSP frame error count: %u\n", val);
1529        break;
1530    case 0x43:
1531        pout("     Received SSP frame error count: %u\n", val);
1532        break;
1533    case 0x44:
1534        pout("     Transmitted CREDIT_BLOCKED count: %u\n", val);
1535        break;
1536    case 0x45:
1537        pout("     Received CREDIT_BLOCKED count: %u\n", val);
1538        break;
1539    case 0x50:
1540        pout("     Transmitted SATA frame count: %u\n", val);
1541        break;
1542    case 0x51:
1543        pout("     Received SATA frame count: %u\n", val);
1544        break;
1545    case 0x52:
1546        pout("     SATA flow control buffer overflow count: %u\n", val);
1547        break;
1548    case 0x60:
1549        pout("     Transmitted SMP frame count: %u\n", val);
1550        break;
1551    case 0x61:
1552        pout("     Received SMP frame count: %u\n", val);
1553        break;
1554    case 0x63:
1555        pout("     Received SMP frame error count: %u\n", val);
1556        break;
1557    default:
1558        break;
1559    }
1560}
1561
1562static void
1563show_sas_port_param(unsigned char * ucp, int param_len)
1564{
1565    int j, m, nphys, t, sz, spld_len;
1566    unsigned char * vcp;
1567    char s[64];
1568
1569    sz = sizeof(s);
1570    // pcb = ucp[2];
1571    t = sg_get_unaligned_be16(ucp + 0);
1572    pout("relative target port id = %d\n", t);
1573    pout("  generation code = %d\n", ucp[6]);
1574    nphys = ucp[7];
1575    pout("  number of phys = %d\n", nphys);
1576
1577    for (j = 0, vcp = ucp + 8; j < (param_len - 8);
1578         vcp += spld_len, j += spld_len) {
1579        pout("  phy identifier = %d\n", vcp[1]);
1580        spld_len = vcp[3];
1581        if (spld_len < 44)
1582            spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
1583        else
1584            spld_len += 4;
1585        t = ((0x70 & vcp[4]) >> 4);
1586        switch (t) {
1587        case 0: snprintf(s, sz, "no device attached"); break;
1588        case 1: snprintf(s, sz, "SAS or SATA device"); break;
1589        case 2: snprintf(s, sz, "expander device"); break;
1590        case 3: snprintf(s, sz, "expander device (fanout)"); break;
1591        default: snprintf(s, sz, "reserved [%d]", t); break;
1592        }
1593        pout("    attached device type: %s\n", s);
1594        t = 0xf & vcp[4];
1595        switch (t) {
1596        case 0: snprintf(s, sz, "unknown"); break;
1597        case 1: snprintf(s, sz, "power on"); break;
1598        case 2: snprintf(s, sz, "hard reset"); break;
1599        case 3: snprintf(s, sz, "SMP phy control function"); break;
1600        case 4: snprintf(s, sz, "loss of dword synchronization"); break;
1601        case 5: snprintf(s, sz, "mux mix up"); break;
1602        case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
1603            break;
1604        case 7: snprintf(s, sz, "break timeout timer expired"); break;
1605        case 8: snprintf(s, sz, "phy test function stopped"); break;
1606        case 9: snprintf(s, sz, "expander device reduced functionality");
1607             break;
1608        default: snprintf(s, sz, "reserved [0x%x]", t); break;
1609        }
1610        pout("    attached reason: %s\n", s);
1611        t = (vcp[5] & 0xf0) >> 4;
1612        switch (t) {
1613        case 0: snprintf(s, sz, "unknown"); break;
1614        case 1: snprintf(s, sz, "power on"); break;
1615        case 2: snprintf(s, sz, "hard reset"); break;
1616        case 3: snprintf(s, sz, "SMP phy control function"); break;
1617        case 4: snprintf(s, sz, "loss of dword synchronization"); break;
1618        case 5: snprintf(s, sz, "mux mix up"); break;
1619        case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
1620            break;
1621        case 7: snprintf(s, sz, "break timeout timer expired"); break;
1622        case 8: snprintf(s, sz, "phy test function stopped"); break;
1623        case 9: snprintf(s, sz, "expander device reduced functionality");
1624             break;
1625        default: snprintf(s, sz, "reserved [0x%x]", t); break;
1626        }
1627        pout("    reason: %s\n", s);
1628        t = (0xf & vcp[5]);
1629        switch (t) {
1630        case 0: snprintf(s, sz, "phy enabled; unknown");
1631                     break;
1632        case 1: snprintf(s, sz, "phy disabled"); break;
1633        case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
1634                     break;
1635        case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
1636                     break;
1637        case 4: snprintf(s, sz, "phy enabled; port selector");
1638                     break;
1639        case 5: snprintf(s, sz, "phy enabled; reset in progress");
1640                     break;
1641        case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
1642                     break;
1643        case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
1644        case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
1645        case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
1646        case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break;
1647        default: snprintf(s, sz, "reserved [%d]", t); break;
1648        }
1649        pout("    negotiated logical link rate: %s\n", s);
1650        pout("    attached initiator port: ssp=%d stp=%d smp=%d\n",
1651               !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
1652        pout("    attached target port: ssp=%d stp=%d smp=%d\n",
1653               !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
1654        if (!dont_print_serial_number) {
1655            uint64_t ull = sg_get_unaligned_be64(vcp + 8);
1656
1657            pout("    SAS address = 0x%" PRIx64 "\n", ull);
1658            ull = sg_get_unaligned_be64(vcp + 16);
1659            pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
1660        }
1661        pout("    attached phy identifier = %d\n", vcp[24]);
1662        unsigned int ui = sg_get_unaligned_be32(vcp + 32);
1663
1664        pout("    Invalid DWORD count = %u\n", ui);
1665        ui = sg_get_unaligned_be32(vcp + 36);
1666        pout("    Running disparity error count = %u\n", ui);
1667        ui = sg_get_unaligned_be32(vcp + 40);
1668        pout("    Loss of DWORD synchronization = %u\n", ui);
1669        ui = sg_get_unaligned_be32(vcp + 44);
1670        pout("    Phy reset problem = %u\n", ui);
1671        if (spld_len > 51) {
1672            int num_ped;
1673            unsigned char * xcp;
1674
1675            num_ped = vcp[51];
1676            if (num_ped > 0)
1677               pout("    Phy event descriptors:\n");
1678            xcp = vcp + 52;
1679            for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
1680                int peis;
1681                unsigned int pvdt;
1682                peis = xcp[3];
1683                ui = sg_get_unaligned_be32(xcp + 4);
1684                pvdt = sg_get_unaligned_be32(xcp + 8);
1685                show_sas_phy_event_info(peis, ui, pvdt);
1686            }
1687        }
1688    }
1689}
1690
1691// Returns 1 if okay, 0 if non SAS descriptors
1692static int
1693show_protocol_specific_page(unsigned char * resp, int len)
1694{
1695    int k, num;
1696    unsigned char * ucp;
1697
1698    num = len - 4;
1699    for (k = 0, ucp = resp + 4; k < num; ) {
1700        int param_len = ucp[3] + 4;
1701        if (SCSI_TPROTO_SAS != (0xf & ucp[4]))
1702            return 0;   /* only decode SAS log page */
1703        if (0 == k)
1704            pout("Protocol Specific port log page for SAS SSP\n");
1705        show_sas_port_param(ucp, param_len);
1706        k += param_len;
1707        ucp += param_len;
1708    }
1709    pout("\n");
1710    return 1;
1711}
1712
1713
1714// See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific
1715// log page [0x18]. Returns 0 if ok else FAIL* bitmask.
1716static int
1717scsiPrintSasPhy(scsi_device * device, int reset)
1718{
1719    int num, err;
1720    static const char * hname = "Protocol specific port";
1721
1722    if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
1723                            LOG_RESP_LONG_LEN, 0))) {
1724        print_on();
1725        pout("%s %s Failed [%s]\n\n", __func__, logSenStr,
1726             scsiErrString(err));
1727        print_off();
1728        return FAILSMART;
1729    }
1730    if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
1731        print_on();
1732        pout("%s %s, page mismatch\n\n", hname, logSenRspStr);
1733        print_off();
1734        return FAILSMART;
1735    }
1736    // compute page length
1737    num = sg_get_unaligned_be16(gBuf + 2);
1738    if (1 != show_protocol_specific_page(gBuf, num + 4)) {
1739        print_on();
1740        pout("Only support %s log page on SAS devices\n\n", hname);
1741        print_off();
1742        return FAILSMART;
1743    }
1744    if (reset) {
1745        if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
1746                                 PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
1747            print_on();
1748            pout("%s Log Select (reset) Failed [%s]\n\n", __func__,
1749                 scsiErrString(err));
1750            print_off();
1751            return FAILSMART;
1752        }
1753    }
1754    return 0;
1755}
1756
1757
1758static const char * peripheral_dt_arr[32] = {
1759        "disk",
1760        "tape",
1761        "printer",
1762        "processor",
1763        "optical disk(4)",
1764        "CD/DVD",
1765        "scanner",
1766        "optical disk(7)",
1767        "medium changer",
1768        "communications",
1769        "graphics(10)",
1770        "graphics(11)",
1771        "storage array",
1772        "enclosure",
1773        "simplified disk",
1774        "optical card reader",
1775        "reserved [0x10]",
1776        "object based storage",
1777        "automation/driver interface",
1778        "security manager device",
1779        "host managed zoned block device",
1780        "reserved [0x15]",
1781        "reserved [0x16]",
1782        "reserved [0x17]",
1783        "reserved [0x18]",
1784        "reserved [0x19]",
1785        "reserved [0x1a]",
1786        "reserved [0x1b]",
1787        "reserved [0x1c]",
1788        "reserved [0x1d]",
1789        "well known logical unit",
1790        "unknown or no device type",
1791};
1792
1793/* Symbolic indexes to this array SCSI_TPROTO_* in scscmds.h */
1794static const char * transport_proto_arr[] = {
1795        "Fibre channel (FCP-2)",
1796        "Parallel SCSI (SPI-4)",
1797        "SSA",
1798        "IEEE 1394 (SBP-2)",
1799        "RDMA (SRP)",
1800        "iSCSI",
1801        "SAS (SPL-3)",
1802        "ADT",
1803        "ATA (ACS-2)",
1804        "UAS",
1805        "SOP",
1806        "PCIe",
1807        "0xc",
1808        "0xd",
1809        "0xe",
1810        "None given [0xf]"
1811};
1812
1813/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
1814static int
1815scsiGetDriveInfo(scsi_device * device, uint8_t * peripheral_type, bool all)
1816{
1817    struct scsi_iec_mode_page iec;
1818    int err, iec_err, len, req_len, avail_len;
1819    bool is_tape = false;
1820    int peri_dt = 0;
1821    int transport = -1;
1822    int form_factor = 0;
1823    int haw_zbc = 0;
1824    int protect = 0;
1825
1826    memset(gBuf, 0, 96);
1827    req_len = 36;
1828    if ((err = scsiStdInquiry(device, gBuf, req_len))) {
1829        print_on();
1830        pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
1831        pout("Retrying with a 64 byte Standard Inquiry\n");
1832        print_off();
1833        /* Marvell controllers fail with 36 byte StdInquiry, but 64 is ok */
1834        req_len = 64;
1835        if ((err = scsiStdInquiry(device, gBuf, req_len))) {
1836            print_on();
1837            pout("Standard Inquiry (64 bytes) failed [%s]\n",
1838                 scsiErrString(err));
1839            print_off();
1840            return 1;
1841        }
1842    }
1843    avail_len = gBuf[4] + 5;
1844    len = (avail_len < req_len) ? avail_len : req_len;
1845    peri_dt = gBuf[0] & 0x1f;
1846    *peripheral_type = peri_dt;
1847    if ((SCSI_PT_SEQUENTIAL_ACCESS == peri_dt) ||
1848        (SCSI_PT_MEDIUM_CHANGER == peri_dt))
1849        is_tape = true;
1850
1851    if (len < 36) {
1852        print_on();
1853        pout("Short INQUIRY response, skip product id\n");
1854        print_off();
1855        return 1;
1856    }
1857    // Upper bits of version bytes were used in older standards
1858    // Only interested in SPC-4 (0x6) and SPC-5 (assumed to be 0x7)
1859    scsi_version = gBuf[2] & 0x7;
1860
1861    if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
1862        char product[16+1], revision[4+1];
1863        scsi_format_id_string(scsi_vendor, &gBuf[8], 8);
1864        scsi_format_id_string(product, &gBuf[16], 16);
1865        scsi_format_id_string(revision, &gBuf[32], 4);
1866
1867        pout("=== START OF INFORMATION SECTION ===\n");
1868        jout("Vendor:               %.8s\n", scsi_vendor);
1869        jglb["vendor"] = scsi_vendor;
1870        jout("Product:              %.16s\n", product);
1871        jglb["product"] = product;
1872        jglb["model_name"] = strprintf("%s%s%s",
1873          scsi_vendor, (*scsi_vendor && *product ? " " : ""), product);
1874        if (gBuf[32] >= ' ') {
1875            jout("Revision:             %.4s\n", revision);
1876            // jglb["firmware_version"] = revision;
1877            jglb["revision"] = revision;        /* could be a hardware rev */
1878        }
1879        if ((scsi_version > 0x3) && (scsi_version < 0x8)) {
1880            char sv_arr[8];
1881
1882            snprintf(sv_arr, sizeof(sv_arr), "SPC-%d", scsi_version - 2);
1883            jout("Compliance:           %s\n", sv_arr);
1884            jglb["scsi_version"] = sv_arr;
1885        }
1886    }
1887
1888    if (!*device->get_req_type()/*no type requested*/ &&
1889               (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
1890        pout("\nProbable ATA device behind a SAT layer\n"
1891             "Try an additional '-d ata' or '-d sat' argument.\n");
1892        return 2;
1893    }
1894    if (! all)
1895        return 0;
1896
1897    protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
1898
1899    if (! is_tape) {    /* assume disk if not tape drive (or tape changer) */
1900        struct scsi_readcap_resp srr;
1901        int lbpme = -1;
1902        int lbprz = -1;
1903        unsigned char lb_prov_resp[8];
1904        uint64_t capacity = scsiGetSize(device, false /*avoid_rcap16 */,
1905                                        &srr);
1906
1907        if (capacity) {
1908            char cap_str[64], si_str[64];
1909            format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
1910            format_capacity(si_str, sizeof(si_str), capacity);
1911            jout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
1912            if (srr.lb_size)
1913              jglb["user_capacity"]["blocks"].set_unsafe_uint64(capacity /
1914                                                                srr.lb_size);
1915            jglb["user_capacity"]["bytes"].set_unsafe_uint64(capacity);
1916            jout("Logical block size:   %u bytes\n", srr.lb_size);
1917            jglb["logical_block_size"] = srr.lb_size;
1918            if (protect || srr.lb_p_pb_exp) {
1919                if (srr.lb_p_pb_exp > 0) {
1920                    unsigned pb_size = srr.lb_size * (1 << srr.lb_p_pb_exp);
1921                    jout("Physical block size:  %u bytes\n", pb_size);
1922                    jglb["physical_block_size"] = pb_size;
1923                    if (srr.l_a_lba > 0)  // not common so cut the clutter
1924                        pout("Lowest aligned LBA:   %u\n", srr.l_a_lba);
1925                }
1926                if (srr.prot_type > 0) {
1927                    switch (srr.prot_type) {
1928                    case 1 :
1929                        pout("Formatted with type 1 protection\n");
1930                        break;
1931                    case 2 :
1932                        pout("Formatted with type 2 protection\n");
1933                        break;
1934                    case 3 :
1935                        pout("Formatted with type 3 protection\n");
1936                        break;
1937                    default:
1938                        pout("Formatted with unknown protection type [%d]\n",
1939                             srr.prot_type);
1940                        break;
1941                    }
1942                    unsigned p_i_per_lb = (1 << srr.p_i_exp);
1943                    const unsigned pi_sz = 8;   /* ref-tag(4 bytes),
1944                                                   app-tag(2), tag-mask(2) */
1945
1946                    if (p_i_per_lb > 1)
1947                        pout("%d protection information intervals per "
1948                             "logical block\n", p_i_per_lb);
1949                    pout("%d bytes of protection information per logical "
1950                         "block\n", pi_sz * p_i_per_lb);
1951                }
1952                /* Pick up some LB provisioning info since its available */
1953                lbpme = (int)srr.lbpme;
1954                lbprz = (int)srr.lbprz;
1955            }
1956        }
1957        /* Thin Provisioning VPD page renamed Logical Block Provisioning VPD
1958         * page in sbc3r25; some fields changed their meaning so that the
1959         * new page covered both thin and resource provisioned LUs. */
1960        if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
1961                                lb_prov_resp, sizeof(lb_prov_resp))) {
1962            int prov_type = lb_prov_resp[6] & 0x7;      /* added sbc3r27 */
1963            int vpd_lbprz = ((lb_prov_resp[5]  >> 2) & 0x7);  /* sbc4r07 */
1964
1965            if (-1 == lbprz)
1966                lbprz = vpd_lbprz;
1967            else if ((0 == vpd_lbprz) && (1 == lbprz))
1968                ;  /* vpd_lbprz introduced in sbc3r27, expanded in sbc4r07 */
1969            else
1970                lbprz = vpd_lbprz;
1971            switch (prov_type) {
1972            case 0:
1973                if (lbpme <= 0) {
1974                    pout("LU is fully provisioned");
1975                    if (lbprz)
1976                        pout(" [LBPRZ=%d]\n", lbprz);
1977                    else
1978                        pout("\n");
1979                } else
1980                     pout("LB provisioning type: not reported [LBPME=1, "
1981                          "LBPRZ=%d]\n", lbprz);
1982                break;
1983            case 1:
1984                pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
1985                break;
1986            case 2:
1987                pout("LU is thin provisioned, LBPRZ=%d\n", lbprz);
1988                break;
1989            default:
1990                pout("LU provisioning type reserved [%d], LBPRZ=%d\n",
1991                     prov_type, lbprz);
1992                break;
1993            }
1994        } else if (1 == lbpme) {
1995            if (scsi_debugmode > 0)
1996                pout("rcap_16 sets LBPME but no LB provisioning VPD page\n");
1997            pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
1998        }
1999
2000        int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc);
2001        if (rpm >= 0) {
2002            if (0 == rpm)
2003                ;       // Not reported
2004            else if (1 == rpm)
2005                jout("Rotation Rate:        Solid State Device\n");
2006            else if ((rpm <= 0x400) || (0xffff == rpm))
2007                ;       // Reserved
2008            else
2009                jout("Rotation Rate:        %d rpm\n", rpm);
2010            jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm);
2011        }
2012        if (form_factor > 0) {
2013            const char * cp = NULL;
2014
2015            switch (form_factor) {
2016            case 1:
2017                cp = "5.25";
2018                break;
2019            case 2:
2020                cp = "3.5";
2021                break;
2022            case 3:
2023                cp = "2.5";
2024                break;
2025            case 4:
2026                cp = "1.8";
2027                break;
2028            case 5:
2029                cp = "< 1.8";
2030                break;
2031            }
2032            jglb["form_factor"]["scsi_value"] = form_factor;
2033            if (cp) {
2034                jout("Form Factor:          %s inches\n", cp);
2035                jglb["form_factor"]["name"] = strprintf("%s inches", cp);
2036            }
2037        }
2038        if (haw_zbc > 0)
2039            pout("Host aware zoned block capable\n");
2040    }
2041
2042    /* Do this here to try and detect badly conforming devices (some USB
2043       keys) that will lock up on a InquiryVpd or log sense or ... */
2044    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
2045        if (SIMPLE_ERR_BAD_RESP == iec_err) {
2046            pout(">> Terminate command early due to bad response to IEC "
2047                 "mode page\n");
2048            print_off();
2049            gIecMPage = 0;
2050            return 1;
2051        }
2052    } else
2053        modese_len = iec.modese_len;
2054
2055    if (! dont_print_serial_number) {
2056        if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
2057                                       gBuf, 252))) {
2058            char s[256];
2059
2060            len = gBuf[3];
2061            scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
2062            if (strlen(s) > 0)
2063                pout("Logical Unit id:      %s\n", s);
2064        } else if (scsi_debugmode > 0) {
2065            print_on();
2066            if (SIMPLE_ERR_BAD_RESP == err)
2067                pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
2068            else
2069                pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
2070            print_off();
2071        }
2072        if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
2073                                       gBuf, 252))) {
2074            char serial[256];
2075            len = gBuf[3];
2076
2077            gBuf[4 + len] = '\0';
2078            scsi_format_id_string(serial, &gBuf[4], len);
2079            jout("Serial number:        %s\n", serial);
2080            jglb["serial_number"] = serial;
2081        } else if (scsi_debugmode > 0) {
2082            print_on();
2083            if (SIMPLE_ERR_BAD_RESP == err)
2084                pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
2085            else
2086                pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
2087            print_off();
2088        }
2089    }
2090
2091    // print SCSI peripheral device type
2092    jglb["device_type"]["scsi_value"] = peri_dt;
2093    if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
2094                        sizeof(peripheral_dt_arr[0]))) {
2095        jout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
2096        jglb["device_type"]["name"] = peripheral_dt_arr[peri_dt];
2097    }
2098    else
2099        jout("Device type:          <%d>\n", peri_dt);
2100
2101    // See if transport protocol is known
2102    if (transport < 0)
2103        transport = scsiFetchTransportProtocol(device, modese_len);
2104    if ((transport >= 0) && (transport <= 0xf))
2105        pout("Transport protocol:   %s\n", transport_proto_arr[transport]);
2106
2107    // print current time and date and timezone
2108    time_t now = time(0);
2109    char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now);
2110    jout("Local Time is:        %s\n", timedatetz);
2111    jglb["local_time"]["time_t"] = now;
2112    jglb["local_time"]["asctime"] = timedatetz;
2113
2114    // See if unit accepts SCSI commmands from us
2115    if ((err = scsiTestUnitReady(device))) {
2116        if (SIMPLE_ERR_NOT_READY == err) {
2117            print_on();
2118            if (!is_tape)
2119                pout("device is NOT READY (e.g. spun down, busy)\n");
2120            else
2121                pout("device is NOT READY (e.g. no tape)\n");
2122            print_off();
2123        } else if (SIMPLE_ERR_NO_MEDIUM == err) {
2124            print_on();
2125            if (is_tape)
2126                pout("NO tape present in drive\n");
2127            else
2128                pout("NO MEDIUM present in device\n");
2129            print_off();
2130        } else if (SIMPLE_ERR_BECOMING_READY == err) {
2131            print_on();
2132            pout("device becoming ready (wait)\n");
2133            print_off();
2134        } else {
2135            print_on();
2136            pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
2137            print_off();
2138        }
2139        if (! is_tape) {
2140            int returnval = 0; // TODO: exit with FAILID if failuretest returns
2141
2142            failuretest(MANDATORY_CMD, returnval|=FAILID);
2143        }
2144    }
2145
2146    if (iec_err) {
2147        if (!is_tape) {
2148            print_on();
2149            pout("SMART support is:     Unavailable - device lacks SMART "
2150                 "capability.\n");
2151            if (scsi_debugmode > 0)
2152                pout(" [%s]\n", scsiErrString(iec_err));
2153            print_off();
2154        }
2155        gIecMPage = 0;
2156        return 0;
2157    }
2158
2159    if (!is_tape)
2160        pout("SMART support is:     Available - device has SMART capability.\n"
2161             "SMART support is:     %s\n",
2162             (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
2163    pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
2164                  "Temperature Warning:  Enabled" :
2165                  "Temperature Warning:  Disabled or Not Supported");
2166    return 0;
2167}
2168
2169static int
2170scsiSmartEnable(scsi_device * device)
2171{
2172    struct scsi_iec_mode_page iec;
2173    int err;
2174
2175    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
2176        print_on();
2177        pout("unable to fetch IEC (SMART) mode page [%s]\n",
2178             scsiErrString(err));
2179        print_off();
2180        return 1;
2181    } else
2182        modese_len = iec.modese_len;
2183
2184    if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
2185        print_on();
2186        pout("unable to enable Exception control and warning [%s]\n",
2187             scsiErrString(err));
2188        print_off();
2189        return 1;
2190    }
2191    /* Need to refetch 'iec' since could be modified by previous call */
2192    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
2193        pout("unable to fetch IEC (SMART) mode page [%s]\n",
2194             scsiErrString(err));
2195        return 1;
2196    } else
2197        modese_len = iec.modese_len;
2198
2199    pout("Informational Exceptions (SMART) %s\n",
2200         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
2201    pout("Temperature warning %s\n",
2202         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
2203    return 0;
2204}
2205
2206static int
2207scsiSmartDisable(scsi_device * device)
2208{
2209    struct scsi_iec_mode_page iec;
2210    int err;
2211
2212    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
2213        print_on();
2214        pout("unable to fetch IEC (SMART) mode page [%s]\n",
2215             scsiErrString(err));
2216        print_off();
2217        return 1;
2218    } else
2219        modese_len = iec.modese_len;
2220
2221    if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
2222        print_on();
2223        pout("unable to disable Exception control and warning [%s]\n",
2224             scsiErrString(err));
2225        print_off();
2226        return 1;
2227    }
2228    /* Need to refetch 'iec' since could be modified by previous call */
2229    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
2230        pout("unable to fetch IEC (SMART) mode page [%s]\n",
2231             scsiErrString(err));
2232        return 1;
2233    } else
2234        modese_len = iec.modese_len;
2235
2236    pout("Informational Exceptions (SMART) %s\n",
2237         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
2238    pout("Temperature warning %s\n",
2239         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
2240    return 0;
2241}
2242
2243static void
2244scsiPrintTemp(scsi_device * device)
2245{
2246    uint8_t temp = 255;
2247    uint8_t trip = 255;
2248
2249    if (scsiGetTemp(device, &temp, &trip))
2250        return;
2251
2252    if (255 == temp)
2253        pout("Current Drive Temperature:     <not available>\n");
2254    else {
2255        jout("Current Drive Temperature:     %d C\n", temp);
2256        jglb["temperature"]["current"] = temp;
2257    }
2258    if (255 == trip)
2259        pout("Drive Trip Temperature:        <not available>\n");
2260    else
2261    {
2262        pout("Drive Trip Temperature:        %d C\n", trip);
2263        jglb["temperature"]["drive_trip"] = trip;
2264    }
2265    pout("\n");
2266}
2267
2268/* Main entry point used by smartctl command. Return 0 for success */
2269int
2270scsiPrintMain(scsi_device * device, const scsi_print_options & options)
2271{
2272    int checkedSupportedLogPages = 0;
2273    uint8_t peripheral_type = 0;
2274    int returnval = 0;
2275    int res, durationSec;
2276    struct scsi_sense_disect sense_info;
2277    bool is_disk;
2278    bool is_tape;
2279
2280    bool any_output = options.drive_info;
2281
2282    if (supported_vpd_pages_p) {
2283        delete supported_vpd_pages_p;
2284        supported_vpd_pages_p = NULL;
2285    }
2286    supported_vpd_pages_p = new supported_vpd_pages(device);
2287
2288    res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
2289    if (res) {
2290        if (2 == res)
2291            return 0;
2292        else
2293            failuretest(MANDATORY_CMD, returnval |= FAILID);
2294        any_output = true;
2295    }
2296    is_disk = ((SCSI_PT_DIRECT_ACCESS == peripheral_type) ||
2297               (SCSI_PT_HOST_MANAGED == peripheral_type));
2298    is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
2299               (SCSI_PT_MEDIUM_CHANGER == peripheral_type));
2300
2301    short int wce = -1, rcd = -1;
2302    // Print read look-ahead status for disks
2303    if (options.get_rcd || options.get_wce) {
2304        if (is_disk) {
2305            res = scsiGetSetCache(device, modese_len, &wce, &rcd);
2306            if (options.get_rcd)
2307                pout("Read Cache is:        %s\n",
2308                     res ? "Unavailable" : // error
2309                     rcd ? "Disabled" : "Enabled");
2310            if (options.get_wce)
2311                pout("Writeback Cache is:   %s\n",
2312                     res ? "Unavailable" : // error
2313                     !wce ? "Disabled" : "Enabled");
2314        }
2315    } else
2316        any_output = true;
2317
2318    if (options.drive_info)
2319        pout("\n");
2320
2321    // START OF THE ENABLE/DISABLE SECTION OF THE CODE
2322    if (options.smart_disable           || options.smart_enable ||
2323        options.smart_auto_save_disable || options.smart_auto_save_enable)
2324        pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
2325
2326    if (options.smart_enable) {
2327        if (scsiSmartEnable(device))
2328            failuretest(MANDATORY_CMD, returnval |= FAILSMART);
2329        any_output = true;
2330    }
2331
2332    if (options.smart_disable) {
2333        if (scsiSmartDisable(device))
2334            failuretest(MANDATORY_CMD,returnval |= FAILSMART);
2335        any_output = true;
2336    }
2337
2338    if (options.smart_auto_save_enable) {
2339        if (scsiSetControlGLTSD(device, 0, modese_len)) {
2340            pout("Enable autosave (clear GLTSD bit) failed\n");
2341            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
2342        } else
2343            pout("Autosave enabled (GLTSD bit cleared).\n");
2344        any_output = true;
2345    }
2346
2347    // Enable/Disable write cache
2348    if (options.set_wce && is_disk) {
2349        short int enable = wce = (options.set_wce > 0);
2350
2351        rcd = -1;
2352        if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
2353            pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
2354                 device->get_errmsg());
2355            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
2356        } else
2357            pout("Write cache %sabled\n", (enable ? "en" : "dis"));
2358        any_output = true;
2359    }
2360
2361    // Enable/Disable read cache
2362    if (options.set_rcd && is_disk) {
2363        short int enable =  (options.set_rcd > 0);
2364
2365        rcd = !enable;
2366        wce = -1;
2367        if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
2368            pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
2369                device->get_errmsg());
2370            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
2371        } else
2372            pout("Read cache %sabled\n", (enable ? "en" : "dis"));
2373        any_output = true;
2374    }
2375
2376    if (options.smart_auto_save_disable) {
2377        if (scsiSetControlGLTSD(device, 1, modese_len)) {
2378            pout("Disable autosave (set GLTSD bit) failed\n");
2379            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
2380        } else
2381            pout("Autosave disabled (GLTSD bit set).\n");
2382        any_output = true;
2383    }
2384    if (options.smart_disable           || options.smart_enable ||
2385        options.smart_auto_save_disable || options.smart_auto_save_enable)
2386        pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
2387
2388    // START OF READ-ONLY OPTIONS APART FROM -V and -i
2389    if (options.smart_check_status  || options.smart_ss_media_log ||
2390        options.smart_vendor_attrib || options.smart_error_log ||
2391        options.smart_selftest_log  || options.smart_background_log ||
2392        options.sasphy)
2393        pout("=== START OF READ SMART DATA SECTION ===\n");
2394
2395    if (options.smart_check_status) {
2396        scsiGetSupportedLogPages(device);
2397        checkedSupportedLogPages = 1;
2398        if (is_tape) {
2399            if (gTapeAlertsLPage) {
2400                if (options.drive_info)
2401                    pout("TapeAlert Supported\n");
2402                if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
2403                    failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
2404            }
2405            else
2406                pout("TapeAlert Not Supported\n");
2407        } else { /* disk, cd/dvd, enclosure, etc */
2408            if ((res = scsiGetSmartData(device,
2409                                        options.smart_vendor_attrib))) {
2410                if (-2 == res)
2411                    returnval |= FAILSTATUS;
2412                else
2413                    returnval |= FAILSMART;
2414            }
2415        }
2416        any_output = true;
2417    }
2418
2419    if (is_disk && options.smart_ss_media_log) {
2420        if (! checkedSupportedLogPages)
2421            scsiGetSupportedLogPages(device);
2422        res = 0;
2423        if (gSSMediaLPage)
2424            res = scsiPrintSSMedia(device);
2425        if (0 != res)
2426            failuretest(OPTIONAL_CMD, returnval|=res);
2427        if (gFormatStatusLPage)
2428            res = scsiPrintFormatStatus(device);
2429        if (0 != res)
2430            failuretest(OPTIONAL_CMD, returnval|=res);
2431        any_output = true;
2432    }
2433    if (options.smart_vendor_attrib) {
2434        if (! checkedSupportedLogPages)
2435            scsiGetSupportedLogPages(device);
2436        if (gTempLPage)
2437            scsiPrintTemp(device);
2438        if (gStartStopLPage)
2439            scsiGetStartStopData(device);
2440        if (is_disk) {
2441            scsiPrintGrownDefectListLen(device);
2442            if (gSeagateCacheLPage)
2443                scsiPrintSeagateCacheLPage(device);
2444            if (gSeagateFactoryLPage)
2445                scsiPrintSeagateFactoryLPage(device);
2446        }
2447        any_output = true;
2448    }
2449    if (options.smart_error_log) {
2450        if (! checkedSupportedLogPages)
2451            scsiGetSupportedLogPages(device);
2452        scsiPrintErrorCounterLog(device);
2453        if (gPendDefectsLPage)
2454            scsiPrintPendingDefectsLPage(device);
2455        if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
2456            pout("\n[GLTSD (Global Logging Target Save Disable) set. "
2457                 "Enable Save with '-S on']\n");
2458        any_output = true;
2459    }
2460    if (options.smart_selftest_log) {
2461        if (! checkedSupportedLogPages)
2462            scsiGetSupportedLogPages(device);
2463        res = 0;
2464        if (gSelfTestLPage)
2465            res = scsiPrintSelfTest(device);
2466        else {
2467            pout("Device does not support Self Test logging\n");
2468            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
2469        }
2470        if (0 != res)
2471            failuretest(OPTIONAL_CMD, returnval|=res);
2472        any_output = true;
2473    }
2474    if (options.smart_background_log && is_disk) {
2475        if (! checkedSupportedLogPages)
2476            scsiGetSupportedLogPages(device);
2477        res = 0;
2478        if (gBackgroundResultsLPage)
2479            res = scsiPrintBackgroundResults(device);
2480        else {
2481            pout("Device does not support Background scan results logging\n");
2482            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
2483        }
2484        if (0 != res)
2485            failuretest(OPTIONAL_CMD, returnval|=res);
2486        any_output = true;
2487    }
2488    if (options.smart_default_selftest) {
2489        if (scsiSmartDefaultSelfTest(device))
2490            return returnval | FAILSMART;
2491        pout("Default Self Test Successful\n");
2492        any_output = true;
2493    }
2494    if (options.smart_short_cap_selftest) {
2495        if (scsiSmartShortCapSelfTest(device))
2496            return returnval | FAILSMART;
2497        pout("Short Foreground Self Test Successful\n");
2498        any_output = true;
2499    }
2500    // check if another test is running
2501    if (options.smart_short_selftest || options.smart_extend_selftest) {
2502        if (!scsiRequestSense(device, &sense_info) &&
2503            (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
2504            if (!options.smart_selftest_force) {
2505                pout("Can't start self-test without aborting current test");
2506                if (sense_info.progress != -1)
2507                    pout(" (%d%% remaining)",
2508                         100 - sense_info.progress * 100 / 65535);
2509                pout(",\nadd '-t force' option to override, or run "
2510                     "'smartctl -X' to abort test.\n");
2511                return -1;
2512            } else
2513                scsiSmartSelfTestAbort(device);
2514        }
2515    }
2516    if (options.smart_short_selftest) {
2517        if (scsiSmartShortSelfTest(device))
2518            return returnval | FAILSMART;
2519        pout("Short Background Self Test has begun\n");
2520        pout("Use smartctl -X to abort test\n");
2521        any_output = true;
2522    }
2523    if (options.smart_extend_selftest) {
2524        if (scsiSmartExtendSelfTest(device))
2525            return returnval | FAILSMART;
2526        pout("Extended Background Self Test has begun\n");
2527        if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
2528                        modese_len)) && (durationSec > 0)) {
2529            time_t t = time(NULL);
2530
2531            t += durationSec;
2532            pout("Please wait %d minutes for test to complete.\n",
2533                 durationSec / 60);
2534            pout("Estimated completion time: %s\n", ctime(&t));
2535        }
2536        pout("Use smartctl -X to abort test\n");
2537        any_output = true;
2538    }
2539    if (options.smart_extend_cap_selftest) {
2540        if (scsiSmartExtendCapSelfTest(device))
2541            return returnval | FAILSMART;
2542        pout("Extended Foreground Self Test Successful\n");
2543    }
2544    if (options.smart_selftest_abort) {
2545        if (scsiSmartSelfTestAbort(device))
2546            return returnval | FAILSMART;
2547        pout("Self Test returned without error\n");
2548        any_output = true;
2549    }
2550    if (options.sasphy && gProtocolSpecificLPage) {
2551        if (scsiPrintSasPhy(device, options.sasphy_reset))
2552            return returnval | FAILSMART;
2553        any_output = true;
2554    }
2555
2556    if (!any_output)
2557        pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') "
2558             "to print SMART (and more) information\n\n");
2559
2560    return returnval;
2561}