json_free_object(root);
}
+void json_print_nvme_subsystem_list(struct subsys_list_item *slist, int n)
+{
+ struct json_object *root;
+ struct json_array *subsystems;
+ struct json_object *subsystem_attrs;
+ struct json_array *paths;
+ struct json_object *path_attrs;
+ struct json_object *path_object;
+ int i, j;
+
+ root = json_create_object();
+ subsystems = json_create_array();
+
+ for (i = 0; i < n; i++) {
+ subsystem_attrs = json_create_object();
+
+ json_object_add_value_string(subsystem_attrs,
+ "Name", slist[i].name);
+ json_object_add_value_string(subsystem_attrs,
+ "NQN", slist[i].subsysnqn);
+
+ json_array_add_value_object(subsystems, subsystem_attrs);
+
+ paths = json_create_array();
+ path_object = json_create_object();
+
+ for (j = 0; j < slist[i].nctrls; j++) {
+ path_attrs = json_create_object();
+ json_object_add_value_string(path_attrs, "Name",
+ slist[i].ctrls[j].name);
+ json_object_add_value_string(path_attrs, "Transport",
+ slist[i].ctrls[j].transport);
+ json_object_add_value_string(path_attrs, "Address",
+ slist[i].ctrls[j].address);
+ json_array_add_value_object(paths, path_attrs);
+ }
+ if (j) {
+ json_object_add_value_array(path_object, "Paths",
+ paths);
+ json_array_add_value_object(subsystems, path_object);
+ }
+
+ }
+
+ if (i)
+ json_object_add_value_array(root, "Subsystems", subsystems);
+ json_print_object(root, NULL);
+}
+
void show_registers_cap(struct nvme_bar_cap *cap)
{
printf("\tMemory Page Size Maximum (MPSMAX): %u bytes\n", 1 << (12 + ((cap->mpsmax_mpsmin & 0xf0) >> 4)));
return membase;
}
+static const char *subsys_dir = "/sys/class/nvme-subsystem/";
+
+static char *get_nvme_subsnqn(char *path)
+{
+ char sspath[319];
+ char *subsysnqn;
+ int fd;
+ int ret;
+
+ snprintf(sspath, sizeof(sspath), "%s/subsysnqn", path);
+
+ fd = open(sspath, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ subsysnqn = calloc(1, 256);
+ if (!subsysnqn)
+ goto close_fd;
+
+ ret = read(fd, subsysnqn, 256);
+ if (ret < 0) {
+ free(subsysnqn);
+ subsysnqn = NULL;
+ }
+
+ if (subsysnqn[strlen(subsysnqn) - 1] == '\n')
+ subsysnqn[strlen(subsysnqn) - 1] = '\0';
+
+close_fd:
+ close(fd);
+
+ return subsysnqn;
+}
+
+static char *get_nvme_ctrl_transport(char *path)
+{
+ char *trpath;
+ char *transport;
+ int fd;
+ ssize_t ret;
+
+ ret = asprintf(&trpath, "%s/transport", path);
+ if (ret < 0)
+ return NULL;
+
+ transport = calloc(1, 1024);
+ if (!transport)
+ goto err_free_trpath;
+
+ fd = open(trpath, O_RDONLY);
+ if (fd < 0)
+ goto err_free_tr;
+
+ ret = read(fd, transport, 1024);
+ if (ret < 0)
+ goto err_close_fd;
+
+ if (transport[strlen(transport) - 1] == '\n')
+ transport[strlen(transport) - 1] = '\0';
+
+ close(fd);
+ free(trpath);
+
+ return transport;
+
+err_close_fd:
+ close(fd);
+err_free_tr:
+ free(transport);
+err_free_trpath:
+ free(trpath);
+
+ return NULL;
+}
+
+static char *get_nvme_ctrl_address(char *path)
+{
+ char *addrpath;
+ char *address;
+ int fd;
+ ssize_t ret;
+ int i;
+
+ ret = asprintf(&addrpath, "%s/address", path);
+ if (ret < 0)
+ return NULL;
+
+ address = calloc(1, 1024);
+ if (!address)
+ goto err_free_addrpath;
+
+ fd = open(addrpath, O_RDONLY);
+ if (fd < 0)
+ goto err_free_addr;
+
+ ret = read(fd, address, 1024);
+ if (ret < 0)
+ goto err_close_fd;
+
+ if (address[strlen(address) - 1] == '\n')
+ address[strlen(address) - 1] = '\0';
+
+ for (i = 0; i < strlen(address); i++) {
+ if (address[i] == ',' )
+ address[i] = ' ';
+ }
+
+ close(fd);
+ free(addrpath);
+
+ return address;
+
+err_close_fd:
+ close(fd);
+err_free_addr:
+ free(address);
+err_free_addrpath:
+ free(addrpath);
+
+ return NULL;
+}
+static int scan_ctrls_filter(const struct dirent *d)
+{
+ int id, nsid;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme")) {
+ if (sscanf(d->d_name, "nvme%dn%d", &id, &nsid) == 2)
+ return 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+void print_nvme_subsystem(struct subsys_list_item *item)
+{
+ int i;
+
+ printf("%s - NQN=%s\n", item->name, item->subsysnqn);
+ printf("\\\n");
+
+ for (i = 0; i < item->nctrls; i++) {
+ printf(" +- %s %s %s\n", item->ctrls[i].name,
+ item->ctrls[i].transport,
+ item->ctrls[i].address);
+ }
+
+}
+
+void print_nvme_subsystem_list(struct subsys_list_item *slist, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ print_nvme_subsystem(&slist[i]);
+}
+
+int get_nvme_subsystem_info(char *name, char *path,
+ struct subsys_list_item *item)
+{
+ char ctrl_path[512];
+ struct dirent **ctrls;
+ int n, i;
+
+ item->subsysnqn = get_nvme_subsnqn(path);
+ if (!item->subsysnqn)
+ return 1;
+
+ item->name = strdup(name);
+
+ n = scandir(path, &ctrls, scan_ctrls_filter, alphasort);
+ if (n < 0)
+ goto free_subysynqn;
+
+ item->ctrls = calloc(n, sizeof(struct ctrl_list_item));
+ if (!item->ctrls)
+ goto free_ctrls;
+
+ item->nctrls = n;
+
+ for (i = 0; i < n; i++) {
+ item->ctrls[i].name = strdup(ctrls[i]->d_name);
+
+ snprintf(ctrl_path, sizeof(ctrl_path), "%s/%s", path,
+ item->ctrls[i].name);
+
+ item->ctrls[i].address = get_nvme_ctrl_address(ctrl_path);
+ if (!item->ctrls[i].address) {
+ free(item->ctrls[i].name);
+ goto free_ctrl_list;
+ }
+
+ item->ctrls[i].transport = get_nvme_ctrl_transport(ctrl_path);
+ if (!item->ctrls[i].transport) {
+ free(item->ctrls[i].name);
+ free(item->ctrls[i].address);
+ goto free_ctrl_list;
+ }
+ }
+
+ for (i = 0; i < n; i++)
+ free(ctrls[i]);
+ free(ctrls);
+
+ return 0;
+
+free_ctrl_list:
+ free(item->ctrls);
+
+free_ctrls:
+ for (i = 0; i < n; i++)
+ free(ctrls[i]);
+ free(ctrls);
+
+free_subysynqn:
+ free(item->subsysnqn);
+ free(item->name);
+
+ return 1;
+}
+
+static int scan_subsys_filter(const struct dirent *d)
+{
+ char path[310];
+ struct stat ss;
+ int id;
+ int tmp;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ /* sanity checking, probably unneeded */
+ if (strstr(d->d_name, "nvme-subsys")) {
+ snprintf(path, sizeof(path), "%s%s", subsys_dir, d->d_name);
+ if (stat(path, &ss))
+ return 0;
+ if (!S_ISDIR(ss.st_mode))
+ return 0;
+ tmp = sscanf(d->d_name, "nvme-subsys%d", &id);
+ if (tmp != 1)
+ return 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void free_subsys_list_item(struct subsys_list_item *item)
+{
+ int i;
+
+ for (i = 0; i < item->nctrls; i++) {
+ free(item->ctrls[i].name);
+ free(item->ctrls[i].transport);
+ free(item->ctrls[i].address);
+ }
+
+ free(item->ctrls);
+ free(item->subsysnqn);
+ free(item->name);
+}
+
+static void free_subsys_list(struct subsys_list_item *slist, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ free_subsys_list_item(&slist[i]);
+
+ free(slist);
+}
+
+static int list_subsys(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ char path[310];
+ struct dirent **subsys;
+ struct subsys_list_item *slist;
+ int fmt, n, i, ret = 0;
+ const char *desc = "Retrieve information for subsystems";
+ 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}
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts, &cfg, sizeof(cfg));
+ if (ret < 0)
+ return ret;
+
+ fmt = validate_output_format(cfg.output_format);
+
+ if (fmt != JSON && fmt != NORMAL)
+ return -EINVAL;
+ n = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort);
+ if (n < 0) {
+ fprintf(stderr, "no NVMe subsystem(s) detected.\n");
+ return n;
+ }
+
+ slist = calloc(n, sizeof(struct subsys_list_item));
+ if (!slist) {
+ ret = ENOMEM;
+ goto free_subsys;
+ }
+
+ for (i = 0; i < n; i++) {
+ snprintf(path, sizeof(path), "%s%s", subsys_dir,
+ subsys[i]->d_name);
+ ret = get_nvme_subsystem_info(subsys[i]->d_name, path, &slist[i]);
+ if (ret)
+ goto free_subsys;
+ }
+
+ if (fmt == JSON)
+ json_print_nvme_subsystem_list(slist, n);
+ else
+ print_nvme_subsystem_list(slist, n);
+
+free_subsys:
+ free_subsys_list(slist, n);
+
+ for (i = 0; i < n; i++)
+ free(subsys[i]);
+ free(subsys);
+
+ return ret;
+}
+
static void print_list_item(struct list_item list_item)
{
long long int lba = 1 << list_item.ns.lbaf[(list_item.ns.flbas & 0x0f)].ds;