diff --git a/cmdline/device.c b/cmdline/device.c index 66386551..98363b16 100644 --- a/cmdline/device.c +++ b/cmdline/device.c @@ -313,12 +313,14 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) unsigned j; size_t device_pad; size_t serial_pad; + int have_parent; double array_failure_rate; double p_at_least_one_failure; /* compute lengths for padding */ device_pad = 0; serial_pad = 0; + have_parent = 0; for (i = tommy_list_head(low); i != 0; i = i->next) { size_t len; devinfo_t* devinfo = i->data; @@ -330,19 +332,22 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) len = strlen(devinfo->smart_serial); if (len > serial_pad) serial_pad = len; + + if (devinfo->parent != 0) + have_parent = 1; } printf("SnapRAID SMART report:\n"); printf("\n"); printf(" Temp"); printf(" Power"); - printf(" Error"); + printf(" Error"); printf(" FP"); printf(" Size"); printf("\n"); - printf(" C°"); + printf(" C"); printf(" OnDays"); - printf(" Count"); + printf(" Count"); printf(" "); printf(" TB"); printf(" "); printl("Serial", serial_pad); @@ -356,6 +361,7 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) for (i = tommy_list_head(low); i != 0; i = i->next) { devinfo_t* devinfo = i->data; double afr; + uint64_t flag; if (devinfo->smart[194] != SMART_UNASSIGNED) printf("%7" PRIu64, devinfo->smart[194]); @@ -369,18 +375,39 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) else printf(" -"); - if (devinfo->smart[SMART_ERROR] != SMART_UNASSIGNED) - printf("%6" PRIu64, devinfo->smart[SMART_ERROR]); + if (devinfo->smart[SMART_FLAGS] != SMART_UNASSIGNED) + flag = devinfo->smart[SMART_FLAGS]; else - printf(" -"); + flag = 0; + if (flag & SMARTCTL_FLAG_FAIL) + printf(" FAIL"); + else if (flag & SMARTCTL_FLAG_PREFAIL) + printf(" PREFAIL"); + else if (flag & SMARTCTL_FLAG_PREFAIL_LOGGED) + printf(" PREFAIL"); + else if (devinfo->smart[SMART_ERROR] != SMART_UNASSIGNED + && devinfo->smart[SMART_ERROR] != 0) + printf("%8" PRIu64, devinfo->smart[SMART_ERROR]); + else if (flag & SMARTCTL_FLAG_ERROR) + printf(" ERROR"); + else if (flag & SMARTCTL_FLAG_ERROR_LOGGED) + printf(" ERROR"); + else if (flag & SMARTCTL_FLAG_OPEN) + printf(" n/o"); + else if (flag & SMARTCTL_FLAG_COMMAND) + printf(" n/c"); + else if (devinfo->smart[SMART_ERROR] == 0) + printf(" 0"); + else + printf(" -"); afr = smart_afr(devinfo->smart); /* use only afr of disks in the array */ - if (devinfo->parent != 0) + if (devinfo->parent != 0 || !have_parent) array_failure_rate += afr; - printf("%5.0f%%", poisson_prob_n_or_more_failures(afr, 1) * 100); + printf("%4.0f%%", poisson_prob_n_or_more_failures(afr, 1) * 100); if (devinfo->smart[SMART_SIZE] != SMART_UNASSIGNED) printf(" %2.1f", devinfo->smart[SMART_SIZE] / 1E12); @@ -403,7 +430,10 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) if (*devinfo->name) printf("%s", devinfo->name); else - printf("- (not in stats)"); + printf("-"); + + if (devinfo->parent == 0 && have_parent) + printf(" (not in stats)"); printf("\n"); diff --git a/cmdline/portable.h b/cmdline/portable.h index 26eef4e7..98e1f31f 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -315,13 +315,25 @@ int randomize(void* ptr, size_t size); /** * SMART attributes. */ -#define SMART_COUNT (256+2) +#define SMART_COUNT (256+3) /** * Additional info attributes. */ #define SMART_ERROR 256 /**< ATA Error count. */ #define SMART_SIZE 257 /**< Size in bytes. */ +#define SMART_FLAGS 258 /**< Flags returned by smartctl. */ + +/** + * Flags returned by smartctl. + */ +#define SMARTCTL_FLAG_OPEN (1 << 1) /**< Device open failed. */ +#define SMARTCTL_FLAG_COMMAND (1 << 2) /**< Some SMART or other ATA command to the disk failed. */ +#define SMARTCTL_FLAG_FAIL (1 << 3) /**< SMART status check returned "DISK FAILING". */ +#define SMARTCTL_FLAG_PREFAIL (1 << 4) /**< We found prefail Attributes <= threshold. */ +#define SMARTCTL_FLAG_PREFAIL_LOGGED (1 << 5) /**< SMART status check returned "DISK OK" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past. */ +#define SMARTCTL_FLAG_ERROR (1 << 6) /**< The device error log contains records of errors. */ +#define SMARTCTL_FLAG_ERROR_LOGGED (1 << 7) /**< The device self-test log contains records of errors. */ /** * SMART max attribute length. diff --git a/cmdline/unix.c b/cmdline/unix.c index f12bfdc5..93479e52 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -637,7 +637,7 @@ static int devsmart(dev_t device, uint64_t* smart, char* serial) f = popen(cmd, "r"); if (!f) { /* LCOV_EXCL_START */ - fprintf(stderr, "Failed to run smartctrl -a on device '%u:%u' (from open).\n", major(device), minor(device)); + fprintf(stderr, "Failed to run smartctl -a on device '%u:%u' (from popen).\n", major(device), minor(device)); return -1; /* LCOV_EXCL_STOP */ } @@ -647,7 +647,7 @@ static int devsmart(dev_t device, uint64_t* smart, char* serial) char buf[256]; char attr[64]; unsigned id; - unsigned long long raw; + uint64_t raw; char* s; s = fgets(buf, sizeof(buf), f); @@ -665,7 +665,7 @@ static int devsmart(dev_t device, uint64_t* smart, char* serial) inside = 1; } else if (strncmp(s, "No Errors Logged", 16) == 0) { smart[SMART_ERROR] = 0; - } else if (sscanf(s, "ATA Error Count: %llu", &raw) == 1) { + } else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) { smart[SMART_ERROR] = raw; } else if (sscanf(s, "Serial Number: %63s", serial) == 1) { } else if (sscanf(s, "User Capacity: %63s", attr) == 1) { @@ -677,9 +677,9 @@ static int devsmart(dev_t device, uint64_t* smart, char* serial) } } } else if (inside) { - if (sscanf(s, "%u %*s %*s %*u %*u %*u %*s %*s %*s %llu", &id, &raw) != 2) { + if (sscanf(s, "%u %*s %*s %*u %*u %*u %*s %*s %*s %" SCNu64, &id, &raw) != 2) { /* LCOV_EXCL_START */ - fprintf(stderr, "Invalid smartctrl line '%s'.\n", buf); + fprintf(stderr, "Invalid smartctl line '%s'.\n", buf); return -1; /* LCOV_EXCL_STOP */ } @@ -698,23 +698,27 @@ static int devsmart(dev_t device, uint64_t* smart, char* serial) ret = pclose(f); if (!WIFEXITED(ret)) { /* LCOV_EXCL_START */ - fprintf(stderr, "Failed to run smartctrl -a on device '%u:%u' (not exited).\n", major(device), minor(device)); + fprintf(stderr, "Failed to run smartctl -a on device '%u:%u' (not exited).\n", major(device), minor(device)); return -1; /* LCOV_EXCL_STOP */ } if (WEXITSTATUS(ret) == 127) { /* LCOV_EXCL_START */ - fprintf(stderr, "Failed to run smartctrl -a on device '%u:%u' (from sh).\n", major(device), minor(device)); + fprintf(stderr, "Failed to run smartctl -a on device '%u:%u' (from sh).\n", major(device), minor(device)); return -1; /* LCOV_EXCL_STOP */ } - if (WEXITSTATUS(ret) != 0) { + /* check only first bit, as other bits are used to report other conditions */ + if ((WEXITSTATUS(ret) & 1) != 0) { /* LCOV_EXCL_START */ - fprintf(stderr, "Failed to run smartctrl -a on device '%u:%u' with return code %d.\n", major(device), minor(device), WEXITSTATUS(ret)); + fprintf(stderr, "Failed to run smartctl -a on device '%u:%u' with return code %xh.\n", major(device), minor(device), WEXITSTATUS(ret)); return -1; /* LCOV_EXCL_STOP */ } + /* store the return smartctl return value */ + smart[SMART_FLAGS] = WEXITSTATUS(ret); + return 0; } @@ -750,7 +754,7 @@ static int devdown(dev_t device) } if (WEXITSTATUS(ret) != 0) { /* LCOV_EXCL_START */ - fprintf(stderr, "Failed to run hdparm -y on device '%u:%u' with return code %u.\n", major(device), minor(device), WEXITSTATUS(ret)); + fprintf(stderr, "Failed to run hdparm -y on device '%u:%u' with return code %xh.\n", major(device), minor(device), WEXITSTATUS(ret)); return -1; /* LCOV_EXCL_STOP */ } @@ -861,7 +865,7 @@ static void* thread_spindown(void* arg) } /** - * Thread for spinning down. + * Thread for getting smart info. */ static void* thread_smart(void* arg) {