From: Scott Bauer Date: Mon, 19 Sep 2016 17:27:43 +0000 (-0600) Subject: Implement json output for list that matches 'isdtc show -output json --intelssd' X-Git-Tag: v1.0~27^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=4937f59c11330f147277cc5e08ad4a08d46a8c3c;p=users%2Fhch%2Fnvme-cli.git Implement json output for list that matches 'isdtc show -output json --intelssd' New command: nvme list -output-format json|normal OR nvme list Signed-off-by: Scott Bauer --- diff --git a/Makefile b/Makefile index 1d68d76..a01a20c 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ NVME_DPKG_VERSION=1~`lsb_release -sc` OBJS := argconfig.o suffix.o parser.o nvme-print.o nvme-ioctl.o \ nvme-lightnvm.o fabrics.o json.o plugin.o intel-nvme.o \ - lnvm-nvme.o memblaze-nvme.o + lnvm-nvme.o memblaze-nvme.o nvme-models.c nvme: nvme.c nvme.h $(OBJS) NVME-VERSION-FILE $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c -o $(NVME) $(OBJS) $(LDFLAGS) diff --git a/nvme-models.c b/nvme-models.c new file mode 100644 index 0000000..c607b76 --- /dev/null +++ b/nvme-models.c @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *_fmt1 = "/sys/class/nvme/nvme%d/device/subsystem_vendor"; +static char *_fmt2 = "/sys/class/nvme/nvme%d/device/subsystem_device"; +static char *_fmt3 = "/sys/class/nvme/nvme%d/device/vendor"; +static char *_fmt4 = "/sys/class/nvme/nvme%d/device/device"; +static char *_fmt5 = "/sys/class/nvme/nvme%d/device/class"; + +static char fmt1[78]; +static char fmt2[78]; +static char fmt3[78]; +static char fmt4[78]; +static char fmt5[78]; + +static char *device_top; +static char *device_mid; +static char *device_final; +static char *class_top; +static char *class_mid; +static char *class_final; + + + +static void free_all(void) +{ + free(device_top); + free(device_mid); + free(device_final); + free(class_top); + free(class_mid); + free(class_final); +} + +static char *find_data(char *data) +{ + while (*data != '\0') { + if (*data >= '0' && *data <= '9') + return data; + data++; + } + return NULL; +} + +static char *locate_info(char *data, bool is_inner, bool is_class) +{ + char *orig = data; + char *locate = find_data(data); + if (!data) + return orig; + + if (is_class) + return locate + 4; + if (!is_inner) + /* 4 to get over the number, 2 for spaces */ + return locate + 4 + 2; + + /* Inner data, has "sub_ven(space)sub_dev(space)(space)string */ + return locate + 4 + 1 + 4 + 2; +} + +static void format_and_print(char *save) +{ + + if (!class_mid) + snprintf(save, 1024, "%s %s %s", + locate_info(device_top, false, false), + locate_info(device_mid, false, false), + locate_info(device_final, true, false)); + else + snprintf(save, 1024, "%s: %s %s %s", + locate_info(class_mid, false, true), + locate_info(device_top, false, false), + locate_info(device_mid, false, false), + locate_info(device_final, true, false)); +} + +static void format_all(char *save, char *vendor, char *device) +{ + if (device_top && device_mid && device_final) + format_and_print(save); + + else if (device_top && !device_mid && class_mid) + snprintf(save, 1024, "%s: %s Device %s", + locate_info(class_mid, false, true), + locate_info(device_top, false, false), + device); + + else if (!device_top && class_mid) + snprintf(save, 1024, "%s: Vendor %s Device %s", + locate_info(class_mid, false, true), + vendor, + device); + else + snprintf(save, 1024, "Unknown device"); +} + +static int is_final_match(char *line, char *search) +{ + return !memcmp(&line[2], search, 2); +} + +static int is_inner_sub_vendev(char *line, char *search, char *search2) +{ + char combine[10]; + snprintf(combine, sizeof(combine), "%s %s", &search[2], &search2[2]); + if (line[0] != '\t' && line[1] != '\t') + return 0; + + return !memcmp(combine, &line[2], 9); +} + +static int is_mid_level_match(char *line, char *device, bool class) +{ + if (!class) + return !memcmp(&line[1], &device[2], 4); + + return !memcmp(&line[1], device, 2); +} + +static inline bool is_comment(char *line) +{ + return line[0] == '#'; +} + +static int is_top_level_match(char *line, const char* device, bool class) +{ + if (line[0] == '\t') + return false; + if (line[0] == '#') + return false; + if (!class) + return !memcmp(line, &device[2], 4); + if (line[0] != 'C') + return false; + /* Skipping C(SPACE) 0x */ + return !memcmp(&line[2], &device[2], 2); +} + +static inline int is_tab(char *line) +{ + return line[0] == '\t'; +} + +static inline int is_class_info(char *line) +{ + return !memcmp(line, "# C class", 9); +} + +static void parse_vendor_device(char **line, FILE *file, + char *device, char *subdev, + char *subven) +{ + bool device_single_found = false; + size_t amnt = 1024; + size_t found = 0; + char *newline; + + while ((found = getline(line, &amnt, file)) != -1) { + newline = *line; + if (is_comment(newline)) + continue; + if (!is_tab(newline)) + return; + + newline[found - 1] = '\0'; + if (!device_single_found && is_mid_level_match(newline, device, false)) { + device_single_found = true; + device_mid = strdup(newline); + continue; + } + + if (device_single_found && is_inner_sub_vendev(newline, subven, subdev)) { + device_final = strdup(newline); + break; + } + } +} + +static void pull_class_info(char **_newline, FILE *file, char *class) +{ + size_t amnt; + size_t size = 1024; + bool top_found = false; + bool mid_found = false; + char *newline; + + while ((amnt = getline(_newline, &size, file)) != -1) { + newline = *_newline; + newline[amnt - 1] = '\0'; + if (!top_found && is_top_level_match(newline, class, true)) { + class_top = strdup(newline); + top_found = true; + continue; + } + if (!mid_found && top_found && + is_mid_level_match(newline, &class[4], true)) { + class_mid = strdup(newline); + mid_found = true; + continue; + } + if (top_found && mid_found && + is_final_match(newline, &class[6])) { + class_final = strdup(newline); + break; + } + } +} + +static int read_sys_node(char *where, char *save, size_t savesz) +{ + char *new; + int fd, ret = 0; + fd = open(where, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s with errno %s\n", + where, strerror(errno)); + return 1; + } + /* -1 so we can safely use strstr below */ + if(!read(fd, save, savesz - 1)) + ret = 1; + + new = strstr(save, "\n"); + if (new) + new[0] = '\0'; + + close(fd); + return ret; +} + +char *nvme_product_name(int id) +{ + char *line; + ssize_t amnt; + FILE *file = fopen("/usr/share/hwdata/pci.ids", "r"); + char vendor[7] = { 0 }; + char device[7] = { 0 }; + char sub_device[7] = { 0 }; + char sub_vendor[7] = { 0 }; + char class[13] = { 0 }; + size_t size = 1024; + char ret; + + snprintf(fmt1, 78, _fmt1, id); + snprintf(fmt2, 78, _fmt2, id); + snprintf(fmt3, 78, _fmt3, id); + snprintf(fmt4, 78, _fmt4, id); + snprintf(fmt5, 78, _fmt5, id); + + ret = read_sys_node(fmt1, sub_vendor, 7); + ret |= read_sys_node(fmt2, sub_device, 7); + ret |= read_sys_node(fmt3, vendor, 7); + ret |= read_sys_node(fmt4, device, 7); + ret |= read_sys_node(fmt5, class, 13); + if (ret) + goto error1; + + + line = malloc(1024); + if (!line) + goto error1; + + while ((amnt = getline(&line, &size, file)) != -1) { + if (is_comment(line) && !is_class_info(line)) + continue; + if (is_top_level_match(line, vendor, false)) { + line[amnt - 1] = '\0'; + device_top = strdup(line); + parse_vendor_device(&line, file, + device, + sub_device, + sub_vendor); + } + if (is_class_info(line)) + pull_class_info(&line, file, class); + } + format_all(line, vendor, device); + free_all(); + return line; + error1: + return strdup("Unknown Device"); +} diff --git a/nvme-models.h b/nvme-models.h new file mode 100644 index 0000000..b8c6e52 --- /dev/null +++ b/nvme-models.h @@ -0,0 +1,2 @@ +char *nvme_product_name(int id); + diff --git a/nvme-print.c b/nvme-print.c index ac56fce..8296617 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -2,9 +2,11 @@ #include #include #include +#include #include "nvme-print.h" #include "json.h" +#include "nvme-models.h" static long double int128_to_double(__u8 *data) { @@ -59,6 +61,20 @@ void d_raw(unsigned char *buf, unsigned len) putchar(*(buf+i)); } +static void format(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz) +{ + + snprintf(formatter,fmt_sz, "%-*.*s", + (int)tofmtsz, (int)tofmtsz, tofmt); + /* trim() the obnoxious trailing white lines */ + while (--fmt_sz) { + if (formatter[fmt_sz - 1] != ' ' && formatter[fmt_sz - 1] != '\0') { + formatter[fmt_sz] = '\0'; + break; + } + } +} + static void show_nvme_id_ctrl_cmic(__u8 cmic) { __u8 rsvd = (cmic & 0xF8) >> 3; @@ -1119,6 +1135,54 @@ void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf } } +void json_print_list_items(struct list_item *list_items, unsigned len) +{ + struct json_object *root; + struct json_object *device_attrs; + char formatter[41] = { 0 }; + int index, i = 0; + char *product; + + root = json_create_object(); + for (i = 0; i < len; i++) { + device_attrs = json_create_object(); + + json_object_add_value_string(device_attrs, + "DevicePath", + list_items[i].node); + json_object_add_value_string(device_attrs, + "Firmware", + list_items[i].ctrl.fr); + + if (sscanf(list_items[i].node, "/dev/nvme%d", &index) == 1) + json_object_add_value_int(device_attrs, + "Index", + index); + + format(formatter, sizeof(formatter), + list_items[i].ctrl.mn, + sizeof(list_items[i].ctrl.mn)); + + json_object_add_value_string(device_attrs, + "ModelNumber", + formatter); + + product = nvme_product_name(index); + + format(formatter, sizeof(formatter), + list_items[i].ctrl.sn, + sizeof(list_items[i].ctrl.sn)); + + json_object_add_value_string(device_attrs, + "SerialNumber", + formatter); + + json_object_add_value_object(root, product, device_attrs); + free((void*)product); + } + json_print_object(root, NULL); +} + void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int mode) { char nguid_buf[2 * sizeof(ns->nguid) + 1], eui64_buf[2 * sizeof(ns->eui64) + 1]; diff --git a/nvme-print.h b/nvme-print.h index eec3b05..384ad61 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -36,6 +36,7 @@ void json_nvme_resv_report(struct nvme_reservation_status *status); void json_error_log(struct nvme_error_log_page *err_log, int entries, const char *devname); void json_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname); void json_fw_log(struct nvme_firmware_log_page *fw_log, const char *devname); +void json_print_list_items(struct list_item *items, unsigned amnt); void show_registers_version(__u32 vs); void show_registers_cap(struct nvme_bar_cap *cap); diff --git a/nvme.c b/nvme.c index ab782d0..1f6b412 100644 --- a/nvme.c +++ b/nvme.c @@ -719,15 +719,6 @@ static void get_registers(struct nvme_bar **bar) *bar = membase; } -#define MAX_LIST_ITEMS 256 -struct list_item { - char node[1024]; - struct nvme_id_ctrl ctrl; - int nsid; - struct nvme_id_ns ns; - unsigned block; -}; - static void print_list_item(struct list_item list_item) { @@ -819,6 +810,27 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi struct dirent **devices; struct list_item *list_items; unsigned int i, n, fd, ret; + int fmt; + const char *desc = "Retrieve basic information for the given device"; + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + const struct argconfig_commandline_options opts[] = { + {"output-format", 'o', "FMT", + CFG_STRING, &cfg.output_format, + required_argument, "Output Format: normal|json"}, + {NULL} + }; + argconfig_parse(argc, argv, desc, opts, &cfg, sizeof(cfg)); + fmt = validate_output_format(cfg.output_format); + + if (fmt != JSON && fmt != NORMAL) + return -EINVAL; n = scandir(dev, &devices, scan_dev_filter, alphasort); if (n <= 0) @@ -835,7 +847,11 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi if (ret) return ret; } - print_list_items(list_items, n); + + if (fmt == JSON) + json_print_list_items(list_items, n); + else + print_list_items(list_items, n); for (i = 0; i < n; i++) free(devices[i]); diff --git a/nvme.h b/nvme.h index 2250e56..f53800b 100644 --- a/nvme.h +++ b/nvme.h @@ -154,6 +154,15 @@ struct nvme_bar_cap { #define le64_to_cpu(x) \ le64toh((__force __u64)(x)) +#define MAX_LIST_ITEMS 256 +struct list_item { + char node[1024]; + struct nvme_id_ctrl ctrl; + int nsid; + struct nvme_id_ns ns; + unsigned block; +}; + void register_extension(struct plugin *plugin); #include "argconfig.h"