diff --git a/cmdline/support.c b/cmdline/support.c index 7ea03982..99c4c115 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -1639,7 +1639,7 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* sm for (i = 0; i < SMART_COUNT; ++i) smart[i] = SMART_UNASSIGNED; for (i = 0; i < INFO_COUNT; ++i) - info[i] = SMART_UNASSIGNED; + info[i] = SMART_UNASSIGNED; /* read the file */ inside = 0; diff --git a/cmdline/unix.c b/cmdline/unix.c index ee7b1417..4261b6df 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -95,7 +95,7 @@ const char* find_smartctl(void) /** * Read a file from sys * - * Return -1 on error, otherwise the data read + * Return -1 on error, otherwise the size of data read */ #if HAVE_LINUX_DEVICE static int sysread(const char* path, char* buf, size_t buf_size) @@ -107,7 +107,6 @@ static int sysread(const char* path, char* buf, size_t buf_size) f = open(path, O_RDONLY); if (f == -1) { /* LCOV_EXCL_START */ - log_fatal("Failed to open '%s'.\n", path); return -1; /* LCOV_EXCL_STOP */ } @@ -116,7 +115,6 @@ static int sysread(const char* path, char* buf, size_t buf_size) if (len < 0) { /* LCOV_EXCL_START */ close(f); - log_fatal("Failed to read '%s'.\n", path); return -1; /* LCOV_EXCL_STOP */ } @@ -124,20 +122,48 @@ static int sysread(const char* path, char* buf, size_t buf_size) ret = close(f); if (ret != 0) { /* LCOV_EXCL_START */ - log_fatal("Failed to close '%s'.\n", path); return -1; /* LCOV_EXCL_STOP */ } - if (len == (int)buf_size) - buf[buf_size - 1] = 0; - else - buf[len] = 0; - return len; } #endif +/** + * Read a file from sys. + * Trim spaces. + * Always put an ending 0. + * Do not report error on reading. + * + * Return 0 on error, otherwise a pointer to buf + */ +#if HAVE_LINUX_DEVICE +static char* sysattr(const char* path, char* buf, size_t buf_size) +{ + int len; + + len = sysread(path, buf, buf_size); + if (len < 0) { + /* LCOV_EXCL_START */ + return 0; + /* LCOV_EXCL_STOP */ + } + + if ((size_t)len + 1 > buf_size) { + /* LCOV_EXCL_START */ + return 0; + /* LCOV_EXCL_STOP */ + } + + buf[len] = 0; + + strtrim(buf); + + return buf; +} +#endif + /** * Get the device file from the device number. * @@ -325,15 +351,22 @@ static int tagread(const char* path, const char* tag, char* value, size_t value_ char* e; ret = sysread(path, buf, sizeof(buf)); - if (ret < 0) + if (ret < 0) { + /* LCOV_EXCL_START */ + log_fatal("Failed to read '%s'.\n", path); return -1; - if (ret == sizeof(buf)) { + /* LCOV_EXCL_STOP */ + } + if ((size_t)ret + 1 > sizeof(buf)) { /* LCOV_EXCL_START */ log_fatal("Too long read '%s'.\n", path); return -1; /* LCOV_EXCL_STOP */ } + /* ending 0 */ + buf[ret] = 0; + tag_len = strlen(tag); for (i = buf; *i; ++i) { @@ -1241,6 +1274,75 @@ static int devsmart(dev_t device, const char* name, const char* smartctl, uint64 } #endif +/** + * Get device attributes. + */ +#if HAVE_LINUX_DEVICE +static void devattr(dev_t device, uint64_t* info, char* serial, char* vendor, char* model) +{ + char path[PATH_MAX]; + char buf[512]; + int ret; + char* attr; + + (void)vendor; /* the vendor file typically contains "ATA" */ + + if (info[INFO_SIZE] == SMART_UNASSIGNED) { + pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/size", major(device), minor(device)); + attr = sysattr(path, buf, sizeof(buf)); + if (attr) { + char* e; + uint64_t v; + v = strtoul(attr, &e, 10); + if (*e == 0) + info[INFO_SIZE] = v * 512; + } + } + + if (info[INFO_ROTATION_RATE] == SMART_UNASSIGNED) { + pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/queue/rotational", major(device), minor(device)); + attr = sysattr(path, buf, sizeof(buf)); + if (attr) { + char* e; + uint64_t v; + v = strtoul(attr, &e, 10); + if (*e == 0) + info[INFO_ROTATION_RATE] = v; + } + } + + if (*model == 0) { + pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/model", major(device), minor(device)); + attr = sysattr(path, buf, sizeof(buf)); + if (attr && *attr) + pathcpy(model, SMART_MAX, attr); + } + + if (*serial == 0) { + pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/serial", major(device), minor(device)); + attr = sysattr(path, buf, sizeof(buf)); + if (attr && *attr) + pathcpy(serial, SMART_MAX, attr); + } + + if (*serial == 0) { + // --- Page 0x80: Unit Serial Number --- + pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/vpd_pg80", major(device), minor(device)); + ret = sysread(path, buf, sizeof(buf)); + if (ret > 4) { + unsigned len = (unsigned char)buf[3]; + if (4 + len <= (size_t)ret && 4 + len + 1 <= sizeof(buf)) { + attr = buf + 4; + attr[len] = 0; + strtrim(attr); + if (*attr) + pathcpy(serial, SMART_MAX, attr); + } + } + } +} +#endif + /** * Get POWER state. */ @@ -1621,6 +1723,14 @@ static void* thread_probe(void* arg) /* LCOV_EXCL_STOP */ } + /* + * Retrieve some attributes directly from the system. + * + * smartctl intentionally skips queries on devices in standby mode + * to prevent accidentally spinning them up. + */ + devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->vendor, devinfo->model); + return 0; #else (void)arg; @@ -1906,7 +2016,6 @@ int ambient_temperature(void) char name[128]; char label[128]; char* dash; - int ret; char* e; int temp; @@ -1923,8 +2032,7 @@ int ambient_temperature(void) /* read the temperature */ pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s", entry->d_name, hwmon_entry->d_name); - ret = sysread(path, value, sizeof(value)); - if (ret < 0) + if (!sysattr(path, value, sizeof(value))) continue; temp = strtol(value, &e, 10) / 1000; @@ -1936,24 +2044,18 @@ int ambient_temperature(void) /* read the corresponding name */ pathprint(path, sizeof(path), "/sys/class/hwmon/%s/name", entry->d_name); - ret = sysread(path, name, sizeof(name)); - if (ret < 0) { + if (!sysattr(path, name, sizeof(name))) { /* fallback to using the hwmon entry */ pathcpy(name, sizeof(name), entry->d_name); } /* read the corresponding label file */ pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s_label", entry->d_name, hwmon_entry->d_name); - ret = sysread(path, label, sizeof(label)); - if (ret < 0) { + if (!sysattr(path, label, sizeof(label))) { /* fallback to using the temp* name (e.g., temp1, temp2) */ pathcpy(label, sizeof(label), hwmon_entry->d_name); } - /* trim spaces */ - strtrim(name); - strtrim(label); - log_tag("thermal:ambient:device:%s:%s:%s:%s:%d\n", entry->d_name, name, hwmon_entry->d_name, label, temp); /* check if temperature is in reasonable range */