]> www.infradead.org Git - users/hch/nvme-cli.git/commitdiff
Implement json output for list that matches 'isdtc show -output json --intelssd'
authorScott Bauer <scott.bauer@intel.com>
Mon, 19 Sep 2016 17:27:43 +0000 (11:27 -0600)
committerScott Bauer <scott.bauer@intel.com>
Wed, 5 Oct 2016 18:14:43 +0000 (12:14 -0600)
    New command:
    nvme list -output-format json|normal
OR
    nvme list

Signed-off-by: Scott Bauer <scott.bauer@intel.com>
Makefile
nvme-models.c [new file with mode: 0644]
nvme-models.h [new file with mode: 0644]
nvme-print.c
nvme-print.h
nvme.c
nvme.h

index 1d68d762a3ab2fe6cff161a90f61d618ba4288ad..a01a20cacb336986fc713b5c3b074ea2e2fd7ac8 100644 (file)
--- 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 (file)
index 0000000..c607b76
--- /dev/null
@@ -0,0 +1,290 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+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 (file)
index 0000000..b8c6e52
--- /dev/null
@@ -0,0 +1,2 @@
+char *nvme_product_name(int id);
+
index ac56fcedcba083810798853501c1a7eddd2bae3e..8296617a2db81b2e5ff8f1de0544afc64a0e40ac 100644 (file)
@@ -2,9 +2,11 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 #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];
index eec3b05ac0cb659af6458ec1dc0d5ef3760109be..384ad61edc8d55c137007dcd2f620703bfd9173a 100644 (file)
@@ -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 ab782d007eff9efce7103cd8815272a1c21975ae..1f6b41245b9ce133c0dcbf2c38ca11c7ed05598a 100644 (file)
--- 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 2250e56a566bab01f3c3a5270da95374eea3c49c..f53800be1174adefd7cde8b5a9c4bbe9445349cc 100644 (file)
--- 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"