diff --git a/HISTORY b/HISTORY index cf04c206..2f52a0e0 100644 --- a/HISTORY +++ b/HISTORY @@ -4,7 +4,6 @@ SnapRAID HISTORY 8.0 2015/01 BETA ================ * Adds new "smart" command to print a SMART report of the array. - At now it's available only in Linux. * Adds new "up" and "down" commands to spin up and down the disks of the array. Note that "down" is available only in Linux. * Allows "sync" and "scrub" to continue after the first diff --git a/Makefile.am b/Makefile.am index ec2a5c0a..f78bb288 100644 --- a/Makefile.am +++ b/Makefile.am @@ -804,6 +804,7 @@ distwindows-x86: $(DISTWIN) rm -f $(PACKAGE)-$(VERSION)-windows-x86.zip mkdir tmp cp $(DISTWIN) tmp + cp support/smartctl-nc-x86.exe tmp/smartctl-nc.exe utod < snapraid.conf.example.windows > tmp/snapraid.conf.example utod < INSTALL.windows > tmp/install.txt utod < README > tmp/readme.txt @@ -818,6 +819,7 @@ distwindows-x64: $(DISTWIN) rm -f $(PACKAGE)-$(VERSION)-windows-x64.zip mkdir tmp cp $(DISTWIN) tmp + cp support/smartctl-nc-x64.exe tmp/smartctl-nc.exe utod < snapraid.conf.example.windows > tmp/snapraid.conf.example utod < INSTALL.windows > tmp/install.txt utod < README > tmp/readme.txt diff --git a/cmdline/mingw.c b/cmdline/mingw.c index d2beff32..fbf26ab0 100644 --- a/cmdline/mingw.c +++ b/cmdline/mingw.c @@ -1773,6 +1773,169 @@ int randomize(void* ptr, size_t size) */ pthread_mutex_t io_lock = PTHREAD_MUTEX_INITIALIZER; +/** + * Scan all the devices. + */ +static int devscan(tommy_list* list) +{ + char cmd[128]; + FILE* f; + int ret; + + snprintf(cmd, sizeof(cmd), "smartctl-nc.exe --scan-open"); + + f = popen(cmd, "r"); + if (!f) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl --scan-open (from popen).\n"); + return -1; + /* LCOV_EXCL_STOP */ + } + + while (1) { + char buf[256]; + char* s; + + s = fgets(buf, sizeof(buf), f); + + if (s == 0) + break; + + if (s[0] == '/') { + char* sep = strchr(s, ' '); + if (sep) { + devinfo_t* devinfo; + + /* clear everything after the first space */ + *sep = 0; + + devinfo = calloc_nofail(1, sizeof(devinfo_t)); + + pathcpy(devinfo->file, sizeof(devinfo->file), s); + + /* insert in the list */ + tommy_list_insert_tail(list, &devinfo->node, devinfo); + } + } + } + + ret = pclose(f); + if (ret == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl --scan-open (from pclose).\n"); + return -1; + /* LCOV_EXCL_STOP */ + } + if (ret != 0) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl --scan-open with return code %xh.\n", ret); + return -1; + /* LCOV_EXCL_STOP */ + } + + return 0; +} + +/** + * Gets SMART attributes. + */ +static int devsmart(const char* file, uint64_t* smart, char* serial) +{ + char cmd[128]; + FILE* f; + int inside; + unsigned i; + int ret; + + *serial = 0; + for (i = 0; i < SMART_COUNT; ++i) + smart[i] = SMART_UNASSIGNED; + *serial = 0; + + snprintf(cmd, sizeof(cmd), "smartctl-nc.exe -a %s", file); + + f = popen(cmd, "r"); + if (!f) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl -a on device '%s' (from open).\n", file); + return -1; + /* LCOV_EXCL_STOP */ + } + + inside = 0; + while (1) { + char buf[256]; + char attr[64]; + unsigned id; + uint64_t raw; + char* s; + + s = fgets(buf, sizeof(buf), f); + + if (s == 0) + break; + + /* skip initial spaces */ + while (isspace(*s)) + ++s; + + if (*s == 0) { + inside = 0; + } else if (strncmp(s, "ID#", 3) == 0) { + inside = 1; + } else if (strncmp(s, "No Errors Logged", 16) == 0) { + smart[SMART_ERROR] = 0; + } 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) { + smart[SMART_SIZE] = 0; + for (i = 0; attr[i]; ++i) { + if (isdigit(attr[i])) { + smart[SMART_SIZE] *= 10; + smart[SMART_SIZE] += attr[i] - '0'; + } + } + } else if (inside) { + if (sscanf(s, "%u %*s %*s %*u %*u %*u %*s %*s %*s %" SCNu64, &id, &raw) != 2) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Invalid smartctl line '%s'.\n", buf); + return -1; + /* LCOV_EXCL_STOP */ + } + + if (id >= 256) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Invalid SMART id '%u'.\n", id); + return -1; + /* LCOV_EXCL_STOP */ + } + + smart[id] = raw; + } + } + + ret = pclose(f); + if (ret == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl -a on device '%s' (from pclose).\n", file); + return -1; + /* LCOV_EXCL_STOP */ + } + /* check only first bit, as other bits are used to report other conditions */ + if ((ret & 1) != 0) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Failed to run smartctl -a on device '%s' with return code %xh.\n", file, ret); + return -1; + /* LCOV_EXCL_STOP */ + } + + /* store the return smartctl return value */ + smart[SMART_FLAGS] = ret; + + return 0; +} + /** * Thread for spinning up. * @@ -1830,22 +1993,32 @@ static void* thread_spinup(void* arg) return 0; } -int devquery(tommy_list* high, tommy_list* low, int operation) +/** + * Thread for getting smart info. + */ +static void* thread_smart(void* arg) +{ + devinfo_t* devinfo = arg; + + if (devsmart(devinfo->file, devinfo->smart, devinfo->smart_serial) != 0) { + /* LCOV_EXCL_START */ + return (void*)-1; + /* LCOV_EXCL_STOP */ + } + + return 0; +} + +static int device_thread(tommy_list* list, void* (*func)(void* arg)) { - tommy_node* i; int fail = 0; - - (void)low; - - /* we support only thread_spinup */ - if (operation != DEVICE_UP) - return -1; + tommy_node* i; /* starts all threads */ - for (i = tommy_list_head(high); i != 0; i = i->next) { + for (i = tommy_list_head(list); i != 0; i = i->next) { devinfo_t* devinfo = i->data; - if (pthread_create(&devinfo->thread, 0, thread_spinup, devinfo) != 0) { + if (pthread_create(&devinfo->thread, 0, func, devinfo) != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Failed to create thread.\n"); exit(EXIT_FAILURE); @@ -1854,9 +2027,10 @@ int devquery(tommy_list* high, tommy_list* low, int operation) } /* joins all threads */ - for (i = tommy_list_head(high); i != 0; i = i->next) { + for (i = tommy_list_head(list); i != 0; i = i->next) { devinfo_t* devinfo = i->data; void* retval; + if (pthread_join(devinfo->thread, &retval) != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Failed to join thread.\n"); @@ -1877,5 +2051,26 @@ int devquery(tommy_list* high, tommy_list* low, int operation) return 0; } +int devquery(tommy_list* high, tommy_list* low, int operation) +{ + tommy_list* list; + void* (*func)(void* arg); + + /* we support only thread_spinup */ + if (operation == DEVICE_UP) { + func = thread_spinup; + list = high; + } else if (operation == DEVICE_SMART) { + func = thread_smart; + list = low; + if (devscan(list) != 0) + return -1; + } else { + return -1; + } + + return device_thread(list, func); +} + #endif