From ab14ec85d7b334ec63df7a183601de0caae217f8 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 09:37:09 -0400 Subject: [PATCH 001/263] discoverd: resolve configured host names without blocking nvme-discoverd resolved the host names of nvme-fabrics.conf with getaddrinfo() in the event loop, one at a time. A slow DNS server stopped the daemon at startup and on reload. A name that failed to resolve was skipped until the next reload, for example at boot before the network is up. Resolve the names in parallel with the daemon-util resolver, and add each connection when its lookup ends. Try a failed lookup again later. Until the first lookups end, a configured connection looks not desired, so the release pass waits for them. Signed-off-by: Martin Belanger --- NEWS.md | 7 + discoverd/src/ctx.h | 2 + discoverd/src/inventory.c | 327 ++++++++++++++++++++++--------- discoverd/src/inventory.h | 31 ++- discoverd/src/main.c | 35 +++- discoverd/tests/test-inventory.c | 243 +++++++++++++++++++++++ discoverd/tests/wringer.sh | 69 ++++++- 7 files changed, 616 insertions(+), 98 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0e09e21d2c..b0614dab22 100644 --- a/NEWS.md +++ b/NEWS.md @@ -194,6 +194,13 @@ still matches. At startup, nvme-discoverd removes the state of controllers that are gone. +* nvme-discoverd resolves the host names in `nvme-fabrics.conf` in + parallel, without blocking. Before, it resolved them one at a time, + and a slow DNS server stopped the daemon. A failed lookup is tried + again, first after 1 s, then less often, up to every 5 minutes. + Before, the connection was skipped until the next reload. While the + first lookups run, nvme-discoverd does not release any controller. + ### libnvme * A failed read of `/dev/nvme-fabrics` is no longer cached for the diff --git a/discoverd/src/ctx.h b/discoverd/src/ctx.h index 99bdea133a..57b9f81fa8 100644 --- a/discoverd/src/ctx.h +++ b/discoverd/src/ctx.h @@ -20,6 +20,7 @@ struct inventory; struct unit_mgr; struct events_ctx; struct mdns_ctx; +struct dmn_resolver; /* * Top-level discoverd application context. Created once at startup and @@ -40,5 +41,6 @@ struct discoverd_ctx { struct unit_mgr *umgr; // transient unit manager struct events_ctx *evts; // udev event monitor struct mdns_ctx *mdns; // mDNS discovery, or NULL + struct dmn_resolver *resolver; // config host name lookups bool force_debug; // --debug forces DEBUG }; diff --git a/discoverd/src/inventory.c b/discoverd/src/inventory.c index f4988ff2a3..d65d8ebab9 100644 --- a/discoverd/src/inventory.c +++ b/discoverd/src/inventory.c @@ -7,19 +7,17 @@ */ #include +#include #include #include -#ifdef NVME_HAVE_NETDB -#include - #include #include -#endif #include #include +#include #include #include #include @@ -49,6 +47,26 @@ struct cfg_conn_entry { const struct libnvmf_config_conn *conn; }; +// The name lookup of a configured traddr: first try, then each retry. +#define LOOKUP_TIMEOUT_USEC (10 * 1000000ULL) +#define LOOKUP_RETRY_MIN_SEC 1 +#define LOOKUP_RETRY_MAX_SEC 300 + +/* + * A configured connection whose traddr is a host name, until the name + * resolves. conn is borrowed, as in cfg_conn_entry. + */ +struct cfg_lookup { + struct list_node entry; + struct inventory *inv; + const struct discoverd_ctx *dctx; + const struct libnvmf_config_conn *conn; + struct dmn_resolve_query *query; + sd_event_source *retry_timer; + unsigned int retry_sec; + bool first; // the first lookup since the configuration was loaded +}; + struct inventory { struct tid_list nbft_dcs; struct tid_list nbft_iocs; @@ -57,6 +75,10 @@ struct inventory { struct tid_list discovered_dcs; // found through mDNS or FC kickstart struct list_head dlp_cache; struct list_head cfg_conns; + struct list_head lookups; + unsigned int nr_first_lookups; // first lookups not ended yet + inventory_lookup_callback lookup_cb; + void *lookup_data; }; /* @@ -75,9 +97,36 @@ struct inventory *inventory_new(void) return NULL; list_head_init(&inv->dlp_cache); list_head_init(&inv->cfg_conns); + list_head_init(&inv->lookups); return inv; } +void inventory_set_lookup_callback(struct inventory *inv, + inventory_lookup_callback cb, + void *user_data) +{ + inv->lookup_cb = cb; + inv->lookup_data = user_data; +} + +static void lookup_free(struct cfg_lookup *l) +{ + list_del(&l->entry); + dmn_resolve_cancel(l->query); + sd_event_source_disable_unref(l->retry_timer); + free(l); +} + +// Stop every lookup, without calling the callback. +static void lookups_free(struct inventory *inv) +{ + struct cfg_lookup *l, *next; + + list_for_each_safe(&inv->lookups, l, next, entry) + lookup_free(l); + inv->nr_first_lookups = 0; +} + /* * Give @tid the daemon's default host if it names none. A candidate * without a host connects as the default host, and its TID must say so to @@ -106,6 +155,7 @@ void inventory_free(struct inventory *inv) if (!inv) return; + lookups_free(inv); tid_list_free_items(&inv->nbft_dcs); tid_list_free_items(&inv->nbft_iocs); tid_list_free_items(&inv->cfg_dcs); @@ -526,99 +576,27 @@ int inventory_load_nbft(struct inventory *inv, } /* - * Resolve traddr to a numeric address if it names a tcp/rdma hostname. - * libnvmf_config_conn_get_traddr() never returns a hostname for FC (no - * hostname concept there), and an already-numeric address is returned - * unchanged. Deliberately blocking and sequential, one getaddrinfo() call - * at a time, no worker thread: config load runs once at startup and, more - * rarely, on SIGHUP — a rare, small path, not the daemon's steady-state - * event loop, so a blocking resolve here is acceptable. - * Returns an allocated numeric-address string, or NULL if traddr is not - * numeric and cannot be resolved. + * Add the configured connection @conn, with @traddr as its numeric + * address. Returns false if the connection cannot be added. */ -static char *resolve_traddr(const char *transport, const char *traddr) -{ - if (libnvmf_traddr_is_numeric(traddr)) - return strdup(traddr); - -#ifdef NVME_HAVE_NETDB - struct addrinfo hints = { .ai_family = AF_UNSPEC }; - struct addrinfo *host_info = NULL; - char addrstr[NVMF_TRADDR_SIZE]; - const char *p = NULL; - char *resolved = NULL; - int ret; - - if (strcmp(transport, "tcp") && strcmp(transport, "rdma")) - return NULL; - - ret = getaddrinfo(traddr, NULL, &hints, &host_info); - if (ret) { - log_warn("failed to resolve host '%s': %s", - traddr, gai_strerror(ret)); - return NULL; - } - - switch (host_info->ai_family) { - case AF_INET: - p = inet_ntop(AF_INET, - &((struct sockaddr_in *)host_info->ai_addr)->sin_addr, - addrstr, sizeof(addrstr)); - break; - case AF_INET6: - p = inet_ntop(AF_INET6, - &((struct sockaddr_in6 *)host_info->ai_addr)->sin6_addr, - addrstr, sizeof(addrstr)); - break; - default: - break; - } - if (p) - resolved = strdup(addrstr); - - freeaddrinfo(host_info); - return resolved; -#else - log_warn("cannot resolve host '%s': hostname resolution not available " - "in this build", traddr); - return NULL; -#endif -} - -struct load_config_args { - struct inventory *inv; - const struct discoverd_ctx *dctx; -}; - -static void load_config_conn_callback(const struct libnvmf_config_conn *conn, - void *user_data) +static bool add_config_conn(struct inventory *inv, + const struct discoverd_ctx *dctx, + const struct libnvmf_config_conn *conn, const char *traddr) { - struct load_config_args *args = user_data; - struct inventory *inv = args->inv; - const char *transport = libnvmf_config_conn_get_transport(conn); - const char *raw_traddr = libnvmf_config_conn_get_traddr(conn); bool is_dc = libnvmf_config_conn_is_dc(conn); - char *traddr; struct libnvmf_tid *t, *t2; struct cfg_conn_entry *ce; - traddr = resolve_traddr(transport, raw_traddr); - if (!traddr) { - log_warn("%s - failed to resolve, skipping", raw_traddr); - return; - } - - t = tid_new(transport, traddr, + t = tid_new(libnvmf_config_conn_get_transport(conn), traddr, libnvmf_config_conn_get_trsvcid(conn), libnvmf_config_conn_get_subsysnqn(conn), libnvmf_config_conn_get_host_traddr(conn), libnvmf_config_conn_get_host_iface(conn), libnvmf_config_conn_get_hostnqn(conn), libnvmf_config_conn_get_hostid(conn), is_dc); - t = with_default_host(args->dctx, t); - free(traddr); + t = with_default_host(dctx, t); if (!t) - return; + return false; // t2 feeds the plain membership set; t is kept (paired with conn) // for inventory_config_conn_for() lookups — each list owns its copy. @@ -627,17 +605,184 @@ static void load_config_conn_callback(const struct libnvmf_config_conn *conn, tid_list_append(is_dc ? &inv->cfg_dcs : &inv->cfg_iocs, t2) < 0) { tid_free(t2); tid_free(t); - return; + return false; } ce = calloc(1, sizeof(*ce)); if (!ce) { tid_free(t); - return; + return false; } ce->tid = t; ce->conn = conn; list_add(&inv->cfg_conns, &ce->entry); + + return true; +} + +static int start_lookup(struct cfg_lookup *l); + +static int retry_lookup(sd_event_source *src __attribute__((unused)), + uint64_t usec __attribute__((unused)), void *user_data) +{ + struct cfg_lookup *l = user_data; + + l->retry_timer = sd_event_source_disable_unref(l->retry_timer); + start_lookup(l); + + return 0; +} + +static void schedule_lookup_retry(struct cfg_lookup *l) +{ + int r; + + r = sd_event_add_time_relative(l->dctx->event, &l->retry_timer, + CLOCK_MONOTONIC, + l->retry_sec * 1000000ULL, 0, + retry_lookup, l); + if (r < 0) { + log_warn("%s - cannot schedule a new lookup: %s", + libnvmf_config_conn_get_traddr(l->conn), + strerror(-r)); + return; + } + if (l->retry_sec < LOOKUP_RETRY_MAX_SEC) + l->retry_sec *= 2; + if (l->retry_sec > LOOKUP_RETRY_MAX_SEC) + l->retry_sec = LOOKUP_RETRY_MAX_SEC; +} + +/* The first address of @res, as a numeric string in @buf. */ +static const char *first_addr(const struct addrinfo *res, char *buf, + size_t size) +{ + const struct sockaddr_in6 *sin6; + const struct sockaddr_in *sin; + + for (; res; res = res->ai_next) { + if (res->ai_family == AF_INET) { + sin = (const struct sockaddr_in *)res->ai_addr; + return inet_ntop(AF_INET, &sin->sin_addr, buf, size); + } + if (res->ai_family == AF_INET6) { + sin6 = (const struct sockaddr_in6 *)res->ai_addr; + return inet_ntop(AF_INET6, &sin6->sin6_addr, buf, size); + } + } + + return NULL; +} + +static void on_lookup(int gai, const struct addrinfo *res, void *user_data) +{ + struct cfg_lookup *l = user_data; + struct inventory *inv = l->inv; + const char *name = libnvmf_config_conn_get_traddr(l->conn); + char addrstr[INET6_ADDRSTRLEN]; + int err = errno; // for EAI_SYSTEM + bool first = l->first; + bool added = false; + const char *addr; + + l->query = NULL; + if (first) { + l->first = false; + inv->nr_first_lookups--; + } + + addr = gai ? NULL : first_addr(res, addrstr, sizeof(addrstr)); + if (addr) { + log_info("%s - resolved to %s", name, addr); + added = add_config_conn(inv, l->dctx, l->conn, addr); + lookup_free(l); + } else { + const char *why = gai == EAI_SYSTEM ? strerror(err) : + gai ? gai_strerror(gai) : "no IP address"; + + if (first) + log_warn("%s - failed to resolve: %s, trying again", + name, why); + else + log_dbg("%s - failed to resolve: %s", name, why); + schedule_lookup_retry(l); + } + + if (inv->lookup_cb) + inv->lookup_cb(added, inv->lookup_data); +} + +static int start_lookup(struct cfg_lookup *l) +{ + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + }; + const char *name = libnvmf_config_conn_get_traddr(l->conn); + int r; + + r = dmn_resolve(l->dctx->resolver, name, NULL, &hints, + LOOKUP_TIMEOUT_USEC, on_lookup, l, &l->query); + if (r < 0) { + log_warn("%s - cannot start the lookup: %s", name, + strerror(-r)); + schedule_lookup_retry(l); + } + + return r; +} + +/* Resolve the host name of @conn without blocking. */ +static void add_lookup(struct inventory *inv, const struct discoverd_ctx *dctx, + const struct libnvmf_config_conn *conn) +{ + struct cfg_lookup *l; + + if (!dctx->resolver) { + log_warn("%s - cannot resolve, skipping", + libnvmf_config_conn_get_traddr(conn)); + return; + } + + l = calloc(1, sizeof(*l)); + if (!l) + return; + l->inv = inv; + l->dctx = dctx; + l->conn = conn; + l->retry_sec = LOOKUP_RETRY_MIN_SEC; + list_add_tail(&inv->lookups, &l->entry); + + if (!start_lookup(l)) { + l->first = true; + inv->nr_first_lookups++; + } +} + +struct load_config_args { + struct inventory *inv; + const struct discoverd_ctx *dctx; +}; + +static void load_config_conn_callback(const struct libnvmf_config_conn *conn, + void *user_data) +{ + struct load_config_args *args = user_data; + const char *transport = libnvmf_config_conn_get_transport(conn); + const char *traddr = libnvmf_config_conn_get_traddr(conn); + + if (libnvmf_traddr_is_numeric(traddr)) { + add_config_conn(args->inv, args->dctx, conn, traddr); + return; + } + + // A host name exists only for tcp and rdma. + if (strcmp(transport, "tcp") && strcmp(transport, "rdma")) { + log_warn("%s - failed to resolve, skipping", traddr); + return; + } + + add_lookup(args->inv, args->dctx, conn); } void inventory_load_config(struct inventory *inv, @@ -646,6 +791,7 @@ void inventory_load_config(struct inventory *inv, struct load_config_args args = { .inv = inv, .dctx = dctx }; struct cfg_conn_entry *ce, *next; + lookups_free(inv); tid_list_free_items(&inv->cfg_dcs); tid_list_free_items(&inv->cfg_iocs); list_for_each_safe(&inv->cfg_conns, ce, next, entry) { @@ -658,8 +804,13 @@ void inventory_load_config(struct inventory *inv, libnvmf_config_conn_for_each(dctx->fabrics_cfg, load_config_conn_callback, &args); - log_dbg("loaded %zu DC(s), %zu IOC(s) from the fabrics config", - inv->cfg_dcs.len, inv->cfg_iocs.len); + log_dbg("fabrics config: %zu DC(s), %zu IOC(s), %u name lookup(s)", + inv->cfg_dcs.len, inv->cfg_iocs.len, inv->nr_first_lookups); +} + +bool inventory_config_pending(const struct inventory *inv) +{ + return inv->nr_first_lookups > 0; } const struct libnvmf_config_conn *inventory_config_conn_for( diff --git a/discoverd/src/inventory.h b/discoverd/src/inventory.h index 6f9dc2b25d..6d61b6d86f 100644 --- a/discoverd/src/inventory.h +++ b/discoverd/src/inventory.h @@ -33,20 +33,37 @@ void inventory_free(struct inventory *inv); int inventory_load_nbft(struct inventory *inv, const struct discoverd_ctx *dctx, char *path); +/* + * Called from the event loop when the name lookup of a configured traddr + * ends. @added is true if the lookup added a connection to the config sets. + */ +typedef void (*inventory_lookup_callback)(bool added, void *user_data); + +void inventory_set_lookup_callback(struct inventory *inv, + inventory_lookup_callback cb, + void *user_data); + /* * Rebuild the config DC/IOC sets from the resolved fabrics configuration * (libnvmf_config_read()). Call at startup and again on every SIGHUP — - * this always fully replaces the previous sets, never merges. A hostname - * traddr is resolved here, blocking, one connection at a time — this is a - * rare, small, startup/SIGHUP-only path, not the daemon's steady-state - * event loop, so no worker thread is warranted. A connection whose traddr - * cannot be resolved is skipped and logged. A candidate that names no host - * gets @dctx's default identity. @dctx->fabrics_cfg may be NULL (equivalent - * to an empty configuration). + * this always fully replaces the previous sets, never merges. A numeric + * traddr is added at once. A host name is resolved through + * @dctx->resolver, without blocking: the connection is added when its + * lookup ends, and a failed lookup is tried again later. Loading the + * configuration again cancels the lookups of the previous one. A + * candidate that names no host gets @dctx's default identity. + * @dctx->fabrics_cfg may be NULL (equivalent to an empty configuration). */ void inventory_load_config(struct inventory *inv, const struct discoverd_ctx *dctx); +/* + * True until the first lookup of each host name of the last + * inventory_load_config() has ended. Until then, the config sets are not + * complete. + */ +bool inventory_config_pending(const struct inventory *inv); + /* * The libnvmf_config_conn that produced @t via inventory_load_config(), or * NULL if @t is not a statically configured connection (i.e. it was diff --git a/discoverd/src/main.c b/discoverd/src/main.c index cafb9b45b3..2e12e38bed 100644 --- a/discoverd/src/main.c +++ b/discoverd/src/main.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,9 @@ #define RETRY_INITIAL_DELAY_SEC 1 #define RETRY_MAX_DELAY_SEC 300 +// Name lookups of the fabrics configuration that run at the same time. +#define RESOLVE_WORKERS 8 + struct active_ctrl { struct list_node entry; char *unit_name; // "nvme-discoverd-<12hex>.service" @@ -382,18 +386,25 @@ static bool release_check_exclusions; * an mDNS DC whose DLP is not fetched yet, is never released here. With * @release_check_exclusions, also release the controllers that the * exclusion list now matches. NBFT controllers are never released. + * While a host name of the fabrics configuration is not resolved yet, its + * controller looks not desired, so the pass waits for on_config_lookup(). */ static int release_undesired(sd_event_source *src, void *user_data __attribute__((unused))) { - bool check_exclusions = release_check_exclusions; struct active_ctrl *e, *next; struct saved_ctrl *s, *snext; + bool check_exclusions; release_pending = false; - release_check_exclusions = false; sd_event_source_disable_unref(src); + if (inventory_config_pending(ctx.inventory)) + return 0; + + check_exclusions = release_check_exclusions; + release_check_exclusions = false; + list_for_each_safe(&g_ctrls, e, next, entry) { bool desired = inventory_is_desired(ctx.inventory, e->tid); @@ -1578,6 +1589,18 @@ static void connect_desired(void) * is used. Called at startup after config_load() and again after a SIGHUP * reload, since the libnvme context outlives any single config. */ +/* + * A name lookup of the fabrics configuration ended. Connect what it added, + * and run the release pass that waited for it. + */ +static void on_config_lookup(bool added, + void *user_data __attribute__((unused))) +{ + if (added) + connect_desired(); + schedule_release(false); +} + static void apply_log_level(void) { int level = ctx.force_debug ? DMN_LOG_DEBUG : ctx.cfg->debug_level; @@ -1771,9 +1794,16 @@ int main(int argc, char **argv) return 1; } + r = dmn_resolver_new(ctx.event, RESOLVE_WORKERS, &ctx.resolver); + if (r < 0) { + log_err("failed to create the resolver: %s", strerror(-r)); + return 1; + } + ctx.inventory = inventory_new(); if (!ctx.inventory) return 1; + inventory_set_lookup_callback(ctx.inventory, on_config_lookup, NULL); if (ctx.cfg->nbft) inventory_load_nbft(ctx.inventory, &ctx, NBFT_SYSFS_PATH); @@ -1839,6 +1869,7 @@ int main(int argc, char **argv) free(nvme_path_abs); free(config_path_abs); inventory_free(ctx.inventory); + dmn_resolver_free(ctx.resolver); free(ctx.hostnqn); free(ctx.hostid); config_free(ctx.cfg); diff --git a/discoverd/tests/test-inventory.c b/discoverd/tests/test-inventory.c index f25a095b8d..853c8eef82 100644 --- a/discoverd/tests/test-inventory.c +++ b/discoverd/tests/test-inventory.c @@ -7,12 +7,15 @@ */ #include +#include #include #include #include #include #include +#include +#include #include #include "ctx.h" @@ -359,6 +362,234 @@ static bool test_nbft_ipv6_uri(struct discoverd_ctx *dctx) return pass; } +/* + * Fake name lookups. "slow.example" answers after SLOW_MS, "fail.example" + * never resolves, and any other name is 192.0.2., with taken from + * the name ("ioc20.example" is 192.0.2.20). + */ +#define SLOW_MS 300 + +static int nr_fail_lookups; + +static int fake_lookup(const char *host, const char *service, + const struct addrinfo *hints __attribute__((unused)), + struct addrinfo **res) +{ + static const struct addrinfo numeric = { + .ai_flags = AI_NUMERICHOST, + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + char addr[32]; + int n = 0; + + if (!strcmp(host, "fail.example")) { + __atomic_add_fetch(&nr_fail_lookups, 1, __ATOMIC_SEQ_CST); + return EAI_AGAIN; + } + if (!strcmp(host, "slow.example")) { + usleep(SLOW_MS * 1000); + n = 30; + } else { + if (sscanf(host, "%*[a-z]%d", &n) != 1) + n = 0; + } + snprintf(addr, sizeof(addr), "192.0.2.%d", n); + + return getaddrinfo(addr, service, &numeric, res); +} + +static int nr_callbacks, nr_added; + +static void on_lookup(bool added, void *user_data __attribute__((unused))) +{ + nr_callbacks++; + nr_added += added; +} + +// The hostid that the default host identity derives from HOST_NQN. +#define HOST_ID "c0ffee00-0000-0000-0000-000000000001" + +/* Is the configured connection to @traddr desired? */ +static bool cfg_desired(struct inventory *inv, const char *traddr, + bool is_dc) +{ + struct libnvmf_tid *t; + bool desired; + + t = tid_new("tcp", traddr, is_dc ? "8009" : "4420", + is_dc ? DISC_NQN : IOC_NQN, NULL, NULL, HOST_NQN, HOST_ID, + is_dc); + if (!t) { + printf(" - tid_new(%s) returned NULL [FAIL]\n", traddr); + exit(EXIT_FAILURE); + } + desired = inventory_is_desired(inv, t); + tid_free(t); + + return desired; +} + +/* Run the event loop until @done() or @limit_ms. */ +static void run_until(sd_event *event, bool (*done)(void *), void *arg, + unsigned int limit_ms) +{ + unsigned int waited; + + for (waited = 0; !done(arg) && waited < limit_ms; waited += 20) + sd_event_run(event, 20 * 1000); +} + +static bool lookups_ended(void *arg) +{ + return !inventory_config_pending(arg); +} + +static bool ioc20_added(void *arg) +{ + return cfg_desired(arg, "192.0.2.20", false); +} + +static bool fail_retried(void *arg __attribute__((unused))) +{ + return __atomic_load_n(&nr_fail_lookups, __ATOMIC_SEQ_CST) >= 2; +} + +/* Write @text to a new nvme-fabrics.conf and read it. */ +static struct libnvmf_config *read_config(struct discoverd_ctx *dctx, + const char *text) +{ + char path[] = "/tmp/discoverd-inventory-XXXXXX"; + struct libnvmf_config *cfg = NULL; + int fd; + + fd = mkstemp(path); + if (fd < 0 || write(fd, text, strlen(text)) != (ssize_t)strlen(text) || + libnvmf_config_read(dctx->nvme_ctx, path, &cfg) < 0) { + printf(" - read the configuration [FAIL]\n"); + exit(EXIT_FAILURE); + } + close(fd); + unlink(path); + + return cfg; +} + +#define CFG_DC(addr) \ + "[Discovery Controller]\n" \ + "controller = transport=tcp;traddr=" addr ";trsvcid=8009\n" +#define CFG_IOC(addr) \ + "[Subsystem]\n" \ + "nqn = " IOC_NQN "\n" \ + "controller = transport=tcp;traddr=" addr ";trsvcid=4420\n" + +/* + * A numeric traddr is added at once. Host names are resolved in parallel, + * without blocking, and the connections are added when the lookups end. + */ +static bool test_config_lookup(struct discoverd_ctx *dctx) +{ + struct libnvmf_config *cfg; + struct inventory *inv; + bool pass = true; + + printf("test_config_lookup:\n"); + inv = inventory_new(); + if (!inv) + exit(EXIT_FAILURE); + inventory_set_lookup_callback(inv, on_lookup, NULL); + nr_callbacks = nr_added = 0; + nr_fail_lookups = 0; + + cfg = read_config(dctx, CFG_DC("192.0.2.1") CFG_DC("slow.example") + CFG_IOC("ioc20.example") CFG_IOC("fail.example")); + dctx->fabrics_cfg = cfg; + inventory_load_config(inv, dctx); + + pass &= check("numeric DC added at once", + cfg_desired(inv, "192.0.2.1", true), true); + pass &= check("host name not added before its lookup", + cfg_desired(inv, "192.0.2.20", false), false); + pass &= check("lookups pending", inventory_config_pending(inv), true); + + run_until(dctx->event, ioc20_added, inv, 5000); + pass &= check("fast lookup does not wait for the slow one", + cfg_desired(inv, "192.0.2.30", true), false); + + run_until(dctx->event, lookups_ended, inv, 5000); + pass &= check("lookups ended", inventory_config_pending(inv), false); + pass &= check("IOC added after its lookup", + cfg_desired(inv, "192.0.2.20", false), true); + pass &= check("slow DC added after its lookup", + cfg_desired(inv, "192.0.2.30", true), true); + pass &= check("one callback per lookup", nr_callbacks == 3, true); + pass &= check("two connections added", nr_added == 2, true); + + run_until(dctx->event, fail_retried, NULL, 3000); + pass &= check("failed lookup tried again", fail_retried(NULL), true); + + inventory_free(inv); + libnvmf_config_free(cfg); + dctx->fabrics_cfg = NULL; + + return pass; +} + +/* + * Loading the configuration again cancels the lookups of the previous + * one: their callbacks never run, and the previous configuration can be + * freed while a lookup is still in a worker. + */ +static bool test_config_reload(struct discoverd_ctx *dctx) +{ + struct libnvmf_config *cfg; + struct inventory *inv; + bool pass = true; + + printf("test_config_reload:\n"); + inv = inventory_new(); + if (!inv) + exit(EXIT_FAILURE); + inventory_set_lookup_callback(inv, on_lookup, NULL); + nr_callbacks = nr_added = 0; + + cfg = read_config(dctx, CFG_DC("slow.example")); + dctx->fabrics_cfg = cfg; + inventory_load_config(inv, dctx); + + // As reload_config() does: free the old configuration, then load. + libnvmf_config_free(cfg); + cfg = read_config(dctx, CFG_IOC("ioc21.example")); + dctx->fabrics_cfg = cfg; + inventory_load_config(inv, dctx); + + run_until(dctx->event, lookups_ended, inv, 5000); + usleep(2 * SLOW_MS * 1000); + sd_event_run(dctx->event, 0); + pass &= check("new host name added", + cfg_desired(inv, "192.0.2.21", false), true); + pass &= check("old host name not added", + cfg_desired(inv, "192.0.2.30", true), false); + pass &= check("no callback for the old lookup", nr_callbacks == 1, + true); + + // Freed while a lookup runs: no callback, no access to @inv. + cfg = read_config(dctx, CFG_DC("slow.example")); + libnvmf_config_free(dctx->fabrics_cfg); + dctx->fabrics_cfg = cfg; + inventory_load_config(inv, dctx); + inventory_free(inv); + usleep(2 * SLOW_MS * 1000); + sd_event_run(dctx->event, 0); + pass &= check("no callback after inventory_free()", nr_callbacks == 1, + true); + + libnvmf_config_free(cfg); + dctx->fabrics_cfg = NULL; + + return pass; +} + int main(void) { struct discoverd_ctx dctx = { @@ -377,6 +608,18 @@ int main(void) exit(EXIT_FAILURE); pass &= test_nbft_ipv4(&dctx); pass &= test_nbft_ipv6_uri(&dctx); + + if (sd_event_new(&dctx.event) < 0 || + dmn_resolver_new(dctx.event, 8, &dctx.resolver) < 0) + exit(EXIT_FAILURE); + dmn_resolver_set_lookup(dctx.resolver, fake_lookup); + pass &= test_config_lookup(&dctx); + pass &= test_config_reload(&dctx); + dmn_resolver_free(dctx.resolver); + sd_event_unref(dctx.event); + // Let the idle workers exit, so that valgrind finds no thread left. + usleep(2 * SLOW_MS * 1000); + libnvme_free_global_ctx(dctx.nvme_ctx); fflush(stdout); diff --git a/discoverd/tests/wringer.sh b/discoverd/tests/wringer.sh index cc1a1dd7e5..2e0727fa1a 100755 --- a/discoverd/tests/wringer.sh +++ b/discoverd/tests/wringer.sh @@ -179,6 +179,13 @@ REF_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:referral REF_PORT=8017 REF_PORT_ID=10 +# Host name phases. nvme-discoverd reads a copy of /etc/hosts, so that a +# phase can add a name without changing the real file. +HN_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:host-name +HN2_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:host-name-late +HN_PORT=8018 +HN_PORT_ID=11 + # mDNS phases only. Advertised through mDNS, on ${IFACE}'s address. The # port is opened and closed per phase, so a phase can advertise a DC whose # port is not open yet. @@ -196,6 +203,9 @@ confirm ETC_NVME_DIR=$(mktemp -d /tmp/discoverd-wringer-etc-nvme.XXXXXX) BACKING_DIR=$(mktemp -d /tmp/discoverd-wringer-ns.XXXXXX) SCRATCH=$(mktemp /tmp/discoverd-wringer-out.XXXXXX) +HOSTS_FILE=$(mktemp /tmp/discoverd-wringer-hosts.XXXXXX) +cp /etc/hosts "${HOSTS_FILE}" +chmod a+r "${HOSTS_FILE}" CYAN="\033[1;36m" RED="\033[1;31m" @@ -393,6 +403,7 @@ discoverd_start() { systemctl reset-failed "${DISCOVERD_UNIT}" >/dev/null 2>&1 systemd-run --unit="${DISCOVERD_UNIT}" --collect \ --property="BindPaths=${ETC_NVME_DIR}:${NVME_CONF_DIR}" \ + --property="BindReadOnlyPaths=${HOSTS_FILE}:/etc/hosts" \ --property=Type=notify-reload \ --property="SyslogIdentifier=nvme-discoverd" \ "${DISCOVERD_BIN}" --nvme-path "${NVME_BIN}" --debug \ @@ -434,6 +445,8 @@ discoverd_stop() { "${NVME_BIN}" disconnect -n "${NL_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${MAP_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${REF_NQN}" >/dev/null 2>&1 || true + "${NVME_BIN}" disconnect -n "${HN_NQN}" >/dev/null 2>&1 || true + "${NVME_BIN}" disconnect -n "${HN2_NQN}" >/dev/null 2>&1 || true } # Is any nvme-discoverd transient unit loaded? @@ -855,7 +868,7 @@ cleanup() { resolved_mdns_restore fi rm -rf "${ETC_NVME_DIR}" "${BACKING_DIR}" - rm -f "${SCRATCH}" "${DESIRED_FILE}" + rm -f "${SCRATCH}" "${DESIRED_FILE}" "${HOSTS_FILE}" if [ "${NVME_CONF_DIR_CREATED}" = true ]; then rmdir "${NVME_CONF_DIR}" 2>/dev/null fi @@ -879,6 +892,8 @@ disconnect_foreign "${NVME_BIN}" disconnect -n "${NL_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${MAP_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${REF_NQN}" >/dev/null 2>&1 || true +"${NVME_BIN}" disconnect -n "${HN_NQN}" >/dev/null 2>&1 || true +"${NVME_BIN}" disconnect -n "${HN2_NQN}" >/dev/null 2>&1 || true # ... and left its desired controllers saved. rm -f "${DESIRED_FILE}" @@ -1323,6 +1338,58 @@ countdown 3 "give a reconnect time to start" assert_not_connected "the excluded IOC is not reconnected" "${NL_NQN}" : > "${ETC_NVME_DIR}/exclusions.conf" +phase "a configured host name is resolved" +nvmet_add_subsystem "${HN_NQN}" +nvmet_add_subsystem "${HN2_NQN}" +nvmet_add_port "${HN_PORT_ID}" "${HN_PORT}" "${HN_NQN}" +ln -sf "/sys/kernel/config/nvmet/subsystems/${HN2_NQN}" \ + "/sys/kernel/config/nvmet/ports/${HN_PORT_ID}/subsystems/${HN2_NQN}" +echo "${TRADDR} wringer-ioc.test" >> "${HOSTS_FILE}" +cat >> "${ETC_NVME_DIR}/nvme-fabrics.conf" <> "${ETC_NVME_DIR}/nvme-fabrics.conf" <> "${HOSTS_FILE}" +assert_connected "connects once the name resolves" "${HN2_NQN}" 60 + +phase "a restart adopts a controller configured by host name" +# +# At startup, the release pass waits for the lookups. Without the wait, +# the pass could run before the lookup ends and release the IOC. +P_DEV=$(connected_dev "${HN_NQN}") +discoverd_stop_daemon_only +P_START=$(date +%H:%M:%S) +discoverd_start +assert_dev_stable "restart adopts the IOC configured by host name" \ + "${HN_NQN}" "${P_DEV}" +countdown 3 "give a release pass time to run" +assert_journal_lacks "the IOC was not released" \ + "${P_START}" "${P_DEV} - .*released" + if [ -z "${IFACE}" ]; then log "No given: mDNS phases not run" results From 61e97a997c2b5e1b6cad4471bc6c3c63b4c8e3f7 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 09:37:23 -0400 Subject: [PATCH 002/263] discoverd: add the FC connections of the fabrics configuration nvme-discoverd skipped every FC connection in nvme-fabrics.conf and logged "failed to resolve, skipping". It took any address that is not an IP address for a host name, and only tcp and rdma have host names. An FC address is not a host name: use it as it is. Fixes: b3e5bc150b0a ("discoverd: add the nvme-discoverd daemon") Signed-off-by: Martin Belanger --- discoverd/src/inventory.c | 13 ++++------ discoverd/tests/test-inventory.c | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/discoverd/src/inventory.c b/discoverd/src/inventory.c index d65d8ebab9..1e447b1c8c 100644 --- a/discoverd/src/inventory.c +++ b/discoverd/src/inventory.c @@ -771,14 +771,11 @@ static void load_config_conn_callback(const struct libnvmf_config_conn *conn, const char *transport = libnvmf_config_conn_get_transport(conn); const char *traddr = libnvmf_config_conn_get_traddr(conn); - if (libnvmf_traddr_is_numeric(traddr)) { - add_config_conn(args->inv, args->dctx, conn, traddr); - return; - } - - // A host name exists only for tcp and rdma. - if (strcmp(transport, "tcp") && strcmp(transport, "rdma")) { - log_warn("%s - failed to resolve, skipping", traddr); + // Only tcp and rdma have host names. An FC address is used as it is. + if (libnvmf_traddr_is_numeric(traddr) || + (strcmp(transport, "tcp") && strcmp(transport, "rdma"))) { + if (!add_config_conn(args->inv, args->dctx, conn, traddr)) + log_warn("%s - cannot add, skipping", traddr); return; } diff --git a/discoverd/tests/test-inventory.c b/discoverd/tests/test-inventory.c index 853c8eef82..16941ff3c3 100644 --- a/discoverd/tests/test-inventory.c +++ b/discoverd/tests/test-inventory.c @@ -590,6 +590,46 @@ static bool test_config_reload(struct discoverd_ctx *dctx) return pass; } +#define FC_TRADDR "nn-0x20000090fae0b5f5:pn-0x10000090fae0b5f5" +#define FC_HOST_TRADDR "nn-0x20000090fa979f2b:pn-0x10000090fa979f2b" + +/* An FC address is not a host name: it is added as it is. */ +static bool test_config_fc(struct discoverd_ctx *dctx) +{ + struct libnvmf_config *cfg; + struct inventory *inv; + struct libnvmf_tid *fc; + bool pass = true; + + printf("test_config_fc:\n"); + inv = inventory_new(); + if (!inv) + exit(EXIT_FAILURE); + + cfg = read_config(dctx, + "[Discovery Controller]\n" + "controller = transport=fc;traddr=" FC_TRADDR + ";host-traddr=" FC_HOST_TRADDR "\n"); + dctx->fabrics_cfg = cfg; + inventory_load_config(inv, dctx); + + fc = tid_new("fc", FC_TRADDR, NULL, DISC_NQN, FC_HOST_TRADDR, NULL, + HOST_NQN, HOST_ID, true); + if (!fc) { + printf(" - tid_new(%s) returned NULL [FAIL]\n", FC_TRADDR); + exit(EXIT_FAILURE); + } + pass &= check("FC DC added", inventory_is_desired(inv, fc), true); + pass &= check("no lookup", inventory_config_pending(inv), false); + + tid_free(fc); + inventory_free(inv); + libnvmf_config_free(cfg); + dctx->fabrics_cfg = NULL; + + return pass; +} + int main(void) { struct discoverd_ctx dctx = { @@ -615,6 +655,7 @@ int main(void) dmn_resolver_set_lookup(dctx.resolver, fake_lookup); pass &= test_config_lookup(&dctx); pass &= test_config_reload(&dctx); + pass &= test_config_fc(&dctx); dmn_resolver_free(dctx.resolver); sd_event_unref(dctx.event); // Let the idle workers exit, so that valgrind finds no thread left. From 30d818cbc7b70f93979a188f031e8aea26bbbbfd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 14 Oct 2026 12:54:50 +0000 Subject: [PATCH 003/263] libnvme: move the /dev/nvme-fabrics I/O into one function Reading the kernel options and connecting a controller both open /dev/nvme-fabrics, optionally write the arguments and read the response. Move that into nvmf_dev_xfer(), which returns a negative errno and the step which failed, so the callers keep their error mapping. No functional change. This prepares forwarding the transfer to the privilege separation helper. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/fabrics.c | 80 ++++++++++++++++++++++++++------------ libnvme/src/nvme/private.h | 7 ++++ 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 2773b1504c..9fc49868d9 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1620,6 +1620,43 @@ static int add_kernel_option(struct libnvme_global_ctx *ctx, size_t *count, return 0; } +/* + * Writes @args, unless NULL, to /dev/nvme-fabrics and reads the response + * into @buf, which is NUL-terminated. + * + * Return: the length of the response or a negative errno. On failure, + * @step is the step which failed. + */ +static ssize_t nvmf_dev_xfer(struct libnvme_global_ctx *ctx, + const char *args, char *buf, size_t size, + enum libnvmf_dev_step *step) +{ + __cleanup_fd int fd = -1; + ssize_t len; + + *step = LIBNVMF_DEV_OPEN; + fd = open(nvmf_dev, args ? O_RDWR : O_RDONLY); + if (fd < 0) + return -errno; + + if (args) { + *step = LIBNVMF_DEV_WRITE; + len = write(fd, args, strlen(args)); + if (len < 0) + return -errno; + if ((size_t)len != strlen(args)) + return -EIO; + } + + *step = LIBNVMF_DEV_READ; + len = read(fd, buf, size - 1); + if (len < 0) + return -errno; + buf[len] = '\0'; + + return len; +} + /* * Read the options the kernel accepts from /dev/nvme-fabrics, once per * @ctx. A failed read is not cached, so the next call retries it. @@ -1627,7 +1664,7 @@ static int add_kernel_option(struct libnvme_global_ctx *ctx, size_t *count, static int __nvmf_supported_options(struct libnvme_global_ctx *ctx) { char buf[0x1000], *options, *p, *v; - __cleanup_fd int fd = -1; + enum libnvmf_dev_step step; size_t count = 0; ssize_t len; int err; @@ -1639,18 +1676,16 @@ static int __nvmf_supported_options(struct libnvme_global_ctx *ctx) if (!ctx->options) return -ENOMEM; - fd = open(nvmf_dev, O_RDONLY); - if (fd < 0) { + len = nvmf_dev_xfer(ctx, NULL, buf, sizeof(buf), &step); + if (len < 0 && step == LIBNVMF_DEV_OPEN) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n", - nvmf_dev, libnvme_strerror(errno)); + nvmf_dev, libnvme_strerror(-len)); err = -ENVME_CONNECT_OPEN; goto out_free; } - memset(buf, 0x0, sizeof(buf)); - len = read(fd, buf, sizeof(buf) - 1); if (len < 0) { - if (errno == EINVAL) { + if (len == -EINVAL) { /* * Older Linux kernels don't allow reading from nvmf_dev * to get supported options, so use a default set @@ -1663,12 +1698,11 @@ static int __nvmf_supported_options(struct libnvme_global_ctx *ctx) } libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n", - nvmf_dev, libnvme_strerror(errno)); + nvmf_dev, libnvme_strerror(-len)); err = -ENVME_CONNECT_READ; goto out_free; } - buf[len] = '\0'; options = buf; libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "kernel supports: "); @@ -1868,25 +1902,24 @@ static void registry_update_on_connect(struct libnvme_global_ctx *ctx, static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr) { - __cleanup_fd int fd = -1; - int ret, len = strlen(argstr); - int instance; char buf[0x1000], *options, *p; + enum libnvmf_dev_step step; + ssize_t len; + int instance; - fd = open(nvmf_dev, O_RDWR); - if (fd < 0) { + libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, '%.*s'\n", + (int)strcspn(argstr, "\n"), argstr); + len = nvmf_dev_xfer(ctx, argstr, buf, sizeof(buf), &step); + if (len < 0 && step == LIBNVMF_DEV_OPEN) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n", - nvmf_dev, libnvme_strerror(errno)); + nvmf_dev, libnvme_strerror(-len)); return -ENVME_CONNECT_OPEN; } - libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, '%.*s'\n", - (int)strcspn(argstr,"\n"), argstr); - ret = write(fd, argstr, len); - if (ret != len) { + if (len < 0 && step == LIBNVMF_DEV_WRITE) { libnvme_msg(ctx, LIBNVME_LOG_INFO, "Failed to write to %s: %s\n", - nvmf_dev, libnvme_strerror(errno)); - switch (errno) { + nvmf_dev, libnvme_strerror(-len)); + switch (-len) { case EALREADY: return -ENVME_CONNECT_ALREADY; case EINVAL: @@ -1908,16 +1941,13 @@ static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr) } } - memset(buf, 0x0, sizeof(buf)); - len = read(fd, buf, sizeof(buf) - 1); if (len < 0) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n", - nvmf_dev, libnvme_strerror(errno)); + nvmf_dev, libnvme_strerror(-len)); return -ENVME_CONNECT_READ; } libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, response '%.*s'\n", (int)strcspn(buf, "\n"), buf); - buf[len] = '\0'; options = buf; while ((p = strsep(&options, ",\n")) != NULL) { if (!*p) diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index 5db79eb3e2..4a91ce7bd1 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -459,6 +459,13 @@ bool __libnvme_decide_retry(struct libnvme_transport_handle *hdl, int __libnvme_exec_dry_run(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd); +/* The steps of a /dev/nvme-fabrics transfer. */ +enum libnvmf_dev_step { + LIBNVMF_DEV_OPEN = 0, + LIBNVMF_DEV_WRITE = 1, + LIBNVMF_DEV_READ = 2, +}; + int __libnvme_transport_handle_open_privsep( struct libnvme_transport_handle *hdl, const char *name, int flags); From 03e25c1b2d36f140225d726a802e54b8379c0b51 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 14 Oct 2026 14:09:57 +0000 Subject: [PATCH 004/263] libnvme: forward /dev/nvme-fabrics through the privsep helper Reading the kernel options and connecting a controller need access to /dev/nvme-fabrics. Add the FABRICS op, which carries the connect arguments, or nothing to read the options, and returns the text the kernel answered or the errno and the step which failed. The helper rejects arguments with NUL bytes and longer than 4 KiB, the limit of the kernel. The test mock emulates /dev/nvme-fabrics. The client test connects through libnvmf_add_ctrl() and reads a minimal sysfs fixture for the new controller. Signed-off-by: Daniel Wagner --- libnvme/privsep/helper.c | 54 ++++++++++++ libnvme/src/nvme/fabrics.c | 4 + libnvme/src/nvme/private.h | 3 + libnvme/src/privsep/no-privsep.c | 8 ++ libnvme/src/privsep/privsep-proto.h | 15 ++++ libnvme/src/privsep/privsep.c | 60 +++++++++++++ libnvme/tests/privsep/meson.build | 3 + libnvme/tests/privsep/mock-ioctl.c | 79 ++++++++++++++++- libnvme/tests/privsep/mock.h | 8 ++ libnvme/tests/privsep/privsep.c | 129 ++++++++++++++++++++++++++++ libnvme/tests/privsep/proto.c | 40 +++++++++ 11 files changed, 401 insertions(+), 2 deletions(-) diff --git a/libnvme/privsep/helper.c b/libnvme/privsep/helper.c index 3241cc3ade..289c79cae6 100644 --- a/libnvme/privsep/helper.c +++ b/libnvme/privsep/helper.c @@ -372,6 +372,57 @@ static int handle_sed(__u32 id, void *payload, __u32 len, } #endif +static int handle_fabrics(__u32 id, const void *payload, __u32 len, + struct reply *reply, struct xfer *xfer) +{ + int fd, step; + ssize_t n; + + if (id || len >= LIBNVME_PRIVSEP_MAX_FABRICS || + (len && memchr(payload, '\0', len))) + return -EINVAL; + + xfer->data = libnvme_alloc(LIBNVME_PRIVSEP_MAX_FABRICS); + if (!xfer->data) + return -ENOMEM; + + step = LIBNVME_PRIVSEP_FABRICS_OPEN; + fd = open("/dev/nvme-fabrics", (len ? O_RDWR : O_RDONLY) | O_CLOEXEC); + if (fd < 0) + goto err; + + if (len) { + step = LIBNVME_PRIVSEP_FABRICS_WRITE; + n = write(fd, payload, len); + if (n < 0) + goto err_close; + if (n != len) { + errno = EIO; + goto err_close; + } + } + + step = LIBNVME_PRIVSEP_FABRICS_READ; + n = read(fd, xfer->data, LIBNVME_PRIVSEP_MAX_FABRICS - 1); + if (n < 0) + goto err_close; + close(fd); + + reply->len = n; + reply->payload = xfer->data; + return 0; + +err_close: + reply->status = -errno; + close(fd); + reply->result = step; + return 0; +err: + reply->status = -errno; + reply->result = step; + return 0; +} + static void close_handles(void) { int i; @@ -450,6 +501,9 @@ static int serve_one(struct libnvme_global_ctx *ctx, int sock, bool *hello) case LIBNVME_PRIVSEP_OP_SED: ret = handle_sed(id, payload, len, &reply); break; + case LIBNVME_PRIVSEP_OP_FABRICS: + ret = handle_fabrics(id, payload, len, &reply, &xfer); + break; default: ret = -EOPNOTSUPP; break; diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 9fc49868d9..18677f382d 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1634,6 +1634,10 @@ static ssize_t nvmf_dev_xfer(struct libnvme_global_ctx *ctx, __cleanup_fd int fd = -1; ssize_t len; + if (ctx->privsep) + return __libnvme_privsep_fabrics(ctx->privsep, args, buf, size, + step); + *step = LIBNVMF_DEV_OPEN; fd = open(nvmf_dev, args ? O_RDWR : O_RDONLY); if (fd < 0) diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index 4a91ce7bd1..82d66e8dbf 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -480,6 +480,9 @@ int __libnvme_privsep_op(struct libnvme_transport_handle *hdl, __u32 op, __u64 *result); int __libnvme_privsep_sed_ioctl(struct libnvme_transport_handle *hdl, __u32 request, const void *arg, size_t size); +ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, + const char *args, char *buf, size_t size, + enum libnvmf_dev_step *step); #ifdef CONFIG_MI struct nvme_mi_msg_hdr; diff --git a/libnvme/src/privsep/no-privsep.c b/libnvme/src/privsep/no-privsep.c index cddcf7837f..7db30f1259 100644 --- a/libnvme/src/privsep/no-privsep.c +++ b/libnvme/src/privsep/no-privsep.c @@ -47,3 +47,11 @@ int __libnvme_privsep_sed_ioctl(struct libnvme_transport_handle *hdl, { return -ENOTSUP; } + +ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, + const char *args, char *buf, size_t size, + enum libnvmf_dev_step *step) +{ + *step = LIBNVMF_DEV_OPEN; + return -ENOTSUP; +} diff --git a/libnvme/src/privsep/privsep-proto.h b/libnvme/src/privsep/privsep-proto.h index fc90c4deec..2634c6f47b 100644 --- a/libnvme/src/privsep/privsep-proto.h +++ b/libnvme/src/privsep/privsep-proto.h @@ -41,6 +41,12 @@ * SED requests carry a struct libnvme_privsep_sed. The helper issues only * the IOC_OPAL_* ioctls which libnvme uses. All of them pass a fixed size * argument to the kernel and return nothing but the status. + * + * FABRICS is a transfer on /dev/nvme-fabrics and uses the handle id 0. + * The payload is the connect argument string without the terminating NUL, + * or empty to read the options the kernel accepts. The response payload + * is the text the kernel returned, shorter than LIBNVME_PRIVSEP_MAX_FABRICS. + * On failure, result is the step which failed. */ #define LIBNVME_PRIVSEP_VERSION 1 @@ -50,6 +56,7 @@ #define LIBNVME_PRIVSEP_MAX_NAME 64 #define LIBNVME_PRIVSEP_MAX_REGS 4096 +#define LIBNVME_PRIVSEP_MAX_FABRICS 4096 #define LIBNVME_PRIVSEP_MAX_DATA (8 * 1024 * 1024) #define LIBNVME_PRIVSEP_MAX_PAYLOAD (LIBNVME_PRIVSEP_MAX_DATA + 4096) @@ -69,6 +76,7 @@ enum libnvme_privsep_op { LIBNVME_PRIVSEP_OP_WRITE_REG32 = 13, LIBNVME_PRIVSEP_OP_WRITE_REG64 = 14, LIBNVME_PRIVSEP_OP_SED = 15, + LIBNVME_PRIVSEP_OP_FABRICS = 16, }; struct libnvme_privsep_req { @@ -102,6 +110,13 @@ struct libnvme_privsep_block_size { __u32 rsvd; }; +/* FABRICS result: the step which failed. */ +enum libnvme_privsep_fabrics_step { + LIBNVME_PRIVSEP_FABRICS_OPEN = 0, + LIBNVME_PRIVSEP_FABRICS_WRITE = 1, + LIBNVME_PRIVSEP_FABRICS_READ = 2, +}; + /* READ_REGS, WRITE_REG32 and WRITE_REG64 payload. */ struct libnvme_privsep_reg { __u64 value; /* WRITE_REG32 and WRITE_REG64 */ diff --git a/libnvme/src/privsep/privsep.c b/libnvme/src/privsep/privsep.c index 27bdf077ff..50085fb7c2 100644 --- a/libnvme/src/privsep/privsep.c +++ b/libnvme/src/privsep/privsep.c @@ -382,3 +382,63 @@ int __libnvme_privsep_sed_ioctl(struct libnvme_transport_handle *hdl, return ret ? ret : reply.status; } + +static int privsep_fabrics_step(__u64 result, enum libnvmf_dev_step *step) +{ + switch (result) { + case LIBNVME_PRIVSEP_FABRICS_OPEN: + *step = LIBNVMF_DEV_OPEN; + return 0; + case LIBNVME_PRIVSEP_FABRICS_WRITE: + *step = LIBNVMF_DEV_WRITE; + return 0; + case LIBNVME_PRIVSEP_FABRICS_READ: + *step = LIBNVMF_DEV_READ; + return 0; + } + + return -EPROTO; +} + +ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, + const char *args, char *buf, size_t size, + enum libnvmf_dev_step *step) +{ + size_t len = args ? strlen(args) : 0; + struct privsep_reply reply; + int ret; + + *step = LIBNVMF_DEV_WRITE; + if (args && (!len || len >= LIBNVME_PRIVSEP_MAX_FABRICS)) + return -EINVAL; + + *step = LIBNVMF_DEV_OPEN; + ret = privsep_call(ps, LIBNVME_PRIVSEP_OP_FABRICS, 0, args, len, + NULL, 0, NULL, 0, &reply); + if (ret) + return ret; + + if (reply.status) { + ret = privsep_check_len(ps, &reply, 0); + if (ret) + return ret; + if (reply.status > 0 || + privsep_fabrics_step(reply.result, step)) { + ps->broken = true; + return -EPROTO; + } + return reply.status; + } + + *step = LIBNVMF_DEV_READ; + if (reply.len >= size) { + ps->broken = true; + return -EPROTO; + } + ret = privsep_recv_payload(ps, buf, reply.len); + if (ret) + return ret; + buf[reply.len] = '\0'; + + return reply.len; +} diff --git a/libnvme/tests/privsep/meson.build b/libnvme/tests/privsep/meson.build index be016d2c5b..32920c0266 100644 --- a/libnvme/tests/privsep/meson.build +++ b/libnvme/tests/privsep/meson.build @@ -9,6 +9,9 @@ privsep_test_args = [] if want_sed and conf.get('NVME_HAVE_SED_OPAL') != 0 privsep_test_args += '-DTEST_SED' endif +if want_fabrics + privsep_test_args += '-DTEST_FABRICS' +endif mock_ioctl = library( 'mock-ioctl', diff --git a/libnvme/tests/privsep/mock-ioctl.c b/libnvme/tests/privsep/mock-ioctl.c index 8fc11faba8..36ccd48186 100644 --- a/libnvme/tests/privsep/mock-ioctl.c +++ b/libnvme/tests/privsep/mock-ioctl.c @@ -25,6 +25,10 @@ * - BLKRRPART succeeds, BLKBSZSET accepts only MOCK_BLOCK_SIZE * - the BAR of nvme0 is the file named by the MOCK_RESOURCE0 environment * variable + * - /dev/nvme-fabrics returns MOCK_FABRICS_OPTIONS, or after a connect + * write MOCK_FABRICS_RESPONSE. A connect to MOCK_NQN_REFUSED fails with + * ECONNREFUSED. The connect arguments are copied to the file named by the + * MOCK_CONNECT_LOG environment variable. * - IOC_OPAL_TAKE_OWNERSHIP and IOC_OPAL_LOCK_UNLOCK on the namespace * return the TCG status MOCK_TCG_NOT_AUTHORIZED unless the key is * MOCK_SED_PASSWORD; IOC_OPAL_LOCK_UNLOCK unlocks only to read-write @@ -99,9 +103,12 @@ typedef int (*open_fn)(const char *path, int flags, ...); typedef int (*close_fn)(int fd); typedef int (*fstat_fn)(int fd, void *buf); typedef int (*ioctl_fn)(int fd, unsigned long request, ...); +typedef ssize_t (*read_fn)(int fd, void *buf, size_t count); +typedef ssize_t (*write_fn)(int fd, const void *buf, size_t count); /* The descriptors of the emulated devices, -1 if not open. */ -static int ctrl_fd = -1, ns_fd = -1; +static int ctrl_fd = -1, ns_fd = -1, fabrics_fd = -1; +static bool fabrics_connected; static int mock_ret(int err) { @@ -166,13 +173,14 @@ static int mock_open(const char *path, int flags, mode_t mode, open_fn real_open = dlsym(RTLD_NEXT, sym); bool ctrl = !strcmp(path, "/dev/nvme0"); bool ns = !strcmp(path, "/dev/nvme0n1"); + bool fabrics = !strcmp(path, "/dev/nvme-fabrics"); int fd; if (!real_open) { errno = EIO; return -1; } - if (ctrl || ns) + if (ctrl || ns || fabrics) path = "/dev/null"; else if (!strcmp(path, "/sys/class/nvme/nvme0/device/resource0") && getenv("MOCK_RESOURCE0")) @@ -183,6 +191,10 @@ static int mock_open(const char *path, int flags, mode_t mode, ctrl_fd = fd; else if (fd >= 0 && ns) ns_fd = fd; + else if (fd >= 0 && fabrics) { + fabrics_fd = fd; + fabrics_connected = false; + } return fd; } @@ -223,6 +235,8 @@ int close(int fd) ctrl_fd = -1; else if (fd == ns_fd) ns_fd = -1; + else if (fd == fabrics_fd) + fabrics_fd = -1; if (!real_close) real_close = dlsym(RTLD_NEXT, "close"); @@ -234,6 +248,67 @@ int close(int fd) return real_close(fd); } +ssize_t read(int fd, void *buf, size_t count) +{ + read_fn real_read = dlsym(RTLD_NEXT, "read"); + const char *text; + size_t len; + + if (fd < 0 || fd != fabrics_fd) { + if (!real_read) { + errno = EIO; + return -1; + } + return real_read(fd, buf, count); + } + + text = fabrics_connected ? MOCK_FABRICS_RESPONSE : MOCK_FABRICS_OPTIONS; + len = strlen(text); + if (len > count) + len = count; + memcpy(buf, text, len); + + return len; +} + +static void mock_log_connect(write_fn real_write, const void *buf, + size_t count) +{ + const char *log = getenv("MOCK_CONNECT_LOG"); + open_fn real_open = dlsym(RTLD_NEXT, "open"); + int fd; + + if (!log || !real_open) + return; + fd = real_open(log, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (fd < 0) + return; + /* A truncated log fails the test. */ + if (real_write(fd, buf, count) != (ssize_t)count) + unlink(log); + close(fd); +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + write_fn real_write = dlsym(RTLD_NEXT, "write"); + + if (!real_write) { + errno = EIO; + return -1; + } + if (fd < 0 || fd != fabrics_fd) + return real_write(fd, buf, count); + + mock_log_connect(real_write, buf, count); + if (memmem(buf, count, "nqn=" MOCK_NQN_REFUSED, + strlen("nqn=" MOCK_NQN_REFUSED))) + return mock_ret(ECONNREFUSED); + fabrics_connected = true; + + return count; +} + /* Patches only st_mode, which works for every struct stat variant. */ static int mock_fstat(int fd, void *buf, const char *sym) { diff --git a/libnvme/tests/privsep/mock.h b/libnvme/tests/privsep/mock.h index 390b5fa936..07a8324568 100644 --- a/libnvme/tests/privsep/mock.h +++ b/libnvme/tests/privsep/mock.h @@ -11,3 +11,11 @@ #define MOCK_SED_PASSWORD "password" #define MOCK_TCG_NOT_AUTHORIZED 0x01 #define MOCK_TCG_INVALID_PARAM 0x0c +#define MOCK_FABRICS_OPTIONS "instance=-1,cntlid=-1,transport=%s," \ + "traddr=%s,trsvcid=%s,nqn=%s,hostnqn=%s," \ + "hostid=%pU,host_traddr=%s,host_iface=%s," \ + "ctrl_loss_tmo=%d\n" +#define MOCK_INSTANCE 5 +#define MOCK_FABRICS_RESPONSE "instance=5,cntlid=1\n" +#define MOCK_NQN "nqn.2026-10.io.example:mock" +#define MOCK_NQN_REFUSED "nqn.2026-10.io.example:refused" diff --git a/libnvme/tests/privsep/privsep.c b/libnvme/tests/privsep/privsep.c index 1e350ab825..0b41e6d79a 100644 --- a/libnvme/tests/privsep/privsep.c +++ b/libnvme/tests/privsep/privsep.c @@ -21,6 +21,7 @@ #include +#include #include #include @@ -381,6 +382,125 @@ static void test_sed(void) } #endif +#ifdef TEST_FABRICS +static char sysfs[64], connect_log[64]; + +static void write_file(const char *dir, const char *name, const char *text) +{ + char path[256]; + int fd; + + snprintf(path, sizeof(path), "%s/%s", dir, name); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + check(fd >= 0, "create %s: %m", path); + check(!shr_write_all(fd, text, strlen(text)), "write %s: %m", path); + close(fd); +} + +/* + * The sysfs entries which libnvme_init_ctrl() reads for the controller + * the mock connects. The client process reads them, not the helper. + */ +static void create_sysfs(void) +{ + char path[256]; + int fd; + + snprintf(sysfs, sizeof(sysfs), "/tmp/test-privsep-sysfs-XXXXXX"); + check(mkdtemp(sysfs), "mkdtemp: %m"); + + snprintf(path, sizeof(path), "%s/sys/class/nvme/nvme%d", sysfs, + MOCK_INSTANCE); + check(!shr_mkdir_p(path, 0755), "mkdir %s", path); + write_file(path, "address", "traddr=192.168.1.1,trsvcid=4420\n"); + write_file(path, "state", "live\n"); + write_file(path, "subsysnqn", MOCK_NQN "\n"); + write_file(path, "transport", "tcp\n"); + + snprintf(path, sizeof(path), + "%s/sys/class/nvme-subsystem/nvme-subsys%d/nvme%d", sysfs, + MOCK_INSTANCE, MOCK_INSTANCE); + check(!shr_mkdir_p(path, 0755), "mkdir %s", path); + snprintf(path, sizeof(path), + "%s/sys/class/nvme-subsystem/nvme-subsys%d", sysfs, + MOCK_INSTANCE); + write_file(path, "subsysnqn", MOCK_NQN "\n"); + + snprintf(connect_log, sizeof(connect_log), + "/tmp/test-privsep-connect-XXXXXX"); + fd = mkstemp(connect_log); + check(fd >= 0, "mkstemp: %m"); + close(fd); + check(!setenv("MOCK_CONNECT_LOG", connect_log, 1), "setenv"); +} + +static int connect_ctrl(struct libnvme_host *h, const char *nqn, + struct libnvme_ctrl **cp) +{ + struct libnvmf_context *fctx; + int ret; + + check(!libnvmf_context_create(ctx, NULL, NULL, NULL, NULL, &fctx), + "create fabrics context"); + check(!libnvmf_context_set_connection(fctx, nqn, "tcp", + "192.168.1.1", "4420", NULL, + NULL), "set connection"); + check(!libnvmf_create_ctrl(ctx, fctx, cp), "create ctrl"); + ret = libnvmf_add_ctrl(h, *cp); + libnvmf_context_free(fctx); + + return ret; +} + +static void test_fabrics(void) +{ + static const char hostnqn[] = "nqn.2026-10.io.example:host"; + static const char hostid[] = "12345678-1234-1234-1234-123456789abc"; + struct libnvme_ctrl *c, *refused; + struct libnvme_host *h; + char args[1024] = {}; + bool supported; + ssize_t len; + int fd, ret; + + /* The mock does not list tos, unlike current kernels. */ + check(!libnvmf_kernel_option_supported(ctx, "ctrl_loss_tmo", + &supported) && supported, + "ctrl_loss_tmo not supported"); + check(!libnvmf_kernel_option_supported(ctx, "tos", &supported) && + !supported, "options not read through the helper"); + + check(!libnvme_get_host(ctx, hostnqn, hostid, &h), "get host"); + + ret = connect_ctrl(h, MOCK_NQN, &c); + check(!ret, "connect failed: %d", ret); + check(!strcmp(libnvme_ctrl_get_name(c), "nvme5"), "ctrl name %s", + libnvme_ctrl_get_name(c)); + + fd = open(connect_log, O_RDONLY); + check(fd >= 0, "open %s: %m", connect_log); + len = read(fd, args, sizeof(args) - 1); + close(fd); + check(len > 0, "connect arguments not written"); + check(strstr(args, "nqn=" MOCK_NQN) && + strstr(args, "transport=tcp") && + strstr(args, "traddr=192.168.1.1"), + "connect arguments '%s'", args); + + ret = connect_ctrl(h, MOCK_NQN_REFUSED, &refused); + check(ret == -ENVME_CONNECT_CONNREFUSED, "refused connect: %d", ret); + libnvme_free_ctrl(refused); +} +#else +static void create_sysfs(void) +{ +} + +static void test_fabrics(void) +{ +} +#endif + static int retries; static bool retry_always(struct libnvme_transport_handle *hdl, @@ -424,6 +544,7 @@ int main(int argc, char **argv) check(argc == 3, "usage: %s HELPER MOCK_LIBRARY", argv[0]); check(!setenv("LD_PRELOAD", argv[2], 1), "setenv"); create_resource0(); + create_sysfs(); test_spawn_failure(); test_closed_stdio(argv[1]); @@ -432,6 +553,9 @@ int main(int argc, char **argv) ctx = libnvme_create_global_ctx(); check(ctx, "create ctx"); libnvme_set_privsep(ctx, ps); +#ifdef TEST_FABRICS + check(!libnvme_set_test_sysfs_dir(ctx, sysfs), "set sysfs dir"); +#endif test_open_invalid(); @@ -449,11 +573,16 @@ int main(int argc, char **argv) test_device_ops(); test_regs(); test_sed(); + test_fabrics(); test_retry_broken(); libnvme_free_global_ctx(ctx); libnvme_privsep_free(ps); unlink(resource0); +#ifdef TEST_FABRICS + shr_rmdir_recursive(sysfs); + unlink(connect_log); +#endif printf("privsep tests passed\n"); return EXIT_SUCCESS; diff --git a/libnvme/tests/privsep/proto.c b/libnvme/tests/privsep/proto.c index 1429fb3e3c..91e3aeae28 100644 --- a/libnvme/tests/privsep/proto.c +++ b/libnvme/tests/privsep/proto.c @@ -346,6 +346,45 @@ static void test_sed(void) } #endif +static void test_fabrics(void) +{ + static const char refused[] = "nqn=" MOCK_NQN_REFUSED; + char buf[LIBNVME_PRIVSEP_MAX_FABRICS + 1]; + struct libnvme_privsep_resp resp; + struct helper h = start(); + + hello(&h); + + request(&h, LIBNVME_PRIVSEP_OP_FABRICS, 0, NULL, 0); + check(!response(&h, &resp), "reading the options failed"); + check(resp.len == strlen(MOCK_FABRICS_OPTIONS), + "options length %u", resp.len); + check(!libnvme_privsep_recv(h.sock, buf, resp.len), "options"); + check(!memcmp(buf, MOCK_FABRICS_OPTIONS, resp.len), "options text"); + + request(&h, LIBNVME_PRIVSEP_OP_FABRICS, 0, refused, + strlen(refused)); + check(response(&h, &resp) == -ECONNREFUSED && !resp.len, + "connect error not returned"); + check(resp.result == LIBNVME_PRIVSEP_FABRICS_WRITE, + "failed step %llu", (unsigned long long)resp.result); + + request(&h, LIBNVME_PRIVSEP_OP_FABRICS, 1, NULL, 0); + check(response(&h, &resp) == -EINVAL, "FABRICS with a handle id"); + + memcpy(buf, "nqn=a", 6); + request(&h, LIBNVME_PRIVSEP_OP_FABRICS, 0, buf, 6); + check(response(&h, &resp) == -EINVAL, "NUL in connect arguments"); + + memset(buf, 'a', sizeof(buf)); + request(&h, LIBNVME_PRIVSEP_OP_FABRICS, 0, buf, + LIBNVME_PRIVSEP_MAX_FABRICS); + check(response(&h, &resp) == -EINVAL, + "oversized connect arguments"); + + check(!stop(&h), "helper failed"); +} + int main(int argc, char **argv) { check(argc == 3, "usage: %s HELPER MOCK_LIBRARY", argv[0]); @@ -360,6 +399,7 @@ int main(int argc, char **argv) test_device_op_len(); test_regs_len(); test_sed(); + test_fabrics(); printf("privsep protocol tests passed\n"); From bf25be4d81c2b6a0037b423b31b3ad37b3c78efa Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 14 Oct 2026 14:15:48 +0000 Subject: [PATCH 005/263] libnvme: forward disconnect through the privsep helper libnvmf_disconnect_ctrl() writes the delete_controller attribute in sysfs, which needs the privileges of the helper. Add the DELETE_CTRL op, which carries the controller name. The helper accepts only nvmeX names and builds the sysfs path itself. The disconnect error message now prints the returned error instead of errno, which the helper path does not set. Signed-off-by: Daniel Wagner --- libnvme/privsep/helper.c | 43 +++++++++++++++++++++++++++++ libnvme/src/nvme/fabrics.c | 9 ++++-- libnvme/src/nvme/private.h | 2 ++ libnvme/src/privsep/no-privsep.c | 6 ++++ libnvme/src/privsep/privsep-proto.h | 5 ++++ libnvme/src/privsep/privsep.c | 21 ++++++++++++++ libnvme/tests/privsep/meson.build | 1 + libnvme/tests/privsep/mock-ioctl.c | 19 +++++++++++-- libnvme/tests/privsep/privsep.c | 38 +++++++++++++++++++------ libnvme/tests/privsep/proto.c | 34 +++++++++++++++++++++++ 10 files changed, 163 insertions(+), 15 deletions(-) diff --git a/libnvme/privsep/helper.c b/libnvme/privsep/helper.c index 289c79cae6..0bc775b1c2 100644 --- a/libnvme/privsep/helper.c +++ b/libnvme/privsep/helper.c @@ -91,6 +91,18 @@ static bool valid_name(const char *name) return !*p; } +/* Accepts the controller names nvmeX. */ +static bool valid_ctrl_name(const char *name) +{ + const char *p; + + if (strncmp(name, "nvme", 4)) + return false; + p = name + 4; + + return parse_number(&p) && !*p; +} + struct reply { int status; __u32 len; @@ -423,6 +435,34 @@ static int handle_fabrics(__u32 id, const void *payload, __u32 len, return 0; } +static int handle_delete_ctrl(__u32 id, const char *payload, __u32 len) +{ + char path[LIBNVME_PRIVSEP_MAX_NAME + 64]; + ssize_t n; + int fd; + + if (id || !len || len > LIBNVME_PRIVSEP_MAX_NAME || + payload[len - 1] || strlen(payload) != len - 1 || + !valid_ctrl_name(payload)) + return -EINVAL; + + if (snprintf(path, sizeof(path), + "/sys/class/nvme/%s/delete_controller", + payload) >= (int)sizeof(path)) + return -EINVAL; + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) + return -errno; + n = write(fd, "1", 1); + if (n < 0) + n = -errno; + else if (n != 1) + n = -EIO; + close(fd); + + return n < 0 ? n : 0; +} + static void close_handles(void) { int i; @@ -504,6 +544,9 @@ static int serve_one(struct libnvme_global_ctx *ctx, int sock, bool *hello) case LIBNVME_PRIVSEP_OP_FABRICS: ret = handle_fabrics(id, payload, len, &reply, &xfer); break; + case LIBNVME_PRIVSEP_OP_DELETE_CTRL: + ret = handle_delete_ctrl(id, payload, len); + break; default: ret = -EOPNOTSUPP; break; diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 18677f382d..7b24cf84e5 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -2107,11 +2107,14 @@ __shr_public int libnvmf_disconnect_ctrl(struct libnvme_ctrl *c) struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL; int ret; - ret = libnvme_set_attr(libnvme_ctrl_get_sysfs_dir(c), - "delete_controller", "1"); + if (ctx && ctx->privsep) + ret = __libnvme_privsep_delete_ctrl(ctx->privsep, c->name); + else + ret = libnvme_set_attr(libnvme_ctrl_get_sysfs_dir(c), + "delete_controller", "1"); if (ret < 0) { libnvme_msg(ctx, LIBNVME_LOG_ERR, - "%s: failed to disconnect, error %d\n", c->name, errno); + "%s: failed to disconnect, error %d\n", c->name, -ret); return ret; } libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: %s disconnected\n", diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index 82d66e8dbf..ab045103a0 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -483,6 +483,8 @@ int __libnvme_privsep_sed_ioctl(struct libnvme_transport_handle *hdl, ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, const char *args, char *buf, size_t size, enum libnvmf_dev_step *step); +int __libnvme_privsep_delete_ctrl(struct libnvme_privsep *ps, + const char *name); #ifdef CONFIG_MI struct nvme_mi_msg_hdr; diff --git a/libnvme/src/privsep/no-privsep.c b/libnvme/src/privsep/no-privsep.c index 7db30f1259..88f12552dd 100644 --- a/libnvme/src/privsep/no-privsep.c +++ b/libnvme/src/privsep/no-privsep.c @@ -55,3 +55,9 @@ ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, *step = LIBNVMF_DEV_OPEN; return -ENOTSUP; } + +int __libnvme_privsep_delete_ctrl(struct libnvme_privsep *ps, + const char *name) +{ + return -ENOTSUP; +} diff --git a/libnvme/src/privsep/privsep-proto.h b/libnvme/src/privsep/privsep-proto.h index 2634c6f47b..2206abd022 100644 --- a/libnvme/src/privsep/privsep-proto.h +++ b/libnvme/src/privsep/privsep-proto.h @@ -47,6 +47,10 @@ * or empty to read the options the kernel accepts. The response payload * is the text the kernel returned, shorter than LIBNVME_PRIVSEP_MAX_FABRICS. * On failure, result is the step which failed. + * + * DELETE_CTRL disconnects a fabrics controller like + * libnvmf_disconnect_ctrl() and uses the handle id 0. The payload is the + * NUL-terminated controller name, nvmeX. */ #define LIBNVME_PRIVSEP_VERSION 1 @@ -77,6 +81,7 @@ enum libnvme_privsep_op { LIBNVME_PRIVSEP_OP_WRITE_REG64 = 14, LIBNVME_PRIVSEP_OP_SED = 15, LIBNVME_PRIVSEP_OP_FABRICS = 16, + LIBNVME_PRIVSEP_OP_DELETE_CTRL = 17, }; struct libnvme_privsep_req { diff --git a/libnvme/src/privsep/privsep.c b/libnvme/src/privsep/privsep.c index 50085fb7c2..04999b1e8b 100644 --- a/libnvme/src/privsep/privsep.c +++ b/libnvme/src/privsep/privsep.c @@ -442,3 +442,24 @@ ssize_t __libnvme_privsep_fabrics(struct libnvme_privsep *ps, return reply.len; } + +int __libnvme_privsep_delete_ctrl(struct libnvme_privsep *ps, + const char *name) +{ + struct privsep_reply reply; + size_t len; + int ret; + + if (!name) + return -EINVAL; + len = strlen(name) + 1; + if (len > LIBNVME_PRIVSEP_MAX_NAME) + return -ENAMETOOLONG; + + ret = privsep_call(ps, LIBNVME_PRIVSEP_OP_DELETE_CTRL, 0, name, len, + NULL, 0, NULL, 0, &reply); + if (!ret) + ret = privsep_check_len(ps, &reply, 0); + + return ret ? ret : reply.status; +} diff --git a/libnvme/tests/privsep/meson.build b/libnvme/tests/privsep/meson.build index 32920c0266..3691fdb366 100644 --- a/libnvme/tests/privsep/meson.build +++ b/libnvme/tests/privsep/meson.build @@ -50,6 +50,7 @@ privsep_proto_test = executable( ['proto.c', '../ioctl/util.c'], c_args: privsep_test_args, dependencies: [ + ccan_dep, config_dep, libnvme_dep, ], diff --git a/libnvme/tests/privsep/mock-ioctl.c b/libnvme/tests/privsep/mock-ioctl.c index 36ccd48186..33608edd82 100644 --- a/libnvme/tests/privsep/mock-ioctl.c +++ b/libnvme/tests/privsep/mock-ioctl.c @@ -29,6 +29,9 @@ * write MOCK_FABRICS_RESPONSE. A connect to MOCK_NQN_REFUSED fails with * ECONNREFUSED. The connect arguments are copied to the file named by the * MOCK_CONNECT_LOG environment variable. + * - the delete_controller attribute of nvme5 is the file named by the + * MOCK_DELETE_CTRL environment variable; writes to the one of nvme6 + * return 0 * - IOC_OPAL_TAKE_OWNERSHIP and IOC_OPAL_LOCK_UNLOCK on the namespace * return the TCG status MOCK_TCG_NOT_AUTHORIZED unless the key is * MOCK_SED_PASSWORD; IOC_OPAL_LOCK_UNLOCK unlocks only to read-write @@ -107,7 +110,7 @@ typedef ssize_t (*read_fn)(int fd, void *buf, size_t count); typedef ssize_t (*write_fn)(int fd, const void *buf, size_t count); /* The descriptors of the emulated devices, -1 if not open. */ -static int ctrl_fd = -1, ns_fd = -1, fabrics_fd = -1; +static int ctrl_fd = -1, ns_fd = -1, fabrics_fd = -1, short_fd = -1; static bool fabrics_connected; static int mock_ret(int err) @@ -174,17 +177,22 @@ static int mock_open(const char *path, int flags, mode_t mode, bool ctrl = !strcmp(path, "/dev/nvme0"); bool ns = !strcmp(path, "/dev/nvme0n1"); bool fabrics = !strcmp(path, "/dev/nvme-fabrics"); + bool short_write = + !strcmp(path, "/sys/class/nvme/nvme6/delete_controller"); int fd; if (!real_open) { errno = EIO; return -1; } - if (ctrl || ns || fabrics) + if (ctrl || ns || fabrics || short_write) path = "/dev/null"; else if (!strcmp(path, "/sys/class/nvme/nvme0/device/resource0") && getenv("MOCK_RESOURCE0")) path = getenv("MOCK_RESOURCE0"); + else if (!strcmp(path, "/sys/class/nvme/nvme5/delete_controller") && + getenv("MOCK_DELETE_CTRL")) + path = getenv("MOCK_DELETE_CTRL"); fd = real_open(path, flags, mode); if (fd >= 0 && ctrl) @@ -194,7 +202,8 @@ static int mock_open(const char *path, int flags, mode_t mode, else if (fd >= 0 && fabrics) { fabrics_fd = fd; fabrics_connected = false; - } + } else if (fd >= 0 && short_write) + short_fd = fd; return fd; } @@ -237,6 +246,8 @@ int close(int fd) ns_fd = -1; else if (fd == fabrics_fd) fabrics_fd = -1; + else if (fd == short_fd) + short_fd = -1; if (!real_close) real_close = dlsym(RTLD_NEXT, "close"); @@ -297,6 +308,8 @@ ssize_t write(int fd, const void *buf, size_t count) errno = EIO; return -1; } + if (fd >= 0 && fd == short_fd) + return 0; if (fd < 0 || fd != fabrics_fd) return real_write(fd, buf, count); diff --git a/libnvme/tests/privsep/privsep.c b/libnvme/tests/privsep/privsep.c index 0b41e6d79a..50d7cebd80 100644 --- a/libnvme/tests/privsep/privsep.c +++ b/libnvme/tests/privsep/privsep.c @@ -383,7 +383,7 @@ static void test_sed(void) #endif #ifdef TEST_FABRICS -static char sysfs[64], connect_log[64]; +static char sysfs[64], connect_log[64], delete_ctrl[64]; static void write_file(const char *dir, const char *name, const char *text) { @@ -432,6 +432,26 @@ static void create_sysfs(void) check(fd >= 0, "mkstemp: %m"); close(fd); check(!setenv("MOCK_CONNECT_LOG", connect_log, 1), "setenv"); + + snprintf(delete_ctrl, sizeof(delete_ctrl), + "/tmp/test-privsep-delete-XXXXXX"); + fd = mkstemp(delete_ctrl); + check(fd >= 0, "mkstemp: %m"); + close(fd); + check(!setenv("MOCK_DELETE_CTRL", delete_ctrl, 1), "setenv"); +} + +static void read_file(const char *path, char *buf, size_t size) +{ + ssize_t len; + int fd; + + fd = open(path, O_RDONLY); + check(fd >= 0, "open %s: %m", path); + len = read(fd, buf, size - 1); + check(len >= 0, "read %s: %m", path); + buf[len] = '\0'; + close(fd); } static int connect_ctrl(struct libnvme_host *h, const char *nqn, @@ -458,10 +478,9 @@ static void test_fabrics(void) static const char hostid[] = "12345678-1234-1234-1234-123456789abc"; struct libnvme_ctrl *c, *refused; struct libnvme_host *h; - char args[1024] = {}; + char args[1024]; bool supported; - ssize_t len; - int fd, ret; + int ret; /* The mock does not list tos, unlike current kernels. */ check(!libnvmf_kernel_option_supported(ctx, "ctrl_loss_tmo", @@ -477,11 +496,7 @@ static void test_fabrics(void) check(!strcmp(libnvme_ctrl_get_name(c), "nvme5"), "ctrl name %s", libnvme_ctrl_get_name(c)); - fd = open(connect_log, O_RDONLY); - check(fd >= 0, "open %s: %m", connect_log); - len = read(fd, args, sizeof(args) - 1); - close(fd); - check(len > 0, "connect arguments not written"); + read_file(connect_log, args, sizeof(args)); check(strstr(args, "nqn=" MOCK_NQN) && strstr(args, "transport=tcp") && strstr(args, "traddr=192.168.1.1"), @@ -490,6 +505,10 @@ static void test_fabrics(void) ret = connect_ctrl(h, MOCK_NQN_REFUSED, &refused); check(ret == -ENVME_CONNECT_CONNREFUSED, "refused connect: %d", ret); libnvme_free_ctrl(refused); + + check(!libnvmf_disconnect_ctrl(c), "disconnect failed"); + read_file(delete_ctrl, args, sizeof(args)); + check(!strcmp(args, "1"), "delete_controller '%s'", args); } #else static void create_sysfs(void) @@ -582,6 +601,7 @@ int main(int argc, char **argv) #ifdef TEST_FABRICS shr_rmdir_recursive(sysfs); unlink(connect_log); + unlink(delete_ctrl); #endif printf("privsep tests passed\n"); diff --git a/libnvme/tests/privsep/proto.c b/libnvme/tests/privsep/proto.c index 91e3aeae28..2d5deaf1f7 100644 --- a/libnvme/tests/privsep/proto.c +++ b/libnvme/tests/privsep/proto.c @@ -24,6 +24,8 @@ #include #endif +#include + #include "privsep/privsep-proto.h" #include "../ioctl/util.h" @@ -385,6 +387,37 @@ static void test_fabrics(void) check(!stop(&h), "helper failed"); } +static void test_delete_ctrl(void) +{ + static const char * const names[] = { + "nvme0n1", "ng0n1", "nvme", "nvme0/../../../../tmp/x", + "../nvme0", "nvme2147483648", "", + }; + struct libnvme_privsep_resp resp; + struct helper h = start(); + size_t i; + + hello(&h); + + for (i = 0; i < ARRAY_SIZE(names); i++) { + request(&h, LIBNVME_PRIVSEP_OP_DELETE_CTRL, 0, names[i], + strlen(names[i]) + 1); + check(response(&h, &resp) == -EINVAL, + "controller name '%s' accepted", names[i]); + } + + request(&h, LIBNVME_PRIVSEP_OP_DELETE_CTRL, 0, "nvme5", 5); + check(response(&h, &resp) == -EINVAL, "unterminated name accepted"); + + request(&h, LIBNVME_PRIVSEP_OP_DELETE_CTRL, 1, "nvme5", 6); + check(response(&h, &resp) == -EINVAL, "DELETE_CTRL with a handle id"); + + request(&h, LIBNVME_PRIVSEP_OP_DELETE_CTRL, 0, "nvme6", 6); + check(response(&h, &resp) == -EIO, "short delete_controller write"); + + check(!stop(&h), "helper failed"); +} + int main(int argc, char **argv) { check(argc == 3, "usage: %s HELPER MOCK_LIBRARY", argv[0]); @@ -400,6 +433,7 @@ int main(int argc, char **argv) test_regs_len(); test_sed(); test_fabrics(); + test_delete_ctrl(); printf("privsep protocol tests passed\n"); From 715d5d5cded462547e8c0ef87524e2a70e44389f Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Sun, 11 Oct 2026 11:04:12 -0400 Subject: [PATCH 006/263] keysd: add a KMIP TTLV encoder and decoder KMIP encodes its messages as TTLV items: a tag, a type, a length and a padded value. This is the base of the KMIP key source. The encoder clears its buffer when it grows and when it is freed, because KMIP messages can carry keys. Signed-off-by: Martin Belanger --- keysd/src/ttlv.c | 351 ++++++++++++++++++++++++++++++++++++++++ keysd/src/ttlv.h | 103 ++++++++++++ keysd/tests/meson.build | 8 + keysd/tests/test-ttlv.c | 265 ++++++++++++++++++++++++++++++ 4 files changed, 727 insertions(+) create mode 100644 keysd/src/ttlv.c create mode 100644 keysd/src/ttlv.h create mode 100644 keysd/tests/test-ttlv.c diff --git a/keysd/src/ttlv.c b/keysd/src/ttlv.c new file mode 100644 index 0000000000..aae30b61ca --- /dev/null +++ b/keysd/src/ttlv.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include + +#include "ttlv.h" + +static size_t pad8(size_t len) +{ + return (len + 7) & ~(size_t)7; +} + +static void put_be32(uint8_t *p, uint32_t v) +{ + p[0] = v >> 24; + p[1] = v >> 16; + p[2] = v >> 8; + p[3] = v; +} + +static void put_be64(uint8_t *p, uint64_t v) +{ + put_be32(p, v >> 32); + put_be32(p + 4, v); +} + +static uint32_t get_be32(const uint8_t *p) +{ + return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | + (uint32_t)p[2] << 8 | p[3]; +} + +static uint64_t get_be64(const uint8_t *p) +{ + return (uint64_t)get_be32(p) << 32 | get_be32(p + 4); +} + +void ttlv_buf_init(struct ttlv_buf *b) +{ + memset(b, 0, sizeof(*b)); +} + +void ttlv_buf_free(struct ttlv_buf *b) +{ + if (b->data) { + explicit_bzero(b->data, b->cap); + free(b->data); + } + ttlv_buf_init(b); +} + +/* Reserve @len zeroed bytes at the end. Returns NULL on error. */ +static uint8_t *reserve(struct ttlv_buf *b, size_t len) +{ + uint8_t *p; + + if (b->err) + return NULL; + + if (len > b->cap - b->len) { + size_t cap = b->cap ? b->cap : 256; + + while (len > cap - b->len) { + if (cap > SIZE_MAX / 2) { + b->err = -ENOMEM; + return NULL; + } + cap *= 2; + } + // Not realloc(): it frees the old copy without clearing it. + p = malloc(cap); + if (!p) { + b->err = -ENOMEM; + return NULL; + } + if (b->data) { + memcpy(p, b->data, b->len); + explicit_bzero(b->data, b->cap); + free(b->data); + } + b->data = p; + b->cap = cap; + } + + p = b->data + b->len; + memset(p, 0, len); + b->len += len; + + return p; +} + +/* Add an item with a value of @len bytes. Returns the value or NULL. */ +static uint8_t *add_item(struct ttlv_buf *b, uint32_t tag, uint8_t type, + size_t len) +{ + uint8_t *p; + + if (len > UINT32_MAX) { + if (!b->err) + b->err = -EOVERFLOW; + return NULL; + } + + p = reserve(b, TTLV_HDR_LEN + pad8(len)); + if (!p) + return NULL; + + put_be32(p, tag << 8 | type); + put_be32(p + 4, len); + + return p + TTLV_HDR_LEN; +} + +void ttlv_begin(struct ttlv_buf *b, uint32_t tag) +{ + if (b->err) + return; + if (b->depth == TTLV_MAX_DEPTH) { + b->err = -E2BIG; + return; + } + + b->open[b->depth] = b->len; + if (add_item(b, tag, TTLV_STRUCTURE, 0)) + b->depth++; +} + +void ttlv_end(struct ttlv_buf *b) +{ + size_t start, len; + + if (b->err) + return; + if (!b->depth) { + b->err = -EINVAL; + return; + } + + start = b->open[--b->depth]; + len = b->len - start - TTLV_HDR_LEN; + if (len > UINT32_MAX) { + b->err = -EOVERFLOW; + return; + } + put_be32(b->data + start + 4, len); +} + +void ttlv_add_int(struct ttlv_buf *b, uint32_t tag, int32_t v) +{ + uint8_t *p = add_item(b, tag, TTLV_INTEGER, 4); + + if (p) + put_be32(p, v); +} + +void ttlv_add_long(struct ttlv_buf *b, uint32_t tag, int64_t v) +{ + uint8_t *p = add_item(b, tag, TTLV_LONG, 8); + + if (p) + put_be64(p, v); +} + +void ttlv_add_enum(struct ttlv_buf *b, uint32_t tag, uint32_t v) +{ + uint8_t *p = add_item(b, tag, TTLV_ENUM, 4); + + if (p) + put_be32(p, v); +} + +void ttlv_add_bool(struct ttlv_buf *b, uint32_t tag, bool v) +{ + uint8_t *p = add_item(b, tag, TTLV_BOOL, 8); + + if (p) + put_be64(p, v); +} + +static void add_string(struct ttlv_buf *b, uint32_t tag, uint8_t type, + const void *v, size_t len) +{ + uint8_t *p = add_item(b, tag, type, len); + + if (p && len) + memcpy(p, v, len); +} + +void ttlv_add_text(struct ttlv_buf *b, uint32_t tag, const char *s) +{ + add_string(b, tag, TTLV_TEXT, s, strlen(s)); +} + +void ttlv_add_bytes(struct ttlv_buf *b, uint32_t tag, const void *v, + size_t len) +{ + add_string(b, tag, TTLV_BYTES, v, len); +} + +void ttlv_add_date_time(struct ttlv_buf *b, uint32_t tag, int64_t v) +{ + uint8_t *p = add_item(b, tag, TTLV_DATE_TIME, 8); + + if (p) + put_be64(p, v); +} + +void ttlv_add_interval(struct ttlv_buf *b, uint32_t tag, uint32_t v) +{ + uint8_t *p = add_item(b, tag, TTLV_INTERVAL, 4); + + if (p) + put_be32(p, v); +} + +void ttlv_reader_init(struct ttlv_reader *r, const void *buf, size_t len) +{ + static const uint8_t empty; + + // Pointer arithmetic on NULL is undefined, even with an offset of 0. + r->p = len ? buf : ∅ + r->end = r->p + len; +} + +/* The fixed value length of @type, 0 for a variable length. */ +static uint32_t fixed_len(uint8_t type) +{ + switch (type) { + case TTLV_INTEGER: + case TTLV_ENUM: + case TTLV_INTERVAL: + return 4; + case TTLV_LONG: + case TTLV_BOOL: + case TTLV_DATE_TIME: + return 8; + default: + return 0; + } +} + +int ttlv_next(struct ttlv_reader *r, struct ttlv_item *it) +{ + size_t avail = r->end - r->p; + uint32_t hdr, fixed; + size_t padded; + + if (!avail) + return 0; + if (avail < TTLV_HDR_LEN) + return -EBADMSG; + + hdr = get_be32(r->p); + it->tag = hdr >> 8; + it->type = hdr & 0xff; + it->len = get_be32(r->p + 4); + + if (it->type < TTLV_STRUCTURE || it->type > TTLV_INTERVAL) + return -EBADMSG; + + fixed = fixed_len(it->type); + if (fixed && it->len != fixed) + return -EBADMSG; + if ((it->type == TTLV_STRUCTURE || it->type == TTLV_BIG_INT) && + it->len % 8) + return -EBADMSG; + + padded = pad8(it->len); + if (padded > avail - TTLV_HDR_LEN) + return -EBADMSG; + + it->val = r->p + TTLV_HDR_LEN; + r->p = it->val + padded; + + return 1; +} + +int ttlv_find(struct ttlv_reader *r, uint32_t tag, struct ttlv_item *it) +{ + int ret; + + while ((ret = ttlv_next(r, it)) > 0) { + if (it->tag == tag) + return 1; + } + + return ret; +} + +int ttlv_open(const struct ttlv_item *it, struct ttlv_reader *r) +{ + if (it->type != TTLV_STRUCTURE) + return -EBADMSG; + + ttlv_reader_init(r, it->val, it->len); + + return 0; +} + +int ttlv_get_int(const struct ttlv_item *it, int32_t *v) +{ + if (it->type != TTLV_INTEGER) + return -EBADMSG; + + *v = (int32_t)get_be32(it->val); + + return 0; +} + +int ttlv_get_long(const struct ttlv_item *it, int64_t *v) +{ + if (it->type != TTLV_LONG) + return -EBADMSG; + + *v = (int64_t)get_be64(it->val); + + return 0; +} + +int ttlv_get_enum(const struct ttlv_item *it, uint32_t *v) +{ + if (it->type != TTLV_ENUM) + return -EBADMSG; + + *v = get_be32(it->val); + + return 0; +} + +int ttlv_get_bool(const struct ttlv_item *it, bool *v) +{ + uint64_t raw; + + if (it->type != TTLV_BOOL) + return -EBADMSG; + + raw = get_be64(it->val); + if (raw > 1) + return -EBADMSG; + + *v = raw; + + return 0; +} diff --git a/keysd/src/ttlv.h b/keysd/src/ttlv.h new file mode 100644 index 0000000000..5ef3df6f00 --- /dev/null +++ b/keysd/src/ttlv.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include +#include + +/* + * KMIP TTLV encoding. Each item is a 3-byte tag, a 1-byte type, a 4-byte + * length and a value padded to a multiple of 8 bytes. All numbers are + * big-endian. The encoding is the same in all KMIP versions (KMIP 1.4, + * section 9.1). + */ + +enum ttlv_type { + TTLV_STRUCTURE = 0x01, + TTLV_INTEGER = 0x02, + TTLV_LONG = 0x03, + TTLV_BIG_INT = 0x04, + TTLV_ENUM = 0x05, + TTLV_BOOL = 0x06, + TTLV_TEXT = 0x07, + TTLV_BYTES = 0x08, + TTLV_DATE_TIME = 0x09, + TTLV_INTERVAL = 0x0a, +}; + +#define TTLV_HDR_LEN 8 +#define TTLV_MAX_DEPTH 16 + +/* + * Encoder. Writes into a buffer that grows as needed. The first error is + * kept in @err and makes all later calls do nothing, so a caller checks + * it once at the end. The buffer may hold secrets: it is cleared when it + * grows and when it is freed. + */ +struct ttlv_buf { + uint8_t *data; + size_t len; + size_t cap; + int err; // 0 or a negative errno + int depth; + size_t open[TTLV_MAX_DEPTH]; // offsets of the open structures +}; + +void ttlv_buf_init(struct ttlv_buf *b); +void ttlv_buf_free(struct ttlv_buf *b); + +void ttlv_begin(struct ttlv_buf *b, uint32_t tag); +void ttlv_end(struct ttlv_buf *b); + +void ttlv_add_int(struct ttlv_buf *b, uint32_t tag, int32_t v); +void ttlv_add_long(struct ttlv_buf *b, uint32_t tag, int64_t v); +void ttlv_add_enum(struct ttlv_buf *b, uint32_t tag, uint32_t v); +void ttlv_add_bool(struct ttlv_buf *b, uint32_t tag, bool v); +void ttlv_add_text(struct ttlv_buf *b, uint32_t tag, const char *s); +void ttlv_add_bytes(struct ttlv_buf *b, uint32_t tag, const void *p, + size_t len); +void ttlv_add_date_time(struct ttlv_buf *b, uint32_t tag, int64_t v); +void ttlv_add_interval(struct ttlv_buf *b, uint32_t tag, uint32_t v); + +/* + * Decoder. Reads items from a buffer without copying. An item's value + * points into the buffer, so the caller clears the buffer if it holds a + * secret. + */ +struct ttlv_item { + uint32_t tag; + uint8_t type; + uint32_t len; // value length without padding + const uint8_t *val; +}; + +struct ttlv_reader { + const uint8_t *p; + const uint8_t *end; +}; + +void ttlv_reader_init(struct ttlv_reader *r, const void *buf, size_t len); + +/* + * Read the next item. Returns 1 and fills @it, 0 at the end of the + * buffer, or -EBADMSG if the item is malformed or does not fit. + */ +int ttlv_next(struct ttlv_reader *r, struct ttlv_item *it); + +/* Find the next item with @tag. Returns as ttlv_next(), 0 if not found. */ +int ttlv_find(struct ttlv_reader *r, uint32_t tag, struct ttlv_item *it); + +/* Read the items of structure @it. Returns -EBADMSG if not a structure. */ +int ttlv_open(const struct ttlv_item *it, struct ttlv_reader *r); + +/* Return -EBADMSG if @it does not have the expected type. */ +int ttlv_get_int(const struct ttlv_item *it, int32_t *v); +int ttlv_get_long(const struct ttlv_item *it, int64_t *v); +int ttlv_get_enum(const struct ttlv_item *it, uint32_t *v); +int ttlv_get_bool(const struct ttlv_item *it, bool *v); diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index a849f1cab1..4b857bc926 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -13,6 +13,14 @@ test_config = executable( test('nvme-keysd - config', test_config) +test_ttlv = executable( + 'test-keysd-ttlv', + ['test-ttlv.c', '../src/ttlv.c'], + include_directories: keysd_inc, +) + +test('nvme-keysd - ttlv', test_ttlv) + test('nvme-keysd - --help', nvme_keysd, args: ['--help']) test('nvme-keysd - unknown option', nvme_keysd, args: ['--bogus'], should_fail: true) diff --git a/keysd/tests/test-ttlv.c b/keysd/tests/test-ttlv.c new file mode 100644 index 0000000000..c40757fd50 --- /dev/null +++ b/keysd/tests/test-ttlv.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include + +#include "ttlv.h" + +#define TAG 0x420020 + +static bool check(bool cond, const char *what) +{ + printf(" - %s [%s]\n", what, cond ? "PASS" : "FAIL"); + return cond; +} + +static bool check_bytes(const struct ttlv_buf *b, const uint8_t *exp, + size_t len, const char *what) +{ + return check(!b->err && b->len == len && !memcmp(b->data, exp, len), + what); +} + +/* The examples of KMIP 1.4, section 9.1.2. */ +static bool test_encode_spec_examples(void) +{ + static const uint8_t integer[] = { + 0x42, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t long_int[] = { + 0x42, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x08, + 0x01, 0xb6, 0x9b, 0x4b, 0xa5, 0x74, 0x92, 0x00, + }; + static const uint8_t enumeration[] = { + 0x42, 0x00, 0x20, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t boolean[] = { + 0x42, 0x00, 0x20, 0x06, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + static const uint8_t text[] = { + 0x42, 0x00, 0x20, 0x07, 0x00, 0x00, 0x00, 0x0b, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, + 0x72, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t bytes[] = { + 0x42, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t date_time[] = { + 0x42, 0x00, 0x20, 0x09, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x47, 0xda, 0x67, 0xf8, + }; + static const uint8_t interval[] = { + 0x42, 0x00, 0x20, 0x0a, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x0d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t structure[] = { + 0x42, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x20, + 0x42, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t raw[] = { 0x01, 0x02, 0x03 }; + struct ttlv_buf b; + bool pass = true; + + printf("test_encode_spec_examples:\n"); + +#define ENCODE(call, exp, what) \ + do { \ + ttlv_buf_init(&b); \ + call; \ + pass &= check_bytes(&b, exp, sizeof(exp), what); \ + ttlv_buf_free(&b); \ + } while (0) + + ENCODE(ttlv_add_int(&b, TAG, 8), integer, "Integer"); + ENCODE(ttlv_add_long(&b, TAG, 123456789000000000LL), long_int, + "Long Integer"); + ENCODE(ttlv_add_enum(&b, TAG, 255), enumeration, "Enumeration"); + ENCODE(ttlv_add_bool(&b, TAG, true), boolean, "Boolean"); + ENCODE(ttlv_add_text(&b, TAG, "Hello World"), text, "Text String"); + ENCODE(ttlv_add_bytes(&b, TAG, raw, sizeof(raw)), bytes, + "Byte String"); + ENCODE(ttlv_add_date_time(&b, TAG, 0x47da67f8), date_time, + "Date-Time"); + ENCODE(ttlv_add_interval(&b, TAG, 10 * 86400), interval, "Interval"); + ENCODE(ttlv_begin(&b, TAG); + ttlv_add_enum(&b, 0x420004, 254); + ttlv_add_int(&b, 0x420005, 255); + ttlv_end(&b), structure, "Structure"); + +#undef ENCODE + + return pass; +} + +static bool test_encode_errors(void) +{ + struct ttlv_buf b; + bool pass = true; + int i; + + printf("test_encode_errors:\n"); + + ttlv_buf_init(&b); + ttlv_end(&b); + pass &= check(b.err == -EINVAL, "end without begin"); + ttlv_add_int(&b, TAG, 1); + pass &= check(b.len == 0, "no output after an error"); + ttlv_buf_free(&b); + + ttlv_buf_init(&b); + for (i = 0; i <= TTLV_MAX_DEPTH; i++) + ttlv_begin(&b, TAG); + pass &= check(b.err == -E2BIG, "nesting deeper than the maximum"); + ttlv_buf_free(&b); + + return pass; +} + +static bool test_decode_round_trip(void) +{ + struct ttlv_reader r, sub; + struct ttlv_item it; + struct ttlv_buf b; + bool pass = true; + uint32_t e; + int32_t i; + int64_t l; + bool v; + + printf("test_decode_round_trip:\n"); + + ttlv_buf_init(&b); + ttlv_begin(&b, 0x420078); + ttlv_add_int(&b, 0x42006a, -2); + ttlv_add_text(&b, 0x420055, "abc"); + ttlv_begin(&b, 0x42007c); + ttlv_add_enum(&b, 0x42005c, 10); + ttlv_end(&b); + ttlv_add_long(&b, 0x420092, -1); + ttlv_add_bool(&b, 0x420007, false); + ttlv_end(&b); + pass &= check(!b.err, "encode"); + + ttlv_reader_init(&r, b.data, b.len); + pass &= check(ttlv_next(&r, &it) == 1 && it.tag == 0x420078 && + ttlv_open(&it, &sub) == 0, "outer structure"); + pass &= check(ttlv_next(&r, &it) == 0, "nothing after it"); + + pass &= check(ttlv_next(&sub, &it) == 1 && + ttlv_get_int(&it, &i) == 0 && i == -2, "Integer -2"); + pass &= check(ttlv_next(&sub, &it) == 1 && it.type == TTLV_TEXT && + it.len == 3 && !memcmp(it.val, "abc", 3), "Text String"); + pass &= check(ttlv_find(&sub, 0x420092, &it) == 1 && + ttlv_get_long(&it, &l) == 0 && l == -1, + "find skips the nested structure"); + pass &= check(ttlv_next(&sub, &it) == 1 && + ttlv_get_bool(&it, &v) == 0 && !v, "Boolean false"); + pass &= check(ttlv_next(&sub, &it) == 0, "end of structure"); + + ttlv_reader_init(&r, b.data, b.len); + ttlv_next(&r, &it); + ttlv_open(&it, &sub); + pass &= check(ttlv_find(&sub, 0x42007c, &it) == 1 && + ttlv_open(&it, &r) == 0 && ttlv_next(&r, &it) == 1 && + ttlv_get_enum(&it, &e) == 0 && e == 10, + "nested Enumeration"); + pass &= check(ttlv_get_int(&it, &i) == -EBADMSG, + "get with the wrong type"); + + ttlv_buf_free(&b); + + return pass; +} + +static int decode_one(const uint8_t *buf, size_t len) +{ + struct ttlv_reader r; + struct ttlv_item it; + + ttlv_reader_init(&r, buf, len); + + return ttlv_next(&r, &it); +} + +static bool test_decode_malformed(void) +{ + static const uint8_t short_hdr[] = { 0x42, 0x00, 0x20, 0x02 }; + static const uint8_t bad_type[] = { + 0x42, 0x00, 0x20, 0x0b, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t bad_int_len[] = { + 0x42, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t bad_struct_len[] = { + 0x42, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t truncated_value[] = { + 0x42, 0x00, 0x20, 0x07, 0x00, 0x00, 0x00, 0x09, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + }; + static const uint8_t huge_len[] = { + 0x42, 0x00, 0x20, 0x08, 0xff, 0xff, 0xff, 0xff, + }; + static const uint8_t bad_bool[] = { + 0x42, 0x00, 0x20, 0x06, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }; + struct ttlv_reader r; + struct ttlv_item it; + bool pass = true; + bool v; + + printf("test_decode_malformed:\n"); + + pass &= check(decode_one(NULL, 0) == 0, "empty buffer"); + pass &= check(decode_one(short_hdr, sizeof(short_hdr)) == -EBADMSG, + "header shorter than 8 bytes"); + pass &= check(decode_one(bad_type, sizeof(bad_type)) == -EBADMSG, + "unknown type"); + pass &= check(decode_one(bad_int_len, sizeof(bad_int_len)) == + -EBADMSG, "Integer with length 8"); + pass &= check(decode_one(bad_struct_len, sizeof(bad_struct_len)) == + -EBADMSG, "Structure length not a multiple of 8"); + pass &= check(decode_one(truncated_value, sizeof(truncated_value)) == + -EBADMSG, "padded value past the end"); + pass &= check(decode_one(huge_len, sizeof(huge_len)) == -EBADMSG, + "length 0xffffffff"); + + ttlv_reader_init(&r, bad_bool, sizeof(bad_bool)); + pass &= check(ttlv_next(&r, &it) == 1 && + ttlv_get_bool(&it, &v) == -EBADMSG, + "Boolean value other than 0 or 1"); + + return pass; +} + +int main(void) +{ + bool pass = true; + + pass &= test_encode_spec_examples(); + pass &= test_encode_errors(); + pass &= test_decode_round_trip(); + pass &= test_decode_malformed(); + + fflush(stdout); + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} From a798d5dc2e1701cf5bb7bb9a8910a43285cd0f15 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 09:13:48 -0400 Subject: [PATCH 007/263] keysd: add the KMIP Locate and Get messages nvme-keysd finds a key on the KMS by its name with Locate, then reads it with Get. Locate can also match on the State of the key, for key rotation. Locate asks for up to two matches. KMIP does not require names to be unique, and a name that matches two keys is reported as an error instead of using one of them. Get accepts a Symmetric Key or Secret Data in Raw or Opaque format. Other objects and formats are rejected. The protocol version is a parameter. Requests use KMIP 1.4. A response may use any minor version of the same major version, as the specification requires. Signed-off-by: Martin Belanger --- keysd/src/kmip.c | 309 +++++++++++++++++++++++++++ keysd/src/kmip.h | 139 +++++++++++++ keysd/tests/kmip-responses.c | 74 +++++++ keysd/tests/kmip-responses.h | 39 ++++ keysd/tests/meson.build | 8 + keysd/tests/test-kmip.c | 390 +++++++++++++++++++++++++++++++++++ 6 files changed, 959 insertions(+) create mode 100644 keysd/src/kmip.c create mode 100644 keysd/src/kmip.h create mode 100644 keysd/tests/kmip-responses.c create mode 100644 keysd/tests/kmip-responses.h create mode 100644 keysd/tests/test-kmip.c diff --git a/keysd/src/kmip.c b/keysd/src/kmip.c new file mode 100644 index 0000000000..06e8ca4e64 --- /dev/null +++ b/keysd/src/kmip.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include + +#include "kmip.h" + +/* Ask for two matches, to find out if a name is ambiguous. */ +#define LOCATE_MAX_ITEMS 2 + +const struct kmip_version kmip_v1_4 = { .major = 1, .minor = 4 }; + +static void begin_request(struct ttlv_buf *b, const struct kmip_version *v, + enum kmip_op op) +{ + ttlv_begin(b, KMIP_TAG_REQUEST_MESSAGE); + + ttlv_begin(b, KMIP_TAG_REQUEST_HEADER); + ttlv_begin(b, KMIP_TAG_PROTOCOL_VERSION); + ttlv_add_int(b, KMIP_TAG_PROTOCOL_VERSION_MAJOR, v->major); + ttlv_add_int(b, KMIP_TAG_PROTOCOL_VERSION_MINOR, v->minor); + ttlv_end(b); + ttlv_add_int(b, KMIP_TAG_BATCH_COUNT, 1); + ttlv_end(b); + + ttlv_begin(b, KMIP_TAG_BATCH_ITEM); + ttlv_add_enum(b, KMIP_TAG_OPERATION, op); + ttlv_begin(b, KMIP_TAG_REQUEST_PAYLOAD); +} + +static int end_request(struct ttlv_buf *b) +{ + ttlv_end(b); // Request Payload + ttlv_end(b); // Batch Item + ttlv_end(b); // Request Message + + return b->err; +} + +/* Add a KMIP 1.x Name attribute with @name as Uninterpreted Text. */ +static void add_name_attr_v1(struct ttlv_buf *b, const char *name) +{ + ttlv_begin(b, KMIP_TAG_ATTRIBUTE); + ttlv_add_text(b, KMIP_TAG_ATTRIBUTE_NAME, "Name"); + ttlv_begin(b, KMIP_TAG_ATTRIBUTE_VALUE); + ttlv_add_text(b, KMIP_TAG_NAME_VALUE, name); + ttlv_add_enum(b, KMIP_TAG_NAME_TYPE, KMIP_NAME_UNINTERPRETED_TEXT); + ttlv_end(b); // Attribute Value + ttlv_end(b); // Attribute +} + +/* Add a KMIP 1.x State attribute. */ +static void add_state_attr_v1(struct ttlv_buf *b, enum kmip_state state) +{ + ttlv_begin(b, KMIP_TAG_ATTRIBUTE); + ttlv_add_text(b, KMIP_TAG_ATTRIBUTE_NAME, "State"); + ttlv_add_enum(b, KMIP_TAG_ATTRIBUTE_VALUE, state); + ttlv_end(b); // Attribute +} + +int kmip_build_locate(struct ttlv_buf *b, const struct kmip_version *v, + const char *name, enum kmip_state state) +{ + if (v->major != 1) + return -EOPNOTSUPP; + + begin_request(b, v, KMIP_OP_LOCATE); + ttlv_add_int(b, KMIP_TAG_MAXIMUM_ITEMS, LOCATE_MAX_ITEMS); + add_name_attr_v1(b, name); + if (state != KMIP_STATE_ANY) + add_state_attr_v1(b, state); + + return end_request(b); +} + +int kmip_build_get(struct ttlv_buf *b, const struct kmip_version *v, + const char *uid) +{ + begin_request(b, v, KMIP_OP_GET); + ttlv_add_text(b, KMIP_TAG_UNIQUE_IDENTIFIER, uid); + + return end_request(b); +} + +/* Find the structure with @tag in @r and open it in @sub. */ +static int open_struct(struct ttlv_reader *r, enum kmip_tag tag, + struct ttlv_reader *sub) +{ + struct ttlv_item it; + int ret; + + ret = ttlv_find(r, tag, &it); + if (ret <= 0) + return -EBADMSG; + + return ttlv_open(&it, sub); +} + +static int find_enum(struct ttlv_reader *r, enum kmip_tag tag, uint32_t *v) +{ + struct ttlv_item it; + + if (ttlv_find(r, tag, &it) <= 0) + return -EBADMSG; + + return ttlv_get_enum(&it, v); +} + +static void copy_text(char *dst, size_t size, const struct ttlv_item *it) +{ + size_t len = it->len < size - 1 ? it->len : size - 1; + + memcpy(dst, it->val, len); + dst[len] = '\0'; +} + +/* Read the reason and message of a failed batch item. Both are optional. */ +static void read_failure(const struct ttlv_reader *item, + struct kmip_result *res) +{ + struct ttlv_reader r = *item; + struct ttlv_item it; + + if (ttlv_find(&r, KMIP_TAG_RESULT_REASON, &it) > 0) + ttlv_get_enum(&it, &res->reason); + + r = *item; + if (ttlv_find(&r, KMIP_TAG_RESULT_MESSAGE, &it) > 0 && + it.type == TTLV_TEXT) + copy_text(res->message, sizeof(res->message), &it); +} + +/* + * A server answers with any minor version of the major version of the + * request (KMIP 1.4, section 6.1). + */ +static int check_version(struct ttlv_reader *hdr, const struct kmip_version *v) +{ + struct ttlv_reader ver; + struct ttlv_item it; + int32_t major; + + if (open_struct(hdr, KMIP_TAG_PROTOCOL_VERSION, &ver) || + ttlv_find(&ver, KMIP_TAG_PROTOCOL_VERSION_MAJOR, &it) <= 0 || + ttlv_get_int(&it, &major)) + return -EBADMSG; + + return major == v->major ? 0 : -EPROTO; +} + +/* + * Check the response to a request for operation @op and protocol version + * @v, and open its payload in @payload. + */ +static int open_response(const void *buf, size_t len, + const struct kmip_version *v, enum kmip_op op, + struct ttlv_reader *payload, struct kmip_result *res) +{ + struct ttlv_reader r, msg, hdr, item; + struct ttlv_item it; + uint32_t resp_op; + int32_t count; + int ret; + + memset(res, 0, sizeof(*res)); + + ttlv_reader_init(&r, buf, len); + ret = open_struct(&r, KMIP_TAG_RESPONSE_MESSAGE, &msg); + if (ret) + return ret; + + r = msg; + ret = open_struct(&r, KMIP_TAG_RESPONSE_HEADER, &hdr); + if (ret) + return ret; + r = hdr; + ret = check_version(&r, v); + if (ret) + return ret; + if (ttlv_find(&hdr, KMIP_TAG_BATCH_COUNT, &it) <= 0 || + ttlv_get_int(&it, &count) || count != 1) + return -EBADMSG; + + ret = open_struct(&msg, KMIP_TAG_BATCH_ITEM, &item); + if (ret) + return ret; + + /* + * A message the server cannot parse fails without an Operation + * (KMIP 1.4, section 11.1), so read the Result Status first. + */ + r = item; + if (find_enum(&r, KMIP_TAG_RESULT_STATUS, &res->status)) + return -EBADMSG; + if (res->status != KMIP_STATUS_SUCCESS) { + read_failure(&item, res); + return -EREMOTEIO; + } + + r = item; + if (find_enum(&r, KMIP_TAG_OPERATION, &resp_op) || resp_op != op) + return -EBADMSG; + + r = item; + + return open_struct(&r, KMIP_TAG_RESPONSE_PAYLOAD, payload); +} + +int kmip_parse_locate(const void *buf, size_t len, + const struct kmip_version *v, char *uid, size_t uid_size, + struct kmip_result *res) +{ + struct ttlv_reader payload; + struct ttlv_item it, next; + int ret; + + ret = open_response(buf, len, v, KMIP_OP_LOCATE, &payload, res); + if (ret) + return ret; + + ret = ttlv_find(&payload, KMIP_TAG_UNIQUE_IDENTIFIER, &it); + if (ret < 0) + return ret; + if (!ret) + return -ENOENT; + if (it.type != TTLV_TEXT || !it.len || memchr(it.val, '\0', it.len)) + return -EBADMSG; + + ret = ttlv_find(&payload, KMIP_TAG_UNIQUE_IDENTIFIER, &next); + if (ret < 0) + return ret; + if (ret) + return -ENOTUNIQ; + + if (it.len >= uid_size) + return -EMSGSIZE; + copy_text(uid, uid_size, &it); + + return 0; +} + +/* Copy the key material of the Key Block @kb to @key. */ +static int read_key_block(struct ttlv_reader *kb, uint8_t *key, + size_t key_size, size_t *key_len) +{ + struct ttlv_reader r = *kb, kv; + struct ttlv_item it; + uint32_t format; + + if (find_enum(&r, KMIP_TAG_KEY_FORMAT_TYPE, &format)) + return -EBADMSG; + if (format != KMIP_KEY_FORMAT_RAW && format != KMIP_KEY_FORMAT_OPAQUE) + return -EOPNOTSUPP; + + r = *kb; + if (open_struct(&r, KMIP_TAG_KEY_VALUE, &kv)) + return -EBADMSG; + if (ttlv_find(&kv, KMIP_TAG_KEY_MATERIAL, &it) <= 0 || + it.type != TTLV_BYTES || !it.len) + return -EBADMSG; + + if (it.len > key_size) + return -EMSGSIZE; + memcpy(key, it.val, it.len); + *key_len = it.len; + + return 0; +} + +int kmip_parse_get(const void *buf, size_t len, + const struct kmip_version *v, uint32_t *obj_type, uint8_t *key, + size_t key_size, size_t *key_len, struct kmip_result *res) +{ + struct ttlv_reader payload, r, obj, kb; + enum kmip_tag tag; + int ret; + + ret = open_response(buf, len, v, KMIP_OP_GET, &payload, res); + if (ret) + return ret; + + r = payload; + if (find_enum(&r, KMIP_TAG_OBJECT_TYPE, obj_type)) + return -EBADMSG; + + switch (*obj_type) { + case KMIP_OBJ_SYMMETRIC_KEY: + tag = KMIP_TAG_SYMMETRIC_KEY; + break; + case KMIP_OBJ_SECRET_DATA: + tag = KMIP_TAG_SECRET_DATA; + break; + default: + return -EOPNOTSUPP; + } + + r = payload; + if (open_struct(&r, tag, &obj) || open_struct(&obj, KMIP_TAG_KEY_BLOCK, + &kb)) + return -EBADMSG; + + return read_key_block(&kb, key, key_size, key_len); +} diff --git a/keysd/src/kmip.h b/keysd/src/kmip.h new file mode 100644 index 0000000000..1e41890886 --- /dev/null +++ b/keysd/src/kmip.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +#include "ttlv.h" + +/* + * KMIP 1.4 requests and responses: Locate a key by name, and Get it. + * Requests carry one batch item. + */ + +/* Protocol Version (KMIP 1.4, section 6.1) */ +struct kmip_version { + int32_t major; + int32_t minor; +}; + +extern const struct kmip_version kmip_v1_4; + +/* Tags (KMIP 1.4, section 9.1.3.1) */ +enum kmip_tag { + KMIP_TAG_ATTRIBUTE = 0x420008, + KMIP_TAG_ATTRIBUTE_NAME = 0x42000a, + KMIP_TAG_ATTRIBUTE_VALUE = 0x42000b, + KMIP_TAG_BATCH_COUNT = 0x42000d, + KMIP_TAG_BATCH_ITEM = 0x42000f, + KMIP_TAG_KEY_BLOCK = 0x420040, + KMIP_TAG_KEY_FORMAT_TYPE = 0x420042, + KMIP_TAG_KEY_MATERIAL = 0x420043, + KMIP_TAG_KEY_VALUE = 0x420045, + KMIP_TAG_MAXIMUM_ITEMS = 0x42004f, + KMIP_TAG_NAME_TYPE = 0x420054, + KMIP_TAG_NAME_VALUE = 0x420055, + KMIP_TAG_OBJECT_TYPE = 0x420057, + KMIP_TAG_OPERATION = 0x42005c, + KMIP_TAG_PROTOCOL_VERSION = 0x420069, + KMIP_TAG_PROTOCOL_VERSION_MAJOR = 0x42006a, + KMIP_TAG_PROTOCOL_VERSION_MINOR = 0x42006b, + KMIP_TAG_REQUEST_HEADER = 0x420077, + KMIP_TAG_REQUEST_MESSAGE = 0x420078, + KMIP_TAG_REQUEST_PAYLOAD = 0x420079, + KMIP_TAG_RESPONSE_HEADER = 0x42007a, + KMIP_TAG_RESPONSE_MESSAGE = 0x42007b, + KMIP_TAG_RESPONSE_PAYLOAD = 0x42007c, + KMIP_TAG_RESULT_MESSAGE = 0x42007d, + KMIP_TAG_RESULT_REASON = 0x42007e, + KMIP_TAG_RESULT_STATUS = 0x42007f, + KMIP_TAG_SECRET_DATA = 0x420085, + KMIP_TAG_STATE = 0x42008d, + KMIP_TAG_SYMMETRIC_KEY = 0x42008f, + KMIP_TAG_TIME_STAMP = 0x420092, + KMIP_TAG_UNIQUE_IDENTIFIER = 0x420094, +}; + +/* Operation (KMIP 1.4, section 9.1.3.2.27) */ +enum kmip_op { + KMIP_OP_LOCATE = 0x08, + KMIP_OP_GET = 0x0a, +}; + +/* Object Type (KMIP 1.4, section 9.1.3.2.12) */ +enum kmip_obj_type { + KMIP_OBJ_SYMMETRIC_KEY = 0x02, + KMIP_OBJ_SECRET_DATA = 0x07, +}; + +/* Result Status (KMIP 1.4, section 9.1.3.2.28) */ +enum kmip_result_status { + KMIP_STATUS_SUCCESS = 0x00, +}; + +/* State (KMIP 1.4, section 9.1.3.2.18) */ +enum kmip_state { + KMIP_STATE_ANY = 0x00, // not a KMIP value: no State filter + KMIP_STATE_ACTIVE = 0x02, +}; + +/* Name Type (KMIP 1.4, section 9.1.3.2.11) */ +enum kmip_name_type { + KMIP_NAME_UNINTERPRETED_TEXT = 0x01, +}; + +/* Key Format Type (KMIP 1.4, section 9.1.3.2.3) */ +enum kmip_key_format { + KMIP_KEY_FORMAT_RAW = 0x01, + KMIP_KEY_FORMAT_OPAQUE = 0x02, +}; + +/* The result of a failed operation, for the log. */ +struct kmip_result { + uint32_t status; + uint32_t reason; // 0 if the response has none + char message[128]; // empty if the response has none +}; + +/* + * Build a request for protocol version @v. Returns 0 or a negative errno: + * -EOPNOTSUPP if Locate does not support the major version of @v. + * + * Locate matches objects named @name and, unless @state is + * KMIP_STATE_ANY, in @state. + */ +int kmip_build_locate(struct ttlv_buf *b, const struct kmip_version *v, + const char *name, enum kmip_state state); +int kmip_build_get(struct ttlv_buf *b, const struct kmip_version *v, + const char *uid); + +/* + * Parse a Locate response to a request for protocol version @v, and copy + * the Unique Identifier of the only match to @uid. Returns 0, or: + * -EREMOTEIO the operation failed, see @res + * -EPROTO the response has another major protocol version + * -ENOENT no object has this name + * -ENOTUNIQ more than one object has this name + * -EMSGSIZE @uid is too small + * -EBADMSG malformed response + */ +int kmip_parse_locate(const void *buf, size_t len, + const struct kmip_version *v, char *uid, size_t uid_size, + struct kmip_result *res); + +/* + * Parse a Get response, set @obj_type (a KMIP_OBJ_* value) and copy the + * key material to @key. The caller clears @key after use. Returns 0, or + * -EREMOTEIO, -EPROTO, -EMSGSIZE, -EBADMSG as kmip_parse_locate(), or: + * -EOPNOTSUPP not a Symmetric Key or Secret Data, or a key + * format other than Raw or Opaque + */ +int kmip_parse_get(const void *buf, size_t len, + const struct kmip_version *v, uint32_t *obj_type, uint8_t *key, + size_t key_size, size_t *key_len, struct kmip_result *res); diff --git a/keysd/tests/kmip-responses.c b/keysd/tests/kmip-responses.c new file mode 100644 index 0000000000..10300e0c82 --- /dev/null +++ b/keysd/tests/kmip-responses.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include "kmip-responses.h" + +void begin_response_version(struct ttlv_buf *b, int32_t major, + int32_t minor, int32_t batch_count, uint32_t op, + uint32_t status) +{ + ttlv_buf_init(b); + ttlv_begin(b, KMIP_TAG_RESPONSE_MESSAGE); + ttlv_begin(b, KMIP_TAG_RESPONSE_HEADER); + ttlv_begin(b, KMIP_TAG_PROTOCOL_VERSION); + ttlv_add_int(b, KMIP_TAG_PROTOCOL_VERSION_MAJOR, major); + ttlv_add_int(b, KMIP_TAG_PROTOCOL_VERSION_MINOR, minor); + ttlv_end(b); + ttlv_add_date_time(b, KMIP_TAG_TIME_STAMP, 1760000000); + ttlv_add_int(b, KMIP_TAG_BATCH_COUNT, batch_count); + ttlv_end(b); + ttlv_begin(b, KMIP_TAG_BATCH_ITEM); + if (op) + ttlv_add_enum(b, KMIP_TAG_OPERATION, op); + ttlv_add_enum(b, KMIP_TAG_RESULT_STATUS, status); +} + +void begin_response(struct ttlv_buf *b, int32_t batch_count, + uint32_t op, uint32_t status) +{ + begin_response_version(b, 1, 4, batch_count, op, status); +} + +void end_response(struct ttlv_buf *b) +{ + ttlv_end(b); // Batch Item + ttlv_end(b); // Response Message +} + +void locate_response(struct ttlv_buf *b, int n) +{ + static const char *const uids[] = { "uid-1", "uid-2" }; + + begin_response(b, 1, KMIP_OP_LOCATE, KMIP_STATUS_SUCCESS); + ttlv_begin(b, KMIP_TAG_RESPONSE_PAYLOAD); + for (int i = 0; i < n; i++) + ttlv_add_text(b, KMIP_TAG_UNIQUE_IDENTIFIER, uids[i]); + ttlv_end(b); + end_response(b); +} + +void get_response(struct ttlv_buf *b, uint32_t obj_type, + uint32_t obj_tag, uint32_t format, const void *key, size_t len) +{ + begin_response(b, 1, KMIP_OP_GET, KMIP_STATUS_SUCCESS); + ttlv_begin(b, KMIP_TAG_RESPONSE_PAYLOAD); + ttlv_add_enum(b, KMIP_TAG_OBJECT_TYPE, obj_type); + ttlv_add_text(b, KMIP_TAG_UNIQUE_IDENTIFIER, "uid-1"); + ttlv_begin(b, obj_tag); + if (obj_type == KMIP_OBJ_SECRET_DATA) + ttlv_add_enum(b, 0x420086, 1); // Secret Data Type: Password + ttlv_begin(b, KMIP_TAG_KEY_BLOCK); + ttlv_add_enum(b, KMIP_TAG_KEY_FORMAT_TYPE, format); + ttlv_begin(b, KMIP_TAG_KEY_VALUE); + ttlv_add_bytes(b, KMIP_TAG_KEY_MATERIAL, key, len); + ttlv_end(b); + ttlv_end(b); + ttlv_end(b); + ttlv_end(b); + end_response(b); +} diff --git a/keysd/tests/kmip-responses.h b/keysd/tests/kmip-responses.h new file mode 100644 index 0000000000..96538d6a5e --- /dev/null +++ b/keysd/tests/kmip-responses.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +#include "kmip.h" + +/* KMIP responses for the tests. Each one initializes @b. */ + +/* + * Start a response with protocol version @major.@minor, and a batch item + * for @op (none if 0) with @status. The caller adds the rest. + */ +void begin_response_version(struct ttlv_buf *b, int32_t major, + int32_t minor, int32_t batch_count, uint32_t op, + uint32_t status); + +/* Start a KMIP 1.4 response to @op with @status. */ +void begin_response(struct ttlv_buf *b, int32_t batch_count, + uint32_t op, uint32_t status); + +void end_response(struct ttlv_buf *b); + +/* A successful Locate response with @n Unique Identifiers (at most 2). */ +void locate_response(struct ttlv_buf *b, int n); + +/* + * A successful Get response, as in the KMIP 1.4 test case + * TC-CREATE-SD-1-14. + */ +void get_response(struct ttlv_buf *b, uint32_t obj_type, + uint32_t obj_tag, uint32_t format, const void *key, size_t len); diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index 4b857bc926..d6e36142c1 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -21,6 +21,14 @@ test_ttlv = executable( test('nvme-keysd - ttlv', test_ttlv) +test_kmip = executable( + 'test-keysd-kmip', + ['test-kmip.c', 'kmip-responses.c', '../src/kmip.c', '../src/ttlv.c'], + include_directories: keysd_inc, +) + +test('nvme-keysd - kmip', test_kmip) + test('nvme-keysd - --help', nvme_keysd, args: ['--help']) test('nvme-keysd - unknown option', nvme_keysd, args: ['--bogus'], should_fail: true) diff --git a/keysd/tests/test-kmip.c b/keysd/tests/test-kmip.c new file mode 100644 index 0000000000..94e35712d5 --- /dev/null +++ b/keysd/tests/test-kmip.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include + +#include "kmip.h" +#include "kmip-responses.h" + +static bool check(bool cond, const char *what) +{ + printf(" - %s [%s]\n", what, cond ? "PASS" : "FAIL"); + return cond; +} + +/* Open the structure with @tag in @r. */ +static bool enter(struct ttlv_reader *r, enum kmip_tag tag, + struct ttlv_reader *sub) +{ + struct ttlv_item it; + + return ttlv_find(r, tag, &it) == 1 && ttlv_open(&it, sub) == 0; +} + +static bool next_int(struct ttlv_reader *r, enum kmip_tag tag, int32_t exp) +{ + struct ttlv_item it; + int32_t v; + + return ttlv_next(r, &it) == 1 && it.tag == tag && + ttlv_get_int(&it, &v) == 0 && v == exp; +} + +static bool next_enum(struct ttlv_reader *r, enum kmip_tag tag, uint32_t exp) +{ + struct ttlv_item it; + uint32_t v; + + return ttlv_next(r, &it) == 1 && it.tag == tag && + ttlv_get_enum(&it, &v) == 0 && v == exp; +} + +static bool next_text(struct ttlv_reader *r, enum kmip_tag tag, + const char *exp) +{ + struct ttlv_item it; + + return ttlv_next(r, &it) == 1 && it.tag == tag && + it.type == TTLV_TEXT && it.len == strlen(exp) && + !memcmp(it.val, exp, it.len); +} + +/* Check the request header and batch item, and open the payload. */ +static bool check_request(const struct ttlv_buf *b, enum kmip_op op, + struct ttlv_reader *payload) +{ + struct ttlv_reader r, msg, hdr, ver, item; + bool pass = true; + + ttlv_reader_init(&r, b->data, b->len); + pass &= check(enter(&r, KMIP_TAG_REQUEST_MESSAGE, &msg), + "Request Message"); + + pass &= check(enter(&msg, KMIP_TAG_REQUEST_HEADER, &hdr) && + enter(&hdr, KMIP_TAG_PROTOCOL_VERSION, &ver) && + next_int(&ver, KMIP_TAG_PROTOCOL_VERSION_MAJOR, 1) && + next_int(&ver, KMIP_TAG_PROTOCOL_VERSION_MINOR, 4), + "Protocol Version 1.4"); + pass &= check(next_int(&hdr, KMIP_TAG_BATCH_COUNT, 1), "Batch Count 1"); + + pass &= check(enter(&msg, KMIP_TAG_BATCH_ITEM, &item) && + next_enum(&item, KMIP_TAG_OPERATION, op), "Operation"); + pass &= check(enter(&item, KMIP_TAG_REQUEST_PAYLOAD, payload), + "Request Payload"); + + return pass; +} + +/* Check a Locate request up to its Name attribute. */ +static bool check_locate(const struct ttlv_buf *b, struct ttlv_reader *payload) +{ + struct ttlv_reader attr, value; + bool pass = true; + + pass &= check_request(b, KMIP_OP_LOCATE, payload); + pass &= check(next_int(payload, KMIP_TAG_MAXIMUM_ITEMS, 2), + "Maximum Items 2"); + pass &= check(enter(payload, KMIP_TAG_ATTRIBUTE, &attr) && + next_text(&attr, KMIP_TAG_ATTRIBUTE_NAME, "Name"), + "Attribute Name"); + pass &= check(enter(&attr, KMIP_TAG_ATTRIBUTE_VALUE, &value) && + next_text(&value, KMIP_TAG_NAME_VALUE, "nvme-psk-1") && + next_enum(&value, KMIP_TAG_NAME_TYPE, + KMIP_NAME_UNINTERPRETED_TEXT), + "Name Value and Name Type"); + + return pass; +} + +static bool test_build_locate(void) +{ + static const struct kmip_version v2_0 = { .major = 2, .minor = 0 }; + struct ttlv_reader payload, attr; + struct ttlv_item it; + struct ttlv_buf b; + bool pass = true; + + printf("test_build_locate:\n"); + + ttlv_buf_init(&b); + pass &= check(kmip_build_locate(&b, &kmip_v1_4, "nvme-psk-1", + KMIP_STATE_ANY) == 0, + "build without State"); + pass &= check_locate(&b, &payload); + pass &= check(ttlv_next(&payload, &it) == 0, "nothing else"); + ttlv_buf_free(&b); + + ttlv_buf_init(&b); + pass &= check(kmip_build_locate(&b, &kmip_v1_4, "nvme-psk-1", + KMIP_STATE_ACTIVE) == 0, + "build with State Active"); + pass &= check_locate(&b, &payload); + pass &= check(enter(&payload, KMIP_TAG_ATTRIBUTE, &attr) && + next_text(&attr, KMIP_TAG_ATTRIBUTE_NAME, "State") && + next_enum(&attr, KMIP_TAG_ATTRIBUTE_VALUE, + KMIP_STATE_ACTIVE), + "State Active"); + pass &= check(ttlv_next(&payload, &it) == 0, "nothing else"); + ttlv_buf_free(&b); + + ttlv_buf_init(&b); + pass &= check(kmip_build_locate(&b, &v2_0, "nvme-psk-1", + KMIP_STATE_ANY) == + -EOPNOTSUPP, "Protocol Version 2.0 not supported"); + ttlv_buf_free(&b); + + return pass; +} + +static bool test_build_get(void) +{ + struct ttlv_reader payload; + struct ttlv_item it; + struct ttlv_buf b; + bool pass = true; + + printf("test_build_get:\n"); + + ttlv_buf_init(&b); + pass &= check(kmip_build_get(&b, &kmip_v1_4, "uid-1") == 0, "build"); + pass &= check_request(&b, KMIP_OP_GET, &payload); + pass &= check(next_text(&payload, KMIP_TAG_UNIQUE_IDENTIFIER, + "uid-1"), "Unique Identifier"); + pass &= check(ttlv_next(&payload, &it) == 0, "nothing else"); + + ttlv_buf_free(&b); + + return pass; +} + +static int parse_locate(struct ttlv_buf *b, char *uid, size_t size, + struct kmip_result *res) +{ + int ret = kmip_parse_locate(b->data, b->len, &kmip_v1_4, uid, size, + res); + + ttlv_buf_free(b); + + return ret; +} + +static bool test_parse_locate(void) +{ + struct kmip_result res; + struct ttlv_buf b; + bool pass = true; + char uid[64]; + + printf("test_parse_locate:\n"); + + locate_response(&b, 1); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == 0 && + !strcmp(uid, "uid-1"), "one match"); + + locate_response(&b, 0); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -ENOENT, + "no match"); + + locate_response(&b, 2); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -ENOTUNIQ, + "two matches"); + + locate_response(&b, 1); + pass &= check(parse_locate(&b, uid, 5, &res) == -EMSGSIZE, + "buffer too small for the identifier"); + + begin_response(&b, 1, KMIP_OP_LOCATE, 1); + ttlv_add_enum(&b, KMIP_TAG_RESULT_REASON, 1); + ttlv_add_text(&b, KMIP_TAG_RESULT_MESSAGE, "no such object"); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == + -EREMOTEIO && res.status == 1 && res.reason == 1 && + !strcmp(res.message, "no such object"), + "failed operation with reason and message"); + + begin_response(&b, 1, KMIP_OP_LOCATE, 1); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == + -EREMOTEIO && res.reason == 0 && !res.message[0], + "failed operation without reason or message"); + + begin_response(&b, 1, KMIP_OP_GET, KMIP_STATUS_SUCCESS); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -EBADMSG, + "response to another operation"); + + begin_response(&b, 2, KMIP_OP_LOCATE, KMIP_STATUS_SUCCESS); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -EBADMSG, + "Batch Count 2"); + + begin_response(&b, 1, KMIP_OP_LOCATE, KMIP_STATUS_SUCCESS); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -EBADMSG, + "no Response Payload"); + + /* A server that cannot parse the request names no Operation. */ + begin_response(&b, 1, 0, 1); + ttlv_add_enum(&b, KMIP_TAG_RESULT_REASON, 4); // Invalid Message + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == + -EREMOTEIO && res.reason == 4, + "failed operation without Operation"); + + begin_response_version(&b, 1, 2, 1, KMIP_OP_LOCATE, + KMIP_STATUS_SUCCESS); + ttlv_begin(&b, KMIP_TAG_RESPONSE_PAYLOAD); + ttlv_add_text(&b, KMIP_TAG_UNIQUE_IDENTIFIER, "uid-1"); + ttlv_end(&b); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == 0 && + !strcmp(uid, "uid-1"), "Protocol Version 1.2"); + + begin_response_version(&b, 2, 0, 1, KMIP_OP_LOCATE, + KMIP_STATUS_SUCCESS); + end_response(&b); + pass &= check(parse_locate(&b, uid, sizeof(uid), &res) == -EPROTO, + "Protocol Version 2.0"); + + locate_response(&b, 1); + pass &= check(kmip_parse_locate(b.data, b.len - 8, &kmip_v1_4, uid, + sizeof(uid), &res) == -EBADMSG, + "truncated response"); + ttlv_buf_free(&b); + + return pass; +} + +static int parse_get(struct ttlv_buf *b, uint32_t *type, uint8_t *key, + size_t size, size_t *len) +{ + struct kmip_result res; + int ret; + + ret = kmip_parse_get(b->data, b->len, &kmip_v1_4, type, key, size, + len, &res); + ttlv_buf_free(b); + + return ret; +} + +/* A PSK interchange string, as stored in Secret Data. */ +static const char psk[] = + "NVMeTLSkey-1:01:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwNf9LrZ:"; + +static bool test_parse_get(void) +{ + uint8_t raw[32], key[128]; + struct ttlv_buf b; + bool pass = true; + uint32_t type; + size_t len; + + printf("test_parse_get:\n"); + + memset(raw, 0xa5, sizeof(raw)); + + get_response(&b, KMIP_OBJ_SECRET_DATA, KMIP_TAG_SECRET_DATA, + KMIP_KEY_FORMAT_OPAQUE, psk, strlen(psk)); + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == 0 && + type == KMIP_OBJ_SECRET_DATA && len == strlen(psk) && + !memcmp(key, psk, len), + "Secret Data, Opaque: interchange string"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, raw, sizeof(raw)); + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == 0 && + type == KMIP_OBJ_SYMMETRIC_KEY && len == sizeof(raw) && + !memcmp(key, raw, len), "Symmetric Key, Raw"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + 0x07, raw, sizeof(raw)); + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == + -EOPNOTSUPP, "Transparent Symmetric Key format"); + + get_response(&b, 0x01, 0x420013, KMIP_KEY_FORMAT_RAW, raw, + sizeof(raw)); // Certificate + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == + -EOPNOTSUPP, "Certificate object"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, raw, sizeof(raw)); + pass &= check(parse_get(&b, &type, key, 16, &len) == -EMSGSIZE, + "buffer too small for the key"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SECRET_DATA, + KMIP_KEY_FORMAT_RAW, raw, sizeof(raw)); + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == + -EBADMSG, "object does not match the Object Type"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, raw, 0); + pass &= check(parse_get(&b, &type, key, sizeof(key), &len) == + -EBADMSG, "empty Key Material"); + + return pass; +} + +/* Every prefix of a valid response is rejected, and read within bounds. */ +static bool test_truncated(void) +{ + struct kmip_result res; + uint8_t key[64]; + struct ttlv_buf b; + bool pass = true; + uint32_t type; + char uid[64]; + size_t len; + + printf("test_truncated:\n"); + + locate_response(&b, 1); + for (size_t n = 0; n < b.len; n++) { + uint8_t *copy = malloc(n ?: 1); + + memcpy(copy, b.data, n); + pass &= kmip_parse_locate(copy, n, &kmip_v1_4, uid, + sizeof(uid), &res) < 0; + free(copy); + } + ttlv_buf_free(&b); + pass &= check(pass, "Locate response prefixes"); + + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, "0123456789abcdef", 16); + for (size_t n = 0; n < b.len; n++) { + uint8_t *copy = malloc(n ?: 1); + + memcpy(copy, b.data, n); + pass &= kmip_parse_get(copy, n, &kmip_v1_4, &type, key, + sizeof(key), &len, &res) < 0; + free(copy); + } + ttlv_buf_free(&b); + pass &= check(pass, "Get response prefixes"); + + return pass; +} + +int main(void) +{ + bool pass = true; + + pass &= test_build_locate(); + pass &= test_build_get(); + pass &= test_parse_locate(); + pass &= test_parse_get(); + pass &= test_truncated(); + + fflush(stdout); + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} From 5c698c7f86597350c5d02cc078394741c219eb09 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 13:11:01 -0400 Subject: [PATCH 008/263] keysd: add a KMIP client over TLS nvme-keysd will get PSKs from a KMIP server. The server is reached over TLS. The server certificate is always verified, and a client certificate is used when one is configured. One timeout covers the connect, the TLS handshake and the exchange. The response is read with a size limit. Signed-off-by: Martin Belanger --- keysd/src/kmip-tls.c | 432 ++++++++++++++++++++++++++++++++++++ keysd/src/kmip-tls.h | 61 +++++ keysd/tests/meson.build | 12 + keysd/tests/test-kmip-tls.c | 382 +++++++++++++++++++++++++++++++ keysd/tests/tls-server.c | 287 ++++++++++++++++++++++++ keysd/tests/tls-server.h | 56 +++++ 6 files changed, 1230 insertions(+) create mode 100644 keysd/src/kmip-tls.c create mode 100644 keysd/src/kmip-tls.h create mode 100644 keysd/tests/test-kmip-tls.c create mode 100644 keysd/tests/tls-server.c create mode 100644 keysd/tests/tls-server.h diff --git a/keysd/src/kmip-tls.c b/keysd/src/kmip-tls.c new file mode 100644 index 0000000000..efe6926a6c --- /dev/null +++ b/keysd/src/kmip-tls.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "kmip.h" +#include "kmip-tls.h" + +#define KMIP_CONNECT_TIMEOUT_MS 3000 + +struct kmip_tls { + SSL_CTX *ctx; + SSL *ssl; + int fd; + int64_t deadline; // CLOCK_MONOTONIC, in ms + char *server; +}; + +static int64_t now_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +/* Wait until @fd is ready for @events. */ +static int wait_fd(int fd, short events, int64_t deadline) +{ + struct pollfd pfd = { .fd = fd, .events = events }; + int64_t left; + int ret; + + for (;;) { + left = deadline - now_ms(); + if (left <= 0) + return -ETIMEDOUT; + + ret = poll(&pfd, 1, (int)left); + if (ret > 0) + return 0; + if (!ret) + return -ETIMEDOUT; + if (errno != EINTR) + return -errno; + } +} + +/* + * Log the OpenSSL error for -EPROTO, else @err. A system error, such as a + * missing file, says more than the OpenSSL errors that follow it. + */ +static void log_failure(const char *server, const char *what, int err) +{ + unsigned long e, last = 0; + char buf[256]; + + if (err == -EPROTO) { + while ((e = ERR_get_error())) { + if (ERR_SYSTEM_ERROR(e)) { + err = -ERR_GET_REASON(e); + last = 0; + break; + } + last = e; + } + ERR_clear_error(); + } + + if (last) { + ERR_error_string_n(last, buf, sizeof(buf)); + log_err("KMIP %s: %s: %s", server, what, buf); + } else { + log_err("KMIP %s: %s: %s", server, what, strerror(-err)); + } +} + +static int connect_addr(const struct addrinfo *ai, int64_t deadline) +{ + socklen_t len = sizeof(int); + int fd, err, ret; + + fd = socket(ai->ai_family, + ai->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, + ai->ai_protocol); + if (fd < 0) + return -errno; + + if (connect(fd, ai->ai_addr, ai->ai_addrlen) && errno != EINPROGRESS) { + ret = -errno; + goto err; + } + + ret = wait_fd(fd, POLLOUT, deadline); + if (ret) + goto err; + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len)) { + ret = -errno; + goto err; + } + if (err) { + ret = -err; + goto err; + } + + return fd; + +err: + close(fd); + + return ret; +} + +/* + * Try each address of the server until one connects. A host that does not + * answer fails after KMIP_CONNECT_TIMEOUT_MS, so the caller can try the + * next server early. + */ +static int tcp_connect(const struct kmip_tls_cfg *cfg, int64_t deadline) +{ + const struct addrinfo *ai; + int fd = -EHOSTUNREACH; + int64_t limit; + + for (ai = cfg->addrs; ai; ai = ai->ai_next) { + limit = now_ms() + KMIP_CONNECT_TIMEOUT_MS; + fd = connect_addr(ai, limit < deadline ? limit : deadline); + if (fd >= 0 || now_ms() >= deadline) + break; + } + + if (fd < 0) + log_failure(cfg->server, "connect", fd); + + return fd; +} + +static int setup_ctx(struct kmip_tls *t, const struct kmip_tls_cfg *cfg) +{ + int ok; + + t->ctx = SSL_CTX_new(TLS_client_method()); + if (!t->ctx) + return -ENOMEM; + + if (!SSL_CTX_set_min_proto_version(t->ctx, TLS1_2_VERSION)) + return -EPROTO; + // The responses hold keys: clear the TLS buffers after use. + SSL_CTX_set_options(t->ctx, SSL_OP_CLEANSE_PLAINTEXT); + SSL_CTX_set_verify(t->ctx, SSL_VERIFY_PEER, NULL); + + if (cfg->ca) + ok = SSL_CTX_load_verify_file(t->ctx, cfg->ca); + else + ok = SSL_CTX_set_default_verify_paths(t->ctx); + if (!ok) { + log_failure(cfg->server, cfg->ca ? cfg->ca : "system CAs", + -EPROTO); + return -EINVAL; + } + + if (!cfg->cert != !cfg->key) { + log_err("KMIP %s: client certificate and key go together", + cfg->server); + return -EINVAL; + } + if (!cfg->cert) + return 0; + + if (!SSL_CTX_use_certificate_chain_file(t->ctx, cfg->cert)) { + log_failure(cfg->server, cfg->cert, -EPROTO); + return -EINVAL; + } + if (!SSL_CTX_use_PrivateKey_file(t->ctx, cfg->key, SSL_FILETYPE_PEM) || + !SSL_CTX_check_private_key(t->ctx)) { + log_failure(cfg->server, "client key", -EPROTO); + return -EINVAL; + } + + return 0; +} + +/* Check the server certificate against @server: an IP address or a name. */ +static int set_peer_name(SSL *ssl, const char *server) +{ + unsigned char addr[sizeof(struct in6_addr)]; + + if (inet_pton(AF_INET, server, addr) == 1 || + inet_pton(AF_INET6, server, addr) == 1) { + if (!X509_VERIFY_PARAM_set1_ip_asc(SSL_get0_param(ssl), server)) + return -EINVAL; + return 0; + } + + if (!SSL_set_tlsext_host_name(ssl, server) || + !SSL_set1_host(ssl, server)) + return -EINVAL; + + return 0; +} + +/* Wait as OpenSSL asks after a call on @t->ssl that returned @ret. */ +static int ssl_wait(struct kmip_tls *t, int ret) +{ + switch (SSL_get_error(t->ssl, ret)) { + case SSL_ERROR_WANT_READ: + return wait_fd(t->fd, POLLIN, t->deadline); + case SSL_ERROR_WANT_WRITE: + return wait_fd(t->fd, POLLOUT, t->deadline); + case SSL_ERROR_ZERO_RETURN: + return -ECONNRESET; + case SSL_ERROR_SYSCALL: + return errno ? -errno : -ECONNRESET; + default: + return -EPROTO; + } +} + +static int tls_handshake(struct kmip_tls *t) +{ + long v; + int ret; + + do { + ERR_clear_error(); + errno = 0; + ret = SSL_connect(t->ssl); + if (ret == 1) + return 0; + ret = ssl_wait(t, ret); + } while (!ret); + + v = SSL_get_verify_result(t->ssl); + if (v != X509_V_OK) { + log_err("KMIP %s: server certificate: %s", t->server, + X509_verify_cert_error_string(v)); + return -EKEYREJECTED; + } + log_failure(t->server, "TLS handshake", ret); + + return ret; +} + +int kmip_tls_connect(const struct kmip_tls_cfg *cfg, struct kmip_tls **out) +{ + struct kmip_tls *t; + int ret; + + t = calloc(1, sizeof(*t)); + if (!t) + return -ENOMEM; + t->fd = -1; + + t->server = strdup(cfg->server); + if (!t->server) { + ret = -ENOMEM; + goto err; + } + + ret = setup_ctx(t, cfg); + if (ret) + goto err; + + t->deadline = now_ms() + cfg->timeout_ms; + ret = tcp_connect(cfg, t->deadline); + if (ret < 0) + goto err; + t->fd = ret; + + t->ssl = SSL_new(t->ctx); + if (!t->ssl || !SSL_set_fd(t->ssl, t->fd)) { + ret = -ENOMEM; + goto err; + } + + ret = set_peer_name(t->ssl, cfg->server); + if (ret) + goto err; + + ret = tls_handshake(t); + if (ret) + goto err; + + *out = t; + + return 0; + +err: + kmip_tls_close(t); + + return ret; +} + +static int tls_write(struct kmip_tls *t, const void *buf, size_t len) +{ + const uint8_t *p = buf; + size_t n; + int ret; + + while (len) { + ERR_clear_error(); + errno = 0; + ret = SSL_write_ex(t->ssl, p, len, &n); + if (ret == 1) { + p += n; + len -= n; + continue; + } + + ret = ssl_wait(t, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tls_read(void *arg, void *buf, size_t len) +{ + struct kmip_tls *t = arg; + uint8_t *p = buf; + size_t n; + int ret; + + while (len) { + ERR_clear_error(); + errno = 0; + ret = SSL_read_ex(t->ssl, p, len, &n); + if (ret == 1) { + p += n; + len -= n; + continue; + } + + ret = ssl_wait(t, ret); + if (ret) + return ret; + } + + return 0; +} + +int kmip_tls_exchange(struct kmip_tls *t, const void *req, size_t req_len, + uint8_t *resp, size_t size, size_t *resp_len) +{ + int ret; + + ret = tls_write(t, req, req_len); + if (ret) { + log_failure(t->server, "send", ret); + return ret; + } + + ret = kmip_read_msg(tls_read, t, resp, size, resp_len); + if (ret) + log_failure(t->server, "receive", ret); + + return ret; +} + +void kmip_tls_close(struct kmip_tls *t) +{ + if (!t) + return; + + // Send close_notify, but do not wait for the answer. + if (t->ssl && SSL_is_init_finished(t->ssl)) + SSL_shutdown(t->ssl); + SSL_free(t->ssl); + SSL_CTX_free(t->ctx); + if (t->fd >= 0) + close(t->fd); + free(t->server); + free(t); +} + +static uint32_t get_be32(const uint8_t *p) +{ + return (uint32_t)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +int kmip_read_msg(kmip_read_fn rd, void *arg, uint8_t *buf, size_t size, + size_t *len) +{ + uint32_t tag_type, body; + int ret; + + if (size < TTLV_HDR_LEN) + return -EMSGSIZE; + + ret = rd(arg, buf, TTLV_HDR_LEN); + if (ret) + return ret; + + // A structure's length is a multiple of 8 (KMIP 1.4, section 9.1.1.3). + tag_type = get_be32(buf); + body = get_be32(buf + 4); + if (tag_type != (KMIP_TAG_RESPONSE_MESSAGE << 8 | TTLV_STRUCTURE) || + body % 8) + return -EBADMSG; + if (body > size - TTLV_HDR_LEN) + return -EMSGSIZE; + + ret = rd(arg, buf + TTLV_HDR_LEN, body); + if (ret) + return ret; + *len = TTLV_HDR_LEN + body; + + return 0; +} diff --git a/keysd/src/kmip-tls.h b/keysd/src/kmip-tls.h new file mode 100644 index 0000000000..84cb1ef91a --- /dev/null +++ b/keysd/src/kmip-tls.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +/* + * KMIP over TLS. The client sends a request and reads the response, with + * a size limit. All errors are logged. A write to a closed connection + * raises SIGPIPE, so the caller ignores SIGPIPE. + */ + +struct addrinfo; + +struct kmip_tls_cfg { + const char *server; // host name or IP address + const struct addrinfo *addrs; // @server, resolved, with the port + const char *ca; // PEM file; NULL for the system CAs + const char *cert; // PEM file of the client certificate, or NULL + const char *key; // PEM file of the client private key, or NULL + int timeout_ms; // for the connect, handshake and exchange +}; + +struct kmip_tls; + +/* + * Connect to the first address of @cfg->addrs that answers, and do the TLS + * handshake. Each TCP connect fails after 3 seconds at most. The server + * certificate must be valid for @cfg->server. Returns 0 or a negative errno: + * -EINVAL if a PEM file cannot be loaded, -ETIMEDOUT, -ECONNREFUSED or + * another connect error, -EKEYREJECTED if the server certificate is not + * valid, -EPROTO for another TLS error. + */ +int kmip_tls_connect(const struct kmip_tls_cfg *cfg, struct kmip_tls **out); + +/* + * Send @req and read the response into @resp. Returns 0 or a negative + * errno: -ETIMEDOUT, -ECONNRESET, -EPROTO, or an error of kmip_read_msg(). + * The caller clears @resp if it holds a secret. + */ +int kmip_tls_exchange(struct kmip_tls *t, const void *req, size_t req_len, + uint8_t *resp, size_t size, size_t *resp_len); + +void kmip_tls_close(struct kmip_tls *t); + +/* Read exactly @len bytes. Returns 0 or a negative errno. */ +typedef int (*kmip_read_fn)(void *arg, void *buf, size_t len); + +/* + * Read one Response Message with @rd into @buf. Returns 0 or a negative + * errno: -EBADMSG if the header is not a Response Message, -EMSGSIZE if + * the message does not fit in @size bytes, or an error of @rd. + */ +int kmip_read_msg(kmip_read_fn rd, void *arg, uint8_t *buf, size_t size, + size_t *len); diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index d6e36142c1..c0d4dd530f 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -29,6 +29,18 @@ test_kmip = executable( test('nvme-keysd - kmip', test_kmip) +test_kmip_tls = executable( + 'test-keysd-kmip-tls', + ['test-kmip-tls.c', 'tls-server.c', '../src/kmip-tls.c'], + include_directories: keysd_inc, + dependencies: [ + daemon_util_dep, + openssl_dep, + ], +) + +test('nvme-keysd - kmip tls', test_kmip_tls) + test('nvme-keysd - --help', nvme_keysd, args: ['--help']) test('nvme-keysd - unknown option', nvme_keysd, args: ['--bogus'], should_fail: true) diff --git a/keysd/tests/test-kmip-tls.c b/keysd/tests/test-kmip-tls.c new file mode 100644 index 0000000000..d27f5f3d25 --- /dev/null +++ b/keysd/tests/test-kmip-tls.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kmip.h" +#include "kmip-tls.h" +#include "tls-server.h" + +static bool check(bool cond, const char *what) +{ + printf(" - %s [%s]\n", what, cond ? "PASS" : "FAIL"); + return cond; +} + +static int fd_read(void *arg, void *buf, size_t len) +{ + int fd = *(int *)arg; + uint8_t *p = buf; + ssize_t n; + + while (len) { + n = read(fd, p, len); + if (n < 0 && errno == EINTR) + continue; + if (n < 0) + return -errno; + if (!n) + return -ECONNRESET; + p += n; + len -= n; + } + + return 0; +} + +static void put_hdr(uint8_t *p, uint32_t tag, uint8_t type, uint32_t len) +{ + uint32_t w[2] = { tag << 8 | type, len }; + int i; + + for (i = 0; i < 8; i++) + p[i] = w[i / 4] >> (24 - 8 * (i % 4)); +} + +/* + * Write a header with @tag, @type and @len, then @body_len bytes of body, + * and read it back with a buffer of @size bytes. + */ +static int read_back(uint32_t tag, uint8_t type, uint32_t len, + size_t body_len, size_t size, size_t *msg_len) +{ + uint8_t msg[64], buf[64]; + int sv[2], ret; + + memset(msg, 0xa5, sizeof(msg)); + put_hdr(msg, tag, type, len); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) + return -errno; + if (write(sv[1], msg, TTLV_HDR_LEN + body_len) < 0) { + ret = -errno; + goto out; + } + shutdown(sv[1], SHUT_WR); + + ret = kmip_read_msg(fd_read, &sv[0], buf, size, msg_len); + if (!ret && memcmp(buf, msg, *msg_len)) + ret = -EIO; + +out: + close(sv[0]); + close(sv[1]); + + return ret; +} + +static bool test_read_msg(void) +{ + bool pass = true; + size_t len = 0; + + printf("test_read_msg:\n"); + + pass &= check(!read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 16, 64, &len) && len == 24, + "message read"); + pass &= check(!read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 16, 24, &len) && len == 24, + "message fills the buffer"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 16, 23, &len) == -EMSGSIZE, + "message too large"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 0, 4, &len) == -EMSGSIZE, + "buffer smaller than a header"); + pass &= check(read_back(KMIP_TAG_REQUEST_MESSAGE, TTLV_STRUCTURE, + 16, 16, 64, &len) == -EBADMSG, + "not a Response Message"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_INTEGER, + 16, 16, 64, &len) == -EBADMSG, + "not a structure"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 12, 16, 64, &len) == -EBADMSG, + "length not a multiple of 8"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 8, 64, &len) == -ECONNRESET, + "connection closed in the body"); + pass &= check(read_back(KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, + 16, 0, 64, &len) == -ECONNRESET, + "connection closed after the header"); + + return pass; +} + +/* Resolve @host and @port, as nvme-keysd-kmip does before the connect. */ +static struct addrinfo *resolve(const char *host, const char *port) +{ + struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; + struct addrinfo *res = NULL; + + if (getaddrinfo(host, port, &hints, &res)) { + fprintf(stderr, "cannot look up %s\n", host); + exit(EXIT_FAILURE); + } + + return res; +} + +static int try_connect(const char *port) +{ + struct kmip_tls_cfg cfg = { + .server = "127.0.0.1", + .timeout_ms = 200, + }; + struct addrinfo *addrs = resolve(cfg.server, port); + struct kmip_tls *t; + int ret; + + cfg.addrs = addrs; + ret = kmip_tls_connect(&cfg, &t); + if (!ret) + kmip_tls_close(t); + freeaddrinfo(addrs); + + return ret; +} + +static bool test_connect(void) +{ + bool pass = true; + char port[8]; + int fd; + + printf("test_connect:\n"); + + // Nothing listens on the port. + fd = bind_loopback(port, sizeof(port)); + pass &= check(fd >= 0 && try_connect(port) == -ECONNREFUSED, + "connection refused"); + if (fd >= 0) + close(fd); + + // The kernel accepts the connection, but no server answers. + fd = bind_loopback(port, sizeof(port)); + pass &= check(fd >= 0 && !listen(fd, 1) && + try_connect(port) == -ETIMEDOUT, + "TLS handshake times out"); + if (fd >= 0) + close(fd); + + return pass; +} + +/* + * The TLS tests send @request to a server in a child process. The server + * answers with @response. + */ + +enum srv_mode { + SRV_ANSWER, // send the response + SRV_HALF, // send half of the response and close + SRV_SILENT, // read the request and never answer +}; + +// A Request and a Response Message with Batch Count 1. +static uint8_t request[24]; +static uint8_t response[24]; + +static void serve(SSL *ssl, void *arg) +{ + enum srv_mode mode = *(enum srv_mode *)arg; + uint8_t req[sizeof(request)]; + size_t n; + + if (!tls_read_msg(ssl, req, sizeof(req), &n)) + return; + + switch (mode) { + case SRV_ANSWER: + SSL_write_ex(ssl, response, sizeof(response), &n); + break; + case SRV_HALF: + SSL_write_ex(ssl, response, sizeof(response) / 2, &n); + break; + case SRV_SILENT: + pause(); + break; + } +} + +/* + * Start a server with @srv, connect to it with @cfg and send a request. + * The response must fit in @size bytes and match @response. + */ +static int run(const struct tls_server *srv, struct kmip_tls_cfg *cfg, + size_t size) +{ + uint8_t resp[64]; + struct addrinfo *addrs; + struct kmip_tls *t; + size_t len = 0; + char port[8]; + pid_t pid; + int ret; + + pid = tls_server_start(srv, port, sizeof(port)); + if (pid < 0) + return -EIO; + + addrs = resolve(cfg->server, port); + cfg->addrs = addrs; + ret = kmip_tls_connect(cfg, &t); + if (!ret) { + ret = kmip_tls_exchange(t, request, sizeof(request), resp, + size, &len); + if (!ret && (len != sizeof(response) || + memcmp(resp, response, len))) + ret = -EIO; + kmip_tls_close(t); + } + freeaddrinfo(addrs); + + tls_server_stop(pid); + + return ret; +} + +static bool test_tls(void) +{ + enum srv_mode mode = SRV_ANSWER; + struct tls_server srv = { + .cert = certs.srv_ip, + .serve = serve, + .arg = &mode, + }; + struct kmip_tls_cfg cfg = { + .server = "127.0.0.1", + .ca = ca_pem, + .timeout_ms = 5000, + }; + bool pass = true; + int ret; + + printf("test_tls:\n"); + + pass &= check(!run(&srv, &cfg, 64), "exchange with an IP address"); + pass &= check(run(&srv, &cfg, 16) == -EMSGSIZE, + "response too large"); + + cfg.server = "localhost"; + srv.cert = certs.srv_name; + pass &= check(!run(&srv, &cfg, 64), "exchange with a host name"); + + cfg.server = "127.0.0.1"; + srv.cert = certs.srv_wrong; + pass &= check(run(&srv, &cfg, 64) == -EKEYREJECTED, + "server certificate for another name"); + + srv.cert = certs.srv_ip; + cfg.ca = other_ca_pem; + pass &= check(run(&srv, &cfg, 64) == -EKEYREJECTED, + "server certificate from another CA"); + cfg.ca = ca_pem; + + srv.client_ca = certs.ca; + ret = run(&srv, &cfg, 64); + pass &= check(ret && ret != -EIO, "client certificate missing"); + cfg.cert = client_pem; + cfg.key = client_key_pem; + pass &= check(!run(&srv, &cfg, 64), + "exchange with a client certificate"); + srv.client_ca = NULL; + + mode = SRV_HALF; + pass &= check(run(&srv, &cfg, 64) == -ECONNRESET, + "server closes in the response"); + + mode = SRV_SILENT; + cfg.timeout_ms = 300; + pass &= check(run(&srv, &cfg, 64) == -ETIMEDOUT, + "server does not answer"); + + return pass; +} + +static int try_setup(const char *ca, const char *cert, const char *key) +{ + struct kmip_tls_cfg cfg = { + .server = "127.0.0.1", + .ca = ca, + .cert = cert, + .key = key, + .timeout_ms = 300, + }; + struct addrinfo *addrs = resolve(cfg.server, "1"); + struct kmip_tls *t; + int ret; + + cfg.addrs = addrs; + ret = kmip_tls_connect(&cfg, &t); + if (!ret) + kmip_tls_close(t); + freeaddrinfo(addrs); + + return ret; +} + +static bool test_setup(void) +{ + bool pass = true; + + printf("test_setup:\n"); + + pass &= check(try_setup("/nonexistent/ca.pem", NULL, NULL) == -EINVAL, + "CA file missing"); + pass &= check(try_setup(ca_pem, client_pem, NULL) == -EINVAL, + "client certificate without a key"); + pass &= check(try_setup(ca_pem, client_pem, wrong_key_pem) == -EINVAL, + "client key does not match the certificate"); + + return pass; +} + +int main(void) +{ + bool pass = true; + + signal(SIGPIPE, SIG_IGN); + + pass &= test_read_msg(); + pass &= test_connect(); + + put_hdr(request, KMIP_TAG_REQUEST_MESSAGE, TTLV_STRUCTURE, 16); + put_hdr(request + 8, KMIP_TAG_BATCH_COUNT, TTLV_INTEGER, 4); + request[19] = 1; + put_hdr(response, KMIP_TAG_RESPONSE_MESSAGE, TTLV_STRUCTURE, 16); + put_hdr(response + 8, KMIP_TAG_BATCH_COUNT, TTLV_INTEGER, 4); + response[19] = 1; + + pass &= check(tls_test_setup(), "TLS test setup"); + pass &= test_tls(); + pass &= test_setup(); + tls_test_cleanup(); + + fflush(stdout); + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/keysd/tests/tls-server.c b/keysd/tests/tls-server.c new file mode 100644 index 0000000000..b70a5ffcb6 --- /dev/null +++ b/keysd/tests/tls-server.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "tls-server.h" + +static struct { + EVP_PKEY *ca; + EVP_PKEY *other_ca; + EVP_PKEY *srv; + EVP_PKEY *client; + X509 *other_ca_cert; + X509 *client_cert; +} priv; + +struct tls_test_certs certs; + +static char dir[32]; +char ca_pem[64], other_ca_pem[64], client_pem[64], client_key_pem[64]; +char wrong_key_pem[64]; + +static bool add_ext(X509 *x, X509V3_CTX *ctx, int nid, const char *value) +{ + X509_EXTENSION *ext; + bool ok; + + ext = X509V3_EXT_conf_nid(NULL, ctx, nid, value); + if (!ext) + return false; + ok = X509_add_ext(x, ext, -1); + X509_EXTENSION_free(ext); + + return ok; +} + +/* Make a certificate for @key, signed by @issuer. A NULL @issuer makes a CA. */ +static X509 *make_cert(EVP_PKEY *key, const char *cn, const char *san, + X509 *issuer, EVP_PKEY *issuer_key) +{ + static long serial = 1; + X509V3_CTX ctx; + X509 *x; + + x = X509_new(); + if (!x) + return NULL; + + X509_set_version(x, 2); // X.509 v3 + ASN1_INTEGER_set(X509_get_serialNumber(x), serial++); + X509_gmtime_adj(X509_getm_notBefore(x), -60); + X509_gmtime_adj(X509_getm_notAfter(x), 3600); + X509_set_pubkey(x, key); + X509_NAME_add_entry_by_txt(X509_get_subject_name(x), "CN", + MBSTRING_ASC, (const unsigned char *)cn, + -1, -1, 0); + X509_set_issuer_name(x, X509_get_subject_name(issuer ? issuer : x)); + + X509V3_set_ctx(&ctx, issuer ? issuer : x, x, NULL, NULL, 0); + if (!add_ext(x, &ctx, NID_basic_constraints, + issuer ? "CA:FALSE" : "critical,CA:TRUE") || + (!issuer && !add_ext(x, &ctx, NID_key_usage, + "critical,keyCertSign")) || + (san && !add_ext(x, &ctx, NID_subject_alt_name, san)) || + !X509_sign(x, issuer ? issuer_key : key, EVP_sha256())) { + X509_free(x); + return NULL; + } + + return x; +} + +static bool write_pem(char *path, size_t size, const char *name, X509 *x, + EVP_PKEY *key) +{ + FILE *f; + bool ok; + + snprintf(path, size, "%s/%s", dir, name); + f = fopen(path, "w"); + if (!f) + return false; + if (x) + ok = PEM_write_X509(f, x); + else + ok = PEM_write_PrivateKey(f, key, NULL, NULL, 0, NULL, NULL); + + return !fclose(f) && ok; +} + +bool tls_test_setup(void) +{ + priv.ca = EVP_PKEY_Q_keygen(NULL, NULL, "EC", "P-256"); + priv.other_ca = EVP_PKEY_Q_keygen(NULL, NULL, "EC", "P-256"); + priv.srv = EVP_PKEY_Q_keygen(NULL, NULL, "EC", "P-256"); + priv.client = EVP_PKEY_Q_keygen(NULL, NULL, "EC", "P-256"); + if (!priv.ca || !priv.other_ca || !priv.srv || !priv.client) + return false; + + certs.ca = make_cert(priv.ca, "test CA", NULL, NULL, NULL); + priv.other_ca_cert = make_cert(priv.other_ca, "other CA", NULL, NULL, + NULL); + certs.srv_ip = make_cert(priv.srv, "kms", "IP:127.0.0.1", certs.ca, + priv.ca); + certs.srv_name = make_cert(priv.srv, "kms", "DNS:localhost", certs.ca, + priv.ca); + certs.srv_wrong = make_cert(priv.srv, "kms", "DNS:kms.example", + certs.ca, priv.ca); + priv.client_cert = make_cert(priv.client, "host", NULL, certs.ca, + priv.ca); + if (!certs.ca || !priv.other_ca_cert || !certs.srv_ip || + !certs.srv_name || !certs.srv_wrong || !priv.client_cert) + return false; + + snprintf(dir, sizeof(dir), "test-kmip-tls-XXXXXX"); + + return mkdtemp(dir) && + write_pem(ca_pem, sizeof(ca_pem), "ca.pem", certs.ca, NULL) && + write_pem(other_ca_pem, sizeof(other_ca_pem), "other-ca.pem", + priv.other_ca_cert, NULL) && + write_pem(client_pem, sizeof(client_pem), "client.pem", + priv.client_cert, NULL) && + write_pem(client_key_pem, sizeof(client_key_pem), "client.key", + NULL, priv.client) && + write_pem(wrong_key_pem, sizeof(wrong_key_pem), "wrong.key", + NULL, priv.srv); +} + +static void free_certs(void) +{ + X509_free(certs.ca); + X509_free(priv.other_ca_cert); + X509_free(certs.srv_ip); + X509_free(certs.srv_name); + X509_free(certs.srv_wrong); + X509_free(priv.client_cert); + EVP_PKEY_free(priv.ca); + EVP_PKEY_free(priv.other_ca); + EVP_PKEY_free(priv.srv); + EVP_PKEY_free(priv.client); +} + +void tls_test_cleanup(void) +{ + unlink(ca_pem); + unlink(other_ca_pem); + unlink(client_pem); + unlink(client_key_pem); + unlink(wrong_key_pem); + rmdir(dir); + free_certs(); +} + +int bind_loopback(char *port, size_t size) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + socklen_t len = sizeof(sin); + int fd; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) || + getsockname(fd, (struct sockaddr *)&sin, &len)) { + close(fd); + return -1; + } + snprintf(port, size, "%u", ntohs(sin.sin_port)); + + return fd; +} + +static bool ssl_read_all(SSL *ssl, uint8_t *p, size_t len) +{ + size_t n; + + while (len) { + if (SSL_read_ex(ssl, p, len, &n) != 1) + return false; + p += n; + len -= n; + } + + return true; +} + +bool tls_read_msg(SSL *ssl, uint8_t *buf, size_t size, size_t *len) +{ + uint32_t body; + + if (size < 8 || !ssl_read_all(ssl, buf, 8)) + return false; + + body = (uint32_t)buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7]; + if (body > size - 8 || !ssl_read_all(ssl, buf + 8, body)) + return false; + *len = 8 + body; + + return true; +} + +/* Serve one connection on @lfd. Runs in the child process. */ +static int serve(int lfd, const struct tls_server *srv) +{ + SSL_CTX *ctx; + SSL *ssl = NULL; + int fd = -1, ret = 1; + + ctx = SSL_CTX_new(TLS_server_method()); + if (!ctx || !SSL_CTX_use_certificate(ctx, srv->cert) || + !SSL_CTX_use_PrivateKey(ctx, priv.srv)) + goto out; + if (srv->client_ca) { + X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), + srv->client_ca); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + } + + fd = accept(lfd, NULL, NULL); + ssl = SSL_new(ctx); + if (fd < 0 || !ssl || !SSL_set_fd(ssl, fd) || SSL_accept(ssl) != 1) + goto out; + + srv->serve(ssl, srv->arg); + SSL_shutdown(ssl); + ret = 0; + +out: + SSL_free(ssl); + SSL_CTX_free(ctx); + if (fd >= 0) + close(fd); + close(lfd); + free_certs(); + + return ret; +} + +pid_t tls_server_start(const struct tls_server *srv, char *port, + size_t size) +{ + pid_t pid; + int lfd; + + lfd = bind_loopback(port, size); + if (lfd < 0) + return -1; + if (listen(lfd, 1)) { + close(lfd); + return -1; + } + + pid = fork(); + if (!pid) + _exit(serve(lfd, srv)); + close(lfd); + + return pid; +} + +void tls_server_stop(pid_t pid) +{ + if (pid <= 0) + return; + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); +} diff --git a/keysd/tests/tls-server.h b/keysd/tests/tls-server.h new file mode 100644 index 0000000000..66b61bd3bc --- /dev/null +++ b/keysd/tests/tls-server.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include +#include +#include + +#include + +/* + * A TLS server for the tests. It runs in a child process and uses + * certificates that tls_test_setup() generates. + */ + +struct tls_test_certs { + X509 *ca; + // Server certificates, signed by @ca + X509 *srv_ip; // for 127.0.0.1 + X509 *srv_name; // for localhost + X509 *srv_wrong; // for kms.example +}; + +extern struct tls_test_certs certs; + +// PEM files: the test CA, another CA, the client certificate and key, +// and a key that does not match the client certificate. +extern char ca_pem[], other_ca_pem[], client_pem[], client_key_pem[]; +extern char wrong_key_pem[]; + +bool tls_test_setup(void); +void tls_test_cleanup(void); + +/* Bind a TCP socket on the loopback address and return its port. */ +int bind_loopback(char *port, size_t size); + +struct tls_server { + X509 *cert; + X509 *client_ca; // require a client certificate from this CA + void (*serve)(SSL *ssl, void *arg); // runs in the child + void *arg; +}; + +/* Start @srv on a loopback port. Returns the child's pid, or -1. */ +pid_t tls_server_start(const struct tls_server *srv, char *port, + size_t size); +void tls_server_stop(pid_t pid); + +/* Read one TTLV message. Returns false on error or end of connection. */ +bool tls_read_msg(SSL *ssl, uint8_t *buf, size_t size, size_t *len); From 87410a20164a8488f048a404d130eccff2cd46e1 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 14:12:07 -0400 Subject: [PATCH 009/263] keysd: parse the [kmip:] sections nvme-keysd will get PSKs from KMIP key management servers (KMS). Each KMS is a [kmip:] section in nvme-keysd.conf. A section can list several servers of one cluster. These servers hold the same keys. Signed-off-by: Martin Belanger --- keysd/etc/nvme/nvme-keysd.conf.in | 20 +++ keysd/src/config.c | 254 +++++++++++++++++++++++++++++- keysd/src/config.h | 36 +++++ keysd/tests/test-config.c | 97 ++++++++++++ 4 files changed, 402 insertions(+), 5 deletions(-) diff --git a/keysd/etc/nvme/nvme-keysd.conf.in b/keysd/etc/nvme/nvme-keysd.conf.in index 9391469acc..45bd39c16e 100644 --- a/keysd/etc/nvme/nvme-keysd.conf.in +++ b/keysd/etc/nvme/nvme-keysd.conf.in @@ -11,3 +11,23 @@ # The --debug command-line option overrides this setting and enables debug # logging. #debug-level = info + +# KMIP key management servers (KMS), for connections with +# "tls-key-source = kmip:". One section per KMS. The name after "kmip:" +# is yours to choose. +# +#[kmip:my-kms] +# Host name or IP address. Required. Repeat the key for each server of a +# cluster: they must hold the same keys. They are tried in order. +#server = kms1.example.com +#server = kms2.example.com +#port = 5696 +# CA certificate that signed the server certificate (PEM). Default: the +# system CAs. +#ca = @SYSCONFDIR@/nvme/kmip/ca.pem +# Client certificate (PEM), and the name of its private key in +# @SYSCONFDIR@/nvme/creds/kmip/, encrypted with systemd-creds(1). +#client-cert = @SYSCONFDIR@/nvme/kmip/client.pem +#client-key = my-client-key +# Time limit for the connection and the requests, per server. +#timeout = 10s diff --git a/keysd/src/config.c b/keysd/src/config.c index 36653c573f..0d4f202111 100644 --- a/keysd/src/config.c +++ b/keysd/src/config.c @@ -13,9 +13,14 @@ #include #include #include +#include +#include #include "config.h" +#define KMIP_SECTION "kmip:" +#define KMIP_TIMEOUT_MAX_MS (10 * 60 * 1000) + static void config_set_defaults(struct keysd_config *cfg) { cfg->debug_level = DMN_DEFAULT_LOGLEVEL; @@ -40,9 +45,220 @@ static void apply_global_key(struct keysd_config *cfg, const char *key, lineno, key); } +/* A name in a section header or a file name: no '/', no leading '.'. */ +static bool name_valid(const char *name) +{ + return name[0] && name[0] != '.' && !strchr(name, '/') && + !strpbrk(name, " \t"); +} + +static int parse_port(const char *val) +{ + unsigned long port; + char *end; + + errno = 0; + port = strtoul(val, &end, 10); + if (errno || end == val || *end || !port || port > 65535) + return -EINVAL; + + return 0; +} + +static int parse_timeout(const char *val, unsigned int *timeout_ms) +{ + uint64_t usec; + int r; + + r = shr_parse_time(val, &usec, SHR_USEC_PER_SEC); + if (r) + return r; + if (usec < SHR_USEC_PER_MSEC || + usec / SHR_USEC_PER_MSEC > KMIP_TIMEOUT_MAX_MS) + return -ERANGE; + *timeout_ms = usec / SHR_USEC_PER_MSEC; + + return 0; +} + +/* Add @server to the servers of @kms. A duplicate is ignored. */ +static int add_server(struct keysd_kmip *kms, const char *server) +{ + char **servers; + size_t i; + + for (i = 0; i < kms->nr_servers; i++) + if (streq(kms->servers[i], server)) + return 0; + + servers = realloc(kms->servers, + (kms->nr_servers + 1) * sizeof(*servers)); + if (!servers) + return -ENOMEM; + kms->servers = servers; + + servers[kms->nr_servers] = strdup(server); + if (!servers[kms->nr_servers]) + return -ENOMEM; + kms->nr_servers++; + + return 0; +} + +/* Replace the string in @field with a copy of @val. */ +static int set_str(char **field, const char *val) +{ + char *s = strdup(val); + + if (!s) + return -ENOMEM; + free(*field); + *field = s; + + return 0; +} + +static int apply_kmip_key(struct keysd_kmip *kms, const char *key, + const char *val, const char *conf_path, + unsigned int lineno) +{ + int r = -EINVAL; + + if (streq(key, "server")) { + if (val[0] && !strpbrk(val, " \t")) + r = add_server(kms, val); + } else if (streq(key, "port")) { + r = parse_port(val); + if (!r) + r = set_str(&kms->port, val); + } else if (streq(key, "ca")) { + if (val[0] == '/') + r = set_str(&kms->ca, val); + } else if (streq(key, "client-cert")) { + if (val[0] == '/') + r = set_str(&kms->client_cert, val); + } else if (streq(key, "client-key")) { + if (name_valid(val)) + r = set_str(&kms->client_key, val); + } else if (streq(key, "timeout")) { + r = parse_timeout(val, &kms->timeout_ms); + } else { + log_warn("%s:%u: unknown key '%s', ignored", conf_path, + lineno, key); + return 0; + } + + if (r == -ENOMEM) + return r; + if (r < 0) + log_warn("%s:%u: invalid value for '%s', ignored", conf_path, + lineno, key); + + return 0; +} + +static void kmip_free(struct keysd_kmip *kms) +{ + size_t i; + + for (i = 0; i < kms->nr_servers; i++) + free(kms->servers[i]); + free(kms->servers); + free(kms->name); + free(kms->port); + free(kms->ca); + free(kms->client_cert); + free(kms->client_key); +} + +const struct keysd_kmip *config_find_kmip(const struct keysd_config *cfg, + const char *name) +{ + size_t i; + + for (i = 0; i < cfg->nr_kmip; i++) + if (streq(cfg->kmip[i].name, name)) + return &cfg->kmip[i]; + + return NULL; +} + +/* + * Start the [kmip:] section of the KMS @name. Returns the new entry, + * or NULL if the section is ignored. Sets *@r to -ENOMEM on allocation + * failure. + */ +static struct keysd_kmip *add_kmip(struct keysd_config *cfg, + const char *name, const char *conf_path, + unsigned int lineno, int *r) +{ + struct keysd_kmip *kmip, *kms; + + if (!name_valid(name)) { + log_warn("%s:%u: invalid KMS name '%s', section ignored", + conf_path, lineno, name); + return NULL; + } + if (config_find_kmip(cfg, name)) { + log_warn("%s:%u: KMS '%s' already defined, section ignored", + conf_path, lineno, name); + return NULL; + } + + kmip = realloc(cfg->kmip, (cfg->nr_kmip + 1) * sizeof(*kmip)); + if (!kmip) { + *r = -ENOMEM; + return NULL; + } + cfg->kmip = kmip; + + kms = &cfg->kmip[cfg->nr_kmip]; + memset(kms, 0, sizeof(*kms)); + kms->timeout_ms = KEYSD_KMIP_TIMEOUT_MS; + kms->name = strdup(name); + kms->port = strdup(KEYSD_KMIP_PORT); + if (!kms->name || !kms->port) { + kmip_free(kms); + *r = -ENOMEM; + return NULL; + } + cfg->nr_kmip++; + + return kms; +} + +/* Drop the KMSes that miss a setting. */ +static void check_kmip(struct keysd_config *cfg, const char *conf_path) +{ + struct keysd_kmip *kms; + const char *err; + size_t i = 0; + + while (i < cfg->nr_kmip) { + kms = &cfg->kmip[i]; + err = NULL; + if (!kms->nr_servers) + err = "no server"; + else if (!kms->client_cert != !kms->client_key) + err = "client-cert and client-key go together"; + + if (!err) { + i++; + continue; + } + + log_warn("%s: KMS '%s': %s, ignored", conf_path, + kms->name, err); + kmip_free(kms); + memmove(kms, kms + 1, (cfg->nr_kmip - i - 1) * sizeof(*kms)); + cfg->nr_kmip--; + } +} + struct config_parse_ctx { struct keysd_config *cfg; const char *conf_path; + struct keysd_kmip *kms; // the current [kmip:] section }; static int config_event(enum shr_ini_event event, const char *section, @@ -50,17 +266,31 @@ static int config_event(enum shr_ini_event event, const char *section, unsigned int line, void *user_data) { struct config_parse_ctx *pc = user_data; + const char *name; + int r = 0; switch (event) { case SHR_INI_SECTION: + pc->kms = NULL; + name = shr_startswith(key, KMIP_SECTION); + if (name) + pc->kms = add_kmip(pc->cfg, name, pc->conf_path, line, + &r); + else if (!streq(key, "Global")) + log_warn("%s:%u: unknown section '%s', ignored", + pc->conf_path, line, key); break; case SHR_INI_KV: - if (section && streq(section, "Global")) + if (pc->kms) + r = apply_kmip_key(pc->kms, key, value, pc->conf_path, + line); + else if (section && streq(section, "Global")) apply_global_key(pc->cfg, key, value, pc->conf_path, line); - else - log_warn("%s:%u: key outside [Global], ignored", + else if (!section) + log_warn("%s:%u: key outside a section, ignored", pc->conf_path, line); + // Else the section is ignored, and was reported. break; case SHR_INI_JUNK: log_warn("%s:%u: malformed line, ignored", pc->conf_path, @@ -68,12 +298,12 @@ static int config_event(enum shr_ini_event event, const char *section, break; } - return 0; + return r; } struct keysd_config *config_load(const char *conf_path) { - struct config_parse_ctx pc; + struct config_parse_ctx pc = {}; struct keysd_config *cfg; int ret; @@ -89,13 +319,27 @@ struct keysd_config *config_load(const char *conf_path) pc.conf_path = conf_path; ret = shr_ini_parse_file(conf_path, config_event, &pc); + if (ret == -ENOMEM) { + config_free(cfg); + return NULL; + } if (ret && ret != -ENOENT) log_warn("%s: %s, using defaults", conf_path, strerror(-ret)); + check_kmip(cfg, conf_path); + return cfg; } void config_free(struct keysd_config *cfg) { + size_t i; + + if (!cfg) + return; + + for (i = 0; i < cfg->nr_kmip; i++) + kmip_free(&cfg->kmip[i]); + free(cfg->kmip); free(cfg); } diff --git a/keysd/src/config.h b/keysd/src/config.h index 5d9d3b5bb4..44cdb9bf9d 100644 --- a/keysd/src/config.h +++ b/keysd/src/config.h @@ -7,15 +7,44 @@ */ #pragma once +#include + /* * nvme-keysd's own settings. Which key belongs to which host and subsystem * is in the shared fabrics config, never here. * * [Global] * debug-level = info + * + * [kmip:my-kms] + * server = kms1.example.com + * server = kms2.example.com + * port = 5696 + * ca = /etc/nvme/kmip/ca.pem + * client-cert = /etc/nvme/kmip/client.pem + * client-key = my-client-key + * timeout = 10 + */ + +/* + * A KMS, from a [kmip:] section. Its servers hold the same keys: + * they are nodes of one cluster. */ +struct keysd_kmip { + char *name; + char **servers; // host names or IP addresses, in file order + size_t nr_servers; + char *port; + char *ca; // NULL: the system CAs + char *client_cert; // NULL: no client certificate + char *client_key; // file in KEYSD_KMIP_CREDS_DIR + unsigned int timeout_ms; +}; + struct keysd_config { int debug_level; // DMN_LOG_* (see daemon-util/log.h) + struct keysd_kmip *kmip; // in the order of the file + size_t nr_kmip; }; /* @@ -28,5 +57,12 @@ struct keysd_config *config_load(const char *conf_path); void config_free(struct keysd_config *cfg); +/* Return the KMS named @name, or NULL. */ +const struct keysd_kmip *config_find_kmip(const struct keysd_config *cfg, + const char *name); + #define KEYSD_CONF_PATH SYSCONFDIR "/nvme/nvme-keysd.conf" #define KEYSD_CREDS_DIR SYSCONFDIR "/nvme/creds" +#define KEYSD_KMIP_CREDS_DIR KEYSD_CREDS_DIR "/kmip" +#define KEYSD_KMIP_PORT "5696" +#define KEYSD_KMIP_TIMEOUT_MS 10000 diff --git a/keysd/tests/test-config.c b/keysd/tests/test-config.c index 1043e938b2..439795a3fe 100644 --- a/keysd/tests/test-config.c +++ b/keysd/tests/test-config.c @@ -13,6 +13,7 @@ #include #include +#include #include "config.h" @@ -154,6 +155,100 @@ static bool test_unreadable_file_defaults(void) return pass; } +static bool test_kmip_parsed(void) +{ + const struct keysd_kmip *kms; + struct keysd_config *cfg; + bool pass = true; + + printf("test_kmip_parsed:\n"); + + cfg = load_text("[kmip:primary]\n" + "server = kms1.example.com\n" + "server = kms2.example.com\n" + "server = kms1.example.com\n" + "port = 5697\n" + "ca = /etc/nvme/kmip/ca.pem\n" + "client-cert = /etc/nvme/kmip/client.pem\n" + "client-key = primary\n" + "timeout = 500ms\n" + "\n" + "[kmip:backup]\n" + "server = 192.0.2.1\n"); + pass &= check(cfg->nr_kmip == 2 && + shr_streq0(cfg->kmip[0].name, "primary") && + shr_streq0(cfg->kmip[1].name, "backup"), + "two KMSes, in the order of the file"); + + kms = config_find_kmip(cfg, "primary"); + pass &= check(kms && kms->nr_servers == 2 && + shr_streq0(kms->servers[0], "kms1.example.com") && + shr_streq0(kms->servers[1], "kms2.example.com") && + shr_streq0(kms->port, "5697") && + shr_streq0(kms->ca, "/etc/nvme/kmip/ca.pem") && + shr_streq0(kms->client_cert, + "/etc/nvme/kmip/client.pem") && + shr_streq0(kms->client_key, "primary") && + kms->timeout_ms == 500, + "all keys parsed"); + + kms = config_find_kmip(cfg, "backup"); + pass &= check(kms && shr_streq0(kms->port, KEYSD_KMIP_PORT) && + !kms->ca && !kms->client_cert && !kms->client_key && + kms->timeout_ms == KEYSD_KMIP_TIMEOUT_MS, + "defaults"); + pass &= check(!config_find_kmip(cfg, "other"), "unknown name"); + + config_free(cfg); + + return pass; +} + +static bool test_kmip_invalid(void) +{ + const struct keysd_kmip *kms; + struct keysd_config *cfg; + bool pass = true; + + printf("test_kmip_invalid:\n"); + + cfg = load_text("[kmip:a]\n" + "server = kms-a\n" + "server = kms a\n" + "port = 0\n" + "ca = ca.pem\n" + "client-key = ../key\n" + "timeout = 1h\n" + "bogus = 1\n" + "\n" + "[kmip:a]\n" + "server = kms-dup\n" + "\n" + "[kmip:b/c]\n" + "server = kms-bc\n" + "\n" + "[kmip:no-server]\n" + "port = 5696\n" + "\n" + "[kmip:cert-only]\n" + "server = kms-cert\n" + "client-cert = /etc/nvme/kmip/client.pem\n"); + pass &= check(cfg->nr_kmip == 1, "invalid sections dropped"); + + kms = config_find_kmip(cfg, "a"); + pass &= check(kms && kms->nr_servers == 1 && + shr_streq0(kms->servers[0], "kms-a"), + "duplicate section and invalid server ignored"); + pass &= check(kms && shr_streq0(kms->port, KEYSD_KMIP_PORT) && + !kms->ca && !kms->client_key && + kms->timeout_ms == KEYSD_KMIP_TIMEOUT_MS, + "invalid values ignored"); + + config_free(cfg); + + return pass; +} + int main(void) { bool pass = true; @@ -164,6 +259,8 @@ int main(void) pass &= test_unknown_key_and_section_ignored(); pass &= test_malformed_line_ignored(); pass &= test_unreadable_file_defaults(); + pass &= test_kmip_parsed(); + pass &= test_kmip_invalid(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From d6cd046294dd0d913495689ef96cdab195a79166 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 14:12:07 -0400 Subject: [PATCH 010/263] keysd: add the nvme-keysd-kmip helper nvme-keysd-kmip gets a PSK from a KMIP server for nvme-keysd. It is a separate process for privilege separation. nvme-keysd needs root to write the keyring, but has no network access. The helper needs the network and the client private key, but runs without privileges. nvme-keysd-kmip.socket starts one helper instance per connection. The helper runs as a dynamic user without capabilities. systemd decrypts the client private keys in /etc/nvme/creds/kmip for each instance only. The helper finds the Active key with the requested name, and returns it in the PSK interchange format. The key is either Secret Data that holds the interchange string, or a raw Symmetric Key of 32 or 48 bytes. It tries the servers of the KMS in order, until one answers. The helper is built with nvme-keysd, unless -Dkmip=disabled. Signed-off-by: Martin Belanger --- keysd/meson.build | 44 +++ keysd/src/kmip-main.c | 359 +++++++++++++++++ keysd/src/kmip-psk.c | 349 +++++++++++++++++ keysd/src/kmip-psk.h | 61 +++ keysd/src/kmip-varlink.h | 21 + keysd/systemd/nvme-keysd-kmip.socket.in | 12 + keysd/systemd/nvme-keysd-kmip@.service.in | 38 ++ keysd/tests/meson.build | 30 ++ keysd/tests/test-kmip-psk.c | 447 ++++++++++++++++++++++ meson.build | 3 + meson_options.txt | 6 + 11 files changed, 1370 insertions(+) create mode 100644 keysd/src/kmip-main.c create mode 100644 keysd/src/kmip-psk.c create mode 100644 keysd/src/kmip-psk.h create mode 100644 keysd/src/kmip-varlink.h create mode 100644 keysd/systemd/nvme-keysd-kmip.socket.in create mode 100644 keysd/systemd/nvme-keysd-kmip@.service.in create mode 100644 keysd/tests/test-kmip-psk.c diff --git a/keysd/meson.build b/keysd/meson.build index 0891485a78..b56ff9190d 100644 --- a/keysd/meson.build +++ b/keysd/meson.build @@ -31,6 +31,50 @@ nvme_keysd = executable( install_tag: 'nvme-keysd', ) +if want_kmip + nvme_keysd_kmip = executable( + 'nvme-keysd-kmip', + files( + 'src/config.c', + 'src/kmip.c', + 'src/kmip-main.c', + 'src/kmip-psk.c', + 'src/kmip-tls.c', + 'src/ttlv.c', + ), + include_directories: keysd_inc, + dependencies: [ + ccan_dep, + daemon_util_dep, + libnvme_dep, + libsystemd_dep, + openssl_dep, + shared_dep, + ], + install: true, + install_dir: sbindir, + install_tag: 'nvme-keysd', + ) + + foreach unit : ['nvme-keysd-kmip.socket', 'nvme-keysd-kmip@.service'] + configure_file( + input: 'systemd' / unit + '.in', + output: unit, + configuration: substs, + install: true, + install_dir: systemddir, + install_tag: 'nvme-keysd', + ) + endforeach + + # The encrypted client private keys. Only root reads them. + install_emptydir( + sysconfdir / 'nvme' / 'creds' / 'kmip', + install_mode: 'rwx------', + install_tag: 'nvme-keysd', + ) +endif + if get_option('tests') subdir('tests') endif diff --git a/keysd/src/kmip-main.c b/keysd/src/kmip-main.c new file mode 100644 index 0000000000..a63a30b2d6 --- /dev/null +++ b/keysd/src/kmip-main.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +/* + * nvme-keysd-kmip: gets a PSK from a KMIP server for nvme-keysd. systemd + * starts one instance per connection to the Varlink socket. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "kmip-psk.h" +#include "kmip-varlink.h" + +/* KMIP Name values are short. Refuse anything that cannot be one. */ +#define KEY_NAME_MAX 256 + +// Enough to try every server of a KMS while one name lookup hangs. +#define RESOLVE_WORKERS 8 + +static SD_VARLINK_DEFINE_METHOD( + GetKey, + SD_VARLINK_FIELD_COMMENT("Name of a [kmip:] section"), + SD_VARLINK_DEFINE_INPUT(kms, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("KMIP Name of the key"), + SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Index of the server to try first"), + SD_VARLINK_DEFINE_INPUT(server, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The PSK in the PSK interchange format"), + SD_VARLINK_DEFINE_OUTPUT(key, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("Index of the server that answered"), + SD_VARLINK_DEFINE_OUTPUT(server, SD_VARLINK_INT, 0)); + +static SD_VARLINK_DEFINE_ERROR(NoSuchKms); +static SD_VARLINK_DEFINE_ERROR(NoSuchKey); +static SD_VARLINK_DEFINE_ERROR(KeyNotUnique); +static SD_VARLINK_DEFINE_ERROR(InvalidKey); +static SD_VARLINK_DEFINE_ERROR(KmsFailed); + +static SD_VARLINK_DEFINE_INTERFACE( + io_nvme_keysd_Kmip, + KMIP_VARLINK_INTERFACE, + SD_VARLINK_INTERFACE_COMMENT("Get TLS PSKs from a KMIP server"), + SD_VARLINK_SYMBOL_COMMENT("Get the Active key with a name"), + &vl_method_GetKey, + SD_VARLINK_SYMBOL_COMMENT("The KMS is not configured"), + &vl_error_NoSuchKms, + SD_VARLINK_SYMBOL_COMMENT("No Active key has this name"), + &vl_error_NoSuchKey, + SD_VARLINK_SYMBOL_COMMENT("Several Active keys have this name"), + &vl_error_KeyNotUnique, + SD_VARLINK_SYMBOL_COMMENT("The key is not a valid PSK"), + &vl_error_InvalidKey, + SD_VARLINK_SYMBOL_COMMENT("No server of the KMS can answer"), + &vl_error_KmsFailed); + +struct helper_ctx { + struct libnvme_global_ctx *nvme_ctx; // PSK encoding + struct keysd_config *cfg; + struct dmn_resolver *resolver; + // The call in progress. Varlink sends one call at a time. + struct kmip_get *get; + sd_varlink *link; + const char *kms; + char *name; +}; + +struct get_key_params { + const char *kms; + const char *name; + uint64_t server; +}; + +/* + * The client private key of @kms. systemd decrypts the files of + * KEYSD_KMIP_CREDS_DIR into $CREDENTIALS_DIRECTORY, as "kmip_". + */ +static int client_key_path(const struct keysd_kmip *kms, char **path) +{ + const char *dir; + + *path = NULL; + if (!kms->client_key) + return 0; + + dir = getenv("CREDENTIALS_DIRECTORY"); + if (!dir) { + log_err("KMS '%s': no credentials directory", kms->name); + return -ENOKEY; + } + if (asprintf(path, "%s/kmip_%s", dir, kms->client_key) < 0) { + *path = NULL; + return -ENOMEM; + } + + return 0; +} + +static int reply_key(sd_varlink *link, const char *psk, size_t server) +{ + sd_json_variant *v = NULL; + int r; + + r = sd_json_buildo(&v, SD_JSON_BUILD_PAIR_STRING("key", psk), + SD_JSON_BUILD_PAIR_UNSIGNED("server", server)); + if (r < 0) + return r; + sd_json_variant_sensitive(v); + + r = sd_varlink_reply(link, v); + sd_json_variant_unref(v); + + return r; +} + +static const char *error_id(int err) +{ + switch (err) { + case -ENOENT: + return KMIP_VARLINK_NO_SUCH_KEY; + case -ENOTUNIQ: + return KMIP_VARLINK_KEY_NOT_UNIQUE; + case -EMEDIUMTYPE: + return KMIP_VARLINK_INVALID_KEY; + default: + return KMIP_VARLINK_KMS_FAILED; + } +} + +static void end_call(struct helper_ctx *hc) +{ + hc->get = NULL; + hc->link = sd_varlink_unref(hc->link); + free(hc->name); + hc->name = NULL; +} + +static void on_psk(int ret, const char *psk, size_t server, void *user_data) +{ + struct helper_ctx *hc = user_data; + const struct keysd_kmip *kms = config_find_kmip(hc->cfg, hc->kms); + int r; + + if (ret) { + log_err("KMS '%s': key '%s': %s", hc->kms, hc->name, + strerror(-ret)); + r = sd_varlink_error(hc->link, error_id(ret), NULL); + } else { + log_info("KMS '%s': key '%s' sent, from %s", hc->kms, + hc->name, kms->servers[server]); + r = reply_key(hc->link, psk, server); + } + if (r < 0) + log_err("KMS '%s': key '%s': reply: %s", hc->kms, hc->name, + strerror(-r)); + end_call(hc); +} + +static int method_get_key(sd_varlink *link, sd_json_variant *params, + sd_varlink_method_flags_t flags, void *userdata) +{ + static const sd_json_dispatch_field table[] = { + { "kms", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, + offsetof(struct get_key_params, kms), SD_JSON_MANDATORY }, + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, + offsetof(struct get_key_params, name), SD_JSON_MANDATORY }, + { "server", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, + offsetof(struct get_key_params, server), 0 }, + {} + }; + struct helper_ctx *hc = userdata; + struct get_key_params p = {}; + const struct keysd_kmip *kms; + char *key_path; + int r; + + r = sd_varlink_dispatch(link, params, table, &p); + if (r) + return r; + + if (!p.name[0] || strlen(p.name) >= KEY_NAME_MAX) + return sd_varlink_error_invalid_parameter_name(link, "name"); + + kms = config_find_kmip(hc->cfg, p.kms); + if (!kms) { + log_err("KMS '%s' is not configured", p.kms); + return sd_varlink_error(link, KMIP_VARLINK_NO_SUCH_KMS, NULL); + } + + hc->name = strdup(p.name); + if (!hc->name) + return -ENOMEM; + hc->kms = kms->name; + hc->link = sd_varlink_ref(link); + + // The reply is sent from on_psk(). + r = client_key_path(kms, &key_path); + if (!r) + r = kmip_get_psk(hc->resolver, hc->nvme_ctx, kms, p.server, + key_path, p.name, on_psk, hc, &hc->get); + free(key_path); + if (r) { + log_err("KMS '%s': key '%s': %s", kms->name, p.name, + strerror(-r)); + r = sd_varlink_error(link, error_id(r), NULL); + end_call(hc); + } + + return r; +} + +static int run_server(struct helper_ctx *hc) +{ + sd_varlink_server *s = NULL; + sd_event *event = NULL; + int r; + + r = sd_event_new(&event); + if (r < 0) + return r; + + r = dmn_resolver_new(event, RESOLVE_WORKERS, &hc->resolver); + if (r < 0) + goto out; + + // Root (nvme-keysd) or our own user (a test). + r = sd_varlink_server_new(&s, SD_VARLINK_SERVER_ROOT_ONLY | + SD_VARLINK_SERVER_MYSELF_ONLY | + SD_VARLINK_SERVER_INHERIT_USERDATA | + SD_VARLINK_SERVER_INPUT_SENSITIVE); + if (r < 0) + goto out; + sd_varlink_server_set_userdata(s, hc); + + r = sd_varlink_server_add_interface(s, + &vl_interface_io_nvme_keysd_Kmip); + if (r >= 0) + r = sd_varlink_server_bind_method(s, KMIP_VARLINK_GET_KEY, + method_get_key); + // Exit when nvme-keysd closes the connection. + if (r >= 0) + r = sd_varlink_server_set_exit_on_idle(s, true); + if (r >= 0) + r = sd_varlink_server_attach_event(s, event, 0); + if (r >= 0) + r = sd_varlink_server_listen_auto(s); + if (r >= 0) + r = sd_event_loop(event); + + kmip_get_cancel(hc->get); + end_call(hc); + +out: + sd_varlink_server_unref(s); + dmn_resolver_free(hc->resolver); + sd_event_unref(event); + + return r; +} + +static void usage(const char *prog) +{ + printf("Usage: %s [OPTIONS]\n" + "\n" + "Started by systemd through nvme-keysd-kmip.socket.\n" + "\n" + " --config FILE, -c FILE nvme-keysd configuration file\n" + " (default: " KEYSD_CONF_PATH ")\n" + " --debug, -d enable debug logging\n" + " --help, -h show this help and exit\n", + prog); +} + +int main(int argc, char **argv) +{ + static const struct option long_opts[] = { + { "config", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct helper_ctx hc = {}; + const char *conf_path = KEYSD_CONF_PATH; + bool debug = false; + int r, c; + + while ((c = getopt_long(argc, argv, "c:dh", long_opts, NULL)) != -1) { + switch (c) { + case 'c': + conf_path = optarg; + break; + case 'd': + debug = true; + break; + case 'h': + usage(argv[0]); + return 0; + default: + fprintf(stderr, "Try '%s --help'.\n", argv[0]); + return 1; + } + } + + if (sd_listen_fds(0) <= 0) { + fprintf(stderr, "%s: must be started by socket activation\n", + argv[0]); + return 1; + } + + // Key material must never end up in a core dump. + if (prctl(PR_SET_DUMPABLE, 0) < 0) { + fprintf(stderr, "prctl(PR_SET_DUMPABLE): %s\n", + strerror(errno)); + return 1; + } + // A write to a closed TLS connection must fail, not kill us. + signal(SIGPIPE, SIG_IGN); + + hc.cfg = config_load(conf_path); + hc.nvme_ctx = libnvme_create_global_ctx(); + if (!hc.cfg || !hc.nvme_ctx) { + log_err("out of memory"); + r = -ENOMEM; + goto out; + } + dmn_log_set_level(debug ? DMN_LOG_DEBUG : hc.cfg->debug_level); + + r = run_server(&hc); + if (r < 0) + log_err("Varlink server: %s", strerror(-r)); + +out: + if (hc.nvme_ctx) + libnvme_free_global_ctx(hc.nvme_ctx); + config_free(hc.cfg); + + return r < 0 ? 1 : 0; +} diff --git a/keysd/src/kmip-psk.c b/keysd/src/kmip-psk.c new file mode 100644 index 0000000000..f095f21f3e --- /dev/null +++ b/keysd/src/kmip-psk.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "kmip.h" +#include "kmip-psk.h" +#include "kmip-tls.h" + +/* Locate and Get responses are small. */ +#define KMIP_MSG_MAX 16384 + +/* Large enough for an interchange string in Secret Data. */ +#define KMIP_KEY_MAX 256 + +static void free_secret(void *p, size_t len) +{ + if (!p) + return; + explicit_bzero(p, len); + free(p); +} + +static int psk_from_secret_data(struct libnvme_global_ctx *ctx, + const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX]) +{ + enum libnvmf_hmac_alg hmac; + unsigned char version; + unsigned char *key; + size_t key_len; + int ret; + + // A trailing newline is common in a key file. + while (len && (data[len - 1] == '\n' || data[len - 1] == '\r')) + len--; + if (!len || len >= KMIP_PSK_MAX || memchr(data, '\0', len)) + return -EMEDIUMTYPE; + memcpy(psk, data, len); + psk[len] = '\0'; + + ret = libnvmf_import_tls_key_versioned(ctx, psk, &version, &hmac, + &key_len, &key); + if (ret) { + explicit_bzero(psk, KMIP_PSK_MAX); + return -EMEDIUMTYPE; + } + free_secret(key, key_len); + + return 0; +} + +static int psk_from_symmetric_key(struct libnvme_global_ctx *ctx, + const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX]) +{ + char *encoded; + size_t n; + int ret; + + if (len != 32 && len != 48) + return -EMEDIUMTYPE; + + ret = libnvmf_export_tls_key(ctx, data, len, &encoded); + if (ret) + return ret; + + n = strlen(encoded); + if (n < KMIP_PSK_MAX) + memcpy(psk, encoded, n + 1); + free_secret(encoded, n); + + return n < KMIP_PSK_MAX ? 0 : -EMEDIUMTYPE; +} + +int kmip_psk_from_object(struct libnvme_global_ctx *ctx, uint32_t obj_type, + const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX]) +{ + switch (obj_type) { + case KMIP_OBJ_SECRET_DATA: + return psk_from_secret_data(ctx, data, len, psk); + case KMIP_OBJ_SYMMETRIC_KEY: + return psk_from_symmetric_key(ctx, data, len, psk); + default: + return -EMEDIUMTYPE; + } +} + +static void log_kms_failure(const char *server, const char *op, + const struct kmip_result *res) +{ + log_err("KMIP %s: %s failed: status %u, reason %u%s%s", server, op, + res->status, res->reason, res->message[0] ? ": " : "", + res->message); +} + +/* Send the request in @req and read the response into @resp. */ +static int exchange(struct kmip_tls *t, struct ttlv_buf *req, uint8_t *resp, + size_t *len) +{ + int ret; + + ret = kmip_tls_exchange(t, req->data, req->len, resp, KMIP_MSG_MAX, + len); + ttlv_buf_free(req); + ttlv_buf_init(req); + + return ret; +} + +/* Get the key from one server of @kms, at the addresses @addrs. */ +static int get_from_server(struct libnvme_global_ctx *ctx, + const struct keysd_kmip *kms, const char *server, + const struct addrinfo *addrs, int timeout_ms, + const char *key_path, const char *name, char psk[KMIP_PSK_MAX]) +{ + const struct kmip_tls_cfg cfg = { + .server = server, + .addrs = addrs, + .ca = kms->ca, + .cert = kms->client_cert, + .key = key_path, + .timeout_ms = timeout_ms, + }; + const struct kmip_version *v = &kmip_v1_4; + uint8_t *resp, key[KMIP_KEY_MAX]; + struct kmip_tls *t = NULL; + struct kmip_result res; + struct ttlv_buf req; + size_t len, key_len; + uint32_t obj_type; + char uid[128]; + int ret; + + resp = malloc(KMIP_MSG_MAX); + if (!resp) + return -ENOMEM; + ttlv_buf_init(&req); + + ret = kmip_tls_connect(&cfg, &t); + if (ret) + goto out; + + ret = kmip_build_locate(&req, v, name, KMIP_STATE_ACTIVE); + if (!ret) + ret = exchange(t, &req, resp, &len); + if (!ret) + ret = kmip_parse_locate(resp, len, v, uid, sizeof(uid), &res); + if (ret == -EREMOTEIO) + log_kms_failure(server, "Locate", &res); + if (ret) + goto out; + + ret = kmip_build_get(&req, v, uid); + if (!ret) + ret = exchange(t, &req, resp, &len); + if (!ret) + ret = kmip_parse_get(resp, len, v, &obj_type, key, sizeof(key), + &key_len, &res); + if (ret == -EREMOTEIO) + log_kms_failure(server, "Get", &res); + if (ret == -EOPNOTSUPP) + ret = -EMEDIUMTYPE; + if (ret) + goto out; + + ret = kmip_psk_from_object(ctx, obj_type, key, key_len, psk); + +out: + kmip_tls_close(t); + ttlv_buf_free(&req); + explicit_bzero(key, sizeof(key)); + free_secret(resp, KMIP_MSG_MAX); + + return ret; +} + +/* The answer does not depend on the server: the servers hold the same keys. */ +static bool final_error(int err) +{ + return err == -ENOENT || err == -ENOTUNIQ || err == -EMEDIUMTYPE || + err == -ENOMEM; +} + +struct kmip_get { + struct dmn_resolver *resolver; + struct libnvme_global_ctx *ctx; + const struct keysd_kmip *kms; + struct dmn_resolve_query *query; + char *key_path; + char *name; + size_t first; + size_t n; // servers tried + int64_t deadline; // of the current server, CLOCK_MONOTONIC in ms + int ret; + kmip_psk_callback done; + void *user_data; +}; + +static int64_t now_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static size_t server_index(const struct kmip_get *g) +{ + return (g->first + g->n) % g->kms->nr_servers; +} + +static void get_free(struct kmip_get *g) +{ + free(g->key_path); + free(g->name); + free(g); +} + +static void on_resolved(int gai, const struct addrinfo *res, + void *user_data); + +/* + * Start the lookup of the next server. Return: 0 if on_resolved() will be + * called, or the error of the last server when no server is left. + */ +static int next_server(struct kmip_get *g) +{ + static const struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; + const struct keysd_kmip *kms = g->kms; + const char *server; + int ret; + + for (; g->n < kms->nr_servers; g->n++) { + server = kms->servers[server_index(g)]; + g->deadline = now_ms() + kms->timeout_ms; + ret = dmn_resolve(g->resolver, server, kms->port, &hints, + kms->timeout_ms * 1000ULL, on_resolved, g, + &g->query); + if (!ret) + return 0; + log_err("KMIP %s: cannot resolve: %s", server, strerror(-ret)); + g->ret = ret; + } + + return g->ret; +} + +static void on_resolved(int gai, const struct addrinfo *res, + void *user_data) +{ + struct kmip_get *g = user_data; + const char *server = g->kms->servers[server_index(g)]; + char psk[KMIP_PSK_MAX] = ""; + int err = errno; // for EAI_SYSTEM + int64_t left; + int ret; + + g->query = NULL; + left = g->deadline - now_ms(); + if (gai == EAI_SYSTEM) { + ret = err ? -err : -EHOSTUNREACH; + log_err("KMIP %s: name lookup: %s", server, strerror(-ret)); + } else if (gai) { + log_err("KMIP %s: %s", server, gai_strerror(gai)); + ret = -EHOSTUNREACH; + } else if (left <= 0) { + log_err("KMIP %s: %s", server, strerror(ETIMEDOUT)); + ret = -ETIMEDOUT; + } else { + ret = get_from_server(g->ctx, g->kms, server, res, (int)left, + g->key_path, g->name, psk); + } + + g->ret = ret; + if (ret && !final_error(ret)) { + g->n++; + ret = next_server(g); + if (!ret) + return; + } + + g->done(ret, psk, server_index(g), g->user_data); + explicit_bzero(psk, sizeof(psk)); + get_free(g); +} + +int kmip_get_psk(struct dmn_resolver *resolver, + struct libnvme_global_ctx *ctx, const struct keysd_kmip *kms, + size_t first, const char *key_path, const char *name, + kmip_psk_callback done, void *user_data, struct kmip_get **ret) +{ + struct kmip_get *g; + int err; + + if (!kms->nr_servers) + return -EHOSTUNREACH; + + g = calloc(1, sizeof(*g)); + if (!g) + return -ENOMEM; + g->name = strdup(name); + g->key_path = key_path ? strdup(key_path) : NULL; + if (!g->name || (key_path && !g->key_path)) { + get_free(g); + return -ENOMEM; + } + g->resolver = resolver; + g->ctx = ctx; + g->kms = kms; + g->first = first < kms->nr_servers ? first : 0; + g->ret = -EHOSTUNREACH; + g->done = done; + g->user_data = user_data; + + err = next_server(g); + if (err) { + get_free(g); + return err; + } + if (ret) + *ret = g; + + return 0; +} + +void kmip_get_cancel(struct kmip_get *g) +{ + if (!g) + return; + + dmn_resolve_cancel(g->query); + get_free(g); +} diff --git a/keysd/src/kmip-psk.h b/keysd/src/kmip-psk.h new file mode 100644 index 0000000000..369722e3e0 --- /dev/null +++ b/keysd/src/kmip-psk.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +struct keysd_kmip; +struct libnvme_global_ctx; + +/* + * Large enough for a PSK in the interchange format: a 48-byte key plus + * its CRC is 72 characters in base64, plus the 17-character framing. + */ +#define KMIP_PSK_MAX 128 + +/* + * Convert a key object of type @obj_type (a KMIP_OBJ_* value) to a PSK in + * the interchange format. Secret Data holds the interchange string. A + * Symmetric Key holds the PSK: 32 bytes for SHA-256, 48 bytes for + * SHA-384. Returns 0, -EMEDIUMTYPE if the object is not a valid PSK, or + * -ENOMEM. + */ +int kmip_psk_from_object(struct libnvme_global_ctx *ctx, uint32_t obj_type, + const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX]); + +struct dmn_resolver; +struct kmip_get; + +/* + * Called from the event loop with 0 and the PSK in @psk, or a negative + * errno. @server is the index of the server that answered. @psk is + * cleared after the call. + */ +typedef void (*kmip_psk_callback)(int ret, const char *psk, size_t server, + void *user_data); + +/* + * Get the Active key named @name from @kms, as a PSK in the interchange + * format. The servers of @kms are tried in order, starting with the one + * at index @first, until one answers. The timeout of @kms applies to each + * server: it covers the name lookup through @resolver, the connection and + * the exchange. @key_path is the client private key, or NULL. + * @done gets -ENOENT if no Active key has this name, -ENOTUNIQ if several + * have, -EMEDIUMTYPE if the key is not a valid PSK, another errno if no + * server can be reached or each reports an error. + * Return: 0 if @done will be called, a negative errno otherwise. *@ret, + * if @ret is not NULL, is valid until @done is called. + */ +int kmip_get_psk(struct dmn_resolver *resolver, + struct libnvme_global_ctx *ctx, const struct keysd_kmip *kms, + size_t first, const char *key_path, const char *name, + kmip_psk_callback done, void *user_data, struct kmip_get **ret); + +/* Stop @g. Its callback is not called. */ +void kmip_get_cancel(struct kmip_get *g); diff --git a/keysd/src/kmip-varlink.h b/keysd/src/kmip-varlink.h new file mode 100644 index 0000000000..c515420ce5 --- /dev/null +++ b/keysd/src/kmip-varlink.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +/* The Varlink interface of nvme-keysd-kmip. */ + +#define KMIP_VARLINK_ADDRESS RUNDIR "/nvme/keysd/kmip" +#define KMIP_VARLINK_INTERFACE "io.nvme.keysd.Kmip" + +#define KMIP_VARLINK_GET_KEY KMIP_VARLINK_INTERFACE ".GetKey" + +#define KMIP_VARLINK_NO_SUCH_KMS KMIP_VARLINK_INTERFACE ".NoSuchKms" +#define KMIP_VARLINK_NO_SUCH_KEY KMIP_VARLINK_INTERFACE ".NoSuchKey" +#define KMIP_VARLINK_KEY_NOT_UNIQUE KMIP_VARLINK_INTERFACE ".KeyNotUnique" +#define KMIP_VARLINK_INVALID_KEY KMIP_VARLINK_INTERFACE ".InvalidKey" +#define KMIP_VARLINK_KMS_FAILED KMIP_VARLINK_INTERFACE ".KmsFailed" diff --git a/keysd/systemd/nvme-keysd-kmip.socket.in b/keysd/systemd/nvme-keysd-kmip.socket.in new file mode 100644 index 0000000000..afbad0c844 --- /dev/null +++ b/keysd/systemd/nvme-keysd-kmip.socket.in @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +[Unit] +Description=NVMe TLS Key Provisioning KMIP Helper Socket +Documentation=man:nvme-keysd(8) + +[Socket] +ListenStream=@RUNDIR@/nvme/keysd/kmip +FileDescriptorName=varlink +SocketMode=0600 +# One helper instance per connection. +Accept=yes +MaxConnections=16 diff --git a/keysd/systemd/nvme-keysd-kmip@.service.in b/keysd/systemd/nvme-keysd-kmip@.service.in new file mode 100644 index 0000000000..27046ff704 --- /dev/null +++ b/keysd/systemd/nvme-keysd-kmip@.service.in @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +[Unit] +Description=NVMe TLS Key Provisioning KMIP Helper +Documentation=man:nvme-keysd(8) +Wants=network-online.target +After=network-online.target + +[Service] +ExecStart=@SBINDIR@/nvme-keysd-kmip +# The client private keys. systemd decrypts each file of the directory +# into $CREDENTIALS_DIRECTORY/kmip_, readable only by this instance. +LoadCredentialEncrypted=kmip:@SYSCONFDIR@/nvme/creds/kmip + +# Hardening. The helper needs the network, but no privileges and no +# writable files. The CA and client certificates must be readable by any +# user. +DynamicUser=yes +CapabilityBoundingSet= +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +ProtectHome=yes +PrivateDevices=yes +ProtectClock=yes +ProtectHostname=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +ProtectProc=invisible +ProcSubset=pid +RestrictNamespaces=yes +RestrictRealtime=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +SystemCallFilter=@system-service +SystemCallFilter=~@privileged @resources +SystemCallArchitectures=native +UMask=0077 +LimitCORE=0 diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index c0d4dd530f..1176a4637e 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -41,6 +41,36 @@ test_kmip_tls = executable( test('nvme-keysd - kmip tls', test_kmip_tls) +if want_kmip + test_kmip_psk = executable( + 'test-keysd-kmip-psk', + [ + 'test-kmip-psk.c', + 'kmip-responses.c', + 'tls-server.c', + '../src/config.c', + '../src/kmip.c', + '../src/kmip-psk.c', + '../src/kmip-tls.c', + '../src/ttlv.c', + ], + include_directories: keysd_inc, + dependencies: [ + ccan_dep, + daemon_util_dep, + libnvme_dep, + openssl_dep, + shared_dep, + ], + ) + + test('nvme-keysd - kmip psk', test_kmip_psk) + + test('nvme-keysd-kmip - --help', nvme_keysd_kmip, args: ['--help']) + test('nvme-keysd-kmip - not socket activated', nvme_keysd_kmip, + should_fail: true) +endif + test('nvme-keysd - --help', nvme_keysd, args: ['--help']) test('nvme-keysd - unknown option', nvme_keysd, args: ['--bogus'], should_fail: true) diff --git a/keysd/tests/test-kmip-psk.c b/keysd/tests/test-kmip-psk.c new file mode 100644 index 0000000000..c56a15b7fc --- /dev/null +++ b/keysd/tests/test-kmip-psk.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "kmip.h" +#include "kmip-psk.h" +#include "kmip-responses.h" +#include "tls-server.h" + +static struct libnvme_global_ctx *ctx; + +// A PSK in the interchange format, made from @raw32. +static char *encoded; +static uint8_t raw32[32], raw48[48]; + +static bool check(bool cond, const char *what) +{ + printf(" - %s [%s]\n", what, cond ? "PASS" : "FAIL"); + return cond; +} + +static int from_object(uint32_t type, const void *data, size_t len, + char psk[KMIP_PSK_MAX]) +{ + return kmip_psk_from_object(ctx, type, data, len, psk); +} + +static bool test_psk_from_object(void) +{ + char psk[KMIP_PSK_MAX], text[KMIP_PSK_MAX + 8]; + bool pass = true; + + printf("test_psk_from_object:\n"); + + pass &= check(!from_object(KMIP_OBJ_SYMMETRIC_KEY, raw32, 32, psk) && + !strcmp(psk, encoded), + "32-byte Symmetric Key"); + pass &= check(!from_object(KMIP_OBJ_SYMMETRIC_KEY, raw48, 48, psk) && + !strncmp(psk, "NVMeTLSkey-1:02:", 16), + "48-byte Symmetric Key uses SHA-384"); + pass &= check(from_object(KMIP_OBJ_SYMMETRIC_KEY, raw48, 40, psk) == + -EMEDIUMTYPE, + "40-byte Symmetric Key"); + + pass &= check(!from_object(KMIP_OBJ_SECRET_DATA, encoded, + strlen(encoded), psk) && + !strcmp(psk, encoded), + "Secret Data"); + snprintf(text, sizeof(text), "%s\n", encoded); + pass &= check(!from_object(KMIP_OBJ_SECRET_DATA, text, strlen(text), + psk) && + !strcmp(psk, encoded), + "Secret Data with a newline"); + pass &= check(from_object(KMIP_OBJ_SECRET_DATA, "secret", 6, psk) == + -EMEDIUMTYPE, + "Secret Data that is not a PSK"); + pass &= check(from_object(KMIP_OBJ_SECRET_DATA, encoded, + strlen(encoded) + 1, psk) == -EMEDIUMTYPE, + "Secret Data with a NUL"); + pass &= check(from_object(KMIP_OBJ_SECRET_DATA, "", 0, psk) == + -EMEDIUMTYPE, + "empty Secret Data"); + pass &= check(from_object(0x01, raw32, 32, psk) == -EMEDIUMTYPE, + "a Certificate"); + + return pass; +} + +/* What the KMIP server sends. */ +enum scenario { + SC_SYMMETRIC_KEY, + SC_SECRET_DATA, + SC_NO_MATCH, + SC_TWO_MATCHES, + SC_LOCATE_FAILS, + SC_SHORT_KEY, + SC_OPAQUE_OBJECT, +}; + +static void send_buf(SSL *ssl, struct ttlv_buf *b) +{ + size_t n; + + SSL_write_ex(ssl, b->data, b->len, &n); + ttlv_buf_free(b); +} + +static uint32_t request_op(const uint8_t *buf, size_t len) +{ + struct ttlv_reader r, msg, item; + struct ttlv_item it; + uint32_t op = 0; + + ttlv_reader_init(&r, buf, len); + if (ttlv_find(&r, KMIP_TAG_REQUEST_MESSAGE, &it) == 1 && + !ttlv_open(&it, &msg) && + ttlv_find(&msg, KMIP_TAG_BATCH_ITEM, &it) == 1 && + !ttlv_open(&it, &item) && + ttlv_find(&item, KMIP_TAG_OPERATION, &it) == 1) + ttlv_get_enum(&it, &op); + + return op; +} + +static void answer_locate(SSL *ssl, enum scenario sc) +{ + struct ttlv_buf b; + + switch (sc) { + case SC_NO_MATCH: + locate_response(&b, 0); + break; + case SC_TWO_MATCHES: + locate_response(&b, 2); + break; + case SC_LOCATE_FAILS: + begin_response(&b, 1, KMIP_OP_LOCATE, 1); // Failed + ttlv_add_enum(&b, KMIP_TAG_RESULT_REASON, 1); // Not Found + ttlv_add_text(&b, KMIP_TAG_RESULT_MESSAGE, "test"); + end_response(&b); + break; + default: + locate_response(&b, 1); + break; + } + send_buf(ssl, &b); +} + +static void answer_get(SSL *ssl, enum scenario sc) +{ + struct ttlv_buf b; + + switch (sc) { + case SC_SECRET_DATA: + get_response(&b, KMIP_OBJ_SECRET_DATA, KMIP_TAG_SECRET_DATA, + KMIP_KEY_FORMAT_OPAQUE, encoded, strlen(encoded)); + break; + case SC_SHORT_KEY: + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, raw32, 16); + break; + case SC_OPAQUE_OBJECT: + // Opaque Object (0x08): not a key object. + get_response(&b, 0x08, 0x420089, KMIP_KEY_FORMAT_OPAQUE, + raw32, 32); + break; + default: + get_response(&b, KMIP_OBJ_SYMMETRIC_KEY, KMIP_TAG_SYMMETRIC_KEY, + KMIP_KEY_FORMAT_RAW, raw32, 32); + break; + } + send_buf(ssl, &b); +} + +static void serve(SSL *ssl, void *arg) +{ + enum scenario sc = *(enum scenario *)arg; + uint8_t buf[1024]; + size_t len; + + while (tls_read_msg(ssl, buf, sizeof(buf), &len)) { + switch (request_op(buf, len)) { + case KMIP_OP_LOCATE: + answer_locate(ssl, sc); + break; + case KMIP_OP_GET: + answer_get(ssl, sc); + break; + default: + return; + } + } +} + +// The test server listens on 127.0.0.1. Nothing listens on 127.0.0.2. +#define LIVE "server = 127.0.0.1\n" +#define DEAD "server = 127.0.0.2\n" + +/* Load a [kmip:test] section with the "server = ..." lines @servers. */ +static struct keysd_config *load_kms(const char *servers, const char *port) +{ + char path[] = "/tmp/nvme-keysd-kmip-XXXXXX"; + struct keysd_config *cfg; + char *ca, *text = NULL; + int fd, len = -1; + + ca = realpath(ca_pem, NULL); + fd = mkstemp(path); + if (ca && fd >= 0) + len = asprintf(&text, "[kmip:test]\n%sport = %s\nca = %s\n", + servers, port, ca); + if (len < 0 || write(fd, text, len) != len) { + fprintf(stderr, "cannot write the config\n"); + exit(EXIT_FAILURE); + } + close(fd); + + cfg = config_load(path); + unlink(path); + free(text); + free(ca); + + return cfg; +} + +/* + * Get a key from a KMS with the servers @servers, starting with the one + * at index @first. The server at index @expect must answer. + */ +// The name lookup of SLOW_NAME takes longer than the KMS timeout. +#define SLOW_NAME "slow.invalid" +#define SLOW_MS 3000 + +static dmn_getaddrinfo_fn lookup_fn; + +static int slow_lookup(const char *host, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +{ + if (!strcmp(host, SLOW_NAME)) { + usleep(SLOW_MS * 1000); + return EAI_AGAIN; + } + + return getaddrinfo(host, service, hints, res); +} + +struct get_result { + bool done; + int ret; + size_t server; + char *psk; +}; + +static void on_psk(int ret, const char *psk, size_t server, void *user_data) +{ + struct get_result *res = user_data; + + res->done = true; + res->ret = ret; + res->server = server; + if (!ret) + snprintf(res->psk, KMIP_PSK_MAX, "%s", psk); +} + +/* Run kmip_get_psk() on an event loop of its own, until it is done. */ +static int get_psk_sync(const struct keysd_kmip *kms, size_t first, + char psk[KMIP_PSK_MAX], size_t *server) +{ + struct get_result res = { .psk = psk }; + struct dmn_resolver *resolver = NULL; + sd_event *event = NULL; + int ret; + + ret = sd_event_new(&event); + if (!ret) + ret = dmn_resolver_new(event, 8, &resolver); + if (!ret && lookup_fn) + dmn_resolver_set_lookup(resolver, lookup_fn); + if (!ret) + ret = kmip_get_psk(resolver, ctx, kms, first, NULL, "key1", + on_psk, &res, NULL); + while (!ret && !res.done) + sd_event_run(event, UINT64_MAX); + if (!ret) + ret = res.ret; + *server = res.server; + + dmn_resolver_free(resolver); + sd_event_unref(event); + + return ret; +} + +static int get_psk_from(enum scenario sc, const char *servers, size_t first, + size_t expect, char psk[KMIP_PSK_MAX]) +{ + struct tls_server srv = { + .cert = certs.srv_ip, + .serve = serve, + .arg = &sc, + }; + const struct keysd_kmip *kms; + struct keysd_config *cfg; + size_t server = 0; + char port[8]; + pid_t pid; + int ret; + + pid = tls_server_start(&srv, port, sizeof(port)); + if (pid < 0) + return -EIO; + + cfg = load_kms(servers, port); + kms = cfg ? config_find_kmip(cfg, "test") : NULL; + ret = kms ? get_psk_sync(kms, first, psk, &server) : -EINVAL; + if (!ret && server != expect) + ret = -EIO; + config_free(cfg); + tls_server_stop(pid); + + return ret; +} + +static int get_psk(enum scenario sc, char psk[KMIP_PSK_MAX]) +{ + return get_psk_from(sc, LIVE, 0, 0, psk); +} + +static bool test_get_psk(void) +{ + char psk[KMIP_PSK_MAX]; + bool pass = true; + + printf("test_get_psk:\n"); + + pass &= check(!get_psk(SC_SYMMETRIC_KEY, psk) && !strcmp(psk, encoded), + "Symmetric Key"); + pass &= check(!get_psk(SC_SECRET_DATA, psk) && !strcmp(psk, encoded), + "Secret Data"); + pass &= check(get_psk(SC_NO_MATCH, psk) == -ENOENT, "no match"); + pass &= check(get_psk(SC_TWO_MATCHES, psk) == -ENOTUNIQ, + "two matches"); + pass &= check(get_psk(SC_LOCATE_FAILS, psk) == -EREMOTEIO, + "Locate fails"); + pass &= check(get_psk(SC_SHORT_KEY, psk) == -EMEDIUMTYPE, + "16-byte key"); + pass &= check(get_psk(SC_OPAQUE_OBJECT, psk) == -EMEDIUMTYPE, + "Opaque Object"); + + return pass; +} + +static bool test_failover(void) +{ + char psk[KMIP_PSK_MAX]; + bool pass = true; + + printf("test_failover:\n"); + + pass &= check(!get_psk_from(SC_SYMMETRIC_KEY, DEAD LIVE, 0, 1, psk) && + !strcmp(psk, encoded), + "second server answers"); + pass &= check(!get_psk_from(SC_SYMMETRIC_KEY, DEAD LIVE, 1, 1, psk), + "start with the second server"); + pass &= check(!get_psk_from(SC_SYMMETRIC_KEY, LIVE DEAD, 1, 0, psk), + "wrap around to the first server"); + pass &= check(!get_psk_from(SC_SYMMETRIC_KEY, LIVE, 5, 0, psk), + "invalid first server"); + pass &= check(get_psk_from(SC_NO_MATCH, LIVE DEAD, 0, 0, psk) == + -ENOENT, + "no match is final"); + pass &= check(get_psk_from(SC_LOCATE_FAILS, LIVE DEAD, 0, 0, psk) == + -ECONNREFUSED, + "server error tries the next server"); + pass &= check(get_psk_from(SC_SYMMETRIC_KEY, DEAD, 0, 0, psk) == + -ECONNREFUSED, + "no server answers"); + + return pass; +} + +static int64_t now_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static bool test_lookup_timeout(void) +{ + char psk[KMIP_PSK_MAX]; + bool pass = true; + int64_t t0; + int ret; + + printf("test_lookup_timeout:\n"); + + lookup_fn = slow_lookup; + t0 = now_ms(); + ret = get_psk_from(SC_SYMMETRIC_KEY, + "server = " SLOW_NAME "\ntimeout = 1s\n" LIVE, + 0, 1, psk); + pass &= check(!ret && !strcmp(psk, encoded), + "second server answers"); + pass &= check(now_ms() - t0 < SLOW_MS, + "without waiting for the first name lookup"); + + ret = get_psk_from(SC_SYMMETRIC_KEY, + "server = " SLOW_NAME "\ntimeout = 1s\n", + 0, 0, psk); + pass &= check(ret == -ETIMEDOUT, "the only server times out"); + lookup_fn = NULL; + + // Let the workers in slow_lookup() end before the next test. + usleep(SLOW_MS * 1000); + + return pass; +} + +int main(void) +{ + bool pass = true; + size_t i; + + signal(SIGPIPE, SIG_IGN); + + for (i = 0; i < sizeof(raw48); i++) + raw48[i] = i; + memcpy(raw32, raw48, sizeof(raw32)); + + ctx = libnvme_create_global_ctx(); + pass &= check(ctx && !libnvmf_export_tls_key(ctx, raw32, 32, &encoded), + "PSK setup"); + pass &= check(tls_test_setup(), "TLS test setup"); + + pass &= test_psk_from_object(); + pass &= test_get_psk(); + pass &= test_failover(); + pass &= test_lookup_timeout(); + + tls_test_cleanup(); + free(encoded); + libnvme_free_global_ctx(ctx); + + fflush(stdout); + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/meson.build b/meson.build index 25f50bfd0f..33427ad448 100644 --- a/meson.build +++ b/meson.build @@ -448,6 +448,8 @@ keysd_opt = keysd_opt.require( keysd_opt = keysd_opt.require(openssl_dep.found() and keyutils_dep.found(), error_message: 'nvme-keysd requires OpenSSL and libkeyutils') want_keysd = keysd_opt.allowed() +want_kmip = get_option('kmip').require(want_keysd, + error_message: 'kmip requires nvme-keysd').allowed() conf.set('CONFIG_DISCOVERD', want_discoverd, description: 'Is nvme-discoverd enabled') conf.set('CONFIG_KEYSD', want_keysd, description: 'Is nvme-keysd enabled') @@ -964,6 +966,7 @@ wanted_dict = { 'nvme-discoverd': want_discoverd, 'mdns': want_mdns, 'nvme-keysd': want_keysd, + 'kmip': want_kmip, 'nvmf-autoconnect': want_nvmf_autoconnect, 'deprecated-cmds': want_deprecated_cmds, 'python bindings ': want_python, diff --git a/meson_options.txt b/meson_options.txt index 0a2f14e656..79f3d6c005 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -53,6 +53,12 @@ option( value: 'disabled', description : 'nvme-keysd daemon, provisions TLS PSKs into the kernel keyring (technology preview; requires fabrics, OpenSSL, libkeyutils and libsystemd >=257)' ) +option( + 'kmip', + type : 'feature', + value: 'auto', + description : 'KMIP key source in nvme-keysd: the nvme-keysd-kmip helper (requires nvme-keysd)' +) option( 'nvmf-autoconnect', type : 'feature', From 0843306407d63f6a07d314c14f454e5a0206286b Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 15:57:06 -0400 Subject: [PATCH 011/263] keysd: split the PSK import from the credential decryption nvme-keysd will also get PSKs from KMIP servers. import_psk() puts a PSK in the keyring, whatever its source. Signed-off-by: Martin Belanger --- keysd/src/import.c | 73 +++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/keysd/src/import.c b/keysd/src/import.c index f40b352fad..ff93802fec 100644 --- a/keysd/src/import.c +++ b/keysd/src/import.c @@ -193,41 +193,29 @@ static void revoke_other_keys(struct libnvme_global_ctx *ctx, free(rc.identities); } -static int import_one(struct import_ctx *ic, const char *source, +/* + * Put the PSK @encoded, in the interchange format, in @keyring for + * @hostnqn and @subsysnqn. @origin says where the PSK comes from, for the + * log. + */ +static int import_psk(struct import_ctx *ic, const char *source, const char *keyring, const char *hostnqn, - const char *subsysnqn, const char *name) + const char *subsysnqn, const char *origin, + const char *encoded) { __cleanup_free char *identity = NULL; - __cleanup_free char *error = NULL; enum libnvmf_hmac_alg hmac; unsigned char version; unsigned char *psk = NULL; - char encoded[CRED_MAX + 1]; size_t psk_len = 0; long keyring_id, key; int r; - if (!cred_name_valid(name)) { - log_err("%s: %s: invalid credential name '%s'", source, - subsysnqn, name); - return -EINVAL; - } - - r = creds_decrypt(ic->creds_dir, name, encoded, sizeof(encoded), - &error); - if (r < 0) { - explicit_bzero(encoded, sizeof(encoded)); - log_err("%s: %s: cannot decrypt credential '%s': %s", source, - subsysnqn, name, error ?: strerror(-r)); - return r; - } - r = libnvmf_import_tls_key_versioned(ic->ctx, encoded, &version, &hmac, &psk_len, &psk); - explicit_bzero(encoded, sizeof(encoded)); if (r < 0) { - log_err("%s: %s: credential '%s' is not a valid PSK: %s", - source, subsysnqn, name, strerror(-r)); + log_err("%s: %s: %s is not a valid PSK: %s", source, + subsysnqn, origin, strerror(-r)); return r; } @@ -264,8 +252,7 @@ static int import_one(struct import_ctx *ic, const char *source, subsysnqn, strerror(-r)); goto out; } - log_info("imported '%s' from credential '%s'", identity, - name); + log_info("imported '%s' from %s", identity, origin); } revoke_other_keys(ic->ctx, source, keyring, hostnqn, subsysnqn, @@ -277,6 +264,40 @@ static int import_one(struct import_ctx *ic, const char *source, return r; } +static int import_cred(struct import_ctx *ic, const char *source, + const char *keyring, const char *hostnqn, + const char *subsysnqn, const char *name) +{ + __cleanup_free char *origin = NULL; + __cleanup_free char *error = NULL; + char encoded[CRED_MAX + 1]; + int r; + + if (!cred_name_valid(name)) { + log_err("%s: %s: invalid credential name '%s'", source, + subsysnqn, name); + return -EINVAL; + } + + r = creds_decrypt(ic->creds_dir, name, encoded, sizeof(encoded), + &error); + if (r < 0) { + explicit_bzero(encoded, sizeof(encoded)); + log_err("%s: %s: cannot decrypt credential '%s': %s", source, + subsysnqn, name, error ?: strerror(-r)); + return r; + } + + if (asprintf(&origin, "credential '%s'", name) < 0) + r = -ENOMEM; + else + r = import_psk(ic, source, keyring, hostnqn, subsysnqn, origin, + encoded); + explicit_bzero(encoded, sizeof(encoded)); + + return r; +} + static void import_conn(const struct libnvmf_config_conn *conn, void *user_data) { @@ -312,8 +333,8 @@ static void import_conn(const struct libnvmf_config_conn *conn, return; } - import_one(ic, source, param(params, "keyring"), hostnqn, subsysnqn, - name); + import_cred(ic, source, param(params, "keyring"), hostnqn, subsysnqn, + name); } static void find_tls_key_source(const struct libnvmf_config_conn *conn, From bb6a80dbb25bbe6ef08594e571d17545a75a6b80 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Mon, 12 Oct 2026 18:28:31 -0400 Subject: [PATCH 012/263] keysd: get PSKs from KMIP servers With key-source = kmip:, nvme-keysd gets the PSK from the KMS through nvme-keysd-kmip, and puts it in the keyring. nvme-keysd reports ready after it imported the keys from systemd credentials. It then gets the KMIP keys in the background, and keeps running, because a KMS may not be reachable at boot. A key that cannot be imported is asked for again, with a delay that doubles up to five minutes. Without a KMIP entry, nvme-keysd still imports the keys and exits. Each KMS gets one request at a time, and the KMSes are asked in parallel. nvme-keysd first asks the server that answered last. Signed-off-by: Martin Belanger --- Documentation/nvme-fabrics.conf.txt | 6 + Documentation/nvme-keysd.txt | 100 +++++++- keysd/meson.build | 1 + keysd/src/import.c | 173 +++++++++++-- keysd/src/import.h | 39 ++- keysd/src/kmip-client.c | 341 ++++++++++++++++++++++++++ keysd/src/kmip-client.h | 50 ++++ keysd/src/main.c | 81 +++++- keysd/systemd/nvme-keysd.service.in | 6 +- keysd/tests/meson.build | 14 ++ keysd/tests/test-kmip-client.c | 366 ++++++++++++++++++++++++++++ keysd/tests/wringer.sh | 17 +- meson.build | 6 + 13 files changed, 1157 insertions(+), 43 deletions(-) create mode 100644 keysd/src/kmip-client.c create mode 100644 keysd/src/kmip-client.h create mode 100644 keysd/tests/test-kmip-client.c diff --git a/Documentation/nvme-fabrics.conf.txt b/Documentation/nvme-fabrics.conf.txt index ac244042af..e5ae3e723e 100644 --- a/Documentation/nvme-fabrics.conf.txt +++ b/Documentation/nvme-fabrics.conf.txt @@ -110,6 +110,12 @@ for what each one means. use 'tls-key': the kernel finds the PSK in the keyring by the host NQN and the subsystem NQN. See linknvme:nvme-keysd[8]. +'kmip:':: + 'tls-key' is the Name of a key on the key management server + (KMS) '', configured in nvme-keysd.conf. nvme-keysd gets the + key and puts it in the keyring. 'kmip' alone is enough when only + one KMS is configured. See linknvme:nvme-keysd[8]. + Any other value also means that 'tls-key' is not the key. libnvme does not check the value: nvme-keysd does. diff --git a/Documentation/nvme-keysd.txt b/Documentation/nvme-keysd.txt index d2d97beb6d..f9a7004542 100644 --- a/Documentation/nvme-keysd.txt +++ b/Documentation/nvme-keysd.txt @@ -26,10 +26,11 @@ This is a technology preview. KEYS ---- nvme-keysd imports every entry of the fabrics configuration that has -'tls-key-source = systemd-creds'. For such an entry, 'tls-key' is the name -of a systemd credential. The credential holds the PSK in the PSK -interchange format ('NVMeTLSkey-1:::'), as created by -'nvme keys gen-tls-psk'. +'tls-key-source = systemd-creds' or 'tls-key-source = kmip'. With +'systemd-creds', 'tls-key' is the name of a systemd credential. The +credential holds the PSK in the PSK interchange format +('NVMeTLSkey-1:::'), as created by 'nvme keys +gen-tls-psk'. nvme-keysd puts the PSK in the keyring named by 'keyring' (default '.nvme'). The key identity is built from the host NQN and the subsystem @@ -37,8 +38,9 @@ NQN, as 'nvme connect' does. A key that is already there is not written again. Any other key for the same host NQN and subsystem NQN is revoked, so that the kernel finds only one key for the pair. -nvme-keysd imports the keys and exits. It never revokes a key because -its entry was removed from the configuration. +Without a 'kmip' entry, nvme-keysd imports the keys and exits. With a +'kmip' entry, it keeps running: see KMIP. nvme-keysd never revokes a key +because its entry was removed from the configuration. The unit does not start when no entry of the fabrics configuration has a 'tls-key-source' other than 'inline'. In that case, nvme-keysd does not @@ -74,6 +76,68 @@ imports the other keys. nvme-keysd needs systemd 257 or later. +KMIP +---- +With 'tls-key-source = kmip:', nvme-keysd gets the PSK from a key +management server (KMS) that supports KMIP 1.4. '' is a +'[kmip:]' section of nvme-keysd.conf. When only one KMS is +configured, 'tls-key-source = kmip' is enough. 'tls-key' is the Name of +the key on the KMS. + +The key on the KMS is one of: + +- Secret Data that holds the PSK in the PSK interchange format. +- A Symmetric Key of 32 bytes (SHA-256) or 48 bytes (SHA-384). + +Only a key in the Active state is used. Two Active keys with the same +Name are an error. + +nvme-keysd itself has no network access. For privilege separation, a +helper process with no privileges handles all communication with the +KMS, over TLS. A client certificate is used when one is configured. +The helper logs its errors in the journal, under the identifier +nvme-keysd-kmip. + +The servers of a KMS are nodes of one cluster, and hold the same keys. +They are tried in order, until one answers. Different KMSes are asked +in parallel. + +nvme-keysd does not delay the boot for a KMS. The services that start +after nvme-keysd find the keys from systemd credentials in the keyring, +but a KMIP key can come later. nvme-keysd keeps running and gets the +KMIP keys in the background. When it cannot get a key, it tries again +after 5 seconds, then after twice the previous delay, up to 5 minutes. +A connection that needs a KMIP key can therefore start before the key +is in the keyring. nvme-discoverd connects again later. A single 'nvme +connect' does not. + +Setup example. The client private key is a systemd credential in +@SYSCONFDIR@/nvme/creds/kmip/. systemd gives it to the helper as +'kmip_', so '--name' must be 'kmip_' and the file name: + + # systemd-creds encrypt --name=kmip_my-client-key client-key.pem \ + @SYSCONFDIR@/nvme/creds/kmip/my-client-key + +The CA and the client certificate are not secret. They must be readable +by any user, because the helper runs without privileges. In +nvme-keysd.conf: + + [kmip:my-kms] + server = kms1.example.com + server = kms2.example.com + ca = @SYSCONFDIR@/nvme/kmip/ca.pem + client-cert = @SYSCONFDIR@/nvme/kmip/client.pem + client-key = my-client-key + +and in nvme-fabrics.conf: + + [Subsystem] + nqn = nqn.2024-01.com.example:storage.vol1 + controller = transport=tcp;traddr=192.0.2.10;trsvcid=4420 + tls = true + tls-key-source = kmip:my-kms + tls-key = my-vol1-psk + OPTIONS ------- -c :: @@ -105,7 +169,7 @@ OPTIONS CONFIGURATION ------------- nvme-keysd.conf holds the daemon's own settings, in a '[Global]' -section: +section, and the KMSes, in '[kmip:]' sections: [Global] debug-level = info @@ -113,6 +177,25 @@ section: 'debug-level' (err, warn, info, or debug; default info) sets the log threshold. '--debug' on the command line overrides it. +Each '[kmip:]' section describes a KMS: + +'server':: + Host name or IP address. Required. Repeat it for each server of + the cluster. +'port':: + TCP port of the servers. Default 5696. +'ca':: + CA certificate that signed the server certificates (PEM). Default: + the system CAs. +'client-cert':: + Client certificate (PEM). Requires 'client-key'. +'client-key':: + File name of the client private key in + @SYSCONFDIR@/nvme/creds/kmip/. Requires 'client-cert'. +'timeout':: + Time limit for one server: connect, TLS handshake and requests. + Default 10s. Each TCP connect fails after 3 seconds at most. + A missing file, or a missing key, keeps the default. FILES @@ -123,6 +206,9 @@ FILES @SYSCONFDIR@/nvme/creds/:: The encrypted credentials; see CREDENTIALS. +@SYSCONFDIR@/nvme/creds/kmip/:: + The encrypted client private keys for the KMSes; see KMIP. + SEE ALSO -------- nvme-fabrics.conf(5) diff --git a/keysd/meson.build b/keysd/meson.build index b56ff9190d..8ae9db146e 100644 --- a/keysd/meson.build +++ b/keysd/meson.build @@ -10,6 +10,7 @@ keysd_sources = files( 'src/config.c', 'src/creds.c', 'src/import.c', + 'src/kmip-client.c', 'src/main.c', ) diff --git a/keysd/src/import.c b/keysd/src/import.c index ff93802fec..862ce284b4 100644 --- a/keysd/src/import.c +++ b/keysd/src/import.c @@ -17,13 +17,18 @@ #include #include #include +#include +#include "config.h" #include "creds.h" #include "import.h" struct import_ctx { struct libnvme_global_ctx *ctx; const char *creds_dir; + const struct keysd_config *cfg; + struct kmip_job *jobs; + size_t n_jobs; char *default_hostnqn; // system identity, for entries without one bool default_read; // default_hostnqn was looked up char **done; // " " already handled @@ -298,6 +303,97 @@ static int import_cred(struct import_ctx *ic, const char *source, return r; } +static void free_job(struct kmip_job *job) +{ + free(job->source); + free(job->keyring); + free(job->hostnqn); + free(job->subsysnqn); + free(job->name); +} + +/* Find the KMS of "kmip:", or of "kmip" when there is only one. */ +static const struct keysd_kmip *find_kms(struct import_ctx *ic, + const char *source, const char *subsysnqn, const char *src) +{ + const char *name = shr_startswith(src, "kmip:"); + const struct keysd_kmip *kms; + + if (!name) { + if (ic->cfg->nr_kmip == 1) + return &ic->cfg->kmip[0]; + if (ic->cfg->nr_kmip) + log_err("%s: %s: several KMSes: use tls-key-source = kmip:", + source, subsysnqn); + else + log_err("%s: %s: tls-key-source is kmip but no KMS is configured, skipped", + source, subsysnqn); + return NULL; + } + + kms = config_find_kmip(ic->cfg, name); + if (!kms) + log_err("%s: %s: KMS '%s' is not configured", source, + subsysnqn, name); + + return kms; +} + +/* Collect a key to get from a KMS. */ +static void add_kmip_job(struct import_ctx *ic, const char *source, + const char *keyring, const char *hostnqn, + const char *subsysnqn, const char *src, + const char *name) +{ + const struct keysd_kmip *kms; + struct kmip_job *jobs, *job; + +#ifndef CONFIG_KMIP + log_warn("%s: %s: this build does not support tls-key-source '%s', skipped", + source, subsysnqn, src); + return; +#endif + + kms = find_kms(ic, source, subsysnqn, src); + if (!kms) + return; + + jobs = realloc(ic->jobs, (ic->n_jobs + 1) * sizeof(*jobs)); + if (!jobs) { + log_err("%s: %s: out of memory", source, subsysnqn); + return; + } + ic->jobs = jobs; + + job = &ic->jobs[ic->n_jobs]; + job->kms = kms; + job->source = strdup(source); + job->keyring = shr_xstrdup(keyring); + job->hostnqn = strdup(hostnqn); + job->subsysnqn = strdup(subsysnqn); + job->name = strdup(name); + if (!job->source || (keyring && !job->keyring) || !job->hostnqn || + !job->subsysnqn || !job->name) { + log_err("%s: %s: out of memory", source, subsysnqn); + free_job(job); + return; + } + ic->n_jobs++; +} + +int import_kmip_psk(struct import_ctx *ic, const struct kmip_job *job, + const char *encoded) +{ + __cleanup_free char *origin = NULL; + + if (asprintf(&origin, "KMS '%s', key '%s'", job->kms->name, + job->name) < 0) + return -ENOMEM; + + return import_psk(ic, job->source, job->keyring, job->hostnqn, + job->subsysnqn, origin, encoded); +} + static void import_conn(const struct libnvmf_config_conn *conn, void *user_data) { @@ -320,21 +416,22 @@ static void import_conn(const struct libnvmf_config_conn *conn, if (!first_time(ic, hostnqn, subsysnqn)) return; - if (!streq(src, "systemd-creds")) { - log_warn("%s: %s: tls-key-source '%s' is not supported, skipped", - source, subsysnqn, src); - return; - } - name = param(params, "tls-key"); if (!name) { - log_warn("%s: %s: tls-key-source is systemd-creds but tls-key is not set, skipped", - source, subsysnqn); + log_warn("%s: %s: tls-key-source is %s but tls-key is not set, skipped", + source, subsysnqn, src); return; } - import_cred(ic, source, param(params, "keyring"), hostnqn, subsysnqn, - name); + if (streq(src, "kmip") || shr_startswith(src, "kmip:")) + add_kmip_job(ic, source, param(params, "keyring"), hostnqn, + subsysnqn, src, name); + else if (streq(src, "systemd-creds")) + import_cred(ic, source, param(params, "keyring"), hostnqn, + subsysnqn, name); + else + log_warn("%s: %s: tls-key-source '%s' is not supported, skipped", + source, subsysnqn, src); } static void find_tls_key_source(const struct libnvmf_config_conn *conn, @@ -367,29 +464,57 @@ bool import_needed(struct libnvme_global_ctx *ctx, const char *fabrics_conf) return found; } -void import_keys(struct libnvme_global_ctx *ctx, const char *fabrics_conf, - const char *creds_dir) +struct import_ctx *import_new(struct libnvme_global_ctx *ctx, + const char *creds_dir, const struct keysd_config *cfg) +{ + struct import_ctx *ic; + + ic = calloc(1, sizeof(*ic)); + if (!ic) + return NULL; + ic->ctx = ctx; + ic->creds_dir = creds_dir; + ic->cfg = cfg; + + return ic; +} + +void import_free(struct import_ctx *ic) { - struct import_ctx ic = { - .ctx = ctx, - .creds_dir = creds_dir, - }; - struct libnvmf_config *cfg; size_t i; + + if (!ic) + return; + + for (i = 0; i < ic->n_jobs; i++) + free_job(&ic->jobs[i]); + free(ic->jobs); + for (i = 0; i < ic->n_done; i++) + free(ic->done[i]); + free(ic->done); + free(ic->default_hostnqn); + free(ic); +} + +void import_keys(struct import_ctx *ic, const char *fabrics_conf) +{ + struct libnvmf_config *cfg; int r; - r = libnvmf_config_read(ctx, fabrics_conf, &cfg); + r = libnvmf_config_read(ic->ctx, fabrics_conf, &cfg); if (r < 0) { log_err("cannot read the fabrics configuration: %s", strerror(-r)); return; } - libnvmf_config_conn_for_each(cfg, import_conn, &ic); - - for (i = 0; i < ic.n_done; i++) - free(ic.done[i]); - free(ic.done); - free(ic.default_hostnqn); + libnvmf_config_conn_for_each(cfg, import_conn, ic); libnvmf_config_free(cfg); } + +size_t import_kmip_jobs(struct import_ctx *ic, const struct kmip_job **jobs) +{ + *jobs = ic->jobs; + + return ic->n_jobs; +} diff --git a/keysd/src/import.h b/keysd/src/import.h index 48b1258808..8fac45c82c 100644 --- a/keysd/src/import.h +++ b/keysd/src/import.h @@ -8,9 +8,23 @@ #pragma once #include +#include +struct import_ctx; +struct keysd_config; +struct keysd_kmip; struct libnvme_global_ctx; +/* A key that nvme-keysd gets from a KMS. */ +struct kmip_job { + char *source; // the configuration file, for the log + char *keyring; // NULL: .nvme + char *hostnqn; + char *subsysnqn; + char *name; // the KMIP Name, from tls-key + const struct keysd_kmip *kms; +}; + /* * Return true if an entry of the fabrics configuration @fabrics_conf (NULL * for the default) has a key source other than "inline". Without such an @@ -18,6 +32,14 @@ struct libnvme_global_ctx; */ bool import_needed(struct libnvme_global_ctx *ctx, const char *fabrics_conf); +/* + * @creds_dir holds the encrypted credentials. systemd-creds decrypts + * them. @cfg holds the KMSes. Free with import_free(). + */ +struct import_ctx *import_new(struct libnvme_global_ctx *ctx, + const char *creds_dir, const struct keysd_config *cfg); +void import_free(struct import_ctx *ic); + /* * Put every TLS PSK that the fabrics configuration @fabrics_conf (NULL for * the default) takes from a systemd credential into its keyring. A key @@ -25,8 +47,17 @@ bool import_needed(struct libnvme_global_ctx *ctx, const char *fabrics_conf); * host NQN and subsystem NQN is revoked, so the kernel's lookup by NQNs * finds only this one. * - * @creds_dir holds the encrypted credentials. systemd-creds decrypts - * them. + * The keys that come from a KMS are only collected: see + * import_kmip_jobs(). + */ +void import_keys(struct import_ctx *ic, const char *fabrics_conf); + +/* Return the keys to get from a KMS, in @jobs. */ +size_t import_kmip_jobs(struct import_ctx *ic, const struct kmip_job **jobs); + +/* + * Put the PSK @encoded, in the interchange format, from the KMS of @job + * into its keyring, as import_keys() does. Returns 0 or a negative errno. */ -void import_keys(struct libnvme_global_ctx *ctx, const char *fabrics_conf, - const char *creds_dir); +int import_kmip_psk(struct import_ctx *ic, const struct kmip_job *job, + const char *encoded); diff --git a/keysd/src/kmip-client.c b/keysd/src/kmip-client.c new file mode 100644 index 0000000000..f7299f79ac --- /dev/null +++ b/keysd/src/kmip-client.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "config.h" +#include "import.h" +#include "kmip-client.h" +#include "kmip-varlink.h" + +// Margin over the time the helper may take, for its start and DNS. +#define CALL_MARGIN_USEC (5 * 1000000ULL) + +struct kmip_call { + const struct kmip_job *job; + uint64_t next_try; // CLOCK_MONOTONIC, in usec + uint64_t delay; // before the next retry + bool done; +}; + +/* The calls to one KMS. Only one is active at a time. */ +struct kms_queue { + struct kmip_client *c; + const struct keysd_kmip *kms; + size_t server; // the last server that answered + struct kmip_call **calls; + size_t nr_calls; + struct kmip_call *active; + sd_varlink *link; + sd_event_source *timer; +}; + +struct kmip_client { + sd_event *event; + const struct kmip_client_cfg *cfg; + struct kmip_call *calls; + size_t nr_calls; + struct kms_queue *queues; + size_t nr_queues; + size_t pending; +}; + +static void queue_next(struct kms_queue *q); + +static void notify_status(struct kmip_client *c) +{ + if (c->pending) + sd_notifyf(0, "STATUS=%zu KMIP key(s) not imported yet", + c->pending); + else + sd_notify(0, "STATUS=All KMIP keys imported"); +} + +static void call_done(struct kms_queue *q, struct kmip_call *call) +{ + call->done = true; + q->c->pending--; + notify_status(q->c); +} + +static void call_failed(struct kms_queue *q, struct kmip_call *call, + const char *why) +{ + const struct kmip_job *job = call->job; + uint64_t now = 0; + + sd_event_now(q->c->event, CLOCK_MONOTONIC, &now); + call->next_try = now + call->delay; + + log_err("%s: %s: KMS '%s', key '%s': %s, retry in %llu s", + job->source, job->subsysnqn, q->kms->name, job->name, why, + (unsigned long long)(call->delay / 1000000)); + + call->delay *= 2; + if (call->delay > q->c->cfg->retry_max_usec) + call->delay = q->c->cfg->retry_max_usec; +} + +static void end_call(struct kms_queue *q) +{ + q->link = sd_varlink_close_unref(q->link); + q->active = NULL; +} + +static int on_reply(sd_varlink *link, sd_json_variant *params, + const char *error_id, sd_varlink_reply_flags_t flags, + void *userdata) +{ + struct kms_queue *q = userdata; + struct kmip_call *call = q->active; + sd_json_variant *key, *server; + int r; + + if (error_id) { + call_failed(q, call, error_id); + goto out; + } + + key = sd_json_variant_by_key(params, "key"); + if (!key || !sd_json_variant_is_string(key)) { + call_failed(q, call, "no key in the reply"); + goto out; + } + + server = sd_json_variant_by_key(params, "server"); + if (server && sd_json_variant_is_unsigned(server) && + sd_json_variant_unsigned(server) < q->kms->nr_servers) + q->server = sd_json_variant_unsigned(server); + + r = q->c->cfg->import(call->job, sd_json_variant_string(key), + q->c->cfg->arg); + if (r < 0) + call_failed(q, call, strerror(-r)); + else + call_done(q, call); + +out: + end_call(q); + queue_next(q); + + return 0; +} + +static int start_call(struct kms_queue *q, struct kmip_call *call) +{ + const struct kmip_job *job = call->job; + uint64_t timeout; + int r; + + r = sd_varlink_connect_address(&q->link, q->c->cfg->address); + if (r < 0) + return r; + + // The helper may try every server of the KMS. + timeout = (uint64_t)q->kms->timeout_ms * 1000 * q->kms->nr_servers + + CALL_MARGIN_USEC; + + sd_varlink_set_userdata(q->link, q); + r = sd_varlink_attach_event(q->link, q->c->event, 0); + if (r >= 0) + r = sd_varlink_set_input_sensitive(q->link); + if (r >= 0) + r = sd_varlink_bind_reply(q->link, on_reply); + if (r >= 0) + r = sd_varlink_set_relative_timeout(q->link, timeout); + if (r >= 0) + r = sd_varlink_invokebo(q->link, KMIP_VARLINK_GET_KEY, + SD_JSON_BUILD_PAIR_STRING("kms", q->kms->name), + SD_JSON_BUILD_PAIR_STRING("name", job->name), + SD_JSON_BUILD_PAIR_UNSIGNED("server", + q->server)); + if (r < 0) { + q->link = sd_varlink_close_unref(q->link); + return r; + } + q->active = call; + + return 0; +} + +static int on_timer(sd_event_source *s, uint64_t usec, void *userdata) +{ + queue_next(userdata); + + return 0; +} + +static void arm_timer(struct kms_queue *q, uint64_t when) +{ + int r; + + if (!q->timer) { + r = sd_event_add_time(q->c->event, &q->timer, CLOCK_MONOTONIC, + when, 0, on_timer, q); + if (r < 0) + log_err("KMS '%s': cannot add a timer: %s", + q->kms->name, strerror(-r)); + return; + } + + sd_event_source_set_time(q->timer, when); + sd_event_source_set_enabled(q->timer, SD_EVENT_ONESHOT); +} + +/* Start the next call that is due, or wait for the first one. */ +static void queue_next(struct kms_queue *q) +{ + struct kmip_call *call, *next = NULL; + uint64_t now = 0; + char why[96]; + size_t i; + int r; + + while (!q->active) { + sd_event_now(q->c->event, CLOCK_MONOTONIC, &now); + + call = NULL; + next = NULL; + for (i = 0; i < q->nr_calls; i++) { + if (q->calls[i]->done) + continue; + if (q->calls[i]->next_try <= now) { + call = q->calls[i]; + break; + } + if (!next || q->calls[i]->next_try < next->next_try) + next = q->calls[i]; + } + + if (!call) { + if (next) + arm_timer(q, next->next_try); + return; + } + + r = start_call(q, call); + if (r < 0) { + snprintf(why, sizeof(why), + "cannot reach nvme-keysd-kmip: %s", + strerror(-r)); + call_failed(q, call, why); + } + } +} + +static struct kms_queue *find_queue(struct kmip_client *c, + const struct keysd_kmip *kms) +{ + size_t i; + + for (i = 0; i < c->nr_queues; i++) + if (c->queues[i].kms == kms) + return &c->queues[i]; + + return NULL; +} + +static int add_to_queue(struct kmip_client *c, struct kmip_call *call) +{ + struct kms_queue *q = find_queue(c, call->job->kms); + struct kmip_call **calls; + + if (!q) { + q = &c->queues[c->nr_queues++]; + q->c = c; + q->kms = call->job->kms; + } + + calls = realloc(q->calls, (q->nr_calls + 1) * sizeof(*calls)); + if (!calls) + return -ENOMEM; + q->calls = calls; + q->calls[q->nr_calls++] = call; + + return 0; +} + +int kmip_client_new(sd_event *event, const struct kmip_client_cfg *cfg, + const struct kmip_job *jobs, size_t nr_jobs, + struct kmip_client **out) +{ + struct kmip_client *c; + size_t i; + int r; + + c = calloc(1, sizeof(*c)); + if (!c) + return -ENOMEM; + c->event = event; + c->cfg = cfg; + + // At most one queue per job. + c->calls = calloc(nr_jobs, sizeof(*c->calls)); + c->queues = calloc(nr_jobs, sizeof(*c->queues)); + if (nr_jobs && (!c->calls || !c->queues)) { + r = -ENOMEM; + goto err; + } + + for (i = 0; i < nr_jobs; i++) { + c->calls[i].job = &jobs[i]; + c->calls[i].delay = cfg->retry_usec; + r = add_to_queue(c, &c->calls[i]); + if (r < 0) + goto err; + } + c->nr_calls = nr_jobs; + c->pending = nr_jobs; + notify_status(c); + + for (i = 0; i < c->nr_queues; i++) + queue_next(&c->queues[i]); + + *out = c; + + return 0; + +err: + kmip_client_free(c); + + return r; +} + +size_t kmip_client_pending(const struct kmip_client *c) +{ + return c->pending; +} + +void kmip_client_free(struct kmip_client *c) +{ + struct kms_queue *q; + size_t i; + + if (!c) + return; + + for (i = 0; i < c->nr_queues; i++) { + q = &c->queues[i]; + sd_varlink_close_unref(q->link); + sd_event_source_disable_unref(q->timer); + free(q->calls); + } + free(c->queues); + free(c->calls); + free(c); +} diff --git a/keysd/src/kmip-client.h b/keysd/src/kmip-client.h new file mode 100644 index 0000000000..e45243b26b --- /dev/null +++ b/keysd/src/kmip-client.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +#include + +struct kmip_job; + +/* + * Gets keys from the KMSes through nvme-keysd-kmip, on an sd-event loop. + * Each KMS gets one request at a time, and the KMSes are asked in + * parallel. A key that cannot be imported is asked for again later. + */ +struct kmip_client; + +/* + * Put the PSK @encoded of @job in the keyring. Returns 0 or a negative + * errno. + */ +typedef int (*kmip_import_fn)(const struct kmip_job *job, const char *encoded, + void *arg); + +struct kmip_client_cfg { + const char *address; // Varlink socket of nvme-keysd-kmip + uint64_t retry_usec; // first retry delay, doubled up to retry_max + uint64_t retry_max_usec; + kmip_import_fn import; + void *arg; // for @import +}; + +/* + * Start getting the keys @jobs. @jobs and @cfg must stay valid until + * kmip_client_free(). + */ +int kmip_client_new(sd_event *event, const struct kmip_client_cfg *cfg, + const struct kmip_job *jobs, size_t nr_jobs, + struct kmip_client **out); + +/* Return the number of keys not imported yet. */ +size_t kmip_client_pending(const struct kmip_client *c); + +void kmip_client_free(struct kmip_client *c); diff --git a/keysd/src/main.c b/keysd/src/main.c index 701692fb8d..d9f3dc83ed 100644 --- a/keysd/src/main.c +++ b/keysd/src/main.c @@ -12,13 +12,23 @@ #include #include #include +#include #include +#include +#include + #include #include #include "config.h" #include "import.h" +#include "kmip-client.h" +#include "kmip-varlink.h" + +// Retry delays for a key that a KMS did not deliver. +#define KMIP_RETRY_USEC (5 * 1000000ULL) +#define KMIP_RETRY_MAX_USEC (5 * 60 * 1000000ULL) struct keysd_ctx { struct libnvme_global_ctx *nvme_ctx; // libnvme logging @@ -50,6 +60,52 @@ static bool should_start(void) return false; } +static int import_kmip(const struct kmip_job *job, const char *encoded, + void *arg) +{ + return import_kmip_psk(arg, job, encoded); +} + +/* + * Get the keys from the KMSes. Runs until SIGTERM: a key that cannot be + * imported is asked for again later. + */ +static int run_kmip(struct import_ctx *ic, const struct kmip_job *jobs, + size_t nr_jobs) +{ + const struct kmip_client_cfg cfg = { + .address = KMIP_VARLINK_ADDRESS, + .retry_usec = KMIP_RETRY_USEC, + .retry_max_usec = KMIP_RETRY_MAX_USEC, + .import = import_kmip, + .arg = ic, + }; + struct kmip_client *client = NULL; + sd_event *event = NULL; + int r; + + r = sd_event_default(&event); + if (r >= 0) + r = sd_event_add_signal(event, NULL, + SIGTERM | SD_EVENT_SIGNAL_PROCMASK, + NULL, NULL); + if (r >= 0) + r = sd_event_add_signal(event, NULL, + SIGINT | SD_EVENT_SIGNAL_PROCMASK, + NULL, NULL); + if (r >= 0) + r = kmip_client_new(event, &cfg, jobs, nr_jobs, &client); + if (r >= 0) + r = sd_event_loop(event); + if (r < 0) + log_err("KMIP: %s", strerror(-r)); + + kmip_client_free(client); + sd_event_unref(event); + + return r; +} + static void usage(const char *prog) { printf("Usage: %s [OPTIONS]\n" @@ -81,7 +137,10 @@ int main(int argc, char **argv) char *config_path_abs = NULL; char *fabrics_path_abs = NULL; char *creds_path_abs = NULL; - int r, c; + const struct kmip_job *jobs; + struct import_ctx *ic; + size_t nr_jobs; + int r = 0, c; while ((c = getopt_long(argc, argv, "c:dh", long_opts, NULL)) != -1) { @@ -169,14 +228,28 @@ int main(int argc, char **argv) return r; } - import_keys(ctx.nvme_ctx, ctx.fabrics_conf, ctx.creds_dir); - log_info("keys imported, exiting"); + ic = import_new(ctx.nvme_ctx, ctx.creds_dir, ctx.cfg); + if (!ic) { + log_err("import_new: failed"); + return 1; + } + import_keys(ic, ctx.fabrics_conf); + nr_jobs = import_kmip_jobs(ic, &jobs); + if (!nr_jobs) + log_info("keys imported, exiting"); + + // The units ordered after nvme-keysd need the local keys only. + sd_notify(0, "READY=1"); + + if (nr_jobs) + r = run_kmip(ic, jobs, nr_jobs) < 0 ? 1 : 0; + import_free(ic); config_free(ctx.cfg); libnvme_free_global_ctx(ctx.nvme_ctx); free(config_path_abs); free(fabrics_path_abs); free(creds_path_abs); - return 0; + return r; } diff --git a/keysd/systemd/nvme-keysd.service.in b/keysd/systemd/nvme-keysd.service.in index d6457522ab..347b1886aa 100644 --- a/keysd/systemd/nvme-keysd.service.in +++ b/keysd/systemd/nvme-keysd.service.in @@ -5,9 +5,13 @@ Documentation=man:nvme-keysd(8) Before=remote-fs.target Wants=systemd-creds.socket After=systemd-creds.socket +@KEYSD_WANTS_KMIP@ +@KEYSD_AFTER_KMIP@ [Service] -Type=oneshot +# READY=1 comes after the local keys are imported. With KMIP keys, +# nvme-keysd keeps running and gets them in the background. +Type=notify # Skip the unit, and the module load, when there is nothing to do. ExecCondition=@SBINDIR@/nvme-keysd --should-start # nvme_keyring creates the .nvme keyring. nvme_core loads it only with diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index 1176a4637e..142aa55bbf 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -66,6 +66,20 @@ if want_kmip test('nvme-keysd - kmip psk', test_kmip_psk) + test_kmip_client = executable( + 'test-keysd-kmip-client', + ['test-kmip-client.c', '../src/config.c', '../src/kmip-client.c'], + include_directories: keysd_inc, + dependencies: [ + ccan_dep, + daemon_util_dep, + libsystemd_dep, + shared_dep, + ], + ) + + test('nvme-keysd - kmip client', test_kmip_client) + test('nvme-keysd-kmip - --help', nvme_keysd_kmip, args: ['--help']) test('nvme-keysd-kmip - not socket activated', nvme_keysd_kmip, should_fail: true) diff --git a/keysd/tests/test-kmip-client.c b/keysd/tests/test-kmip-client.c new file mode 100644 index 0000000000..d5f4991bca --- /dev/null +++ b/keysd/tests/test-kmip-client.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "config.h" +#include "import.h" +#include "kmip-client.h" +#include "kmip-varlink.h" + +#define MAX_CALLS 16 + +/* + * A fake nvme-keysd-kmip. It answers GetKey after a short delay, so that + * calls overlap. The key of "name" is "PSK-name". It answers from server + * 1 when the KMS has two servers. + * + * "fail-once" fails the first call with KmsFailed. + */ +struct fake_call { + char kms[32]; + char name[32]; + uint64_t server; +}; + +static struct fake { + sd_event *event; + struct fake_call calls[MAX_CALLS]; + int nr_calls; + int in_flight; + int max_in_flight; + int in_flight_kms[2]; // for KMS "a" and "b" + int max_in_flight_kms; +} fake; + +// Imports, done by the client. +static struct { + int nr; + int expected; // stop the loop after this many imports + int fail_left; // imports to fail before one succeeds + bool bad_key; +} imports; + +static struct kmip_client *client; +static sd_varlink_server *server; + +static bool check(bool cond, const char *what) +{ + printf(" - %s [%s]\n", what, cond ? "PASS" : "FAIL"); + return cond; +} + +struct pending_reply { + sd_varlink *link; + struct fake_call *call; +}; + +static int kms_index(const char *kms) +{ + return strcmp(kms, "a") ? 1 : 0; +} + +static int send_reply(sd_event_source *s, uint64_t usec, void *userdata) +{ + struct pending_reply *pr = userdata; + struct fake_call *call = pr->call; + char key[48]; + + fake.in_flight--; + fake.in_flight_kms[kms_index(call->kms)]--; + + if (!strcmp(call->name, "fail-once") && + call == &fake.calls[0]) + sd_varlink_error(pr->link, KMIP_VARLINK_KMS_FAILED, NULL); + else { + snprintf(key, sizeof(key), "PSK-%s", call->name); + sd_varlink_replybo(pr->link, + SD_JSON_BUILD_PAIR_STRING("key", key), + SD_JSON_BUILD_PAIR_UNSIGNED("server", + kms_index(call->kms))); + } + + sd_varlink_unref(pr->link); + free(pr); + sd_event_source_disable_unref(s); + + return 0; +} + +static int fake_get_key(sd_varlink *link, sd_json_variant *params, + sd_varlink_method_flags_t flags, void *userdata) +{ + struct pending_reply *pr; + struct fake_call *call; + sd_json_variant *v; + uint64_t now; + int i; + + if (fake.nr_calls == MAX_CALLS) + return sd_varlink_error(link, KMIP_VARLINK_KMS_FAILED, NULL); + call = &fake.calls[fake.nr_calls++]; + + v = sd_json_variant_by_key(params, "kms"); + snprintf(call->kms, sizeof(call->kms), "%s", + v ? sd_json_variant_string(v) : ""); + v = sd_json_variant_by_key(params, "name"); + snprintf(call->name, sizeof(call->name), "%s", + v ? sd_json_variant_string(v) : ""); + v = sd_json_variant_by_key(params, "server"); + call->server = v ? sd_json_variant_unsigned(v) : 99; + + fake.in_flight++; + if (fake.in_flight > fake.max_in_flight) + fake.max_in_flight = fake.in_flight; + i = kms_index(call->kms); + fake.in_flight_kms[i]++; + if (fake.in_flight_kms[i] > fake.max_in_flight_kms) + fake.max_in_flight_kms = fake.in_flight_kms[i]; + + pr = calloc(1, sizeof(*pr)); + if (!pr) + return -ENOMEM; + pr->link = sd_varlink_ref(link); + pr->call = call; + + sd_event_now(fake.event, CLOCK_MONOTONIC, &now); + + return sd_event_add_time(fake.event, NULL, CLOCK_MONOTONIC, + now + 20000, 0, send_reply, pr); +} + +static int test_import(const struct kmip_job *job, const char *encoded, + void *arg) +{ + char key[48]; + + snprintf(key, sizeof(key), "PSK-%s", job->name); + if (strcmp(encoded, key)) + imports.bad_key = true; + + if (imports.fail_left) { + imports.fail_left--; + return -EIO; + } + imports.nr++; + + if (imports.nr == imports.expected) + sd_event_exit(fake.event, 0); + + return 0; +} + +/* Load nvme-keysd.conf with KMS "a" (one server) and "b" (two servers). */ +static struct keysd_config *load_cfg(void) +{ + static const char text[] = + "[kmip:a]\nserver = 192.0.2.1\n" + "[kmip:b]\nserver = 192.0.2.2\nserver = 192.0.2.3\n"; + char path[] = "/tmp/nvme-keysd-client-XXXXXX"; + struct keysd_config *cfg; + int fd; + + fd = mkstemp(path); + if (fd < 0 || shr_write_all(fd, text, sizeof(text) - 1)) { + fprintf(stderr, "cannot write the config\n"); + exit(EXIT_FAILURE); + } + close(fd); + cfg = config_load(path); + unlink(path); + + return cfg; +} + +static void set_job(struct kmip_job *job, const struct keysd_config *cfg, + const char *kms, const char *name) +{ + memset(job, 0, sizeof(*job)); + job->kms = config_find_kmip(cfg, kms); + job->source = strdup("test.conf"); + job->subsysnqn = strdup("nqn.test"); + job->name = strdup(name); +} + +static void free_jobs(struct kmip_job *jobs, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + free(jobs[i].source); + free(jobs[i].subsysnqn); + free(jobs[i].name); + } +} + +/* + * Run the client on @jobs against the fake helper, until every key is + * imported or @limit_usec passes. + */ +static void run(const char *address, const struct kmip_job *jobs, size_t n, + uint64_t limit_usec) +{ + const struct kmip_client_cfg cfg = { + .address = address, + .retry_usec = 10000, + .retry_max_usec = 40000, + .import = test_import, + }; + sd_event_source *limit; + uint64_t now; + + memset(&fake.calls, 0, sizeof(fake.calls)); + fake.nr_calls = 0; + fake.max_in_flight = 0; + fake.max_in_flight_kms = 0; + imports.nr = 0; + imports.expected = n; + imports.bad_key = false; + + // A loop cannot run again after it ends: use a new one. + if (sd_event_new(&fake.event) < 0 || + sd_varlink_server_attach_event(server, fake.event, 0) < 0) { + fprintf(stderr, "event loop setup failed\n"); + exit(EXIT_FAILURE); + } + + // A NULL callback ends the loop. + sd_event_now(fake.event, CLOCK_MONOTONIC, &now); + if (sd_event_add_time(fake.event, &limit, CLOCK_MONOTONIC, + now + limit_usec, 0, NULL, NULL) < 0) { + fprintf(stderr, "sd_event_add_time failed\n"); + exit(EXIT_FAILURE); + } + + if (kmip_client_new(fake.event, &cfg, jobs, n, &client) < 0) { + fprintf(stderr, "kmip_client_new failed\n"); + exit(EXIT_FAILURE); + } + sd_event_loop(fake.event); + kmip_client_free(client); + client = NULL; + sd_event_source_disable_unref(limit); + sd_varlink_server_detach_event(server); + fake.event = sd_event_unref(fake.event); +} + +static bool test_parallel(const char *address, + const struct keysd_config *cfg) +{ + struct kmip_job jobs[4]; + bool pass = true; + int i, b_calls = 0; + + printf("test_parallel:\n"); + + set_job(&jobs[0], cfg, "a", "k1"); + set_job(&jobs[1], cfg, "a", "k2"); + set_job(&jobs[2], cfg, "b", "k3"); + set_job(&jobs[3], cfg, "b", "k4"); + run(address, jobs, 4, 2000000); + + pass &= check(imports.nr == 4 && !imports.bad_key, + "all keys imported"); + pass &= check(fake.max_in_flight_kms == 1, + "one call at a time per KMS"); + pass &= check(fake.max_in_flight == 2, "KMSes asked in parallel"); + + for (i = 0; i < fake.nr_calls; i++) { + if (strcmp(fake.calls[i].kms, "b")) + continue; + if (b_calls) + pass &= check(fake.calls[i].server == 1, + "next call asks the last server first"); + else + pass &= check(fake.calls[i].server == 0, + "first call asks server 0 first"); + b_calls++; + } + free_jobs(jobs, 4); + + return pass; +} + +static bool test_retry(const char *address, const struct keysd_config *cfg) +{ + struct kmip_job job; + bool pass = true; + + printf("test_retry:\n"); + + set_job(&job, cfg, "a", "fail-once"); + run(address, &job, 1, 2000000); + pass &= check(imports.nr == 1 && fake.nr_calls == 2, + "KMS error retried"); + free_jobs(&job, 1); + + set_job(&job, cfg, "a", "k1"); + imports.fail_left = 2; + run(address, &job, 1, 2000000); + pass &= check(imports.nr == 1 && fake.nr_calls == 3, + "import error retried"); + free_jobs(&job, 1); + + set_job(&job, cfg, "a", "k1"); + run("/nonexistent/kmip", &job, 1, 100000); + pass &= check(imports.nr == 0, "no helper: key stays pending"); + free_jobs(&job, 1); + + return pass; +} + +int main(void) +{ + char dir[] = "/tmp/test-kmip-client-XXXXXX", address[108], *abs; + struct keysd_config *cfg; + bool pass = true; + + cfg = load_cfg(); + if (!cfg || cfg->nr_kmip != 2 || !mkdtemp(dir) || + sd_varlink_server_new(&server, 0) < 0) { + fprintf(stderr, "test setup failed\n"); + exit(EXIT_FAILURE); + } + // A socket address must be absolute. + abs = realpath(dir, NULL); + if (!abs || snprintf(address, sizeof(address), "%s/kmip", abs) >= + (int)sizeof(address)) { + fprintf(stderr, "socket path too long\n"); + exit(EXIT_FAILURE); + } + free(abs); + + if (sd_varlink_server_bind_method(server, KMIP_VARLINK_GET_KEY, + fake_get_key) < 0 || + sd_varlink_server_listen_address(server, address, 0600) < 0) { + fprintf(stderr, "fake helper setup failed\n"); + exit(EXIT_FAILURE); + } + + pass &= test_parallel(address, cfg); + pass &= test_retry(address, cfg); + + sd_varlink_server_unref(server); + unlink(address); + rmdir(dir); + config_free(cfg); + + fflush(stdout); + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/keysd/tests/wringer.sh b/keysd/tests/wringer.sh index 8c0558e2a2..a09bd1b160 100755 --- a/keysd/tests/wringer.sh +++ b/keysd/tests/wringer.sh @@ -396,9 +396,17 @@ keysd_restart() { systemctl restart "${UNIT}" >"${SCRATCH}" 2>&1 } -# nvme-keysd ran since $1, and exited with status 0. +# nvme-keysd ran since $1, and exited with status 0. With Type=notify, +# systemctl returns at READY=1, before the exit: wait for it. keysd_ran() { - unit_journal_has "$1" "Finished ${UNIT}" + local i + + for i in $(seq 20); do + unit_journal_has "$1" "Deactivated successfully" && return 0 + sleep 0.5 + done + + return 1 } journal_has() { @@ -559,6 +567,7 @@ write_cred "${CRED_DIR}/keysd-wringer-keyring" "${KEY_A}" \ printf '%s\n' "$@" "" } entry inline "tls-key-source = inline" "tls-key = ${KEY_A}" + entry nosuch "tls-key-source = nosuch" "tls-key = ${CRED_NAME}" entry kmip "tls-key-source = kmip" "tls-key = ${CRED_NAME}" entry notlskey "tls-key-source = systemd-creds" entry badname "tls-key-source = systemd-creds" "tls-key = ../x" @@ -575,7 +584,9 @@ check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "the inline entry is ignored" \ test "$(journal_count "${PHASE_START}" "${NQN_BASE}:inline")" -eq 0 check "an unsupported tls-key-source was logged" \ - journal_has "${PHASE_START}" "tls-key-source 'kmip' is not supported" + journal_has "${PHASE_START}" "${NQN_BASE}:nosuch: tls-key-source 'nosuch' is not supported" +check "kmip without a KMS was logged" \ + journal_has "${PHASE_START}" "${NQN_BASE}:kmip: tls-key-source is kmip but no KMS is configured" check "a missing tls-key was logged" \ journal_has "${PHASE_START}" "${NQN_BASE}:notlskey: tls-key-source is systemd-creds but tls-key is not set" check "an invalid credential name was logged" \ diff --git a/meson.build b/meson.build index 33427ad448..fdc0ece671 100644 --- a/meson.build +++ b/meson.build @@ -450,6 +450,7 @@ keysd_opt = keysd_opt.require(openssl_dep.found() and keyutils_dep.found(), want_keysd = keysd_opt.allowed() want_kmip = get_option('kmip').require(want_keysd, error_message: 'kmip requires nvme-keysd').allowed() +conf.set('CONFIG_KMIP', want_kmip, description: 'Is the KMIP key source enabled') conf.set('CONFIG_DISCOVERD', want_discoverd, description: 'Is nvme-discoverd enabled') conf.set('CONFIG_KEYSD', want_keysd, description: 'Is nvme-keysd enabled') @@ -672,6 +673,11 @@ substs.set('SYSTEMCTL', get_option('systemctl')) # mDNS uses systemd-resolved, so nvme-discoverd must start after it. substs.set('DISCOVERD_AFTER_RESOLVED', want_mdns ? 'After=systemd-resolved.service' : '') +# nvme-keysd calls nvme-keysd-kmip through its socket. +substs.set('KEYSD_WANTS_KMIP', + want_kmip ? 'Wants=nvme-keysd-kmip.socket' : '') +substs.set('KEYSD_AFTER_KMIP', + want_kmip ? 'After=nvme-keysd-kmip.socket' : '') substs.set('PREFIX', prefixdir) substs.set('URL', 'https://github.com/linux-nvme/nvme-cli/') substs.set('LIBNVME_API_NAME', libnvme_api_name) From 774bd338a3225cc03de5f0773d61dbb6d2e36ade Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Tue, 13 Oct 2026 06:51:40 -0400 Subject: [PATCH 013/263] keysd: test KMIP with a PyKMIP server The wringer tests nvme-keysd and nvme-keysd-kmip with PyKMIP, a separate KMIP implementation. The KMIP phases run when the helper is built. The nightly installs PyKMIP and builds with -Dkmip=enabled. The new nvme-keysd option --kmip-test-addr lets the wringer use its own helper socket. Signed-off-by: Martin Belanger --- .github/workflows/run-nightly-tests.yml | 13 +- Documentation/nvme-keysd.txt | 7 +- keysd/src/main.c | 11 +- keysd/tests/pykmip-kms.py | 111 +++++ keysd/tests/wringer.sh | 618 ++++++++++++++++++++++-- 5 files changed, 706 insertions(+), 54 deletions(-) create mode 100644 keysd/tests/pykmip-kms.py diff --git a/.github/workflows/run-nightly-tests.yml b/.github/workflows/run-nightly-tests.yml index af11437be9..6b072d88b9 100644 --- a/.github/workflows/run-nightly-tests.yml +++ b/.github/workflows/run-nightly-tests.yml @@ -192,18 +192,22 @@ jobs: sudo dnf install -y gcc meson git kmod iproute systemd \ systemd-devel json-c-devel openssl-devel keyutils-libs-devel \ - dbus-devel gcovr avahi avahi-tools ktls-utils + dbus-devel gcovr avahi avahi-tools ktls-utils python3 + + # The KMS for the KMIP phases of the nvme-keysd wringer. + python3 -m venv "$HOME/pykmip" + "$HOME/pykmip/bin/pip" install pykmip git clone https://github.com/${{ github.repository }} nvme-cli cd nvme-cli git checkout ${{ github.sha }} # Enable explicitly so a missing or too old libsystemd fails - # the build instead of silently skipping a daemon or the mDNS - # support of nvme-discoverd. + # the build instead of silently skipping a daemon, the mDNS + # support of nvme-discoverd, or the KMIP support of nvme-keysd. meson setup .build-ci --buildtype=release \ -Dnvme-discoverd=enabled -Dmdns=enabled \ - -Dnvme-keysd=enabled -Db_coverage=true + -Dnvme-keysd=enabled -Dkmip=enabled -Db_coverage=true meson compile -C .build-ci # Test link for the mDNS phases. The mDNS traffic stays on @@ -226,6 +230,7 @@ jobs: tee daemon-results/discoverd-wringer.log discoverd_status=$? sudo env BUILD_DIR="$PWD/.build-ci" \ + PYKMIP_PYTHON="$HOME/pykmip/bin/python" \ keysd/tests/wringer.sh -y 2>&1 | tee daemon-results/keysd-wringer.log keysd_status=$? diff --git a/Documentation/nvme-keysd.txt b/Documentation/nvme-keysd.txt index f9a7004542..c003e94ed4 100644 --- a/Documentation/nvme-keysd.txt +++ b/Documentation/nvme-keysd.txt @@ -9,7 +9,8 @@ SYNOPSIS -------- [verse] 'nvme-keysd' [--config=] [--fabrics-config=] - [--creds-dir=] [--should-start] [--debug] [--help] + [--creds-dir=] [--kmip-test-addr=] + [--should-start] [--debug] [--help] DESCRIPTION ----------- @@ -152,6 +153,10 @@ OPTIONS Read the encrypted credentials from instead of @SYSCONFDIR@/nvme/creds. +--kmip-test-addr=:: + Connect to the nvme-keysd-kmip Varlink socket at instead of + @RUNDIR@/nvme/keysd/kmip. For tests. + --should-start:: Exit with status 0 if an entry of the fabrics configuration has a 'tls-key-source' other than 'inline', and with status 1 otherwise. diff --git a/keysd/src/main.c b/keysd/src/main.c index d9f3dc83ed..4810edae6f 100644 --- a/keysd/src/main.c +++ b/keysd/src/main.c @@ -35,6 +35,7 @@ struct keysd_ctx { const char *conf_path; // nvme-keysd's conf path const char *fabrics_conf; // NULL: libnvme's default const char *creds_dir; // encrypted credentials + const char *kmip_address; // Varlink socket of the helper struct keysd_config *cfg; // parsed @conf_path bool force_debug; // --debug forces DEBUG bool should_start; // --should-start: test, exit @@ -74,7 +75,7 @@ static int run_kmip(struct import_ctx *ic, const struct kmip_job *jobs, size_t nr_jobs) { const struct kmip_client_cfg cfg = { - .address = KMIP_VARLINK_ADDRESS, + .address = ctx.kmip_address, .retry_usec = KMIP_RETRY_USEC, .retry_max_usec = KMIP_RETRY_MAX_USEC, .import = import_kmip, @@ -116,6 +117,8 @@ static void usage(const char *prog) " (default: libnvme's default)\n" " --creds-dir DIR encrypted credentials\n" " (default: " KEYSD_CREDS_DIR ")\n" + " --kmip-test-addr PATH Varlink socket of nvme-keysd-kmip\n" + " (default: " KMIP_VARLINK_ADDRESS ")\n" " --should-start exit 0 if there is work to do,\n" " 1 otherwise\n" " --debug, -d enable debug logging (journal + libnvme)\n" @@ -129,6 +132,7 @@ int main(int argc, char **argv) { "config", required_argument, NULL, 'c' }, { "fabrics-config", required_argument, NULL, 'J' }, { "creds-dir", required_argument, NULL, 'C' }, + { "kmip-test-addr", required_argument, NULL, 'K' }, { "should-start", no_argument, NULL, 'S' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, @@ -168,6 +172,9 @@ int main(int argc, char **argv) case 'd': ctx.force_debug = true; break; + case 'K': + ctx.kmip_address = optarg; + break; case 'S': ctx.should_start = true; break; @@ -192,6 +199,8 @@ int main(int argc, char **argv) ctx.conf_path = config_path_abs ? config_path_abs : KEYSD_CONF_PATH; ctx.fabrics_conf = fabrics_path_abs; ctx.creds_dir = creds_path_abs ? creds_path_abs : KEYSD_CREDS_DIR; + if (!ctx.kmip_address) + ctx.kmip_address = KMIP_VARLINK_ADDRESS; // Key material must never end up in a core dump. if (prctl(PR_SET_DUMPABLE, 0) < 0) { diff --git a/keysd/tests/pykmip-kms.py b/keysd/tests/pykmip-kms.py new file mode 100644 index 0000000000..1bfa791630 --- /dev/null +++ b/keysd/tests/pykmip-kms.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. +# +# Authors: Martin Belanger +# +# Test KMS for the nvme-keysd wringer, built on PyKMIP. "serve" runs a +# PyKMIP server. The other commands manage the keys on it, as a KMIP +# client with the certificates in DIR: ca.pem, client.pem, client.key. + +import argparse +import logging +import sys + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from kmip.core import enums +from kmip.pie import objects +from kmip.pie.client import ProxyKmipClient + + +def serve(args): + from kmip.services.server import server + + sys.argv = [sys.argv[0], '-f', args.config, '-l', args.log] + server.main() + + +def client(args): + return ProxyKmipClient( + hostname='127.0.0.1', port=args.port, + cert=f'{args.dir}/client.pem', key=f'{args.dir}/client.key', + ca=f'{args.dir}/ca.pem', + kmip_version=enums.KMIPVersion.KMIP_1_4) + + +def register(args, obj): + with client(args) as c: + uid = c.register(obj) + if args.activate: + c.activate(uid) + print(uid) + + +def register_secret(args): + register(args, objects.SecretData( + args.value.encode(), enums.SecretDataType.PASSWORD, + name=args.name)) + + +def register_symmetric(args): + key = bytes.fromhex(args.hex) + register(args, objects.SymmetricKey( + enums.CryptographicAlgorithm.AES, len(key) * 8, key, + name=args.name)) + + +def register_certificate(args): + with open(args.pem, 'rb') as f: + cert = x509.load_pem_x509_certificate(f.read()) + register(args, objects.X509Certificate( + cert.public_bytes(serialization.Encoding.DER), name=args.name)) + + +def activate(args): + with client(args) as c: + c.activate(args.uid) + + +def revoke(args): + with client(args) as c: + c.revoke(enums.RevocationReasonCode.CESSATION_OF_OPERATION, + args.uid) + + +def main(): + p = argparse.ArgumentParser() + p.add_argument('--dir', help='directory of the client certificates') + p.add_argument('--port', type=int, default=5696) + sub = p.add_subparsers(required=True) + + s = sub.add_parser('serve', help='run a PyKMIP server') + s.add_argument('config', help='PyKMIP server configuration file') + s.add_argument('log', help='PyKMIP server log file') + s.set_defaults(func=serve) + + for name, func, value in ( + ('register-secret', register_secret, 'value'), + ('register-symmetric', register_symmetric, 'hex'), + ('register-certificate', register_certificate, 'pem')): + s = sub.add_parser(name, help='register a key, print its ID') + s.add_argument('--activate', action='store_true') + s.add_argument('name', help='KMIP Name of the key') + s.add_argument(value) + s.set_defaults(func=func) + + for name, func in (('activate', activate), ('revoke', revoke)): + s = sub.add_parser(name) + s.add_argument('uid', help='Unique Identifier of the key') + s.set_defaults(func=func) + + args = p.parse_args() + # PyKMIP warns about its missing default configuration files. + logging.getLogger('kmip').setLevel(logging.ERROR) + args.func(args) + + +if __name__ == '__main__': + main() diff --git a/keysd/tests/wringer.sh b/keysd/tests/wringer.sh index a09bd1b160..2459488cb9 100755 --- a/keysd/tests/wringer.sh +++ b/keysd/tests/wringer.sh @@ -15,13 +15,17 @@ # and real kernel modules (nvmet, nvmet-tcp, nvme-tcp, tls). Invoke # directly, after building with -Dnvme-keysd=enabled: # -# sudo [TLSHD=/path/to/tlshd] "$0" [-y] +# sudo [TLSHD=/path/to/tlshd] [PYKMIP_PYTHON=/path/to/python] "$0" [-y] # # It asks for confirmation first. -y skips the question. # # tlshd.service is used if it is installed. Otherwise the test runs a # tlshd binary as a transient unit: TLSHD, or tlshd in PATH. # +# When nvme-keysd-kmip is built (-Dkmip), the KMIP phases run against a +# PyKMIP server on loopback. PYKMIP_PYTHON is a Python interpreter that +# can import PyKMIP (default: python3). +# # On loopback, the host and the target share the .nvme keyring, so one key # serves both ends of the connection. @@ -95,6 +99,46 @@ PORT_ID=30 # Test-only PSKs, never used anywhere else. KEY_A='NVMeTLSkey-1:01:FJeRbUOvWkhSbfjCKeQYjPqtZpGO+OthuIsXjWIItWlnUfzl:' KEY_B='NVMeTLSkey-1:01:yyMDXyYiAi6eDt01wprARTn+XEhk9DQgFweyGFLfvJfSuD3o:' +NQN_BASE=nqn.2026-09.org.nvmexpress.keysd-wringer + +# KMIP +KMIP_BIN="${BUILD_DIR}/keysd/nvme-keysd-kmip" +KMIP_SOCKET_FILE="${BUILD_DIR}/keysd/nvme-keysd-kmip.socket" +KMIP_SERVICE_FILE="${BUILD_DIR}/keysd/nvme-keysd-kmip@.service" +KMIP_UNIT=keysd-wringer-kmip +KMIP_SOCKET="${WORK_DIR}/kmip.sock" +KMIP_DIR="${WORK_DIR}/kmip" +KMIP_GCOV_DIR="${WORK_DIR}/gcov" +KEYSD_CONF="${WORK_DIR}/nvme-keysd.conf" +PYKMIP_PYTHON="${PYKMIP_PYTHON:-python3}" +PYKMIP_KMS="${REPO_ROOT}/keysd/tests/pykmip-kms.py" +KMS_UNIT=keysd-wringer-pykmip.service +KMS_PORT=5697 +VARLINK_IF=io.nvme.keysd.Kmip + +# Test-only raw keys for the Symmetric Key phases. +HEX_32=$(printf '%02x' $(seq 0 31)) +HEX_48=$(printf '%02x' $(seq 32 79)) + +KMIP=false +if [ -x "${KMIP_BIN}" ]; then + KMIP=true + for tool in openssl setpriv ss varlinkctl; do + if ! command -v "${tool}" >/dev/null 2>&1; then + echo "Missing required tool for KMIP: ${tool}" >&2 + exit 1 + fi + done + if ! "${PYKMIP_PYTHON}" -c 'import kmip' 2>/dev/null; then + cat >&2 </dev/null | tail -n 1 +} + +# The PSK interchange string of the raw key $1 (hex), for hash $2 +# (1: SHA-256, 2: SHA-384). +psk_of() { + "${NVME_BIN}" keys gen-tls-psk --secret="$1" --hmac="$2" \ 2>/dev/null | tail -n 1 } @@ -206,15 +271,32 @@ write_cred() { printf '%s' "$2" | systemd-creds encrypt --name="$3" - "$1" } +revoke_key() { + "${NVME_BIN}" keys revoke --identity="$1" >/dev/null 2>&1 +} + revoke_test_keys() { local id - for id in "${ID_A:-}" "${ID_B:-}"; do + for id in "${ID_A:-}" "${ID_B:-}" "${ID_B2:-}" "${ID_S32:-}" \ + "${ID_S48:-}"; do [ -n "${id}" ] || continue - "${NVME_BIN}" keys revoke --identity="${id}" >/dev/null 2>&1 + revoke_key "${id}" done } +# Run "$@" until it succeeds, for at most $1 seconds. +wait_until() { + local i + + for i in $(seq $(($1 * 2))); do + "${@:2}" && return 0 + sleep 0.5 + done + + "${@:2}" +} + # --------------------------------------------------------------------------- # tlshd and the nvmet-tcp target # --------------------------------------------------------------------------- @@ -342,6 +424,7 @@ keysd_install() { cp "${LIBNVME_SO}" "${WORK_DIR}/libnvme/src/" write_fabrics_conf systemd-creds + : > "${KEYSD_CONF}" # The unit as built, with the binary and its arguments replaced. The # credentials are in ${CRED_DIR}, so the unit must not create @@ -349,7 +432,8 @@ keysd_install() { local exec_start="${WORK_DIR}/keysd/nvme-keysd" exec_start+=" --fabrics-config ${FABRICS_CONF}" - exec_start+=" --creds-dir ${CRED_DIR} --debug" + exec_start+=" --creds-dir ${CRED_DIR} --config ${KEYSD_CONF}" + exec_start+=" --kmip-test-addr ${KMIP_SOCKET} --debug" local coverage=() # A coverage build writes its .gcda files into ${BUILD_DIR}, which @@ -363,12 +447,14 @@ keysd_install() { sed -e "s|^ExecStart=.*|ExecStart=${exec_start}|" \ -e "s|^ExecCondition=.*|ExecCondition=${exec_start} --should-start|" \ -e "/^ConfigurationDirectory/d" \ + -e "s|nvme-keysd-kmip.socket|${KMIP_UNIT}.socket|" \ "${coverage[@]}" \ "${KEYSD_UNIT_FILE}" > "${UNIT_FILE}" systemctl daemon-reload } -# $1: the key source of the subsystem entry, or "inline". +# $1: the key source of the subsystem entry, or "inline". $2: its tls-key +# (default: ${CRED_NAME}). write_fabrics_conf() { cat > "${FABRICS_CONF}" </dev/null systemctl reset-failed "${UNIT}" 2>/dev/null @@ -399,30 +494,227 @@ keysd_restart() { # nvme-keysd ran since $1, and exited with status 0. With Type=notify, # systemctl returns at READY=1, before the exit: wait for it. keysd_ran() { - local i - - for i in $(seq 20); do - unit_journal_has "$1" "Deactivated successfully" && return 0 - sleep 0.5 - done + wait_until 10 journal_match -u "${UNIT}" "$1" "Deactivated successfully" +} - return 1 +# The journal has, for the selector $1 $2, a message since $3 that +# contains $4. +journal_match() { + journalctl "$1" "$2" --since "$3" 2>/dev/null | grep -q -- "$4" } +# journald stores a message some time after it is sent: the *_has checks +# wait for it. journal_has() { - journalctl -t nvme-keysd --since "$1" 2>/dev/null | grep -q -- "$2" + wait_until 5 journal_match -t nvme-keysd "$1" "$2" } # systemd unloads an inactive unit, and "systemctl show" then returns # default values. The unit's journal keeps what happened. unit_journal_has() { - journalctl -u "${UNIT}" --since "$1" 2>/dev/null | grep -q -- "$2" + wait_until 5 journal_match -u "${UNIT}" "$1" "$2" } journal_count() { journalctl -t nvme-keysd --since "$1" 2>/dev/null | grep -c -- "$2" } +keysd_active() { + test "$(systemctl is-active "${UNIT}")" = active +} + +# The STATUS= that nvme-keysd last sent to systemd is $1. +keysd_status_is() { + test "$(systemctl show -p StatusText --value "${UNIT}")" = "$1" +} + +keysd_main_pid() { + systemctl show -p MainPID --value "${UNIT}" +} + +# nvme-keysd runs, with a main PID other than $1. +keysd_restarted_after() { + local pid + + pid=$(keysd_main_pid) + [ "${pid}" != 0 ] && [ "${pid}" != "$1" ] +} + +# --------------------------------------------------------------------------- +# KMIP: nvme-keysd-kmip and the PyKMIP server +# --------------------------------------------------------------------------- + +kmip_install() { + local unit_dir=/run/systemd/system + + log "Install ${KMIP_UNIT}.socket" + cp "${KMIP_BIN}" "${WORK_DIR}/keysd/" + mkdir -m 0700 -p "${CRED_DIR}/kmip" + + local exec_start="${WORK_DIR}/keysd/nvme-keysd-kmip" + + exec_start+=" --config ${KEYSD_CONF} --debug" + local coverage=() + + # The helper's dynamic user cannot write into ${BUILD_DIR}. Its + # coverage counters go to ${KMIP_GCOV_DIR}, and cleanup copies them + # back. All instances share one user, so they can update the same + # counter files. + if compgen -G "${BUILD_DIR}/keysd/nvme-keysd-kmip.p/*.gcno" >/dev/null; then + mkdir -m 1777 -p "${KMIP_GCOV_DIR}" + coverage=(-e "/^ExecStart=/a Environment=GCOV_PREFIX=${KMIP_GCOV_DIR}" + -e "/^ExecStart=/a ReadWritePaths=${KMIP_GCOV_DIR}" + -e "/^ExecStart=/a User=${KMIP_UNIT}" + -e "s|^UMask=.*|UMask=0022|") + fi + sed -e "s|^ExecStart=.*|ExecStart=${exec_start}|" \ + -e "s|^LoadCredentialEncrypted=kmip:.*|LoadCredentialEncrypted=kmip:${CRED_DIR}/kmip|" \ + "${coverage[@]}" \ + "${KMIP_SERVICE_FILE}" > "${unit_dir}/${KMIP_UNIT}@.service" + sed -e "s|^ListenStream=.*|ListenStream=${KMIP_SOCKET}|" \ + "${KMIP_SOCKET_FILE}" > "${unit_dir}/${KMIP_UNIT}.socket" + systemctl daemon-reload + systemctl start "${KMIP_UNIT}.socket" +} + +kmip_uninstall() { + local src="${KMIP_GCOV_DIR}${BUILD_DIR}/keysd/nvme-keysd-kmip.p" + + systemctl stop "${KMIP_UNIT}.socket" "${KMIP_UNIT}@*.service" \ + 2>/dev/null + rm -f "/run/systemd/system/${KMIP_UNIT}.socket" \ + "/run/systemd/system/${KMIP_UNIT}@.service" + systemctl daemon-reload + if compgen -G "${src}/*.gcda" >/dev/null; then + cp "${src}"/*.gcda "${BUILD_DIR}/keysd/nvme-keysd-kmip.p/" + fi +} + +# Certificate $1 (.pem and .key), signed by CA $2, with the X.509 v3 +# extensions $3. +kmip_cert() { + openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \ + -keyout "$1.key" -out "$1.csr" -subj "/CN=keysd-wringer-$1" \ + 2>/dev/null && + openssl x509 -req -in "$1.csr" -CA "$2.pem" -CAkey "$2.key" \ + -CAcreateserial -out "$1.pem" -days 1 \ + -extfile <(printf '%s\n' "$3") 2>/dev/null +} + +kmip_ca() { + openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \ + -keyout "$1.key" -out "$1.pem" -days 1 \ + -subj "/CN=keysd-wringer-$1" 2>/dev/null +} + +# A CA, a server and a client certificate for the KMS, and a second CA +# that signed nothing. The certificates must be readable by the helper. +kms_setup() { + log "PyKMIP: KMS on ${TRADDR}:${KMS_PORT}" + mkdir -p "${KMIP_DIR}/policies" + ( + cd "${KMIP_DIR}" || exit 1 + kmip_ca ca && kmip_ca other-ca && + kmip_cert server ca "subjectAltName=IP:${TRADDR}" && + kmip_cert client ca "extendedKeyUsage=clientAuth" + ) || { echo "cannot create the KMIP certificates" >&2; exit 1; } + chmod 0644 "${KMIP_DIR}"/*.pem + systemd-creds encrypt --name=kmip_client "${KMIP_DIR}/client.key" \ + "${CRED_DIR}/kmip/client" + + cat > "${KMIP_DIR}/server.conf" <&2; exit 1; } +} + +kms_listening() { + ss -Hltn "sport = :${KMS_PORT}" | grep -q . +} + +kms_start() { + systemd-run --unit="${KMS_UNIT}" --collect -p WorkingDirectory=/ \ + "${PYKMIP_PYTHON}" -I "${PYKMIP_KMS}" serve \ + "${KMIP_DIR}/server.conf" "${KMIP_DIR}/server.log" >/dev/null + wait_until 20 kms_listening +} + +kms_stop() { + systemctl stop "${KMS_UNIT}" 2>/dev/null +} + +# Manage the keys on the KMS. PyKMIP's own messages go to client.log. +kms() { + "${PYKMIP_PYTHON}" -I "${PYKMIP_KMS}" --dir "${KMIP_DIR}" \ + --port "${KMS_PORT}" "$@" 2>>"${KMIP_DIR}/client.log" +} + +# $1: the CA file. The other arguments: the servers. +write_keysd_conf() { + local ca=$1 server + + shift + { + echo "[kmip:wringer]" + for server in "$@"; do + echo "server = ${server}" + done + cat < "${KEYSD_CONF}" +} + +helper_journal_has() { + wait_until 5 journal_match -t nvme-keysd-kmip "$1" "$2" +} + +helper_journal_count() { + journalctl -t nvme-keysd-kmip --since "$1" 2>/dev/null | + grep -c -- "$2" +} + +# The helper logged since $1, and never as root. +helper_not_root() { + local uids + + uids=$(journalctl -t nvme-keysd-kmip --since "$1" -o export \ + 2>/dev/null | sed -n 's/^_UID=//p' | sort -u) + [ -n "${uids}" ] && ! grep -qx 0 <<< "${uids}" +} + +# nvme-keysd logged that key $1 failed with Varlink error $2, and the retry +# delay $3. +kmip_failed() { + journal_match -t nvme-keysd "${PHASE_START}" \ + "KMS 'wringer', key '$1': ${VARLINK_IF}.$2, retry in $3 s" +} + +# Call the helper directly, as user $1, with the parameters $2. +helper_call() { + setpriv --reuid="$1" --regid="$1" --clear-groups \ + varlinkctl call "unix:${KMIP_SOCKET}" "${VARLINK_IF}.GetKey" "$2" \ + >"${SCRATCH}" 2>&1 +} + +# The socket is root's only: "nobody" cannot connect. +helper_refuses_nobody() { + ! helper_call 65534 '{"kms":"wringer","name":"x"}' && + grep -q "Permission denied" "${SCRATCH}" +} + # --------------------------------------------------------------------------- # Cleanup: always runs, even on Ctrl-C or an assertion failing partway. # --------------------------------------------------------------------------- @@ -431,6 +723,8 @@ cleanup() { log "Cleanup" disconnect keysd_uninstall + kmip_uninstall + kms_stop revoke_test_keys nvmet_teardown tlshd_stop @@ -447,7 +741,13 @@ trap cleanup EXIT mkdir -p "${WORK_DIR}" ID_A=$(identity_of "${KEY_A}") ID_B=$(identity_of "${KEY_B}") -if [ -z "${ID_A}" ] || [ -z "${ID_B}" ]; then +ID_B2=$(identity_of "${KEY_B}" "${NQN_BASE}:second") +PSK_S32=$(psk_of "${HEX_32}" 1) +PSK_S48=$(psk_of "${HEX_48}" 2) +ID_S32=$(identity_of "${PSK_S32}") +ID_S48=$(identity_of "${PSK_S48}") +if [ -z "${ID_A}" ] || [ -z "${ID_B}" ] || [ -z "${ID_B2}" ] || + [ -z "${ID_S32}" ] || [ -z "${ID_S48}" ]; then echo "cannot derive the test identities with ${NVME_BIN}" >&2 exit 1 fi @@ -459,10 +759,14 @@ revoke_test_keys nvmet_setup tlshd_start keysd_install +if [ "${KMIP}" = true ]; then + kmip_install + kms_setup +fi phase "the unit does not start without a key source" write_fabrics_conf inline -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "the unit is not active" test "$(systemctl is-active "${UNIT}")" = inactive check "the unit is not failed" \ @@ -473,7 +777,7 @@ check "the check was logged" journal_has "${PHASE_START}" "nothing to do" write_fabrics_conf systemd-creds phase "a missing credential is reported" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd runs without its credential" keysd_ran "${PHASE_START}" check "the missing credential was logged" \ @@ -481,7 +785,7 @@ check "the missing credential was logged" \ phase "a credential is imported at startup" write_cred "${CRED_FILE}" "${KEY_A}" "${CRED_NAME}" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "key A is in .nvme with the expected identity" key_present "${ID_A}" @@ -495,7 +799,7 @@ check "the connection uses key A" ctrl_uses_key "${ID_A}" phase "a restart with nothing changed changes nothing" SERIAL_A=$(key_serial "${ID_A}") -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "key A keeps its serial" test "$(key_serial "${ID_A}")" = "${SERIAL_A}" @@ -504,7 +808,7 @@ check "'already present' was logged" \ phase "a new credential and a restart replace the key" write_cred "${CRED_FILE}" "${KEY_B}" "${CRED_NAME}" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "key B is in .nvme" key_present "${ID_B}" @@ -528,7 +832,7 @@ disconnect phase "a credential with the wrong name is rejected" write_cred "${CRED_FILE}" "${KEY_A}" wrong-name -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "the name mismatch was logged" \ @@ -540,7 +844,7 @@ phase "every path of a subsystem imports the key once" write_cred "${CRED_FILE}" "${KEY_B}" "${CRED_NAME}" sed -i "/^controller/a controller = transport=tcp;traddr=${TRADDR};trsvcid=$((TRSVCID + 1))" \ "${FABRICS_CONF}" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "'already present' was logged once" \ @@ -551,7 +855,6 @@ check "'already present' was logged once" \ phase "entries that cannot be imported are skipped" DROPIN_DIR="${FABRICS_CONF}.d" DROPIN="${DROPIN_DIR}/errors.conf" -NQN_BASE=nqn.2026-09.org.nvmexpress.keysd-wringer mkdir -p "${DROPIN_DIR}" write_cred "${CRED_DIR}/keysd-wringer-notpsk" "not-a-psk" keysd-wringer-notpsk write_cred "${CRED_DIR}/keysd-wringer-big" "$(printf '%0200d' 0)" \ @@ -559,26 +862,19 @@ write_cred "${CRED_DIR}/keysd-wringer-big" "$(printf '%0200d' 0)" \ write_cred "${CRED_DIR}/keysd-wringer-keyring" "${KEY_A}" \ keysd-wringer-keyring { - entry() { - printf '[Subsystem]\nnqn = %s:%s\n' "${NQN_BASE}" "$1" - printf 'controller = transport=tcp;traddr=%s;trsvcid=%s\n' \ - "${TRADDR}" "${TRSVCID}" - shift - printf '%s\n' "$@" "" - } - entry inline "tls-key-source = inline" "tls-key = ${KEY_A}" - entry nosuch "tls-key-source = nosuch" "tls-key = ${CRED_NAME}" - entry kmip "tls-key-source = kmip" "tls-key = ${CRED_NAME}" - entry notlskey "tls-key-source = systemd-creds" - entry badname "tls-key-source = systemd-creds" "tls-key = ../x" - entry notpsk "tls-key-source = systemd-creds" \ + subsys_entry inline "tls-key-source = inline" "tls-key = ${KEY_A}" + subsys_entry nosuch "tls-key-source = nosuch" "tls-key = ${CRED_NAME}" + subsys_entry kmip "tls-key-source = kmip" "tls-key = ${CRED_NAME}" + subsys_entry notlskey "tls-key-source = systemd-creds" + subsys_entry badname "tls-key-source = systemd-creds" "tls-key = ../x" + subsys_entry notpsk "tls-key-source = systemd-creds" \ "tls-key = keysd-wringer-notpsk" - entry big "tls-key-source = systemd-creds" "tls-key = keysd-wringer-big" - entry keyring "tls-key-source = systemd-creds" \ + subsys_entry big "tls-key-source = systemd-creds" "tls-key = keysd-wringer-big" + subsys_entry keyring "tls-key-source = systemd-creds" \ "tls-key = keysd-wringer-keyring" \ "keyring = keysd-wringer-nosuch" } > "${DROPIN}" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "nvme-keysd ran" keysd_ran "${PHASE_START}" check "the inline entry is ignored" \ @@ -603,7 +899,7 @@ check "the main file is still imported" \ phase "a fabrics configuration that does not parse is reported" printf '[Subsystem]\nnqn = not-an-nqn\n' > "${DROPIN}" -PHASE_START=$(date '+%Y-%m-%d %H:%M:%S') +PHASE_START=$(now) keysd_restart check "the unit is skipped" \ unit_journal_has "${PHASE_START}" "Skipped due to 'exec-condition'" @@ -611,9 +907,235 @@ check "the read failure was logged" \ journal_has "${PHASE_START}" "cannot read the fabrics configuration" check "key B is still in .nvme" key_present "${ID_B}" -if [ -n "${GITHUB_ACTIONS:-}" ] && [ "${PHASE}" -gt 0 ]; then - echo "::endgroup::" +finish() { + if [ -n "${GITHUB_ACTIONS:-}" ] && [ "${PHASE}" -gt 0 ]; then + echo "::endgroup::" + fi + printf "\n" + log "Results: ${PASS} passed, ${FAIL} failed" + [ "${FAIL}" -eq 0 ] + exit +} + +rm -rf "${DROPIN_DIR}" +if [ "${KMIP}" != true ]; then + log "nvme-keysd-kmip is not built: no KMIP phases" + finish fi -printf "\n" -log "Results: ${PASS} passed, ${FAIL} failed" -[ "${FAIL}" -eq 0 ] + +# --------------------------------------------------------------------------- +# KMIP phases +# --------------------------------------------------------------------------- + +NAME_A=keysd-wringer-a +NAME_B2=keysd-wringer-b2 +NAME_S32=keysd-wringer-s32 +NAME_S48=keysd-wringer-s48 +NAME_DUP=keysd-wringer-dup + +phase "a KMIP key that is not Active is not used" +revoke_test_keys +UID_A=$(kms register-secret "${NAME_A}" "${KEY_A}") +check "key A is on the KMS" test -n "${UID_A}" +write_keysd_conf "${KMIP_DIR}/ca.pem" "${TRADDR}" +write_fabrics_conf kmip:wringer "${NAME_A}" +PHASE_START=$(now) +keysd_restart +check "nvme-keysd keeps running" keysd_active +check "NoSuchKey was logged" wait_until 5 kmip_failed "${NAME_A}" NoSuchKey 5 +check "the status counts the missing key" \ + keysd_status_is "1 KMIP key(s) not imported yet" +check "key A is not in .nvme" key_absent "${ID_A}" +check "the retry delay doubles" \ + wait_until 10 kmip_failed "${NAME_A}" NoSuchKey 10 + +phase "an activated key is imported at the next try" +kms activate "${UID_A}" +check "key A is in .nvme" wait_until 15 key_present "${ID_A}" +check "the import was logged" journal_has "${PHASE_START}" \ + "imported '${ID_A}' from KMS 'wringer', key '${NAME_A}'" +check "the helper logged the server" helper_journal_has "${PHASE_START}" \ + "KMS 'wringer': key '${NAME_A}' sent, from ${TRADDR}" +check "the status says all keys are imported" \ + keysd_status_is "All KMIP keys imported" +check "nvme-keysd keeps running" keysd_active + +phase "a TLS connection uses the KMIP key" +check "nvme connect -J connects" connect_from_config +check "the controller is live" ctrl_live +check "the connection uses key A" ctrl_uses_key "${ID_A}" +disconnect + +phase "the helper runs without privileges" +check "the helper never ran as root" helper_not_root "${PHASE_START}" +check "root can call the helper" \ + helper_call 0 "{\"kms\":\"wringer\",\"name\":\"${NAME_A}\"}" +check "another user cannot call the helper" helper_refuses_nobody + +phase "the helper refuses requests it cannot serve" +helper_call 0 '{"kms":"nosuch","name":"x"}' +check "an unknown KMS gets NoSuchKms" grep -q "${VARLINK_IF}.NoSuchKms" "${SCRATCH}" +helper_call 0 '{"kms":"wringer","name":""}' +# varlinkctl prints InvalidParameter as "Invalid argument" and the parameter. +check "an empty name is an invalid parameter" \ + grep -q '"parameter":"name"' "${SCRATCH}" +helper_call 0 '{"kms":"wringer","name":"keysd-wringer-nosuch"}' +check "an unknown key gets NoSuchKey" grep -q "${VARLINK_IF}.NoSuchKey" "${SCRATCH}" + +phase "a Symmetric Key of 32 bytes is a SHA-256 PSK" +kms register-symmetric --activate "${NAME_S32}" "${HEX_32}" >/dev/null +write_fabrics_conf kmip:wringer "${NAME_S32}" +PHASE_START=$(now) +keysd_restart +check "the key is in .nvme" wait_until 10 key_present "${ID_S32}" +check "the PSK uses SHA-256" test "${PSK_S32#NVMeTLSkey-1:01:}" != "${PSK_S32}" +check "key A is revoked" key_absent "${ID_A}" + +phase "a Symmetric Key of 48 bytes is a SHA-384 PSK" +kms register-symmetric --activate "${NAME_S48}" "${HEX_48}" >/dev/null +write_fabrics_conf kmip:wringer "${NAME_S48}" +PHASE_START=$(now) +keysd_restart +check "the key is in .nvme" wait_until 10 key_present "${ID_S48}" +check "the PSK uses SHA-384" test "${PSK_S48#NVMeTLSkey-1:02:}" != "${PSK_S48}" +check "a new connection succeeds" connect_from_config +check "the new connection uses the key" ctrl_uses_key "${ID_S48}" +disconnect + +phase "objects that are not PSKs are refused" +kms register-secret --activate keysd-wringer-notpsk not-a-psk >/dev/null +kms register-symmetric --activate keysd-wringer-short \ + "${HEX_32:0:32}" >/dev/null +kms register-certificate --activate keysd-wringer-cert \ + "${KMIP_DIR}/client.pem" >/dev/null +{ + subsys_entry notpsk "tls-key = keysd-wringer-notpsk" + subsys_entry short "tls-key = keysd-wringer-short" + subsys_entry cert "tls-key = keysd-wringer-cert" +} >> "${FABRICS_CONF}" +PHASE_START=$(now) +keysd_restart +check "Secret Data that is not a PSK gets InvalidKey" \ + wait_until 10 kmip_failed keysd-wringer-notpsk InvalidKey 5 +check "a Symmetric Key of 16 bytes gets InvalidKey" \ + wait_until 10 kmip_failed keysd-wringer-short InvalidKey 5 +check "a certificate gets InvalidKey" \ + wait_until 10 kmip_failed keysd-wringer-cert InvalidKey 5 +check "the status counts the three keys" \ + keysd_status_is "3 KMIP key(s) not imported yet" +check "the valid key is still in .nvme" key_present "${ID_S48}" + +phase "two Active keys with one name are refused" +UID_DUP=$(kms register-secret --activate "${NAME_DUP}" "${KEY_A}") +kms register-secret --activate "${NAME_DUP}" "${KEY_B}" >/dev/null +write_fabrics_conf kmip:wringer "${NAME_DUP}" +PHASE_START=$(now) +keysd_restart +check "KeyNotUnique was logged" \ + wait_until 5 kmip_failed "${NAME_DUP}" KeyNotUnique 5 +check "key A is not in .nvme" key_absent "${ID_A}" +check "key B is not in .nvme" key_absent "${ID_B}" +kms revoke "${UID_DUP}" +check "the remaining key is imported" wait_until 15 key_present "${ID_B}" + +phase "a KMS that is down at startup is asked again" +kms_stop +revoke_key "${ID_B}" +PHASE_START=$(now) +keysd_restart +check "KmsFailed was logged" \ + wait_until 5 kmip_failed "${NAME_DUP}" KmsFailed 5 +check "the helper logged the refused connection" \ + helper_journal_has "${PHASE_START}" \ + "KMIP ${TRADDR}: connect: Connection refused" +check "PyKMIP starts" kms_start +check "the key is imported when the KMS is back" \ + wait_until 20 key_present "${ID_B}" + +phase "the servers of a KMS are tried in order" +# Nothing listens on 127.0.0.2. The second key must start with the server +# that answered for the first one. +kms register-secret --activate "${NAME_B2}" "${KEY_B}" >/dev/null +write_keysd_conf "${KMIP_DIR}/ca.pem" 127.0.0.2 "${TRADDR}" +write_fabrics_conf kmip:wringer "${NAME_A}" +subsys_entry second "tls-key = ${NAME_B2}" >> "${FABRICS_CONF}" +PHASE_START=$(now) +keysd_restart +check "key A is in .nvme" wait_until 10 key_present "${ID_A}" +check "the second key is in .nvme" wait_until 10 key_present "${ID_B2}" +check "both keys came from ${TRADDR}" test "$(helper_journal_count \ + "${PHASE_START}" "sent, from ${TRADDR}")" -eq 2 +check "127.0.0.2 was tried once" test "$(helper_journal_count \ + "${PHASE_START}" "KMIP 127.0.0.2: connect")" -eq 1 +revoke_key "${ID_B2}" +write_fabrics_conf kmip:wringer "${NAME_A}" + +phase "a server certificate from another CA is refused" +write_keysd_conf "${KMIP_DIR}/other-ca.pem" "${TRADDR}" +revoke_key "${ID_A}" +PHASE_START=$(now) +keysd_restart +check "KmsFailed was logged" \ + wait_until 10 kmip_failed "${NAME_A}" KmsFailed 5 +check "the helper logged the certificate" \ + helper_journal_has "${PHASE_START}" "KMIP ${TRADDR}: server certificate:" +check "key A is not in .nvme" key_absent "${ID_A}" + +phase "a KMS that requires a client certificate refuses a client without one" +write_keysd_conf "${KMIP_DIR}/ca.pem" "${TRADDR}" +sed -i '/^client-/d' "${KEYSD_CONF}" +PHASE_START=$(now) +keysd_restart +check "KmsFailed was logged" \ + wait_until 10 kmip_failed "${NAME_A}" KmsFailed 5 +check "the helper logged the failed handshake" \ + helper_journal_has "${PHASE_START}" "KMIP ${TRADDR}: TLS handshake:" +check "key A is not in .nvme" key_absent "${ID_A}" + +phase "a missing client key is reported" +write_keysd_conf "${KMIP_DIR}/ca.pem" "${TRADDR}" +sed -i 's/^client-key .*/client-key = nosuch/' "${KEYSD_CONF}" +PHASE_START=$(now) +keysd_restart +check "KmsFailed was logged" \ + wait_until 10 kmip_failed "${NAME_A}" KmsFailed 5 +check "the helper logged the client key" \ + helper_journal_has "${PHASE_START}" \ + "KMIP ${TRADDR}: client key: No such file or directory" + +phase "tls-key-source names a KMS when there are several" +write_keysd_conf "${KMIP_DIR}/ca.pem" "${TRADDR}" +printf '\n[kmip:other]\nserver = %s\n' "${TRADDR}" >> "${KEYSD_CONF}" +write_fabrics_conf kmip "${NAME_A}" +subsys_entry nosuchkms "tls-key-source = kmip:nosuch" "tls-key = ${NAME_A}" \ + >> "${FABRICS_CONF}" +PHASE_START=$(now) +keysd_restart +check "nvme-keysd ran" keysd_ran "${PHASE_START}" +check "a plain kmip source was logged" journal_has "${PHASE_START}" \ + "${SUBSYS_NQN}: several KMSes: use tls-key-source = kmip:" +check "an unknown KMS was logged" journal_has "${PHASE_START}" \ + "${NQN_BASE}:nosuchkms: KMS 'nosuch' is not configured" + +phase "nvme-keysd is restarted after a crash" +write_keysd_conf "${KMIP_DIR}/ca.pem" "${TRADDR}" +write_fabrics_conf kmip:wringer "${NAME_A}" +PHASE_START=$(now) +keysd_restart +check "key A is in .nvme" wait_until 10 key_present "${ID_A}" +OLD_PID=$(keysd_main_pid) +systemctl kill -s KILL --kill-whom=main "${UNIT}" +check "systemd started nvme-keysd again" \ + wait_until 10 keysd_restarted_after "${OLD_PID}" +check "nvme-keysd keeps running" keysd_active +check "key A is still in .nvme" key_present "${ID_A}" + +phase "nvme-keysd stops on SIGTERM" +PHASE_START=$(now) +systemctl stop "${UNIT}" +check "the unit is not active" test "$(systemctl is-active "${UNIT}")" = inactive +check "the unit is not failed" \ + test "$(systemctl is-failed "${UNIT}")" != failed +check "key A is still in .nvme" key_present "${ID_A}" + +finish From 4b272414dee04df090d0e91023fd4c936077ae71 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 15:14:23 -0400 Subject: [PATCH 014/263] shared: fix strict aliasing in the address helpers parse_numeric_addr() writes a struct sockaddr_in or sockaddr_in6 into a struct sockaddr_storage, and sockaddrs_eq() reads it as a struct sockaddr. This breaks the strict aliasing rule: the compiler may assume that the write does not change what the read returns. The bug was latent. An unrelated change in this file made gcc 14 inline differently in a musl release build. The generated code then compared two equal IPv4 addresses as different. Access the addresses through a union of the sockaddr types, and copy the addresses of getifaddrs() into it. Fixes: 74b5d637a8a8 ("libnvme/util: reimplement numeric address helpers without getaddrinfo") Signed-off-by: Martin Belanger --- shared/net-util-linux.c | 160 +++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 83 deletions(-) diff --git a/shared/net-util-linux.c b/shared/net-util-linux.c index 90adddf15c..1edae28c55 100644 --- a/shared/net-util-linux.c +++ b/shared/net-util-linux.c @@ -27,26 +27,34 @@ #include "net-util.h" +/* + * The members share their storage. Read a member that was written as + * another only through the union: other accesses break strict aliasing. + */ +union sockaddr_any { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; +}; + /* * Parse @addr (IPv4, or IPv6 with an optional "%scope" suffix on a - * link-local address) into @ss. @addr is never a hostname -- resolving one + * link-local address) into @sa. @addr is never a hostname -- resolving one * is the caller's job, done before this is reached. * * Return: 0 on success; -EINVAL if @addr is not numeric; -ENOMEM on * allocation failure. */ -static int parse_numeric_addr(const char *addr, struct sockaddr_storage *ss) +static int parse_numeric_addr(const char *addr, union sockaddr_any *sa) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)ss; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)ss; char *tmp; char *scope; int ret = 0; - memset(ss, 0, sizeof(*ss)); + memset(sa, 0, sizeof(*sa)); - if (inet_pton(AF_INET, addr, &addr4->sin_addr) == 1) { - addr4->sin_family = AF_INET; + if (inet_pton(AF_INET, addr, &sa->in4.sin_addr) == 1) { + sa->in4.sin_family = AF_INET; return 0; } @@ -58,69 +66,66 @@ static int parse_numeric_addr(const char *addr, struct sockaddr_storage *ss) if (scope) *scope++ = '\0'; - if (inet_pton(AF_INET6, tmp, &addr6->sin6_addr) != 1) { + if (inet_pton(AF_INET6, tmp, &sa->in6.sin6_addr) != 1) { ret = -EINVAL; goto out; } - addr6->sin6_family = AF_INET6; - if (scope && IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) - addr6->sin6_scope_id = if_nametoindex(scope); + sa->in6.sin6_family = AF_INET6; + if (scope && IN6_IS_ADDR_LINKLOCAL(&sa->in6.sin6_addr)) + sa->in6.sin6_scope_id = if_nametoindex(scope); out: free(tmp); return ret; } -static bool sockaddrs_eq(struct sockaddr *addr1, struct sockaddr *addr2) +/* Copy @addr, from libc, into @sa. Return false if it is not IP. */ +static bool sockaddr_copy(const struct sockaddr *addr, union sockaddr_any *sa) { - struct sockaddr_in *sockaddr_v4; - struct sockaddr_in6 *sockaddr_v6; - - if (addr1->sa_family == AF_INET && addr2->sa_family == AF_INET) { - struct sockaddr_in *sockaddr1 = (struct sockaddr_in *)addr1; - struct sockaddr_in *sockaddr2 = (struct sockaddr_in *)addr2; + memset(sa, 0, sizeof(*sa)); - return sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr; + switch (addr->sa_family) { + case AF_INET: + memcpy(&sa->in4, addr, sizeof(sa->in4)); + return true; + case AF_INET6: + memcpy(&sa->in6, addr, sizeof(sa->in6)); + return true; + default: + return false; } +} - if (addr1->sa_family == AF_INET6 && addr2->sa_family == AF_INET6) { - struct sockaddr_in6 *sockaddr1 = (struct sockaddr_in6 *)addr1; - struct sockaddr_in6 *sockaddr2 = (struct sockaddr_in6 *)addr2; +static bool sockaddrs_eq(const union sockaddr_any *a1, + const union sockaddr_any *a2) +{ + sa_family_t f1 = a1->sa.sa_family; + sa_family_t f2 = a2->sa.sa_family; - return !memcmp(&sockaddr1->sin6_addr, &sockaddr2->sin6_addr, - sizeof(struct in6_addr)); - } + if (f1 == AF_INET && f2 == AF_INET) + return a1->in4.sin_addr.s_addr == a2->in4.sin_addr.s_addr; - switch (addr1->sa_family) { - case AF_INET: - sockaddr_v6 = (struct sockaddr_in6 *)addr2; - if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { - sockaddr_v4 = (struct sockaddr_in *)addr1; - return sockaddr_v4->sin_addr.s_addr == - sockaddr_v6->sin6_addr.s6_addr32[3]; - } - break; + if (f1 == AF_INET6 && f2 == AF_INET6) + return !memcmp(&a1->in6.sin6_addr, &a2->in6.sin6_addr, + sizeof(struct in6_addr)); - case AF_INET6: - sockaddr_v6 = (struct sockaddr_in6 *)addr1; - if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { - sockaddr_v4 = (struct sockaddr_in *)addr2; - return sockaddr_v4->sin_addr.s_addr == - sockaddr_v6->sin6_addr.s6_addr32[3]; - } - break; + if (f1 == AF_INET && f2 == AF_INET6) + return IN6_IS_ADDR_V4MAPPED(&a2->in6.sin6_addr) && + a1->in4.sin_addr.s_addr == + a2->in6.sin6_addr.s6_addr32[3]; - default: - break; - } + if (f1 == AF_INET6 && f2 == AF_INET) + return IN6_IS_ADDR_V4MAPPED(&a1->in6.sin6_addr) && + a2->in4.sin_addr.s_addr == + a1->in6.sin6_addr.s6_addr32[3]; return false; } bool shr_ipaddrs_eq(const char *addr1, const char *addr2) { - struct sockaddr_storage ss1, ss2; + union sockaddr_any sa1, sa2; if (addr1 == addr2) return true; @@ -128,13 +133,13 @@ bool shr_ipaddrs_eq(const char *addr1, const char *addr2) if (!addr1 || !addr2) return false; - if (parse_numeric_addr(addr1, &ss1)) + if (parse_numeric_addr(addr1, &sa1)) return false; - if (parse_numeric_addr(addr2, &ss2)) + if (parse_numeric_addr(addr2, &sa2)) return false; - return sockaddrs_eq((struct sockaddr *)&ss1, (struct sockaddr *)&ss2); + return sockaddrs_eq(&sa1, &sa2); } bool shr_ipv6_is_link_local(const char *addr) @@ -160,19 +165,17 @@ const char *shr_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr) { const struct ifaddrs *iface_it; - struct sockaddr_storage ss; + union sockaddr_any sa, ifaddr; const char *iface_name = NULL; - if (!iface_list || !addr || parse_numeric_addr(addr, &ss)) + if (!iface_list || !addr || parse_numeric_addr(addr, &sa)) return NULL; /* Walk through the linked list */ for (iface_it = iface_list; iface_it; iface_it = iface_it->ifa_next) { - struct sockaddr *ifaddr = iface_it->ifa_addr; - bool is_inet = ifaddr && (ifaddr->sa_family == AF_INET || - ifaddr->sa_family == AF_INET6); - - if (is_inet && sockaddrs_eq((struct sockaddr *)&ss, ifaddr)) { + if (iface_it->ifa_addr && + sockaddr_copy(iface_it->ifa_addr, &ifaddr) && + sockaddrs_eq(&sa, &ifaddr)) { iface_name = iface_it->ifa_name; break; } @@ -185,10 +188,10 @@ bool shr_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr) { const struct ifaddrs *iface_it; - struct sockaddr_storage ss; + union sockaddr_any sa, ifaddr; bool match_found = false; - if (!iface_list || !addr || parse_numeric_addr(addr, &ss)) + if (!iface_list || !addr || parse_numeric_addr(addr, &sa)) return false; /* Walk through the linked list */ @@ -202,9 +205,10 @@ bool shr_iface_primary_addr_matches(const struct ifaddrs *iface_list, * have found the primary address for that family. */ if (iface_it->ifa_addr && - (iface_it->ifa_addr->sa_family == ss.ss_family)) { - match_found = sockaddrs_eq((struct sockaddr *)&ss, - iface_it->ifa_addr); + iface_it->ifa_addr->sa_family == sa.sa.sa_family) { + match_found = sockaddr_copy(iface_it->ifa_addr, + &ifaddr) && + sockaddrs_eq(&sa, &ifaddr); break; } } @@ -240,10 +244,8 @@ int shr_route_get_egress_iface(const char *saddr, const char *daddr, char *ifname, size_t iflen) { struct sockaddr_nl nl = {.nl_family = AF_NETLINK}; - struct sockaddr_storage ss_src, ss_dst; char buf[NETLINK_BUFFER_SIZE] = {}; - struct sockaddr_in6 *src6, *dst6; - struct sockaddr_in *src, *dst; + union sockaddr_any src, dst; struct msghdr msg = {}; struct nlmsghdr *nlh; struct rtmsg *rtmsg; @@ -255,16 +257,16 @@ int shr_route_get_egress_iface(const char *saddr, const char *daddr, if (!daddr || !ifname) return -EINVAL; - ret = parse_numeric_addr(daddr, &ss_dst); + ret = parse_numeric_addr(daddr, &dst); if (ret < 0) return ret; if (saddr) { - ret = parse_numeric_addr(saddr, &ss_src); + ret = parse_numeric_addr(saddr, &src); if (ret < 0) return ret; - if (ss_src.ss_family != ss_dst.ss_family) + if (src.sa.sa_family != dst.sa.sa_family) return -EINVAL; } @@ -288,42 +290,34 @@ int shr_route_get_egress_iface(const char *saddr, const char *daddr, nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len); rtmsg = (struct rtmsg *)(buf + nlh->nlmsg_len); - rtmsg->rtm_family = ss_dst.ss_family; - rtmsg->rtm_dst_len = (ss_dst.ss_family == AF_INET) ? 32 : 128; + rtmsg->rtm_family = dst.sa.sa_family; + rtmsg->rtm_dst_len = (dst.sa.sa_family == AF_INET) ? 32 : 128; nlh->nlmsg_len += sizeof(struct rtmsg); /* append attribute RTA_DST */ - if (ss_dst.ss_family == AF_INET) { - dst = (struct sockaddr_in *)&ss_dst; - + if (dst.sa.sa_family == AF_INET) { ret = rtattr_append(nlh, buf, sizeof(buf), RTA_DST, - &dst->sin_addr, sizeof(struct in_addr)); + &dst.in4.sin_addr, sizeof(struct in_addr)); if (ret) goto out; } else { - dst6 = (struct sockaddr_in6 *)&ss_dst; - ret = rtattr_append(nlh, buf, sizeof(buf), RTA_DST, - &dst6->sin6_addr, sizeof(struct in6_addr)); + &dst.in6.sin6_addr, sizeof(struct in6_addr)); if (ret) goto out; } if (saddr) { /* append attribute RTA_SRC */ - if (ss_src.ss_family == AF_INET) { - src = (struct sockaddr_in *)&ss_src; - + if (src.sa.sa_family == AF_INET) { ret = rtattr_append(nlh, buf, sizeof(buf), RTA_SRC, - &src->sin_addr, sizeof(struct in_addr)); + &src.in4.sin_addr, sizeof(struct in_addr)); if (ret) goto out; } else { - src6 = (struct sockaddr_in6 *)&ss_src; - ret = rtattr_append(nlh, buf, sizeof(buf), RTA_SRC, - &src6->sin6_addr, sizeof(struct in6_addr)); + &src.in6.sin6_addr, sizeof(struct in6_addr)); if (ret) goto out; } From 9e813f57650b362c35a5757c50017a3d6e42e1ea Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 14:41:08 -0400 Subject: [PATCH 015/263] shared: use the scope of a link-local egress address Every interface has a route to the IPv6 link-local prefix, so the route lookup for a link-local traddr returns any interface. The I/O queue count then comes from the wrong NIC. For a link-local destination, return the interface that the scope names. Accept an interface index as scope too, as the kernel does. Fixes: 615371089e89 ("shared/net-util-linux: add support for retrieving egress netdev") Signed-off-by: Martin Belanger --- shared/net-util-linux.c | 38 +++++++++++++++++++++++++++++++++++- shared/net-util.h | 7 ++++++- shared/tests/test-net-util.c | 23 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/shared/net-util-linux.c b/shared/net-util-linux.c index 1edae28c55..03115b3bb6 100644 --- a/shared/net-util-linux.c +++ b/shared/net-util-linux.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,27 @@ #include "net-util.h" +/* + * Return the interface index that @scope names: an interface name or, as + * the kernel accepts too, an interface index. Return 0 if there is none. + */ +static unsigned int scope_to_index(const char *scope) +{ + unsigned int index = if_nametoindex(scope); + unsigned long val; + char *end; + + if (index) + return index; + + errno = 0; + val = strtoul(scope, &end, 10); + if (errno || end == scope || *end || val > UINT_MAX) + return 0; + + return val; +} + /* * The members share their storage. Read a member that was written as * another only through the union: other accesses break strict aliasing. @@ -73,7 +95,7 @@ static int parse_numeric_addr(const char *addr, union sockaddr_any *sa) sa->in6.sin6_family = AF_INET6; if (scope && IN6_IS_ADDR_LINKLOCAL(&sa->in6.sin6_addr)) - sa->in6.sin6_scope_id = if_nametoindex(scope); + sa->in6.sin6_scope_id = scope_to_index(scope); out: free(tmp); @@ -273,6 +295,20 @@ int shr_route_get_egress_iface(const char *saddr, const char *daddr, if (iflen < IF_NAMESIZE) return -EINVAL; + /* + * Every interface has a route to the link-local prefix, so the route + * table cannot tell which one to use. The scope names it. + */ + if (dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL(&dst.in6.sin6_addr)) { + if (!dst.in6.sin6_scope_id) + return -EINVAL; + if (!if_indextoname(dst.in6.sin6_scope_id, ifname)) + return -errno; + + return 0; + } + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) return -errno; diff --git a/shared/net-util.h b/shared/net-util.h index a396c095d2..f4da44d3f0 100644 --- a/shared/net-util.h +++ b/shared/net-util.h @@ -70,7 +70,12 @@ bool shr_iface_primary_addr_matches(const struct ifaddrs *iface_list, * Lookup route table using specified saddr and daddr and find the egress * interface. The saddr is optional however daddr is mandatory. * - * Return: 0 on success and negative errno on failure. + * For an IPv6 link-local daddr, the egress interface is the one its + * "%scope" suffix names: an interface name or index. The route table is + * not used. + * + * Return: 0 on success and negative errno on failure. -EINVAL for a + * link-local daddr without a valid scope. */ int shr_route_get_egress_iface(const char *saddr, const char *daddr, char *ifname, size_t iflen); diff --git a/shared/tests/test-net-util.c b/shared/tests/test-net-util.c index f49f7d1710..6b304a5a6a 100644 --- a/shared/tests/test-net-util.c +++ b/shared/tests/test-net-util.c @@ -217,6 +217,7 @@ static bool check_ret(const char *name, int got, int want) static bool test_route_get_egress_iface(void) { char ifname[IF_NAMESIZE]; + char scoped[INET6_ADDRSTRLEN + IF_NAMESIZE]; char small_buf[8]; bool pass = true; int ret; @@ -274,6 +275,28 @@ static bool test_route_get_egress_iface(void) pass &= check_str("IPv6 loopback src/dst egress interface is lo", ifname, "lo"); + /* Link-local: the scope names the interface */ + memset(ifname, 0, sizeof(ifname)); + ret = shr_route_get_egress_iface(NULL, "fe80::1%lo", ifname, + sizeof(ifname)); + pass &= check_ret("link-local with a scope name returns 0", ret, 0); + pass &= check_str("link-local scope name gives lo", ifname, "lo"); + + snprintf(scoped, sizeof(scoped), "fe80::1%%%u", if_nametoindex("lo")); + memset(ifname, 0, sizeof(ifname)); + ret = shr_route_get_egress_iface(NULL, scoped, ifname, sizeof(ifname)); + pass &= check_ret("link-local with a scope index returns 0", ret, 0); + pass &= check_str("link-local scope index gives lo", ifname, "lo"); + + pass &= check_ret("link-local without a scope fails with -EINVAL", + shr_route_get_egress_iface(NULL, "fe80::1", + ifname, sizeof(ifname)), + -EINVAL); + pass &= check_ret("link-local with an unknown scope fails with -EINVAL", + shr_route_get_egress_iface(NULL, "fe80::1%nosuchif0", + ifname, sizeof(ifname)), + -EINVAL); + return pass; } From e8762d4c26e32ed780755d5ad790c5b654418caf Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Tue, 13 Oct 2026 09:29:36 -0400 Subject: [PATCH 016/263] log: add a helper to read a log page without a length field The Supported Capacity Configuration List log has no length field, so it is read again with a larger buffer until it holds all the descriptors. The Media Unit Status log needs the same loop. Move the loop to a helper. The helper reads with libnvme_get_log_dynamic_chunk(), and the maximum length is raised from 256 KiB to 4 MiB. Signed-off-by: Martin Belanger --- plugins/log/log-plugin.c | 99 +++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index f43ef15ec2..20c798a046 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -1621,6 +1621,52 @@ static int get_phy_rx_eom_log(int argc, char **argv, struct command *acmd, return err; } +#define LOG_NO_LEN_MAX_LEN (4 * 1024 * 1024) + +/* + * Read a log page that has no length field. @log_len returns the length + * of the log in a buffer of @len bytes, or a larger value if the buffer is + * too short. Read the log again with a larger buffer until it holds the + * whole log, up to LOG_NO_LEN_MAX_LEN bytes. The data beyond the end of + * the log page is undefined and is not used. + */ +static int get_log_no_len(struct libnvme_transport_handle *hdl, + const struct libnvme_passthru_cmd *init, void **log, + size_t *len, size_t (*log_len)(void *log, size_t len)) +{ + struct libnvme_passthru_cmd cmd; + size_t need; + int err; + + *len = NVME_LOG_PAGE_PDU_SIZE; + for (;;) { + void *p = libnvme_realloc(*log, *len); + + if (!p) + return -ENOMEM; + *log = p; + + cmd = *init; + cmd.addr = (__u64)(uintptr_t)p; + cmd.data_len = *len; + err = libnvme_get_log_dynamic_chunk(hdl, &cmd, false, *len); + if (err) + return err; + + need = log_len(p, *len); + if (need <= *len) + return 0; + if (*len == LOG_NO_LEN_MAX_LEN) { + nvme_show_error("log longer than %d bytes, truncated", + LOG_NO_LEN_MAX_LEN); + return 0; + } + need = (need + NVME_LOG_PAGE_PDU_SIZE - 1) & + ~(size_t)(NVME_LOG_PAGE_PDU_SIZE - 1); + *len = min_t(size_t, max(need, 2 * *len), LOG_NO_LEN_MAX_LEN); + } +} + static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { @@ -1676,8 +1722,6 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, return err; } -#define SUPP_CAP_CONFIG_LOG_MAX_LEN (256 * 1024) - /* * The functions below return the offset after a descriptor that starts * at @off. A value larger than @len means that the buffer is too short. @@ -1731,11 +1775,10 @@ static size_t cap_config_egcd_end(const unsigned char *buf, size_t off, return off; } -/* The log page has no length field. */ -static size_t supp_cap_config_log_len( - struct nvme_supported_cap_config_list_log *log, size_t len) +static size_t supp_cap_config_log_len(void *p, size_t len) { - const unsigned char *buf = (const unsigned char *)log; + struct nvme_supported_cap_config_list_log *log = p; + const unsigned char *buf = p; struct nvme_capacity_config_desc desc; size_t off = sizeof(*log); @@ -1758,10 +1801,10 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, { const char *desc = "Retrieve the list of Supported Capacity Configuration Descriptors"; - __cleanup_libnvme_free struct nvme_supported_cap_config_list_log *cap_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - size_t len = NVME_LOG_PAGE_PDU_SIZE, need; + __cleanup_libnvme_free void *cap_log = NULL; + size_t len; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; int err = -1; @@ -1793,40 +1836,12 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, if (cfg.raw_binary) flags = BINARY; - /* - * Read the log again with a larger buffer until it holds all the - * descriptors. The data beyond the end of the log page is undefined - * and is not used. - */ - for (;;) { - void *p = libnvme_realloc(cap_log, len); - - if (!p) - return -ENOMEM; - cap_log = p; - - nvme_init_get_log_support_cap_config_list(&cmd, cfg.domainid, - cap_log); - cmd.data_len = len; - err = libnvme_get_log(hdl, &cmd, false, NVME_LOG_PAGE_PDU_SIZE); - if (err) { - nvme_show_err(err, - "supported capacity configuration list log"); - return err; - } - - need = supp_cap_config_log_len(cap_log, len); - if (need <= len) - break; - if (len == SUPP_CAP_CONFIG_LOG_MAX_LEN) { - nvme_show_error("log longer than %d bytes, truncated", - SUPP_CAP_CONFIG_LOG_MAX_LEN); - break; - } - need = (need + NVME_LOG_PAGE_PDU_SIZE - 1) & - ~(size_t)(NVME_LOG_PAGE_PDU_SIZE - 1); - len = min_t(size_t, max(need, 2 * len), - SUPP_CAP_CONFIG_LOG_MAX_LEN); + nvme_init_get_log_support_cap_config_list(&cmd, cfg.domainid, NULL); + err = get_log_no_len(hdl, &cmd, &cap_log, &len, + supp_cap_config_log_len); + if (err) { + nvme_show_err(err, "supported capacity configuration list log"); + return err; } nvme_show_supported_cap_config_log(cap_log, len, flags); From 1c14a0d92d18514c4167925bf02dd1308332c491 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Tue, 13 Oct 2026 09:29:36 -0400 Subject: [PATCH 017/263] nvme-print: fix the media unit status log Only the 16-byte log header was read. The printers then read the descriptors from memory past the end of the buffer. They indexed the descriptors as an array of 14-byte structs, but a descriptor is CIO + 2 * MUCS bytes long. So every descriptor after the first one was read at a wrong offset. The JSON printer used NMU without converting it from little endian, and the binary printer dumped only the header. Read the log again with a larger buffer until it holds all the descriptors. Pass the length to the printers and stop at the end of the buffer, or at a descriptor whose CIO points into its own header. Also print the Channel Identifiers. Fixes: #4155 Signed-off-by: Martin Belanger --- plugins/log/log-plugin.c | 25 +++++++---- src/nvme-print-binary.c | 5 ++- src/nvme-print-json.c | 47 ++++++++++++++------- src/nvme-print-stdout-log.c | 47 +++++++++++++-------- src/nvme-print-stdout.h | 3 +- src/nvme-print.c | 5 ++- src/nvme-print.h | 81 +++++++++++++++++++++++++++++++++++- tests/cli/nvme_print_test.py | 44 +++++++++++++++++++- 8 files changed, 209 insertions(+), 48 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 20c798a046..6ede4c8dee 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -1667,15 +1667,28 @@ static int get_log_no_len(struct libnvme_transport_handle *hdl, } } +static size_t media_unit_stat_log_len(void *p, size_t len) +{ + struct nvme_media_unit_stat_desc desc; + struct mus_desc_iter it; + + mus_desc_iter_init(&it, p, len); + while (mus_desc_iter_next(&it, &desc)) + ; + + return it.need ? it.need : len; +} + static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Retrieve the configuration and wear of media units and print it"; - __cleanup_libnvme_free struct nvme_media_unit_stat_log *mus = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; + __cleanup_libnvme_free void *mus = NULL; struct libnvme_passthru_cmd cmd; + size_t len; nvme_print_flags_t flags; int err = -1; @@ -1706,18 +1719,14 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, if (cfg.raw_binary) flags = BINARY; - mus = libnvme_alloc(sizeof(*mus)); - if (!mus) - return -ENOMEM; - - nvme_init_get_log_media_unit_stat(&cmd, cfg.domainid, mus); - err = libnvme_get_log(hdl, &cmd, false, sizeof(*mus)); + nvme_init_get_log_media_unit_stat(&cmd, cfg.domainid, NULL); + err = get_log_no_len(hdl, &cmd, &mus, &len, media_unit_stat_log_len); if (err) { nvme_show_err(err, "media unit status log"); return err; } - nvme_show_media_unit_stat_log(mus, flags); + nvme_show_media_unit_stat_log(mus, len, flags); return err; } diff --git a/src/nvme-print-binary.c b/src/nvme-print-binary.c index 847a6b7d23..506028561a 100644 --- a/src/nvme-print-binary.c +++ b/src/nvme-print-binary.c @@ -72,9 +72,10 @@ static void binary_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 control d_raw((unsigned char *)log, len); } -static void binary_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log) +static void binary_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log, + size_t len) { - d_raw((unsigned char *)mus_log, sizeof(*mus_log)); + d_raw((unsigned char *)mus_log, len); } static void binary_fdp_configs(struct nvme_fdp_config_log *log, size_t len) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 6c75a1df7c..7e251f8462 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2530,28 +2530,47 @@ static void json_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controlle } } -static void json_media_unit_stat_log(struct nvme_media_unit_stat_log *mus) +static void json_media_unit_stat_log(struct nvme_media_unit_stat_log *mus, + size_t len) { struct json_object *r = json_r; struct json_object *entries = json_create_array(); - struct json_object *entry; - int i; + struct nvme_media_unit_stat_desc desc; + struct mus_desc_iter it; + + if (len < sizeof(*mus)) { + obj_add_array(r, "mus_list", entries); + return; + } obj_add_uint(r, "nmu", le16_to_cpu(mus->nmu)); obj_add_uint(r, "cchans", le16_to_cpu(mus->cchans)); obj_add_uint(r, "sel_config", le16_to_cpu(mus->sel_config)); - for (i = 0; i < mus->nmu; i++) { - entry = json_create_object(); - obj_add_uint(entry, "muid", le16_to_cpu(mus->mus_desc[i].muid)); - obj_add_uint(entry, "domainid", le16_to_cpu(mus->mus_desc[i].domainid)); - obj_add_uint(entry, "endgid", le16_to_cpu(mus->mus_desc[i].endgid)); - obj_add_uint(entry, "nvmsetid", le16_to_cpu(mus->mus_desc[i].nvmsetid)); - obj_add_uint(entry, "cap_adj_fctr", le16_to_cpu(mus->mus_desc[i].cap_adj_fctr)); - obj_add_uint(entry, "avl_spare", mus->mus_desc[i].avl_spare); - obj_add_uint(entry, "percent_used", mus->mus_desc[i].percent_used); - obj_add_uint(entry, "mucs", mus->mus_desc[i].mucs); - obj_add_uint(entry, "cio", mus->mus_desc[i].cio); + mus_desc_iter_init(&it, mus, len); + while (mus_desc_iter_next(&it, &desc)) { + struct json_object *entry = json_create_object(); + struct json_object *chans = json_create_array(); + + obj_add_uint(entry, "muid", le16_to_cpu(desc.muid)); + obj_add_uint(entry, "domainid", le16_to_cpu(desc.domainid)); + obj_add_uint(entry, "endgid", le16_to_cpu(desc.endgid)); + obj_add_uint(entry, "nvmsetid", le16_to_cpu(desc.nvmsetid)); + obj_add_uint(entry, "cap_adj_fctr", + le16_to_cpu(desc.cap_adj_fctr)); + obj_add_uint(entry, "avl_spare", desc.avl_spare); + obj_add_uint(entry, "percent_used", desc.percent_used); + obj_add_uint(entry, "mucs", desc.mucs); + obj_add_uint(entry, "cio", desc.cio); + + for (int k = 0; k < desc.mucs; k++) { + struct json_object *chan = json_create_object(); + + obj_add_uint(chan, "chanid", + mus_desc_iter_chan(&it, k)); + array_add_obj(chans, chan); + } + obj_add_array(entry, "chanids", chans); array_add_obj(entries, entry); } diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 9060d4b2b2..0fcd81c145 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -1239,17 +1239,22 @@ void stdout_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controller, stdout_phy_rx_eom_descs(log, len); } -void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log) +void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log, + size_t len) { - int i; - int nmu = le16_to_cpu(mus_log->nmu); + struct nvme_media_unit_stat_desc desc; + struct mus_desc_iter it; struct shr_table *t; + if (len < sizeof(*mus_log)) + return; + t = stdout_kv_table_create(); if (!t) return; - stdout_kv_add(t, "Number of Media Unit Status Descriptors", "%u", nmu); + stdout_kv_add(t, "Number of Media Unit Status Descriptors", "%u", + le16_to_cpu(mus_log->nmu)); stdout_kv_add(t, "Number of Channels", "%u", le16_to_cpu(mus_log->cchans)); stdout_kv_add(t, "Selected Configuration", "%u", @@ -1257,7 +1262,8 @@ void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log) stdout_kv_table_finish(t, "media-unit-stat-log"); - for (i = 0; i < nmu; i++) { + mus_desc_iter_init(&it, mus_log, len); + for (int i = 0; mus_desc_iter_next(&it, &desc); i++) { printf("Media Unit Status Descriptor: %u\n", i); t = stdout_kv_table_create(); @@ -1265,23 +1271,28 @@ void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log) return; stdout_kv_add(t, "Media Unit Identifier", "%u", - le16_to_cpu(mus_log->mus_desc[i].muid)); + le16_to_cpu(desc.muid)); stdout_kv_add(t, "Domain Identifier", "%u", - le16_to_cpu(mus_log->mus_desc[i].domainid)); + le16_to_cpu(desc.domainid)); stdout_kv_add(t, "Endurance Group Identifier", "%u", - le16_to_cpu(mus_log->mus_desc[i].endgid)); + le16_to_cpu(desc.endgid)); stdout_kv_add(t, "NVM Set Identifier", "%u", - le16_to_cpu(mus_log->mus_desc[i].nvmsetid)); + le16_to_cpu(desc.nvmsetid)); stdout_kv_add(t, "Capacity Adjustment Factor", "%u", - le16_to_cpu(mus_log->mus_desc[i].cap_adj_fctr)); - stdout_kv_add(t, "Available Spare", "%u", - mus_log->mus_desc[i].avl_spare); - stdout_kv_add(t, "Percentage Used", "%u", - mus_log->mus_desc[i].percent_used); - stdout_kv_add(t, "Number of Channels", "%u", - mus_log->mus_desc[i].mucs); - stdout_kv_add(t, "Channel Identifiers Offset", "%u", - mus_log->mus_desc[i].cio); + le16_to_cpu(desc.cap_adj_fctr)); + stdout_kv_add(t, "Available Spare", "%u", desc.avl_spare); + stdout_kv_add(t, "Percentage Used", "%u", desc.percent_used); + stdout_kv_add(t, "Number of Channels", "%u", desc.mucs); + stdout_kv_add(t, "Channel Identifiers Offset", "%u", desc.cio); + + for (int k = 0; k < desc.mucs; k++) { + char name[32]; + + snprintf(name, sizeof(name), "Channel %d Identifier", + k); + stdout_kv_add(t, name, "%u", + mus_desc_iter_chan(&it, k)); + } stdout_kv_table_finish(t, "media-unit-stat-log"); } diff --git a/src/nvme-print-stdout.h b/src/nvme-print-stdout.h index 9ede1fe921..410dca02c6 100644 --- a/src/nvme-print-stdout.h +++ b/src/nvme-print-stdout.h @@ -60,7 +60,8 @@ void stdout_fid_support_effects_log( struct nvme_fid_supported_effects_log *fid_log, const char *devname); void stdout_fw_log(struct nvme_firmware_slot *fw_log, const char *devname); void stdout_lba_status_log(void *lba_status, __u32 size, const char *devname); -void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log); +void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log, + size_t len); void stdout_mi_cmd_support_effects_log( struct nvme_mi_cmd_supported_effects_log *mi_cmd_log, const char *devname); diff --git a/src/nvme-print.c b/src/nvme-print.c index 5df0f0c008..84112eaf4b 100644 --- a/src/nvme-print.c +++ b/src/nvme-print.c @@ -419,9 +419,10 @@ void nvme_show_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controller, } void nvme_show_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log, + size_t len, nvme_print_flags_t flags) { - nvme_print(media_unit_stat_log, flags, mus_log); + nvme_print(media_unit_stat_log, flags, mus_log, len); } void nvme_show_fdp_configs(struct nvme_fdp_config_log *log, size_t len, @@ -2060,7 +2061,7 @@ void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsi break; } case NVME_LOG_LID_MEDIA_UNIT_STATUS: - nvme_show_media_unit_stat_log(log, flags); + nvme_show_media_unit_stat_log(log, len, flags); break; case NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST: nvme_show_supported_cap_config_log(log, len, flags); diff --git a/src/nvme-print.h b/src/nvme-print.h index 6351c95dc8..72ae2c1ad7 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -187,7 +187,7 @@ struct print_ops { void (*id_uuid_list)(const struct nvme_id_uuid_list *uuid_list); void (*lba_status)(struct nvme_lba_status *list, unsigned long len); void (*lba_status_log)(void *lba_status, __u32 size, const char *devname); - void (*media_unit_stat_log)(struct nvme_media_unit_stat_log *mus); + void (*media_unit_stat_log)(struct nvme_media_unit_stat_log *mus, size_t len); void (*mi_cmd_support_effects_log)(struct nvme_mi_cmd_supported_effects_log *mi_cmd_log, const char *devname); void (*ns_list)(struct nvme_ns_list *ns_list); void (*ns_list_log)(struct nvme_ns_list *log, const char *devname, bool alloc); @@ -418,12 +418,89 @@ static inline unsigned char *eom_desc_iter_vsdata(struct eom_desc_iter *it, *vsdatalen = edlen; return it->cur + vsdataoffset; } + +/* + * Walks the Media Unit Status Descriptors. A descriptor is CIO + 2 * MUCS + * bytes long. CIO shall be a multiple of 16, but a device may report + * another value, so each descriptor is copied before it is read. + */ +struct mus_desc_iter { + unsigned char *start; + unsigned char *p; + unsigned char *end; + unsigned char *chans; // Channel Identifiers of the last descriptor + uint16_t remaining; + size_t need; // log length needed if the buffer is short +}; + +static inline void mus_desc_iter_init(struct mus_desc_iter *it, + struct nvme_media_unit_stat_log *log, size_t len) +{ + it->start = (unsigned char *)log; + it->p = (unsigned char *)log->mus_desc; + it->end = it->start + len; + it->chans = NULL; + it->remaining = len < sizeof(*log) ? 0 : le16_to_cpu(log->nmu); + it->need = 0; +} + +static inline bool mus_desc_iter_short(struct mus_desc_iter *it, size_t size) +{ + it->need = it->p - it->start + size; + it->remaining = 0; + + return false; +} + +/* + * Copies the next descriptor to @desc. Returns false at the end of the + * list, at the end of the buffer, or at a descriptor whose CIO points + * into its own header: the next descriptors cannot be located then. + */ +static inline bool mus_desc_iter_next(struct mus_desc_iter *it, + struct nvme_media_unit_stat_desc *desc) +{ + size_t size; + + if (!it->remaining) + return false; + + if (!shr_buf_has_room(it->p, it->end, sizeof(*desc))) + return mus_desc_iter_short(it, sizeof(*desc)); + + memcpy(desc, it->p, sizeof(*desc)); + if (desc->cio < sizeof(*desc)) { + it->remaining = 0; + return false; + } + + size = desc->cio + desc->mucs * sizeof(__le16); + if (!shr_buf_has_room(it->p, it->end, size)) + return mus_desc_iter_short(it, size); + + it->chans = it->p + desc->cio; + it->p += size; + it->remaining--; + + return true; +} + +/* Channel Identifier @i of the descriptor most recently returned. */ +static inline uint16_t mus_desc_iter_chan(const struct mus_desc_iter *it, + int i) +{ + __le16 id; + + memcpy(&id, it->chans + i * sizeof(id), sizeof(id)); + + return le16_to_cpu(id); +} void nvme_show_fid_support_effects_log(struct nvme_fid_supported_effects_log *fid_log, const char *devname, nvme_print_flags_t flags); void nvme_show_mi_cmd_support_effects_log(struct nvme_mi_cmd_supported_effects_log *mi_cmd_log, const char *devname, nvme_print_flags_t flags); void nvme_show_media_unit_stat_log(struct nvme_media_unit_stat_log *mus, - nvme_print_flags_t flags); + size_t len, nvme_print_flags_t flags); void nvme_show_supported_cap_config_log(struct nvme_supported_cap_config_list_log *caplog, size_t len, nvme_print_flags_t flags); void nvme_show_ctrl_registers(void *bar, bool fabrics, nvme_print_flags_t flags); diff --git a/tests/cli/nvme_print_test.py b/tests/cli/nvme_print_test.py index caa05d18c8..dbfb7ed3cc 100644 --- a/tests/cli/nvme_print_test.py +++ b/tests/cli/nvme_print_test.py @@ -52,6 +52,7 @@ _OPC_GET_FEATURES = 0x0a _FID_FDP_EVENTS = 0x1e _LID_PERSISTENT_EVENT = 0x0d +_LID_MEDIA_UNIT_STATUS = 0x10 _LID_SUPPORTED_CAP_CONFIG = 0x11 _CNS_CTRL = 0x01 _NVME_IOCTL_ID = 0x40 @@ -68,7 +69,7 @@ def _put(buf, fmt, offset, value): 0x0c: [(' Date: Wed, 14 Oct 2026 18:04:30 +0200 Subject: [PATCH 018/263] libnvme: add a namespace WWID accessor Expose the namespace wwid through the generated lazy accessors and use it in the topology test instead of the legacy uuid sysfs attribute, which warns when falling back to the NGUID. Keep the existing identifier accessors unchanged. Signed-off-by: ooonea <35407790+ooonea@users.noreply.github.com> Assisted-by: OpenAI Codex --- libnvme/libnvme3/attr-accessors.i | 19 ++++++++++++++++ libnvme/src/attr-accessors.ld | 1 + libnvme/src/nvme/attr_accessors_specs.py | 5 +++++ libnvme/src/nvme/generated/attr-accessors.c | 24 +++++++++++++++++++++ libnvme/src/nvme/generated/attr-accessors.h | 14 ++++++++++++ libnvme/tests/test.c | 16 +++++--------- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/libnvme/libnvme3/attr-accessors.i b/libnvme/libnvme3/attr-accessors.i index 6645d6469f..a2c047ff7f 100644 --- a/libnvme/libnvme3/attr-accessors.i +++ b/libnvme/libnvme3/attr-accessors.i @@ -574,6 +574,19 @@ return PyLong_FromLong(val); } + static const char *libnvme_ns_wwid_get(const struct libnvme_ns *p) + { + const char *val; + int ret = libnvme_ns_get_wwid(p, &val, NULL); + + if (ret == 0) + return val; + if (ret == -ENOENT) + return NULL; + + raise_nvme(NvmeError, ret); + return NULL; + } static PyObject *libnvme_ns_command_retry_count_get(const struct libnvme_ns *p) { long val; @@ -656,6 +669,10 @@ $action if (PyErr_Occurred()) SWIG_fail; } +%exception libnvme_ns::wwid { + $action + if (PyErr_Occurred()) SWIG_fail; +} %exception libnvme_ns::command_retry_count { $action if (PyErr_Occurred()) SWIG_fail; @@ -686,6 +703,8 @@ PyObject * meta_size; %immutable csi; PyObject * csi; + %immutable wwid; + const char * wwid; %immutable command_retry_count; PyObject * command_retry_count; %immutable command_error_count; diff --git a/libnvme/src/attr-accessors.ld b/libnvme/src/attr-accessors.ld index b534cf388a..2b8c23717e 100644 --- a/libnvme/src/attr-accessors.ld +++ b/libnvme/src/attr-accessors.ld @@ -58,6 +58,7 @@ LIBNVME_ATTR_ACCESSORS_3 { libnvme_ns_get_eui64; libnvme_ns_get_nguid; libnvme_ns_get_uuid; + libnvme_ns_get_wwid; libnvme_ns_get_command_retry_count; libnvme_ns_get_command_error_count; libnvme_ns_get_io_requeue_no_usable_path_count; diff --git a/libnvme/src/nvme/attr_accessors_specs.py b/libnvme/src/nvme/attr_accessors_specs.py index 62b0b4ec99..0918400cc6 100644 --- a/libnvme/src/nvme/attr_accessors_specs.py +++ b/libnvme/src/nvme/attr_accessors_specs.py @@ -290,6 +290,11 @@ 'type': 'unsigned char *', 'custom': True, }, + { + 'name': 'wwid', + 'attr': 'wwid', + 'type': 'char *', + }, # The four diag/* counters need no per-OS override: like most # CTRL_ATTRS members, Windows absence falls out of the existing # libnvme_get_ns_attr() stub (unconditionally NULL) for free -- diff --git a/libnvme/src/nvme/generated/attr-accessors.c b/libnvme/src/nvme/generated/attr-accessors.c index b164b347f2..45e5bb6fad 100644 --- a/libnvme/src/nvme/generated/attr-accessors.c +++ b/libnvme/src/nvme/generated/attr-accessors.c @@ -581,6 +581,7 @@ struct libnvme_ns_attrs { uint8_t *eui64; uint8_t *nguid; unsigned char *uuid; + char *wwid; long command_retry_count; long command_error_count; long io_requeue_no_usable_path_count; @@ -615,9 +616,32 @@ void libnvme_ns_attrs_free( ATTR_FREE(attrs->eui64); ATTR_FREE(attrs->nguid); ATTR_FREE(attrs->uuid); + ATTR_FREE(attrs->wwid); free(attrs); } +__shr_public int libnvme_ns_get_wwid( + const struct libnvme_ns *p, + const char **val, + const char *dflt) +{ + struct libnvme_ns *c = (struct libnvme_ns *)p; + + *val = dflt; + + if (__shr_unlikely(!ATTR_IS_LOADED(c->attrs->wwid))) { + c->attrs->wwid = libnvme_get_ns_attr(c, "wwid"); + if (!c->attrs->wwid) + c->attrs->wwid = NO_ATTR; + } + + if (ATTR_IS_ABSENT(c->attrs->wwid)) + return -ENOENT; + + *val = c->attrs->wwid; + return 0; +} + __shr_public int libnvme_ns_get_command_retry_count( const struct libnvme_ns *p, long *val, diff --git a/libnvme/src/nvme/generated/attr-accessors.h b/libnvme/src/nvme/generated/attr-accessors.h index 9b942dc992..762e7fe7ff 100644 --- a/libnvme/src/nvme/generated/attr-accessors.h +++ b/libnvme/src/nvme/generated/attr-accessors.h @@ -589,6 +589,20 @@ int libnvme_ns_get_uuid( const unsigned char **uuid, const unsigned char *dflt); +/** + * libnvme_ns_get_wwid() - Get wwid. + * @p: The &struct libnvme_ns instance to query. + * @wwid: Where to store the value on success. + * @dflt: Value to store in @wwid on failure. + * + * Return: 0 on success, -ENOENT if the attribute does not + * exist, or a negative errno on failure. + */ +int libnvme_ns_get_wwid( + const struct libnvme_ns *p, + const char **wwid, + const char *dflt); + /** * libnvme_ns_get_command_retry_count() - Get command_retry_count. * @p: The &struct libnvme_ns instance to query. diff --git a/libnvme/tests/test.c b/libnvme/tests/test.c index 2ff4c10d0e..cb990812f0 100644 --- a/libnvme/tests/test.c +++ b/libnvme/tests/test.c @@ -378,10 +378,9 @@ static void print_hex(const uint8_t *x, int len) printf("%02x", x[i]); } -/* Safe display fallbacks for the lazy eui64/nguid/uuid getters below. */ +/* Safe display fallbacks for the lazy eui64/nguid getters below. */ static const uint8_t zero_eui64[8]; static const uint8_t zero_nguid[16]; -static const unsigned char zero_uuid[NVME_UUID_LEN]; int main(int argc, char **argv) { @@ -448,8 +447,7 @@ int main(int argc, char **argv) libnvme_ctrl_get_state(c)); libnvme_ctrl_for_each_ns(c, n) { - char uuid_str[NVME_UUID_LEN_STRING]; - const unsigned char *uuid; + const char *wwid; const uint8_t *eui64; const uint8_t *nguid; int lba_size; @@ -471,15 +469,11 @@ int main(int argc, char **argv) libnvme_ns_get_nguid(n, &nguid, zero_nguid); print_hex(nguid, 16); - libnvme_ns_get_uuid(n, &uuid, - zero_uuid); - libnvme_uuid_to_string( - (unsigned char *)uuid, - uuid_str); + libnvme_ns_get_wwid(n, &wwid, ""); libnvme_ns_get_csi(n, &csi, NVME_CSI_NVM); - printf(" uuid:%s csi:%d\n", - uuid_str, csi); + printf(" wwid:%s csi:%d\n", + wwid, csi); } libnvme_ctrl_for_each_path(c, p) { From efa99039eebc8c0e3a16717f7e962a21c95a872d Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 15:57:56 -0400 Subject: [PATCH 019/263] daemon-util: add a pool of workers for blocking work nvme-discoverd reads discovery logs in its event loop, and a read waits for the DC. The resolver already runs getaddrinfo() in worker threads. Move its thread pool into dmn_work, which runs any function in a worker and reports its end from the event loop. The resolver uses dmn_work and keeps its API. A timeout now counts from the current time. sd_event_now() is the time of the last wakeup of the event loop, so a timeout started at the end of a long callback expired too early. Signed-off-by: Martin Belanger --- daemon-util/meson.build | 1 + daemon-util/resolve.c | 353 ++++-------------------------- daemon-util/tests/meson.build | 11 + daemon-util/tests/test-work.c | 288 ++++++++++++++++++++++++ daemon-util/work.c | 398 ++++++++++++++++++++++++++++++++++ daemon-util/work.h | 68 ++++++ 6 files changed, 806 insertions(+), 313 deletions(-) create mode 100644 daemon-util/tests/test-work.c create mode 100644 daemon-util/work.c create mode 100644 daemon-util/work.h diff --git a/daemon-util/meson.build b/daemon-util/meson.build index 9ae11dcd56..4e3a42101b 100644 --- a/daemon-util/meson.build +++ b/daemon-util/meson.build @@ -14,6 +14,7 @@ daemon_util_lib = static_library( 'log.c', 'resolve.c', 'signals.c', + 'work.c', ), dependencies: [ ccan_dep, diff --git a/daemon-util/resolve.c b/daemon-util/resolve.c index 5fbea23e10..f9430e4e50 100644 --- a/daemon-util/resolve.c +++ b/daemon-util/resolve.c @@ -7,60 +7,25 @@ */ #include -#include -#include #include #include #include -#include -#include -#include - -#include #include "resolve.h" +#include "work.h" -enum query_state { - QUERY_QUEUED, // waiting for a worker - QUERY_RUNNING, // a worker is in getaddrinfo() - QUERY_DONE, // waiting for the event loop - QUERY_DROPPED, // cancelled while running: the worker frees it +struct dmn_resolver { + struct dmn_work_pool *pool; + dmn_getaddrinfo_fn lookup; }; /* - * The state shared with the workers. A worker can outlive the resolver - * while it is in getaddrinfo(), so the pool is freed by its last user. + * The worker writes only res, gai and sys_errno. The other fields do not + * change after dmn_resolve(). */ -struct pool { - pthread_mutex_t lock; - pthread_cond_t cond; - struct list_head queued; - struct list_head done; - unsigned int max_workers; - unsigned int nr_workers; - unsigned int nr_idle; - unsigned int nr_queued; - unsigned int refs; // the resolver and each worker - bool closing; - int fd; // eventfd: the workers wake up the event loop - dmn_getaddrinfo_fn lookup; -}; - -struct dmn_resolver { - struct pool *pool; - sd_event *event; - sd_event_source *io; - struct list_head active; // event loop only - bool dispatching; - bool free_pending; -}; - struct dmn_resolve_query { - struct list_node pool_node; // in pool->queued or pool->done - struct list_node node; // in resolver->active - struct dmn_resolver *r; - sd_event_source *timer; - enum query_state state; // pool->lock + struct dmn_work *work; + dmn_getaddrinfo_fn lookup; char *host; char *service; struct addrinfo hints; @@ -72,267 +37,61 @@ struct dmn_resolve_query { void *user_data; }; -static void query_free(struct dmn_resolve_query *q) +static void query_free(void *data) { + struct dmn_resolve_query *q = data; + if (!q) return; + if (q->res) + freeaddrinfo(q->res); free(q->host); free(q->service); free(q); } -// Called with pool->lock held. Unlocks it. -static void pool_unref_unlock(struct pool *p) -{ - bool last = --p->refs == 0; - - pthread_mutex_unlock(&p->lock); - if (!last) - return; - - pthread_cond_destroy(&p->cond); - pthread_mutex_destroy(&p->lock); - close(p->fd); - free(p); -} - -static void *worker(void *arg) -{ - static const uint64_t one = 1; - struct pool *p = arg; - struct dmn_resolve_query *q; - dmn_getaddrinfo_fn lookup; - struct addrinfo *res; - int gai, sys_errno; - - pthread_mutex_lock(&p->lock); - for (;;) { - while (!p->closing && list_empty(&p->queued)) { - p->nr_idle++; - pthread_cond_wait(&p->cond, &p->lock); - p->nr_idle--; - } - if (p->closing) - break; - - q = list_pop(&p->queued, struct dmn_resolve_query, pool_node); - p->nr_queued--; - q->state = QUERY_RUNNING; - lookup = p->lookup; - pthread_mutex_unlock(&p->lock); - - res = NULL; - gai = lookup(q->host, q->service, - q->has_hints ? &q->hints : NULL, &res); - sys_errno = errno; - - pthread_mutex_lock(&p->lock); - if (q->state == QUERY_DROPPED) { - if (res) - freeaddrinfo(res); - query_free(q); - continue; - } - - q->state = QUERY_DONE; - q->gai = gai; - q->sys_errno = sys_errno; - q->res = res; - list_add_tail(&p->done, &q->pool_node); - // Cannot fail: the counter is far from overflowing. - (void)!write(p->fd, &one, sizeof(one)); - } - pool_unref_unlock(p); - - return NULL; -} - -// Called with pool->lock held. -static int start_worker(struct pool *p) -{ - pthread_attr_t attr; - sigset_t all, old; - pthread_t t; - int ret; - - ret = pthread_attr_init(&attr); - if (ret) - return -ret; - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - // Signals go to the event loop thread, not to a worker. - sigfillset(&all); - pthread_sigmask(SIG_BLOCK, &all, &old); - ret = pthread_create(&t, &attr, worker, p); - pthread_sigmask(SIG_SETMASK, &old, NULL); - pthread_attr_destroy(&attr); - if (ret) - return -ret; - - p->refs++; - p->nr_workers++; - - return 0; -} - -/* - * Take @q away from the pool and from the event loop. Returns the query if - * the caller must free it, NULL if a worker frees it. - */ -static struct dmn_resolve_query *detach(struct dmn_resolve_query *q) -{ - struct pool *p = q->r->pool; - bool running; - - list_del(&q->node); - q->timer = sd_event_source_disable_unref(q->timer); - - pthread_mutex_lock(&p->lock); - running = q->state == QUERY_RUNNING; - if (running) - q->state = QUERY_DROPPED; - else - list_del(&q->pool_node); - if (q->state == QUERY_QUEUED) - p->nr_queued--; - pthread_mutex_unlock(&p->lock); - - if (running) - return NULL; - - if (q->res) - freeaddrinfo(q->res); - q->res = NULL; - - return q; -} - -static void destroy(struct dmn_resolver *r) -{ - struct dmn_resolve_query *q, *next; - struct pool *p = r->pool; - - list_for_each_safe(&r->active, q, next, node) { - q = detach(q); - if (q) - query_free(q); - } - sd_event_source_disable_unref(r->io); - sd_event_unref(r->event); - free(r); - - pthread_mutex_lock(&p->lock); - p->closing = true; - pthread_cond_broadcast(&p->cond); - pool_unref_unlock(p); -} - -// Detach @q, free it, then call its callback. -static void finish(struct dmn_resolve_query *q, int gai, int sys_errno, - struct addrinfo *res) +static void lookup(void *data) { - dmn_resolve_callback cb = q->cb; - void *user_data = q->user_data; - - q->res = NULL; - q = detach(q); - query_free(q); + struct dmn_resolve_query *q = data; - errno = sys_errno; - cb(gai, res, user_data); - if (res) - freeaddrinfo(res); + q->gai = q->lookup(q->host, q->service, + q->has_hints ? &q->hints : NULL, &q->res); + q->sys_errno = errno; } -static int on_done(sd_event_source *src __attribute__((unused)), int fd, - uint32_t revents __attribute__((unused)), void *user_data) +static void lookup_done(void *data, bool timed_out) { - struct dmn_resolver *r = user_data; - struct dmn_resolve_query *q; - uint64_t n; + struct dmn_resolve_query *q = data; - (void)!read(fd, &n, sizeof(n)); - - r->dispatching = true; - while (!r->free_pending) { - pthread_mutex_lock(&r->pool->lock); - q = list_top(&r->pool->done, struct dmn_resolve_query, - pool_node); - pthread_mutex_unlock(&r->pool->lock); - if (!q) - break; - - finish(q, q->gai, q->sys_errno, q->res); + if (timed_out) { + errno = ETIMEDOUT; + q->cb(EAI_SYSTEM, NULL, q->user_data); + return; } - r->dispatching = false; - if (r->free_pending) - destroy(r); - - return 0; -} - -static int on_timeout(sd_event_source *src __attribute__((unused)), - uint64_t usec __attribute__((unused)), void *user_data) -{ - struct dmn_resolve_query *q = user_data; - struct pool *p = q->r->pool; - bool done; - - // A result that is ready is reported by on_done(). - pthread_mutex_lock(&p->lock); - done = q->state == QUERY_DONE; - pthread_mutex_unlock(&p->lock); - if (done) - return 0; - - finish(q, EAI_SYSTEM, ETIMEDOUT, NULL); - - return 0; + errno = q->sys_errno; + q->cb(q->gai, q->res, q->user_data); } int dmn_resolver_new(sd_event *event, unsigned int max_workers, struct dmn_resolver **ret) { struct dmn_resolver *r; - struct pool *p; int err; if (!event || !max_workers || !ret) return -EINVAL; r = calloc(1, sizeof(*r)); - p = calloc(1, sizeof(*p)); - if (!r || !p) { - free(r); - free(p); + if (!r) return -ENOMEM; - } - p->fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (p->fd < 0) { - err = -errno; - free(r); - free(p); - return err; - } - pthread_mutex_init(&p->lock, NULL); - pthread_cond_init(&p->cond, NULL); - list_head_init(&p->queued); - list_head_init(&p->done); - p->max_workers = max_workers; - p->refs = 1; - p->lookup = getaddrinfo; - - r->pool = p; - r->event = sd_event_ref(event); - list_head_init(&r->active); - - err = sd_event_add_io(event, &r->io, p->fd, EPOLLIN, on_done, r); + err = dmn_work_pool_new(event, max_workers, &r->pool); if (err < 0) { - destroy(r); + free(r); return err; } + r->lookup = getaddrinfo; *ret = r; @@ -344,18 +103,13 @@ void dmn_resolver_free(struct dmn_resolver *r) if (!r) return; - if (r->dispatching) { - r->free_pending = true; - return; - } - destroy(r); + dmn_work_pool_free(r->pool); + free(r); } void dmn_resolver_set_lookup(struct dmn_resolver *r, dmn_getaddrinfo_fn fn) { - pthread_mutex_lock(&r->pool->lock); - r->pool->lookup = fn; - pthread_mutex_unlock(&r->pool->lock); + r->lookup = fn; } static struct dmn_resolve_query *query_new(const char *host, @@ -392,49 +146,25 @@ int dmn_resolve(struct dmn_resolver *r, const char *host, const char *service, struct dmn_resolve_query **ret) { struct dmn_resolve_query *q; - struct pool *p; - int err = 0; + int err; if (!r || (!host && !service) || !cb) return -EINVAL; - p = r->pool; q = query_new(host, service, hints); if (!q) return -ENOMEM; - q->r = r; + q->lookup = r->lookup; q->cb = cb; q->user_data = user_data; - if (timeout_usec) { - err = sd_event_add_time_relative(r->event, &q->timer, - CLOCK_MONOTONIC, timeout_usec, - 0, on_timeout, q); - if (err < 0) { - query_free(q); - return err; - } - } - - pthread_mutex_lock(&p->lock); - q->state = QUERY_QUEUED; - list_add_tail(&p->queued, &q->pool_node); - p->nr_queued++; - if (p->nr_queued > p->nr_idle && p->nr_workers < p->max_workers) - err = start_worker(p); - // Without any worker, the query would never run. - if (err && !p->nr_workers) { - list_del(&q->pool_node); - p->nr_queued--; - pthread_mutex_unlock(&p->lock); - sd_event_source_disable_unref(q->timer); + err = dmn_work_submit(r->pool, lookup, lookup_done, query_free, q, + timeout_usec, &q->work); + if (err < 0) { query_free(q); return err; } - pthread_cond_signal(&p->cond); - pthread_mutex_unlock(&p->lock); - list_add_tail(&r->active, &q->node); if (ret) *ret = q; @@ -443,9 +173,6 @@ int dmn_resolve(struct dmn_resolver *r, const char *host, const char *service, void dmn_resolve_cancel(struct dmn_resolve_query *q) { - if (!q) - return; - - q = detach(q); - query_free(q); + if (q) + dmn_work_cancel(q->work); } diff --git a/daemon-util/tests/meson.build b/daemon-util/tests/meson.build index e45aeccc84..345fa4dc63 100644 --- a/daemon-util/tests/meson.build +++ b/daemon-util/tests/meson.build @@ -15,3 +15,14 @@ test_resolve = executable( ) test('daemon-util - resolve', test_resolve) + +test_work = executable( + 'test-daemon-util-work', + ['test-work.c'], + dependencies: [ + ccan_dep, + daemon_util_dep, + ], +) + +test('daemon-util - work', test_work) diff --git a/daemon-util/tests/test-work.c b/daemon-util/tests/test-work.c new file mode 100644 index 0000000000..64bbce34e0 --- /dev/null +++ b/daemon-util/tests/test-work.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define DELAY_MS 200 +#define USEC_PER_MSEC 1000ULL + +struct job { + unsigned int sleep_ms; + int in; + int out; // written by the work + int done_calls; + bool timed_out; + bool free_pool; // free the pool from the done callback + bool submit_again; // submit another job from the done callback +}; + +static sd_event *event; +static struct dmn_work_pool *pool; +static int pending; +static atomic_int nr_freed; +static atomic_int nr_running; +static atomic_int max_running; + +static bool check(const char *name, bool ok) +{ + printf(" - %s [%s]\n", name, ok ? "PASS" : "FAIL"); + return ok; +} + +static uint64_t now_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static void work(void *data) +{ + struct job *j = data; + int n = atomic_fetch_add(&nr_running, 1) + 1; + int max = atomic_load(&max_running); + + while (n > max && !atomic_compare_exchange_weak(&max_running, &max, n)) + ; + usleep(j->sleep_ms * USEC_PER_MSEC); + j->out = j->in * 2; + atomic_fetch_sub(&nr_running, 1); +} + +// The jobs are owned by the test: count the frees only. +static void job_free(void *data __attribute__((unused))) +{ + atomic_fetch_add(&nr_freed, 1); +} + +static struct job extra; + +static void done(void *data, bool timed_out) +{ + struct job *j = data; + + j->done_calls++; + j->timed_out = timed_out; + pending--; + if (j->free_pool) { + dmn_work_pool_free(pool); + pool = NULL; + } + if (j->submit_again && + !dmn_work_submit(pool, work, done, job_free, &extra, 0, NULL)) + pending++; +} + +static void run(unsigned int limit_ms) +{ + uint64_t end = now_ms() + limit_ms; + + while (pending > 0 && now_ms() < end) + sd_event_run(event, 50 * USEC_PER_MSEC); +} + +static void setup(unsigned int max_workers) +{ + if (dmn_work_pool_new(event, max_workers, &pool)) { + printf("cannot create the pool\n"); + exit(EXIT_FAILURE); + } + pending = 0; + atomic_store(&nr_freed, 0); + atomic_store(&max_running, 0); +} + +static void submit(struct job *j, unsigned int sleep_ms, uint64_t timeout_ms, + struct dmn_work **w) +{ + *j = (struct job){ .sleep_ms = sleep_ms, .in = 21 }; + if (dmn_work_submit(pool, work, done, job_free, j, + timeout_ms * USEC_PER_MSEC, w)) { + printf("dmn_work_submit() failed\n"); + exit(EXIT_FAILURE); + } + pending++; +} + +// Let a worker that still runs a job finish and free it. +static void drain_workers(void) +{ + usleep(2 * DELAY_MS * USEC_PER_MSEC); +} + +static void teardown(void) +{ + dmn_work_pool_free(pool); + pool = NULL; + drain_workers(); +} + +static bool test_result(void) +{ + struct job j; + bool pass; + + printf("test_result:\n"); + setup(2); + submit(&j, 0, 0, NULL); + run(1000); + pass = check("the done callback is called once", + j.done_calls == 1 && !j.timed_out); + pass &= check("it sees the result of the work", j.out == 42); + pass &= check("the job is freed", atomic_load(&nr_freed) == 1); + teardown(); + + return pass; +} + +static bool test_max_workers(void) +{ + struct job j[4]; + bool pass = true; + int i; + + printf("test_max_workers:\n"); + setup(2); + for (i = 0; i < 4; i++) + submit(&j[i], DELAY_MS, 0, NULL); + run(5000); + for (i = 0; i < 4; i++) + pass &= j[i].done_calls == 1 && j[i].out == 42; + pass = check("all jobs end", pass); + pass &= check("at most 2 jobs run at the same time", + atomic_load(&max_running) == 2); + teardown(); + + return pass; +} + +static bool test_timeout(void) +{ + uint64_t t0, elapsed; + struct job j; + bool pass; + + printf("test_timeout:\n"); + setup(1); + // The event loop does not run, so its time of the last wakeup gets old. + sd_event_run(event, 0); + usleep(DELAY_MS * USEC_PER_MSEC); + + t0 = now_ms(); + submit(&j, 2 * DELAY_MS, DELAY_MS / 2, NULL); + run(1000); + elapsed = now_ms() - t0; + pass = check("the done callback reports the timeout", + j.done_calls == 1 && j.timed_out); + pass &= check("not before the timeout", elapsed >= DELAY_MS / 2); + pass &= check("before the work ends", elapsed < 2 * DELAY_MS); + pass &= check("the job is not freed yet", atomic_load(&nr_freed) == 0); + drain_workers(); + pass &= check("the worker frees it when the work ends", + atomic_load(&nr_freed) == 1); + teardown(); + + return pass; +} + +static bool test_cancel(void) +{ + struct dmn_work *wa, *wb; + struct job a, b; + bool pass; + + printf("test_cancel:\n"); + setup(1); + submit(&a, DELAY_MS, 0, &wa); + submit(&b, DELAY_MS, 0, &wb); + usleep(DELAY_MS / 2 * USEC_PER_MSEC); // a runs, b waits + + dmn_work_cancel(wb); + pass = check("a waiting job is freed at once", + atomic_load(&nr_freed) == 1); + dmn_work_cancel(wa); + pass &= check("a running job is not freed yet", + atomic_load(&nr_freed) == 1); + drain_workers(); + sd_event_run(event, 0); + pass &= check("the worker frees it when the work ends", + atomic_load(&nr_freed) == 2); + pass &= check("no done callback is called", + !a.done_calls && !b.done_calls); + teardown(); + + return pass; +} + +static bool test_free_from_callback(void) +{ + struct job a, b; + bool pass; + + printf("test_free_from_callback:\n"); + setup(2); + submit(&a, 0, 0, NULL); + submit(&b, DELAY_MS, 0, NULL); + a.free_pool = true; + run(DELAY_MS / 2); + pass = check("the first done callback frees the pool", + a.done_calls == 1 && !pool); + drain_workers(); + pass &= check("the other job is freed, without callback", + !b.done_calls && atomic_load(&nr_freed) == 2); + + return pass; +} + +static bool test_submit_from_callback(void) +{ + struct job a; + bool pass; + + printf("test_submit_from_callback:\n"); + setup(1); + submit(&a, 0, 0, NULL); + a.submit_again = true; + extra = (struct job){ .in = 5 }; + run(1000); + pass = check("a job submitted from a done callback runs", + extra.done_calls == 1 && extra.out == 10); + teardown(); + + return pass; +} + +int main(void) +{ + bool pass = true; + + if (sd_event_new(&event) < 0) { + printf("cannot create the event loop\n"); + return EXIT_FAILURE; + } + + pass &= test_result(); + pass &= test_max_workers(); + pass &= test_timeout(); + pass &= test_cancel(); + pass &= test_free_from_callback(); + pass &= test_submit_from_callback(); + + sd_event_unref(event); + + fflush(stdout); + return pass ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/daemon-util/work.c b/daemon-util/work.c new file mode 100644 index 0000000000..6b7dcc2f15 --- /dev/null +++ b/daemon-util/work.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "work.h" + +#define USEC_PER_SEC 1000000ULL +#define NSEC_PER_USEC 1000ULL +#define TIMER_ACCURACY_USEC 1000ULL + +enum work_state { + WORK_QUEUED, // waiting for a worker + WORK_RUNNING, // a worker runs it + WORK_DONE, // waiting for the event loop + WORK_DROPPED, // cancelled while running: the worker frees it +}; + +/* + * The state shared with the workers. A worker can outlive the pool while it + * runs a work, so the shared state is freed by its last user. + */ +struct shared { + pthread_mutex_t lock; + pthread_cond_t cond; + struct list_head queued; + struct list_head done; + unsigned int max_workers; + unsigned int nr_workers; + unsigned int nr_idle; + unsigned int nr_queued; + unsigned int refs; // the pool and each worker + bool closing; + int fd; // eventfd: the workers wake up the event loop +}; + +struct dmn_work_pool { + struct shared *s; + sd_event *event; + sd_event_source *io; + struct list_head active; // event loop only + bool dispatching; + bool free_pending; +}; + +struct dmn_work { + struct list_node pool_node; // in s->queued or s->done + struct list_node node; // in pool->active + struct dmn_work_pool *p; + sd_event_source *timer; + enum work_state state; // s->lock + dmn_work_fn fn; + dmn_work_done_fn done; + dmn_work_free_fn free_fn; + void *data; +}; + +static void work_free(struct dmn_work *w) +{ + if (!w) + return; + if (w->free_fn) + w->free_fn(w->data); + free(w); +} + +// Called with s->lock held. Unlocks it. +static void shared_unref_unlock(struct shared *s) +{ + bool last = --s->refs == 0; + + pthread_mutex_unlock(&s->lock); + if (!last) + return; + + pthread_cond_destroy(&s->cond); + pthread_mutex_destroy(&s->lock); + close(s->fd); + free(s); +} + +static void *worker(void *arg) +{ + static const uint64_t one = 1; + struct shared *s = arg; + struct dmn_work *w; + + pthread_mutex_lock(&s->lock); + for (;;) { + while (!s->closing && list_empty(&s->queued)) { + s->nr_idle++; + pthread_cond_wait(&s->cond, &s->lock); + s->nr_idle--; + } + if (s->closing) + break; + + w = list_pop(&s->queued, struct dmn_work, pool_node); + s->nr_queued--; + w->state = WORK_RUNNING; + pthread_mutex_unlock(&s->lock); + + w->fn(w->data); + + pthread_mutex_lock(&s->lock); + if (w->state == WORK_DROPPED) { + pthread_mutex_unlock(&s->lock); + work_free(w); + pthread_mutex_lock(&s->lock); + continue; + } + + w->state = WORK_DONE; + list_add_tail(&s->done, &w->pool_node); + // Cannot fail: the counter is far from overflowing. + (void)!write(s->fd, &one, sizeof(one)); + } + shared_unref_unlock(s); + + return NULL; +} + +// Called with s->lock held. +static int start_worker(struct shared *s) +{ + pthread_attr_t attr; + sigset_t all, old; + pthread_t t; + int ret; + + ret = pthread_attr_init(&attr); + if (ret) + return -ret; + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + // Signals go to the event loop thread, not to a worker. + sigfillset(&all); + pthread_sigmask(SIG_BLOCK, &all, &old); + ret = pthread_create(&t, &attr, worker, s); + pthread_sigmask(SIG_SETMASK, &old, NULL); + pthread_attr_destroy(&attr); + if (ret) + return -ret; + + s->refs++; + s->nr_workers++; + + return 0; +} + +/* + * Take @w away from the workers and from the event loop. Returns the work + * if the caller must free it, NULL if a worker frees it. + */ +static struct dmn_work *detach(struct dmn_work *w) +{ + struct shared *s = w->p->s; + bool running; + + list_del(&w->node); + w->timer = sd_event_source_disable_unref(w->timer); + + pthread_mutex_lock(&s->lock); + running = w->state == WORK_RUNNING; + if (running) + w->state = WORK_DROPPED; + else + list_del(&w->pool_node); + if (w->state == WORK_QUEUED) + s->nr_queued--; + pthread_mutex_unlock(&s->lock); + + return running ? NULL : w; +} + +static void destroy(struct dmn_work_pool *p) +{ + struct dmn_work *w, *next; + struct shared *s = p->s; + + list_for_each_safe(&p->active, w, next, node) + work_free(detach(w)); + sd_event_source_disable_unref(p->io); + sd_event_unref(p->event); + free(p); + + pthread_mutex_lock(&s->lock); + s->closing = true; + pthread_cond_broadcast(&s->cond); + shared_unref_unlock(s); +} + +// Detach @w, call its done callback, then free it. +static void finish(struct dmn_work *w, bool timed_out) +{ + dmn_work_done_fn done = w->done; + void *data = w->data; + + w = detach(w); + done(data, timed_out); + work_free(w); +} + +static int on_done(sd_event_source *src __attribute__((unused)), int fd, + uint32_t revents __attribute__((unused)), void *user_data) +{ + struct dmn_work_pool *p = user_data; + struct dmn_work *w; + uint64_t n; + + (void)!read(fd, &n, sizeof(n)); + + p->dispatching = true; + while (!p->free_pending) { + pthread_mutex_lock(&p->s->lock); + w = list_top(&p->s->done, struct dmn_work, pool_node); + pthread_mutex_unlock(&p->s->lock); + if (!w) + break; + + finish(w, false); + } + p->dispatching = false; + + if (p->free_pending) + destroy(p); + + return 0; +} + +static int on_timeout(sd_event_source *src __attribute__((unused)), + uint64_t usec __attribute__((unused)), void *user_data) +{ + struct dmn_work *w = user_data; + struct shared *s = w->p->s; + bool done; + + // A work that ended is reported by on_done(). + pthread_mutex_lock(&s->lock); + done = w->state == WORK_DONE; + pthread_mutex_unlock(&s->lock); + if (done) + return 0; + + finish(w, true); + + return 0; +} + +/* + * sd_event_now() is the time of the last wakeup of the event loop, which + * can be long ago when the caller ran for a while. Use the current time. + */ +static uint64_t now_usec(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} + +int dmn_work_pool_new(sd_event *event, unsigned int max_workers, + struct dmn_work_pool **ret) +{ + struct dmn_work_pool *p; + struct shared *s; + int err; + + if (!event || !max_workers || !ret) + return -EINVAL; + + p = calloc(1, sizeof(*p)); + s = calloc(1, sizeof(*s)); + if (!p || !s) { + free(p); + free(s); + return -ENOMEM; + } + + s->fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (s->fd < 0) { + err = -errno; + free(p); + free(s); + return err; + } + pthread_mutex_init(&s->lock, NULL); + pthread_cond_init(&s->cond, NULL); + list_head_init(&s->queued); + list_head_init(&s->done); + s->max_workers = max_workers; + s->refs = 1; + + p->s = s; + p->event = sd_event_ref(event); + list_head_init(&p->active); + + err = sd_event_add_io(event, &p->io, s->fd, EPOLLIN, on_done, p); + if (err < 0) { + destroy(p); + return err; + } + + *ret = p; + + return 0; +} + +void dmn_work_pool_free(struct dmn_work_pool *p) +{ + if (!p) + return; + + if (p->dispatching) { + p->free_pending = true; + return; + } + destroy(p); +} + +int dmn_work_submit(struct dmn_work_pool *p, dmn_work_fn fn, + dmn_work_done_fn done, dmn_work_free_fn free_fn, + void *data, uint64_t timeout_usec, struct dmn_work **ret) +{ + struct dmn_work *w; + struct shared *s; + int err = 0; + + if (!p || !fn || !done) + return -EINVAL; + s = p->s; + + w = calloc(1, sizeof(*w)); + if (!w) + return -ENOMEM; + w->p = p; + w->fn = fn; + w->done = done; + w->free_fn = free_fn; + w->data = data; + + if (timeout_usec) { + err = sd_event_add_time(p->event, &w->timer, CLOCK_MONOTONIC, + now_usec() + timeout_usec, + TIMER_ACCURACY_USEC, on_timeout, w); + if (err < 0) { + free(w); + return err; + } + } + + pthread_mutex_lock(&s->lock); + w->state = WORK_QUEUED; + list_add_tail(&s->queued, &w->pool_node); + s->nr_queued++; + if (s->nr_queued > s->nr_idle && s->nr_workers < s->max_workers) + err = start_worker(s); + // Without any worker, the work would never run. + if (err && !s->nr_workers) { + list_del(&w->pool_node); + s->nr_queued--; + pthread_mutex_unlock(&s->lock); + sd_event_source_disable_unref(w->timer); + free(w); + return err; + } + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->lock); + + list_add_tail(&p->active, &w->node); + if (ret) + *ret = w; + + return 0; +} + +void dmn_work_cancel(struct dmn_work *w) +{ + if (!w) + return; + + work_free(detach(w)); +} diff --git a/daemon-util/work.h b/daemon-util/work.h new file mode 100644 index 0000000000..a2c2d9f617 --- /dev/null +++ b/daemon-util/work.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. + * + * Authors: Martin Belanger + */ +#pragma once + +#include +#include + +#include + +/* + * Blocking work for an sd-event loop. The work runs in worker threads. Its + * end is reported from the event loop. + * + * The work must not share state with the event loop while it runs, except + * what the done callback reads after the work ended. For libnvme, this + * means that the work uses a global context of its own. + */ +struct dmn_work_pool; +struct dmn_work; + +// Runs in a worker thread. +typedef void (*dmn_work_fn)(void *data); + +/* + * Runs in the event loop when the work ended, or when it timed out. With + * @timed_out, a worker may still run the work: read only the fields of + * @data that the work does not write, and do not free @data. + */ +typedef void (*dmn_work_done_fn)(void *data, bool timed_out); + +/* + * Frees @data. Runs in the event loop, or in the worker thread when the + * work ends after a timeout or a cancel. + */ +typedef void (*dmn_work_free_fn)(void *data); + +/* + * Create a pool that runs up to @max_workers works at the same time. + * Return: 0 on success, a negative errno otherwise. + */ +int dmn_work_pool_new(sd_event *event, unsigned int max_workers, + struct dmn_work_pool **ret); + +/* + * Cancel all works and free @p. No done callback is called. A worker that + * still runs a work frees it when the work returns. May be called from a + * done callback. + */ +void dmn_work_pool_free(struct dmn_work_pool *p); + +/* + * Run @fn(@data) in a worker thread, then call @done(@data) from the event + * loop and free @data with @free_fn. On success, the pool owns @data, even + * if @fn never runs. A @timeout_usec of 0 means no timeout. *@ret, if @ret + * is not NULL, is valid until @done is called or the work is cancelled. + * Return: 0 on success, a negative errno otherwise. + */ +int dmn_work_submit(struct dmn_work_pool *p, dmn_work_fn fn, + dmn_work_done_fn done, dmn_work_free_fn free_fn, + void *data, uint64_t timeout_usec, struct dmn_work **ret); + +// Cancel @w. Its done callback is not called. +void dmn_work_cancel(struct dmn_work *w); From 0ec54f7d04bc78e316fa90282ff7648ce51b8aa2 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Wed, 14 Oct 2026 15:57:56 -0400 Subject: [PATCH 020/263] discoverd: read discovery logs without blocking Reading a discovery log blocks until the DC answers. A DC that does not answer blocked the event loop until the kernel admin timeout, 60 seconds by default: no events, no D-Bus, no other DC. Read the log in a worker, with a libnvme global context of its own, and process it in the event loop. One read runs at a time per DC. A change event during a read reads the log again when the read ends. Signed-off-by: Martin Belanger --- discoverd/src/ctx.h | 2 + discoverd/src/dlp.c | 108 ++++++++++++++++++++++++++++++++----------- discoverd/src/dlp.h | 48 ++++++++++--------- discoverd/src/main.c | 108 ++++++++++++++++++++++++++++++++----------- 4 files changed, 191 insertions(+), 75 deletions(-) diff --git a/discoverd/src/ctx.h b/discoverd/src/ctx.h index 57b9f81fa8..15ce8871f0 100644 --- a/discoverd/src/ctx.h +++ b/discoverd/src/ctx.h @@ -21,6 +21,7 @@ struct unit_mgr; struct events_ctx; struct mdns_ctx; struct dmn_resolver; +struct dmn_work_pool; /* * Top-level discoverd application context. Created once at startup and @@ -42,5 +43,6 @@ struct discoverd_ctx { struct events_ctx *evts; // udev event monitor struct mdns_ctx *mdns; // mDNS discovery, or NULL struct dmn_resolver *resolver; // config host name lookups + struct dmn_work_pool *dlp_pool; // DLP reads bool force_debug; // --debug forces DEBUG }; diff --git a/discoverd/src/dlp.c b/discoverd/src/dlp.c index a204e12370..087e5158a8 100644 --- a/discoverd/src/dlp.c +++ b/discoverd/src/dlp.c @@ -7,10 +7,12 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -129,39 +131,91 @@ void dlp_process_log(const struct nvmf_discovery_log *log, } } -int dlp_fetch(struct discoverd_ctx *ctx, const char *devname, - const struct libnvmf_tid *dc_tid, - void (*ioc_callback)(const struct libnvmf_tid *t, - void *user_data), - void (*dc_callback)(const struct libnvmf_tid *t, bool epcsd, - void *user_data), - void (*self_callback)(bool epcsd, void *user_data), - void *user_data) +struct dlp_read_job { + char *devname; + int log_level; + dlp_read_callback cb; + void *user_data; + // Written by the worker. + struct nvmf_discovery_log *log; + const char *failed; // the step that failed + int ret; +}; + +static void read_job_free(void *data) +{ + struct dlp_read_job *j = data; + + free(j->log); + free(j->devname); + free(j); +} + +/* + * Runs in a worker. The global context is its own: libnvme objects are not + * shared with the event loop. + */ +static void read_log(void *data) { + struct dlp_read_job *j = data; + struct libnvme_global_ctx *nctx; struct libnvme_ctrl *ctrl = NULL; - struct nvmf_discovery_log *log = NULL; - int ret; - ret = libnvme_scan_ctrl(ctx->nvme_ctx, devname, &ctrl); - if (ret < 0) { - log_warn("%s | %s - scan_ctrl failed: %s", - libnvmf_tid_str(dc_tid), devname, strerror(-ret)); - goto out; + nctx = libnvme_create_global_ctx(); + if (!nctx) { + j->failed = "create_global_ctx"; + j->ret = -ENOMEM; + return; } + libnvme_set_logging_level(nctx, j->log_level, false, false); + + j->ret = libnvme_scan_ctrl(nctx, j->devname, &ctrl); + if (j->ret < 0) + j->failed = "scan_ctrl"; + else + j->ret = libnvmf_get_discovery_log(ctrl, NULL, &j->log); + if (j->ret < 0 && !j->failed) + j->failed = "get_discovery_log"; + + libnvme_free_ctrl(ctrl); + libnvme_free_global_ctx(nctx); +} + +static void read_done(void *data, bool timed_out __attribute__((unused))) +{ + struct dlp_read_job *j = data; - ret = libnvmf_get_discovery_log(ctrl, NULL, &log); - if (ret < 0) { - log_warn("%s | %s - get_discovery_log failed: %s", - libnvmf_tid_str(dc_tid), devname, strerror(-ret)); - goto out; + if (j->ret < 0) + log_dbg("%s - %s failed: %s", j->devname, j->failed, + strerror(-j->ret)); + j->cb(j->ret < 0 ? j->ret : 0, j->log, j->user_data); +} + +int dlp_read(struct dmn_work_pool *pool, const char *devname, int log_level, + dlp_read_callback cb, void *user_data, struct dmn_work **ret) +{ + struct dlp_read_job *j; + int err; + + if (!pool || !devname || !cb) + return -EINVAL; + + j = calloc(1, sizeof(*j)); + if (!j) + return -ENOMEM; + j->devname = strdup(devname); + if (!j->devname) { + free(j); + return -ENOMEM; } + j->log_level = log_level; + j->cb = cb; + j->user_data = user_data; - dlp_process_log(log, dc_tid, ioc_callback, dc_callback, self_callback, - user_data); + err = dmn_work_submit(pool, read_log, read_done, read_job_free, j, 0, + ret); + if (err < 0) + read_job_free(j); - ret = 0; -out: - free(log); - libnvme_free_ctrl(ctrl); // detach from the global-ctx tree; NULL-safe - return ret; + return err; } diff --git a/discoverd/src/dlp.h b/discoverd/src/dlp.h index c64e72c6c4..2b1d863666 100644 --- a/discoverd/src/dlp.h +++ b/discoverd/src/dlp.h @@ -7,13 +7,37 @@ */ #pragma once +#include + #include -#include "ctx.h" #include "tid.h" +struct dmn_work_pool; +struct dmn_work; + +/* + * Called from the event loop when dlp_read() ends. @ret is 0 on success, a + * negative errno otherwise. @log is valid only during the call. + */ +typedef void (*dlp_read_callback)(int ret, + const struct nvmf_discovery_log *log, + void *user_data); + +/* + * Read the Discovery Log Page of the connected DC @devname, e.g. "nvme0". + * The read blocks until the DC answers, so it runs in a worker of @pool, + * with a libnvme global context of its own. @log_level is the libnvme + * logging level of that context. *@ret, if @ret is not NULL, is valid + * until @cb is called or the read is cancelled with dmn_work_cancel(). + * + * Return: 0 on success, a negative errno otherwise. + */ +int dlp_read(struct dmn_work_pool *pool, const char *devname, int log_level, + dlp_read_callback cb, void *user_data, struct dmn_work **ret); + /* - * Fetch the Discovery Log Page from a connected DC and parse all entries. + * Parse all the entries of a Discovery Log Page. * * For each DLPE: * subtype == NVME_NQN_NVME (I/O controller): ioc_callback is called. @@ -30,26 +54,8 @@ * The caller can keep it as a fallback for when that DC is later connected * to and its own self entry turns out to be absent. * - * devname: kernel device name, e.g. "nvme0". - * dc_tid: TID of the DC (used as the cache key in the caller). - * - * Returns 0 on success, negative errno on failure. - */ -int dlp_fetch(struct discoverd_ctx *ctx, const char *devname, - const struct libnvmf_tid *dc_tid, - void (*ioc_callback)(const struct libnvmf_tid *t, - void *user_data), - void (*dc_callback)(const struct libnvmf_tid *t, bool epcsd, - void *user_data), - void (*self_callback)(bool epcsd, void *user_data), - void *user_data); - -/* - * Dispatch an already-fetched Discovery Log Page, the second half of - * dlp_fetch(). Split out so the entry dispatch can be unit tested against a - * synthetic log page, without a device or a fabrics connection. - * * log: a Discovery Log Page; numrec is read from its own header. + * dc_tid: TID of the DC; the entries inherit its host-side fields. */ void dlp_process_log(const struct nvmf_discovery_log *log, const struct libnvmf_tid *dc_tid, diff --git a/discoverd/src/main.c b/discoverd/src/main.c index 2e12e38bed..74479899d9 100644 --- a/discoverd/src/main.c +++ b/discoverd/src/main.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,7 @@ // Name lookups of the fabrics configuration that run at the same time. #define RESOLVE_WORKERS 8 +#define DLP_WORKERS 4 struct active_ctrl { struct list_node entry; @@ -94,6 +96,9 @@ struct active_ctrl { // A DC whose DLP was fetched since nvme-discoverd started. bool dlp_fetched; + + struct dmn_work *dlp_read; // NULL when no DLP read runs + bool dlp_again; // read the DLP again when this read ends }; /* @@ -140,6 +145,7 @@ static void ctrl_free(struct active_ctrl *e) return; sd_event_source_unref(e->retry_timer); sd_event_source_unref(e->epcsd_poll_timer); + dmn_work_cancel(e->dlp_read); tid_free(e->tid); free(e->unit_name); free(e->devname); @@ -612,8 +618,7 @@ static const struct libnvmf_params *params_for( is_dc); } -static void fetch_and_process_dlp(const char *devname, - const struct libnvmf_tid *dc_tid); +static void dlp_fetch(struct active_ctrl *e); static bool devname_matches_tid(const char *devname, const struct libnvmf_tid *tid); @@ -623,8 +628,8 @@ static int adopted_dc_fetch(sd_event_source *src, void *user_data) char *unit_name = user_data; struct active_ctrl *e = ctrl_find_by_unit(unit_name); - if (e && e->devname) - fetch_and_process_dlp(e->devname, e->tid); + if (e) + dlp_fetch(e); free(unit_name); sd_event_source_disable_unref(src); @@ -672,7 +677,7 @@ static void adopt_ctrl(const char *unit_name, const struct libnvmf_tid *tid, log_warn("%s - cannot defer DLP fetch: %s", libnvmf_tid_str(tid), strerror(-r)); free(name); - fetch_and_process_dlp(devname, tid); + dlp_fetch(e); } } } @@ -846,41 +851,31 @@ static bool dc_effective_epcsd(const struct dlp_fetch_ctx *fctx, static void epcsd_park(struct active_ctrl *e); -static void fetch_and_process_dlp(const char *devname, - const struct libnvmf_tid *dc_tid) +// Process the DLP of the DC @e. +static void process_dlp(struct active_ctrl *e, + const struct nvmf_discovery_log *log) { __cleanup_conn_scan struct conn_scan scan = { 0 }; + const struct libnvmf_tid *dc_tid = e->tid; struct dlp_fetch_ctx fctx = { .via_dc = inventory_config_conn_for(ctx.inventory, dc_tid), .hops = inventory_referral_hops(ctx.inventory, dc_tid), }; - struct active_ctrl *e = ctrl_find_by_devname(devname); bool epcsd; - int r; // One snapshot for every entry this log page turns out to hold. conn_scan_load(&scan); fctx.scan = &scan; - r = dlp_fetch(&ctx, devname, dc_tid, dlp_ioc_callback, - dlp_dc_callback, dlp_self_callback, &fctx); - - /* - * A failed fetch tells nothing about EPCSD or the DC's entries, so - * keep the DC and its last list. - */ - if (r < 0) { - tid_list_free_items(&fctx.iocs); - tid_list_free_items(&fctx.referrals); - return; - } + dlp_process_log(log, dc_tid, dlp_ioc_callback, dlp_dc_callback, + dlp_self_callback, &fctx); epcsd = dc_effective_epcsd(&fctx, e); log_dbg("%s: self entry %s, effective EPCSD=%d", libnvmf_tid_str(dc_tid), fctx.self_seen ? "seen" : "absent", epcsd); - if (e && e->is_dc && !epcsd) { + if (e->is_dc && !epcsd) { if (e->force_persistent) log_info_once(&e->force_persistent_logged, "%s - EPCSD=0, but persistent=force: staying connected", @@ -894,8 +889,7 @@ static void fetch_and_process_dlp(const char *devname, tid_list_append(&fctx.referrals, NULL) == 0) { inventory_update_dlp(ctx.inventory, dc_tid, fctx.iocs.items, fctx.referrals.items); - if (e) - e->dlp_fetched = true; + e->dlp_fetched = true; schedule_release(false); } else { tid_list_free_items(&fctx.iocs); @@ -903,6 +897,59 @@ static void fetch_and_process_dlp(const char *devname, } } +static int log_level(void) +{ + return ctx.force_debug ? DMN_LOG_DEBUG : ctx.cfg->debug_level; +} + +static void on_dlp_read(int ret, const struct nvmf_discovery_log *log, + void *user_data) +{ + struct active_ctrl *e = user_data; + + e->dlp_read = NULL; + + /* + * A failed read tells nothing about EPCSD or the DC's entries, so + * keep the DC and its last list. + */ + if (ret < 0) + log_warn("%s | %s - cannot read the discovery log: %s", + libnvmf_tid_str(e->tid), e->devname ? e->devname : "-", + strerror(-ret)); + else + process_dlp(e, log); + + if (e->dlp_again) { + e->dlp_again = false; + dlp_fetch(e); + } +} + +/* + * Read the DLP of the DC @e, then process it. The read waits for the DC, + * so it runs in a worker and the event loop goes on. One read at a time + * per DC: a request during a read reads the DLP again when it ends. + */ +static void dlp_fetch(struct active_ctrl *e) +{ + int r; + + if (!e->devname) + return; + + if (e->dlp_read) { + e->dlp_again = true; + return; + } + + r = dlp_read(ctx.dlp_pool, e->devname, log_level(), on_dlp_read, e, + &e->dlp_read); + if (r < 0) + log_warn("%s | %s - cannot read the discovery log: %s", + libnvmf_tid_str(e->tid), e->devname, strerror(-r)); +} + /* * Arm a dynamically-discovered DC's give-up deadline the first time it is * seen failing to (re)connect. Static/NBFT-sourced DCs and IOCs never get @@ -1132,7 +1179,7 @@ static void dc_connected(struct active_ctrl *e, const char *devname) * address the kernel selected, which the configuration did not ask * for. */ - fetch_and_process_dlp(devname, e->tid); + dlp_fetch(e); } static void on_job_done(const char *unit_name, bool success, @@ -1202,7 +1249,7 @@ static void on_dc_changed(const char *devname, log_dbg("%s | %s: discovery log changed, re-fetching", libnvmf_tid_str(e->tid), devname); - fetch_and_process_dlp(devname, e->tid); + dlp_fetch(e); } static void on_ioc_add(const char *devname, @@ -1603,7 +1650,7 @@ static void on_config_lookup(bool added, static void apply_log_level(void) { - int level = ctx.force_debug ? DMN_LOG_DEBUG : ctx.cfg->debug_level; + int level = log_level(); dmn_log_set_level(level); libnvme_set_logging_level(ctx.nvme_ctx, level, false, false); @@ -1800,6 +1847,12 @@ int main(int argc, char **argv) return 1; } + r = dmn_work_pool_new(ctx.event, DLP_WORKERS, &ctx.dlp_pool); + if (r < 0) { + log_err("failed to create the DLP workers: %s", strerror(-r)); + return 1; + } + ctx.inventory = inventory_new(); if (!ctx.inventory) return 1; @@ -1870,6 +1923,7 @@ int main(int argc, char **argv) free(config_path_abs); inventory_free(ctx.inventory); dmn_resolver_free(ctx.resolver); + dmn_work_pool_free(ctx.dlp_pool); free(ctx.hostnqn); free(ctx.hostid); config_free(ctx.cfg); From bffe43b9a413a3518f6fb57f2ad4a31ee8563a49 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:13:32 +0000 Subject: [PATCH 021/263] nvme: make --human-readable increment the verbose level The hidden global --human-readable/-H is kept for backwards compatibility as a spelling of --verbose, but it was an OPT_FLAG which stores a bool into the unsigned int nvme_args.verbose. That only writes the first byte of the counter: on big-endian machines -H sets verbose to 0x01000000, and on little-endian machines it resets an already counted -vv back to 1. Declare it as OPT_INCR like --verbose and make the counter an int, which is the type CFG_INCREMENT stores. Reject verbose values from nvme-cli.conf which do not fit. Signed-off-by: Daniel Wagner --- src/args.h | 5 +++-- src/global-config.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/args.h b/src/args.h index 544246af43..d4eec52f93 100644 --- a/src/args.h +++ b/src/args.h @@ -17,7 +17,7 @@ typedef uint32_t nvme_print_flags_t; struct nvme_args { char *output_format; - unsigned int verbose; + int verbose; bool quiet; uint32_t timeout; bool dry_run; @@ -71,7 +71,8 @@ enum nvme_print_flags { "disable 64-bit IOCTL support probing"), \ OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, \ "output format version: 1|2"), \ - OPT_FLAG("human-readable", 'H', &nvme_args.verbose, NULL, NULL, true), \ + OPT_INCR("human-readable", 'H', &nvme_args.verbose, \ + NULL, NULL, true), \ OPT_STRING("set-options", 0, "KEY=VALUE", \ &nvme_args.set_options, \ "set a libnvme library option (key=value[,key=value,...]);"), \ diff --git a/src/global-config.c b/src/global-config.c index 4cc08bda24..8945f9db81 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -55,7 +56,7 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, if (!strcmp(key, "verbose")) { err = parse_uint(value, &uval); - if (err) + if (err || uval > INT_MAX) goto bad_value; nvme_args.verbose = uval; } else if (!strcmp(key, "quiet")) { From 73c3918204965f6064db623d4b0f437795cabbf6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:15:12 +0000 Subject: [PATCH 022/263] argconfig: store OPT_LONG values as 64-bit integers All OPT_LONG options store into __u64 or uint64_t variables (offsets, sizes, a timestamp, a component id), but CFG_LONG writes an unsigned long. That is only 32 bits wide on Windows and 32-bit targets, so the upper half of the variable is never written and values above 4 GiB are truncated. Parse with strtoull() and store a uint64_t. Signed-off-by: Daniel Wagner --- src/argconfig.c | 4 ++-- src/argconfig.h | 2 +- tests/unit/c/test-argconfig-parse.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 5c651e4e34..6b59c67c76 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -174,7 +174,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s) *(int *)value += 1; break; case CFG_LONG: - *(unsigned long *)value = strtoul(optarg, &endptr, 0); + *(uint64_t *)value = strtoull(optarg, &endptr, 0); if (errno || optarg == endptr) ret = argconfig_error("long integer", s->option, optarg); break; @@ -214,7 +214,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *(int *)val = opt_val->int_val; break; case CFG_LONG: - *(unsigned long *)val = opt_val->long_val; + *(uint64_t *)val = opt_val->long_val; break; case CFG_DOUBLE: *(double *)val = opt_val->double_val; diff --git a/src/argconfig.h b/src/argconfig.h index 11ab23ff4d..182998efba 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -156,7 +156,7 @@ union argconfig_val { uint16_t short_val; uint32_t positive; int increment; - unsigned long long_val; + uint64_t long_val; uint64_t long_suffix; double double_val; bool flag; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index bb909acd64..7d7de7a25a 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -107,6 +107,8 @@ static struct toval_test toval_tests[] = { VAL_TEST("--uint=1", uint, 1, true, 0), VAL_TEST("--int=1", int_val, 1, true, 0), VAL_TEST("--long=1", long_val, 1, true, 0), + VAL_TEST("--long=0x123456789abcdef0", long_val, 0x123456789abcdef0, + true, 0), VAL_TEST("--double=1", double_val, 1, true, 0), VAL_TEST("--byte=1", byte, 1, true, 0), VAL_TEST("--byte=256", byte, 0, true, -EINVAL), From 06fdc4eeac887b467627d77fe69766902f429249 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 023/263] nvme: parse the set-property value as a 64-bit number The value of set-property is a __u64, since properties can be 8 bytes wide, but it was parsed with OPT_UINT which only stores 32 bits. The variable is initialized to -1 to detect a missing value, so the upper half stayed all ones and an 8-byte property was set to 0xffffffff00000000 | value. Use OPT_SUFFIX like set-reg does for its 64-bit value. Signed-off-by: Daniel Wagner --- src/nvme-cmds-registers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index b0b597ca66..b6e66129e0 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -822,7 +822,7 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug NVME_ARGS(opts, OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_UINT("value", 'V', &cfg.value, value)); + OPT_SUFFIX("value", 'V', &cfg.value, value)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From 363431928130b7e770103d8b753949a0678aab1c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 024/263] fabrics: store the legacy --tls_key option in an int OPT_INT stores an int, but the tls_key_id variable is a long, so only the first four bytes are written. On big-endian machines the value lands in the upper half of the long and the key id is lost. Making the variable an int does not lose any key ids: - The kernel key serial is a key_serial_t, which is an int32_t, and serials are always positive. - OPT_INT already parsed the value into an int, so nothing wider was ever accepted. - libnvmf_context_set_tls_key_id() takes a long, so the int is widened on the call, and libnvme narrows it back to an int when it builds the connect string (__add_hex_argument()), skipping negative values. On little-endian machines the upper half of the zero-initialized long stayed zero, so the same 32 bits reached the kernel before and after this change. A TLS connection to an nvmet target showed byte-identical connect strings with and without this change for decimal, hex, negative and out-of-range --tls_key values, and the key given by its serial was the one used for the handshake. Signed-off-by: Daniel Wagner --- src/fabrics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fabrics.h b/src/fabrics.h index 763c44a564..cab015f873 100644 --- a/src/fabrics.h +++ b/src/fabrics.h @@ -28,7 +28,7 @@ struct nvmf_args { int nr_poll_queues; int tos; long keyring_id; - long tls_key_id; + int tls_key_id; long tls_configured_key_id; bool duplicate_connect; bool disable_sqflow; From 6ce750f0425cc1db1fd0464fd769e0e187196397 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 025/263] plugins/ocp: match the latency monitor option types to the fields set-latency-monitor-feature parses every option with OPT_UINT, which stores 32 bits, but the fields are __u8 and __u16. Each option overwrites its neighbours, depending on the order on the command line, and latency_monitor_feature_enable, the last field, writes 3 bytes past the end of the struct. Use OPT_BYTE and OPT_SHRT, which also reject values that do not fit. The interval of set-plp-health-check-interval is a 16-bit host order value which was stored with OPT_BYTE into an __le16; make it an __u16 parsed with OPT_SHRT. Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-nvme.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 4ced2d032d..3e8257fc2d 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -338,16 +338,31 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("active_bucket_timer_threshold", 't', &cfg.active_bucket_timer_threshold, active_bucket_timer_threshold), - OPT_UINT("active_threshold_a", 'a', &cfg.active_threshold_a, active_threshold_a), - OPT_UINT("active_threshold_b", 'b', &cfg.active_threshold_b, active_threshold_b), - OPT_UINT("active_threshold_c", 'c', &cfg.active_threshold_c, active_threshold_c), - OPT_UINT("active_threshold_d", 'd', &cfg.active_threshold_d, active_threshold_d), - OPT_UINT("active_latency_config", 'f', &cfg.active_latency_config, active_latency_config), - OPT_UINT("active_latency_minimum_window", 'w', &cfg.active_latency_minimum_window, active_latency_minimum_window), - OPT_UINT("debug_log_trigger_enable", 'r', &cfg.debug_log_trigger_enable, debug_log_trigger_enable), - OPT_UINT("discard_debug_log", 'l', &cfg.discard_debug_log, discard_debug_log), - OPT_UINT("latency_monitor_feature_enable", 'e', &cfg.latency_monitor_feature_enable, latency_monitor_feature_enable)); + OPT_SHRT("active_bucket_timer_threshold", 't', + &cfg.active_bucket_timer_threshold, + active_bucket_timer_threshold), + OPT_BYTE("active_threshold_a", 'a', &cfg.active_threshold_a, + active_threshold_a), + OPT_BYTE("active_threshold_b", 'b', &cfg.active_threshold_b, + active_threshold_b), + OPT_BYTE("active_threshold_c", 'c', &cfg.active_threshold_c, + active_threshold_c), + OPT_BYTE("active_threshold_d", 'd', &cfg.active_threshold_d, + active_threshold_d), + OPT_SHRT("active_latency_config", 'f', + &cfg.active_latency_config, + active_latency_config), + OPT_BYTE("active_latency_minimum_window", 'w', + &cfg.active_latency_minimum_window, + active_latency_minimum_window), + OPT_SHRT("debug_log_trigger_enable", 'r', + &cfg.debug_log_trigger_enable, + debug_log_trigger_enable), + OPT_BYTE("discard_debug_log", 'l', &cfg.discard_debug_log, + discard_debug_log), + OPT_BYTE("latency_monitor_feature_enable", 'e', + &cfg.latency_monitor_feature_enable, + latency_monitor_feature_enable)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1731,7 +1746,7 @@ static int set_plp_health_check_interval(int argc, char **argv, struct command * __u8 uidx = 0; struct config { - __le16 plp_health_interval; + __u16 plp_health_interval; bool sv; }; @@ -1741,7 +1756,8 @@ static int set_plp_health_check_interval(int argc, char **argv, struct command * }; NVME_ARGS(opts, - OPT_BYTE("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), + OPT_SHRT("plp_health_interval", 'p', &cfg.plp_health_interval, + plp_health_interval), OPT_FLAG("save", 's', &cfg.sv, sv), OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); From f44ffbc9c8ed13b557e2e41cd5a5af6149a2dfde Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 026/263] plugins/wdc: match the option types to the fields The latency monitor command has the same OPT_UINT into __u8/__u16 overflow as the OCP version, writing past the end of its config struct. The vs-smart-add-log interval is a uint8_t parsed with OPT_UINT and the drive resize size is a uint64_t of which only 32 bits were written. Use OPT_BYTE, OPT_SHRT and OPT_LONG matching the field widths. Signed-off-by: Daniel Wagner --- plugins/wdc/wdc-nvme.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 81dfed3500..f5938b3b6a 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -8455,7 +8455,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("interval", 'i', &cfg.interval, interval), + OPT_BYTE("interval", 'i', &cfg.interval, interval), OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); @@ -10647,7 +10647,7 @@ static int wdc_drive_resize(int argc, char **argv, }; NVME_ARGS(opts, - OPT_UINT("size", 's', &cfg.size, size)); + OPT_LONG("size", 's', &cfg.size, size)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12820,27 +12820,27 @@ int wdc_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("active_bucket_timer_threshold", 't', + OPT_SHRT("active_bucket_timer_threshold", 't', &cfg.active_bucket_timer_threshold, active_bucket_timer_threshold), - OPT_UINT("active_threshold_a", 'a', &cfg.active_threshold_a, + OPT_BYTE("active_threshold_a", 'a', &cfg.active_threshold_a, active_threshold_a), - OPT_UINT("active_threshold_b", 'b', &cfg.active_threshold_b, + OPT_BYTE("active_threshold_b", 'b', &cfg.active_threshold_b, active_threshold_b), - OPT_UINT("active_threshold_c", 'c', &cfg.active_threshold_c, + OPT_BYTE("active_threshold_c", 'c', &cfg.active_threshold_c, active_threshold_c), - OPT_UINT("active_threshold_d", 'd', &cfg.active_threshold_d, + OPT_BYTE("active_threshold_d", 'd', &cfg.active_threshold_d, active_threshold_d), - OPT_UINT("active_latency_config", 'f', + OPT_SHRT("active_latency_config", 'f', &cfg.active_latency_config, active_latency_config), - OPT_UINT("active_latency_minimum_window", 'w', + OPT_BYTE("active_latency_minimum_window", 'w', &cfg.active_latency_minimum_window, active_latency_minimum_window), - OPT_UINT("debug_log_trigger_enable", 'r', + OPT_SHRT("debug_log_trigger_enable", 'r', &cfg.debug_log_trigger_enable, debug_log_trigger_enable), - OPT_UINT("discard_debug_log", 'l', &cfg.discard_debug_log, + OPT_BYTE("discard_debug_log", 'l', &cfg.discard_debug_log, discard_debug_log), - OPT_UINT("latency_monitor_feature_enable", 'e', + OPT_BYTE("latency_monitor_feature_enable", 'e', &cfg.latency_monitor_feature_enable, latency_monitor_feature_enable)); From 5efe1e6fac4edbb048db9a7ff95f56dfb9f61127 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 027/263] plugins/sandisk: match the option types to the fields The vs-smart-add-log interval is a uint8_t parsed with OPT_UINT, which writes 32 bits, and the drive resize size is a uint64_t of which only 32 bits were written. Use OPT_BYTE and OPT_LONG. Signed-off-by: Daniel Wagner --- plugins/sandisk/sandisk-nvme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index 7691318dcf..de5d5f353a 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -620,7 +620,7 @@ static int sndk_vs_smart_add_log(int argc, char **argv, }; NVME_ARGS(opts, - OPT_UINT("interval", 'i', &cfg.interval, interval), + OPT_BYTE("interval", 'i', &cfg.interval, interval), OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); @@ -789,7 +789,7 @@ static int sndk_drive_resize(int argc, char **argv, }; NVME_ARGS(opts, - OPT_UINT("size", 's', &cfg.size, size)); + OPT_LONG("size", 's', &cfg.size, size)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) From aa3e84f6ee8b15eda89c5547b8518dd46d95785c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 028/263] plugins/fdp: parse the endurance group id as 16-bit value The egid variables are __u16 but parsed with OPT_UINT, which writes 32 bits and overwrites the next field. Use OPT_SHRT. Signed-off-by: Daniel Wagner --- plugins/fdp/fdp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index 0777216efb..586147598b 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -49,7 +49,7 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); @@ -122,7 +122,7 @@ static int fdp_usage(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -186,7 +186,7 @@ static int fdp_stats(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -246,7 +246,7 @@ static int fdp_events(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), OPT_FLAG("host-events", 'E', &cfg.host_events, host_events), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); From 829439ba2e7fa7531017d658eff050fad40ecff9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:56 +0000 Subject: [PATCH 029/263] plugins/id: parse the 16-bit options with OPT_SHRT lba-format-index and cntlid are stored in __u16 variables, but OPT_UINT writes 32 bits and overwrites the next field. Signed-off-by: Daniel Wagner --- plugins/id/id-plugin.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index d704a1b185..d48ebd8124 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -186,7 +186,8 @@ static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct }; NVME_ARGS(opts, - OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index), + OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, + lba_format_index), OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -425,7 +426,8 @@ static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, str }; NVME_ARGS(opts, - OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index), + OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, + lba_format_index), OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -976,7 +978,7 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct }; NVME_ARGS(opts, - OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid)); + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From cd0d42fb2428c69e360043d585f9c0a67098c069 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:57 +0000 Subject: [PATCH 030/263] plugins/log: match the option types to the fields domain-id and endg-id are __u16 variables parsed with OPT_UINT, which writes 32 bits and overwrites the next field. log-entries is an __u64 of which OPT_UINT writes only the first half, the upper half on big-endian machines; the count is limited by 32-bit and 16-bit identify fields, so make it an __u32. Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 6ede4c8dee..9859daa8e7 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -1066,7 +1066,7 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, int err; struct config { - __u64 log_entries; + __u32 log_entries; bool rae; bool raw_binary; }; @@ -1263,7 +1263,7 @@ static int get_endurance_event_agg_log(int argc, char **argv, int err; struct config { - __u64 log_entries; + __u32 log_entries; bool rae; bool raw_binary; }; @@ -1703,7 +1703,7 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("domain-id", 'd', &cfg.domainid, domainid), + OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1829,7 +1829,7 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("domain-id", 'd', &cfg.domainid, domainid), + OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -2119,7 +2119,7 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * }; NVME_ARGS(opts, - OPT_UINT("endg-id", 'e', &cfg.endgid, endgid)); + OPT_SHRT("endg-id", 'e', &cfg.endgid, endgid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From a1837c9b44723ed7f9bea08aad86c49fbe3ae134 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:57 +0000 Subject: [PATCH 031/263] plugins/resv: store the reservation flags in bools OPT_FLAG stores a bool; declare iekey and eds as bool instead of __u8 like the other reservation commands do. Signed-off-by: Daniel Wagner --- plugins/resv/resv-plugin.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/resv/resv-plugin.c b/plugins/resv/resv-plugin.c index 763984a4dc..f4454db679 100644 --- a/plugins/resv/resv-plugin.c +++ b/plugins/resv/resv-plugin.c @@ -211,7 +211,7 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug __u64 crkey; __u8 rtype; __u8 rrela; - __u8 iekey; + bool iekey; }; struct config cfg = { @@ -219,7 +219,7 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug .crkey = 0, .rtype = 0, .rrela = 0, - .iekey = 0, + .iekey = false, }; NVME_ARGS(opts, @@ -308,7 +308,7 @@ static int resv_report(int argc, char **argv, struct command *acmd, struct plugi struct config { __u32 nsid; __u32 numd; - __u8 eds; + bool eds; bool raw_binary; }; From 6c9e4d9fc01cbbba8f58baf97805d30b80d9718f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:16:57 +0000 Subject: [PATCH 032/263] plugins/ibm: store the raw-binary flag in a bool OPT_FLAG stores a bool, but the raw_binary variable of the vpd command is an int, so only its first byte is written. It works because the variable is zero-initialized, but the types should match. Signed-off-by: Daniel Wagner --- plugins/ibm/ibm-nvme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 5320e6ad2e..3fdfed8dd4 100644 --- a/plugins/ibm/ibm-nvme.c +++ b/plugins/ibm/ibm-nvme.c @@ -364,7 +364,7 @@ static int get_ibm_vpd_log(int argc, char **argv, struct command *cmd, struct pl const char *raw = "dump output in binary format"; struct config { - int raw_binary; + bool raw_binary; }; struct config cfg = { From 67c161922bd79ea31509e3f0908523b203b59596 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:18:53 +0000 Subject: [PATCH 033/263] argconfig: don't end the option table for OPT_FLAG_JSON Without json-c OPT_FLAG_JSON expands to OPT_END(). The --json flag is the last command option of its users and NVME_ARGS appends the global options after the command options, so the table ends early and all global options are rejected: $ nvme intel smart-log-add --timeout=1 /dev/nvme0 smart-log-add: unrecognized option '--timeout=1' Expand to a group separator instead, which is a no-op in the parser and invisible in the help output when no option follows it. Signed-off-by: Daniel Wagner --- src/argconfig.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/argconfig.h b/src/argconfig.h index 182998efba..59abfcbdbc 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -109,7 +109,12 @@ enum argconfig_types { #ifdef CONFIG_JSONC #define OPT_FLAG_JSON(l, s, v, d, ...) OPT_FLAG(l, s, v, d, __VA_ARGS__) #else /* CONFIG_JSONC */ -#define OPT_FLAG_JSON(l, s, v, d, ...) OPT_END() +/* + * A placeholder which does not end the table, the global options are + * appended after the command options. A separator is invisible in the + * help output unless options follow it. + */ +#define OPT_FLAG_JSON(...) OPT_GROUP("Options") #endif /* CONFIG_JSONC */ #define OPT_VALS(n) \ From 0e89d2a04ffceb25dfcac28e17dabaa59c1d0a35 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:20:51 +0000 Subject: [PATCH 034/263] plugins/feat: drop the unreachable short options of lba-range-type argconfig always handles -h as help, so -h for --hlbars never worked. FEAT_ARGS adds -s for --save, but --slbas also uses -s and comes first in the table, so -s always meant --slbas while the help, the man page and the zsh completion advertised it for --save as well. Make --hlbars and --save long-only for this command, keeping -s for --slbas as it behaved so far. Signed-off-by: Daniel Wagner --- Documentation/nvme-feat-lba-range-type.txt | 6 ++---- plugins/feat/feat-nvme.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Documentation/nvme-feat-lba-range-type.txt b/Documentation/nvme-feat-lba-range-type.txt index 186661f185..a7770b7102 100644 --- a/Documentation/nvme-feat-lba-range-type.txt +++ b/Documentation/nvme-feat-lba-range-type.txt @@ -12,11 +12,11 @@ SYNOPSIS [--nsid= | -n ] [--num= | -N ] [--types= | -t ] [--lbaros= | -l ] - [--hlbars= | -h ] + [--hlbars=] [--slbas= | -s ] [--nlbs= | -b ] [--guids= | -g ] - [--sel=] [--save | -s] + [--sel=] [--save] DESCRIPTION ----------- @@ -49,7 +49,6 @@ OPTIONS --lbaros=:: LBA range overwriteable list. --h :: --hlbars=:: hide LBA range list. @@ -70,7 +69,6 @@ OPTIONS Select (SEL): This field specifies which value of the attributes to return in the provided data: --s:: --save:: Save the attribute so that it persists through all power states and resets. diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index ba518ff461..d7dda3f734 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -37,10 +37,13 @@ #define ASYNC_EVENT_CONF_DESC "Get and set async event configuration feature" #define KEEP_ALIVE_TIMER_DESC "Get and set keep alive timer feature" -#define FEAT_ARGS(n, ...) \ - NVME_ARGS(n, ##__VA_ARGS__, OPT_FLAG("save", 's', NULL, save), \ +#define FEAT_ARGS_SAVE(n, save_short, ...) \ + NVME_ARGS(n, ##__VA_ARGS__, \ + OPT_FLAG("save", save_short, NULL, save), \ OPT_BYTE("sel", 'S', &cfg.sel, sel)) +#define FEAT_ARGS(n, ...) FEAT_ARGS_SAVE(n, 's', ##__VA_ARGS__) + #define STR(x) #x #define TMT(n) "thermal management temperature " STR(n) @@ -1223,12 +1226,13 @@ static int feat_lba_range_type(int argc, char **argv, struct command *acmd, struct lba_range_type_config cfg = { 0 }; int err; - FEAT_ARGS(opts, + /* -s is --slbas and -h is help, --save and --hlbars are long only */ + FEAT_ARGS_SAVE(opts, 0, OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired), OPT_BYTE("num", 'N', &cfg.num, num_desc), OPT_LIST("types", 't', &cfg.types, types_desc), OPT_LIST("lbaros", 'l', &cfg.lbaros, lbaros_desc), - OPT_LIST("hlbars", 'h', &cfg.hlbars, hlbars_desc), + OPT_LIST("hlbars", 0, &cfg.hlbars, hlbars_desc), OPT_LIST("slbas", 's', &cfg.slbas, slbas_desc), OPT_LIST("nlbs", 'b', &cfg.nlbs, nlbs_desc), OPT_LIST("guids", 'g', &cfg.guids, guids_desc)); From c25e0d3fb4004a58cee95bb9b5db6bc109161255 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:21:55 +0000 Subject: [PATCH 035/263] plugins/samsung: store the vs-internal-log flags in bools OPT_FLAG stores a bool, but compress and hide_progress are ints, so only their first byte is written. It works because the variables are zero-initialized, but on big-endian machines the value is 0x01000000 instead of 1 and the types should match. Signed-off-by: Daniel Wagner --- plugins/samsung/samsung-nvme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/samsung/samsung-nvme.c b/plugins/samsung/samsung-nvme.c index 279927b505..9d9418f494 100644 --- a/plugins/samsung/samsung-nvme.c +++ b/plugins/samsung/samsung-nvme.c @@ -1323,16 +1323,16 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, char *type; int area; char *file; - int compress; - int hide_progress; + bool compress; + bool hide_progress; }; struct config cfg = { .type = NULL, .area = 0, .file = NULL, - .compress = 0, - .hide_progress = 0, + .compress = false, + .hide_progress = false, }; NVME_ARGS_OUTPUT_FORMATS(opts, NORMAL, "Output format: normal", From 31e3024b4a673ad46bebf1aa39d4a6e6538d1d63 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:23:12 +0000 Subject: [PATCH 036/263] doc: document the --quiet and --set-options global options global-options.txt is included by every command man page but lacks two of the global options every command accepts. Signed-off-by: Daniel Wagner --- Documentation/global-options.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/global-options.txt b/Documentation/global-options.txt index 632a02491e..20584042d3 100644 --- a/Documentation/global-options.txt +++ b/Documentation/global-options.txt @@ -24,6 +24,15 @@ and are available to this subcommand: field naming, while version '2' (default) provides more consistent and script-friendly field names. +--quiet:: + Suppress informational messages. Errors are still reported, and + --quiet cancels any --verbose for the library log level. + +--set-options=[,...]:: + Set libnvme library options for this invocation. Supported keys are + 'force-4k', 'mi-probe-enabled', 'test-base-dir' and + 'test-sysfs-dir'. + --timeout=:: Set the timeout for the command in milliseconds. From e9d9687ea0a0b98def4e62bc5c5a2edb24556621 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 14:21:23 +0000 Subject: [PATCH 037/263] completions: update the shell completions Regenerate the completions for the options dropped from feat lba-range-type. Signed-off-by: Daniel Wagner --- completions/_nvme | 2 -- completions/bash-nvme-completion.sh | 4 ++-- completions/nvme-completion.ps1 | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 55050f041e..5f2026e0a0 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -1505,7 +1505,6 @@ _nvme () { --guids=':unique identifier list' -g':alias for --guids' --hlbars=':hide LBA range list' - -h':alias for --hlbars' --lbaros=':LBA range overwriteable list' -l':alias for --lbaros' --nlbs=':zeroes based logical blocks number list' @@ -1521,7 +1520,6 @@ _nvme () { --output-format-version=':output format version: 1|2' --quiet':suppress informational output' --save':Specifies that the controller shall save the attribute' - -s':alias for --save' --sel=':[0-3]: current/default/saved/supported' -S':alias for --sel' --set-options=':set a libnvme library option (key=value[,key=value,...]);' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index c1dc18105c..2c158e5d3f 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -1536,8 +1536,8 @@ plugin_feat_opts () { ;; "lba-range-type") - opts+=" --dry-run --guids= -g --hlbars= -h --lbaros= -l --nlbs= -b --no-ioctl-probing --no-retries --nsid= -n --num= -N --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --slbas= -s --timeout= --types= -t --verbose -v" - valopts+=" --guids -g --hlbars -h --lbaros -l --nlbs -b --nsid -n --num -N --output-format -o --output-format-version --sel -S --set-options --slbas -s --timeout --types -t" + opts+=" --dry-run --guids= -g --hlbars= --lbaros= -l --nlbs= -b --no-ioctl-probing --no-retries --nsid= -n --num= -N --output-format= -o --output-format-version= --quiet --save --sel= -S --set-options= --slbas= -s --timeout= --types= -t --verbose -v" + valopts+=" --guids -g --hlbars --lbaros -l --nlbs -b --nsid -n --num -N --output-format -o --output-format-version --sel -S --set-options --slbas -s --timeout --types -t" _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ "--output-format-version" "1 2" \ diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 071454c94d..7379d91742 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -301,7 +301,7 @@ $script:NvmeOptions = @{ 'feat int-coalesce' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--thr', '-t', '--time', '-T', '--timeout', '--verbose', '-v', '--help', '-h') 'feat int-vector-config' = @('--cd', '-c', '--dry-run', '--iv', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat keep-alive-timer' = @('--dry-run', '--kato', '-k', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'feat lba-range-type' = @('--dry-run', '--guids', '-g', '--hlbars', '-h', '--lbaros', '-l', '--nlbs', '-b', '--no-ioctl-probing', '--no-retries', '--nsid', '-n', '--num', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--slbas', '-s', '--timeout', '--types', '-t', '--verbose', '-v', '--help', '-h') + 'feat lba-range-type' = @('--dry-run', '--guids', '-g', '--hlbars', '--lbaros', '-l', '--nlbs', '-b', '--no-ioctl-probing', '--no-retries', '--nsid', '-n', '--num', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '--sel', '-S', '--set-options', '--slbas', '-s', '--timeout', '--types', '-t', '--verbose', '-v', '--help', '-h') 'feat num-queues' = @('--dry-run', '--ncqr', '-c', '--no-ioctl-probing', '--no-retries', '--nsqr', '-n', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat perf-characteristics' = @('--attri', '-a', '--attrl', '-A', '--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--paid', '-p', '--quiet', '--r4karl', '-R', '--rvspa', '-r', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--vs-data', '-V', '--help', '-h') 'feat power-limit' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--pls', '-l', '--plv', '-p', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--uuid-index', '-u', '--verbose', '-v', '--help', '-h') From 6a53dbe59206b972b94c0ca09ae93db0ccb0ab1f Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 07:13:29 -0400 Subject: [PATCH 038/263] keysd: describe KMIP in the README The README still said that keys come from systemd credentials only. Signed-off-by: Martin Belanger --- keysd/README.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/keysd/README.md b/keysd/README.md index dec0f24134..ea5e880b60 100644 --- a/keysd/README.md +++ b/keysd/README.md @@ -2,30 +2,44 @@ A daemon that puts NVMe/TCP TLS pre-shared keys (PSKs) into the kernel's `.nvme` keyring before a connection needs them. The kernel finds the key there when `nvme connect` or `nvme-discoverd` connects with TLS. -> Technology preview. The `nvme-keysd` meson option defaults to `disabled`. Keys come from systemd credentials only (`tls-key-source = systemd-creds`). +> Technology preview. The `nvme-keysd` meson option defaults to `disabled`. Keys come from systemd credentials (`tls-key-source = systemd-creds`) or from a KMIP key management server (`tls-key-source = kmip:`). The `kmip` meson option (default `auto`) builds the `nvme-keysd-kmip` helper. ## Design -- **Runs once.** The unit is `Type=oneshot`. The daemon imports the keys and exits. +- **Runs once, unless KMIP is used.** The unit is `Type=notify`. The daemon imports the keys from systemd credentials, reports ready and exits. With KMIP keys, it keeps running and gets them in the background, with retries. - **Least privilege.** The daemon runs as uid 0, because the kernel checks access to the `.nvme` keyring by uid. It needs no capabilities and no network. The systemd unit drops both. +- **KMIP in a separate process.** `nvme-keysd-kmip` does all communication with the KMS, over TLS. It runs with a dynamic user and no privileges. systemd starts one instance per connection to its Varlink socket. The daemon itself never parses KMS replies. - **No key material in logs or core dumps.** The daemon disables core dumps at startup (`PR_SET_DUMPABLE`). ## Source layout +nvme-keysd: + | File | Role | |---|---| | `main.c` | Startup, `--should-start`, the import run | -| `config.c` | The daemon's own settings (`nvme-keysd.conf`) | -| `import.c` | Imports the credentials named in the fabrics configuration and puts the PSKs in the keyring | +| `config.c` | The daemon's own settings (`nvme-keysd.conf`), including the `[kmip:]` sections | +| `import.c` | Imports the keys named in the fabrics configuration and puts the PSKs in the keyring | | `creds.c` | Decrypts one credential through systemd-creds (`io.systemd.Credentials.Decrypt`) | +| `kmip-client.c` | Gets the KMIP keys through `nvme-keysd-kmip`, on the event loop | + +nvme-keysd-kmip: + +| File | Role | +|---|---| +| `kmip-main.c` | The Varlink service: one request, one key | +| `kmip.c` | KMIP 1.4 requests and responses (Locate by Name, Get) | +| `ttlv.c` | KMIP TTLV encoding | +| `kmip-tls.c` | KMIP over TLS | +| `kmip-psk.c` | Converts a key object to a PSK in the interchange format | -Logging comes from `daemon-util/`, shared with the other nvme-cli daemons. +`config.c` is built into both programs. `kmip-varlink.h` describes the Varlink interface between them. Logging comes from `daemon-util/`, shared with the other nvme-cli daemons. ## Configuration -- **`nvme-keysd.conf`**: the daemon's own settings (`debug-level`). Optional; a missing file or key keeps its default. +- **`nvme-keysd.conf`**: the daemon's own settings (`debug-level`) and the KMSes (`[kmip:]`). Optional; a missing file or key keeps its default. - **The shared NVMe-oF fabrics configuration** (`nvme-fabrics.conf(5)`): which key belongs to which host and subsystem. -Both are read at each start. `systemctl restart nvme-keysd` imports the keys again, and picks up new or changed credentials. +Both are read at each start. `systemctl restart nvme-keysd` imports the keys again, and picks up new or changed credentials and KMIP keys. The unit runs `nvme-keysd --should-start` as `ExecCondition=`. When no entry of the fabrics configuration has a key source, the unit is skipped: the daemon does not start, and `nvme_keyring` is not loaded. From 1c024db27328cbc1813b19999e76cccee41c708f Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 08:58:38 -0400 Subject: [PATCH 039/263] nvme-print: limit the FDP events log to 63 entries The number of events in the FDP events log is reported by the device and can exceed the 63 entries of the data structure. nvme-cli read past the end of the log. Print at most 63 entries. Signed-off-by: Martin Belanger --- src/nvme-print-json.c | 2 +- src/nvme-print-stdout-log.c | 2 +- tests/cli/nvme_print_test.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 7e251f8462..f272ff4c11 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2834,7 +2834,7 @@ static void json_nvme_fdp_events(struct nvme_fdp_events_log *log) obj_add_uint(r, "n", n); - for (unsigned int i = 0; i < n; i++) { + for (unsigned int i = 0; i < n && i < ARRAY_SIZE(log->events); i++) { struct nvme_fdp_event *event = &log->events[i]; struct json_object *obj_event = json_create_object(); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 0fcd81c145..4038032206 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -1481,7 +1481,7 @@ void stdout_fdp_events(struct nvme_fdp_events_log *log) uint32_t n = le32_to_cpu(log->n); struct shr_table *t; - for (unsigned int i = 0; i < n; i++) { + for (unsigned int i = 0; i < n && i < ARRAY_SIZE(log->events); i++) { struct nvme_fdp_event *event = &log->events[i]; ts = int48_to_long(event->ts.timestamp) / 1000; diff --git a/tests/cli/nvme_print_test.py b/tests/cli/nvme_print_test.py index dbfb7ed3cc..b7e18099ec 100644 --- a/tests/cli/nvme_print_test.py +++ b/tests/cli/nvme_print_test.py @@ -78,7 +78,6 @@ def _put(buf, fmt, offset, value): 0x20: [(' Date: Thu, 15 Oct 2026 08:58:38 -0400 Subject: [PATCH 040/263] nvme-print: limit the changed zone list to 511 entries The number of zone identifiers in the changed zone list is reported by the device and can exceed the 511 entries of the data structure. nvme-cli read past the end of the list. Print at most 511 entries. Signed-off-by: Martin Belanger --- src/nvme-print-json.c | 2 +- src/nvme-print-stdout-log.c | 2 +- tests/cli/nvme_print_test.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index f272ff4c11..208c1356bc 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -3693,7 +3693,7 @@ static void json_zns_changed(struct nvme_zns_changed_zone_log *log) obj_add_result(r, "Too many zones have changed to fit into the log. Use report zones for changes."); } else { obj_add_uint(r, "nrzid", nrzid); - for (i = 0; i < nrzid; i++) { + for (i = 0; i < nrzid && i < ARRAY_SIZE(log->zid); i++) { sprintf(json_str, "zid %03d", i); obj_add_uint64(r, json_str, (uint64_t)le64_to_cpu(log->zid[i])); } diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 4038032206..127186664f 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -1706,7 +1706,7 @@ void stdout_zns_changed(struct nvme_zns_changed_zone_log *log) return; stdout_kv_add(t, "nrzid", "%u", nrzid); - for (i = 0; i < nrzid; i++) { + for (i = 0; i < nrzid && i < ARRAY_SIZE(log->zid); i++) { char name[16]; snprintf(name, sizeof(name), "zid %03d", i); diff --git a/tests/cli/nvme_print_test.py b/tests/cli/nvme_print_test.py index b7e18099ec..f21b949165 100644 --- a/tests/cli/nvme_print_test.py +++ b/tests/cli/nvme_print_test.py @@ -83,7 +83,6 @@ def _put(buf, fmt, offset, value): 0x71: [(' Date: Thu, 15 Oct 2026 08:58:38 -0400 Subject: [PATCH 041/263] nvme-print: use only bits 7:0 of the FDP events feature result Bits 7:0 of the Get Features result for FDP Events hold the number of FDP Event Types (NOET). Bits 31:8 are reserved. nvme-cli used the whole value as the number of event descriptors and read past the end of the buffer. Signed-off-by: Martin Belanger --- src/nvme-print-json.c | 3 ++- src/nvme-print-stdout.c | 3 ++- tests/cli/nvme_print_test.py | 4 ---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 208c1356bc..d7b209c3f0 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -4303,7 +4303,8 @@ static void json_feature_show_fields_fdp_events(struct json_object *r, unsigned struct nvme_fdp_supported_event_desc *d; char json_str[STR_LEN]; - for (i = 0; i < result; i++) { + // NOET is in bits 7:0; bits 31:8 are reserved. + for (i = 0; i < (result & 0xff); i++) { d = &((struct nvme_fdp_supported_event_desc *)buf)[i]; sprintf(json_str, "%s", d->evta & 0x1 ? "Enabled" : "Not enabled"); obj_add_str(r, nvme_fdp_event_to_string(d->evt), json_str); diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 885f06df27..2c1bc03576 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -3093,7 +3093,8 @@ void stdout_feature_show_fields(enum nvme_features_id fid, unsigned int result, if (!t) return; - for (unsigned int i = 0; i < result; i++) { + // NOET is in bits 7:0; bits 31:8 are reserved. + for (unsigned int i = 0; i < (result & 0xff); i++) { struct nvme_fdp_supported_event_desc *d; d = &((struct nvme_fdp_supported_event_desc *)buf)[i]; diff --git a/tests/cli/nvme_print_test.py b/tests/cli/nvme_print_test.py index f21b949165..35575c11b9 100644 --- a/tests/cli/nvme_print_test.py +++ b/tests/cli/nvme_print_test.py @@ -49,8 +49,6 @@ _OPC_RESV_REPORT = 0x0e _OPC_DIR_RECV = 0x1a _DIR_STREAMS_STATUS = 0x0102 # directive type 1, operation 2 -_OPC_GET_FEATURES = 0x0a -_FID_FDP_EVENTS = 0x1e _LID_PERSISTENT_EVENT = 0x0d _LID_MEDIA_UNIT_STATUS = 0x10 _LID_SUPPORTED_CAP_CONFIG = 0x11 @@ -129,8 +127,6 @@ def handle_ioctl(self, conn, fd, request, opcode, nsid, elif (opcode == _OPC_DIR_RECV and cdw11 & 0xffff == _DIR_STREAMS_STATUS): _put(buf, ' Date: Thu, 15 Oct 2026 09:49:07 -0400 Subject: [PATCH 042/263] ci: use a cached copy of checkpatch.pl The checkpatch job downloaded checkpatch.pl for each pull request. When GitHub rate limited the download (HTTP 429), the job failed without checking anything. A daily run now saves a copy in the Actions cache, and pull requests use it. They download the script only when no copy is saved. Signed-off-by: Martin Belanger --- .github/workflows/checkpatch.yml | 36 ++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml index 2416576944..f64dae1dc2 100644 --- a/.github/workflows/checkpatch.yml +++ b/.github/workflows/checkpatch.yml @@ -7,18 +7,50 @@ # Authors: Daniel Wagner name: Checkpatch Review -on: [pull_request] +on: + pull_request: + workflow_dispatch: + schedule: + - cron: '0 02 * * *' + +# A pull request reads only the caches of its own branch and of the +# default branch. So only a run on the default branch saves the copy of +# checkpatch.pl. A scheduled run always runs there. jobs: + refresh: + name: refresh checkpatch.pl + if: ${{ github.event_name != 'pull_request' && + github.ref_name == github.event.repository.default_branch }} + runs-on: ubuntu-latest + steps: + - name: Fetch checkpatch.pl + run: | + curl -sSf \ + https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl \ + -o checkpatch.pl + - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: checkpatch.pl + key: checkpatch-${{ github.run_id }} + checkpatch: name: checkpatch review + if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - name: Fetch checkpatch.pl + # The newest saved copy. No key is an exact match. + - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: checkpatch.pl + key: checkpatch- + restore-keys: checkpatch- + - name: Fetch checkpatch.pl if no copy was saved run: | + [ -f checkpatch.pl ] && exit 0 curl -sSf \ https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl \ -o checkpatch.pl From 50c07a604fd489e26a63ae3bde176fdbe9c263bb Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 12:34:02 +0000 Subject: [PATCH 043/263] argconfig: check the type of option targets The option macros take a void pointer, so any variable can be passed and argconfig writes its own type into it. That hid a number of size mismatches which overwrote neighbouring fields or only set part of a variable. Check the pointer type with _Generic, so a mismatch is a compile error. The macro names stay the same and no option table has to change. The signed variant of a type is accepted as well: options which use -1 as "not given" marker store into signed variables, and the same width makes that a semantic and not a memory safety issue. NULL is still accepted for options which are only checked with argconfig_parse_seen(). The 64-bit check needs a nested selection because uint64_t and unsigned long long are the same type on some ABIs only and _Generic does not allow listing a type twice. Signed-off-by: Daniel Wagner --- src/argconfig.h | 56 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/argconfig.h b/src/argconfig.h index 59abfcbdbc..15f0d502f9 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -56,40 +56,73 @@ enum argconfig_types { #define OPT_ARGS(n) \ struct argconfig_commandline_options n[] +/* + * The option macros check the type of the variable they store to, so a + * mismatch is a compile error instead of a write of the wrong size. The + * signed variant of a type is accepted as well. NULL is accepted for + * options which are only checked with argconfig_parse_seen(). + */ +#define OPT_CHECK(v, T, S) _Generic(v, T : (v), S : (v), void * : (v)) +/* + * uint64_t is unsigned long long on some ABIs only, and listing a type + * twice is an error. The inner selection falls back to a void expression, + * which only fails if it is selected. + */ +#define OPT_CHECK_64(v) \ + _Generic(v, uint64_t * : (v), int64_t * : (v), void * : (v), \ + default : \ + _Generic(v, unsigned long long * : (v), \ + long long * : (v), \ + default : \ + (void)0)) +#define OPT_CHECK_STR(v) OPT_CHECK(v, char **, const char **) +#define OPT_CHECK_BOOL(v) _Generic(v, bool * : (v), void * : (v)) +#define OPT_CHECK_DOUBLE(v) _Generic(v, double * : (v), void * : (v)) + #define OPT_END() { NULL } #define OPT_GROUP(d) \ {"", 0, NULL, CFG_GROUP_SEPARATOR, NULL, no_argument, d, false, NULL} #define OPT_FLAG(l, s, v, d, ...) \ - {l, s, NULL, CFG_FLAG, v, no_argument, d, false, __VA_ARGS__} + {l, s, NULL, CFG_FLAG, OPT_CHECK_BOOL(v), \ + no_argument, d, false, __VA_ARGS__} #define OPT_SUFFIX(l, s, v, d, ...) \ - {l, s, "IONUM", CFG_LONG_SUFFIX, v, required_argument, d, false, __VA_ARGS__} + {l, s, "IONUM", CFG_LONG_SUFFIX, OPT_CHECK_64(v), \ + required_argument, d, false, __VA_ARGS__} #define OPT_UINT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_POSITIVE, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_POSITIVE, OPT_CHECK(v, uint32_t *, int32_t *), \ + required_argument, d, false, __VA_ARGS__} #define OPT_INT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_INT, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_INT, OPT_CHECK(v, int *, unsigned int *), \ + required_argument, d, false, __VA_ARGS__} #define OPT_LONG(l, s, v, d, ...) \ - {l, s, "NUM", CFG_LONG, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_LONG, OPT_CHECK_64(v), \ + required_argument, d, false, __VA_ARGS__} #define OPT_DOUBLE(l, s, v, d, ...) \ - {l, s, "NUM", CFG_DOUBLE, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_DOUBLE, OPT_CHECK_DOUBLE(v), \ + required_argument, d, false, __VA_ARGS__} #define OPT_BYTE(l, s, v, d, ...) \ - {l, s, "NUM", CFG_BYTE, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_BYTE, OPT_CHECK(v, uint8_t *, int8_t *), \ + required_argument, d, false, __VA_ARGS__} #define OPT_SHRT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_SHORT, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_SHORT, OPT_CHECK(v, uint16_t *, int16_t *), \ + required_argument, d, false, __VA_ARGS__} #define OPT_INCR(l, s, v, d, ...) \ - {l, s, "NUM", CFG_INCREMENT, v, no_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_INCREMENT, OPT_CHECK(v, int *, unsigned int *), \ + no_argument, d, false, __VA_ARGS__} #define OPT_STRING(l, s, m, v, d, ...) \ - {l, s, m, CFG_STRING, v, required_argument, d, false, __VA_ARGS__} + {l, s, m, CFG_STRING, OPT_CHECK_STR(v), \ + required_argument, d, false, __VA_ARGS__} /* * Like OPT_STRING(), but the value is optional. getopt_long() only @@ -99,7 +132,8 @@ enum argconfig_types { * "given with no value" (optarg is NULL). */ #define OPT_STRING_OPTIONAL(l, s, m, v, d, ...) \ - {l, s, m, CFG_STRING, v, optional_argument, d, false, __VA_ARGS__} + {l, s, m, CFG_STRING, OPT_CHECK_STR(v), \ + optional_argument, d, false, __VA_ARGS__} #define OPT_FMT(l, s, v, d, ...) OPT_STRING(l, s, "FMT", v, d, __VA_ARGS__) #define OPT_FILE(l, s, v, d, ...) OPT_STRING(l, s, "FILE", v, d, __VA_ARGS__) From 4d1fa7565b47fdcff52d049a12f92a49187b6c9b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 14:46:56 +0000 Subject: [PATCH 044/263] argconfig: pass the option argument to the value parsers argconfig_parse_val() and argconfig_parse_type() read the argument from getopt's global optarg. Pass it as a parameter instead, so the value parsing does not depend on the getopt state. No functional change. Signed-off-by: Daniel Wagner --- src/argconfig.c | 57 ++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 6b59c67c76..6792fefdb1 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -126,7 +126,8 @@ static int argconfig_error(char *type, const char *opt, const char *arg) return -EINVAL; } -static int argconfig_parse_type(struct argconfig_commandline_options *s) +static int argconfig_parse_type(struct argconfig_commandline_options *s, + char *arg) { void *value = s->default_value; char *endptr; @@ -136,36 +137,36 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s) switch (s->config_type) { case CFG_STRING: - *(char **)value = optarg; + *(char **)value = arg; break; case CFG_INT: - *(int *)value = strtol(optarg, &endptr, 0); - if (errno || optarg == endptr) - ret = argconfig_error("integer", s->option, optarg); + *(int *)value = strtol(arg, &endptr, 0); + if (errno || arg == endptr) + ret = argconfig_error("integer", s->option, arg); break; case CFG_BYTE: { - unsigned long tmp = strtoul(optarg, &endptr, 0); + unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 8 || optarg == endptr) - ret = argconfig_error("byte", s->option, optarg); + if (errno || tmp >= 1 << 8 || arg == endptr) + ret = argconfig_error("byte", s->option, arg); else *(uint8_t *)value = tmp; break; } case CFG_SHORT: { - unsigned long tmp = strtoul(optarg, &endptr, 0); + unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 16 || optarg == endptr) - ret = argconfig_error("short", s->option, optarg); + if (errno || tmp >= 1 << 16 || arg == endptr) + ret = argconfig_error("short", s->option, arg); else *(uint16_t *)value = tmp; break; } case CFG_POSITIVE: { - uint32_t tmp = strtoul(optarg, &endptr, 0); + uint32_t tmp = strtoul(arg, &endptr, 0); - if (errno || optarg == endptr) - ret = argconfig_error("word", s->option, optarg); + if (errno || arg == endptr) + ret = argconfig_error("word", s->option, arg); else *(uint32_t *)value = tmp; break; @@ -174,19 +175,20 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s) *(int *)value += 1; break; case CFG_LONG: - *(uint64_t *)value = strtoull(optarg, &endptr, 0); - if (errno || optarg == endptr) - ret = argconfig_error("long integer", s->option, optarg); + *(uint64_t *)value = strtoull(arg, &endptr, 0); + if (errno || arg == endptr) + ret = argconfig_error("long integer", s->option, arg); break; case CFG_LONG_SUFFIX: - ret = shr_suffix_binary_parse(optarg, &endptr, (uint64_t *)value); + ret = shr_suffix_binary_parse(arg, &endptr, (uint64_t *)value); if (ret) - argconfig_error("long suffixed integer", s->option, optarg); + argconfig_error("long suffixed integer", s->option, + arg); break; case CFG_DOUBLE: - *(double *)value = strtod(optarg, &endptr); - if (errno || optarg == endptr) - ret = argconfig_error("float", s->option, optarg); + *(double *)value = strtod(arg, &endptr); + if (errno || arg == endptr) + ret = argconfig_error("float", s->option, arg); break; case CFG_FLAG: *(bool *)value = true; @@ -258,14 +260,15 @@ argconfig_match_val(struct argconfig_opt_val *v, const char *str) return match; } -static int argconfig_parse_val(struct argconfig_commandline_options *s) +static int argconfig_parse_val(struct argconfig_commandline_options *s, + char *arg) { struct argconfig_opt_val *v = s->opt_val; if (v) - v = argconfig_match_val(v, optarg); + v = argconfig_match_val(v, arg); if (!v) - return argconfig_parse_type(s); + return argconfig_parse_type(s, arg); argconfig_set_opt_val(v->type, &v->val, s->default_value); return 0; @@ -365,7 +368,7 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, if (!s->default_value) continue; - ret = argconfig_parse_val(s); + ret = argconfig_parse_val(s, optarg); if (ret) break; } @@ -459,7 +462,7 @@ int argconfig_parse_global(int argc, char *argv[], if (!s->default_value) continue; - ret = argconfig_parse_val(s); + ret = argconfig_parse_val(s, optarg); if (ret) break; } From 05c8c2078d0e89d5eed37f60596a5af3872150a6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 14:48:30 +0000 Subject: [PATCH 045/263] argconfig: provide the operands through an accessor Commands find their device as argv[optind] after argconfig_parse(), which relies on getopt permuting argv so that all non-option arguments end up at the end, and the dispatcher skips the global options with argv += optind after argconfig_parse_global(). Add argconfig_nr_operands() and argconfig_operand() for the non-option arguments of the last successful parse, and let argconfig_parse_global() return the index of the subcommand through a parameter. Convert all users of optind. The parser can then be replaced without the callers depending on getopt's global state or on argv being reordered. No functional change. Signed-off-by: Daniel Wagner --- plugins/micron/micron-nvme.c | 6 +- plugins/netapp/netapp-nvme.c | 8 +-- plugins/registry/registry-nvme.c | 4 +- plugins/solidigm/solidigm-telemetry.c | 4 +- plugins/ymtc/ymtc-nvme.c | 2 +- src/argconfig.c | 38 ++++++++++++- src/argconfig.h | 5 +- src/global-ctx.c | 4 +- src/nvme-cmds-enumerate.c | 8 +-- src/nvme-cmds-io.c | 3 +- src/nvme-cmds-sanitize.c | 3 +- src/plugin.c | 8 +-- tests/unit/c/test-argconfig-parse.c | 82 ++++++++++++++++++++++----- 13 files changed, 134 insertions(+), 41 deletions(-) diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 6dfc17446d..cfd4f45e5e 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -4490,9 +4490,11 @@ static int micron_health_info(int argc, char **argv, struct command *acmd, } if (is_json) - print_micron_health_log_json(&smart_log, argv[optind]); + print_micron_health_log_json(&smart_log, + argconfig_operand(0)); else - print_micron_health_log_normal(&smart_log, argv[optind]); + print_micron_health_log_normal(&smart_log, + argconfig_operand(0)); return 0; } diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 9100651bd0..209c686441 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -986,8 +986,8 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, return devs.num; } - if (optind < argc) - devname = basename(argv[optind++]); + if (argconfig_nr_operands()) + devname = basename(argconfig_operand(0)); if (devname) { int subsys_num, nsid; @@ -1077,8 +1077,8 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, return -EINVAL; } - if (optind < argc) - devname = basename(argv[optind++]); + if (argconfig_nr_operands()) + devname = basename(argconfig_operand(0)); if (devname) { int subsys_num, nsid; diff --git a/plugins/registry/registry-nvme.c b/plugins/registry/registry-nvme.c index 5beb3b9cfa..cfafce8088 100644 --- a/plugins/registry/registry-nvme.c +++ b/plugins/registry/registry-nvme.c @@ -37,10 +37,10 @@ static char *get_device(int argc, char **argv) { char *device; - if (optind >= argc) + if (!argconfig_nr_operands()) return NULL; - device = argv[optind]; + device = argconfig_operand(0); strip_dev_prefix(&device); return device; } diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 42a14b2ff1..931d3db246 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -89,9 +89,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *acmd, stru has_binary_file = argconfig_parse_seen(opts, "source-file"); if (has_binary_file) { // If a binary file is provided, we don't want to open a device. - // GNU getopt() permutes the contents of argv as it scans, - // so that eventually all the nonoptions are at the end. - if (argc > optind) { + if (argconfig_nr_operands()) { nvme_show_error( "Device path not allowed when using --source-file"); return -EINVAL; diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index 190763ae91..be02b86206 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -138,7 +138,7 @@ static int ymtc_parse_options(struct libnvme_global_ctx **ctx, } if (model) { - if (sscanf(argv[optind], "/dev/nvme%d", &idx) != 1) + if (sscanf(argconfig_operand(0), "/dev/nvme%d", &idx) != 1) idx = 0; *model = GetSSDModel(idx); } diff --git a/src/argconfig.c b/src/argconfig.c index 6792fefdb1..3d059386eb 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -285,6 +285,31 @@ static bool argconfig_check_verbose(struct argconfig_commandline_options *s) return false; } +/* The non-option arguments of the last successful argconfig_parse() */ +static struct { + char **argv; + int argc; +} operands; + +static void argconfig_set_operands(char **argv, int argc) +{ + operands.argv = argv; + operands.argc = argc; +} + +int argconfig_nr_operands(void) +{ + return operands.argc; +} + +char *argconfig_operand(int i) +{ + if (i < 0 || i >= operands.argc) + return NULL; + + return operands.argv[i]; +} + static argconfig_parse_hook_fn argconfig_parse_hook; void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) @@ -302,6 +327,8 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, int c, long_opt_index = 0, opt_index = 0, short_index = 0, options_count = 0; int ret = 0; + argconfig_set_operands(NULL, 0); + if (argconfig_parse_hook) return argconfig_parse_hook(argc, argv, program_desc, options); @@ -373,6 +400,9 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, break; } + if (!ret) + argconfig_set_operands(&argv[optind], argc - optind); + if (!argconfig_check_verbose(options)) setlocale(LC_ALL, "C"); @@ -383,12 +413,14 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, * Parse global (pre-subcommand) options from argc/argv. Stops at the first * non-option argument (i.e. the subcommand name) so that subcommand-specific * options are left in place for the subcommand dispatcher. After a successful - * call, optind points to the first non-option argument (the subcommand). + * call, *argind is the index of the first non-option argument (the + * subcommand), or argc if there is none. * * Returns 0 on success, negative errno on failure. */ int argconfig_parse_global(int argc, char *argv[], - struct argconfig_commandline_options *options) + struct argconfig_commandline_options *options, + int *argind) { __cleanup_free char *short_opts = NULL; __cleanup_free struct option *long_opts = NULL; @@ -467,6 +499,8 @@ int argconfig_parse_global(int argc, char *argv[], break; } + *argind = optind; + return ret; } diff --git a/src/argconfig.h b/src/argconfig.h index 15f0d502f9..f230d0d3f3 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -226,7 +226,10 @@ void argconfig_print_help(const char *program_desc, int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_commandline_options *options); int argconfig_parse_global(int argc, char *argv[], - struct argconfig_commandline_options *options); + struct argconfig_commandline_options *options, + int *argind); +int argconfig_nr_operands(void); +char *argconfig_operand(int i); typedef int (*argconfig_parse_hook_fn)(int argc, char *argv[], const char *program_desc, diff --git a/src/global-ctx.c b/src/global-ctx.c index 5f3db97333..3fd3334e62 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -27,7 +27,7 @@ static int check_arg_dev(int argc, char **argv) { - if (optind >= argc) { + if (!argconfig_nr_operands()) { errno = EINVAL; nvme_show_perror(argv[0]); return -EINVAL; @@ -46,7 +46,7 @@ static int get_transport_handle(struct libnvme_global_ctx *ctx, int argc, if (ret) return ret; - devname = argv[optind]; + devname = argconfig_operand(0); ret = libnvme_open(ctx, devname, flags, hdl); if (ret) diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 76594a0175..de5e564248 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -50,8 +50,8 @@ static int list_subsys(int argc, char **argv, struct command *acmd, return err; devname = NULL; - if (optind < argc) - devname = basename(argv[optind++]); + if (argconfig_nr_operands()) + devname = basename(argconfig_operand(0)); err = validate_output_format(nvme_args.output_format, &flags); if (err < 0 || (flags != JSON && flags != NORMAL)) { @@ -149,8 +149,8 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (optind < argc) - devname = basename(argv[optind++]); + if (argconfig_nr_operands()) + devname = basename(argconfig_operand(0)); if (devname) { int subsys_id, nsid; diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index d2843300cf..11203a43ed 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -1061,7 +1061,8 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char err = open_exclusive(&ctx, &hdl, argc, argv, cfg.force, opts); if (err) { if (err == -EBUSY) { - nvme_show_error("Failed to open %s.", basename(argv[optind])); + nvme_show_error("Failed to open %s.", + basename(argconfig_operand(0))); nvme_show_error("Namespace is currently busy."); if (!cfg.force) nvme_show_error( diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 79f29a080a..aa01f294e5 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -793,7 +793,8 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin err = open_exclusive(&ctx, &hdl, argc, argv, cfg.force, opts); if (err) { if (-err == EBUSY) { - nvme_show_error("Failed to open %s.", basename(argv[optind])); + nvme_show_error("Failed to open %s.", + basename(argconfig_operand(0))); nvme_show_error("Namespace is currently busy."); if (!cfg.force) nvme_show_error("Use the force [--force] option to ignore that."); diff --git a/src/plugin.c b/src/plugin.c index ca1a14f778..b2e347cd22 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -388,8 +388,8 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) struct command *cr = NULL; bool opt_help = false, opt_version = false; bool cr_valid = false; + int err, argind; char *str; - int err; if (!argc) { general_help(plugin, NULL); @@ -403,14 +403,14 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) NVME_ARGS(global_opts, OPT_FLAG("help", 'h', &opt_help, "show help text"), OPT_FLAG("version", 'V', &opt_version, "show version")); - err = argconfig_parse_global(argc, argv, global_opts); + err = argconfig_parse_global(argc, argv, global_opts, &argind); if (err) { general_help(plugin, NULL); return err; } - argc -= optind; - argv += optind; + argc -= argind; + argv += argind; if (opt_help) { __cleanup_free char **help_argv = NULL; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 7d7de7a25a..242590a8e0 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -298,6 +298,58 @@ static void combined_opts_test(void) } } +/* Number of arguments in a NULL terminated argument vector */ +static int argv_len(char **argv) +{ + int argc = 0; + + while (argv[argc]) + argc++; + + return argc; +} + +/* + * The non-option arguments are available through argconfig_operand(), in + * the order they were given, wherever they appear between the options. + */ +static void operands_test(void) +{ + const char *desc = "Test operands"; + char **argv = (char *[]){ "test-argconfig", "dev0", "--flag", "dev1", + "--uint", "3", "dev2", "--", "--flag", + NULL }; + int ret; + + OPT_ARGS(opts) = { + OPT_FLAG("flag", 'f', &cfg.flag, "flag"), + OPT_UINT("uint", 'u', &cfg.uint, "uint"), + OPT_END() + }; + + ret = argconfig_parse(argv_len(argv), argv, desc, opts); + if (ret || argconfig_nr_operands() != 4 || + strcmp(argconfig_operand(0), "dev0") || + strcmp(argconfig_operand(1), "dev1") || + strcmp(argconfig_operand(2), "dev2") || + strcmp(argconfig_operand(3), "--flag") || + argconfig_operand(4) || argconfig_operand(-1)) { + printf("ERROR: operands: unexpected operands\n"); + test_rc = 1; + } + + { + char **bad = (char *[]){ "test-argconfig", "dev0", "--bogus", + NULL }; + + ret = argconfig_parse(argv_len(bad), bad, desc, opts); + if (!ret || argconfig_nr_operands()) { + printf("ERROR: operands: kept after a failed parse\n"); + test_rc = 1; + } + } +} + #define COMMA_SEP_ARRAY_MAX_VALUES 4 struct comma_sep_array_test { @@ -356,12 +408,11 @@ struct global_parse_test { char *argv[8]; int argc; /* - * Expected optind value after argconfig_parse_global() returns. + * Expected argind value after argconfig_parse_global() returns. * This is the index of the first non-option argument (the subcommand) - * in argv, as seen by getopt – i.e. 1-based because getopt always - * skips argv[0] as the program name. + * in argv, i.e. 1-based because argv[0] is the program name. */ - int expected_optind; + int expected_argind; int expected_ret; int expected_verbose; bool expected_dry_run; @@ -434,7 +485,7 @@ static const struct global_parse_test global_parse_tests[] = { static int parse_global_capture_stderr(int argc, char *argv[], struct argconfig_commandline_options *opts, - char *buf, size_t buf_size) + int *argind, char *buf, size_t buf_size) { FILE *tmp = NULL; int ret; @@ -465,7 +516,7 @@ static int parse_global_capture_stderr(int argc, char *argv[], return -errno; } - ret = argconfig_parse_global(argc, argv, opts); + ret = argconfig_parse_global(argc, argv, opts, argind); fflush(stderr); if (fseek(tmp, 0, SEEK_SET) != 0) { @@ -495,7 +546,7 @@ static int parse_global_capture_stderr(int argc, char *argv[], static void do_global_parse_test(const struct global_parse_test *test) { - int ret; + int ret, argind = -1; char stderr_buf[512] = ""; OPT_ARGS(opts) = { @@ -509,9 +560,11 @@ static void do_global_parse_test(const struct global_parse_test *test) if (test->stderr_must_not_contain) ret = parse_global_capture_stderr(test->argc, (char **)test->argv, - opts, stderr_buf, sizeof(stderr_buf)); + opts, &argind, stderr_buf, + sizeof(stderr_buf)); else - ret = argconfig_parse_global(test->argc, (char **)test->argv, opts); + ret = argconfig_parse_global(test->argc, (char **)test->argv, + opts, &argind); if (ret != test->expected_ret) { printf("ERROR: global_parse {%s}: ret=%d expected=%d\n", @@ -520,9 +573,9 @@ static void do_global_parse_test(const struct global_parse_test *test) return; } - if (test->expected_ret == 0 && optind != test->expected_optind) { - printf("ERROR: global_parse {%s}: optind=%d expected=%d\n", - test->desc, optind, test->expected_optind); + if (test->expected_ret == 0 && argind != test->expected_argind) { + printf("ERROR: global_parse {%s}: argind=%d expected=%d\n", + test->desc, argind, test->expected_argind); test_rc = 1; return; } @@ -559,14 +612,14 @@ static void global_parse_missing_arg_test(void) { char *argv[] = { "prog", "--count" }; unsigned int count = 0; - int ret; + int ret, argind; OPT_ARGS(opts) = { OPT_UINT("count", 'c', &count, "count"), OPT_END() }; - ret = argconfig_parse_global(ARRAY_SIZE(argv), argv, opts); + ret = argconfig_parse_global(ARRAY_SIZE(argv), argv, opts, &argind); if (ret != -EINVAL) { printf("ERROR: global_parse: '--count' with no value: ret=%d expected=%d\n", ret, -EINVAL); @@ -592,6 +645,7 @@ int main(void) optional_string_test(); combined_opts_test(); + operands_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) comma_sep_array_test(&comma_sep_array_tests[i]); From 300c47710312f492017aae1ed1f4b7ae04628cbd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 14:51:07 +0000 Subject: [PATCH 046/263] tests: compare argconfig with a frozen copy of the getopt parser Add a copy of the getopt based argconfig_parse() and argconfig_parse_global() to the unit tests, and a test which runs both on generated command lines from a vocabulary of option forms and values: every sequence of up to two words, the sequences of three words and random longer sequences. The return value, the option values, the seen flags and the operands must be identical. The three word and the random sequences are limited by time, not by count: natively all three word sequences and a few million random ones run in about three seconds, while an emulator for a cross build or valgrind only get through part of them in the same few seconds. argconfig is about to get its own parser, this makes sure it behaves like the getopt one. Single-dash long options and abbreviated long options are filtered out; they will not be supported anymore. getopt's permutation and error handling differ between C libraries, the comparison runs with glibc only. Signed-off-by: Daniel Wagner --- tests/unit/c/argconfig-getopt-ref.c | 360 ++++++++++++++++++++++ tests/unit/c/argconfig-getopt-ref.h | 11 + tests/unit/c/meson.build | 17 ++ tests/unit/c/test-argconfig-diff.c | 450 ++++++++++++++++++++++++++++ 4 files changed, 838 insertions(+) create mode 100644 tests/unit/c/argconfig-getopt-ref.c create mode 100644 tests/unit/c/argconfig-getopt-ref.h create mode 100644 tests/unit/c/test-argconfig-diff.c diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c new file mode 100644 index 0000000000..0ae4437b25 --- /dev/null +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A frozen copy of the getopt based argconfig parser, as it was before + * argconfig got its own parser. test-argconfig-diff compares both, so any + * change in behaviour shows up. Only the value conversion is shared in + * spirit: it is copied unchanged, help output and the locale side effect + * are left out. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "../../../src/argconfig.h" +#include "../../../src/cleanup.h" +#include "argconfig-getopt-ref.h" + +static bool is_null_or_empty(const char *s) +{ + return !s || !*s; +} + +static int argconfig_error(char *type, const char *opt, const char *arg) +{ + fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", + type, opt, arg); + return -EINVAL; +} + +static int argconfig_parse_type(struct argconfig_commandline_options *s, + char *arg) +{ + void *value = s->default_value; + char *endptr; + int ret = 0; + + /* To distinguish success/failure after strtol/stroul call */ + errno = 0; + + switch (s->config_type) { + case CFG_STRING: + *(char **)value = arg; + break; + case CFG_INT: + *(int *)value = strtol(arg, &endptr, 0); + if (errno || arg == endptr) + ret = argconfig_error("integer", s->option, arg); + break; + case CFG_BYTE: { + unsigned long tmp = strtoul(arg, &endptr, 0); + + if (errno || tmp >= 1 << 8 || arg == endptr) + ret = argconfig_error("byte", s->option, arg); + else + *(uint8_t *)value = tmp; + break; + } + case CFG_SHORT: { + unsigned long tmp = strtoul(arg, &endptr, 0); + + if (errno || tmp >= 1 << 16 || arg == endptr) + ret = argconfig_error("short", s->option, arg); + else + *(uint16_t *)value = tmp; + break; + } + case CFG_POSITIVE: { + uint32_t tmp = strtoul(arg, &endptr, 0); + + if (errno || arg == endptr) + ret = argconfig_error("word", s->option, arg); + else + *(uint32_t *)value = tmp; + break; + } + case CFG_INCREMENT: + *(int *)value += 1; + break; + case CFG_LONG: + *(uint64_t *)value = strtoull(arg, &endptr, 0); + if (errno || arg == endptr) + ret = argconfig_error("long integer", s->option, arg); + break; + case CFG_LONG_SUFFIX: + ret = shr_suffix_binary_parse(arg, &endptr, (uint64_t *)value); + if (ret) + argconfig_error("long suffixed integer", s->option, + arg); + break; + case CFG_DOUBLE: + *(double *)value = strtod(arg, &endptr); + if (errno || arg == endptr) + ret = argconfig_error("float", s->option, arg); + break; + case CFG_FLAG: + *(bool *)value = true; + break; + case CFG_GROUP_SEPARATOR: + break; + } + + return ret; +} + +static void argconfig_set_opt_val(enum argconfig_types type, + union argconfig_val *opt_val, void *val) +{ + switch (type) { + case CFG_FLAG: + *(bool *)val = opt_val->bool_val; + break; + case CFG_LONG_SUFFIX: + *(uint64_t *)val = opt_val->long_suffix; + break; + case CFG_POSITIVE: + *(uint32_t *)val = opt_val->positive; + break; + case CFG_INT: + *(int *)val = opt_val->int_val; + break; + case CFG_LONG: + *(uint64_t *)val = opt_val->long_val; + break; + case CFG_DOUBLE: + *(double *)val = opt_val->double_val; + break; + case CFG_BYTE: + *(uint8_t *)val = opt_val->byte; + break; + case CFG_SHORT: + *(uint16_t *)val = opt_val->short_val; + break; + case CFG_INCREMENT: + *(int *)val = opt_val->increment; + break; + case CFG_STRING: + *(char **)val = opt_val->string; + break; + case CFG_GROUP_SEPARATOR: + break; + } +} + +static struct argconfig_opt_val * +argconfig_match_val(struct argconfig_opt_val *v, const char *str) +{ + size_t len = strlen(str); + struct argconfig_opt_val *match = NULL; + + for (; v->str; v++) { + if (strncasecmp(str, v->str, len)) + continue; + + if (len == strlen(v->str)) + return v; + + if (match) + return NULL; /* multiple matches; input is ambiguous */ + + match = v; + } + + return match; +} + +static int argconfig_parse_val(struct argconfig_commandline_options *s, + char *arg) +{ + struct argconfig_opt_val *v = s->opt_val; + + if (v) + v = argconfig_match_val(v, arg); + if (!v) + return argconfig_parse_type(s, arg); + + argconfig_set_opt_val(v->type, &v->val, s->default_value); + return 0; +} + +int ref_argconfig_parse(int argc, char *argv[], + struct argconfig_commandline_options *options, + char ***operands, int *nr_operands) +{ + __cleanup_free char *short_opts = NULL; + __cleanup_free struct option *long_opts = NULL; + __cleanup_free int *long_opt_map = NULL; + struct argconfig_commandline_options *s; + int c, long_opt_index = 0, opt_index = 0, short_index = 0; + int options_count = 0; + int ret = 0; + + errno = 0; + for (s = options; s->option; s++) + options_count++; + + long_opts = calloc(options_count + 2, sizeof(struct option)); + short_opts = calloc(options_count * 3 + 3, sizeof(*short_opts)); + long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); + + if (!long_opts || !short_opts || !long_opt_map) { + fprintf(stderr, "failed to allocate memory for opts: %s\n", + libnvme_strerror(errno)); + return -errno; + } + + for (s = options, opt_index = 0; s->option; s++, opt_index++) { + s->seen = false; + if (s->config_type == CFG_GROUP_SEPARATOR) + continue; + if (s->short_option) { + short_opts[short_index++] = s->short_option; + if (s->argument_type == required_argument || + s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + if (s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + } + if (!is_null_or_empty(s->option)) { + long_opts[long_opt_index].name = s->option; + long_opts[long_opt_index].has_arg = s->argument_type; + long_opt_map[long_opt_index] = opt_index; + long_opt_index++; + } + } + + long_opts[long_opt_index].name = "help"; + long_opts[long_opt_index].val = 'h'; + + short_opts[short_index++] = '?'; + short_opts[short_index] = 'h'; + + optind = 0; + while ((c = getopt_long_only(argc, argv, short_opts, long_opts, + &long_opt_index)) != -1) { + if (c) { + if (c == '?' || c == 'h') { + ret = -EINVAL; + break; + } + for (opt_index = 0; opt_index < options_count; + opt_index++) { + if (c == options[opt_index].short_option) + break; + } + if (opt_index == options_count) + continue; + } else { + opt_index = long_opt_map[long_opt_index]; + } + + s = &options[opt_index]; + s->seen = true; + + if (!s->default_value) + continue; + + ret = argconfig_parse_val(s, optarg); + if (ret) + break; + } + + *operands = &argv[optind]; + *nr_operands = argc - optind; + + return ret; +} + +/* Global options: stop at the first non-option argument. */ +int ref_argconfig_parse_global(int argc, char *argv[], + struct argconfig_commandline_options *options, + int *argind) +{ + __cleanup_free char *short_opts = NULL; + __cleanup_free struct option *long_opts = NULL; + __cleanup_free int *long_opt_map = NULL; + struct argconfig_commandline_options *s; + int c, long_opt_index = 0, opt_index = 0; + int short_index = 0, options_count = 0; + int ret = 0; + + errno = 0; + for (s = options; s->option; s++) + options_count++; + + long_opts = calloc(options_count + 2, sizeof(struct option)); + /* '+' prefix: stop at the first non-option argument (the subcommand) */ + short_opts = calloc(options_count * 3 + 4, sizeof(*short_opts)); + long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); + + if (!long_opts || !short_opts || !long_opt_map) { + fprintf(stderr, "failed to allocate memory for opts: %s\n", + libnvme_strerror(errno)); + return -errno; + } + + short_opts[short_index++] = '+'; + + for (s = options, opt_index = 0; s->option; s++, opt_index++) { + s->seen = false; + if (s->config_type == CFG_GROUP_SEPARATOR) + continue; + if (s->short_option) { + short_opts[short_index++] = s->short_option; + if (s->argument_type == required_argument || + s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + if (s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + } + if (!is_null_or_empty(s->option)) { + long_opts[long_opt_index].name = s->option; + long_opts[long_opt_index].has_arg = s->argument_type; + long_opt_map[long_opt_index] = opt_index; + long_opt_index++; + } + } + + optind = 0; + while ((c = getopt_long(argc, argv, short_opts, + long_opts, &long_opt_index)) != -1) { + if (c == '?' || c == ':') { + ret = -EINVAL; + break; + } + if (c) { + for (opt_index = 0; opt_index < options_count; + opt_index++) { + if (c == options[opt_index].short_option) + break; + } + if (opt_index == options_count) { + ret = -EINVAL; + break; + } + } else { + opt_index = long_opt_map[long_opt_index]; + } + + s = &options[opt_index]; + s->seen = true; + + if (!s->default_value) + continue; + + ret = argconfig_parse_val(s, optarg); + if (ret) + break; + } + + *argind = optind; + + return ret; +} diff --git a/tests/unit/c/argconfig-getopt-ref.h b/tests/unit/c/argconfig-getopt-ref.h new file mode 100644 index 0000000000..3bb83530c1 --- /dev/null +++ b/tests/unit/c/argconfig-getopt-ref.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include "../../../src/argconfig.h" + +int ref_argconfig_parse(int argc, char *argv[], + struct argconfig_commandline_options *options, + char ***operands, int *nr_operands); +int ref_argconfig_parse_global(int argc, char *argv[], + struct argconfig_commandline_options *options, + int *argind); diff --git a/tests/unit/c/meson.build b/tests/unit/c/meson.build index bf2b3b103c..8b7edf7fd1 100644 --- a/tests/unit/c/meson.build +++ b/tests/unit/c/meson.build @@ -13,6 +13,23 @@ test_argconfig_parse = executable( test('nvme-cli - argconfig_parse', test_argconfig_parse) +test_argconfig_diff = executable( + 'test-argconfig-diff', + [ + 'test-argconfig-diff.c', + 'argconfig-getopt-ref.c', + '../../../src/argconfig.c', + ], + dependencies: [ + config_dep, + ccan_dep, + libnvme_dep, + shared_dep, + ], +) + +test('nvme-cli - argconfig_diff', test_argconfig_diff) + test_global_config = executable( 'test-global-config', ['test-global-config.c', '../../../src/global-config.c'], diff --git a/tests/unit/c/test-argconfig-diff.c b/tests/unit/c/test-argconfig-diff.c new file mode 100644 index 0000000000..63935d994e --- /dev/null +++ b/tests/unit/c/test-argconfig-diff.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Compare argconfig_parse() and argconfig_parse_global() with a frozen copy + * of the getopt based parser (argconfig-getopt-ref.c) on generated command + * lines: the return value, all option values, the seen flags and the + * operands must be identical. + * + * Two kinds of input are deliberately not supported anymore and are + * filtered out: single-dash long options ("-flag", which getopt_long_only() + * accepted) and abbreviated long options ("--fla"). They are tested in + * test-argconfig-parse. + * + * getopt's permutation and error handling differ between C libraries, the + * reference is only meaningful with glibc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../../src/argconfig.h" +#include "argconfig-getopt-ref.h" + +const char *libnvme_strerror(int errnum); + +#ifdef __GLIBC__ + +struct diff_cfg { + bool flag; + uint64_t suffix; + uint32_t uint; + int int_val; + uint64_t long_val; + double double_val; + uint8_t byte; + uint16_t shrt; + int incr; + char *string; + char *str; + char *optstr; + uint32_t longonly; + bool dup1; + bool dup2; +}; + +struct global_cfg { + int verbose; + bool dry_run; + char *format; + uint32_t timeout; +}; + +#define MAX_OPTS 32 +#define MAX_ARGS 8 + +struct result { + int ret; + struct diff_cfg cfg; + struct global_cfg gcfg; + bool seen[MAX_OPTS]; + char *operands[MAX_ARGS]; + int nr_operands; + int argind; +}; + +static struct argconfig_opt_val byte_vals[] = { + VAL_BYTE("one", 1), + VAL_BYTE("two", 2), + VAL_BYTE("three", 3), + VAL_END() +}; + +static const char * const long_names[] = { + "flag", "suffix", "uint", "int", "long", "double", "byte", "shrt", + "incr", "string", "str", "optstr", "noval", "longonly", "dup1", + "dup2", "help", "verbose", "dry-run", "format", "timeout", +}; + +static const char * const vocabulary[] = { + "--flag", "-f", "--flag=1", + "--suffix=4Ki", "--suffix", "-s", "-s1", "4Ki", + "--uint=5", "--uint", "-u", "-u7", "--uint=", "5", + "--int=-3", "-i", "-3", + "--long=0x10", "--double=1.5", + "--byte=two", "--byte=tw", "--byte=t", "--byte=300", "-b", "one", + "--shrt=65535", "--shrt=65536", + "--incr", "-I", "-II", "-fI", "-fu9", "-fu", + "--string=x", "--string", "-t", "-tval", "--str=y", "--str", + "--optstr", "--optstr=z", "-O", "-Oval", + "--noval", "-n", "--longonly=4", "--dup1", "--dup2", "-D", + "--", "-", "dev", "", "=x", + "--bogus", "-z", "--help", "-h", "-?", "-fh", + "-v", "-vv", "--verbose", "--dry-run", "-o", "--format=json", + "--timeout", "--timeout=10", "list", +}; + +static void diff_opts(struct argconfig_commandline_options *opts, + struct diff_cfg *cfg) +{ + struct argconfig_commandline_options o[] = { + OPT_FLAG("flag", 'f', &cfg->flag, "flag"), + OPT_SUFFIX("suffix", 's', &cfg->suffix, "suffix"), + OPT_UINT("uint", 'u', &cfg->uint, "uint"), + OPT_INT("int", 'i', &cfg->int_val, "int"), + OPT_LONG("long", 'l', &cfg->long_val, "long"), + OPT_DOUBLE("double", 'd', &cfg->double_val, "double"), + OPT_BYTE("byte", 'b', &cfg->byte, "byte", byte_vals), + OPT_GROUP("More options"), + OPT_SHRT("shrt", 'S', &cfg->shrt, "shrt"), + OPT_INCR("incr", 'I', &cfg->incr, "incr"), + OPT_STRING("string", 't', "STRING", &cfg->string, "string"), + OPT_STR("str", 'r', &cfg->str, "str"), + OPT_STRING_OPTIONAL("optstr", 'O', "STR", &cfg->optstr, + "optstr"), + OPT_FLAG("noval", 'n', NULL, "noval"), + OPT_UINT("longonly", 0, &cfg->longonly, "longonly"), + OPT_FLAG("dup1", 'D', &cfg->dup1, "dup1"), + OPT_FLAG("dup2", 'D', &cfg->dup2, "dup2"), + OPT_END() + }; + + memcpy(opts, o, sizeof(o)); +} + +static void global_opts(struct argconfig_commandline_options *opts, + struct global_cfg *cfg) +{ + struct argconfig_commandline_options o[] = { + OPT_INCR("verbose", 'v', &cfg->verbose, "verbose"), + OPT_FLAG("dry-run", 0, &cfg->dry_run, "dry-run"), + OPT_FMT("format", 'o', &cfg->format, "format"), + OPT_UINT("timeout", 0, &cfg->timeout, "timeout"), + OPT_FLAG("help", 'h', NULL, "help"), + OPT_END() + }; + + memcpy(opts, o, sizeof(o)); +} + +/* The short options of diff_opts() and the implicit help options */ +static const char command_shorts[] = "fsuildbSItrnODh?"; + +/* + * True if getopt would treat the argument as a single-dash long option or + * as an abbreviated long option, which are not supported anymore. For the + * command options getopt_long_only() also took "-x" for an abbreviated + * long option if x is not a short option. + */ +static bool unsupported(const char *arg, bool global) +{ + const char *name, *eq; + size_t len; + unsigned int i; + + if (arg[0] != '-' || !arg[1]) + return false; + if (arg[1] == '-') { + name = arg + 2; + if (!*name) + return false; + } else { + name = arg + 1; + if (global) + return false; + if (!name[1] && strchr(command_shorts, name[0])) + return false; + } + + eq = strchr(name, '='); + len = eq ? (size_t)(eq - name) : strlen(name); + + /* "--name" is fine even if a longer name starts with it */ + if (arg[1] == '-') { + for (i = 0; i < ARRAY_SIZE(long_names); i++) { + if (strlen(long_names[i]) == len && + !strncmp(long_names[i], name, len)) + return false; + } + } + + /* "-name", "-nam" and "--nam" are not */ + for (i = 0; i < ARRAY_SIZE(long_names); i++) { + if (!strncmp(long_names[i], name, len)) + return true; + } + + return false; +} + +/* The filter must not skip the supported forms of the comparison */ +static bool filter_test(void) +{ + static const struct { + const char *arg; + bool global; + bool unsupported; + } tests[] = { + /* exact names, also when a longer name starts with them */ + { "--str", false, false }, + { "--str=y", false, false }, + { "--long=0x10", false, false }, + { "--string=x", false, false }, + { "-f", false, false }, + { "-fu9", false, false }, + { "dev", false, false }, + { "-verbose", true, false }, + /* abbreviations and single-dash long options */ + { "--stri", false, true }, + { "--lon", false, true }, + { "--verb", true, true }, + { "-str", false, true }, + { "-o", false, true }, + { "-flag", false, true }, + }; + bool ok = true; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + if (unsupported(tests[i].arg, tests[i].global) == + tests[i].unsupported) + continue; + printf("ERROR: filter: '%s' %s taken as unsupported\n", + tests[i].arg, tests[i].unsupported ? "not" : "wrongly"); + ok = false; + } + + return ok; +} + +static void collect_seen(struct argconfig_commandline_options *opts, + bool *seen) +{ + int i; + + for (i = 0; opts[i].option && i < MAX_OPTS; i++) + seen[i] = opts[i].seen; +} + +static void run(char **args, int nargs, bool ref, bool global, + struct result *r) +{ + struct argconfig_commandline_options opts[MAX_OPTS]; + char *argv[MAX_ARGS + 2]; + char **operands; + int i; + + memset(r, 0, sizeof(*r)); + argv[0] = "test-argconfig-diff"; + for (i = 0; i < nargs; i++) + argv[i + 1] = args[i]; + argv[nargs + 1] = NULL; + + if (global) { + global_opts(opts, &r->gcfg); + if (ref) + r->ret = ref_argconfig_parse_global(nargs + 1, argv, + opts, &r->argind); + else + r->ret = argconfig_parse_global(nargs + 1, argv, opts, + &r->argind); + if (r->ret) + r->argind = 0; + collect_seen(opts, r->seen); + return; + } + + diff_opts(opts, &r->cfg); + if (ref) { + r->ret = ref_argconfig_parse(nargs + 1, argv, opts, + &operands, &r->nr_operands); + for (i = 0; !r->ret && i < r->nr_operands; i++) + r->operands[i] = operands[i]; + } else { + r->ret = argconfig_parse(nargs + 1, argv, "diff", opts); + r->nr_operands = argconfig_nr_operands(); + for (i = 0; !r->ret && i < r->nr_operands; i++) + r->operands[i] = argconfig_operand(i); + } + if (r->ret) + r->nr_operands = 0; + collect_seen(opts, r->seen); +} + +static unsigned long nr_cases, nr_skipped; + +static bool compare(char **args, int nargs, bool global) +{ + struct result ref, cur; + int i; + + for (i = 0; i < nargs; i++) { + if (unsupported(args[i], global)) { + nr_skipped++; + return true; + } + } + + nr_cases++; + run(args, nargs, true, global, &ref); + run(args, nargs, false, global, &cur); + + if (!memcmp(&ref, &cur, sizeof(ref))) + return true; + + printf("ERROR: %s parse differs for:", global ? "global" : "command"); + for (i = 0; i < nargs; i++) + printf(" '%s'", args[i]); + printf("\n ret %d/%d argind %d/%d operands %d/%d\n", + ref.ret, cur.ret, ref.argind, cur.argind, + ref.nr_operands, cur.nr_operands); + + return false; +} + +/* + * Time budgets in seconds. The tests run in an emulator for cross builds + * and under valgrind, so the larger parts are limited by time, not by a + * number of cases. + */ +#define BUDGET_THREE_WORDS 2.0 +#define BUDGET_RANDOM 1.0 + +static double now(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +static bool expired(double end, unsigned long i) +{ + /* reading the clock for every case would dominate the runtime */ + return !(i % 256) && now() > end; +} + +/* All sequences of up to two words from the vocabulary */ +static bool two_words(bool global) +{ + unsigned int n = ARRAY_SIZE(vocabulary), i, j; + char *args[2]; + bool ok = compare(args, 0, global); + + for (i = 0; i < n; i++) { + args[0] = (char *)vocabulary[i]; + ok &= compare(args, 1, global); + for (j = 0; j < n; j++) { + args[1] = (char *)vocabulary[j]; + ok &= compare(args, 2, global); + } + } + + return ok; +} + +/* Sequences of three words, in a fixed order until the time is up */ +static bool three_words(bool global, unsigned long *count) +{ + unsigned int n = ARRAY_SIZE(vocabulary), i; + double end = now() + BUDGET_THREE_WORDS; + char *args[3]; + bool ok = true; + + for (i = 0; i < n * n * n && !expired(end, i); i++) { + args[0] = (char *)vocabulary[i / (n * n)]; + args[1] = (char *)vocabulary[i / n % n]; + args[2] = (char *)vocabulary[i % n]; + ok &= compare(args, 3, global); + } + *count += i; + + return ok; +} + +/* + * Longer sequences until the time is up. The seed is fixed and a failure + * prints its arguments, so it can be reproduced. + */ +static bool random_cases(bool global, unsigned long *count) +{ + unsigned int n = ARRAY_SIZE(vocabulary); + double end = now() + BUDGET_RANDOM; + unsigned int seed = 0x6e766d65; + char *args[MAX_ARGS]; + unsigned long i; + bool ok = true; + int nargs, j; + + for (i = 0; !expired(end, i); i++) { + seed = seed * 1103515245 + 12345; + nargs = 4 + (seed >> 16) % (MAX_ARGS - 3); + for (j = 0; j < nargs; j++) { + seed = seed * 1103515245 + 12345; + args[j] = (char *)vocabulary[(seed >> 16) % n]; + } + ok &= compare(args, nargs, global); + } + *count += i; + + return ok; +} + +int main(void) +{ + unsigned int n = ARRAY_SIZE(vocabulary); + unsigned long nr_three = 0, nr_random = 0; + bool ok = true; + FILE *f; + + setlocale(LC_NUMERIC, "C"); + unsetenv("POSIXLY_CORRECT"); + f = freopen(shr_dev_null(), "w", stderr); + if (!f) { + printf("ERROR: reopening stderr failed: %s\n", + libnvme_strerror(errno)); + return EXIT_FAILURE; + } + + ok &= filter_test(); + ok &= two_words(false); + ok &= two_words(true); + ok &= three_words(false, &nr_three); + ok &= three_words(true, &nr_three); + ok &= random_cases(false, &nr_random); + ok &= random_cases(true, &nr_random); + + printf("%lu cases compared, %lu skipped\n", nr_cases, nr_skipped); + printf("three words: %lu of %u, random: %lu\n", + nr_three, 2 * n * n * n, nr_random); + fclose(f); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* __GLIBC__ */ + +int main(void) +{ + return 77; +} + +#endif /* __GLIBC__ */ From 9fbe8f646ae7e7d168b233dbf65eb16d05460a6b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 14:59:06 +0000 Subject: [PATCH 047/263] argconfig: parse the command line without getopt argconfig used getopt_long_only() for the commands and getopt_long() for the global options. That ties the parser to getopt's global state (optind, optarg), permutes argv, and the details of permutation and long option handling depend on the C library. Parse the arguments in argconfig itself. The state of a parse lives in a struct, argv is not modified, and the operands are collected in the order they are given. The accepted forms are those of getopt_long(): "--name", "--name=value", "--name value", "-o value", "-ovalue", short option clusters, "--" to end the options and "-" as an operand. Options and operands can be mixed. As before, optional values are only taken when attached, -h, -? and --help print the help in commands, the first matching option of the table wins, and the parse errors keep getopt's wording. Two getopt_long_only() features are dropped: - single-dash long options: "-verbose" is the short options -v -e ... - abbreviated long options: "--namespace" no longer selects "--namespace-id" Both made the meaning of an argument depend on the other options of a command; a new option could turn a working abbreviation ambiguous. The comparison with the getopt parser covers every combination of up to three words of a vocabulary of option forms; all of them parse the same except the dropped forms. The keys test used both forms for the 2.x spellings of gen-dhchap-key's --nqn; it now covers the remaining spellings and checks that abbreviations are rejected. Signed-off-by: Daniel Wagner --- NEWS.md | 8 + src/argconfig.c | 407 +++++++++++++++++----------- tests/cli/nvme_keys_test.py | 34 +-- tests/unit/c/test-argconfig-parse.c | 28 ++ 4 files changed, 300 insertions(+), 177 deletions(-) diff --git a/NEWS.md b/NEWS.md index b0614dab22..905926ea69 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,14 @@ ### Feature removals and incompatible changes +* nvme-cli parses its command line itself instead of with getopt, so + it behaves the same with every C library. Long options now need two + dashes and their full name: single-dash long options such as + `-verbose` and abbreviations such as `--namespace` for + `--namespace-id` are no longer accepted. A single dash always starts + short options, so `-verbose` is read as `-v -e -r -b -o -s -e`. + Options and arguments can still be given in any order. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. diff --git a/src/argconfig.c b/src/argconfig.c index 3d059386eb..3142aad005 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -30,7 +30,6 @@ */ #include -#include #include #include #include @@ -291,12 +290,6 @@ static struct { int argc; } operands; -static void argconfig_set_operands(char **argv, int argc) -{ - operands.argv = argv; - operands.argc = argc; -} - int argconfig_nr_operands(void) { return operands.argc; @@ -310,98 +303,266 @@ char *argconfig_operand(int i) return operands.argv[i]; } -static argconfig_parse_hook_fn argconfig_parse_hook; +/* + * The state of one parse. argv is not modified: the operands are collected + * in their own array, in the order they are given. + */ +struct argconfig_parser { + int argc; + char **argv; + int ind; + struct argconfig_commandline_options *options; + /* + * Global options stop at the first operand, the subcommand, and + * have no implicit help option. + */ + bool global; + char **operands; + int nr_operands; +}; -void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) +/* + * Internal results of argconfig_parse_args(): the help was requested, or + * an option was malformed. Both print the help. + */ +#define ARGCONFIG_HELP 1 +#define ARGCONFIG_USAGE 2 + +/* + * The parse errors use the wording of glibc's getopt, which printed them + * before argconfig had its own parser. + */ +static int argconfig_parse_error(struct argconfig_parser *p, + const char *fmt, ...) { - argconfig_parse_hook = hook; + va_list ap; + + fprintf(stderr, "%s: ", p->argv[0]); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + return ARGCONFIG_USAGE; } -int argconfig_parse(int argc, char *argv[], const char *program_desc, - struct argconfig_commandline_options *options) +static struct argconfig_commandline_options * +argconfig_find_long(struct argconfig_commandline_options *s, + const char *name, size_t len) { - __cleanup_free char *short_opts = NULL; - __cleanup_free struct option *long_opts = NULL; - __cleanup_free int *long_opt_map = NULL; - struct argconfig_commandline_options *s; - int c, long_opt_index = 0, opt_index = 0, short_index = 0, options_count = 0; - int ret = 0; + for (; s->option; s++) { + if (s->config_type == CFG_GROUP_SEPARATOR || + is_null_or_empty(s->option)) + continue; + if (strlen(s->option) == len && !strncmp(s->option, name, len)) + return s; + } - argconfig_set_operands(NULL, 0); + return NULL; +} - if (argconfig_parse_hook) - return argconfig_parse_hook(argc, argv, program_desc, options); +static struct argconfig_commandline_options * +argconfig_find_short(struct argconfig_commandline_options *s, char c) +{ + for (; s->option; s++) { + if (s->config_type == CFG_GROUP_SEPARATOR) + continue; + if (s->short_option == c) + return s; + } + + return NULL; +} + +static int argconfig_apply(struct argconfig_commandline_options *s, + char *arg) +{ + s->seen = true; + + if (!s->default_value) + return 0; + + return argconfig_parse_val(s, arg); +} - errno = 0; - for (s = options; s->option; s++) - options_count++; +/* The value of a required argument which is not attached to the option */ +static char *argconfig_next_arg(struct argconfig_parser *p) +{ + if (p->ind + 1 >= p->argc) + return NULL; - long_opts = calloc(options_count + 2, sizeof(struct option)); - short_opts = calloc(options_count * 3 + 3, sizeof(*short_opts)); - long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); + return p->argv[++p->ind]; +} - if (!long_opts || !short_opts || !long_opt_map) { - fprintf(stderr, "failed to allocate memory for opts: %s\n", libnvme_strerror(errno)); - return -errno; +/* "--name", "--name=value" or "--name value" */ +static int argconfig_parse_long(struct argconfig_parser *p, char *arg) +{ + struct argconfig_commandline_options *s; + char *name = arg + 2, *value = NULL; + char *eq = strchr(name, '='); + size_t len = eq ? (size_t)(eq - name) : strlen(name); + + s = argconfig_find_long(p->options, name, len); + if (!s) { + if (!p->global && len == 4 && !strncmp(name, "help", 4)) + return ARGCONFIG_HELP; + return argconfig_parse_error(p, "unrecognized option '%s'", + arg); } - for (s = options, opt_index = 0; s->option; s++, opt_index++) { - s->seen = false; - if (s->config_type == CFG_GROUP_SEPARATOR) - continue; - if (s->short_option) { - short_opts[short_index++] = s->short_option; - if (s->argument_type == required_argument || - s->argument_type == optional_argument) - short_opts[short_index++] = ':'; - if (s->argument_type == optional_argument) - short_opts[short_index++] = ':'; - } - if (!is_null_or_empty(s->option)) { - long_opts[long_opt_index].name = s->option; - long_opts[long_opt_index].has_arg = s->argument_type; - long_opt_map[long_opt_index] = opt_index; - long_opt_index++; - } + switch (s->argument_type) { + case no_argument: + if (eq) + return argconfig_parse_error(p, + "option '--%s' doesn't allow an argument", + s->option); + break; + case required_argument: + value = eq ? eq + 1 : argconfig_next_arg(p); + if (!value) + return argconfig_parse_error(p, + "option '--%s' requires an argument", + s->option); + break; + case optional_argument: + /* Only an attached value, "--name value" is an operand */ + if (eq) + value = eq + 1; + break; } - long_opts[long_opt_index].name = "help"; - long_opts[long_opt_index].val = 'h'; - - short_opts[short_index++] = '?'; - short_opts[short_index] = 'h'; - - optind = 0; - while ((c = getopt_long_only(argc, argv, short_opts, long_opts, &long_opt_index)) != -1) { - if (c) { - if (c == '?' || c == 'h') { - argconfig_print_help(program_desc, options); - ret = -EINVAL; - break; - } - for (opt_index = 0; opt_index < options_count; opt_index++) { - if (c == options[opt_index].short_option) - break; - } - if (opt_index == options_count) - continue; - } else { - opt_index = long_opt_map[long_opt_index]; + return argconfig_apply(s, value); +} + +/* "-a", "-abc", "-ovalue", "-o value" */ +static int argconfig_parse_short(struct argconfig_parser *p, char *arg) +{ + struct argconfig_commandline_options *s; + char *c, *value; + int ret; + + for (c = arg + 1; *c; c++) { + if (!p->global && (*c == 'h' || *c == '?')) + return ARGCONFIG_HELP; + + s = argconfig_find_short(p->options, *c); + if (!s) + return argconfig_parse_error(p, + "invalid option -- '%c'", *c); + + value = NULL; + switch (s->argument_type) { + case no_argument: + break; + case required_argument: + value = c[1] ? c + 1 : argconfig_next_arg(p); + if (!value) + return argconfig_parse_error(p, + "option requires an argument -- '%c'", + *c); + break; + case optional_argument: + if (c[1]) + value = c + 1; + break; } - s = &options[opt_index]; - s->seen = true; + ret = argconfig_apply(s, value); + if (ret || s->argument_type != no_argument) + return ret; + } + + return 0; +} + +/* + * Options and operands may be given in any order, "--" ends the options. + * Returns 0, ARGCONFIG_HELP, ARGCONFIG_USAGE or a negative errno from the + * value conversion. + */ +static int argconfig_parse_args(struct argconfig_parser *p) +{ + struct argconfig_commandline_options *s; + char *arg; + int ret; + + for (s = p->options; s->option; s++) + s->seen = false; + + for (p->ind = 1; p->ind < p->argc; p->ind++) { + arg = p->argv[p->ind]; + + if (!strcmp(arg, "--")) { + p->ind++; + if (p->global) + return 0; + while (p->ind < p->argc) + p->operands[p->nr_operands++] = + p->argv[p->ind++]; + return 0; + } - if (!s->default_value) + if (arg[0] != '-' || !arg[1]) { + if (p->global) + return 0; + p->operands[p->nr_operands++] = arg; continue; + } - ret = argconfig_parse_val(s, optarg); + if (arg[1] == '-') + ret = argconfig_parse_long(p, arg); + else + ret = argconfig_parse_short(p, arg); if (ret) - break; + return ret; } - if (!ret) - argconfig_set_operands(&argv[optind], argc - optind); + return 0; +} + +static argconfig_parse_hook_fn argconfig_parse_hook; + +void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) +{ + argconfig_parse_hook = hook; +} + +int argconfig_parse(int argc, char *argv[], const char *program_desc, + struct argconfig_commandline_options *options) +{ + static char **operand_buf; + struct argconfig_parser p = { + .argc = argc, + .argv = argv, + .options = options, + }; + int ret; + + free(operand_buf); + operand_buf = NULL; + operands.argv = NULL; + operands.argc = 0; + + if (argconfig_parse_hook) + return argconfig_parse_hook(argc, argv, program_desc, options); + + p.operands = calloc(argc > 0 ? argc : 1, sizeof(*p.operands)); + if (!p.operands) + return -ENOMEM; + + ret = argconfig_parse_args(&p); + if (ret) { + /* A malformed option prints the help, a bad value does not */ + if (ret > 0) { + argconfig_print_help(program_desc, options); + ret = -EINVAL; + } + free(p.operands); + } else { + operand_buf = p.operands; + operands.argv = p.operands; + operands.argc = p.nr_operands; + } if (!argconfig_check_verbose(options)) setlocale(LC_ALL, "C"); @@ -422,86 +583,18 @@ int argconfig_parse_global(int argc, char *argv[], struct argconfig_commandline_options *options, int *argind) { - __cleanup_free char *short_opts = NULL; - __cleanup_free struct option *long_opts = NULL; - __cleanup_free int *long_opt_map = NULL; - struct argconfig_commandline_options *s; - int c, long_opt_index = 0, opt_index = 0; - int short_index = 0, options_count = 0; - int ret = 0; - - errno = 0; - for (s = options; s->option; s++) - options_count++; - - long_opts = calloc(options_count + 2, sizeof(struct option)); - /* '+' prefix: stop at the first non-option argument (the subcommand) */ - short_opts = calloc(options_count * 3 + 4, sizeof(*short_opts)); - long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); - - if (!long_opts || !short_opts || !long_opt_map) { - fprintf(stderr, "failed to allocate memory for opts: %s\n", - libnvme_strerror(errno)); - return -errno; - } - - short_opts[short_index++] = '+'; - - for (s = options, opt_index = 0; s->option; s++, opt_index++) { - s->seen = false; - if (s->config_type == CFG_GROUP_SEPARATOR) - continue; - if (s->short_option) { - short_opts[short_index++] = s->short_option; - if (s->argument_type == required_argument || - s->argument_type == optional_argument) - short_opts[short_index++] = ':'; - if (s->argument_type == optional_argument) - short_opts[short_index++] = ':'; - } - if (!is_null_or_empty(s->option)) { - long_opts[long_opt_index].name = s->option; - long_opts[long_opt_index].has_arg = s->argument_type; - long_opt_map[long_opt_index] = opt_index; - long_opt_index++; - } - } - - optind = 0; - while ((c = getopt_long(argc, argv, short_opts, - long_opts, &long_opt_index)) != -1) { - if (c == '?' || c == ':') { - ret = -EINVAL; - break; - } - if (c) { - for (opt_index = 0; opt_index < options_count; - opt_index++) { - if (c == options[opt_index].short_option) - break; - } - if (opt_index == options_count) { - ret = -EINVAL; - break; - } - } else { - opt_index = long_opt_map[long_opt_index]; - } - - s = &options[opt_index]; - s->seen = true; - - if (!s->default_value) - continue; - - ret = argconfig_parse_val(s, optarg); - if (ret) - break; - } - - *argind = optind; - - return ret; + struct argconfig_parser p = { + .argc = argc, + .argv = argv, + .options = options, + .global = true, + }; + int ret; + + ret = argconfig_parse_args(&p); + *argind = p.ind; + + return ret > 0 ? -EINVAL : ret; } bool argconfig_parse_seen(struct argconfig_commandline_options *s, diff --git a/tests/cli/nvme_keys_test.py b/tests/cli/nvme_keys_test.py index 704a5c5c84..dcd865011b 100644 --- a/tests/cli/nvme_keys_test.py +++ b/tests/cli/nvme_keys_test.py @@ -170,17 +170,13 @@ def test_gen_kxchap_nqn_is_rejected(self): @unittest.skipUnless(_DEPRECATED_CMDS, 'built without deprecated commands') def test_gen_dhchap_key_alias_drops_nqn(self): - # Every spelling nvme-cli 2.x accepted for the host NQN: one or two - # dashes before a prefix of 'nqn' that stayed unambiguous against - # the global --no-retries, with the value as the next argument, - # attached by '=', or attached bare to the short option. The - # trailing '--hmac=1' must survive, so a consumed value cannot - # have swallowed the option after it. - forms = [(s, _HOST_NQN) - for s in ('-n', '-nq', '-nqn', '--nq', '--nqn')] - forms += [(f'{s}={_HOST_NQN}',) - for s in ('-nq', '-nqn', '--nq', '--nqn')] - forms += [(f'-n{_HOST_NQN}',)] # short option, value attached + # The spellings of the host NQN option: the short option with the + # value as the next argument or attached, the long option with the + # value as the next argument or attached by '='. The trailing + # '--hmac=1' must survive, so a consumed value cannot have + # swallowed the option after it. + forms = [('-n', _HOST_NQN), (f'-n{_HOST_NQN}',), + ('--nqn', _HOST_NQN), (f'--nqn={_HOST_NQN}',)] expected = self._run_alias('gen-dhchap-key', '--secret=pin:1234', '--hmac=1') @@ -193,12 +189,11 @@ def test_gen_dhchap_key_alias_drops_nqn(self): self.assertIn(_NQN_IGNORED, result.stderr) @unittest.skipUnless(_DEPRECATED_CMDS, 'built without deprecated commands') - def test_gen_dhchap_key_alias_keeps_2x_rejections(self): - # '--n' was ambiguous with --no-retries even in 2.x, and so were - # '-n=' and '--n='. Keep rejecting them rather than - # accepting more than the command ever did. - for form in (('--n', _HOST_NQN), (f'-n={_HOST_NQN}',), - (f'--n={_HOST_NQN}',)): + def test_gen_dhchap_key_alias_rejects_abbreviations(self): + # Long options are only accepted with their full name; 2.x also + # took unambiguous prefixes such as '--nq'. + for form in (('--n', _HOST_NQN), (f'--n={_HOST_NQN}',), + ('--nq', _HOST_NQN), (f'--nq={_HOST_NQN}',)): with self.subTest(form=form): result = self._run_alias('gen-dhchap-key', '--secret=pin:1234', *form, @@ -221,9 +216,8 @@ def test_check_dhchap_key_alias_takes_the_2x_key_option(self): @unittest.skipUnless(_DEPRECATED_CMDS, 'built without deprecated commands') def test_gen_dhchap_key_alias_keeps_other_options(self): # Dropping '--nqn' must not swallow the global options that also - # start with an 'n', which getopt_long_only() accepts with a - # single dash. - for opt in ('-no-retries', '--no-retries', '--no-ioctl-probing'): + # start with an 'n'. + for opt in ('--no-retries', '--no-ioctl-probing'): with self.subTest(opt=opt): result = self._run_alias('gen-dhchap-key', '--secret=pin:1234', opt) diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 242590a8e0..ff82e95b1e 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -155,6 +155,18 @@ static struct toval_test toval_tests[] = { /* Unrecognized long option. */ { "--bogus", &cfg.flag, { .flag = false }, 0, -EINVAL }, + + /* + * Long options need two dashes and their full name: "-flag" is + * the short options -f -l with "ag" as value of -l, which is not a + * number. + */ + { "--fla", &cfg.flag, { .flag = false }, 0, -EINVAL }, + { "--stri=x", &cfg.string, { .string = NULL }, 0, -EINVAL }, + { "-flag", &cfg.flag, { .flag = false }, 0, -EINVAL }, + { "-string=x", &cfg.string, { .string = NULL }, 0, -EINVAL }, + /* An exact name wins over a longer name it is a prefix of. */ + VAL_TEST("--str=exact", str, "exact", false, 0), }; void toval_test(struct toval_test *test) @@ -481,6 +493,22 @@ static const struct global_parse_test global_parse_tests[] = { {"prog", "--no-such-opt", "list"}, 3, 0, -EINVAL, 0, false, }, + { + "abbreviated long option fails", + {"prog", "--verb", "list"}, + 3, 0, -EINVAL, 0, false, + }, + { + /* -v is applied before 'e' is rejected, as with getopt */ + "single-dash long option fails", + {"prog", "-verbose", "list"}, + 3, 0, -EINVAL, 1, false, + }, + { + "-- ends the global options", + {"prog", "--", "-v"}, + 3, 2, 0, 0, false, + }, }; static int parse_global_capture_stderr(int argc, char *argv[], From 8f4a38b10fcaadca686db7de5387cc54e1934c9d Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 15:56:40 +0000 Subject: [PATCH 048/263] libnvme: skip resets, rescans and Opal ioctls in dry-run mode libnvme's dry-run mode skips the passthru commands, but libnvme_reset_ctrl(), libnvme_reset_subsystem() and libnvme_rescan_ns() issue their ioctl regardless. So 'nvme reset --dry-run' resets the controller, and the same holds for subsystem-reset and ns-rescan. The sed operations which change the drive (take ownership, locking range setup, lock and unlock, password changes, activate LSP and the reverts) are Opal ioctls of the block device and ignore the dry-run mode as well. With dry-run set in nvme-cli.conf, 'nvme sed revert' reverts the drive. Level 0 Discovery, the only read, is a Security Receive passthru command and already skipped. Skip all of them in dry-run mode. Like a skipped passthru command, the operation succeeds and shows up in the debug log only. The check comes before the privsep forwarding, so the helper is not asked to do it either. The new test runs reset, subsystem-reset and ns-rescan against the mock device with and without --dry-run and checks which ioctls reach it. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/ioctl-freebsd.c | 3 + libnvme/src/nvme/ioctl-linux.c | 9 +++ libnvme/src/nvme/ioctl-win.c | 6 ++ libnvme/src/nvme/ioctl.c | 16 +++++ libnvme/src/nvme/private.h | 1 + libnvme/src/sed/sed-linux.c | 4 ++ tests/cli/meson.build | 6 ++ tests/cli/nvme_dry_run_test.py | 104 +++++++++++++++++++++++++++++++ 8 files changed, 149 insertions(+) create mode 100644 tests/cli/nvme_dry_run_test.py diff --git a/libnvme/src/nvme/ioctl-freebsd.c b/libnvme/src/nvme/ioctl-freebsd.c index e416271583..79d80837d4 100644 --- a/libnvme/src/nvme/ioctl-freebsd.c +++ b/libnvme/src/nvme/ioctl-freebsd.c @@ -62,6 +62,9 @@ __shr_public int libnvme_reset_ctrl(struct libnvme_transport_handle *hdl) { int ret; + if (__libnvme_dry_run(hdl, "controller reset")) + return 0; + ret = nvme_verify_chr(hdl); if (ret) return ret; diff --git a/libnvme/src/nvme/ioctl-linux.c b/libnvme/src/nvme/ioctl-linux.c index b0e04d7b64..66177f20ba 100644 --- a/libnvme/src/nvme/ioctl-linux.c +++ b/libnvme/src/nvme/ioctl-linux.c @@ -52,6 +52,9 @@ __shr_public int libnvme_reset_subsystem( { int ret; + if (__libnvme_dry_run(hdl, "subsystem reset")) + return 0; + if (hdl->type == LIBNVME_TRANSPORT_HANDLE_TYPE_PRIVSEP) return __libnvme_privsep_op(hdl, LIBNVME_PRIVSEP_OP_RESET_SUBSYSTEM, @@ -71,6 +74,9 @@ __shr_public int libnvme_reset_ctrl(struct libnvme_transport_handle *hdl) { int ret; + if (__libnvme_dry_run(hdl, "controller reset")) + return 0; + if (hdl->type == LIBNVME_TRANSPORT_HANDLE_TYPE_PRIVSEP) return __libnvme_privsep_op(hdl, LIBNVME_PRIVSEP_OP_RESET_CTRL, NULL, 0, NULL, 0, NULL); @@ -89,6 +95,9 @@ __shr_public int libnvme_rescan_ns(struct libnvme_transport_handle *hdl) { int ret; + if (__libnvme_dry_run(hdl, "namespace rescan")) + return 0; + if (hdl->type == LIBNVME_TRANSPORT_HANDLE_TYPE_PRIVSEP) return __libnvme_privsep_op(hdl, LIBNVME_PRIVSEP_OP_RESCAN_NS, NULL, 0, NULL, 0, NULL); diff --git a/libnvme/src/nvme/ioctl-win.c b/libnvme/src/nvme/ioctl-win.c index 094f31d57e..128c97a9eb 100644 --- a/libnvme/src/nvme/ioctl-win.c +++ b/libnvme/src/nvme/ioctl-win.c @@ -190,6 +190,9 @@ __shr_public int libnvme_reset_ctrl(struct libnvme_transport_handle *hdl) if (!libnvme_transport_handle_is_ctrl(hdl)) return -EINVAL; + if (__libnvme_dry_run(hdl, "controller reset")) + return 0; + entry = libnvme_ctrl_map_lookup(hdl->ctx, libnvme_transport_handle_get_name(hdl)); if (!entry) @@ -212,6 +215,9 @@ __shr_public int libnvme_rescan_ns(struct libnvme_transport_handle *hdl) * but we can cause the system to invalidate its cached partition table * and re-enumerate the device. */ + if (__libnvme_dry_run(hdl, "namespace rescan")) + return 0; + if (!DeviceIoControl(hdl->fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, NULL, NULL)) { errno = get_errno_from_error(GetLastError()); diff --git a/libnvme/src/nvme/ioctl.c b/libnvme/src/nvme/ioctl.c index d6370dee09..88b6cfe521 100644 --- a/libnvme/src/nvme/ioctl.c +++ b/libnvme/src/nvme/ioctl.c @@ -6,6 +6,8 @@ * Authors: Keith Busch * Chaitanya Kulkarni */ +#include + #include "private.h" void *__libnvme_submit_entry(struct libnvme_transport_handle *hdl, @@ -25,6 +27,20 @@ bool __libnvme_decide_retry(struct libnvme_transport_handle *hdl, return false; } +/* + * In dry-run mode an operation which changes the device is skipped and + * succeeds. It only shows up in the debug log, like the passthru commands + * which are skipped. + */ +bool __libnvme_dry_run(struct libnvme_transport_handle *hdl, const char *op) +{ + if (!hdl->ctx->dry_run) + return false; + + libnvme_msg(hdl->ctx, LIBNVME_LOG_DEBUG, "dry-run: %s skipped\n", op); + return true; +} + int __libnvme_exec_dry_run(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd) { diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index ab045103a0..b7b55afb13 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -458,6 +458,7 @@ bool __libnvme_decide_retry(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd, int err); int __libnvme_exec_dry_run(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd); +bool __libnvme_dry_run(struct libnvme_transport_handle *hdl, const char *op); /* The steps of a /dev/nvme-fabrics transfer. */ enum libnvmf_dev_step { diff --git a/libnvme/src/sed/sed-linux.c b/libnvme/src/sed/sed-linux.c index d0eb9d7cb8..873d05b14e 100644 --- a/libnvme/src/sed/sed-linux.c +++ b/libnvme/src/sed/sed-linux.c @@ -66,6 +66,10 @@ static int sed_opal_ioctl(struct libnvme_transport_handle *hdl, if (!libnvme_transport_handle_is_ns(hdl)) return -EINVAL; + /* all of the Opal ioctls change the state of the device */ + if (__libnvme_dry_run(hdl, "Opal ioctl")) + return 0; + if (hdl->type == LIBNVME_TRANSPORT_HANDLE_TYPE_PRIVSEP) return __libnvme_privsep_sed_ioctl(hdl, req, arg, size); diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 3e6167bf58..6286617c86 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -106,6 +106,12 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) + test('nvme-cli - dry-run-cli', + python3, + args: [files('nvme_dry_run_test.py'), nvme_exe, mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib]) + # Not on big-endian hosts yet: the test times out on s390x. if 'id' in selected_plugins and 'log' in selected_plugins and \ 'feat' in selected_plugins and host_machine.endian() == 'little' diff --git a/tests/cli/nvme_dry_run_test.py b/tests/cli/nvme_dry_run_test.py new file mode 100644 index 0000000000..add08cb4c9 --- /dev/null +++ b/tests/cli/nvme_dry_run_test.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE LLC +# +# Authors: Daniel Wagner +"""Tests that --dry-run keeps the commands which do not send an NVMe +command from changing the device: reset, subsystem-reset and ns-rescan +issue a controller ioctl, which libmock_nvme.c forwards raw. + +Usage: python3 nvme_dry_run_test.py +""" +import os +import shutil +import sys +import tempfile +import unittest + +from nvme_mock_ipc import (MockIPCServer, make_mock_env, + resolve_mock_lib_path, run_nvme) + +_NVME_BIN = (sys.argv[1] + if len(sys.argv) > 1 and not sys.argv[1].startswith('-') + else 'nvme') +_MOCK_LIB = resolve_mock_lib_path("./libmock_nvme.so") + +# linux/nvme_ioctl.h: _IO('N', nr) +_NVME_IOC_TYPE = ord('N') +_NVME_IOCTLS = { + 0x44: 'RESET', + 0x45: 'SUBSYS_RESET', + 0x46: 'RESCAN', +} + + +class DryRunMockIPCServer(MockIPCServer): + + def __init__(self, sock_path): + super().__init__(sock_path) + self.ioctls = [] + + def handle_raw_ioctl(self, conn, fd, ioc, payload): + if ioc.type == _NVME_IOC_TYPE and ioc.nr in _NVME_IOCTLS: + self.ioctls.append(_NVME_IOCTLS[ioc.nr]) + self.send_response(conn, 0) + else: + super().handle_raw_ioctl(conn, fd, ioc, payload) + + +class DryRunCLITest(unittest.TestCase): + + DEVICE = '/dev/nvme0' + + def setUp(self): + self.sysfs_dir = tempfile.mkdtemp(prefix='nvme-dry-sysfs-', + dir='/tmp') + self.base_dir = tempfile.mkdtemp(prefix='nvme-dry-base-', dir='/tmp') + self.ipc_dir = tempfile.mkdtemp(prefix='nvme-dry-ipc-', dir='/tmp') + self.ipc_sock_path = os.path.join(self.ipc_dir, "ipc.sock") + + self.server = DryRunMockIPCServer(self.ipc_sock_path) + self.server.start() + + self.env = make_mock_env(_MOCK_LIB, self.ipc_sock_path) + + def tearDown(self): + self.server.shutdown() + self.server.join() + + shutil.rmtree(self.sysfs_dir, ignore_errors=True) + shutil.rmtree(self.base_dir, ignore_errors=True) + shutil.rmtree(self.ipc_dir, ignore_errors=True) + + def _run(self, *args): + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + *args) + self.assertEqual(result.returncode, 0, + f'{args} failed:\nstdout:\n{result.stdout}\n' + f'stderr:\n{result.stderr}') + return result + + def _check(self, command, ioctl): + # Without --dry-run the ioctl reaches the device, so the test + # would notice it with --dry-run as well. + self._run(command, self.DEVICE) + self.assertEqual(self.server.ioctls, [ioctl]) + + self.server.ioctls.clear() + self._run(command, self.DEVICE, '--dry-run') + self.assertEqual(self.server.ioctls, []) + + def test_reset(self): + self._check('reset', 'RESET') + + def test_subsystem_reset(self): + self._check('subsystem-reset', 'SUBSYS_RESET') + + def test_ns_rescan(self): + self._check('ns-rescan', 'RESCAN') + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]] + sys.argv[3:]) From 09e28c0bbf75e32d415f6e207a56316f0f674ebb Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:02:20 +0000 Subject: [PATCH 049/263] fabrics: skip the connects and disconnects in dry-run mode 'nvme connect --dry-run' connects the controller and 'nvme disconnect --dry-run' disconnects it: nvme-cli only sets libnvme's dry-run mode when it opens a device, which the fabrics commands don't, and the fabrics code ignores the mode anyway. Set dry-run mode on every global context nvme-cli creates. In dry-run mode libnvme logs the connect arguments as before, but does not write them to /dev/nvme-fabrics and returns the new -ENVME_CONNECT_DRY_RUN instead, without logging an error. A disconnect is skipped and succeeds. nvme-cli treats a skipped connect as success. Discovery needs a connection to the Discovery Controller, so 'nvme discover' and 'nvme connect-all' stop there and say so. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/fabrics.c | 16 ++++++++++++-- libnvme/src/nvme/util.c | 3 ++- libnvme/src/nvme/util.h | 2 ++ src/fabrics.c | 9 +++++++- src/global-ctx.c | 1 + tests/cli/nvme_fabrics_mock_test.py | 34 +++++++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 7b24cf84e5..1966c09846 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1913,6 +1913,11 @@ static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr) libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, '%.*s'\n", (int)strcspn(argstr, "\n"), argstr); + if (ctx->dry_run) { + libnvme_msg(ctx, LIBNVME_LOG_DEBUG, + "dry-run: connect skipped\n"); + return -ENVME_CONNECT_DRY_RUN; + } len = nvmf_dev_xfer(ctx, argstr, buf, sizeof(buf), &step); if (len < 0 && step == LIBNVMF_DEV_OPEN) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n", @@ -2107,6 +2112,12 @@ __shr_public int libnvmf_disconnect_ctrl(struct libnvme_ctrl *c) struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL; int ret; + if (ctx && ctx->dry_run) { + libnvme_msg(ctx, LIBNVME_LOG_DEBUG, + "dry-run: disconnect of %s skipped\n", c->name); + return 0; + } + if (ctx && ctx->privsep) ret = __libnvme_privsep_delete_ctrl(ctx->privsep, c->name); else @@ -4543,7 +4554,8 @@ __shr_public int libnvmf_discover(struct libnvme_global_ctx *ctx, err = dc_open(ctx, fctx, h, &fctx->ctrl_params, true, &own, &c); if (err) { - if (err != -ENVME_CONNECT_IGNORED) + if (err != -ENVME_CONNECT_IGNORED && + err != -ENVME_CONNECT_DRY_RUN) libnvme_msg(ctx, LIBNVME_LOG_ERR, "failed to add controller, error %s\n", libnvme_strerror(-err)); @@ -4629,7 +4641,7 @@ __shr_public int libnvmf_connect( fctx->ctrl_params.traddr, fctx->ctrl_params.trsvcid, fctx->hooks.user_data); - } else { + } else if (err != -ENVME_CONNECT_DRY_RUN) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "could not add new controller: %s\n", libnvme_strerror(-err)); } diff --git a/libnvme/src/nvme/util.c b/libnvme/src/nvme/util.c index 893ab62a96..dea7a96879 100644 --- a/libnvme/src/nvme/util.c +++ b/libnvme/src/nvme/util.c @@ -457,7 +457,8 @@ static const char * const libnvme_status[] = { [ENVME_CONNECT_CONNREFUSED] = "connection refused", [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address", [ENVME_CONNECT_IGNORED] = "connection ignored", - [ENVME_CONNECT_NOKEY] = "TLS PSK or KX-HMAC-CHAP secret not available" + [ENVME_CONNECT_NOKEY] = "TLS PSK or KX-HMAC-CHAP secret not available", + [ENVME_CONNECT_DRY_RUN] = "not connected in dry-run mode", }; __shr_public const char *libnvme_errno_to_string(int status) diff --git a/libnvme/src/nvme/util.h b/libnvme/src/nvme/util.h index 4cf8a58528..10396d1186 100644 --- a/libnvme/src/nvme/util.h +++ b/libnvme/src/nvme/util.h @@ -39,6 +39,7 @@ * @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address * @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration * @ENVME_CONNECT_NOKEY: TLS PSK or KX-HMAC-CHAP secret not available + * @ENVME_CONNECT_DRY_RUN: not connected, the context is in dry-run mode */ enum libnvme_connect_err { ENVME_CONNECT_RESOLVE = 1000, @@ -62,6 +63,7 @@ enum libnvme_connect_err { ENVME_CONNECT_ADDRNOTAVAIL, ENVME_CONNECT_IGNORED, ENVME_CONNECT_NOKEY, + ENVME_CONNECT_DRY_RUN, }; /** diff --git a/src/fabrics.c b/src/fabrics.c index 62748280c2..b72e60da4c 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -542,7 +542,7 @@ static void consume_conn(const struct libnvmf_config_conn *conn, } else { err = libnvmf_connect(st->ctx, fctx); } - if (err == -ENVME_CONNECT_ALREADY) + if (err == -ENVME_CONNECT_ALREADY || err == -ENVME_CONNECT_DRY_RUN) err = 0; record_err: @@ -994,6 +994,11 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) return 0; ret = libnvmf_discover(ctx, fctx); + if (ret == -ENVME_CONNECT_DRY_RUN) { + nvme_show_error( + "dry-run: the discovery needs a connection to the discovery controller, nothing discovered"); + ret = 0; + } } return ret; @@ -1196,6 +1201,8 @@ int fabrics_connect(const char *desc, int argc, char **argv) ret = libnvmf_connect(ctx, fctx); if (ret == -ENVME_CONNECT_ALREADY && idempotent) ret = 0; + if (ret == -ENVME_CONNECT_DRY_RUN) + ret = 0; if (ret) { /* * hook_already_connected() already reported the specific diff --git a/src/global-ctx.c b/src/global-ctx.c index 3fd3334e62..ea420ef595 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -179,6 +179,7 @@ static int __nvme_create_global_ctx(struct libnvme_global_ctx **pctx) log_level = map_log_level(nvme_args.verbose, nvme_args.quiet); libnvme_set_logging_file(ctx, stderr); libnvme_set_logging_level(ctx, log_level, false, false); + libnvme_set_dry_run(ctx, nvme_args.dry_run); if (!nvme_args.set_options) goto out; diff --git a/tests/cli/nvme_fabrics_mock_test.py b/tests/cli/nvme_fabrics_mock_test.py index 8eed565b43..af9a7ce385 100644 --- a/tests/cli/nvme_fabrics_mock_test.py +++ b/tests/cli/nvme_fabrics_mock_test.py @@ -437,6 +437,40 @@ def test_connect_all_link_local_global_scoped_dc(self): by_nqn = {c['subsysnqn']: c['traddr'] for c in self.server.controllers.values()} self.assertEqual(by_nqn.get(io_subsys), 'fe80::2') + def test_connect_dry_run(self): + """--dry-run does not write the connect arguments to the kernel.""" + subsysnqn = "nqn.2014-08.org.nvmexpress:my-io-subsys-1" + self._run('connect', '-t', 'tcp', '-a', '192.168.1.10', '-s', '4420', + '-n', subsysnqn, '--dry-run') + self.assertEqual(self.server.controllers, {}) + self.assertFalse(self._ctrl_path(0).exists()) + + def test_connect_all_dry_run(self): + """Discovery needs a connection to the Discovery Controller, so + --dry-run stops before connecting it and says so.""" + self.server.discovery_entries = [ + { + 'transport': 'tcp', + 'traddr': '192.168.1.200', + 'trsvcid': '4420', + 'subsysnqn': 'nqn.2014-08.org.nvmexpress:mock-target-nvm-subsystem', + }, + ] + res = self._run('connect-all', '-t', 'tcp', '-a', '192.168.1.1', + '--dry-run') + self.assertEqual(self.server.controllers, {}) + self.assertIn("dry-run", res.stderr) + + def test_disconnect_dry_run(self): + """--dry-run does not delete the controller.""" + subsysnqn = "nqn.2014-08.org.nvmexpress:my-io-subsys-1" + self._run('connect', '-t', 'tcp', '-a', '192.168.1.10', '-s', '4420', + '-n', subsysnqn) + self._run('disconnect', '-n', subsysnqn, '--dry-run') + self._assert_persisted(0) + self._run('disconnect', '-n', subsysnqn) + self._assert_disconnected(0) + def test_connect_already_connected(self): """Test how connect handles EALREADY (already connected) error gracefully.""" self.server.connect_errno = 114 # EALREADY From f5a1439fedde7f9e10105f59820d337a08ca2840 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:04:24 +0000 Subject: [PATCH 050/263] NEWS: document the dry-run fixes --dry-run now also skips the resets, the rescans, the Opal operations and the fabrics connects and disconnects, which changed the device or the system before. Signed-off-by: Daniel Wagner --- NEWS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS.md b/NEWS.md index 905926ea69..6af84b63d4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -108,6 +108,17 @@ ### nvme-cli +* `--dry-run` now also skips `nvme reset`, `nvme subsystem-reset`, + `nvme ns-rescan`, the `nvme sed` operations which change the drive, + and the fabrics connects and disconnects. Before, only NVMe commands + were skipped: these still reset the controller or subsystem, + rescanned the namespaces, connected and disconnected controllers, and + with `dry-run` set in nvme-cli.conf the sed operations still ran. + `nvme connect --dry-run -vv` shows the connect arguments instead. + `nvme discover` and `nvme connect-all` stop before connecting the + Discovery Controller, as there is nothing to discover without a + connection. + * `nvme discover` and `nvme connect-all` now send the keep-alive timeout to a persistent Discovery Controller. Before, the value was set too late. The DC got no keep-alive timeout, so it was never From a8106e5bd01d310de9ce612b78f751a354d75ab7 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 17:17:47 +0000 Subject: [PATCH 051/263] shared: add shr_strsep() string-util.h defines strsep() itself where the C library lacks it, as on Windows. A caller which forgets the header still builds on Linux, but not on Windows. Add shr_strsep(), like the other shr_ helpers: it uses strsep() of the C library where there is one and the fallback otherwise, so the check is in one place. Use it everywhere. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/fabrics.c | 6 +++--- shared/string-util.h | 15 +++++++++++---- src/fabrics.c | 17 ++++++++++------- src/global-ctx.c | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 1966c09846..44905a7ca8 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1711,10 +1711,10 @@ static int __nvmf_supported_options(struct libnvme_global_ctx *ctx) libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "kernel supports: "); - while ((p = strsep(&options, ",\n")) != NULL) { + while ((p = shr_strsep(&options, ",\n")) != NULL) { if (!*p) continue; - v = strsep(&p, "= "); + v = shr_strsep(&p, "= "); if (!v) continue; libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s ", v); @@ -1958,7 +1958,7 @@ static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr) libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, response '%.*s'\n", (int)strcspn(buf, "\n"), buf); options = buf; - while ((p = strsep(&options, ",\n")) != NULL) { + while ((p = shr_strsep(&options, ",\n")) != NULL) { if (!*p) continue; if (sscanf(p, "instance=%d", &instance) == 1) { diff --git a/shared/string-util.h b/shared/string-util.h index 886239f280..86d9c3a4dc 100644 --- a/shared/string-util.h +++ b/shared/string-util.h @@ -13,10 +13,17 @@ #include #include -#ifndef HAVE_STRSEP -/* strsep() is missing on some platforms (e.g. mingw/MSVC runtimes). */ -static inline char *strsep(char **stringp, const char *delim) +/* + * strsep() of the C library, with a fallback for those which don't have + * it (e.g. mingw/MSVC runtimes). Splits *stringp at the first character of + * @delim and advances it past that, or sets it to NULL at the end. Returns + * the token, or NULL once *stringp is NULL. + */ +static inline char *shr_strsep(char **stringp, const char *delim) { +#ifdef HAVE_STRSEP + return strsep(stringp, delim); +#else char *s, *end; if (!stringp || !*stringp) @@ -32,8 +39,8 @@ static inline char *strsep(char **stringp, const char *delim) *stringp = end; return s; -} #endif +} /* * NULL-safe string equality: two NULLs are equal, one NULL and one diff --git a/src/fabrics.c b/src/fabrics.c index b72e60da4c..02f3f6512b 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "cleanup.h" #include "config-convert.h" @@ -567,7 +568,8 @@ const char *nvmf_resolve_persistent_arg( * Parse one discovery.conf line -- the same argv-style syntax 'nvme * discover'/'connect-all' accept, reusing NVMF_ARGS so every short and long * form works identically to real usage -- and hand the parsed arguments to - * nvme_config_convert_discovery_args(). @line is modified in place (strsep()). + * nvme_config_convert_discovery_args(). @line is modified in place + * (shr_strsep()). * * A blank line, a comment, or one with neither transport nor traddr is * silently skipped (0, nothing added), tolerating a discovery.conf that @@ -606,7 +608,8 @@ int nvmf_convert_discovery_line(struct libnvmf_config_emitter *emitter, * getopt_long() parses '=' within a single token natively for * every option shape, so splitting it out here is never required. */ - while ((ptr = strsep(&p, "\n ")) != NULL && argc < MAX_DISC_ARGS - 1) + while ((ptr = shr_strsep(&p, "\n ")) != NULL && + argc < MAX_DISC_ARGS - 1) argv[argc++] = ptr; argv[argc] = NULL; @@ -1262,7 +1265,7 @@ static int nvmf_disconnect_nqn(struct libnvme_global_ctx *ctx, char *nqn) struct libnvme_subsystem *s; struct libnvme_ctrl *c; - while ((p = strsep(&n, ",")) != NULL) { + while ((p = shr_strsep(&n, ",")) != NULL) { if (!strlen(p)) continue; libnvme_for_each_host(ctx, h) { @@ -1352,7 +1355,7 @@ static int disconnect_by_device(struct libnvme_global_ctx *ctx, char *p; int err; - while ((p = strsep(&device, ",")) != NULL) { + while ((p = shr_strsep(&device, ",")) != NULL) { if (!strncmp(p, "/dev/", 5)) p += 5; @@ -1427,7 +1430,7 @@ static int disconnect_by_nqn(struct libnvme_global_ctx *ctx, const char *nqn, return -ENOMEM; t = excl; - while ((p = strsep(&t, ",")) != NULL) { + while ((p = shr_strsep(&t, ",")) != NULL) { if (!*p) continue; @@ -1784,7 +1787,7 @@ int fabrics_dim(const char *desc, int argc, char **argv) struct libnvme_subsystem *s; char *n = cfg.nqn; - while ((p = strsep(&n, ",")) != NULL) { + while ((p = shr_strsep(&n, ",")) != NULL) { if (!strlen(p)) continue; libnvme_for_each_host(ctx, h) { @@ -1801,7 +1804,7 @@ int fabrics_dim(const char *desc, int argc, char **argv) if (cfg.device) { char *d = cfg.device; - while ((p = strsep(&d, ",")) != NULL) { + while ((p = shr_strsep(&d, ",")) != NULL) { if (!strncmp(p, "/dev/", 5)) p += 5; ret = libnvme_scan_ctrl(ctx, p, &c); diff --git a/src/global-ctx.c b/src/global-ctx.c index ea420ef595..4c6adf998b 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -189,7 +189,7 @@ static int __nvme_create_global_ctx(struct libnvme_global_ctx **pctx) return -ENOMEM; p = buf; - while ((opt = strsep(&p, ",")) != NULL) { + while ((opt = shr_strsep(&p, ",")) != NULL) { if (!*opt) continue; err = nvme_apply_option(ctx, opt); From cf3dd72bd70248a0bec9dee751368a168ecdf35e Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 15:46:30 +0000 Subject: [PATCH 052/263] argconfig: support a table of global options nvme-cli adds its global options (--verbose, --output-format, ...) to every command's option table through NVME_ARGS. Each command thus carries a copy of them, and they are told apart from the command's options only by the "Global options" separator. Let a program register one table of global options with argconfig_set_global_options(). argconfig_parse() and argconfig_parse_global() look an option up in the given table first and then in the global one, so a command's own option still takes precedence. The help prints the global options after the command's options, argconfig_parse_seen() finds them, and the metadata dump lists them behind a "Global options" separator as before. The seen flags of the global options are not reset by a parse: they can be given before the command name, and the global options are parsed again for each plugin level. Nothing registers a table yet. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata.c | 41 +++++++++++- src/argconfig.c | 97 +++++++++++++++++++++++------ src/argconfig.h | 3 + tests/unit/c/test-argconfig-parse.c | 82 ++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 21 deletions(-) diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 115bc20b62..68c04b109b 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -334,6 +334,40 @@ static int copy_options(const struct argconfig_commandline_options *opts, * command's options into the model, then returns the sentinel so the parser * unwinds before the command opens a device. */ +/* + * The command's options followed by the registered global options, behind a + * "Global options" separator as json_command() expects. The caller frees + * the returned array; the entries alias the original tables. + */ +static struct argconfig_commandline_options * +with_global_options(struct argconfig_commandline_options *options) +{ + struct argconfig_commandline_options *g, *all; + size_t n = 0, ng = 0, i; + + g = argconfig_get_global_options(); + while (options[n].option) + n++; + while (g && g[ng].option) + ng++; + + all = calloc(n + ng + 2, sizeof(*all)); + if (!all) + return NULL; + + memcpy(all, options, n * sizeof(*all)); + if (ng) { + struct argconfig_commandline_options sep = + OPT_GROUP("Global options"); + + memcpy(&all[n], &sep, sizeof(sep)); + for (i = 0; i < ng; i++) + memcpy(&all[n + 1 + i], &g[i], sizeof(*all)); + } + + return all; +} + static int metadata_capture_hook(int argc, char **argv, const char *program_desc, struct argconfig_commandline_options *options) { @@ -342,7 +376,12 @@ static int metadata_capture_hook(int argc, char **argv, const char *program_desc (void)program_desc; if (command_metadata_cur_command && !command_metadata_cur_command->captured) { - int err = copy_options(options, + __cleanup_free struct argconfig_commandline_options *all = + with_global_options(options); + int err = -ENOMEM; + + if (all) + err = copy_options(all, &command_metadata_cur_command->options, &command_metadata_cur_command->num_options); if (err) diff --git a/src/argconfig.c b/src/argconfig.c index 3142aad005..d4d2f38bda 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -52,6 +52,30 @@ static bool is_null_or_empty(const char *s) static const char *append_usage_str = ""; +/* + * Options every command accepts, registered once instead of being added to + * each command's table. The command's own options are looked up first, so + * a command can still use a short or long name of a global option. + */ +static struct argconfig_commandline_options *global_options; + +void argconfig_set_global_options(struct argconfig_commandline_options *options) +{ + global_options = options; +} + +struct argconfig_commandline_options *argconfig_get_global_options(void) +{ + return global_options; +} + +/* The global options, unless they are the table itself */ +static struct argconfig_commandline_options * +argconfig_globals(struct argconfig_commandline_options *options) +{ + return options == global_options ? NULL : global_options; +} + void argconfig_append_usage(const char *str) { append_usage_str = str; @@ -89,29 +113,20 @@ static void show_option(const struct argconfig_commandline_options *option) fprintf(stderr, "\n"); } -void argconfig_print_help(const char *program_desc, - struct argconfig_commandline_options *s) +/* A group header is printed before the first option which follows it */ +static void print_options(struct argconfig_commandline_options *s, + const char *header) { - const char *pending_header = "Options"; bool header_printed = false; - fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n", - append_usage_str); - - shr_print_word_wrapped(program_desc, 0, 0, stderr); - fprintf(stderr, "\n"); - - if (!s || !s->option) - return; - - for (; s->option; s++) { + for (; s && s->option; s++) { if (s->config_type == CFG_GROUP_SEPARATOR) { - pending_header = s->help; + header = s->help; header_printed = false; continue; } if (!header_printed) { - fprintf(stderr, "\n\033[1m%s:\033[0m\n", pending_header); + fprintf(stderr, "\n\033[1m%s:\033[0m\n", header); header_printed = true; } if (!s->hidden) @@ -119,6 +134,19 @@ void argconfig_print_help(const char *program_desc, } } +void argconfig_print_help(const char *program_desc, + struct argconfig_commandline_options *s) +{ + fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n", + append_usage_str); + + shr_print_word_wrapped(program_desc, 0, 0, stderr); + fprintf(stderr, "\n"); + + print_options(s, "Options"); + print_options(argconfig_globals(s), "Global options"); +} + static int argconfig_error(char *type, const char *opt, const char *arg) { fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", type, opt, arg); @@ -273,13 +301,39 @@ static int argconfig_parse_val(struct argconfig_commandline_options *s, return 0; } +static struct argconfig_commandline_options * +argconfig_find_option(struct argconfig_commandline_options *s, + const char *option) +{ + struct argconfig_commandline_options *g = argconfig_globals(s); + + for (; s && s->option; s++) { + if (!strcmp(s->option, option)) + return s; + } + for (; g && g->option; g++) { + if (!strcmp(g->option, option)) + return g; + } + + return NULL; +} + +/* --verbose counts, a command may have a flag of the same name */ static bool argconfig_check_verbose(struct argconfig_commandline_options *s) { + struct argconfig_commandline_options *g = argconfig_globals(s); + for (; s && s->option; s++) { if (!strcmp(s->option, "verbose") && s->config_type == CFG_INCREMENT) return s->seen; } + for (; g && g->option; g++) { + if (!strcmp(g->option, "verbose") && + g->config_type == CFG_INCREMENT) + return g->seen; + } return false; } @@ -403,6 +457,9 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) size_t len = eq ? (size_t)(eq - name) : strlen(name); s = argconfig_find_long(p->options, name, len); + if (!s && argconfig_globals(p->options)) + s = argconfig_find_long(argconfig_globals(p->options), name, + len); if (!s) { if (!p->global && len == 4 && !strncmp(name, "help", 4)) return ARGCONFIG_HELP; @@ -446,6 +503,9 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) return ARGCONFIG_HELP; s = argconfig_find_short(p->options, *c); + if (!s && argconfig_globals(p->options)) + s = argconfig_find_short(argconfig_globals(p->options), + *c); if (!s) return argconfig_parse_error(p, "invalid option -- '%c'", *c); @@ -600,10 +660,7 @@ int argconfig_parse_global(int argc, char *argv[], bool argconfig_parse_seen(struct argconfig_commandline_options *s, const char *option) { - for (; s && s->option; s++) { - if (!strcmp(s->option, option)) - return s->seen; - } + s = argconfig_find_option(s, option); - return false; + return s && s->seen; } diff --git a/src/argconfig.h b/src/argconfig.h index f230d0d3f3..aced986eba 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -228,6 +228,9 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, int argconfig_parse_global(int argc, char *argv[], struct argconfig_commandline_options *options, int *argind); +void argconfig_set_global_options( + struct argconfig_commandline_options *options); +struct argconfig_commandline_options *argconfig_get_global_options(void); int argconfig_nr_operands(void); char *argconfig_operand(int i); diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index ff82e95b1e..a243e6ed19 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -362,6 +362,87 @@ static void operands_test(void) } } +/* + * Options registered with argconfig_set_global_options() are accepted by + * every command after the command's own options, which take precedence. + * Their seen flags are kept across parses: the global options may already + * have been given before the command name. + */ +static void global_options_test(void) +{ + const char *desc = "Test global options"; + int verbose = 0; + bool dry_run = false; + char *format = NULL; + int argind, ret; + + OPT_ARGS(globals) = { + OPT_INCR("verbose", 'v', &verbose, "verbose"), + OPT_FLAG("dry-run", 'd', &dry_run, "dry run"), + OPT_FMT("output-format", 'o', &format, "format"), + OPT_END() + }; + OPT_ARGS(opts) = { + OPT_FLAG("flag", 'f', &cfg.flag, "flag"), + /* shadows -d of the global options */ + OPT_UINT("uint", 'd', &cfg.uint, "uint"), + OPT_END() + }; + OPT_ARGS(local) = { + OPT_FLAG("flag", 'f', &cfg.flag, "flag"), + OPT_END() + }; + + argconfig_set_global_options(globals); + + { + char **argv = (char *[]){ "prog", "-v", "list", NULL }; + + ret = argconfig_parse_global(argv_len(argv), argv, local, + &argind); + if (ret || argind != 2 || verbose != 1) { + printf("ERROR: global options: global parse failed\n"); + test_rc = 1; + } + } + + { + char **argv = (char *[]){ "list", "-f", "--verbose", "-d", "7", + "-o", "json", "dev", NULL }; + + cfg.flag = false; + cfg.uint = 0; + ret = argconfig_parse(argv_len(argv), argv, desc, opts); + if (ret || !cfg.flag || cfg.uint != 7 || verbose != 2 || + dry_run || !format || strcmp(format, "json") || + argconfig_nr_operands() != 1) { + printf("ERROR: global options: command parse failed\n"); + test_rc = 1; + } + if (!argconfig_parse_seen(opts, "output-format") || + !argconfig_parse_seen(opts, "verbose") || + argconfig_parse_seen(opts, "dry-run")) { + printf("ERROR: global options: seen mismatch\n"); + test_rc = 1; + } + } + + { + /* a later parse keeps the seen flags of the global options */ + char **argv = (char *[]){ "list", "--dry-run", NULL }; + + ret = argconfig_parse(argv_len(argv), argv, desc, local); + if (ret || !dry_run || + !argconfig_parse_seen(local, "output-format") || + !argconfig_parse_seen(local, "dry-run")) { + printf("ERROR: global options: seen not kept\n"); + test_rc = 1; + } + } + + argconfig_set_global_options(NULL); +} + #define COMMA_SEP_ARRAY_MAX_VALUES 4 struct comma_sep_array_test { @@ -674,6 +755,7 @@ int main(void) optional_string_test(); combined_opts_test(); operands_test(); + global_options_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) comma_sep_array_test(&comma_sep_array_tests[i]); From 2cbf7f80aabef6d3504351fd3441051ed188dab8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 15:48:56 +0000 Subject: [PATCH 053/263] nvme: register the global options once NVME_ARGS appends the global options to every command's option table. Register them once with argconfig instead: args.c has the only table, which nvme_args_register() hands to argconfig_set_global_options() at startup. NVME_ARGS and NVME_ARGS_OUTPUT_FORMATS only build the command's own table and record the supported output formats, from which the help text of --output-format is generated. The pre-command parse in handle_plugin() keeps only --help and --version in its own table. The help and the command metadata of the commands built with NVME_ARGS do not change. The 14 commands with their own option tables accept the global options now as well: config validate, status and convert, intel lat-stats-tracking, memblaze lat-stats and smart-log-add-x, sndk and wdc vs-internal-log and sed discover, initialize, revert, lock, unlock and password. libnvme skips the Opal ioctls in dry-run mode, so 'nvme sed initialize --dry-run' and 'nvme sed password --dry-run' do not change the drive; the sed test checks that. Signed-off-by: Daniel Wagner --- src/args.c | 46 ++++++++++++++++++++++++++++++++++++++ src/args.h | 43 ++++++++++++----------------------- src/nvme.c | 1 + src/plugin.c | 6 +++-- tests/cli/nvme_sed_test.py | 13 +++++++++++ 5 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/args.c b/src/args.c index 96d1109b24..ba79ca2b4f 100644 --- a/src/args.c +++ b/src/args.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -22,6 +23,51 @@ struct nvme_args nvme_args = { .supported_output_formats = DEFAULT_OUTPUT_FORMATS, }; +static char output_format_help[64]; + +static struct argconfig_commandline_options global_options[] = { + OPT_INCR("verbose", 'v', &nvme_args.verbose, + "Increase output verbosity"), + OPT_FLAG("quiet", 0, &nvme_args.quiet, + "suppress informational output"), + OPT_FMT("output-format", 'o', &nvme_args.output_format, + output_format_help), + OPT_UINT("timeout", 0, &nvme_args.timeout, + "timeout value, in milliseconds"), + OPT_FLAG("dry-run", 0, &nvme_args.dry_run, + "show command instead of executing"), + OPT_FLAG("no-retries", 0, &nvme_args.no_retries, + "disable retry logic on errors"), + OPT_FLAG("no-ioctl-probing", 0, &nvme_args.no_ioctl_probing, + "disable 64-bit IOCTL support probing"), + OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, + "output format version: 1|2"), + /* the spelling of --verbose of older versions */ + OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, NULL, + true), + OPT_STRING("set-options", 0, "KEY=VALUE", &nvme_args.set_options, + "set a libnvme library option (key=value[,key=value,...]);"), + OPT_END() +}; + +/* Register the options every command accepts */ +void nvme_args_register(void) +{ + nvme_args_set_output_formats(DEFAULT_OUTPUT_FORMATS); + argconfig_set_global_options(global_options); +} + +/* The output formats the current command supports */ +void nvme_args_set_output_formats(nvme_print_flags_t formats) +{ + nvme_args.supported_output_formats = formats; + snprintf(output_format_help, sizeof(output_format_help), + "Output format: normal%s%s%s", + formats & JSON ? "|json" : "", + formats & BINARY ? "|binary" : "", + formats & TABULAR ? "|tabular" : ""); +} + const char *uuid_index = "UUID index"; const char *namespace_id_desired = "identifier of desired namespace"; diff --git a/src/args.h b/src/args.h index d4eec52f93..52c0b0bf20 100644 --- a/src/args.h +++ b/src/args.h @@ -47,36 +47,19 @@ enum nvme_print_flags { #endif /* CONFIG_JSONC */ /* - * the ordering of the arguments matters, as the argument parser uses the first - * match, thus any command which defines -t shorthand will match first. + * The command's options. The global options are registered once with + * argconfig (nvme_args_register()) and accepted by every command; a + * command's own option with the same short or long name takes precedence. + * The supported output formats select the values accepted by + * --output-format and its help text. */ -#define NVME_ARGS_OUTPUT_FORMATS(n, of_mask, of_desc, ...) \ - nvme_args.supported_output_formats = of_mask; \ - struct argconfig_commandline_options n[] = { \ - OPT_GROUP("Options"), \ - ##__VA_ARGS__, \ - OPT_GROUP("Global options"), \ - OPT_INCR("verbose", 'v', &nvme_args.verbose, \ - "Increase output verbosity"), \ - OPT_FLAG("quiet", 0, &nvme_args.quiet, \ - "suppress informational output"), \ - OPT_FMT("output-format", 'o', &nvme_args.output_format, of_desc), \ - OPT_UINT("timeout", 0, &nvme_args.timeout, \ - "timeout value, in milliseconds"), \ - OPT_FLAG("dry-run", 0, &nvme_args.dry_run, \ - "show command instead of executing"), \ - OPT_FLAG("no-retries", 0, &nvme_args.no_retries, \ - "disable retry logic on errors"), \ - OPT_FLAG("no-ioctl-probing", 0, &nvme_args.no_ioctl_probing, \ - "disable 64-bit IOCTL support probing"), \ - OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, \ - "output format version: 1|2"), \ - OPT_INCR("human-readable", 'H', &nvme_args.verbose, \ - NULL, NULL, true), \ - OPT_STRING("set-options", 0, "KEY=VALUE", \ - &nvme_args.set_options, \ - "set a libnvme library option (key=value[,key=value,...]);"), \ - OPT_END() \ +#define NVME_ARGS_OUTPUT_FORMATS(n, of_mask, of_desc, ...) \ + (void)(of_desc); \ + nvme_args_set_output_formats(of_mask); \ + struct argconfig_commandline_options n[] = { \ + OPT_GROUP("Options"), \ + ##__VA_ARGS__, \ + OPT_END() \ } #define NVME_ARGS(n, ...) \ @@ -88,6 +71,8 @@ enum nvme_print_flags { extern const char *uuid_index; extern const char *namespace_id_desired; +void nvme_args_register(void); +void nvme_args_set_output_formats(nvme_print_flags_t formats); int parse_args(int argc, char *argv[], const char *desc, struct argconfig_commandline_options *opts); int validate_output_format(const char *format, nvme_print_flags_t *flags); diff --git a/src/nvme.c b/src/nvme.c index a62689b581..eb46a7e133 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -182,6 +182,7 @@ int main(int argc, char **argv) return 0; } setlocale(LC_ALL, ""); + nvme_args_register(); err = nvme_load_global_config(); if (err) diff --git a/src/plugin.c b/src/plugin.c index b2e347cd22..5c33074c8c 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -400,9 +400,11 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) * look for global options before the sub command parser and * pre-fill the global nvme_args variable. */ - NVME_ARGS(global_opts, + OPT_ARGS(global_opts) = { OPT_FLAG("help", 'h', &opt_help, "show help text"), - OPT_FLAG("version", 'V', &opt_version, "show version")); + OPT_FLAG("version", 'V', &opt_version, "show version"), + OPT_END() + }; err = argconfig_parse_global(argc, argv, global_opts, &argind); if (err) { general_help(plugin, NULL); diff --git a/tests/cli/nvme_sed_test.py b/tests/cli/nvme_sed_test.py index 23bcc0e032..ecb99b2e83 100644 --- a/tests/cli/nvme_sed_test.py +++ b/tests/cli/nvme_sed_test.py @@ -416,6 +416,19 @@ def test_revert(self): self.assertEqual(revert_lsp['options'], _OPAL_PRESERVE) self._assert_key(revert_tpr, _PASSWORD) + def test_dry_run(self): + """--dry-run issues none of the Opal ioctls which change the drive. + initialize and password do not depend on Level 0 Discovery, which + is skipped in dry-run mode as well, so they reach the ioctls.""" + for cmd, passwords in ( + ('initialize', [_PASSWORD, _PASSWORD]), + ('password', [_PASSWORD, _NEW_PASSWORD, _NEW_PASSWORD])): + with self.subTest(cmd=cmd): + self.server.ioctls.clear() + self.server.passwords = passwords + self._sed(cmd, '--dry-run') + self.assertEqual(self.server.ioctls, []) + def test_revert_locked_drive_fails(self): self.server.l0 = _pack_l0(_LOCKING_SUPPORTED | _LOCKING_ENABLED | _LOCKING_LOCKED) From 1ecc7bed01b08242bc257aa558a1b333ea62ccca Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:39:42 +0000 Subject: [PATCH 054/263] argconfig: accept --help and --version in the global parse argconfig gives every command an implicit -h and --help, but before the command name handle_plugin() declared them itself, together with -V and --version. Accept -h, --help, -V and --version in argconfig_parse_global(), unless the table has its own. The parse goes on to the subcommand as before and reports them with ARGCONFIG_HELP and ARGCONFIG_VERSION. handle_plugin() needs no option table of its own anymore. Signed-off-by: Daniel Wagner --- src/argconfig.c | 50 +++++++++++++----- src/argconfig.h | 4 ++ src/plugin.c | 15 +++--- tests/unit/c/test-argconfig-parse.c | 79 +++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 19 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index d4d2f38bda..cc5e79e25b 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -367,20 +367,22 @@ struct argconfig_parser { int ind; struct argconfig_commandline_options *options; /* - * Global options stop at the first operand, the subcommand, and - * have no implicit help option. + * Global options stop at the first operand, the subcommand. Their + * implicit -h, --help, -V and --version are recorded and the parse + * goes on. */ bool global; + bool help; + bool version; char **operands; int nr_operands; }; /* - * Internal results of argconfig_parse_args(): the help was requested, or - * an option was malformed. Both print the help. + * Internal result of argconfig_parse_args(): an option was malformed. Like + * ARGCONFIG_HELP, it prints the help of a command. */ -#define ARGCONFIG_HELP 1 -#define ARGCONFIG_USAGE 2 +#define ARGCONFIG_USAGE 4 /* * The parse errors use the wording of glibc's getopt, which printed them @@ -461,10 +463,21 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) s = argconfig_find_long(argconfig_globals(p->options), name, len); if (!s) { - if (!p->global && len == 4 && !strncmp(name, "help", 4)) + bool help = len == 4 && !strncmp(name, "help", 4); + bool version = len == 7 && !strncmp(name, "version", 7); + + if (help && !p->global) return ARGCONFIG_HELP; - return argconfig_parse_error(p, "unrecognized option '%s'", - arg); + if (!p->global || (!help && !version)) + return argconfig_parse_error(p, + "unrecognized option '%s'", arg); + if (eq) + return argconfig_parse_error(p, + "option '--%.*s' doesn't allow an argument", + (int)len, name); + p->help |= help; + p->version |= version; + return 0; } switch (s->argument_type) { @@ -506,6 +519,11 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) if (!s && argconfig_globals(p->options)) s = argconfig_find_short(argconfig_globals(p->options), *c); + if (!s && p->global && (*c == 'h' || *c == 'V')) { + p->help |= *c == 'h'; + p->version |= *c == 'V'; + continue; + } if (!s) return argconfig_parse_error(p, "invalid option -- '%c'", *c); @@ -637,24 +655,32 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, * call, *argind is the index of the first non-option argument (the * subcommand), or argc if there is none. * - * Returns 0 on success, negative errno on failure. + * Unless the table has its own, -h, --help, -V and --version are accepted + * like a global option. Returns ARGCONFIG_HELP and ARGCONFIG_VERSION for + * them, 0 otherwise, or a negative errno on failure. @options may be NULL. */ int argconfig_parse_global(int argc, char *argv[], struct argconfig_commandline_options *options, int *argind) { + static struct argconfig_commandline_options no_options[] = { + OPT_END() + }; struct argconfig_parser p = { .argc = argc, .argv = argv, - .options = options, + .options = options ? options : no_options, .global = true, }; int ret; ret = argconfig_parse_args(&p); *argind = p.ind; + if (ret) + return ret > 0 ? -EINVAL : ret; - return ret > 0 ? -EINVAL : ret; + return (p.help ? ARGCONFIG_HELP : 0) | + (p.version ? ARGCONFIG_VERSION : 0); } bool argconfig_parse_seen(struct argconfig_commandline_options *s, diff --git a/src/argconfig.h b/src/argconfig.h index aced986eba..4c233f62ae 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -225,6 +225,10 @@ void argconfig_print_help(const char *program_desc, struct argconfig_commandline_options *options); int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_commandline_options *options); +/* argconfig_parse_global(): -h or --help, -V or --version was given */ +#define ARGCONFIG_HELP 1 +#define ARGCONFIG_VERSION 2 + int argconfig_parse_global(int argc, char *argv[], struct argconfig_commandline_options *options, int *argind); diff --git a/src/plugin.c b/src/plugin.c index 5c33074c8c..3f7f617360 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -400,16 +400,17 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) * look for global options before the sub command parser and * pre-fill the global nvme_args variable. */ - OPT_ARGS(global_opts) = { - OPT_FLAG("help", 'h', &opt_help, "show help text"), - OPT_FLAG("version", 'V', &opt_version, "show version"), - OPT_END() - }; - err = argconfig_parse_global(argc, argv, global_opts, &argind); - if (err) { + /* + * -h and --help show the help of the command which follows, if any, + * -V and --version replace the command. + */ + err = argconfig_parse_global(argc, argv, NULL, &argind); + if (err < 0) { general_help(plugin, NULL); return err; } + opt_help = err & ARGCONFIG_HELP; + opt_version = err & ARGCONFIG_VERSION; argc -= argind; argv += argind; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index a243e6ed19..78b8614dd4 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -736,6 +736,84 @@ static void global_parse_missing_arg_test(void) } } +/* + * The global parse accepts -h, --help, -V and --version unless the table + * has its own, reports them with ARGCONFIG_HELP and ARGCONFIG_VERSION and + * still parses the other options up to the subcommand. + */ +static void global_parse_help_test(void) +{ + bool own_help = false; + int verbose = 0, argind, ret; + + OPT_ARGS(opts) = { + OPT_INCR("verbose", 'v', &verbose, "increase verbosity"), + OPT_END() + }; + OPT_ARGS(help_opts) = { + OPT_FLAG("help", 'h', &own_help, "help"), + OPT_END() + }; + + { + char **argv = (char *[]){ "prog", "-h", "-v", "list", NULL }; + + ret = argconfig_parse_global(argv_len(argv), argv, opts, + &argind); + if (ret != ARGCONFIG_HELP || argind != 3 || verbose != 1) { + printf("ERROR: global help: -h -v list\n"); + test_rc = 1; + } + } + + { + char **argv = (char *[]){ "prog", "-vh", "--help", NULL }; + + verbose = 0; + ret = argconfig_parse_global(argv_len(argv), argv, opts, + &argind); + if (ret != ARGCONFIG_HELP || argind != 3 || verbose != 1) { + printf("ERROR: global help: -vh --help\n"); + test_rc = 1; + } + } + + { + char **argv = (char *[]){ "prog", "-h", "list", NULL }; + + ret = argconfig_parse_global(argv_len(argv), argv, help_opts, + &argind); + if (ret || argind != 2 || !own_help) { + printf("ERROR: global help: own -h of the table\n"); + test_rc = 1; + } + } + + { + char **argv = (char *[]){ "prog", "-vV", "list", NULL }; + + verbose = 0; + ret = argconfig_parse_global(argv_len(argv), argv, opts, + &argind); + if (ret != ARGCONFIG_VERSION || argind != 2 || verbose != 1) { + printf("ERROR: global version: -vV list\n"); + test_rc = 1; + } + } + + { + char **argv = (char *[]){ "prog", "--help", "--version", NULL }; + + ret = argconfig_parse_global(argv_len(argv), argv, NULL, + &argind); + if (ret != (ARGCONFIG_HELP | ARGCONFIG_VERSION) || + argind != 3) { + printf("ERROR: global version: --help --version\n"); + test_rc = 1; + } + } +} + int main(void) { unsigned int i; @@ -764,6 +842,7 @@ int main(void) do_global_parse_test(&global_parse_tests[i]); global_parse_missing_arg_test(); + global_parse_help_test(); if (f) fclose(f); From a1861c0d41423dac59e3f80eb93f2efa7126065e Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:14:34 +0000 Subject: [PATCH 055/263] nvme: check the output format in parse_args() Nearly every command checks --output-format against the formats it supports with the same block of code, and the commands which don't use the output format accept any value. Check it once in parse_args(), which all commands use, and keep the result in nvme_args.output_flags. An invalid value is rejected by every command now, also by those which ignore the output format, and before the device is opened. The netapp smdevices and ontapdevices commands accept "column", which is none of the common formats. OUTPUT_FORMATS_CUSTOM lets a command parse --output-format itself, with its own help text; their help listed the default formats so far. The micron cloud commands test now expects an invalid value to be rejected. Signed-off-by: Daniel Wagner --- plugins/netapp/netapp-nvme.c | 12 +++++++-- src/args.c | 26 ++++++++++++++++--- src/args.h | 14 +++++++--- .../micron/micron_cloud_commands_mock_test.py | 25 ++++++++---------- 4 files changed, 55 insertions(+), 22 deletions(-) diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 209c686441..12e0236248 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -934,6 +934,12 @@ static int netapp_nvme_filter(const struct dirent *d) return 0; } +#ifdef CONFIG_JSONC +#define NETAPP_OUTPUT_FORMAT_DESC "Output format: normal|column|json" +#else /* CONFIG_JSONC */ +#define NETAPP_OUTPUT_FORMAT_DESC "Output format: normal|column" +#endif /* CONFIG_JSONC */ + static int netapp_output_format(char *format) { if (!format) @@ -963,7 +969,8 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, int num_smdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS(opts); + NVME_ARGS_OUTPUT_FORMATS(opts, OUTPUT_FORMATS_CUSTOM, + NETAPP_OUTPUT_FORMAT_DESC); ret = parse_args(argc, argv, desc, opts); if (ret < 0) @@ -1060,7 +1067,8 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, int num_ontapdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS(opts); + NVME_ARGS_OUTPUT_FORMATS(opts, OUTPUT_FORMATS_CUSTOM, + NETAPP_OUTPUT_FORMAT_DESC); ret = parse_args(argc, argv, desc, opts); if (ret < 0) diff --git a/src/args.c b/src/args.c index ba79ca2b4f..db6892dd4e 100644 --- a/src/args.c +++ b/src/args.c @@ -53,14 +53,23 @@ static struct argconfig_commandline_options global_options[] = { /* Register the options every command accepts */ void nvme_args_register(void) { - nvme_args_set_output_formats(DEFAULT_OUTPUT_FORMATS); + nvme_args_set_output_formats(DEFAULT_OUTPUT_FORMATS, NULL); argconfig_set_global_options(global_options); } -/* The output formats the current command supports */ -void nvme_args_set_output_formats(nvme_print_flags_t formats) +/* + * The output formats the current command supports. The help text lists + * them, a command which parses --output-format itself describes its own. + */ +void nvme_args_set_output_formats(nvme_print_flags_t formats, + const char *custom_desc) { nvme_args.supported_output_formats = formats; + if (formats & OUTPUT_FORMATS_CUSTOM) { + snprintf(output_format_help, sizeof(output_format_help), "%s", + custom_desc); + return; + } snprintf(output_format_help, sizeof(output_format_help), "Output format: normal%s%s%s", formats & JSON ? "|json" : "", @@ -80,6 +89,17 @@ int parse_args(int argc, char *argv[], const char *desc, if (ret) return ret; + if (nvme_args.supported_output_formats & OUTPUT_FORMATS_CUSTOM) { + nvme_args.output_flags = NORMAL; + } else { + ret = validate_output_format(nvme_args.output_format, + &nvme_args.output_flags); + if (ret < 0) { + nvme_show_error("Invalid output format"); + return ret; + } + } + nvme_show_init(); return 0; diff --git a/src/args.h b/src/args.h index 52c0b0bf20..b00bdd2015 100644 --- a/src/args.h +++ b/src/args.h @@ -25,6 +25,8 @@ struct nvme_args { bool no_ioctl_probing; unsigned int output_format_ver; nvme_print_flags_t supported_output_formats; + /* --output-format of the current command, set by parse_args() */ + nvme_print_flags_t output_flags; const char *set_options; }; extern struct nvme_args nvme_args; @@ -38,6 +40,12 @@ enum nvme_print_flags { TABULAR = 1 << 4, /* prints aligned columns for easy reading */ }; +/* + * The command parses --output-format itself, for names other than the + * ones above. parse_args() does not check it then. + */ +#define OUTPUT_FORMATS_CUSTOM (1U << 31) + #ifdef CONFIG_JSONC #define DEFAULT_OUTPUT_FORMATS (NORMAL | JSON | BINARY) #define DEFAULT_OUTPUT_FORMAT_DESC "Output format: normal|json|binary" @@ -54,8 +62,7 @@ enum nvme_print_flags { * --output-format and its help text. */ #define NVME_ARGS_OUTPUT_FORMATS(n, of_mask, of_desc, ...) \ - (void)(of_desc); \ - nvme_args_set_output_formats(of_mask); \ + nvme_args_set_output_formats(of_mask, of_desc); \ struct argconfig_commandline_options n[] = { \ OPT_GROUP("Options"), \ ##__VA_ARGS__, \ @@ -72,7 +79,8 @@ extern const char *uuid_index; extern const char *namespace_id_desired; void nvme_args_register(void); -void nvme_args_set_output_formats(nvme_print_flags_t formats); +void nvme_args_set_output_formats(nvme_print_flags_t formats, + const char *custom_desc); int parse_args(int argc, char *argv[], const char *desc, struct argconfig_commandline_options *opts); int validate_output_format(const char *format, nvme_print_flags_t *flags); diff --git a/tests/cli/micron/micron_cloud_commands_mock_test.py b/tests/cli/micron/micron_cloud_commands_mock_test.py index f167c833fd..d4ee8ef6ae 100644 --- a/tests/cli/micron/micron_cloud_commands_mock_test.py +++ b/tests/cli/micron/micron_cloud_commands_mock_test.py @@ -104,21 +104,18 @@ def assert_customer_id_gate(self, command): result.stderr) def assert_output_format_ignored(self, command): - """A command declaring no output formats must ignore the flag.""" + """A command without format-dependent output ignores a valid + --output-format. An invalid one is rejected like everywhere.""" baseline = self.run_plugin_cmd_check(command) - for value in ("binary", "notaformat"): - with self.subTest(value=value): - result = self.run_plugin_cmd(command, - args=f"--output-format={value}") - - self.assertNotIn( - "Invalid output format", result.stderr + result.stdout, - f"micron {command} does not validate --output-format but " - f"rejected {value!r}") - self.assertEqual( - result.stdout, baseline.stdout, - f"micron {command} output changed for " - f"--output-format={value}") + result = self.run_plugin_cmd(command, args="--output-format=binary") + self.assertNotIn( + "Invalid output format", result.stderr + result.stdout, + f"micron {command} rejected --output-format=binary") + self.assertEqual( + result.stdout, baseline.stdout, + f"micron {command} output changed for --output-format=binary") + + self.check_output_format_rejected(command, "notaformat") class TestMicronCloudBootVersion(CloudTestBase): From 1b39a7832dec1201c74fc95da5ae5c79f4dccdca Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:16:57 +0000 Subject: [PATCH 056/263] treewide: drop the output format checks of the commands parse_args() checks --output-format against the formats the command supports, so the same check in the commands can no longer fail. Replace it with the result in nvme_args.output_flags, and drop it where the command never used the result. The blocks of the common shape were replaced with a script; five which differed only in their error message or layout by hand. The checks which narrow the formats further (flags != JSON && flags != NORMAL) remain until the commands declare their formats. Signed-off-by: Daniel Wagner --- plugins/dapustor/dapustor-nvme.c | 6 +- plugins/fdp/fdp.c | 16 +- plugins/feat/feat-nvme.c | 6 +- plugins/fw/fw-plugin.c | 14 -- plugins/id/id-plugin.c | 84 ++------ plugins/intel/intel-nvme.c | 12 +- plugins/lm/lm-nvme.c | 12 +- plugins/log/log-plugin.c | 193 +++--------------- plugins/micron/micron-nvme.c | 72 ++----- plugins/nbft/nbft-plugin.c | 4 +- plugins/ns/ns-plugin.c | 28 --- plugins/ocp/ocp-fw-activation-history.c | 7 +- plugins/ocp/ocp-hardware-component-log.c | 6 +- plugins/ocp/ocp-nvme.c | 6 +- plugins/resv/resv-plugin.c | 27 +-- plugins/scaleflux/sfx-nvme.c | 12 +- plugins/seagate/seagate-nvme.c | 30 +-- plugins/security/security-plugin.c | 14 -- .../solidigm/solidigm-garbage-collection.c | 7 +- plugins/solidigm/solidigm-latency-tracking.c | 7 +- plugins/solidigm/solidigm-log-page-dir.c | 7 +- plugins/solidigm/solidigm-smart.c | 7 +- plugins/wdc/wdc-nvme.c | 15 +- plugins/zns/zns.c | 21 +- src/fabrics.c | 18 +- src/nvme-cmds-ctrl.c | 7 - src/nvme-cmds-discovery.c | 7 - src/nvme-cmds-enumerate.c | 6 +- src/nvme-cmds-feature.c | 13 +- src/nvme-cmds-io.c | 20 +- src/nvme-cmds-passthru.c | 7 - src/nvme-cmds-registers.c | 25 +-- src/nvme-cmds-sanitize.c | 27 +-- src/nvme.c | 6 +- 34 files changed, 100 insertions(+), 649 deletions(-) diff --git a/plugins/dapustor/dapustor-nvme.c b/plugins/dapustor/dapustor-nvme.c index 1512c4f410..57b4246e7b 100644 --- a/plugins/dapustor/dapustor-nvme.c +++ b/plugins/dapustor/dapustor-nvme.c @@ -535,11 +535,7 @@ static int dapustor_additional_smart_log(int argc, char **argv, struct command * if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = dapustor_additional_smart_log_data(hdl, &smart_log, &ext_smart_log, &has_ext); diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index 586147598b..eced56d3d4 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -57,9 +57,7 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -129,9 +127,7 @@ static int fdp_usage(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -254,9 +250,7 @@ static int fdp_events(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -313,9 +307,7 @@ static int fdp_status(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index d7dda3f734..ac0c8ca2da 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -172,11 +172,7 @@ static int feat_get_nsid(struct libnvme_transport_handle *hdl, __u32 nsid, return err; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; diff --git a/plugins/fw/fw-plugin.c b/plugins/fw/fw-plugin.c index cd7ba532cb..bd39857055 100644 --- a/plugins/fw/fw-plugin.c +++ b/plugins/fw/fw-plugin.c @@ -170,7 +170,6 @@ static int fw_download(int argc, char **argv, struct command *acmd, struct plugi void *fw_buf; struct nvme_id_ctrl ctrl = { 0 }; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; struct config { char *fw; @@ -205,12 +204,6 @@ static int fw_download(int argc, char **argv, struct command *acmd, struct plugi if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - fw_fd = shr_open_rawdata(cfg.fw, O_RDONLY); cfg.offset <<= 2; if (fw_fd < 0) { @@ -399,7 +392,6 @@ static int fw_commit(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; int err; - nvme_print_flags_t flags; bool mud_supported; struct config { @@ -440,12 +432,6 @@ static int fw_commit(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.slot > 7) { nvme_show_error("invalid slot:%d", cfg.slot); return -EINVAL; diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index d48ebd8124..3df5323d32 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -194,11 +194,7 @@ static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -298,11 +294,7 @@ static int nvm_id_ctrl(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -356,11 +348,7 @@ static int nvm_id_ns(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -434,11 +422,7 @@ static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, str if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -505,11 +489,7 @@ static int ns_descs(int argc, char **argv, struct command *acmd, struct plugin * if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -581,11 +561,7 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -658,11 +634,7 @@ static int cmd_set_independent_id_ns(int argc, char **argv, struct command *acmd if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -715,11 +687,7 @@ static int id_ns_granularity(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; granularity_list = libnvme_alloc(NVME_IDENTIFY_DATA_SIZE); if (!granularity_list) @@ -767,11 +735,7 @@ static int id_nvmset(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; nvmset = libnvme_alloc(sizeof(*nvmset)); if (!nvmset) @@ -819,11 +783,7 @@ static int id_uuid(int argc, char **argv, struct command *acmd, struct plugin *p if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -876,11 +836,7 @@ static int id_iocs(int argc, char **argv, struct command *acmd, struct plugin *p if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -931,11 +887,7 @@ static int id_domain(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; id_domain = libnvme_alloc(sizeof(*id_domain)); if (!id_domain) @@ -984,11 +936,7 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -1041,11 +989,7 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *acmd, stru if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!cfg.num_entries) { nvme_show_error("non-zero num-entries is required param"); diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index f8a1d7f36c..56d1d387b0 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -371,11 +371,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = nvme_get_log_simple(hdl, 0xca, &smart_log, sizeof(smart_log)); if (!err) { @@ -1067,11 +1063,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; /* For optate, latency stats are deleted every time their LID is pulled. * Therefore, we query the longest lat_stats log page first. diff --git a/plugins/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index cbecbc366b..99d1ac6e33 100644 --- a/plugins/lm/lm-nvme.c +++ b/plugins/lm/lm-nvme.c @@ -442,11 +442,7 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.output_format && cfg.offset != 0 && !(flags & BINARY)) { nvme_show_error("cannot parse non-zero offset"); @@ -576,11 +572,7 @@ static int lm_get_cdq(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; struct nvme_lm_ctrl_data_queue_fid_data data; diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 9859daa8e7..67aa3482ef 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -90,11 +90,7 @@ static int get_supported_log_pages(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -148,11 +144,7 @@ static int get_smart_log(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -209,11 +201,7 @@ static int get_ana_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; ctrl = libnvme_alloc(sizeof(*ctrl)); if (!ctrl) @@ -466,7 +454,6 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, struct libnvme_passthru_cmd cmd; int err = 0; size_t total_size = 0; - nvme_print_flags_t flags; bool da4_support = false, host_behavior_changed = false; @@ -501,12 +488,6 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!cfg.file_name) { nvme_show_error("Please provide an output file!"); return -EINVAL; @@ -628,11 +609,7 @@ static int get_endurance_log(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; endurance_log = libnvme_alloc(sizeof(*endurance_log)); if (!endurance_log) @@ -708,11 +685,7 @@ static int get_effects_log(int argc, char **argv, struct command *acmd, struct p if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -816,11 +789,7 @@ static int get_error_log(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -886,11 +855,7 @@ static int get_fw_log(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -943,11 +908,7 @@ static int get_changed_ns_list_log(int argc, char **argv, bool alloc) if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1022,11 +983,7 @@ static int get_pred_lat_per_nvmset_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1086,11 +1043,7 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1171,11 +1124,7 @@ static int get_persistent_event_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1283,11 +1232,7 @@ static int get_endurance_event_agg_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1360,11 +1305,7 @@ static int get_lba_status_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; nvme_init_get_log_lba_status(&cmd, 0, &lslplen, sizeof(__u32)); err = libnvme_get_log(hdl, &cmd, false, sizeof(__u32)); @@ -1411,11 +1352,7 @@ static int get_resv_notif_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; resv = libnvme_alloc(sizeof(*resv)); if (!resv) @@ -1470,11 +1407,7 @@ static int get_boot_part_log(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!cfg.file_name) { nvme_show_error("Please provide an output file!"); @@ -1563,11 +1496,7 @@ static int get_phy_rx_eom_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.lsp > 127) { nvme_show_error("invalid lsp param: %u", cfg.lsp); @@ -1710,11 +1639,7 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1836,11 +1761,7 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1887,11 +1808,7 @@ static int sanitize_log(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -1934,11 +1851,7 @@ static int get_fid_support_effects_log(int argc, char **argv, struct command *ac if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -1979,11 +1892,7 @@ static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -2033,11 +1942,7 @@ static int self_test_log(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -2076,11 +1981,7 @@ static int get_mgmt_addr_list_log(int argc, char **argv, struct command *acmd, s if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; ma_log = libnvme_alloc(sizeof(*ma_log)); if (!ma_log) @@ -2125,11 +2026,7 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; info = libnvme_alloc(sizeof(*info)); if (!info) @@ -2213,11 +2110,7 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_dispersed_ns_psub(hdl, cfg.namespace_id, &log); if (err) { @@ -2261,11 +2154,7 @@ static int get_power_measurement_log(int argc, char **argv, struct command *acmd if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; @@ -2445,11 +2334,7 @@ static int get_reachability_groups_log(int argc, char **argv, struct command *ac if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_reachability_groups(hdl, cfg.rgo, cfg.rae, &log, &len); if (err) { @@ -2558,11 +2443,7 @@ static int get_reachability_associations_log(int argc, char **argv, struct comma if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_reachability_associations(hdl, cfg.rao, cfg.rae, &log, &len); if (err) { @@ -2641,11 +2522,7 @@ static int get_host_discovery_log(int argc, char **argv, struct command *acmd, s if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_host_discovery(hdl, cfg.allhoste, cfg.rae, &log); if (err) { @@ -2718,11 +2595,7 @@ static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, st if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_ave_discovery(hdl, cfg.rae, &log); if (err) { @@ -2796,11 +2669,7 @@ static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acm if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = get_pull_model_ddc_req(hdl, cfg.rae, &log); if (err) { diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index cfd4f45e5e..1bcb6b79d0 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -790,11 +790,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log); @@ -924,11 +920,7 @@ static int micron_pcie_stats(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; /* pull log details based on the model name */ @@ -1667,11 +1659,7 @@ static int micron_nand_stats(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; /* pull log details based on the model name */ @@ -1796,11 +1784,7 @@ static int micron_smart_ext_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; if (eModel == M51CX || eModel == M51BY || eModel == M51CY || eModel == M6003 || @@ -1842,11 +1826,7 @@ static int micron_work_load_log(int argc, char **argv, struct command *acmd, str if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; if (eModel == M6001 || eModel == M6004 || eModel == M6003) { @@ -1886,11 +1866,7 @@ static int micron_vendor_telemetry_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; if (eModel == M6001 || eModel == M6004 || eModel == M6003) { @@ -2632,11 +2608,7 @@ static int micron_drive_info(int argc, char **argv, struct command *acmd, return -1; } - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; if (model == M5407) { @@ -2966,11 +2938,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *a if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; /* check if product supports fw_history log */ @@ -3346,11 +3314,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *a if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; /* For M5410 and M5407, this option prints 0xFB log page */ @@ -4235,11 +4199,7 @@ static int micron_cloud_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; /* check for models that support 0xC0 log */ @@ -4475,11 +4435,7 @@ static int micron_health_info(int argc, char **argv, struct command *acmd, if (eModel == UNKNOWN_MODEL) fprintf(stderr, "WARNING: Unknown drive model\n"); - err = validate_output_format(nvme_args.output_format, &format); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + format = nvme_args.output_flags; is_json = format == JSON; err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log); @@ -4549,11 +4505,7 @@ static int micron_id_ctrl(int argc, char **argv, struct command *acmd, fprintf(stderr, "WARNING: Drive not recognized as Micron, proceeding anyway\n"); - err = validate_output_format(nvme_args.output_format, &flags); - if (err) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; nvme_init_identify_ctrl(&cmd, &ctrl); err = libnvme_exec_admin_passthru(hdl, &cmd); diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index c3ac480041..4e100a8456 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -674,9 +674,7 @@ int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin if (ret) return ret; - ret = validate_output_format(nvme_args.output_format, &flags); - if (ret < 0) - return ret; + flags = nvme_args.output_flags; ret = nvme_create_global_ctx(&ctx); if (ret) diff --git a/plugins/ns/ns-plugin.c b/plugins/ns/ns-plugin.c index d1fd93eba7..b5efc534f9 100644 --- a/plugins/ns/ns-plugin.c +++ b/plugins/ns/ns-plugin.c @@ -76,7 +76,6 @@ static int delete_ns(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err; struct config { @@ -97,12 +96,6 @@ static int delete_ns(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { @@ -132,7 +125,6 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s __cleanup_libnvme_free struct nvme_ctrl_list *cntlist = NULL; __u16 list[NVME_ID_CTRL_LIST_MAX]; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err, num; const char *namespace_id = "namespace to attach"; @@ -159,12 +151,6 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (libnvme_transport_handle_is_ns(hdl)) { nvme_show_error("%s: a namespace device opened (dev: %s, nsid: %d)", acmd->name, libnvme_transport_handle_get_name(hdl), cfg.nsid); @@ -366,7 +352,6 @@ static int create_ns(int argc, char **argv, struct command *acmd, struct plugin __u64 align_ncap = align_nsze; struct libnvme_passthru_cmd cmd; uint16_t phndl[128] = { 0, }; - nvme_print_flags_t flags; uint16_t num_phandle; int err = 0, i; __u32 nsid; @@ -443,12 +428,6 @@ static int create_ns(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.flbas != 0xff && cfg.bs != 0x00) { nvme_show_error( "Invalid specification of both FLBAS and Block Size, please specify only one"); @@ -606,7 +585,6 @@ static int get_ns_id(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; unsigned int nsid; int err; - nvme_print_flags_t flags; NVME_ARGS(opts); @@ -614,12 +592,6 @@ static int get_ns_id(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - err = libnvme_get_nsid(hdl, &nsid); if (err < 0) { nvme_show_error("get namespace ID: %s", libnvme_strerror(-err)); diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index 40ab955443..c7a5854c23 100644 --- a/plugins/ocp/ocp-fw-activation-history.c +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -71,12 +71,7 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *acmd, if (!err) { nvme_print_flags_t print_flag; - err = validate_output_format(nvme_args.output_format, - &print_flag); - if (err < 0) { - nvme_show_error("Error: Invalid output format."); - return err; - } + print_flag = nvme_args.output_flags; ocp_fw_act_history(&fw_history, print_flag); } diff --git a/plugins/ocp/ocp-hardware-component-log.c b/plugins/ocp/ocp-hardware-component-log.c index 1e14b0f4ed..b8e1b054b3 100644 --- a/plugins/ocp/ocp-hardware-component-log.c +++ b/plugins/ocp/ocp-hardware-component-log.c @@ -264,11 +264,7 @@ static int get_hwcomp_log(struct libnvme_transport_handle *hdl, __u32 id, bool l .desc = NULL, }; - ret = validate_output_format(nvme_args.output_format, &fmt); - if (ret < 0) { - nvme_show_error("error: ocp: invalid output format"); - return ret; - } + fmt = nvme_args.output_flags; ret = get_hwcomp_log_data(hdl, &log); if (ret) { diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 3e8257fc2d..881dfbc6e5 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -2488,11 +2488,7 @@ static int ocp_get_persistent_event_log(int argc, char **argv, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; diff --git a/plugins/resv/resv-plugin.c b/plugins/resv/resv-plugin.c index f4454db679..71cabb6c48 100644 --- a/plugins/resv/resv-plugin.c +++ b/plugins/resv/resv-plugin.c @@ -40,7 +40,6 @@ static int resv_acquire(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; __le64 payload[2]; int err; @@ -74,12 +73,6 @@ static int resv_acquire(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { @@ -117,7 +110,6 @@ static int resv_register(int argc, char **argv, struct command *acmd, struct plu __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; __le64 payload[2]; int err; @@ -150,12 +142,6 @@ static int resv_register(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { @@ -202,7 +188,6 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; __le64 payload[1]; int err; @@ -233,12 +218,6 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!cfg.nsid) { err = libnvme_get_nsid(hdl, &cfg.nsid); if (err < 0) { @@ -329,11 +308,7 @@ static int resv_report(int argc, char **argv, struct command *acmd, struct plugi if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index baa5c2982c..ed465493ff 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -356,11 +356,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; nvme_init_get_log(&cmd, cfg.namespace_id, 0xca, NVME_CSI_NVM, (void *)&smart_log, sizeof(smart_log)); @@ -1550,11 +1546,7 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; //Calculate formatted capacity, not concerned with errors, we may have a char device if (libnvme_transport_handle_is_ns(hdl) && diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index c1777b4b72..615684d44c 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -164,11 +164,7 @@ static int log_pages_supp(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = nvme_get_log_simple(hdl, 0xc5, &logPageMap, sizeof(logPageMap)); if (!err) { @@ -919,11 +915,7 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug return -1; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (flags & JSON) printf("Seagate Extended SMART Information :\n"); @@ -1099,11 +1091,7 @@ static int temp_stats(int argc, char **argv, struct command *acmd, struct plugin return -1; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate Temperature Stats Information :\n"); @@ -1262,11 +1250,7 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *acmd, struct return -1; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate PCIe error counters Information :\n"); @@ -1399,11 +1383,7 @@ static int stx_vs_fw_activate_history(int argc, char **argv, struct command *acm return -1; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate FW Activation History Information :\n"); diff --git a/plugins/security/security-plugin.c b/plugins/security/security-plugin.c index 12c7ce2a7b..d4bd656722 100644 --- a/plugins/security/security-plugin.c +++ b/plugins/security/security-plugin.c @@ -50,7 +50,6 @@ static int sec_send(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_fd int sec_fd = -1; unsigned int sec_size; int err; - nvme_print_flags_t flags; struct config { bool ish; @@ -85,12 +84,6 @@ static int sec_send(int argc, char **argv, struct command *acmd, struct plugin * if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.tl == 0) { nvme_show_error("--tl unspecified or zero"); return -EINVAL; @@ -173,7 +166,6 @@ static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_libnvme_free void *sec_buf = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err; struct config { @@ -212,12 +204,6 @@ static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin * if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.size) { sec_buf = libnvme_alloc(cfg.size); if (!sec_buf) diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c index 54d46cd64c..7dedb1678b 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -84,12 +84,7 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *a if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err) { - nvme_show_error("Invalid output format '%s'", - nvme_args.output_format); - return -EINVAL; - } + flags = nvme_args.output_flags; sldgm_get_uuid_index(hdl, &uuid_index); diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 8a7fb5941e..01bd912695 100644 --- a/plugins/solidigm/solidigm-latency-tracking.c +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -376,12 +376,7 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *acm lt.hdl = hdl; - err = validate_output_format(nvme_args.output_format, <.print_flags); - if (err < 0) { - nvme_show_error("Invalid output format '%s'", - nvme_args.output_format); - return -EINVAL; - } + lt.print_flags = nvme_args.output_flags; if (lt.cfg.type > 0xf) { nvme_show_error("Invalid Log type value '%d'", lt.cfg.type); diff --git a/plugins/solidigm/solidigm-log-page-dir.c b/plugins/solidigm/solidigm-log-page-dir.c index 0d7c8d552a..81459545e6 100644 --- a/plugins/solidigm/solidigm-log-page-dir.c +++ b/plugins/solidigm/solidigm-log-page-dir.c @@ -247,12 +247,7 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *a if (!err) { nvme_print_flags_t print_flag; - err = validate_output_format(nvme_args.output_format, &print_flag); - if (err) { - nvme_show_error("Error: Invalid output format specified: %s.", - nvme_args.output_format); - return err; - } + print_flag = nvme_args.output_flags; if (print_flag == NORMAL) { supported_log_pages_normal(lid_dirs); diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 82ee827e07..3f85fd490e 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -277,12 +277,7 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *acm if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format '%s'", - nvme_args.output_format); - return err; - } + flags = nvme_args.output_flags; sldgm_get_uuid_index(hdl, &uuid_index); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index f5938b3b6a..530d846e01 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -8525,13 +8525,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *acmd, "ERROR: WDC: Failure reading the C0 Log Page, ret = %d\n", ret); } else { - ret = validate_output_format(nvme_args.output_format, - &fmt); - if (ret < 0) { - nvme_show_error("Invalid output format: %s", - nvme_args.output_format); - goto out; - } + fmt = nvme_args.output_flags; ret = nvme_get_print_ocp_cloud_smart_log(hdl, 0, @@ -8611,12 +8605,7 @@ static int wdc_cu_smart_log(int argc, char **argv, struct command *acmd, if (!wdc_check_device(ctx, hdl)) return -1; - ret = validate_output_format(nvme_args.output_format, &fmt); - - if (ret < 0) { - nvme_show_error("ERROR: WDC: invalid output format"); - return ret; - } + fmt = nvme_args.output_flags; /* verify the 0xCA log page is supported */ if (wdc_nvme_check_supported_log_page(ctx, hdl, diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 68bc877653..31bedfd9cd 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -154,9 +154,7 @@ static int id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *p if (err) return errno; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; nvme_init_zns_identify_ctrl(&cmd, &ctrl); err = libnvme_exec_admin_passthru(hdl, &cmd); @@ -201,9 +199,7 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu if (err) return errno; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.vendor_specific) flags |= VS; if (cfg.human_readable) @@ -653,7 +649,6 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *acmd, struct pl __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; void *data = NULL; int err = -1; @@ -680,10 +675,6 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *acmd, struct pl if (err) return errno; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; - if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { @@ -776,9 +767,7 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug if (err) return errno; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (cfg.verbose) flags |= VERBOSE; @@ -1097,9 +1086,7 @@ static int changed_zone_list(int argc, char **argv, struct command *acmd, struct if (err) return errno; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) - return err; + flags = nvme_args.output_flags; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); diff --git a/src/fabrics.c b/src/fabrics.c index 02f3f6512b..a6ac8237f3 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -899,11 +899,7 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) load_nvme_fabrics_module(); - ret = validate_output_format(nvme_args.output_format, &flags); - if (ret < 0) { - nvme_show_error("Invalid output format"); - return ret; - } + flags = nvme_args.output_flags; persistent = nvmf_resolve_persistent_arg(opts, persistent_arg); @@ -1098,11 +1094,7 @@ int fabrics_connect(const char *desc, int argc, char **argv) load_nvme_fabrics_module(); - ret = validate_output_format(nvme_args.output_format, &flags); - if (ret < 0) { - nvme_show_error("Invalid output format"); - return ret; - } + flags = nvme_args.output_flags; if (config_file && !strcmp(config_file, "none")) config_file = NULL; @@ -1677,11 +1669,7 @@ int fabrics_config_show(const char *desc, int argc, char **argv) if (ret) return ret; - ret = validate_output_format(nvme_args.output_format, &flags); - if (ret < 0) { - nvme_show_error("Invalid output format"); - return ret; - } + flags = nvme_args.output_flags; ret = nvme_create_global_ctx(&ctx); if (ret) diff --git a/src/nvme-cmds-ctrl.c b/src/nvme-cmds-ctrl.c index 7c3466581e..1d8cab9c37 100644 --- a/src/nvme-cmds-ctrl.c +++ b/src/nvme-cmds-ctrl.c @@ -162,7 +162,6 @@ static int ns_rescan(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - nvme_print_flags_t flags; NVME_ARGS(opts); @@ -175,12 +174,6 @@ static int ns_rescan(int argc, char **argv, struct command *acmd, struct plugin return -EINVAL; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - err = libnvme_rescan_ns(hdl); if (err < 0) nvme_show_error("Namespace Rescan: %s\n", libnvme_strerror(-err)); diff --git a/src/nvme-cmds-discovery.c b/src/nvme-cmds-discovery.c index 54c5e552d6..baeca3fc76 100644 --- a/src/nvme-cmds-discovery.c +++ b/src/nvme-cmds-discovery.c @@ -53,7 +53,6 @@ static int get_log(int argc, char **argv, struct command *acmd, struct plugin *p __cleanup_libnvme_free unsigned char *log = NULL; struct libnvme_passthru_cmd cmd; int err; - nvme_print_flags_t flags; struct config { bool ish; @@ -156,12 +155,6 @@ static int get_log(int argc, char **argv, struct command *acmd, struct plugin *p if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.aen) { cfg.log_len = 4096; cfg.log_id = (cfg.aen >> 16) & 0xff; diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index de5e564248..5b1d3fb410 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -125,11 +125,7 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index 1bddf1bdf2..c00f3a2108 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -275,11 +275,7 @@ static int get_feature(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!argconfig_parse_seen(opts, "namespace-id")) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -336,7 +332,6 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi __cleanup_fd int ffd = STDIN_FILENO; int err; __u64 result; - nvme_print_flags_t flags; struct config { __u32 nsid; @@ -373,12 +368,6 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!argconfig_parse_seen(opts, "namespace-id")) { err = libnvme_get_nsid(hdl, &cfg.nsid); if (err < 0) { diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 11203a43ed..9988d76051 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -577,7 +577,6 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi __u32 ctx_attrs[NVME_DSM_MAX_RANGES] = {0,}; __u32 nlbs[NVME_DSM_MAX_RANGES] = {0,}; __u64 slbas[NVME_DSM_MAX_RANGES] = {0,}; - nvme_print_flags_t flags; uint16_t nc, nb, ns; int err; @@ -621,12 +620,6 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - nc = shr_parse_csv_u32(cfg.ctx_attrs, ctx_attrs, ARRAY_SIZE(ctx_attrs)); nb = shr_parse_csv_u32(cfg.blocks, nlbs, ARRAY_SIZE(nlbs)); ns = shr_parse_csv_u64(cfg.slbas, slbas, ARRAY_SIZE(slbas)); @@ -1458,11 +1451,7 @@ static int get_lba_status(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (!cfg.atype) { nvme_show_error("action type (--action) has to be given"); @@ -1510,7 +1499,6 @@ static int capacity_mgmt(int argc, char **argv, struct command *acmd, struct plu __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; int err = -1; - nvme_print_flags_t flags; struct config { bool ish; @@ -1539,12 +1527,6 @@ static int capacity_mgmt(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.operation > 0xf) { nvme_show_error("invalid operation field: %u", cfg.operation); return -1; diff --git a/src/nvme-cmds-passthru.c b/src/nvme-cmds-passthru.c index fd752e2cde..71605c36a1 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -113,7 +113,6 @@ static int passthru(int argc, char **argv, bool admin, int err = 0; const char *cmd_name = NULL; struct timeval start_time, end_time; - nvme_print_flags_t flags_t; struct passthru_config cfg = { .opcode = 0, @@ -168,12 +167,6 @@ static int passthru(int argc, char **argv, bool admin, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags_t); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!argconfig_parse_seen(opts, "opcode")) { nvme_show_error("%s: opcode parameter required", acmd->name); return -EINVAL; diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index b6e66129e0..bc2c124d79 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -162,11 +162,7 @@ static int show_registers(int argc, char **argv, struct command *acmd, struct pl return -EINVAL; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -434,11 +430,7 @@ static int get_register(int argc, char **argv, struct command *acmd, struct plug return -EINVAL; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -782,11 +774,7 @@ static int get_property(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.offset == -1) { nvme_show_error("offset required param"); @@ -813,7 +801,6 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - nvme_print_flags_t flags; struct set_reg_config cfg = { .offset = -1, @@ -828,12 +815,6 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.offset == -1) { nvme_show_error("offset required param"); return -EINVAL; diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index aa01f294e5..5e6ed0bcd0 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -182,7 +182,6 @@ static int device_self_test(int argc, char **argv, struct command *acmd, struct __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err; struct config { @@ -209,12 +208,6 @@ static int device_self_test(int argc, char **argv, struct command *acmd, struct if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (cfg.stc == NVME_ST_CODE_RESERVED) { __cleanup_libnvme_free struct nvme_self_test_log *log = NULL; @@ -448,7 +441,6 @@ static int sanitize_cmd(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_libnvme_free struct nvme_id_ctrl *ctrl = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err; bool sanitized; @@ -505,12 +497,6 @@ static int sanitize_cmd(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - ctrl = identify_ctrl(hdl); if (!ctrl) return -errno; @@ -619,7 +605,6 @@ static int sanitize_ns_cmd(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvme_passthru_cmd cmd; - nvme_print_flags_t flags; int err; struct config { @@ -658,12 +643,6 @@ static int sanitize_ns_cmd(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - switch (cfg.sanact) { case NVME_SANITIZE_SANACT_EXIT_FAILURE: case NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: @@ -804,11 +783,7 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin return err; } - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.lbaf != 0xff && cfg.bs != 0) { nvme_show_error( diff --git a/src/nvme.c b/src/nvme.c index eb46a7e133..d187faf022 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -104,11 +104,7 @@ int __id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; if (cfg.raw_binary) flags = BINARY; From 2ef515927ce03ed1d4b8d29fb4e426a2ffe24411 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:26:51 +0000 Subject: [PATCH 057/263] nvme: declare the output formats in struct command A command chose its output formats with NVME_ARGS_OUTPUT_FORMATS(), a side effect of declaring its option table, or narrowed the formats with its own check after validating them. The help and the command metadata listed the default formats for the latter, and the metadata listed "tabular" for every command. Declare the output formats in struct command instead. The dispatcher and the metadata capture apply them before running the command, 0 selects the default formats, OUTPUT_FORMATS_NORMAL only the normal output (NORMAL is 0), and OUTPUT_FORMATS_CUSTOM comes with its help text. NVME_ARGS() only declares the option table, and the checks which narrowed the formats go away. The metadata takes the values of --output-format from its help text, which lists the formats of the command. Signed-off-by: Daniel Wagner --- plugins/huawei/huawei-nvme.c | 8 +--- plugins/id/id-plugin.c | 21 +++-------- plugins/micron/micron-nvme.c | 44 +++++++++++----------- plugins/netapp/netapp-nvme.c | 10 +++-- plugins/samsung/samsung-nvme.c | 3 +- plugins/solidigm/solidigm-get-drive-info.c | 6 +-- plugins/solidigm/solidigm-nvme.c | 2 + plugins/utils/command-metadata.c | 24 ++++++++---- plugins/zns/zns.c | 3 +- src/args.c | 1 + src/args.h | 18 ++++----- src/nvme-cmds-enumerate.c | 38 +++++++------------ src/plugin.c | 11 ++++++ src/plugin.h | 9 +++++ tests/unit/c/test-plugin.c | 8 ++++ 15 files changed, 110 insertions(+), 96 deletions(-) diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index acfe7221e6..dcc2df63f5 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -327,7 +327,6 @@ static int huawei_list(int argc, char **argv, struct command *acmd, struct huawei_list_item *list_items; unsigned int i, n; unsigned int huawei_num = 0; - nvme_print_flags_t fmt; int ret; NVME_ARGS(opts); @@ -336,10 +335,6 @@ static int huawei_list(int argc, char **argv, struct command *acmd, if (ret) return ret; - ret = validate_output_format(nvme_args.output_format, &fmt); - if (ret < 0 || (fmt != JSON && fmt != NORMAL)) - return ret; - ret = nvme_create_global_ctx(&ctx); if (ret) return ret; @@ -375,7 +370,7 @@ static int huawei_list(int argc, char **argv, struct command *acmd, if (huawei_num > 0) { #ifdef CONFIG_JSONC - if (fmt == JSON) + if (nvme_args.output_flags == JSON) huawei_json_print_list_items(list_items, huawei_num); else #endif /* CONFIG_JSONC */ @@ -411,6 +406,7 @@ static struct command huawei_list_cmd = { .name = "list", .help = "List all Huawei NVMe devices and namespaces on machine", .fn = huawei_list, + .output_formats = NORMAL | JSON, .no_device = true, }; diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index 3df5323d32..ce36735292 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -58,11 +58,7 @@ static int list_ctrl(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || (flags != JSON && flags != NORMAL)) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; cntlist = libnvme_alloc(sizeof(*cntlist)); if (!cntlist) @@ -122,11 +118,7 @@ static int list_ns(int argc, char **argv, struct command *acmd, struct plugin *p if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || (flags != JSON && flags != NORMAL)) { - nvme_show_error("Invalid output format"); - return -EINVAL; - } + flags = nvme_args.output_flags; if (!cfg.namespace_id) { nvme_show_error("invalid nsid parameter"); @@ -246,11 +238,7 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || (flags != JSON && flags != NORMAL)) { - nvme_show_error("invalid output format"); - return -EINVAL; - } + flags = nvme_args.output_flags; endgrp_list = libnvme_alloc(sizeof(*endgrp_list)); if (!endgrp_list) @@ -1041,12 +1029,14 @@ static struct command list_ns_cmd = { .name = "ns-list", .help = "Send NVMe Identify List, display structure", .fn = list_ns, + .output_formats = NORMAL | JSON, }; static struct command list_ctrl_cmd = { .name = "ctrl-list", .help = "Send NVMe Identify Controller List, display structure", .fn = list_ctrl, + .output_formats = NORMAL | JSON, }; static struct command nvm_id_ctrl_cmd = { @@ -1120,6 +1110,7 @@ static struct command id_endurance_grp_list_cmd = { .name = "endgrp-list", .help = "Send NVMe Identify Endurance Group List, display structure", .fn = id_endurance_grp_list, + .output_formats = NORMAL | JSON, }; static struct command *commands[] = { diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 1bcb6b79d0..f8ea2eecfa 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -776,7 +776,6 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, unsigned int temperature = 0, i = 0; unsigned int tempSensors[SensorCount] = { 0 }; const char *desc = "Retrieve Micron temperature info for the given device "; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; bool is_json = false; struct json_object *root; @@ -784,7 +783,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -909,12 +908,11 @@ static int micron_pcie_stats(int argc, char **argv, bool is_json = false; bool counters = false; const char *desc = "Retrieve PCIe event counters"; - const char *fmt = "Output format: normal|json"; __u32 correctable_errors = 0; __u32 uncorrectable_errors = 0; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1650,10 +1648,9 @@ static int micron_nand_stats(int argc, char **argv, bool has_fb_log = false; bool is_json = false; nsze_from_oacs = false; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1775,10 +1772,9 @@ static int micron_smart_ext_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; bool is_json = false; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1817,10 +1813,9 @@ static int micron_work_load_log(int argc, char **argv, struct command *acmd, str int err = 0; bool is_json = false; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1857,10 +1852,9 @@ static int micron_vendor_telemetry_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -2594,10 +2588,9 @@ static int micron_drive_info(int argc, char **argv, struct command *acmd, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err = 0; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -2929,10 +2922,9 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *a struct json_object *root, *fw_act; struct json_object *entry; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -3304,11 +3296,10 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *a __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; bool is_json = false; nsze_from_oacs = false; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; int err = 0; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4190,10 +4181,9 @@ static int micron_cloud_log(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; bool is_json = false; - const char *fmt = "Output format: normal|json"; nvme_print_flags_t format; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4418,14 +4408,13 @@ static int micron_health_info(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; const char *desc = "Retrieve SMART/Health log for Micron drives"; - const char *fmt = "Output format: normal|json"; enum eDriveModel eModel = UNKNOWN_MODEL; struct nvme_smart_log smart_log = { 0 }; bool is_json = false; nvme_print_flags_t format; int err = 0; - NVME_ARGS_OUTPUT_FORMATS(opts, (JSON | NORMAL), fmt); + NVME_ARGS(opts); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4530,12 +4519,14 @@ static struct command vs_temperature_stats_cmd = { .name = "vs-temperature-stats", .help = "Retrieve Micron temperature statistics ", .fn = micron_temp_stats, + .output_formats = NORMAL | JSON, }; static struct command vs_pcie_stats_cmd = { .name = "vs-pcie-stats", .help = "Retrieve Micron PCIe error stats", .fn = micron_pcie_stats, + .output_formats = NORMAL | JSON, }; static struct command clear_pcie_correctable_errors_cmd = { @@ -4560,18 +4551,21 @@ static struct command vs_nand_stats_cmd = { .name = "vs-nand-stats", .help = "Retrieve NAND Stats", .fn = micron_nand_stats, + .output_formats = NORMAL | JSON, }; static struct command vs_smart_ext_log_cmd = { .name = "vs-smart-ext-log", .help = "Retrieve extended SMART logs", .fn = micron_smart_ext_log, + .output_formats = NORMAL | JSON, }; static struct command vs_drive_info_cmd = { .name = "vs-drive-info", .help = "Retrieve Drive information", .fn = micron_drive_info, + .output_formats = NORMAL | JSON, }; static struct command plugin_version_cmd = { @@ -4598,6 +4592,7 @@ static struct command vs_fw_activate_history_cmd = { .name = "vs-fw-activate-history", .help = "Display FW activation history", .fn = micron_fw_activation_history, + .output_formats = NORMAL | JSON, }; static struct command latency_tracking_cmd = { @@ -4622,6 +4617,7 @@ static struct command vs_smart_add_log_cmd = { .name = "vs-smart-add-log", .help = "Retrieve extended SMART data", .fn = micron_ocp_smart_health_logs, + .output_formats = NORMAL | JSON, }; static struct command clear_fw_activate_history_cmd = { @@ -4652,24 +4648,28 @@ static struct command vs_cloud_log_cmd = { .name = "vs-cloud-log", .help = "Retrieve Extended Health Information of Hyperscale NVMe Boot SSD", .fn = micron_cloud_log, + .output_formats = NORMAL | JSON, }; static struct command vs_work_load_log_cmd = { .name = "vs-work-load-log", .help = "Retrieve Workload logs", .fn = micron_work_load_log, + .output_formats = NORMAL | JSON, }; static struct command vs_vendor_telemetry_log_cmd = { .name = "vs-vendor-telemetry-log", .help = "Retrieve Vendor Telemetry logs", .fn = micron_vendor_telemetry_log, + .output_formats = NORMAL | JSON, }; static struct command smart_log_cmd = { .name = "smart-log", .help = "Retrieve SMART/Health Log", .fn = micron_health_info, + .output_formats = NORMAL | JSON, }; static struct command id_ctrl_cmd = { diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 12e0236248..0c8d9fbce9 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -969,8 +969,7 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, int num_smdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS_OUTPUT_FORMATS(opts, OUTPUT_FORMATS_CUSTOM, - NETAPP_OUTPUT_FORMAT_DESC); + NVME_ARGS(opts); ret = parse_args(argc, argv, desc, opts); if (ret < 0) @@ -1067,8 +1066,7 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, int num_ontapdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS_OUTPUT_FORMATS(opts, OUTPUT_FORMATS_CUSTOM, - NETAPP_OUTPUT_FORMAT_DESC); + NVME_ARGS(opts); ret = parse_args(argc, argv, desc, opts); if (ret < 0) @@ -1156,6 +1154,8 @@ static struct command netapp_smdevices_cmd = { .name = "smdevices", .help = "NetApp SMdevices", .fn = netapp_smdevices, + .output_formats = OUTPUT_FORMATS_CUSTOM, + .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, .no_device = true, }; @@ -1163,6 +1163,8 @@ static struct command netapp_ontapdevices_cmd = { .name = "ontapdevices", .help = "NetApp ONTAPdevices", .fn = netapp_ontapdevices, + .output_formats = OUTPUT_FORMATS_CUSTOM, + .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, .no_device = true, }; diff --git a/plugins/samsung/samsung-nvme.c b/plugins/samsung/samsung-nvme.c index 9d9418f494..cd54513226 100644 --- a/plugins/samsung/samsung-nvme.c +++ b/plugins/samsung/samsung-nvme.c @@ -1335,7 +1335,7 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, .hide_progress = false, }; - NVME_ARGS_OUTPUT_FORMATS(opts, NORMAL, "Output format: normal", + NVME_ARGS(opts, OPT_STRING("dump-type", 't', "TYPE", &cfg.type, type), OPT_INT("telemetry-data-area", 'a', &cfg.area, area), OPT_FILE("output-file", 'O', &cfg.file, file), @@ -1510,6 +1510,7 @@ static struct command vs_internal_log_cmd = { .name = "vs-internal-log", .help = "Retrieve and save internal firmware log", .fn = vs_internal_log, + .output_formats = OUTPUT_FORMATS_NORMAL, }; static struct command *commands[] = { diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index 8a07597c99..81a8deffcc 100644 --- a/plugins/solidigm/solidigm-get-drive-info.c +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -37,11 +37,7 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if ((err < 0) || !(flags == NORMAL || flags == JSON)) { - nvme_show_error("Invalid output format"); - return err; - } + flags = nvme_args.output_flags; err = libnvme_scan_topology(ctx, NULL, NULL); if (err) diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c index 58a35d9cab..c2dd0811b5 100644 --- a/plugins/solidigm/solidigm-nvme.c +++ b/plugins/solidigm/solidigm-nvme.c @@ -7,6 +7,7 @@ #include +#include "args.h" #include "plugin.h" #define SOLIDIGM_PLUGIN_VERSION "1.26" @@ -197,6 +198,7 @@ static struct command get_drive_info_cmd = { .name = "vs-drive-info", .help = "Retrieve drive information", .fn = get_drive_info, + .output_formats = NORMAL | JSON, }; static struct command get_cloud_SSDplugin_version_cmd = { diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 68c04b109b..a7a527e2dd 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -31,6 +31,7 @@ #endif #include +#include #include "command-metadata.h" #include "nvme-json.h" @@ -418,6 +419,7 @@ static int capture_command(struct command_metadata_command *mc, struct command * command_metadata_capture_error = 0; command_metadata_cur_command = mc; capture_current_command = cmd->name; + command_set_output_formats(cmd); (void)cmd->fn(2, argv, cmd, plugin); capture_current_command = NULL; command_metadata_cur_command = NULL; @@ -603,24 +605,30 @@ static bool opt_is_emittable(const struct command_metadata_option *o) * caller owns the returned array. * * output-format is special-cased because its values are not represented via an - * opt_val table; keep the hard-coded list below in sync with - * validate_output_format() / DESC_OUTPUT_FORMAT. Since the whole command is - * compiled out without json-c, "json" is always a valid value here. Every + * opt_val table. Its help text lists the formats the command supports + * ("Output format: normal|json"), see nvme_args_set_output_formats(). Every * other value set comes from the option's opt_val table, which is the set the * parser actually enforces; options whose value is unconstrained (e.g. any * OPT_UINT such as output-format-version) have no values array. */ static struct json_object *json_option_values(const struct command_metadata_option *o) { + static const char prefix[] = "Output format: "; const struct argconfig_opt_val *v; struct json_object *vals; - if (!strcmp(o->option, "output-format")) { + if (!strcmp(o->option, "output-format") && o->help && + !strncmp(o->help, prefix, strlen(prefix))) { + __cleanup_free char *formats = strdup(o->help + strlen(prefix)); + char *p, *name; + + if (!formats) + return NULL; + vals = json_create_array(); - json_array_add_value_string(vals, "normal"); - json_array_add_value_string(vals, "json"); - json_array_add_value_string(vals, "binary"); - json_array_add_value_string(vals, "tabular"); + p = formats; + while ((name = shr_strsep(&p, "|")) != NULL) + json_array_add_value_string(vals, name); return vals; } if (!o->opt_val) diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 31bedfd9cd..88f761f7e1 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -104,7 +104,7 @@ static int list(int argc, char **argv, struct command *acmd, }; struct shr_table *t; - NVME_ARGS_OUTPUT_FORMATS(opts, NORMAL, "Output format: normal"); + NVME_ARGS(opts); err = parse_args(argc, argv, desc, opts); if (err) @@ -1111,6 +1111,7 @@ static struct command list_cmd = { .name = "list", .help = "List all NVMe devices with Zoned Namespace Command Set support", .fn = list, + .output_formats = OUTPUT_FORMATS_NORMAL, .no_device = true, }; diff --git a/src/args.c b/src/args.c index db6892dd4e..90663913c7 100644 --- a/src/args.c +++ b/src/args.c @@ -64,6 +64,7 @@ void nvme_args_register(void) void nvme_args_set_output_formats(nvme_print_flags_t formats, const char *custom_desc) { + formats &= ~OUTPUT_FORMATS_NORMAL; nvme_args.supported_output_formats = formats; if (formats & OUTPUT_FORMATS_CUSTOM) { snprintf(output_format_help, sizeof(output_format_help), "%s", diff --git a/src/args.h b/src/args.h index b00bdd2015..327fdcd7c9 100644 --- a/src/args.h +++ b/src/args.h @@ -45,6 +45,11 @@ enum nvme_print_flags { * ones above. parse_args() does not check it then. */ #define OUTPUT_FORMATS_CUSTOM (1U << 31) +/* + * Only the normal output. NORMAL is 0 and a struct command without output + * formats gets the default ones. + */ +#define OUTPUT_FORMATS_NORMAL (1U << 30) #ifdef CONFIG_JSONC #define DEFAULT_OUTPUT_FORMATS (NORMAL | JSON | BINARY) @@ -58,23 +63,16 @@ enum nvme_print_flags { * The command's options. The global options are registered once with * argconfig (nvme_args_register()) and accepted by every command; a * command's own option with the same short or long name takes precedence. - * The supported output formats select the values accepted by - * --output-format and its help text. + * The output formats a command supports are declared in its struct + * command. */ -#define NVME_ARGS_OUTPUT_FORMATS(n, of_mask, of_desc, ...) \ - nvme_args_set_output_formats(of_mask, of_desc); \ +#define NVME_ARGS(n, ...) \ struct argconfig_commandline_options n[] = { \ OPT_GROUP("Options"), \ ##__VA_ARGS__, \ OPT_END() \ } -#define NVME_ARGS(n, ...) \ - NVME_ARGS_OUTPUT_FORMATS(n, \ - DEFAULT_OUTPUT_FORMATS, \ - DEFAULT_OUTPUT_FORMAT_DESC, \ - ##__VA_ARGS__) - extern const char *uuid_index; extern const char *namespace_id_desired; diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 5b1d3fb410..6b5bd169f8 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -32,6 +32,12 @@ #include "nvme-print.h" #include "plugin.h" +#ifdef CONFIG_JSONC +#define TOPOLOGY_OUTPUT_FORMATS (NORMAL | JSON | TABULAR) +#else /* CONFIG_JSONC */ +#define TOPOLOGY_OUTPUT_FORMATS (NORMAL | TABULAR) +#endif /* CONFIG_JSONC */ + static int list_subsys(int argc, char **argv, struct command *acmd, struct plugin *plugin) { @@ -53,11 +59,7 @@ static int list_subsys(int argc, char **argv, struct command *acmd, if (argconfig_nr_operands()) devname = basename(argconfig_operand(0)); - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || (flags != JSON && flags != NORMAL)) { - nvme_show_error("Invalid output format"); - return -EINVAL; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -102,14 +104,6 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct enum nvme_cli_topo_ranking rank; int err; -#ifdef CONFIG_JSONC - nvme_print_flags_t supported_formats = (NORMAL | JSON | TABULAR); - const char *supported_formats_desc = "Output format: normal|json|tabular"; -#else /* CONFIG_JSONC */ - nvme_print_flags_t supported_formats = (NORMAL | TABULAR); - const char *supported_formats_desc = "Output format: normal|tabular"; -#endif /* CONFIG_JSONC */ - struct config { char *ranking; }; @@ -118,7 +112,7 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct .ranking = "namespace", }; - NVME_ARGS_OUTPUT_FORMATS(opts, supported_formats, supported_formats_desc, + NVME_ARGS(opts, OPT_FMT("ranking", 'r', &cfg.ranking, ranking)); err = parse_args(argc, argv, desc, opts); @@ -195,11 +189,7 @@ static int top(int argc, char **argv, struct command *acmd, if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || flags != NORMAL) { - nvme_show_error("Invalid output format"); - return -EINVAL; - } + flags = nvme_args.output_flags; if (cfg.delay < 1) { nvme_show_error("delay must be greater than or equal to 1"); @@ -231,11 +221,7 @@ static int list(int argc, char **argv, struct command *acmd, struct plugin *plug if (err) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0 || (flags != JSON && flags != NORMAL)) { - nvme_show_error("Invalid output format"); - return -EINVAL; - } + flags = nvme_args.output_flags; if (nvme_args.verbose) flags |= VERBOSE; @@ -257,6 +243,7 @@ static struct command list_cmd = { .name = "list", .help = "List all NVMe devices and namespaces on machine", .fn = list, + .output_formats = NORMAL | JSON, .no_device = true, }; @@ -264,6 +251,7 @@ static struct command list_subsys_cmd = { .name = "list-subsys", .help = "List nvme subsystems", .fn = list_subsys, + .output_formats = NORMAL | JSON, .no_device = true, }; @@ -273,6 +261,7 @@ static struct command top_cmd = { .name = "top", .help = "nvme top", .fn = top, + .output_formats = OUTPUT_FORMATS_NORMAL, .no_device = true, }; @@ -282,6 +271,7 @@ static struct command show_topology_cmd_cmd = { .name = "show-topology", .help = "Show the topology", .fn = show_topology_cmd, + .output_formats = TOPOLOGY_OUTPUT_FORMATS, .no_device = true, }; diff --git a/src/plugin.c b/src/plugin.c index 3f7f617360..30c2d7060d 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -379,6 +379,15 @@ void general_help(struct plugin *plugin, char *str) } } +/* Apply the output formats a command supports before it runs */ +void command_set_output_formats(struct command *cmd) +{ + nvme_args_set_output_formats(cmd->output_formats ? + cmd->output_formats : + DEFAULT_OUTPUT_FORMATS, + cmd->output_format_desc); +} + int handle_plugin(int argc, char **argv, struct plugin *plugin) { char use[0x100]; @@ -457,6 +466,7 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) ((*cmd)->alias && !strcmp(str, (*cmd)->alias))) { build_command_usage(use, sizeof(use), prog, plugin, *cmd); argconfig_append_usage(use); + command_set_output_formats(*cmd); return (*cmd)->fn(argc, argv, *cmd, plugin); } if (!strncmp(str, (*cmd)->name, strlen(str))) { @@ -473,6 +483,7 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) if (cr && cr_valid) { build_command_usage(use, sizeof(use), prog, plugin, cr); argconfig_append_usage(use); + command_set_output_formats(cr); return cr->fn(argc, argv, cr, plugin); } diff --git a/src/plugin.h b/src/plugin.h index 562d44aec8..e46aabbc76 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -48,6 +48,14 @@ struct command { bool deprecated; /* Set for commands that don't take a positional argument. */ bool no_device; + /* + * The values --output-format accepts (enum nvme_print_flags), 0 for + * the default ones, OUTPUT_FORMATS_NORMAL for only the normal output. + * With OUTPUT_FORMATS_CUSTOM the command parses --output-format + * itself, and output_format_desc is its help text. + */ + uint32_t output_formats; + const char *output_format_desc; }; /* @@ -60,6 +68,7 @@ struct command { extern struct plugin builtin; void general_help(struct plugin *plugin, char *str); +void command_set_output_formats(struct command *cmd); int handle_plugin(int argc, char **argv, struct plugin *plugin); void register_extension(struct plugin *plugin); diff --git a/tests/unit/c/test-plugin.c b/tests/unit/c/test-plugin.c index d84906fe5c..95ad5ea2f5 100644 --- a/tests/unit/c/test-plugin.c +++ b/tests/unit/c/test-plugin.c @@ -37,6 +37,14 @@ struct nvme_args nvme_args = { .supported_output_formats = NORMAL, }; +/* plugin.c applies the output formats of a command before it runs */ +void nvme_args_set_output_formats(nvme_print_flags_t formats, + const char *custom_desc) +{ + (void)formats; + (void)custom_desc; +} + static int test_rc; struct last_call_info { From 9d38ba9eb7b3dc6dd5fbbfd1b7e884cc6ea82419 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:28:28 +0000 Subject: [PATCH 058/263] nvme: move the global options to their own file The table of global options and the functions which register it and set the output formats of a command only depend on argconfig. Move them from args.c, which also has the parsing and output format helpers of the commands, to global-options.c, so a unit test can use the table without the printing code. No functional change. Signed-off-by: Daniel Wagner --- src/args.c | 56 ----------------------------------- src/global-options.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + 3 files changed, 70 insertions(+), 56 deletions(-) create mode 100644 src/global-options.c diff --git a/src/args.c b/src/args.c index 90663913c7..1d1dfc4b93 100644 --- a/src/args.c +++ b/src/args.c @@ -7,7 +7,6 @@ */ #include -#include #include #include @@ -23,61 +22,6 @@ struct nvme_args nvme_args = { .supported_output_formats = DEFAULT_OUTPUT_FORMATS, }; -static char output_format_help[64]; - -static struct argconfig_commandline_options global_options[] = { - OPT_INCR("verbose", 'v', &nvme_args.verbose, - "Increase output verbosity"), - OPT_FLAG("quiet", 0, &nvme_args.quiet, - "suppress informational output"), - OPT_FMT("output-format", 'o', &nvme_args.output_format, - output_format_help), - OPT_UINT("timeout", 0, &nvme_args.timeout, - "timeout value, in milliseconds"), - OPT_FLAG("dry-run", 0, &nvme_args.dry_run, - "show command instead of executing"), - OPT_FLAG("no-retries", 0, &nvme_args.no_retries, - "disable retry logic on errors"), - OPT_FLAG("no-ioctl-probing", 0, &nvme_args.no_ioctl_probing, - "disable 64-bit IOCTL support probing"), - OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, - "output format version: 1|2"), - /* the spelling of --verbose of older versions */ - OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, NULL, - true), - OPT_STRING("set-options", 0, "KEY=VALUE", &nvme_args.set_options, - "set a libnvme library option (key=value[,key=value,...]);"), - OPT_END() -}; - -/* Register the options every command accepts */ -void nvme_args_register(void) -{ - nvme_args_set_output_formats(DEFAULT_OUTPUT_FORMATS, NULL); - argconfig_set_global_options(global_options); -} - -/* - * The output formats the current command supports. The help text lists - * them, a command which parses --output-format itself describes its own. - */ -void nvme_args_set_output_formats(nvme_print_flags_t formats, - const char *custom_desc) -{ - formats &= ~OUTPUT_FORMATS_NORMAL; - nvme_args.supported_output_formats = formats; - if (formats & OUTPUT_FORMATS_CUSTOM) { - snprintf(output_format_help, sizeof(output_format_help), "%s", - custom_desc); - return; - } - snprintf(output_format_help, sizeof(output_format_help), - "Output format: normal%s%s%s", - formats & JSON ? "|json" : "", - formats & BINARY ? "|binary" : "", - formats & TABULAR ? "|tabular" : ""); -} - const char *uuid_index = "UUID index"; const char *namespace_id_desired = "identifier of desired namespace"; diff --git a/src/global-options.c b/src/global-options.c new file mode 100644 index 0000000000..eb5ea095f9 --- /dev/null +++ b/src/global-options.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + * + * The options every command accepts, registered once with argconfig. + */ + +#include + +#include "argconfig.h" +#include "args.h" + +static char output_format_help[64]; + +static struct argconfig_commandline_options global_options[] = { + OPT_INCR("verbose", 'v', &nvme_args.verbose, + "Increase output verbosity"), + OPT_FLAG("quiet", 0, &nvme_args.quiet, + "suppress informational output"), + OPT_FMT("output-format", 'o', &nvme_args.output_format, + output_format_help), + OPT_UINT("timeout", 0, &nvme_args.timeout, + "timeout value, in milliseconds"), + OPT_FLAG("dry-run", 0, &nvme_args.dry_run, + "show command instead of executing"), + OPT_FLAG("no-retries", 0, &nvme_args.no_retries, + "disable retry logic on errors"), + OPT_FLAG("no-ioctl-probing", 0, &nvme_args.no_ioctl_probing, + "disable 64-bit IOCTL support probing"), + OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, + "output format version: 1|2"), + /* the spelling of --verbose of older versions */ + OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, NULL, + true), + OPT_STRING("set-options", 0, "KEY=VALUE", &nvme_args.set_options, + "set a libnvme library option (key=value[,key=value,...]);"), + OPT_END() +}; + +/* Register the options every command accepts */ +void nvme_args_register(void) +{ + nvme_args_set_output_formats(DEFAULT_OUTPUT_FORMATS, NULL); + argconfig_set_global_options(global_options); +} + +/* + * The output formats the current command supports. The help text lists + * them, a command which parses --output-format itself describes its own. + */ +void nvme_args_set_output_formats(nvme_print_flags_t formats, + const char *custom_desc) +{ + formats &= ~OUTPUT_FORMATS_NORMAL; + nvme_args.supported_output_formats = formats; + if (formats & OUTPUT_FORMATS_CUSTOM) { + snprintf(output_format_help, sizeof(output_format_help), "%s", + custom_desc); + return; + } + snprintf(output_format_help, sizeof(output_format_help), + "Output format: normal%s%s%s", + formats & JSON ? "|json" : "", + formats & BINARY ? "|binary" : "", + formats & TABULAR ? "|tabular" : ""); +} diff --git a/src/meson.build b/src/meson.build index bff55cbc38..d68c7b2d27 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ sources = [ 'src/argconfig.c', 'src/global-config.c', 'src/global-ctx.c', + 'src/global-options.c', 'src/logging.c', 'src/nvme-models.c', 'src/nvme-pci-ids.c', From 1fd5e44611f2c742fe2763ec1ecd173b3ebe688a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:30:51 +0000 Subject: [PATCH 059/263] nvme: take the nvme-cli.conf keys from the global options nvme-cli.conf has a key for every global option, but apply_kv() listed them a second time, with the type of each. Look the key up in the registered table of global options instead and parse the value by the option's type. The keys are the same: the visible global options; the hidden --human-readable is none. A verbose value beyond INT_MAX was skipped without the "invalid value" warning, now it gets it. A new test checks that global-options.txt describes every global option and that nvme-cli.conf.txt lists each as a key. The config test uses the real table now. Signed-off-by: Daniel Wagner --- src/global-config.c | 86 +++++++++++++----------- tests/unit/c/meson.build | 8 ++- tests/unit/c/test-global-config.c | 34 ++++++++++ tests/unit/py/meson.build | 14 ++++ tests/unit/py/test_global_options_doc.py | 66 ++++++++++++++++++ 5 files changed, 167 insertions(+), 41 deletions(-) create mode 100644 tests/unit/py/test_global_options_doc.py diff --git a/src/global-config.c b/src/global-config.c index 8945f9db81..761d6f6dd7 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -7,10 +7,10 @@ */ /* - * /etc/nvme/nvme-cli.conf: a global INI config file for the options - * defined by NVME_ARGS() in nvme.h (struct nvme_args). Values from this - * file become the new defaults; any command-line flag still overrides - * them, since this is loaded before argument parsing runs. + * /etc/nvme/nvme-cli.conf: a global INI config file for the global options + * (global-options.c, struct nvme_args). Its keys are their long names. + * Values from this file become the new defaults; any command-line flag + * still overrides them, since this is loaded before argument parsing runs. */ #include @@ -22,7 +22,7 @@ #include #include -#include "args.h" +#include "argconfig.h" #include "global-config.h" struct global_config_ctx { @@ -46,62 +46,68 @@ static int parse_uint(const char *value, unsigned int *out) return 0; } +/* The keys are the global options which the help shows */ +static struct argconfig_commandline_options *find_key(const char *key) +{ + struct argconfig_commandline_options *s; + + for (s = argconfig_get_global_options(); s && s->option; s++) { + if (s->config_type == CFG_GROUP_SEPARATOR || s->hidden) + continue; + if (!strcmp(s->option, key)) + return s; + } + + return NULL; +} + static int apply_kv(struct global_config_ctx *gc, const char *key, const char *value, unsigned int line) { + struct argconfig_commandline_options *s = find_key(key); unsigned int uval; bool bval; char *dup; int err; - if (!strcmp(key, "verbose")) { - err = parse_uint(value, &uval); - if (err || uval > INT_MAX) - goto bad_value; - nvme_args.verbose = uval; - } else if (!strcmp(key, "quiet")) { + if (!s || !s->default_value) { + fprintf(stderr, "%s:%u: unknown key \"%s\", ignoring\n", + gc->path, line, key); + return 0; + } + + switch (s->config_type) { + case CFG_FLAG: err = shr_parse_bool(value, &bval); if (err) goto bad_value; - nvme_args.quiet = bval; - } else if (!strcmp(key, "output-format")) { - dup = strdup(value); - if (!dup) - return -ENOMEM; - nvme_args.output_format = dup; - } else if (!strcmp(key, "timeout")) { + *(bool *)s->default_value = bval; + break; + case CFG_INCREMENT: + /* the number of times the option is given */ err = parse_uint(value, &uval); + if (!err && uval > INT_MAX) + err = -EINVAL; if (err) goto bad_value; - nvme_args.timeout = uval; - } else if (!strcmp(key, "dry-run")) { - err = shr_parse_bool(value, &bval); - if (err) - goto bad_value; - nvme_args.dry_run = bval; - } else if (!strcmp(key, "no-retries")) { - err = shr_parse_bool(value, &bval); - if (err) - goto bad_value; - nvme_args.no_retries = bval; - } else if (!strcmp(key, "no-ioctl-probing")) { - err = shr_parse_bool(value, &bval); - if (err) - goto bad_value; - nvme_args.no_ioctl_probing = bval; - } else if (!strcmp(key, "output-format-version")) { + *(int *)s->default_value = uval; + break; + case CFG_POSITIVE: err = parse_uint(value, &uval); if (err) goto bad_value; - nvme_args.output_format_ver = uval; - } else if (!strcmp(key, "set-options")) { + *(uint32_t *)s->default_value = uval; + break; + case CFG_STRING: dup = strdup(value); if (!dup) return -ENOMEM; - nvme_args.set_options = dup; - } else { - fprintf(stderr, "%s:%u: unknown key \"%s\", ignoring\n", + *(char **)s->default_value = dup; + break; + default: + fprintf(stderr, "%s:%u: key \"%s\" can't be set, ignoring\n", gc->path, line, key); + break; } return 0; diff --git a/tests/unit/c/meson.build b/tests/unit/c/meson.build index 8b7edf7fd1..cb9d384c7a 100644 --- a/tests/unit/c/meson.build +++ b/tests/unit/c/meson.build @@ -32,10 +32,16 @@ test('nvme-cli - argconfig_diff', test_argconfig_diff) test_global_config = executable( 'test-global-config', - ['test-global-config.c', '../../../src/global-config.c'], + [ + 'test-global-config.c', + '../../../src/argconfig.c', + '../../../src/global-config.c', + '../../../src/global-options.c', + ], dependencies: [ config_dep, ccan_dep, + libnvme_dep, shared_dep, ], ) diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index baa63f2080..7b96f0edc0 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -140,6 +140,37 @@ static bool test_malformed_value_skipped(void) return pass; } +/* + * The keys are the global options the help shows; the hidden + * human-readable is none. A verbose count beyond INT_MAX is rejected. + */ +static bool test_hidden_and_out_of_range(void) +{ + char *path; + bool pass = true; + + nvme_args.verbose = 1; + path = write_temp( + "[Global]\n" + "human-readable = 5\n" + "verbose = 4294967295\n"); + + printf("test_hidden_and_out_of_range:\n"); + + nvme_load_global_config_from(path); + unlink(path); + free(path); + + if (nvme_args.verbose != 1) { + printf(" - hidden key and huge verbose ignored [FAIL]\n"); + pass = false; + } else { + printf(" - hidden key and huge verbose ignored [PASS]\n"); + } + + return pass; +} + static bool test_keys_outside_global_ignored(void) { char *path; @@ -191,9 +222,12 @@ int main(void) { bool pass = true; + nvme_args_register(); + pass &= test_valid_keys(); pass &= test_unknown_key_ignored(); pass &= test_malformed_value_skipped(); + pass &= test_hidden_and_out_of_range(); pass &= test_keys_outside_global_ignored(); pass &= test_missing_file_is_noop(); diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index c3ba7d311b..2f10ddac99 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -22,6 +22,20 @@ if python3.found() depends: nvme_exe, timeout: 120, ) + + # The global options are one table; the documentation has to list + # each of them, also as a key of nvme-cli.conf. + test( + 'nvme-cli - global-options-doc', + python3, + args: [ + files('test_global_options_doc.py'), + nvme_exe, + meson.project_source_root() / 'Documentation', + ], + depends: nvme_exe, + timeout: 60, + ) endif # Argument handling of the completion generator; needs no nvme binary or diff --git a/tests/unit/py/test_global_options_doc.py b/tests/unit/py/test_global_options_doc.py new file mode 100644 index 0000000000..e35e01d907 --- /dev/null +++ b/tests/unit/py/test_global_options_doc.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +"""Check that the documentation lists every global option. + +The global options are one table in src/global-options.c. Every command +accepts them, and the visible ones are also the keys of nvme-cli.conf. This +test takes them from `nvme utils dump-command-metadata` and checks that +Documentation/global-options.txt describes each of them and that +Documentation/nvme-cli.conf.txt lists each as a key. + +Usage: test_global_options_doc.py +""" +import json +import os +import re +import subprocess +import sys +import unittest + +if len(sys.argv) < 3: + print("usage: %s " % sys.argv[0]) + sys.exit(77) + +NVME_BIN = sys.argv[1] +DOC_DIR = sys.argv[2] + + +def global_options(): + """The long names of the visible global options.""" + out = subprocess.run([NVME_BIN, 'utils', 'dump-command-metadata'], + capture_output=True, text=True, check=True).stdout + model = json.loads(out) + for cmd in model['commands']: + opts = [o for o in cmd.get('options', []) if o.get('global')] + if opts: + return {o['long'] for o in opts if not o.get('hidden')} + raise AssertionError('no command with global options') + + +class GlobalOptionsDocTest(unittest.TestCase): + + def setUp(self): + self.options = global_options() + + def test_global_options_txt(self): + with open(os.path.join(DOC_DIR, 'global-options.txt')) as f: + doc = f.read() + for name in sorted(self.options): + with self.subTest(option=name): + self.assertRegex(doc, rf'(?m)^--{re.escape(name)}\b') + + def test_nvme_cli_conf_keys(self): + with open(os.path.join(DOC_DIR, 'nvme-cli.conf.txt')) as f: + doc = f.read() + keys = re.search(r'^KEYS\n-+\n(.*?)\n[A-Z ]+\n-+\n', doc, + re.M | re.S) + self.assertIsNotNone(keys, 'no KEYS section') + listed = set(re.findall(r'[a-z][a-z-]+', + keys.group(1).split('\n\n')[1])) + self.assertEqual(listed, self.options) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]]) From e5cf0153afa54a295972e5037f5ef5fafbc61791 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:31:11 +0000 Subject: [PATCH 060/263] completions: update the shell completions Regenerate the completions: --output-format offers the formats of each command, and the commands with their own option tables offer the global options. Signed-off-by: Daniel Wagner --- completions/_nvme | 1164 +++++++++------- completions/bash-nvme-completion.sh | 1030 +++++++------- completions/nvme-completion.ps1 | 1971 ++++++++++++++------------- 3 files changed, 2231 insertions(+), 1934 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 5f2026e0a0..0b3ddc6013 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -296,7 +296,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _amzn_id_ctrl _amzn_id_ctrl=( @@ -324,7 +324,7 @@ _nvme () { ;; (stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _amzn_stats _amzn_stats=( @@ -373,15 +373,28 @@ _nvme () { case ${words[2]} in (convert) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" \ "--output -o" && return local _config_convert _config_convert=( --config=':convert this JSON file (default: config.json)' -J':alias for --config' + --dry-run':show command instead of executing' --force':overwrite an existing target' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' --output=':write result here (default: nvme-fabrics.conf)' -o':alias for --output' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -391,7 +404,7 @@ _nvme () { ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output" && return local _config_create @@ -471,7 +484,7 @@ _nvme () { ;; (show) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" && return local _config_show @@ -498,15 +511,48 @@ _nvme () { ;; (status) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return + local _config_status + _config_status=( + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' + --help':show help' + -h':alias for --help' + ) + _arguments '*:: :->subcmds' + _nvme_describe "nvme config status options" _config_status _nvme_has_device || compadd -- /dev/nvme*(N) ;; (validate) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" && return local _config_validate _config_validate=( --config=':INI configuration file (default: /usr/local/etc/nvme/nvme-fabrics.conf)' -J':alias for --config' + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -533,7 +579,7 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dapustor_smart_log_add _dapustor_smart_log_add=( @@ -580,7 +626,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dell_id_ctrl _dell_id_ctrl=( @@ -626,7 +672,7 @@ _nvme () { case ${words[2]} in (smart-log-add|stat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dera_smart_log_add _dera_smart_log_add=( @@ -668,7 +714,7 @@ _nvme () { case ${words[2]} in (receive) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dir_receive _dir_receive=( @@ -706,7 +752,7 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _dir_send @@ -771,7 +817,7 @@ _nvme () { case ${words[2]} in (add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_add _exclusion_add=( @@ -799,7 +845,7 @@ _nvme () { ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_create _exclusion_create=( @@ -825,7 +871,7 @@ _nvme () { ;; (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_delete _exclusion_delete=( @@ -851,7 +897,7 @@ _nvme () { ;; (edit) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_edit _exclusion_edit=( @@ -877,7 +923,7 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_list _exclusion_list=( @@ -903,7 +949,7 @@ _nvme () { ;; (remove) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _exclusion_remove _exclusion_remove=( @@ -953,7 +999,7 @@ _nvme () { case ${words[2]} in (configs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_configs _fdp_configs=( @@ -983,7 +1029,7 @@ _nvme () { ;; (events) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_events _fdp_events=( @@ -1013,7 +1059,7 @@ _nvme () { ;; (feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_feature _fdp_feature=( @@ -1043,7 +1089,7 @@ _nvme () { ;; (set-events) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_set_events _fdp_set_events=( @@ -1077,7 +1123,7 @@ _nvme () { ;; (stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_stats _fdp_stats=( @@ -1105,7 +1151,7 @@ _nvme () { ;; (status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_status _fdp_status=( @@ -1133,7 +1179,7 @@ _nvme () { ;; (update) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_update _fdp_update=( @@ -1161,7 +1207,7 @@ _nvme () { ;; (usage) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fdp_usage _fdp_usage=( @@ -1224,7 +1270,7 @@ _nvme () { case ${words[2]} in (arbitration) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_arbitration @@ -1261,7 +1307,7 @@ _nvme () { ;; (async-event-conf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_async_event_conf @@ -1292,7 +1338,7 @@ _nvme () { ;; (err-recovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_err_recovery @@ -1327,7 +1373,7 @@ _nvme () { ;; (hctm) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_hctm @@ -1360,7 +1406,7 @@ _nvme () { ;; (host-behavior-support) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_host_behavior_support @@ -1399,7 +1445,7 @@ _nvme () { ;; (int-coalesce) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_int_coalesce @@ -1432,7 +1478,7 @@ _nvme () { ;; (int-vector-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_int_vector_config @@ -1465,7 +1511,7 @@ _nvme () { ;; (keep-alive-timer) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_keep_alive_timer @@ -1496,7 +1542,7 @@ _nvme () { ;; (lba-range-type) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_lba_range_type @@ -1539,7 +1585,7 @@ _nvme () { ;; (num-queues) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_num_queues @@ -1572,7 +1618,7 @@ _nvme () { ;; (perf-characteristics) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return _nvme_opt_files "--vs-data -V" && return @@ -1616,7 +1662,7 @@ _nvme () { ;; (power-limit) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_power_limit @@ -1651,7 +1697,7 @@ _nvme () { ;; (power-meas) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_power_meas @@ -1685,7 +1731,7 @@ _nvme () { ;; (power-mgmt) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_power_mgmt @@ -1718,7 +1764,7 @@ _nvme () { ;; (power-thresh) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_power_thresh @@ -1757,7 +1803,7 @@ _nvme () { ;; (temp-thresh) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_temp_thresh @@ -1794,7 +1840,7 @@ _nvme () { ;; (timestamp) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_timestamp @@ -1825,7 +1871,7 @@ _nvme () { ;; (volatile-wc) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_volatile_wc @@ -1856,7 +1902,7 @@ _nvme () { ;; (write-atom-normal) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _feat_write_atom_normal @@ -1907,7 +1953,7 @@ _nvme () { case ${words[2]} in (commit|activate) _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fw_commit _fw_commit=( @@ -1939,7 +1985,7 @@ _nvme () { ;; (download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--fw -f" && return local _fw_download @@ -1995,7 +2041,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _huawei_id_ctrl _huawei_id_ctrl=( @@ -2023,14 +2069,14 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _huawei_list _huawei_list=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -2066,7 +2112,7 @@ _nvme () { case ${words[2]} in (crit-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ibm_crit_log _ibm_crit_log=( @@ -2092,7 +2138,7 @@ _nvme () { ;; (persist-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ibm_persist_event_log _ibm_persist_event_log=( @@ -2122,7 +2168,7 @@ _nvme () { ;; (vpd) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ibm_vpd _ibm_vpd=( @@ -2182,7 +2228,7 @@ _nvme () { case ${words[2]} in (ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ctrl _id_ctrl=( @@ -2210,7 +2256,7 @@ _nvme () { ;; (ctrl-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _id_ctrl_list _id_ctrl_list=( @@ -2221,7 +2267,7 @@ _nvme () { -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -2238,7 +2284,7 @@ _nvme () { ;; (domain) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_domain _id_domain=( @@ -2264,7 +2310,7 @@ _nvme () { ;; (endgrp-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _id_endgrp_list _id_endgrp_list=( @@ -2273,7 +2319,7 @@ _nvme () { -i':alias for --endgrp-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -2290,7 +2336,7 @@ _nvme () { ;; (iocs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_iocs _id_iocs=( @@ -2316,7 +2362,7 @@ _nvme () { ;; (ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns _id_ns=( @@ -2347,7 +2393,7 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_descs _id_ns_descs=( @@ -2375,7 +2421,7 @@ _nvme () { ;; (ns-granularity) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_granularity _id_ns_granularity=( @@ -2399,7 +2445,7 @@ _nvme () { ;; (ns-ind) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_ind _id_ns_ind=( @@ -2427,7 +2473,7 @@ _nvme () { ;; (ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_lba_format _id_ns_lba_format=( @@ -2455,7 +2501,7 @@ _nvme () { ;; (ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _id_ns_list _id_ns_list=( @@ -2468,7 +2514,7 @@ _nvme () { -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -2485,7 +2531,7 @@ _nvme () { ;; (nvm-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_nvm_ctrl _id_nvm_ctrl=( @@ -2509,7 +2555,7 @@ _nvme () { ;; (nvm-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_nvm_ns _id_nvm_ns=( @@ -2537,7 +2583,7 @@ _nvme () { ;; (nvm-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_nvm_ns_lba_format _id_nvm_ns_lba_format=( @@ -2565,7 +2611,7 @@ _nvme () { ;; (nvmset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_nvmset _id_nvmset=( @@ -2591,7 +2637,7 @@ _nvme () { ;; (primary-ctrl-caps) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_primary_ctrl_caps _id_primary_ctrl_caps=( @@ -2617,7 +2663,7 @@ _nvme () { ;; (secondary-ctrl-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_secondary_ctrl_list _id_secondary_ctrl_list=( @@ -2645,7 +2691,7 @@ _nvme () { ;; (uuid) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_uuid _id_uuid=( @@ -2689,7 +2735,7 @@ _nvme () { case ${words[2]} in (get-cdump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _innogrit_get_cdump _innogrit_get_cdump=( @@ -2713,7 +2759,7 @@ _nvme () { ;; (get-eventlog) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _innogrit_get_eventlog _innogrit_get_eventlog=( @@ -2754,7 +2800,7 @@ _nvme () { case ${words[2]} in (nvme-vendor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _inspur_nvme_vendor_log _inspur_nvme_vendor_log=( @@ -2802,7 +2848,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_id_ctrl _intel_id_ctrl=( @@ -2830,7 +2876,7 @@ _nvme () { ;; (internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _intel_internal_log @@ -2867,7 +2913,7 @@ _nvme () { ;; (lat-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_lat_stats _intel_lat_stats=( @@ -2897,12 +2943,25 @@ _nvme () { ;; (lat-stats-tracking) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _intel_lat_stats_tracking _intel_lat_stats_tracking=( --disable':Disable LST' -d':alias for --disable' + --dry-run':show command instead of executing' --enable':Enable LST' -e':alias for --enable' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -2912,7 +2971,7 @@ _nvme () { ;; (market-name) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_market_name _intel_market_name=( @@ -2938,7 +2997,7 @@ _nvme () { ;; (set-bucket-thresholds) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_set_bucket_thresholds _intel_set_bucket_thresholds=( @@ -2966,7 +3025,7 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_smart_log_add _intel_smart_log_add=( @@ -2996,7 +3055,7 @@ _nvme () { ;; (temp-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _intel_temp_stats _intel_temp_stats=( @@ -3040,7 +3099,7 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _io_mgmt_recv @@ -3075,7 +3134,7 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _io_mgmt_send @@ -3134,7 +3193,7 @@ _nvme () { case ${words[2]} in (check-kxchap-secret) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_check_kxchap_secret _keys_check_kxchap_secret=( @@ -3166,7 +3225,7 @@ _nvme () { ;; (check-tls-psk) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_check_tls_psk _keys_check_tls_psk=( @@ -3204,7 +3263,7 @@ _nvme () { ;; (export) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_export _keys_export=( @@ -3232,7 +3291,7 @@ _nvme () { ;; (gen-kxchap-secret) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_gen_kxchap_secret _keys_gen_kxchap_secret=( @@ -3262,7 +3321,7 @@ _nvme () { ;; (gen-tls-psk) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_gen_tls_psk _keys_gen_tls_psk=( @@ -3306,7 +3365,7 @@ _nvme () { ;; (import) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_import _keys_import=( @@ -3338,7 +3397,7 @@ _nvme () { ;; (insert-tls-psk) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_insert_tls_psk _keys_insert_tls_psk=( @@ -3378,7 +3437,7 @@ _nvme () { ;; (revoke) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _keys_revoke _keys_revoke=( @@ -3431,7 +3490,7 @@ _nvme () { case ${words[2]} in (create-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lm_create_cdq _lm_create_cdq=( @@ -3462,7 +3521,7 @@ _nvme () { ;; (delete-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lm_delete_cdq _lm_delete_cdq=( @@ -3488,7 +3547,7 @@ _nvme () { ;; (get-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lm_get_cdq _lm_get_cdq=( @@ -3514,7 +3573,7 @@ _nvme () { ;; (migration-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return _nvme_opt_files "--output-file -f" && return @@ -3558,7 +3617,7 @@ _nvme () { ;; (migration-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return _nvme_opt_files "--input-file -f" && return @@ -3606,7 +3665,7 @@ _nvme () { ;; (set-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lm_set_cdq _lm_set_cdq=( @@ -3636,7 +3695,7 @@ _nvme () { ;; (track-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _lm_track_send @@ -3718,7 +3777,7 @@ _nvme () { case ${words[2]} in (ana) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_ana _log_ana=( @@ -3744,7 +3803,7 @@ _nvme () { ;; (ave-discovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_ave_discovery _log_ave_discovery=( @@ -3770,7 +3829,7 @@ _nvme () { ;; (boot-part) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -f" && return local _log_boot_part @@ -3799,7 +3858,7 @@ _nvme () { ;; (changed-alloc-ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_changed_alloc_ns_list _log_changed_alloc_ns_list=( @@ -3825,7 +3884,7 @@ _nvme () { ;; (changed-ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_changed_ns_list _log_changed_ns_list=( @@ -3851,7 +3910,7 @@ _nvme () { ;; (dispersed-ns-participating-nss) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_dispersed_ns_participating_nss _log_dispersed_ns_participating_nss=( @@ -3877,7 +3936,7 @@ _nvme () { ;; (effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_effects _log_effects=( @@ -3905,7 +3964,7 @@ _nvme () { ;; (endurance) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_endurance _log_endurance=( @@ -3931,7 +3990,7 @@ _nvme () { ;; (endurance-event-agg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_endurance_event_agg _log_endurance_event_agg=( @@ -3961,7 +4020,7 @@ _nvme () { ;; (error) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_error _log_error=( @@ -4005,7 +4064,7 @@ _nvme () { ;; (fid-support-effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_fid_support_effects _log_fid_support_effects=( @@ -4029,7 +4088,7 @@ _nvme () { ;; (fw) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_fw _log_fw=( @@ -4055,7 +4114,7 @@ _nvme () { ;; (host-discovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_host_discovery _log_host_discovery=( @@ -4083,7 +4142,7 @@ _nvme () { ;; (lba-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_lba_status _log_lba_status=( @@ -4109,7 +4168,7 @@ _nvme () { ;; (media-unit-stat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_media_unit_stat _log_media_unit_stat=( @@ -4137,7 +4196,7 @@ _nvme () { ;; (mgmt-addr-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_mgmt_addr_list _log_mgmt_addr_list=( @@ -4161,7 +4220,7 @@ _nvme () { ;; (mi-cmd-support-effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_mi_cmd_support_effects _log_mi_cmd_support_effects=( @@ -4185,7 +4244,7 @@ _nvme () { ;; (persistent-event) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_persistent_event _log_persistent_event=( @@ -4215,7 +4274,7 @@ _nvme () { ;; (phy-rx-eom) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_phy_rx_eom _log_phy_rx_eom=( @@ -4243,7 +4302,7 @@ _nvme () { ;; (power-measurement) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_power_measurement _log_power_measurement=( @@ -4269,7 +4328,7 @@ _nvme () { ;; (pred-lat-event-agg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_pred_lat_event_agg _log_pred_lat_event_agg=( @@ -4299,7 +4358,7 @@ _nvme () { ;; (predictable-lat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_predictable_lat _log_predictable_lat=( @@ -4327,7 +4386,7 @@ _nvme () { ;; (pull-model-ddc-req) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_pull_model_ddc_req _log_pull_model_ddc_req=( @@ -4353,7 +4412,7 @@ _nvme () { ;; (reachability-associations) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_reachability_associations _log_reachability_associations=( @@ -4381,7 +4440,7 @@ _nvme () { ;; (reachability-groups) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_reachability_groups _log_reachability_groups=( @@ -4409,7 +4468,7 @@ _nvme () { ;; (resv-notif) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_resv_notif _log_resv_notif=( @@ -4433,7 +4492,7 @@ _nvme () { ;; (rotational-media-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_rotational_media_info _log_rotational_media_info=( @@ -4459,7 +4518,7 @@ _nvme () { ;; (sanitize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_sanitize _log_sanitize=( @@ -4487,7 +4546,7 @@ _nvme () { ;; (self-test) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_self_test _log_self_test=( @@ -4513,7 +4572,7 @@ _nvme () { ;; (smart) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_smart _log_smart=( @@ -4541,7 +4600,7 @@ _nvme () { ;; (supported-cap-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_supported_cap_config _log_supported_cap_config=( @@ -4569,7 +4628,7 @@ _nvme () { ;; (supported-pages) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _log_supported_pages _log_supported_pages=( @@ -4593,7 +4652,7 @@ _nvme () { ;; (telemetry) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _log_telemetry @@ -4648,7 +4707,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _mangoboost_id_ctrl _mangoboost_id_ctrl=( @@ -4707,7 +4766,7 @@ _nvme () { case ${words[2]} in (clear-error-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_clear_error_log _memblaze_clear_error_log=( @@ -4731,7 +4790,7 @@ _nvme () { ;; (get-pm-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_get_pm_status _memblaze_get_pm_status=( @@ -4755,7 +4814,7 @@ _nvme () { ;; (lat-get-feature-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_get_feature_x _memblaze_lat_get_feature_x=( @@ -4779,7 +4838,7 @@ _nvme () { ;; (lat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_log _memblaze_lat_log=( @@ -4805,7 +4864,7 @@ _nvme () { ;; (lat-log-print) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_log_print _memblaze_lat_log_print=( @@ -4829,7 +4888,7 @@ _nvme () { ;; (lat-log-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_log_print_x _memblaze_lat_log_print_x=( @@ -4855,7 +4914,7 @@ _nvme () { ;; (lat-set-feature-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_set_feature_x _memblaze_lat_set_feature_x=( @@ -4889,12 +4948,25 @@ _nvme () { ;; (lat-stats) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _memblaze_lat_stats _memblaze_lat_stats=( --disable':Disable LST' -d':alias for --disable' + --dry-run':show command instead of executing' --enable':Enable LST' -e':alias for --enable' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -4904,7 +4976,7 @@ _nvme () { ;; (lat-stats-print) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_stats_print _memblaze_lat_stats_print=( @@ -4930,7 +5002,7 @@ _nvme () { ;; (lat-stats-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_lat_stats_print_x _memblaze_lat_stats_print_x=( @@ -4956,7 +5028,7 @@ _nvme () { ;; (perf-stats-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_perf_stats_print_x _memblaze_perf_stats_print_x=( @@ -4984,7 +5056,7 @@ _nvme () { ;; (select-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--fw -f" && return local _memblaze_select_download @@ -5013,7 +5085,7 @@ _nvme () { ;; (set-pm-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_set_pm_status _memblaze_set_pm_status=( @@ -5041,7 +5113,7 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _memblaze_smart_log_add _memblaze_smart_log_add=( @@ -5069,10 +5141,23 @@ _nvme () { ;; (smart-log-add-x) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _memblaze_smart_log_add_x _memblaze_smart_log_add_x=( + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' --raw-binary':dump the whole log buffer in binary format' -b':alias for --raw-binary' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -5124,7 +5209,7 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_clear_fw_activate_history _micron_clear_fw_activate_history=( @@ -5148,7 +5233,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_clear_pcie_correctable_errors _micron_clear_pcie_correctable_errors=( @@ -5172,7 +5257,7 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_cloud_SSD_plugin_version _micron_cloud_SSD_plugin_version=( @@ -5196,7 +5281,7 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_cloud_boot_SSD_version _micron_cloud_boot_SSD_version=( @@ -5220,7 +5305,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_id_ctrl _micron_id_ctrl=( @@ -5244,7 +5329,7 @@ _nvme () { ;; (latency-logs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_latency_logs _micron_latency_logs=( @@ -5268,7 +5353,7 @@ _nvme () { ;; (latency-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_latency_stats _micron_latency_stats=( @@ -5294,7 +5379,7 @@ _nvme () { ;; (latency-tracking) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_latency_tracking _micron_latency_tracking=( @@ -5324,7 +5409,7 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_log_page_directory _micron_log_page_directory=( @@ -5348,7 +5433,7 @@ _nvme () { ;; (plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_plugin_version _micron_plugin_version=( @@ -5372,7 +5457,7 @@ _nvme () { ;; (select-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--fw -f" && return local _micron_select_download @@ -5401,7 +5486,7 @@ _nvme () { ;; (smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_smart_log _micron_smart_log=( @@ -5425,7 +5510,7 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_cloud_log _micron_vs_cloud_log=( @@ -5449,7 +5534,7 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_vs_device_waf _micron_vs_device_waf=( @@ -5473,7 +5558,7 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_drive_info _micron_vs_drive_info=( @@ -5497,7 +5582,7 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_fw_activate_history _micron_vs_fw_activate_history=( @@ -5521,7 +5606,7 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--package -p" && return local _micron_vs_internal_log @@ -5552,7 +5637,7 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_nand_stats _micron_vs_nand_stats=( @@ -5576,7 +5661,7 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_pcie_stats _micron_vs_pcie_stats=( @@ -5600,7 +5685,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_smart_add_log _micron_vs_smart_add_log=( @@ -5624,7 +5709,7 @@ _nvme () { ;; (vs-smart-ext-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_smart_ext_log _micron_vs_smart_ext_log=( @@ -5648,7 +5733,7 @@ _nvme () { ;; (vs-smbus-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_vs_smbus_option _micron_vs_smbus_option=( @@ -5678,7 +5763,7 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _micron_vs_telemetry_controller_option _micron_vs_telemetry_controller_option=( @@ -5706,7 +5791,7 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_temperature_stats _micron_vs_temperature_stats=( @@ -5730,7 +5815,7 @@ _nvme () { ;; (vs-vendor-telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_vendor_telemetry_log _micron_vs_vendor_telemetry_log=( @@ -5754,7 +5839,7 @@ _nvme () { ;; (vs-work-load-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _micron_vs_work_load_log _micron_vs_work_load_log=( @@ -5795,7 +5880,7 @@ _nvme () { case ${words[2]} in (show) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _nbft_show _nbft_show=( @@ -5844,14 +5929,14 @@ _nvme () { case ${words[2]} in (ontapdevices) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal column json" \ "--output-format-version" "1 2" && return local _netapp_ontapdevices _netapp_ontapdevices=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|column|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -5868,14 +5953,14 @@ _nvme () { ;; (smdevices) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal column json" \ "--output-format-version" "1 2" && return local _netapp_smdevices _netapp_smdevices=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|column|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -5913,7 +5998,7 @@ _nvme () { case ${words[2]} in (attach) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_attach _ns_attach=( @@ -5943,7 +6028,7 @@ _nvme () { ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_create _ns_create=( @@ -6007,7 +6092,7 @@ _nvme () { ;; (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_delete _ns_delete=( @@ -6035,7 +6120,7 @@ _nvme () { ;; (detach) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_detach _ns_detach=( @@ -6065,7 +6150,7 @@ _nvme () { ;; (get-id) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_get_id _ns_get_id=( @@ -6106,7 +6191,7 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _nvidia_id_ctrl _nvidia_id_ctrl=( @@ -6152,7 +6237,7 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_recv @@ -6191,7 +6276,7 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_send @@ -6276,7 +6361,7 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_clear_fw_activate_history _ocp_clear_fw_activate_history=( @@ -6302,7 +6387,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_clear_pcie_correctable_errors _ocp_clear_pcie_correctable_errors=( @@ -6328,7 +6413,7 @@ _nvme () { ;; (device-capability-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_device_capability_log _ocp_device_capability_log=( @@ -6354,7 +6439,7 @@ _nvme () { ;; (eol-plp-failure-mode) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _ocp_eol_plp_failure_mode @@ -6387,7 +6472,7 @@ _nvme () { ;; (error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_error_recovery_log _ocp_error_recovery_log=( @@ -6413,7 +6498,7 @@ _nvme () { ;; (fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_fw_activate_history _ocp_fw_activate_history=( @@ -6437,7 +6522,7 @@ _nvme () { ;; (get-clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_clear_pcie_correctable_errors @@ -6468,7 +6553,7 @@ _nvme () { ;; (get-dssd-async-event-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _ocp_get_dssd_async_event_config @@ -6495,7 +6580,7 @@ _nvme () { ;; (get-dssd-power-state-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _ocp_get_dssd_power_state_feature @@ -6526,7 +6611,7 @@ _nvme () { ;; (get-enable-ieee1667-silo) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_enable_ieee1667_silo @@ -6555,7 +6640,7 @@ _nvme () { ;; (get-error-injection) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_error_injection @@ -6586,7 +6671,7 @@ _nvme () { ;; (get-idle-wakeup-time) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_idle_wakeup_time @@ -6617,7 +6702,7 @@ _nvme () { ;; (get-latency-monitor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_latency_monitor @@ -6648,7 +6733,7 @@ _nvme () { ;; (get-plp-health-check-interval) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" && return local _ocp_get_plp_health_check_interval @@ -6675,7 +6760,7 @@ _nvme () { ;; (get-telemetry-profile) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _ocp_get_telemetry_profile @@ -6707,7 +6792,7 @@ _nvme () { (hardware-component-log) _nvme_opt_vals "--comp-id -i" "asic nand dram pmic pcb cap reg case sn country hw-rev born-on-date vendor" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_hardware_component_log _ocp_hardware_component_log=( @@ -6735,7 +6820,7 @@ _nvme () { ;; (internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -f" && return local _ocp_internal_log @@ -6774,7 +6859,7 @@ _nvme () { ;; (latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_latency_monitor_log _ocp_latency_monitor_log=( @@ -6800,7 +6885,7 @@ _nvme () { ;; (persistent-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_persistent_event_log _ocp_persistent_event_log=( @@ -6830,7 +6915,7 @@ _nvme () { ;; (set-dssd-async-event-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_dssd_async_event_config _ocp_set_dssd_async_event_config=( @@ -6858,7 +6943,7 @@ _nvme () { ;; (set-dssd-power-state-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_dssd_power_state_feature _ocp_set_dssd_power_state_feature=( @@ -6888,7 +6973,7 @@ _nvme () { ;; (set-enable-ieee1667-silo) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_enable_ieee1667_silo _ocp_set_enable_ieee1667_silo=( @@ -6918,7 +7003,7 @@ _nvme () { ;; (set-error-injection) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _ocp_set_error_injection @@ -6955,7 +7040,7 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_latency_monitor_feature _ocp_set_latency_monitor_feature=( @@ -6999,7 +7084,7 @@ _nvme () { ;; (set-plp-health-check-interval) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_plp_health_check_interval _ocp_set_plp_health_check_interval=( @@ -7029,7 +7114,7 @@ _nvme () { ;; (set-telemetry-profile) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_set_telemetry_profile _ocp_set_telemetry_profile=( @@ -7055,7 +7140,7 @@ _nvme () { ;; (smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_smart_add_log _ocp_smart_add_log=( @@ -7081,7 +7166,7 @@ _nvme () { ;; (tcg-configuration-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_tcg_configuration_log _ocp_tcg_configuration_log=( @@ -7107,7 +7192,7 @@ _nvme () { ;; (telemetry-string-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -f" && return local _ocp_telemetry_string_log @@ -7136,7 +7221,7 @@ _nvme () { ;; (unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ocp_unsupported_reqs_log _ocp_unsupported_reqs_log=( @@ -7182,7 +7267,7 @@ _nvme () { case ${words[2]} in (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _registry_delete _registry_delete=( @@ -7208,7 +7293,7 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _registry_list _registry_list=( @@ -7232,7 +7317,7 @@ _nvme () { ;; (retrieve) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _registry_retrieve _registry_retrieve=( @@ -7258,7 +7343,7 @@ _nvme () { ;; (update) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _registry_update _registry_update=( @@ -7306,7 +7391,7 @@ _nvme () { case ${words[2]} in (acquire) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_acquire _resv_acquire=( @@ -7342,7 +7427,7 @@ _nvme () { ;; (register) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_register _resv_register=( @@ -7378,7 +7463,7 @@ _nvme () { ;; (release) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_release _resv_release=( @@ -7412,7 +7497,7 @@ _nvme () { ;; (report) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_report _resv_report=( @@ -7467,7 +7552,7 @@ _nvme () { case ${words[2]} in (info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _rpmb_info _rpmb_info=( @@ -7491,7 +7576,7 @@ _nvme () { ;; (program-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--keyfile -g" && return local _rpmb_program_key @@ -7522,7 +7607,7 @@ _nvme () { ;; (read-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--msgfile -f" && return local _rpmb_read_config @@ -7549,7 +7634,7 @@ _nvme () { ;; (read-counter) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _rpmb_read_counter _rpmb_read_counter=( @@ -7575,7 +7660,7 @@ _nvme () { ;; (read-data) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--msgfile -f" && return local _rpmb_read_data @@ -7608,7 +7693,7 @@ _nvme () { ;; (write-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return @@ -7642,7 +7727,7 @@ _nvme () { ;; (write-data) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return @@ -7699,7 +7784,7 @@ _nvme () { case ${words[2]} in (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _samsung_vs_internal_log @@ -7762,7 +7847,7 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_clear_fw_activate_history _seagate_clear_fw_activate_history=( @@ -7788,7 +7873,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_clear_pcie_correctable_errors _seagate_clear_pcie_correctable_errors=( @@ -7814,7 +7899,7 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_cloud_SSD_plugin_version _seagate_cloud_SSD_plugin_version=( @@ -7838,7 +7923,7 @@ _nvme () { ;; (get-ctrl-tele) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_get_ctrl_tele _seagate_get_ctrl_tele=( @@ -7866,7 +7951,7 @@ _nvme () { ;; (get-host-tele) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_get_host_tele _seagate_get_host_tele=( @@ -7896,7 +7981,7 @@ _nvme () { ;; (plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_plugin_version _seagate_plugin_version=( @@ -7920,7 +8005,7 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_vs_fw_activate_history _seagate_vs_fw_activate_history=( @@ -7944,7 +8029,7 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--dump-file -f" && return local _seagate_vs_internal_log @@ -7973,7 +8058,7 @@ _nvme () { ;; (vs-log-page-sup) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_vs_log_page_sup _seagate_vs_log_page_sup=( @@ -7997,7 +8082,7 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_vs_pcie_stats _seagate_vs_pcie_stats=( @@ -8021,7 +8106,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_vs_smart_add_log _seagate_vs_smart_add_log=( @@ -8045,7 +8130,7 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _seagate_vs_temperature_stats _seagate_vs_temperature_stats=( @@ -8087,7 +8172,7 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _security_recv _security_recv=( @@ -8127,7 +8212,7 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--file -f" && return local _security_send @@ -8189,12 +8274,25 @@ _nvme () { case ${words[2]} in (discover|1) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _sed_discover _sed_discover=( + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' --udev':Print locking information in form suitable for udev rules' -u':alias for --udev' --verbose':Print extended discovery information' -V':alias for --verbose' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -8204,10 +8302,23 @@ _nvme () { ;; (initialize) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _sed_initialize _sed_initialize=( + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' --read-only':Set locking range to read-only' -r':alias for --read-only' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -8217,12 +8328,25 @@ _nvme () { ;; (lock) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _sed_lock _sed_lock=( --ask-key':prompt for SED authentication key' -k':alias for --ask-key' + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' --read-only':Set locking range to read-only' -r':alias for --read-only' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -8232,16 +8356,49 @@ _nvme () { ;; (password) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return + local _sed_password + _sed_password=( + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' + --help':show help' + -h':alias for --help' + ) + _arguments '*:: :->subcmds' + _nvme_describe "nvme sed password options" _sed_password _nvme_has_device || compadd -- /dev/nvme*(N) ;; (revert) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _sed_revert _sed_revert=( --destructive':destructive revert' -e':alias for --destructive' + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' --psid':PSID revert' -p':alias for --psid' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -8251,12 +8408,25 @@ _nvme () { ;; (unlock) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return local _sed_unlock _sed_unlock=( --ask-key':prompt for SED authentication key' -k':alias for --ask-key' + --dry-run':show command instead of executing' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' --read-only':Set locking range to read-only' -r':alias for --read-only' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -8292,7 +8462,7 @@ _nvme () { case ${words[2]} in (change-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_change_cap _sfx_change_cap=( @@ -8322,7 +8492,7 @@ _nvme () { ;; (dump-evtlog) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--file -f" \ "--output -O" && return @@ -8358,7 +8528,7 @@ _nvme () { ;; (expand-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_expand_cap _sfx_expand_cap=( @@ -8392,7 +8562,7 @@ _nvme () { ;; (get-bad-block) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_get_bad_block _sfx_get_bad_block=( @@ -8416,7 +8586,7 @@ _nvme () { ;; (get-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_get_feature _sfx_get_feature=( @@ -8444,7 +8614,7 @@ _nvme () { ;; (lat-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_lat_stats _sfx_lat_stats=( @@ -8472,7 +8642,7 @@ _nvme () { ;; (query-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_query_cap _sfx_query_cap=( @@ -8498,7 +8668,7 @@ _nvme () { ;; (set-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_set_feature _sfx_set_feature=( @@ -8530,7 +8700,7 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_smart_log_add _sfx_smart_log_add=( @@ -8560,7 +8730,7 @@ _nvme () { ;; (status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sfx_status _sfx_status=( @@ -8606,7 +8776,7 @@ _nvme () { case ${words[2]} in (get-additional-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _shannon_get_additional_feature @@ -8645,7 +8815,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _shannon_id_ctrl _shannon_id_ctrl=( @@ -8673,7 +8843,7 @@ _nvme () { ;; (set-additioal-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _shannon_set_additioal_feature @@ -8710,7 +8880,7 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _shannon_smart_log_add _shannon_smart_log_add=( @@ -8782,7 +8952,7 @@ _nvme () { case ${words[2]} in (capabilities) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_capabilities _sndk_capabilities=( @@ -8806,7 +8976,7 @@ _nvme () { ;; (clear-assert-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_clear_assert_dump _sndk_clear_assert_dump=( @@ -8830,7 +9000,7 @@ _nvme () { ;; (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_clear_fw_activate_history _sndk_clear_fw_activate_history=( @@ -8854,7 +9024,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_clear_pcie_correctable_errors _sndk_clear_pcie_correctable_errors=( @@ -8878,7 +9048,7 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_cloud_SSD_plugin_version _sndk_cloud_SSD_plugin_version=( @@ -8902,7 +9072,7 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_cloud_boot_SSD_version _sndk_cloud_boot_SSD_version=( @@ -8928,7 +9098,7 @@ _nvme () { ;; (cu-smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_cu_smart_log _sndk_cu_smart_log=( @@ -8954,7 +9124,7 @@ _nvme () { ;; (drive-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_drive_resize _sndk_drive_resize=( @@ -8980,7 +9150,7 @@ _nvme () { ;; (get-dev-capabilities-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_get_dev_capabilities_log _sndk_get_dev_capabilities_log=( @@ -9004,7 +9174,7 @@ _nvme () { ;; (get-drive-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_get_drive_status _sndk_get_drive_status=( @@ -9028,7 +9198,7 @@ _nvme () { ;; (get-error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_get_error_recovery_log _sndk_get_error_recovery_log=( @@ -9052,7 +9222,7 @@ _nvme () { ;; (get-latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_get_latency_monitor_log _sndk_get_latency_monitor_log=( @@ -9076,7 +9246,7 @@ _nvme () { ;; (get-unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_get_unsupported_reqs_log _sndk_get_unsupported_reqs_log=( @@ -9100,7 +9270,7 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_log_page_directory _sndk_log_page_directory=( @@ -9124,7 +9294,7 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_namespace_resize _sndk_namespace_resize=( @@ -9152,7 +9322,7 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_set_latency_monitor_feature _sndk_set_latency_monitor_feature=( @@ -9196,7 +9366,7 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_cloud_log _sndk_vs_cloud_log=( @@ -9222,7 +9392,7 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_device_waf _sndk_vs_device_waf=( @@ -9248,7 +9418,7 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_drive_info _sndk_vs_drive_info=( @@ -9272,7 +9442,7 @@ _nvme () { ;; (vs-error-reason-identifier) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--file -O" && return local _sndk_vs_error_reason_identifier @@ -9301,7 +9471,7 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_fw_activate_history _sndk_vs_fw_activate_history=( @@ -9325,7 +9495,7 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_hw_rev_log _sndk_vs_hw_rev_log=( @@ -9351,24 +9521,37 @@ _nvme () { ;; (vs-internal-log) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" \ "--type -t" && return local _sndk_vs_internal_log _sndk_vs_internal_log=( --data-area=':Data area to retrieve up to. Supported for telemetry, see man page for other use cases.' -d':alias for --data-area' + --dry-run':show command instead of executing' --file-size=':Output file size. Deprecated, see man page for supported devices.' -f':alias for --file-size' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' --offset=':Output file data offset. Deprecated, see man page for supported devices.' -e':alias for --offset' --output-file=':Output file pathname.' -O':alias for --output-file' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' --transfer-size=':Data retrieval transfer size.' -s':alias for --transfer-size' --type=':Telemetry type - NONE, HOST, CONTROLLER, or BOTH: NONE - Default, capture without using NVMe telemetry. HOST - Host-initiated telemetry. CONTROLLER - Controller-initiated telemetry. BOTH - Both HOST and CONTROLLER telemetry packaged in tar file.' -t':alias for --type' --verbose':Display more debug messages.' -V':alias for --verbose' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -9378,7 +9561,7 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_nand_stats _sndk_vs_nand_stats=( @@ -9402,7 +9585,7 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_pcie_stats _sndk_vs_pcie_stats=( @@ -9426,7 +9609,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_smart_add_log _sndk_vs_smart_add_log=( @@ -9458,7 +9641,7 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_telemetry_controller_option _sndk_vs_telemetry_controller_option=( @@ -9488,7 +9671,7 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sndk_vs_temperature_stats _sndk_vs_temperature_stats=( @@ -9544,7 +9727,7 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_clear_fw_activate_history _solidigm_clear_fw_activate_history=( @@ -9570,7 +9753,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_clear_pcie_correctable_errors _solidigm_clear_pcie_correctable_errors=( @@ -9596,7 +9779,7 @@ _nvme () { ;; (cloud-SSDplugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_cloud_SSDplugin_version _solidigm_cloud_SSDplugin_version=( @@ -9620,7 +9803,7 @@ _nvme () { ;; (garbage-collect-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_garbage_collect_log _solidigm_garbage_collect_log=( @@ -9644,7 +9827,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_id_ctrl _solidigm_id_ctrl=( @@ -9672,7 +9855,7 @@ _nvme () { ;; (latency-tracking-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_latency_tracking_log _solidigm_latency_tracking_log=( @@ -9706,7 +9889,7 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_log_page_directory _solidigm_log_page_directory=( @@ -9730,7 +9913,7 @@ _nvme () { ;; (market-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_market_log _solidigm_market_log=( @@ -9756,7 +9939,7 @@ _nvme () { ;; (parse-telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--config-file -j" \ "--source-file -s" && return @@ -9792,7 +9975,7 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_smart_log_add _solidigm_smart_log_add=( @@ -9818,7 +10001,7 @@ _nvme () { ;; (temp-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_temp_stats _solidigm_temp_stats=( @@ -9844,14 +10027,14 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _solidigm_vs_drive_info _solidigm_vs_drive_info=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -9868,7 +10051,7 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_vs_fw_activate_history _solidigm_vs_fw_activate_history=( @@ -9892,7 +10075,7 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--dir-name -d" && return local _solidigm_vs_internal_log @@ -9921,7 +10104,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_vs_smart_add_log _solidigm_vs_smart_add_log=( @@ -9947,7 +10130,7 @@ _nvme () { ;; (workload-tracker) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _solidigm_workload_tracker _solidigm_workload_tracker=( @@ -10012,7 +10195,7 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ssstc_smart_log_add _ssstc_smart_log_add=( @@ -10061,7 +10244,7 @@ _nvme () { case ${words[2]} in (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _toshiba_clear_pcie_correctable_errors _toshiba_clear_pcie_correctable_errors=( @@ -10085,7 +10268,7 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _toshiba_vs_internal_log @@ -10114,7 +10297,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _toshiba_vs_smart_add_log @@ -10163,7 +10346,7 @@ _nvme () { case ${words[2]} in (badblock) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _transcend_badblock _transcend_badblock=( @@ -10187,7 +10370,7 @@ _nvme () { ;; (healthvalue) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _transcend_healthvalue _transcend_healthvalue=( @@ -10250,7 +10433,7 @@ _nvme () { case ${words[2]} in (save-smart-to-vtview-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _virtium_save_smart_to_vtview_log @@ -10283,7 +10466,7 @@ _nvme () { ;; (show-identify) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _virtium_show_identify _virtium_show_identify=( @@ -10360,7 +10543,7 @@ _nvme () { case ${words[2]} in (cap-diag) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _wdc_cap_diag @@ -10389,7 +10572,7 @@ _nvme () { ;; (capabilities) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_capabilities _wdc_capabilities=( @@ -10413,7 +10596,7 @@ _nvme () { ;; (clear-assert-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_clear_assert_dump _wdc_clear_assert_dump=( @@ -10437,7 +10620,7 @@ _nvme () { ;; (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_clear_fw_activate_history _wdc_clear_fw_activate_history=( @@ -10461,7 +10644,7 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_clear_pcie_correctable_errors _wdc_clear_pcie_correctable_errors=( @@ -10485,7 +10668,7 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_cloud_SSD_plugin_version _wdc_cloud_SSD_plugin_version=( @@ -10509,7 +10692,7 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_cloud_boot_SSD_version _wdc_cloud_boot_SSD_version=( @@ -10535,7 +10718,7 @@ _nvme () { ;; (cu-smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_cu_smart_log _wdc_cu_smart_log=( @@ -10561,7 +10744,7 @@ _nvme () { ;; (drive-essentials) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--dir-name -d" && return local _wdc_drive_essentials @@ -10588,7 +10771,7 @@ _nvme () { ;; (drive-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _wdc_drive_log @@ -10615,7 +10798,7 @@ _nvme () { ;; (drive-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_drive_resize _wdc_drive_resize=( @@ -10641,7 +10824,7 @@ _nvme () { ;; (enc-get-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _wdc_enc_get_log @@ -10672,7 +10855,7 @@ _nvme () { ;; (get-crash-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _wdc_get_crash_dump @@ -10699,7 +10882,7 @@ _nvme () { ;; (get-dev-capabilities-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_get_dev_capabilities_log _wdc_get_dev_capabilities_log=( @@ -10723,7 +10906,7 @@ _nvme () { ;; (get-drive-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_get_drive_status _wdc_get_drive_status=( @@ -10747,7 +10930,7 @@ _nvme () { ;; (get-error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_get_error_recovery_log _wdc_get_error_recovery_log=( @@ -10771,7 +10954,7 @@ _nvme () { ;; (get-latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_get_latency_monitor_log _wdc_get_latency_monitor_log=( @@ -10795,7 +10978,7 @@ _nvme () { ;; (get-pfail-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _wdc_get_pfail_dump @@ -10822,7 +11005,7 @@ _nvme () { ;; (get-unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_get_unsupported_reqs_log _wdc_get_unsupported_reqs_log=( @@ -10846,7 +11029,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_id_ctrl _wdc_id_ctrl=( @@ -10874,7 +11057,7 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_log_page_directory _wdc_log_page_directory=( @@ -10898,7 +11081,7 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_namespace_resize _wdc_namespace_resize=( @@ -10926,7 +11109,7 @@ _nvme () { ;; (purge) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_purge _wdc_purge=( @@ -10950,7 +11133,7 @@ _nvme () { ;; (purge-monitor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_purge_monitor _wdc_purge_monitor=( @@ -10974,7 +11157,7 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_set_latency_monitor_feature _wdc_set_latency_monitor_feature=( @@ -11018,7 +11201,7 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_cloud_log _wdc_vs_cloud_log=( @@ -11044,7 +11227,7 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_device_waf _wdc_vs_device_waf=( @@ -11070,7 +11253,7 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_drive_info _wdc_vs_drive_info=( @@ -11094,7 +11277,7 @@ _nvme () { ;; (vs-error-reason-identifier) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--file -O" && return local _wdc_vs_error_reason_identifier @@ -11123,7 +11306,7 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_fw_activate_history _wdc_vs_fw_activate_history=( @@ -11147,7 +11330,7 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_hw_rev_log _wdc_vs_hw_rev_log=( @@ -11173,24 +11356,37 @@ _nvme () { ;; (vs-internal-log) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" \ "--type -t" && return local _wdc_vs_internal_log _wdc_vs_internal_log=( --data-area=':Data area to retrieve up to. Supported for telemetry, see man page for other use cases.' -d':alias for --data-area' + --dry-run':show command instead of executing' --file-size=':Output file size. Deprecated, see man page for supported devices.' -f':alias for --file-size' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' --offset=':Output file data offset. Deprecated, see man page for supported devices.' -e':alias for --offset' --output-file=':Output file pathname.' -O':alias for --output-file' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' --transfer-size=':Data retrieval transfer size.' -s':alias for --transfer-size' --type=':Telemetry type - NONE, HOST, or CONTROLLER: NONE - Default, capture without using NVMe telemetry. HOST - Host-initiated telemetry. CONTROLLER - Controller-initiated telemetry.' -t':alias for --type' --verbose':Display more debug messages.' -V':alias for --verbose' + --verbose':Increase output verbosity' + -v':alias for --verbose' --help':show help' -h':alias for --help' ) @@ -11200,7 +11396,7 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_nand_stats _wdc_vs_nand_stats=( @@ -11224,7 +11420,7 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_pcie_stats _wdc_vs_pcie_stats=( @@ -11248,7 +11444,7 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_smart_add_log _wdc_vs_smart_add_log=( @@ -11280,7 +11476,7 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_telemetry_controller_option _wdc_vs_telemetry_controller_option=( @@ -11310,7 +11506,7 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _wdc_vs_temperature_stats _wdc_vs_temperature_stats=( @@ -11351,7 +11547,7 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ymtc_smart_log_add _ymtc_smart_log_add=( @@ -11410,7 +11606,7 @@ _nvme () { case ${words[2]} in (changed-zone-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_changed_zone_list _zns_changed_zone_list=( @@ -11438,7 +11634,7 @@ _nvme () { ;; (close-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_close_zone _zns_close_zone=( @@ -11468,7 +11664,7 @@ _nvme () { ;; (finish-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_finish_zone _zns_finish_zone=( @@ -11498,7 +11694,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_id_ctrl _zns_id_ctrl=( @@ -11522,7 +11718,7 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_id_ns _zns_id_ns=( @@ -11552,7 +11748,7 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" && return local _zns_list _zns_list=( @@ -11576,7 +11772,7 @@ _nvme () { ;; (offline-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_offline_zone _zns_offline_zone=( @@ -11606,7 +11802,7 @@ _nvme () { ;; (open-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_open_zone _zns_open_zone=( @@ -11638,7 +11834,7 @@ _nvme () { ;; (report-zones) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_report_zones _zns_report_zones=( @@ -11676,7 +11872,7 @@ _nvme () { ;; (reset-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_reset_zone _zns_reset_zone=( @@ -11706,7 +11902,7 @@ _nvme () { ;; (set-zone-desc) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _zns_set_zone_desc @@ -11739,7 +11935,7 @@ _nvme () { ;; (zone-append) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return @@ -11787,7 +11983,7 @@ _nvme () { ;; (zone-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_zone_mgmt_recv _zns_zone_mgmt_recv=( @@ -11823,7 +12019,7 @@ _nvme () { ;; (zone-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _zns_zone_mgmt_send @@ -11862,7 +12058,7 @@ _nvme () { ;; (zrwa-flush-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _zns_zrwa_flush_zone _zns_zrwa_flush_zone=( @@ -11896,7 +12092,7 @@ _nvme () { ;; (admin-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" \ "--metadata -M" && return @@ -11966,7 +12162,7 @@ _nvme () { ;; (ana-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ana_log _ana_log=( @@ -11992,7 +12188,7 @@ _nvme () { ;; (attach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _attach_ns _attach_ns=( @@ -12022,7 +12218,7 @@ _nvme () { ;; (ave-discovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ave_discovery_log _ave_discovery_log=( @@ -12048,7 +12244,7 @@ _nvme () { ;; (boot-part-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -f" && return local _boot_part_log @@ -12077,7 +12273,7 @@ _nvme () { ;; (capacity-mgmt) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _capacity_mgmt _capacity_mgmt=( @@ -12111,7 +12307,7 @@ _nvme () { ;; (changed-alloc-ns-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _changed_alloc_ns_list_log _changed_alloc_ns_list_log=( @@ -12137,7 +12333,7 @@ _nvme () { ;; (changed-ns-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _changed_ns_list_log _changed_ns_list_log=( @@ -12163,7 +12359,7 @@ _nvme () { ;; (check-dhchap-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _check_dhchap_key _check_dhchap_key=( @@ -12189,7 +12385,7 @@ _nvme () { ;; (check-tls-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _check_tls_key _check_tls_key=( @@ -12227,7 +12423,7 @@ _nvme () { ;; (cmdset-ind-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _cmdset_ind_id_ns _cmdset_ind_id_ns=( @@ -12255,7 +12451,7 @@ _nvme () { ;; (compare) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return @@ -12324,7 +12520,7 @@ _nvme () { ;; (connect) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" \ "--devid-file" && return @@ -12407,7 +12603,7 @@ _nvme () { ;; (connect-all) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" \ "--raw -r" && return @@ -12498,7 +12694,7 @@ _nvme () { ;; (copy) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _copy _copy=( @@ -12564,7 +12760,7 @@ _nvme () { ;; (create-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _create_ns _create_ns=( @@ -12628,7 +12824,7 @@ _nvme () { ;; (delete-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _delete_ns _delete_ns=( @@ -12656,7 +12852,7 @@ _nvme () { ;; (detach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _detach_ns _detach_ns=( @@ -12686,7 +12882,7 @@ _nvme () { ;; (device-self-test) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _device_self_test _device_self_test=( @@ -12718,7 +12914,7 @@ _nvme () { ;; (dim) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dim _dim=( @@ -12748,7 +12944,7 @@ _nvme () { ;; (dir-receive) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dir_receive _dir_receive=( @@ -12786,7 +12982,7 @@ _nvme () { ;; (dir-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _dir_send @@ -12829,7 +13025,7 @@ _nvme () { ;; (disconnect) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _disconnect _disconnect=( @@ -12908,7 +13104,7 @@ _nvme () { ;; (disconnect-all) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _disconnect_all _disconnect_all=( @@ -12936,7 +13132,7 @@ _nvme () { ;; (discover) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--config -J" \ "--raw -r" && return @@ -13027,7 +13223,7 @@ _nvme () { ;; (dispersed-ns-participating-nss-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dispersed_ns_participating_nss_log _dispersed_ns_participating_nss_log=( @@ -13053,7 +13249,7 @@ _nvme () { ;; (dsm) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _dsm _dsm=( @@ -13093,7 +13289,7 @@ _nvme () { ;; (effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _effects_log _effects_log=( @@ -13121,7 +13317,7 @@ _nvme () { ;; (endurance-event-agg-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _endurance_event_agg_log _endurance_event_agg_log=( @@ -13151,7 +13347,7 @@ _nvme () { ;; (endurance-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _endurance_log _endurance_log=( @@ -13177,7 +13373,7 @@ _nvme () { ;; (error-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _error_log _error_log=( @@ -13221,7 +13417,7 @@ _nvme () { ;; (fid-support-effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fid_support_effects_log _fid_support_effects_log=( @@ -13245,7 +13441,7 @@ _nvme () { ;; (flush) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _flush _flush=( @@ -13271,7 +13467,7 @@ _nvme () { ;; (format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _format _format=( @@ -13315,7 +13511,7 @@ _nvme () { (fw-commit|fw-activate) _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fw_commit _fw_commit=( @@ -13347,7 +13543,7 @@ _nvme () { ;; (fw-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--fw -f" && return local _fw_download @@ -13385,7 +13581,7 @@ _nvme () { ;; (fw-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _fw_log _fw_log=( @@ -13411,7 +13607,7 @@ _nvme () { ;; (gen-dhchap-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _gen_dhchap_key _gen_dhchap_key=( @@ -13443,7 +13639,7 @@ _nvme () { ;; (gen-hostnqn) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _gen_hostnqn _gen_hostnqn=( @@ -13467,7 +13663,7 @@ _nvme () { ;; (gen-tls-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _gen_tls_key _gen_tls_key=( @@ -13512,7 +13708,7 @@ _nvme () { (get-feature) _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" && return local _get_feature @@ -13553,7 +13749,7 @@ _nvme () { ;; (get-lba-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _get_lba_status _get_lba_status=( @@ -13590,7 +13786,7 @@ _nvme () { (get-log) _nvme_opt_vals "--log-id -i" "supported-log-pages error smart fw-slot changed-attached-ns changed-ns cmd-effects device-self-test telemetry-host telemetry-ctrl endurance-group predictable-lat-nvmset predictable-lat-agg ana persistent-event lba-status endurance-grp-evt media-unit-status supported-cap-config-list fid-supported-effects mi-cmd-supported-effects cmd-and-feat-lockdown boot-partition rotational-media-info dispersed-ns-participating-ns mgmt-addr-list phy-rx-eom reachability-groups reachability-associations changed-alloc-ns-list fdp-configs fdp-ruh-usage fdp-stats fdp-events discover host-discover ave-discover pull-model-ddc-req reservation sanitize zns-changed-zones" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _get_log _get_log=( @@ -13642,7 +13838,7 @@ _nvme () { ;; (get-ns-id) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _get_ns_id _get_ns_id=( @@ -13666,7 +13862,7 @@ _nvme () { ;; (get-property) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _get_property _get_property=( @@ -13692,7 +13888,7 @@ _nvme () { ;; (get-reg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _get_reg _get_reg=( @@ -13746,7 +13942,7 @@ _nvme () { ;; (host-discovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _host_discovery_log _host_discovery_log=( @@ -13774,7 +13970,7 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ctrl _id_ctrl=( @@ -13802,7 +13998,7 @@ _nvme () { ;; (id-domain) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_domain _id_domain=( @@ -13828,7 +14024,7 @@ _nvme () { ;; (id-iocs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_iocs _id_iocs=( @@ -13854,7 +14050,7 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns _id_ns=( @@ -13885,7 +14081,7 @@ _nvme () { ;; (id-ns-granularity) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_granularity _id_ns_granularity=( @@ -13909,7 +14105,7 @@ _nvme () { ;; (id-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_ns_lba_format _id_ns_lba_format=( @@ -13937,7 +14133,7 @@ _nvme () { ;; (id-nvmset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_nvmset _id_nvmset=( @@ -13963,7 +14159,7 @@ _nvme () { ;; (id-uuid) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _id_uuid _id_uuid=( @@ -13989,7 +14185,7 @@ _nvme () { ;; (io-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _io_mgmt_recv @@ -14024,7 +14220,7 @@ _nvme () { ;; (io-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _io_mgmt_send @@ -14059,7 +14255,7 @@ _nvme () { ;; (io-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" \ "--metadata -M" && return @@ -14129,7 +14325,7 @@ _nvme () { ;; (lba-status-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lba_status_log _lba_status_log=( @@ -14155,14 +14351,14 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _list _list=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -14179,7 +14375,7 @@ _nvme () { ;; (list-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _list_ctrl _list_ctrl=( @@ -14190,7 +14386,7 @@ _nvme () { -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -14207,7 +14403,7 @@ _nvme () { ;; (list-endgrp) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _list_endgrp _list_endgrp=( @@ -14216,7 +14412,7 @@ _nvme () { -i':alias for --endgrp-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -14233,7 +14429,7 @@ _nvme () { ;; (list-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _list_ns _list_ns=( @@ -14246,7 +14442,7 @@ _nvme () { -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -14263,7 +14459,7 @@ _nvme () { ;; (list-secondary) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _list_secondary _list_secondary=( @@ -14291,14 +14487,14 @@ _nvme () { ;; (list-subsys) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" && return local _list_subsys _list_subsys=( --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal|json' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -14315,7 +14511,7 @@ _nvme () { ;; (lockdown) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _lockdown _lockdown=( @@ -14353,7 +14549,7 @@ _nvme () { ;; (media-unit-stat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _media_unit_stat_log _media_unit_stat_log=( @@ -14381,7 +14577,7 @@ _nvme () { ;; (mgmt-addr-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _mgmt_addr_list_log _mgmt_addr_list_log=( @@ -14405,7 +14601,7 @@ _nvme () { ;; (mi-cmd-support-effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _mi_cmd_support_effects_log _mi_cmd_support_effects_log=( @@ -14429,7 +14625,7 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_descs _ns_descs=( @@ -14457,7 +14653,7 @@ _nvme () { ;; (ns-rescan) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _ns_rescan _ns_rescan=( @@ -14481,7 +14677,7 @@ _nvme () { ;; (nvm-id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _nvm_id_ctrl _nvm_id_ctrl=( @@ -14505,7 +14701,7 @@ _nvme () { ;; (nvm-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _nvm_id_ns _nvm_id_ns=( @@ -14533,7 +14729,7 @@ _nvme () { ;; (nvm-id-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _nvm_id_ns_lba_format _nvm_id_ns_lba_format=( @@ -14561,7 +14757,7 @@ _nvme () { ;; (nvme-mi-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_recv @@ -14600,7 +14796,7 @@ _nvme () { ;; (nvme-mi-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_send @@ -14639,7 +14835,7 @@ _nvme () { ;; (persistent-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _persistent_event_log _persistent_event_log=( @@ -14669,7 +14865,7 @@ _nvme () { ;; (phy-rx-eom-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _phy_rx_eom_log _phy_rx_eom_log=( @@ -14697,7 +14893,7 @@ _nvme () { ;; (power-measurement-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _power_measurement_log _power_measurement_log=( @@ -14723,7 +14919,7 @@ _nvme () { ;; (pred-lat-event-agg-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _pred_lat_event_agg_log _pred_lat_event_agg_log=( @@ -14753,7 +14949,7 @@ _nvme () { ;; (predictable-lat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _predictable_lat_log _predictable_lat_log=( @@ -14781,7 +14977,7 @@ _nvme () { ;; (primary-ctrl-caps) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _primary_ctrl_caps _primary_ctrl_caps=( @@ -14807,7 +15003,7 @@ _nvme () { ;; (pull-model-ddc-req-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _pull_model_ddc_req_log _pull_model_ddc_req_log=( @@ -14833,7 +15029,7 @@ _nvme () { ;; (reachability-associations-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _reachability_associations_log _reachability_associations_log=( @@ -14861,7 +15057,7 @@ _nvme () { ;; (reachability-groups-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _reachability_groups_log _reachability_groups_log=( @@ -14889,7 +15085,7 @@ _nvme () { ;; (read) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return @@ -14958,7 +15154,7 @@ _nvme () { ;; (reset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _reset _reset=( @@ -14982,7 +15178,7 @@ _nvme () { ;; (resv-acquire) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_acquire _resv_acquire=( @@ -15018,7 +15214,7 @@ _nvme () { ;; (resv-notif-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_notif_log _resv_notif_log=( @@ -15042,7 +15238,7 @@ _nvme () { ;; (resv-register) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_register _resv_register=( @@ -15078,7 +15274,7 @@ _nvme () { ;; (resv-release) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_release _resv_release=( @@ -15112,7 +15308,7 @@ _nvme () { ;; (resv-report) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _resv_report _resv_report=( @@ -15144,7 +15340,7 @@ _nvme () { ;; (rotational-media-info-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _rotational_media_info_log _rotational_media_info_log=( @@ -15170,7 +15366,7 @@ _nvme () { ;; (sanitize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" && return local _sanitize @@ -15217,7 +15413,7 @@ _nvme () { ;; (sanitize-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _sanitize_log _sanitize_log=( @@ -15245,7 +15441,7 @@ _nvme () { ;; (sanitize-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" && return local _sanitize_ns @@ -15280,7 +15476,7 @@ _nvme () { ;; (security-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _security_recv _security_recv=( @@ -15320,7 +15516,7 @@ _nvme () { ;; (security-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--file -f" && return local _security_send @@ -15359,7 +15555,7 @@ _nvme () { ;; (self-test-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _self_test_log _self_test_log=( @@ -15386,7 +15582,7 @@ _nvme () { (set-feature) _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" && return local _set_feature @@ -15427,7 +15623,7 @@ _nvme () { ;; (set-property) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _set_property _set_property=( @@ -15455,7 +15651,7 @@ _nvme () { ;; (set-reg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _set_reg _set_reg=( @@ -15500,7 +15696,7 @@ _nvme () { ;; (show-hostnqn) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _show_hostnqn _show_hostnqn=( @@ -15524,7 +15720,7 @@ _nvme () { ;; (show-regs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _show_regs _show_regs=( @@ -15548,7 +15744,7 @@ _nvme () { ;; (show-topology) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json tabular" \ "--output-format-version" "1 2" && return local _show_topology _show_topology=( @@ -15574,7 +15770,7 @@ _nvme () { ;; (smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _smart_log _smart_log=( @@ -15602,7 +15798,7 @@ _nvme () { ;; (subsystem-reset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _subsystem_reset _subsystem_reset=( @@ -15626,7 +15822,7 @@ _nvme () { ;; (supported-cap-config-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _supported_cap_config_log _supported_cap_config_log=( @@ -15654,7 +15850,7 @@ _nvme () { ;; (supported-log-pages) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _supported_log_pages _supported_log_pages=( @@ -15678,7 +15874,7 @@ _nvme () { ;; (telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--output-file -O" && return local _telemetry_log @@ -15720,7 +15916,7 @@ _nvme () { ;; (top) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" && return local _top _top=( @@ -15729,7 +15925,7 @@ _nvme () { --dry-run':show command instead of executing' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' - --output-format=':Output format: normal|json|binary' + --output-format=':Output format: normal' -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' @@ -15746,7 +15942,7 @@ _nvme () { ;; (verify) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _verify _verify=( @@ -15792,7 +15988,7 @@ _nvme () { ;; (virt-mgmt) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _virt_mgmt _virt_mgmt=( @@ -15824,7 +16020,7 @@ _nvme () { ;; (write) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return @@ -15893,7 +16089,7 @@ _nvme () { ;; (write-uncor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _write_uncor _write_uncor=( @@ -15927,7 +16123,7 @@ _nvme () { ;; (write-zeroes) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" && return local _write_zeroes _write_zeroes=( diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index 2c158e5d3f..d420505cfd 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -160,7 +160,7 @@ nvme_list_opts () { opts+=" --cdw10= -4 --cdw11= -5 --cdw12= -6 --cdw13= -7 --cdw14= -8 --cdw15= -9 --cdw2= -2 --cdw3= -3 --data-len= -l --dry-run --flags= -f --input-file= -i --latency -T --metadata= -M --metadata-len= -m --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --prefill= -p --quiet --raw-binary -b --read -r --rsvd= -R --set-options= --show-command -s --timeout= --verbose -v --write -w" valopts+=" --cdw10 -4 --cdw11 -5 --cdw12 -6 --cdw13 -7 --cdw14 -8 --cdw15 -9 --cdw2 -2 --cdw3 -3 --data-len -l --flags -f --input-file -i --metadata -M --metadata-len -m --namespace-id -n --opcode -O --output-format -o --output-format-version --prefill -p --rsvd -R --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" "--metadata -M" ;; @@ -169,7 +169,7 @@ nvme_list_opts () { opts+=" --dry-run --groups -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -177,7 +177,7 @@ nvme_list_opts () { opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -185,7 +185,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -193,7 +193,7 @@ nvme_list_opts () { opts+=" --dry-run --lsp= -s --no-ioctl-probing --no-retries --output-file= -f --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --lsp -s --output-file -f --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -f" ;; @@ -202,7 +202,7 @@ nvme_list_opts () { opts+=" --cap-lower= -l --cap-upper= -u --dry-run --element-id= -i --ish -I --no-ioctl-probing --no-retries --operation= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cap-lower -l --cap-upper -u --element-id -i --operation -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -210,7 +210,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -218,7 +218,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -226,7 +226,7 @@ nvme_list_opts () { opts+=" --dry-run --key= -k --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --key -k --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -234,7 +234,7 @@ nvme_list_opts () { opts+=" --compat -C --dry-run --hostnqn= -n --identity= -I --keydata= -d --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsysnqn= -c --timeout= --verbose -v" valopts+=" --hostnqn -n --identity -I --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -242,7 +242,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -250,7 +250,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -259,7 +259,7 @@ nvme_list_opts () { opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --devid-file= --disable-sqflow --discovery --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --idempotent --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --no-ioctl-probing --no-retries --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --queue-size= -Q --quiet --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --config -J --ctrl-loss-tmo -l --devid-file --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--devid-file" ;; @@ -268,7 +268,7 @@ nvme_list_opts () { opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --force --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--raw -r" ;; @@ -277,7 +277,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --blocks= -b --dir-spec= -S --dir-type= -T --dry-run --expected-app-tag-masks= -M --expected-app-tags= -A --expected-ref-tags= -R --force-unit-access -f --format= -F --limited-retry -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfor= -P --prinfow= -p --quiet --ref-tag= -r --sdlba= -d --set-options= --slbs= -s --snsids= -N --sopts= -O --storage-tag= -t --storage-tag-check -c --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --blocks -b --dir-spec -S --dir-type -T --expected-app-tag-masks -M --expected-app-tags -A --expected-ref-tags -R --format -F --namespace-id -n --output-format -o --output-format-version --prinfor -P --prinfow -p --ref-tag -r --sdlba -d --set-options --slbs -s --snsids -N --sopts -O --storage-tag -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -285,7 +285,7 @@ nvme_list_opts () { opts+=" --anagrp-id= -a --azr -z --block-size= -b --csi= -y --dps= -d --dry-run --endg-id= -e --flbas= -f --ish -I --lbstm= -l --ncap= -c --ncap-si= -C --nmic= -m --no-ioctl-probing --no-retries --nphndls= -n --nsze= -s --nsze-si= -S --nvmset-id= -i --output-format= -o --output-format-version= --phndls= -p --quiet --rar= -r --rnumzrwa= -u --ror= -O --set-options= --timeout= --verbose -v" valopts+=" --anagrp-id -a --block-size -b --csi -y --dps -d --endg-id -e --flbas -f --lbstm -l --ncap -c --ncap-si -C --nmic -m --nphndls -n --nsze -s --nsze-si -S --nvmset-id -i --output-format -o --output-format-version --phndls -p --rar -r --rnumzrwa -u --ror -O --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -293,7 +293,7 @@ nvme_list_opts () { opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -301,7 +301,7 @@ nvme_list_opts () { opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -309,7 +309,7 @@ nvme_list_opts () { opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --self-test-code= -s --set-options= --timeout= --verbose -v --wait -w" valopts+=" --namespace-id -n --output-format -o --output-format-version --self-test-code -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -317,7 +317,7 @@ nvme_list_opts () { opts+=" --device= -d --dry-run --no-ioctl-probing --no-retries --nqn= -n --output-format= -o --output-format-version= --quiet --set-options= --task= -t --timeout= --verbose -v" valopts+=" --device -d --nqn -n --output-format -o --output-format-version --set-options --task -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -325,7 +325,7 @@ nvme_list_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --req-resource= -r --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --namespace-id -n --output-format -o --output-format-version --req-resource -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -333,7 +333,7 @@ nvme_list_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --endir= -e --input-file= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --target-dir= -T --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --endir -e --input-file -i --namespace-id -n --output-format -o --output-format-version --set-options --target-dir -T --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -342,7 +342,7 @@ nvme_list_opts () { opts+=" --concat --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --exclude -x --fast-io-fail-tmo= -F --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --no-ioctl-probing --no-retries --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --queue-size= -Q --quiet --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -350,7 +350,7 @@ nvme_list_opts () { opts+=" --dry-run --force --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --owner= --quiet --set-options= --timeout= --transport= -t --verbose -v" valopts+=" --output-format -o --output-format-version --owner --set-options --timeout --transport -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -358,7 +358,7 @@ nvme_list_opts () { opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --force --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--raw -r" ;; @@ -367,7 +367,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -375,7 +375,7 @@ nvme_list_opts () { opts+=" --ad -d --blocks= -b --cdw11= -c --ctx-attrs= -a --dry-run --idr -r --idw -w --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --slbs= -s --timeout= --verbose -v" valopts+=" --blocks -b --cdw11 -c --ctx-attrs -a --namespace-id -n --output-format -o --output-format-version --set-options --slbs -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -383,7 +383,7 @@ nvme_list_opts () { opts+=" --csi= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --csi -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -391,7 +391,7 @@ nvme_list_opts () { opts+=" --dry-run --log-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -399,7 +399,7 @@ nvme_list_opts () { opts+=" --dry-run --group-id= -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --group-id -g --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -407,7 +407,7 @@ nvme_list_opts () { opts+=" --csi= -c --dry-run --lba= -l --log-entries= -e --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --sqid= -S --status= -s --timeout= --trtype= -t --valid-entry -V --verbose -v" valopts+=" --csi -c --lba -l --log-entries -e --namespace-id -n --opcode -O --output-format -o --output-format-version --set-options --sqid -S --status -s --timeout --trtype -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -415,7 +415,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -423,7 +423,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -431,7 +431,7 @@ nvme_list_opts () { opts+=" --block-size= -b --dry-run --force --ish -I --lbaf= -l --ms= -m --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pi= -i --pil= -p --quiet --reset -r --ses= -s --set-options= --timeout= --verbose -v" valopts+=" --block-size -b --lbaf -l --ms -m --namespace-id -n --output-format -o --output-format-version --pi -i --pil -p --ses -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -440,7 +440,7 @@ nvme_list_opts () { valopts+=" --action -a --bpid -b --output-format -o --output-format-version --set-options --slot -s --timeout" _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -448,7 +448,7 @@ nvme_list_opts () { opts+=" --dry-run --fw= -f --ignore-ovr -i --ish -I --no-ioctl-probing --no-retries --offset= -O --output-format= -o --output-format-version= --progress -p --quiet --set-options= --stream --timeout= --verbose -v --xfer= -x" valopts+=" --fw -f --offset -O --output-format -o --output-format-version --set-options --timeout --xfer -x" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--fw -f" ;; @@ -457,7 +457,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -465,7 +465,7 @@ nvme_list_opts () { opts+=" --dry-run --hmac= -m --key-length= -l --no-ioctl-probing --no-retries --nqn= -n --output-format= -o --output-format-version= --quiet --secret= -s --set-options= --timeout= --verbose -v" valopts+=" --hmac -m --key-length -l --nqn -n --output-format -o --output-format-version --secret -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -473,7 +473,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -481,7 +481,7 @@ nvme_list_opts () { opts+=" --compat -C --dry-run --hmac= -m --hostnqn= -n --identity= -I --insert -i --keyfile= -f --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --secret= -s --set-options= --subsysnqn= -c --timeout= --verbose -v" valopts+=" --hmac -m --hostnqn -n --identity -I --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --secret -s --set-options --subsysnqn -c --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -490,7 +490,7 @@ nvme_list_opts () { valopts+=" --cdw11 -c --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout --uuid-index -U" _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -499,7 +499,7 @@ nvme_list_opts () { opts+=" --action= -a --dry-run --ish -I --max-dw= -m --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --range-len= -l --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --action -a --max-dw -m --namespace-id -n --output-format -o --output-format-version --range-len -l --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -508,7 +508,7 @@ nvme_list_opts () { valopts+=" --aen -a --csi -y --log-id -i --log-len -l --lpo -L --lsi -S --lsp -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U --xfer-len -x" _nvme_opt_vals "--log-id -i" "supported-log-pages error smart fw-slot changed-attached-ns changed-ns cmd-effects device-self-test telemetry-host telemetry-ctrl endurance-group predictable-lat-nvmset predictable-lat-agg ana persistent-event lba-status endurance-grp-evt media-unit-status supported-cap-config-list fid-supported-effects mi-cmd-supported-effects cmd-and-feat-lockdown boot-partition rotational-media-info dispersed-ns-participating-ns mgmt-addr-list phy-rx-eom reachability-groups reachability-associations changed-alloc-ns-list fdp-configs fdp-ruh-usage fdp-stats fdp-events discover host-discover ave-discover pull-model-ddc-req reservation sanitize zns-changed-zones" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -516,7 +516,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -524,7 +524,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --offset= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -532,7 +532,7 @@ nvme_list_opts () { opts+=" --acq --aqa --asq --bpinfo --bpmbl --bprsel --cap --cc --cmbebs --cmbloc --cmbmsc --cmbsts --cmbswtp --cmbsz --crto --csts --dry-run --intmc --intms --no-ioctl-probing --no-retries --nssd --nssr --offset= -O --output-format= -o --output-format-version= --pmrcap --pmrctl --pmrebs --pmrmscl --pmrmscu --pmrsts --pmrswtp --quiet --set-options= --timeout= --verbose -v --vs" valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -540,7 +540,7 @@ nvme_list_opts () { opts+=" --all-host-entries -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -548,7 +548,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -556,7 +556,7 @@ nvme_list_opts () { opts+=" --dom-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dom-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -564,7 +564,7 @@ nvme_list_opts () { opts+=" --controller-id= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controller-id -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -572,7 +572,7 @@ nvme_list_opts () { opts+=" --dry-run --force --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -580,7 +580,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -588,7 +588,7 @@ nvme_list_opts () { opts+=" --dry-run --lba-format-index= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -596,7 +596,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --nvmset_id= -i --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --nvmset_id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -604,7 +604,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -612,7 +612,7 @@ nvme_list_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -621,7 +621,7 @@ nvme_list_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -630,7 +630,7 @@ nvme_list_opts () { opts+=" --cdw10= -4 --cdw11= -5 --cdw12= -6 --cdw13= -7 --cdw14= -8 --cdw15= -9 --cdw2= -2 --cdw3= -3 --data-len= -l --dry-run --flags= -f --input-file= -i --latency -T --metadata= -M --metadata-len= -m --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --prefill= -p --quiet --raw-binary -b --read -r --rsvd= -R --set-options= --show-command -s --timeout= --verbose -v --write -w" valopts+=" --cdw10 -4 --cdw11 -5 --cdw12 -6 --cdw13 -7 --cdw14 -8 --cdw15 -9 --cdw2 -2 --cdw3 -3 --data-len -l --flags -f --input-file -i --metadata -M --metadata-len -m --namespace-id -n --opcode -O --output-format -o --output-format-version --prefill -p --rsvd -R --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" "--metadata -M" ;; @@ -639,7 +639,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -647,7 +647,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -655,7 +655,7 @@ nvme_list_opts () { opts+=" --cntid= -c --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -663,7 +663,7 @@ nvme_list_opts () { opts+=" --dry-run --endgrp-id= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -671,7 +671,7 @@ nvme_list_opts () { opts+=" --all -a --csi= -y --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --csi -y --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -679,7 +679,7 @@ nvme_list_opts () { opts+=" --cntid= -c --dry-run --no-ioctl-probing --no-retries --num-entries= -e --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --num-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -687,7 +687,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -695,7 +695,7 @@ nvme_list_opts () { opts+=" --csel= -c --css= -C --dry-run --ifc= -f --no-ioctl-probing --no-retries --ofi= -O --output-format= -o --output-format-version= --prhbt= -p --quiet --scp= -s --set-options= --timeout= --uuid= -U --verbose -v" valopts+=" --csel -c --css -C --ifc -f --ofi -O --output-format -o --output-format-version --prhbt -p --scp -s --set-options --timeout --uuid -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -703,7 +703,7 @@ nvme_list_opts () { opts+=" --domain-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -711,7 +711,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -719,7 +719,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -727,7 +727,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -735,7 +735,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -743,7 +743,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -751,7 +751,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -759,7 +759,7 @@ nvme_list_opts () { opts+=" --dry-run --lba-format-index= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -767,7 +767,7 @@ nvme_list_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -776,7 +776,7 @@ nvme_list_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -785,7 +785,7 @@ nvme_list_opts () { opts+=" --action= -a --dry-run --log_len= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -793,7 +793,7 @@ nvme_list_opts () { opts+=" --controller= -c --dry-run --lsp= -s --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controller -c --lsp -s --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -801,7 +801,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -809,7 +809,7 @@ nvme_list_opts () { opts+=" --dry-run --log-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -817,7 +817,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --nvmset-id= -i --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --nvmset-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -825,7 +825,7 @@ nvme_list_opts () { opts+=" --cntlid= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntlid -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -833,7 +833,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -841,7 +841,7 @@ nvme_list_opts () { opts+=" --associations-only -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -849,7 +849,7 @@ nvme_list_opts () { opts+=" --dry-run --groups-only -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -857,7 +857,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -866,7 +866,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -874,7 +874,7 @@ nvme_list_opts () { opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prkey= -p --quiet --racqa= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --prkey -p --racqa -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -882,7 +882,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -890,7 +890,7 @@ nvme_list_opts () { opts+=" --cptpl= -p --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --nrkey= -k --output-format= -o --output-format-version= --quiet --rrega= -r --set-options= --timeout= --verbose -v" valopts+=" --cptpl -p --crkey -c --namespace-id -n --nrkey -k --output-format -o --output-format-version --rrega -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -898,7 +898,7 @@ nvme_list_opts () { opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rrela= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --rrela -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -906,7 +906,7 @@ nvme_list_opts () { opts+=" --dry-run --eds -e --namespace-id= -n --no-ioctl-probing --no-retries --numd= -d --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --numd -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -914,7 +914,7 @@ nvme_list_opts () { opts+=" --dry-run --endg-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --endg-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -922,7 +922,7 @@ nvme_list_opts () { opts+=" --ause -u --dry-run --emvs -e --ish -I --no-dealloc -d --no-ioctl-probing --no-retries --oipbp -i --output-format= -o --output-format-version= --ovrpat= -p --owpass= -n --preq -q --quiet --repeat= -r --sanact= -a --set-options= --timeout= --verbose -v --wait -w" valopts+=" --output-format -o --output-format-version --ovrpat -p --owpass -n --repeat -r --sanact -a --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" ;; @@ -931,7 +931,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -939,7 +939,7 @@ nvme_list_opts () { opts+=" --ause -u --dry-run --emvs -e --ish -I --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --preq -q --quiet --sanact= -a --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sanact -a --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" ;; @@ -948,7 +948,7 @@ nvme_list_opts () { opts+=" --al= -t --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --raw-binary -b --secp= -p --set-options= --size= -x --spsp= -s --timeout= --verbose -v" valopts+=" --al -t --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --size -x --spsp -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -956,7 +956,7 @@ nvme_list_opts () { opts+=" --dry-run --file= -f --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --secp= -p --set-options= --spsp= -s --timeout= --tl= -t --verbose -v" valopts+=" --file -f --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --spsp -s --timeout --tl -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--file -f" ;; @@ -965,7 +965,7 @@ nvme_list_opts () { opts+=" --dry-run --dst-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dst-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -974,7 +974,7 @@ nvme_list_opts () { valopts+=" --cdw12 -c --data -d --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U --value -V" _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -983,7 +983,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --offset= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --value= -V --verbose -v" valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -991,7 +991,7 @@ nvme_list_opts () { opts+=" --acq= --aqa= --asq= --bpmbl= --bprsel= --cc= --cmbmsc= --csts= --dry-run --intmc= --intms= --mmio32 -m --no-ioctl-probing --no-retries --nssd= --nssr= --offset= -O --output-format= -o --output-format-version= --pmrctl= --pmrmscl= --pmrmscu= --quiet --set-options= --timeout= --value= -V --verbose -v" valopts+=" --acq --aqa --asq --bpmbl --bprsel --cc --cmbmsc --csts --intmc --intms --nssd --nssr --offset -O --output-format -o --output-format-version --pmrctl --pmrmscl --pmrmscu --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -999,7 +999,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1007,7 +1007,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1015,7 +1015,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --ranking= -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --ranking -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json tabular" \ "--output-format-version" "1 2" ;; @@ -1023,7 +1023,7 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1031,7 +1031,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1039,7 +1039,7 @@ nvme_list_opts () { opts+=" --domain-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1047,7 +1047,7 @@ nvme_list_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1055,7 +1055,7 @@ nvme_list_opts () { opts+=" --controller-init -c --data-area= -d --dry-run --force --host-generate= -g --mcda= -m --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --data-area -d --host-generate -g --mcda -m --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -1064,7 +1064,7 @@ nvme_list_opts () { opts+=" --delay= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --delay -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" ;; @@ -1072,7 +1072,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --dry-run --force-unit-access -f --limited-retry -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --start-block= -s --storage-tag= -S --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1080,7 +1080,7 @@ nvme_list_opts () { opts+=" --act= -a --cntlid= -c --dry-run --no-ioctl-probing --no-retries --nr= -n --output-format= -o --output-format-version= --quiet --rt= -r --set-options= --timeout= --verbose -v" valopts+=" --act -a --cntlid -c --nr -n --output-format -o --output-format-version --rt -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1088,7 +1088,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -1097,7 +1097,7 @@ nvme_list_opts () { opts+=" --block-count= -c --dir-spec= -S --dir-type= -T --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --start-block= -s --timeout= --verbose -v" valopts+=" --block-count -c --dir-spec -S --dir-type -T --namespace-id -n --output-format -o --output-format-version --set-options --start-block -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1105,7 +1105,7 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --deac -d --dir-spec= -D --dir-type= -T --dry-run --force-unit-access -f --limited-retry -l --namespace-id= -n --namespace-zeroes -Z --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --start-block= -s --storage-tag= -S --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --dir-spec -D --dir-type -T --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1130,7 +1130,7 @@ plugin_amzn_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1138,7 +1138,7 @@ plugin_amzn_opts () { opts+=" --details -d --dry-run --interval= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --interval -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1160,9 +1160,11 @@ plugin_config_opts () { _nvme_detect_value_completion case "$1" in "convert") - opts+=" --config= -J --force --output= -o" - valopts+=" --config -J --output -o" + opts+=" --config= -J --dry-run --force --no-ioctl-probing --no-retries --output= -o --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --config -J --output -o --output-format -o --output-format-version --set-options --timeout" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--output -o" ;; @@ -1170,7 +1172,7 @@ plugin_config_opts () { opts+=" --concat --ctrl-loss-tmo= -l --data-digest -G --disable-sqflow --discovery --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --hdr-digest -g --host-iface= -f --host-symname= --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --no-ioctl-probing --no-retries --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output= --output-format= -o --output-format-version= --persistent= --queue-size= -Q --quiet --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --ctrl-loss-tmo -l --fast-io-fail-tmo -F --host-iface -f --host-symname --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output --output-format -o --output-format-version --persistent --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output" ;; @@ -1179,15 +1181,25 @@ plugin_config_opts () { opts+=" --config= -J --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --config -J --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" ;; + "status") + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" + ;; + "validate") - opts+=" --config= -J" - valopts+=" --config -J" + opts+=" --config= -J --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --config -J --output-format -o --output-format-version --set-options --timeout" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" _nvme_opt_files "--config -J" ;; esac @@ -1212,7 +1224,7 @@ plugin_dapustor_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1237,7 +1249,7 @@ plugin_dell_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1262,7 +1274,7 @@ plugin_dera_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1287,7 +1299,7 @@ plugin_dir_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --req-resource= -r --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --namespace-id -n --output-format -o --output-format-version --req-resource -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1295,7 +1307,7 @@ plugin_dir_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --endir= -e --input-file= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --target-dir= -T --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --endir -e --input-file -i --namespace-id -n --output-format -o --output-format-version --set-options --target-dir -T --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -1321,7 +1333,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --entry= -e --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --entry -e --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1329,7 +1341,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1337,7 +1349,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1345,7 +1357,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1353,7 +1365,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1361,7 +1373,7 @@ plugin_exclusion_opts () { opts+=" --dry-run --name= -N --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1386,7 +1398,7 @@ plugin_fdp_opts () { opts+=" --dry-run --endgrp-id= -e --human-readable -H --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1394,7 +1406,7 @@ plugin_fdp_opts () { opts+=" --dry-run --endgrp-id= -e --host-events -E --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1402,7 +1414,7 @@ plugin_fdp_opts () { opts+=" --disable -d --dry-run --enable-conf-idx= -c --endgrp-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --enable-conf-idx -c --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1410,7 +1422,7 @@ plugin_fdp_opts () { opts+=" --dry-run --enable -e --event-types= -t --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --placement-handle= -p --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --event-types -t --namespace-id -n --output-format -o --output-format-version --placement-handle -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1418,7 +1430,7 @@ plugin_fdp_opts () { opts+=" --dry-run --endgrp-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1426,7 +1438,7 @@ plugin_fdp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1434,7 +1446,7 @@ plugin_fdp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pids= -p --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --pids -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1442,7 +1454,7 @@ plugin_fdp_opts () { opts+=" --dry-run --endgrp-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1467,7 +1479,7 @@ plugin_feat_opts () { opts+=" --ab= -a --dry-run --hpw= -H --lpw= -l --mpw= -m --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --ab -a --hpw -H --lpw -l --mpw -m --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1476,7 +1488,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --result= -r --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --result -r --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1485,7 +1497,7 @@ plugin_feat_opts () { opts+=" --dry-run --dulbe -d --no-ioctl-probing --no-retries --nsid= -n --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --tler= -t --verbose -v" valopts+=" --nsid -n --output-format -o --output-format-version --sel -S --set-options --timeout --tler -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1494,7 +1506,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --tmt1= -t --tmt2= -T --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout --tmt1 -t --tmt2 -T" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1503,7 +1515,7 @@ plugin_feat_opts () { opts+=" --acre= -a --cdfe= -c --dry-run --etdas= -e --hdisns= -H --lbafee= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --acre -a --cdfe -c --etdas -e --hdisns -H --lbafee -l --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1512,7 +1524,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --thr= -t --time= -T --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --thr -t --time -T --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1521,7 +1533,7 @@ plugin_feat_opts () { opts+=" --cd -c --dry-run --iv= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --iv -i --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1530,7 +1542,7 @@ plugin_feat_opts () { opts+=" --dry-run --kato= -k --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --kato -k --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1539,7 +1551,7 @@ plugin_feat_opts () { opts+=" --dry-run --guids= -g --hlbars= --lbaros= -l --nlbs= -b --no-ioctl-probing --no-retries --nsid= -n --num= -N --output-format= -o --output-format-version= --quiet --save --sel= -S --set-options= --slbas= -s --timeout= --types= -t --verbose -v" valopts+=" --guids -g --hlbars --lbaros -l --nlbs -b --nsid -n --num -N --output-format -o --output-format-version --sel -S --set-options --slbas -s --timeout --types -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1548,7 +1560,7 @@ plugin_feat_opts () { opts+=" --dry-run --ncqr= -c --no-ioctl-probing --no-retries --nsqr= -n --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --ncqr -c --nsqr -n --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1557,7 +1569,7 @@ plugin_feat_opts () { opts+=" --attri= -a --attrl= -A --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --paid= -p --quiet --r4karl= -R --rvspa -r --save -s --sel= -S --set-options= --timeout= --verbose -v --vs-data= -V" valopts+=" --attri -a --attrl -A --namespace-id -n --output-format -o --output-format-version --paid -p --r4karl -R --sel -S --set-options --timeout --vs-data -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" _nvme_opt_files "--vs-data -V" @@ -1567,7 +1579,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pls= -l --plv= -p --quiet --save -s --sel= -S --set-options= --timeout= --uuid-index= -u --verbose -v" valopts+=" --output-format -o --output-format-version --pls -l --plv -p --sel -S --set-options --timeout --uuid-index -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1576,7 +1588,7 @@ plugin_feat_opts () { opts+=" --act= --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pmts= --quiet --save -s --sel= -S --set-options= --smt= --timeout= --uuid-index= -u --verbose -v" valopts+=" --act --output-format -o --output-format-version --pmts --sel -S --set-options --smt --timeout --uuid-index -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1585,7 +1597,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --ps= -p --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v --wh= -w" valopts+=" --output-format -o --output-format-version --ps -p --sel -S --set-options --timeout --wh -w" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1594,7 +1606,7 @@ plugin_feat_opts () { opts+=" --dry-run --ept= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pmts= -m --pts= -t --ptv= -p --quiet --save -s --sel= -S --set-options= --timeout= --uuid-index= -u --verbose -v" valopts+=" --ept -e --output-format -o --output-format-version --pmts -m --pts -t --ptv -p --sel -S --set-options --timeout --uuid-index -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1603,7 +1615,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --thsel= -H --timeout= --tmpsel= -m --tmpth= -T --tmpthh= -M --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --thsel -H --timeout --tmpsel -m --tmpth -T --tmpthh -M" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1612,7 +1624,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --tstmp= -t --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout --tstmp -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1621,7 +1633,7 @@ plugin_feat_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v --wce -w" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1630,7 +1642,7 @@ plugin_feat_opts () { opts+=" --dn -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -1657,7 +1669,7 @@ plugin_fw_opts () { valopts+=" --action -a --bpid -b --output-format -o --output-format-version --set-options --slot -s --timeout" _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1665,7 +1677,7 @@ plugin_fw_opts () { opts+=" --dry-run --fw= -f --ignore-ovr -i --ish -I --no-ioctl-probing --no-retries --offset= -O --output-format= -o --output-format-version= --progress -p --quiet --set-options= --stream --timeout= --verbose -v --xfer= -x" valopts+=" --fw -f --offset -O --output-format -o --output-format-version --set-options --timeout --xfer -x" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--fw -f" ;; @@ -1691,7 +1703,7 @@ plugin_huawei_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1699,7 +1711,7 @@ plugin_huawei_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; esac @@ -1724,7 +1736,7 @@ plugin_ibm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1732,7 +1744,7 @@ plugin_ibm_opts () { opts+=" --action= -a --dry-run --log_len= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1740,7 +1752,7 @@ plugin_ibm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1765,7 +1777,7 @@ plugin_id_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1773,7 +1785,7 @@ plugin_id_opts () { opts+=" --cntid= -c --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -1781,7 +1793,7 @@ plugin_id_opts () { opts+=" --dom-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dom-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1789,7 +1801,7 @@ plugin_id_opts () { opts+=" --dry-run --endgrp-id= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -1797,7 +1809,7 @@ plugin_id_opts () { opts+=" --controller-id= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controller-id -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1805,7 +1817,7 @@ plugin_id_opts () { opts+=" --dry-run --force --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1813,7 +1825,7 @@ plugin_id_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1821,7 +1833,7 @@ plugin_id_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1829,7 +1841,7 @@ plugin_id_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1837,7 +1849,7 @@ plugin_id_opts () { opts+=" --dry-run --lba-format-index= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1845,7 +1857,7 @@ plugin_id_opts () { opts+=" --all -a --csi= -y --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --csi -y --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -1853,7 +1865,7 @@ plugin_id_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1861,7 +1873,7 @@ plugin_id_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1869,7 +1881,7 @@ plugin_id_opts () { opts+=" --dry-run --lba-format-index= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1877,7 +1889,7 @@ plugin_id_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --nvmset_id= -i --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --nvmset_id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1885,7 +1897,7 @@ plugin_id_opts () { opts+=" --cntlid= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntlid -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1893,7 +1905,7 @@ plugin_id_opts () { opts+=" --cntid= -c --dry-run --no-ioctl-probing --no-retries --num-entries= -e --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --num-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1901,7 +1913,7 @@ plugin_id_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1926,7 +1938,7 @@ plugin_innogrit_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1934,7 +1946,7 @@ plugin_innogrit_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1959,7 +1971,7 @@ plugin_inspur_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -1984,7 +1996,7 @@ plugin_intel_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -1992,7 +2004,7 @@ plugin_intel_opts () { opts+=" --dry-run --log= -l --namespace-id= -n --nlognum= -m --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --region= -r --set-options= --timeout= --verbose -v --verbose-nlog -V" valopts+=" --log -l --namespace-id -n --nlognum -m --output-file -O --output-format -o --output-format-version --region -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -2001,19 +2013,23 @@ plugin_intel_opts () { opts+=" --dry-run --json -j --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v --write -w" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; "lat-stats-tracking") - opts+=" --disable -d --enable -e" + opts+=" --disable -d --dry-run --enable -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "market-name") opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2021,7 +2037,7 @@ plugin_intel_opts () { opts+=" --bucket-thresholds= -t --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v --write -w" valopts+=" --bucket-thresholds -t --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2029,7 +2045,7 @@ plugin_intel_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2037,7 +2053,7 @@ plugin_intel_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -2062,7 +2078,7 @@ plugin_io_mgmt_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -2071,7 +2087,7 @@ plugin_io_mgmt_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -2097,7 +2113,7 @@ plugin_keys_opts () { opts+=" --dry-run --identity= -i --keydata= -d --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --identity -i --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2105,7 +2121,7 @@ plugin_keys_opts () { opts+=" --compat -C --dry-run --hostnqn= -n --identity= -I --keydata= -d --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsysnqn= -c --timeout= --verbose -v" valopts+=" --hostnqn -n --identity -I --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2113,7 +2129,7 @@ plugin_keys_opts () { opts+=" --dry-run --keyfile= -f --keyring= -k --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --keyfile -f --keyring -k --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2121,7 +2137,7 @@ plugin_keys_opts () { opts+=" --dry-run --hmac= -m --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --secret= -s --secret-length= -l --set-options= --timeout= --verbose -v" valopts+=" --hmac -m --output-format -o --output-format-version --secret -s --secret-length -l --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2129,7 +2145,7 @@ plugin_keys_opts () { opts+=" --compat -C --dry-run --hmac= -m --hostnqn= -n --identity= -I --insert -i --keyfile= -f --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --secret= -s --set-options= --subsysnqn= -c --timeout= --verbose -v" valopts+=" --hmac -m --hostnqn -n --identity -I --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --secret -s --set-options --subsysnqn -c --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2137,7 +2153,7 @@ plugin_keys_opts () { opts+=" --dry-run --identity= -i --keydata= -d --keyfile= -f --keyring= -k --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --identity -i --keydata -d --keyfile -f --keyring -k --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2145,7 +2161,7 @@ plugin_keys_opts () { opts+=" --compat -C --dry-run --hostnqn= -n --identity= -I --keydata= -d --keyfile= -f --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsysnqn= -c --timeout= --verbose -v" valopts+=" --hostnqn -n --identity -I --keydata -d --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2153,7 +2169,7 @@ plugin_keys_opts () { opts+=" --dry-run --identity= -i --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --identity -i --keyring -k --keytype -t --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -2178,7 +2194,7 @@ plugin_lm_opts () { opts+=" --cntlid= -c --consent --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --queue-type= -q --quiet --set-options= --size= -s --timeout= --verbose -v" valopts+=" --cntlid -c --output-format -o --output-format-version --queue-type -q --set-options --size -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2186,7 +2202,7 @@ plugin_lm_opts () { opts+=" --cdqid= -C --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cdqid -C --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2194,7 +2210,7 @@ plugin_lm_opts () { opts+=" --cdqid= -C --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cdqid -C --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2202,7 +2218,7 @@ plugin_lm_opts () { opts+=" --cntlid= -c --csuuidi= -U --csvi= -V --dry-run --human-readable -H --no-ioctl-probing --no-retries --numd= -n --offset= -O --output-file= -f --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --uidx= -u --verbose -v" valopts+=" --cntlid -c --csuuidi -U --csvi -V --numd -n --offset -O --output-file -f --output-format -o --output-format-version --sel -s --set-options --timeout --uidx -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" _nvme_opt_files "--output-file -f" @@ -2212,7 +2228,7 @@ plugin_lm_opts () { opts+=" --cntlid= -c --csuuidi= -U --csvi= -V --dry-run --dudmq -d --input-file= -f --no-ioctl-probing --no-retries --numd= -n --offset= -O --output-format= -o --output-format-version= --quiet --sel= -s --seq-ind= -S --set-options= --stype= -t --timeout= --uidx= -u --verbose -v" valopts+=" --cntlid -c --csuuidi -U --csvi -V --input-file -f --numd -n --offset -O --output-format -o --output-format-version --sel -s --seq-ind -S --set-options --stype -t --timeout --uidx -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" _nvme_opt_files "--input-file -f" @@ -2222,7 +2238,7 @@ plugin_lm_opts () { opts+=" --cdqid= -C --dry-run --hp= -H --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --tpt= -T --verbose -v" valopts+=" --cdqid -C --hp -H --output-format -o --output-format-version --set-options --timeout --tpt -T" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2230,7 +2246,7 @@ plugin_lm_opts () { opts+=" --cdqid= -C --dry-run --mos= -m --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --start --stop --timeout= --verbose -v" valopts+=" --cdqid -C --mos -m --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -2256,7 +2272,7 @@ plugin_log_opts () { opts+=" --dry-run --groups -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2264,7 +2280,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2272,7 +2288,7 @@ plugin_log_opts () { opts+=" --dry-run --lsp= -s --no-ioctl-probing --no-retries --output-file= -f --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --lsp -s --output-file -f --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -f" ;; @@ -2281,7 +2297,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2289,7 +2305,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2297,7 +2313,7 @@ plugin_log_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2305,7 +2321,7 @@ plugin_log_opts () { opts+=" --csi= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --csi -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2313,7 +2329,7 @@ plugin_log_opts () { opts+=" --dry-run --group-id= -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --group-id -g --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2321,7 +2337,7 @@ plugin_log_opts () { opts+=" --dry-run --log-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2329,7 +2345,7 @@ plugin_log_opts () { opts+=" --csi= -c --dry-run --lba= -l --log-entries= -e --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --sqid= -S --status= -s --timeout= --trtype= -t --valid-entry -V --verbose -v" valopts+=" --csi -c --lba -l --log-entries -e --namespace-id -n --opcode -O --output-format -o --output-format-version --set-options --sqid -S --status -s --timeout --trtype -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2337,7 +2353,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2345,7 +2361,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2353,7 +2369,7 @@ plugin_log_opts () { opts+=" --all-host-entries -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2361,7 +2377,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2369,7 +2385,7 @@ plugin_log_opts () { opts+=" --domain-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2377,7 +2393,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2385,7 +2401,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2393,7 +2409,7 @@ plugin_log_opts () { opts+=" --action= -a --dry-run --log_len= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2401,7 +2417,7 @@ plugin_log_opts () { opts+=" --controller= -c --dry-run --lsp= -s --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controller -c --lsp -s --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2409,7 +2425,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2417,7 +2433,7 @@ plugin_log_opts () { opts+=" --dry-run --log-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2425,7 +2441,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --nvmset-id= -i --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --nvmset-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2433,7 +2449,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2441,7 +2457,7 @@ plugin_log_opts () { opts+=" --associations-only -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2449,7 +2465,7 @@ plugin_log_opts () { opts+=" --dry-run --groups-only -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2457,7 +2473,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2465,7 +2481,7 @@ plugin_log_opts () { opts+=" --dry-run --endg-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --endg-id -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2473,7 +2489,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2481,7 +2497,7 @@ plugin_log_opts () { opts+=" --dry-run --dst-entries= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dst-entries -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2489,7 +2505,7 @@ plugin_log_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2497,7 +2513,7 @@ plugin_log_opts () { opts+=" --domain-id= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2505,7 +2521,7 @@ plugin_log_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2513,7 +2529,7 @@ plugin_log_opts () { opts+=" --controller-init -c --data-area= -d --dry-run --force --host-generate= -g --mcda= -m --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --data-area -d --host-generate -g --mcda -m --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -2539,7 +2555,7 @@ plugin_mangoboost_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -2564,7 +2580,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2572,7 +2588,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2580,7 +2596,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2588,7 +2604,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --param= -p --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --param -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2596,7 +2612,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2604,7 +2620,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2612,19 +2628,23 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --sel-perf-log= -s --set-commands-mask= -m --set-options= --set-read-threshold= -r --set-trim-threshold= -t --set-write-threshold= -w --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel-perf-log -s --set-commands-mask -m --set-options --set-read-threshold -r --set-trim-threshold -t --set-write-threshold -w --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; "lat-stats") - opts+=" --disable -d --enable -e" + opts+=" --disable -d --dry-run --enable -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "lat-stats-print") opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v --write -w" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2632,7 +2652,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2640,7 +2660,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --duration= -d --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --duration -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2648,7 +2668,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --fw= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select= -s --set-options= --timeout= --verbose -v" valopts+=" --fw -f --output-format -o --output-format-version --select -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--fw -f" ;; @@ -2657,7 +2677,7 @@ plugin_memblaze_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --value= -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2665,12 +2685,16 @@ plugin_memblaze_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; "smart-log-add-x") - opts+=" --raw-binary -b" + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; esac _nvme_finish_completion "$1" 3 @@ -2694,7 +2718,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2702,7 +2726,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2710,7 +2734,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2718,7 +2742,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2726,7 +2750,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2734,7 +2758,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2742,7 +2766,7 @@ plugin_micron_opts () { opts+=" --command= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --command -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2750,7 +2774,7 @@ plugin_micron_opts () { opts+=" --command= -c --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --set-options= --threshold= -t --timeout= --verbose -v" valopts+=" --command -c --option -O --output-format -o --output-format-version --set-options --threshold -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2758,7 +2782,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2766,7 +2790,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2774,7 +2798,7 @@ plugin_micron_opts () { opts+=" --dry-run --fw= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select= -s --set-options= --timeout= --verbose -v" valopts+=" --fw -f --output-format -o --output-format-version --select -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--fw -f" ;; @@ -2783,7 +2807,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2791,7 +2815,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2799,7 +2823,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2807,7 +2831,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2815,7 +2839,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2823,7 +2847,7 @@ plugin_micron_opts () { opts+=" --data_area= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --package= -p --quiet --set-options= --timeout= --type= -t --verbose -v" valopts+=" --data_area -d --output-format -o --output-format-version --package -p --set-options --timeout --type -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--package -p" ;; @@ -2832,7 +2856,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2840,7 +2864,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2848,7 +2872,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2856,7 +2880,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2864,7 +2888,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --save= -s --set-options= --timeout= --value= -V --verbose -v" valopts+=" --option -O --output-format -o --output-format-version --save -s --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2872,7 +2896,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --select= -s --set-options= --timeout= --verbose -v" valopts+=" --option -O --output-format -o --output-format-version --select -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2880,7 +2904,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2888,7 +2912,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -2896,7 +2920,7 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; esac @@ -2921,7 +2945,7 @@ plugin_nbft_opts () { opts+=" --discovery -d --dry-run --hfi -H --nbft-path= --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsystem -s --timeout= --verbose -v" valopts+=" --nbft-path --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -2946,7 +2970,7 @@ plugin_netapp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal column json" \ "--output-format-version" "1 2" ;; @@ -2954,7 +2978,7 @@ plugin_netapp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal column json" \ "--output-format-version" "1 2" ;; esac @@ -2979,7 +3003,7 @@ plugin_ns_opts () { opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2987,7 +3011,7 @@ plugin_ns_opts () { opts+=" --anagrp-id= -a --azr -z --block-size= -b --csi= -y --dps= -d --dry-run --endg-id= -e --flbas= -f --ish -I --lbstm= -l --ncap= -c --ncap-si= -C --nmic= -m --no-ioctl-probing --no-retries --nphndls= -n --nsze= -s --nsze-si= -S --nvmset-id= -i --output-format= -o --output-format-version= --phndls= -p --quiet --rar= -r --rnumzrwa= -u --ror= -O --set-options= --timeout= --verbose -v" valopts+=" --anagrp-id -a --block-size -b --csi -y --dps -d --endg-id -e --flbas -f --lbstm -l --ncap -c --ncap-si -C --nmic -m --nphndls -n --nsze -s --nsze-si -S --nvmset-id -i --output-format -o --output-format-version --phndls -p --rar -r --rnumzrwa -u --ror -O --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -2995,7 +3019,7 @@ plugin_ns_opts () { opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3003,7 +3027,7 @@ plugin_ns_opts () { opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3011,7 +3035,7 @@ plugin_ns_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3036,7 +3060,7 @@ plugin_nvidia_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3061,7 +3085,7 @@ plugin_nvme_mi_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -3070,7 +3094,7 @@ plugin_nvme_mi_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--input-file -i" ;; @@ -3096,7 +3120,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3104,7 +3128,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3112,7 +3136,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3120,7 +3144,7 @@ plugin_ocp_opts () { opts+=" --dry-run --mode= -m --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --mode -m --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -3129,7 +3153,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3137,7 +3161,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3145,7 +3169,7 @@ plugin_ocp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3154,7 +3178,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -3163,7 +3187,7 @@ plugin_ocp_opts () { opts+=" --all -a --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -3172,7 +3196,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3181,7 +3205,7 @@ plugin_ocp_opts () { opts+=" --all-ns -a --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3190,7 +3214,7 @@ plugin_ocp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3199,7 +3223,7 @@ plugin_ocp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3208,7 +3232,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --sel= -S --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --sel -S --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -S" "0 1 2 3" ;; @@ -3217,7 +3241,7 @@ plugin_ocp_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3227,7 +3251,7 @@ plugin_ocp_opts () { valopts+=" --comp-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--comp-id -i" "asic nand dram pmic pcb cap reg case sn country hw-rev born-on-date vendor" \ - "--output-format -o" "normal json binary tabular" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3235,7 +3259,7 @@ plugin_ocp_opts () { opts+=" --data-area= -a --dry-run --host-generate= -g --no-ioctl-probing --no-retries --no-uuid -n --output-file= -f --output-format= -o --output-format-version= --quiet --set-options= --string-log= -s --telemetry-log= -l --telemetry-type= -t --timeout= --verbose -v" valopts+=" --data-area -a --host-generate -g --output-file -f --output-format -o --output-format-version --set-options --string-log -s --telemetry-log -l --telemetry-type -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -f" ;; @@ -3244,7 +3268,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3252,7 +3276,7 @@ plugin_ocp_opts () { opts+=" --action= -a --dry-run --log_len= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3260,7 +3284,7 @@ plugin_ocp_opts () { opts+=" --dry-run --enable-panic-notices -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3268,7 +3292,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --power-state= -p --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --power-state -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3276,7 +3300,7 @@ plugin_ocp_opts () { opts+=" --dry-run --enable -e --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3284,7 +3308,7 @@ plugin_ocp_opts () { opts+=" --all-ns -a --data= -d --dry-run --no-ioctl-probing --no-retries --no-uuid -N --nrtdp= -r --number= -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --type= -t --verbose -v" valopts+=" --data -d --nrtdp -r --number -n --output-format -o --output-format-version --set-options --timeout --type -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -3293,7 +3317,7 @@ plugin_ocp_opts () { opts+=" --active_bucket_timer_threshold= -t --active_latency_config= -f --active_latency_minimum_window= -w --active_threshold_a= -a --active_threshold_b= -b --active_threshold_c= -c --active_threshold_d= -d --debug_log_trigger_enable= -r --discard_debug_log= -l --dry-run --latency_monitor_feature_enable= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3301,7 +3325,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --plp_health_interval= -p --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --plp_health_interval -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3309,7 +3333,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --telemetry-profile-select= -t --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --telemetry-profile-select -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3317,7 +3341,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3325,7 +3349,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3333,7 +3357,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-file= -f --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-file -f --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -f" ;; @@ -3342,7 +3366,7 @@ plugin_ocp_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3367,7 +3391,7 @@ plugin_registry_opts () { opts+=" --attr= -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3375,7 +3399,7 @@ plugin_registry_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3383,7 +3407,7 @@ plugin_registry_opts () { opts+=" --attr= -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3391,7 +3415,7 @@ plugin_registry_opts () { opts+=" --attr= -a --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --value= -V --verbose -v" valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3416,7 +3440,7 @@ plugin_resv_opts () { opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prkey= -p --quiet --racqa= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --prkey -p --racqa -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3424,7 +3448,7 @@ plugin_resv_opts () { opts+=" --cptpl= -p --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --nrkey= -k --output-format= -o --output-format-version= --quiet --rrega= -r --set-options= --timeout= --verbose -v" valopts+=" --cptpl -p --crkey -c --namespace-id -n --nrkey -k --output-format -o --output-format-version --rrega -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3432,7 +3456,7 @@ plugin_resv_opts () { opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rrela= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --rrela -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3440,7 +3464,7 @@ plugin_resv_opts () { opts+=" --dry-run --eds -e --namespace-id= -n --no-ioctl-probing --no-retries --numd= -d --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --numd -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3465,7 +3489,7 @@ plugin_rpmb_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3473,7 +3497,7 @@ plugin_rpmb_opts () { opts+=" --dry-run --key= -k --keyfile= -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" valopts+=" --key -k --keyfile -g --output-format -o --output-format-version --set-options --target -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--keyfile -g" ;; @@ -3482,7 +3506,7 @@ plugin_rpmb_opts () { opts+=" --dry-run --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --msgfile -f --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--msgfile -f" ;; @@ -3491,7 +3515,7 @@ plugin_rpmb_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --target -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3499,7 +3523,7 @@ plugin_rpmb_opts () { opts+=" --address= -o --blocks= -b --dry-run --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" valopts+=" --address -o --blocks -b --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--msgfile -f" ;; @@ -3508,7 +3532,7 @@ plugin_rpmb_opts () { opts+=" --dry-run --key= -k --keyfile= -g --msg= -d --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; @@ -3517,7 +3541,7 @@ plugin_rpmb_opts () { opts+=" --address= -o --blocks= -b --dry-run --key= -k --keyfile= -g --msg= -d --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" valopts+=" --address -o --blocks -b --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; @@ -3543,7 +3567,7 @@ plugin_samsung_opts () { opts+=" --compress -z --dry-run --dump-type= -t --hide-progress -H --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --telemetry-data-area= -a --timeout= --verbose -v" valopts+=" --dump-type -t --output-file -O --output-format -o --output-format-version --set-options --telemetry-data-area -a --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -3569,7 +3593,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3577,7 +3601,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3585,7 +3609,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3593,7 +3617,7 @@ plugin_seagate_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3601,7 +3625,7 @@ plugin_seagate_opts () { opts+=" --dry-run --log_specific= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log_specific -i --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3609,7 +3633,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3617,7 +3641,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3625,7 +3649,7 @@ plugin_seagate_opts () { opts+=" --dry-run --dump-file= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dump-file -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--dump-file -f" ;; @@ -3634,7 +3658,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3642,7 +3666,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3650,7 +3674,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3658,7 +3682,7 @@ plugin_seagate_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3683,7 +3707,7 @@ plugin_security_opts () { opts+=" --al= -t --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --raw-binary -b --secp= -p --set-options= --size= -x --spsp= -s --timeout= --verbose -v" valopts+=" --al -t --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --size -x --spsp -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3691,7 +3715,7 @@ plugin_security_opts () { opts+=" --dry-run --file= -f --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --secp= -p --set-options= --spsp= -s --timeout= --tl= -t --verbose -v" valopts+=" --file -f --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --spsp -s --timeout --tl -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--file -f" ;; @@ -3714,23 +3738,51 @@ plugin_sed_opts () { _nvme_detect_value_completion case "$1" in "discover"|"1") - opts+=" --udev -u --verbose -V" + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --udev -u --verbose -V --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "initialize") - opts+=" --read-only -r" + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --read-only -r --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "lock") - opts+=" --ask-key -k --read-only -r" + opts+=" --ask-key -k --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --read-only -r --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" + ;; + + "password") + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "revert") - opts+=" --destructive -e --psid -p" + opts+=" --destructive -e --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --psid -p --quiet --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; "unlock") - opts+=" --ask-key -k --read-only -r" + opts+=" --ask-key -k --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --read-only -r --set-options= --timeout= --verbose -v" + valopts+=" --output-format -o --output-format-version --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" ;; esac _nvme_finish_completion "$1" 3 @@ -3754,7 +3806,7 @@ plugin_sfx_opts () { opts+=" --cap= -c --cap-byte= -z --dry-run --force -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cap -c --cap-byte -z --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3762,7 +3814,7 @@ plugin_sfx_opts () { opts+=" --dry-run --file= -f --namespace_id= -n --no-ioctl-probing --no-retries --output= -O --output-format= -o --output-format-version= --parse -p --quiet --set-options= --storage_medium= -s --timeout= --verbose -v" valopts+=" --file -f --namespace_id -n --output -O --output-format -o --output-format-version --set-options --storage_medium -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--file -f" "--output -O" ;; @@ -3771,7 +3823,7 @@ plugin_sfx_opts () { opts+=" --dry-run --lbaf= -l --namespace_cap= -c --namespace_id= -n --namespace_size= -s --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --units= -u --verbose -v" valopts+=" --lbaf -l --namespace_cap -c --namespace_id -n --namespace_size -s --output-format -o --output-format-version --set-options --timeout --units -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3779,7 +3831,7 @@ plugin_sfx_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3787,7 +3839,7 @@ plugin_sfx_opts () { opts+=" --dry-run --feature-id= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3795,7 +3847,7 @@ plugin_sfx_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v --write -w" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3803,7 +3855,7 @@ plugin_sfx_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3811,7 +3863,7 @@ plugin_sfx_opts () { opts+=" --dry-run --feature-id= -f --force -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --value= -V --verbose -v" valopts+=" --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3819,7 +3871,7 @@ plugin_sfx_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3827,7 +3879,7 @@ plugin_sfx_opts () { opts+=" --dry-run --json-print -j --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3852,7 +3904,7 @@ plugin_shannon_opts () { opts+=" --cdw11= -c --data-len= -l --dry-run --feature-id= -f --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --cdw11 -c --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--sel -s" "0 1 2 3" ;; @@ -3861,7 +3913,7 @@ plugin_shannon_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3869,7 +3921,7 @@ plugin_shannon_opts () { opts+=" --data= -d --data-len= -l --dry-run --feature-id= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --value= -V --verbose -v" valopts+=" --data -d --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -3878,7 +3930,7 @@ plugin_shannon_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -3903,7 +3955,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3911,7 +3963,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3919,7 +3971,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3927,7 +3979,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3935,7 +3987,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3943,7 +3995,7 @@ plugin_sndk_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3951,7 +4003,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -u --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout --uuid-index -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3959,7 +4011,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --size= -s --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --size -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3967,7 +4019,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3975,7 +4027,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3983,7 +4035,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3991,7 +4043,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -3999,7 +4051,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4007,7 +4059,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4015,7 +4067,7 @@ plugin_sndk_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --op-option= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --op-option -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4023,7 +4075,7 @@ plugin_sndk_opts () { opts+=" --active_bucket_timer_threshold= -t --active_latency_config= -f --active_latency_minimum_window= -w --active_threshold_a= -a --active_threshold_b= -b --active_threshold_c= -c --active_threshold_d= -d --debug_log_trigger_enable= -r --discard_debug_log= -l --dry-run --latency_monitor_feature_enable= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4031,7 +4083,7 @@ plugin_sndk_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4039,7 +4091,7 @@ plugin_sndk_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4047,7 +4099,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4055,7 +4107,7 @@ plugin_sndk_opts () { opts+=" --dry-run --file= -O --log-id= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --file -O --log-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--file -O" ;; @@ -4064,7 +4116,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4072,14 +4124,16 @@ plugin_sndk_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; "vs-internal-log") - opts+=" --data-area= -d --file-size= -f --offset= -e --output-file= -O --transfer-size= -s --type= -t --verbose -V" - valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --transfer-size -s --type -t" + opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -V --verbose -v" + valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" "--type -t" ;; @@ -4087,7 +4141,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4095,7 +4149,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4103,7 +4157,7 @@ plugin_sndk_opts () { opts+=" --dry-run --interval= -i --log-page-mask= -p --log-page-version= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --interval -i --log-page-mask -p --log-page-version -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4111,7 +4165,7 @@ plugin_sndk_opts () { opts+=" --disable -d --dry-run --enable -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --status -s --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4119,7 +4173,7 @@ plugin_sndk_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4144,7 +4198,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4152,7 +4206,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4160,7 +4214,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4168,7 +4222,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4176,7 +4230,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4184,7 +4238,7 @@ plugin_solidigm_opts () { opts+=" --disable -d --dry-run --enable -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --read -r --set-options= --timeout= --type= -t --verbose -v --write -w" valopts+=" --output-format -o --output-format-version --set-options --timeout --type -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4192,7 +4246,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4200,7 +4254,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4208,7 +4262,7 @@ plugin_solidigm_opts () { opts+=" --config-file= -j --controller-init -c --data-area= -d --dry-run --host-generate= -g --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --source-file= -s --timeout= --verbose -v" valopts+=" --config-file -j --data-area -d --host-generate -g --output-format -o --output-format-version --set-options --source-file -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config-file -j" "--source-file -s" ;; @@ -4217,7 +4271,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4225,7 +4279,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4233,7 +4287,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" ;; @@ -4241,7 +4295,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4249,7 +4303,7 @@ plugin_solidigm_opts () { opts+=" --dir-name= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --type= -t --verbose -v" valopts+=" --dir-name -d --output-format -o --output-format-version --set-options --timeout --type -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--dir-name -d" ;; @@ -4258,7 +4312,7 @@ plugin_solidigm_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --no-uuid -n --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4266,7 +4320,7 @@ plugin_solidigm_opts () { opts+=" --disable -d --dry-run --enable -e --flush-freq= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --run-time= -r --sample-time= -s --set-options= --timeout= --trigger-field= -T --trigger-on-delta -D --trigger-on-latency -L --trigger-threshold= -V --type= -t --uuid-index= -U --verbose -v --wall-clock -w" valopts+=" --flush-freq -f --output-format -o --output-format-version --run-time -r --sample-time -s --set-options --timeout --trigger-field -T --trigger-threshold -V --type -t --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4291,7 +4345,7 @@ plugin_ssstc_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4316,7 +4370,7 @@ plugin_toshiba_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4324,7 +4378,7 @@ plugin_toshiba_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --prev-log -p --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4333,7 +4387,7 @@ plugin_toshiba_opts () { opts+=" --dry-run --log= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --log -l --namespace-id -n --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4359,7 +4413,7 @@ plugin_transcend_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4367,7 +4421,7 @@ plugin_transcend_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4408,7 +4462,7 @@ plugin_virtium_opts () { opts+=" --dry-run --freq= -f --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --run-time= -r --set-options= --test-name= -n --timeout= --verbose -v" valopts+=" --freq -f --output-file -O --output-format -o --output-format-version --run-time -r --set-options --test-name -n --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4417,7 +4471,7 @@ plugin_virtium_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4442,7 +4496,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --verbose -v" valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4451,7 +4505,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4459,7 +4513,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4467,7 +4521,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4475,7 +4529,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4483,7 +4537,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4491,7 +4545,7 @@ plugin_wdc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4499,7 +4553,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -u --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout --uuid-index -u" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4507,7 +4561,7 @@ plugin_wdc_opts () { opts+=" --dir-name= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dir-name -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--dir-name -d" ;; @@ -4516,7 +4570,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4525,7 +4579,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --size= -s --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --size -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4533,7 +4587,7 @@ plugin_wdc_opts () { opts+=" --dry-run --log-id= -l --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --verbose -v" valopts+=" --log-id -l --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4542,7 +4596,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4551,7 +4605,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4559,7 +4613,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4567,7 +4621,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4575,7 +4629,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4583,7 +4637,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" ;; @@ -4592,7 +4646,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4600,7 +4654,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4608,7 +4662,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4616,7 +4670,7 @@ plugin_wdc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --op-option= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --op-option -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4624,7 +4678,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4632,7 +4686,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4640,7 +4694,7 @@ plugin_wdc_opts () { opts+=" --active_bucket_timer_threshold= -t --active_latency_config= -f --active_latency_minimum_window= -w --active_threshold_a= -a --active_threshold_b= -b --active_threshold_c= -c --active_threshold_d= -d --debug_log_trigger_enable= -r --discard_debug_log= -l --dry-run --latency_monitor_feature_enable= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4648,7 +4702,7 @@ plugin_wdc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4656,7 +4710,7 @@ plugin_wdc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4664,7 +4718,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4672,7 +4726,7 @@ plugin_wdc_opts () { opts+=" --dry-run --file= -O --log-id= -i --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --file -O --log-id -i --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--file -O" ;; @@ -4681,7 +4735,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4689,14 +4743,16 @@ plugin_wdc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; "vs-internal-log") - opts+=" --data-area= -d --file-size= -f --offset= -e --output-file= -O --transfer-size= -s --type= -t --verbose -V" - valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --transfer-size -s --type -t" + opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -V --verbose -v" + valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" _nvme_opt_files "--output-file -O" "--type -t" ;; @@ -4704,7 +4760,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4712,7 +4768,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4720,7 +4776,7 @@ plugin_wdc_opts () { opts+=" --dry-run --interval= -i --log-page-mask= -p --log-page-version= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --interval -i --log-page-mask -p --log-page-version -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4728,7 +4784,7 @@ plugin_wdc_opts () { opts+=" --disable -d --dry-run --enable -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --status -s --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4736,7 +4792,7 @@ plugin_wdc_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4761,7 +4817,7 @@ plugin_ymtc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac @@ -4786,7 +4842,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4794,7 +4850,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4802,7 +4858,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4810,7 +4866,7 @@ plugin_zns_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4818,7 +4874,7 @@ plugin_zns_opts () { opts+=" --dry-run --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4826,7 +4882,7 @@ plugin_zns_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" ;; @@ -4834,7 +4890,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4842,7 +4898,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v --zrwaa -r" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4850,7 +4906,7 @@ plugin_zns_opts () { opts+=" --descs= -d --dry-run --extended -e --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --state= -S --timeout= --verbose -V --verbose -v" valopts+=" --descs -d --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --state -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4858,7 +4914,7 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4866,7 +4922,7 @@ plugin_zns_opts () { opts+=" --data= -d --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --start-lba= -s --timeout= --verbose -v --zrwaa -r" valopts+=" --data -d --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -4875,7 +4931,7 @@ plugin_zns_opts () { opts+=" --data= -d --data-size= -z --dry-run --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --piremap -P --prinfo= -p --quiet --set-options= --timeout= --verbose -v --zslba= -s" valopts+=" --data -d --data-size -z --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --set-options --timeout --zslba -s" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -4884,7 +4940,7 @@ plugin_zns_opts () { opts+=" --data-len= -l --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --timeout= --verbose -v --zra= -z --zrasf= -S" valopts+=" --data-len -l --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout --zra -z --zrasf -S" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; @@ -4892,7 +4948,7 @@ plugin_zns_opts () { opts+=" --data= -d --data-len= -l --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v --zsa= -z --zsaso -O" valopts+=" --data -d --data-len -l --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout --zsa -z" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--data -d" ;; @@ -4901,7 +4957,7 @@ plugin_zns_opts () { opts+=" --dry-run --lba= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --lba -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" ;; esac diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 7379d91742..a77a04dc94 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -249,11 +249,11 @@ $script:NvmeOptions = @{ 'check-tls-key' = @('--compat', '-C', '--dry-run', '--hostnqn', '-n', '--identity', '-I', '--keydata', '-d', '--keyring', '-k', '--keytype', '-t', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--subsysnqn', '-c', '--timeout', '--verbose', '-v', '--help', '-h') 'cmdset-ind-id-ns' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'compare' = @('--app-tag', '-a', '--app-tag-mask', '-m', '--block-count', '-c', '--block-size', '-b', '--data', '-d', '--data-size', '-z', '--dir-spec', '-S', '--dir-type', '-T', '--dry-run', '--dsm', '-D', '--force', '--force-unit-access', '-f', '--latency', '-t', '--limited-retry', '-l', '--metadata', '-M', '--metadata-size', '-y', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--prinfo', '-p', '--quiet', '--ref-tag', '-r', '--set-options', '--show-command', '-V', '--start-block', '-s', '--storage-tag', '-g', '--storage-tag-check', '-C', '--timeout', '--verbose', '-v', '--help', '-h') - 'config convert' = @('--config', '-J', '--force', '--output', '-o', '--help', '-h') + 'config convert' = @('--config', '-J', '--dry-run', '--force', '--no-ioctl-probing', '--no-retries', '--output', '-o', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'config create' = @('--concat', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--disable-sqflow', '--discovery', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-symname', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--no-ioctl-probing', '--no-retries', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output', '--output-format', '-o', '--output-format-version', '--persistent', '--queue-size', '-Q', '--quiet', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'config show' = @('--config', '-J', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'config status' = @('--help', '-h') - 'config validate' = @('--config', '-J', '--help', '-h') + 'config status' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'config validate' = @('--config', '-J', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'connect' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--devid-file', '--disable-sqflow', '--discovery', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--idempotent', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--no-ioctl-probing', '--no-retries', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--queue-size', '-Q', '--quiet', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'connect-all' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--force', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--nbft', '--nbft-path', '--no-ioctl-probing', '--no-nbft', '--no-retries', '--no-reuse', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--persistent', '-p', '--queue-size', '-Q', '--quiet', '--raw', '-r', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'copy' = @('--app-tag', '-a', '--app-tag-mask', '-m', '--blocks', '-b', '--dir-spec', '-S', '--dir-type', '-T', '--dry-run', '--expected-app-tag-masks', '-M', '--expected-app-tags', '-A', '--expected-ref-tags', '-R', '--force-unit-access', '-f', '--format', '-F', '--limited-retry', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--prinfor', '-P', '--prinfow', '-p', '--quiet', '--ref-tag', '-r', '--sdlba', '-d', '--set-options', '--slbs', '-s', '--snsids', '-N', '--sopts', '-O', '--storage-tag', '-t', '--storage-tag-check', '-c', '--timeout', '--verbose', '-v', '--help', '-h') @@ -369,7 +369,7 @@ $script:NvmeOptions = @{ 'intel id-ctrl' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--vendor-specific', '-V', '--verbose', '-v', '--help', '-h') 'intel internal-log' = @('--dry-run', '--log', '-l', '--namespace-id', '-n', '--nlognum', '-m', '--no-ioctl-probing', '--no-retries', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--region', '-r', '--set-options', '--timeout', '--verbose', '-v', '--verbose-nlog', '-V', '--help', '-h') 'intel lat-stats' = @('--dry-run', '--json', '-j', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--write', '-w', '--help', '-h') - 'intel lat-stats-tracking' = @('--disable', '-d', '--enable', '-e', '--help', '-h') + 'intel lat-stats-tracking' = @('--disable', '-d', '--dry-run', '--enable', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'intel market-name' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'intel set-bucket-thresholds' = @('--bucket-thresholds', '-t', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--write', '-w', '--help', '-h') 'intel smart-log-add' = @('--dry-run', '--json', '-j', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -444,14 +444,14 @@ $script:NvmeOptions = @{ 'memblaze lat-log-print' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze lat-log-print-x' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze lat-set-feature-x' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--sel-perf-log', '-s', '--set-commands-mask', '-m', '--set-options', '--set-read-threshold', '-r', '--set-trim-threshold', '-t', '--set-write-threshold', '-w', '--timeout', '--verbose', '-v', '--help', '-h') - 'memblaze lat-stats' = @('--disable', '-d', '--enable', '-e', '--help', '-h') + 'memblaze lat-stats' = @('--disable', '-d', '--dry-run', '--enable', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze lat-stats-print' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--write', '-w', '--help', '-h') 'memblaze lat-stats-print-x' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze perf-stats-print-x' = @('--dry-run', '--duration', '-d', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze select-download' = @('--dry-run', '--fw', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select', '-s', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'memblaze set-pm-status' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--set-options', '--timeout', '--value', '-V', '--verbose', '-v', '--help', '-h') 'memblaze smart-log-add' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'memblaze smart-log-add-x' = @('--raw-binary', '-b', '--help', '-h') + 'memblaze smart-log-add-x' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'mgmt-addr-list-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'mi-cmd-support-effects-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'micron clear-fw-activate-history' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -580,13 +580,13 @@ $script:NvmeOptions = @{ 'security send' = @('--dry-run', '--file', '-f', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--secp', '-p', '--set-options', '--spsp', '-s', '--timeout', '--tl', '-t', '--verbose', '-v', '--help', '-h') 'security-recv' = @('--al', '-t', '--dry-run', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--secp', '-p', '--set-options', '--size', '-x', '--spsp', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'security-send' = @('--dry-run', '--file', '-f', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--secp', '-p', '--set-options', '--spsp', '-s', '--timeout', '--tl', '-t', '--verbose', '-v', '--help', '-h') - 'sed 1' = @('--udev', '-u', '--verbose', '-V', '--help', '-h') - 'sed discover' = @('--udev', '-u', '--verbose', '-V', '--help', '-h') - 'sed initialize' = @('--read-only', '-r', '--help', '-h') - 'sed lock' = @('--ask-key', '-k', '--read-only', '-r', '--help', '-h') - 'sed password' = @('--help', '-h') - 'sed revert' = @('--destructive', '-e', '--psid', '-p', '--help', '-h') - 'sed unlock' = @('--ask-key', '-k', '--read-only', '-r', '--help', '-h') + 'sed 1' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'sed discover' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'sed initialize' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--read-only', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'sed lock' = @('--ask-key', '-k', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--read-only', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'sed password' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'sed revert' = @('--destructive', '-e', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--psid', '-p', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'sed unlock' = @('--ask-key', '-k', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--read-only', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'self-test-log' = @('--dry-run', '--dst-entries', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'set-feature' = @('--cdw12', '-c', '--data', '-d', '--data-len', '-l', '--dry-run', '--feature-id', '-f', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--set-options', '--timeout', '--uuid-index', '-U', '--value', '-V', '--verbose', '-v', '--help', '-h') 'set-property' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--offset', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--value', '-V', '--verbose', '-v', '--help', '-h') @@ -631,7 +631,7 @@ $script:NvmeOptions = @{ 'sndk vs-error-reason-identifier' = @('--dry-run', '--file', '-O', '--log-id', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-fw-activate-history' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-hw-rev-log' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'sndk vs-internal-log' = @('--data-area', '-d', '--file-size', '-f', '--offset', '-e', '--output-file', '-O', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--help', '-h') + 'sndk vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--verbose', '-v', '--help', '-h') 'sndk vs-nand-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-pcie-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-smart-add-log' = @('--dry-run', '--interval', '-i', '--log-page-mask', '-p', '--log-page-version', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -701,7 +701,7 @@ $script:NvmeOptions = @{ 'wdc vs-error-reason-identifier' = @('--dry-run', '--file', '-O', '--log-id', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-fw-activate-history' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-hw-rev-log' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'wdc vs-internal-log' = @('--data-area', '-d', '--file-size', '-f', '--offset', '-e', '--output-file', '-O', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--help', '-h') + 'wdc vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--verbose', '-v', '--help', '-h') 'wdc vs-nand-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-pcie-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-smart-add-log' = @('--dry-run', '--interval', '-i', '--log-page-mask', '-p', '--log-page-version', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -898,1516 +898,1561 @@ $script:NvmeFileOptions = @{ } $script:NvmeOptionValues = @{ - 'admin-passthru --output-format' = @('normal', 'json', 'binary', 'tabular') + 'admin-passthru --output-format' = @('normal', 'json', 'binary') 'admin-passthru --output-format-version' = @('1', '2') - 'admin-passthru -o' = @('normal', 'json', 'binary', 'tabular') - 'amzn id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'admin-passthru -o' = @('normal', 'json', 'binary') + 'amzn id-ctrl --output-format' = @('normal', 'json', 'binary') 'amzn id-ctrl --output-format-version' = @('1', '2') - 'amzn id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'amzn stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'amzn id-ctrl -o' = @('normal', 'json', 'binary') + 'amzn stats --output-format' = @('normal', 'json', 'binary') 'amzn stats --output-format-version' = @('1', '2') - 'amzn stats -o' = @('normal', 'json', 'binary', 'tabular') - 'ana-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'amzn stats -o' = @('normal', 'json', 'binary') + 'ana-log --output-format' = @('normal', 'json', 'binary') 'ana-log --output-format-version' = @('1', '2') - 'ana-log -o' = @('normal', 'json', 'binary', 'tabular') - 'attach-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ana-log -o' = @('normal', 'json', 'binary') + 'attach-ns --output-format' = @('normal', 'json', 'binary') 'attach-ns --output-format-version' = @('1', '2') - 'attach-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'ave-discovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'attach-ns -o' = @('normal', 'json', 'binary') + 'ave-discovery-log --output-format' = @('normal', 'json', 'binary') 'ave-discovery-log --output-format-version' = @('1', '2') - 'ave-discovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'boot-part-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ave-discovery-log -o' = @('normal', 'json', 'binary') + 'boot-part-log --output-format' = @('normal', 'json', 'binary') 'boot-part-log --output-format-version' = @('1', '2') - 'boot-part-log -o' = @('normal', 'json', 'binary', 'tabular') - 'capacity-mgmt --output-format' = @('normal', 'json', 'binary', 'tabular') + 'boot-part-log -o' = @('normal', 'json', 'binary') + 'capacity-mgmt --output-format' = @('normal', 'json', 'binary') 'capacity-mgmt --output-format-version' = @('1', '2') - 'capacity-mgmt -o' = @('normal', 'json', 'binary', 'tabular') - 'changed-alloc-ns-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'capacity-mgmt -o' = @('normal', 'json', 'binary') + 'changed-alloc-ns-list-log --output-format' = @('normal', 'json', 'binary') 'changed-alloc-ns-list-log --output-format-version' = @('1', '2') - 'changed-alloc-ns-list-log -o' = @('normal', 'json', 'binary', 'tabular') - 'changed-ns-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'changed-alloc-ns-list-log -o' = @('normal', 'json', 'binary') + 'changed-ns-list-log --output-format' = @('normal', 'json', 'binary') 'changed-ns-list-log --output-format-version' = @('1', '2') - 'changed-ns-list-log -o' = @('normal', 'json', 'binary', 'tabular') - 'check-dhchap-key --output-format' = @('normal', 'json', 'binary', 'tabular') + 'changed-ns-list-log -o' = @('normal', 'json', 'binary') + 'check-dhchap-key --output-format' = @('normal', 'json', 'binary') 'check-dhchap-key --output-format-version' = @('1', '2') - 'check-dhchap-key -o' = @('normal', 'json', 'binary', 'tabular') - 'check-tls-key --output-format' = @('normal', 'json', 'binary', 'tabular') + 'check-dhchap-key -o' = @('normal', 'json', 'binary') + 'check-tls-key --output-format' = @('normal', 'json', 'binary') 'check-tls-key --output-format-version' = @('1', '2') - 'check-tls-key -o' = @('normal', 'json', 'binary', 'tabular') - 'cmdset-ind-id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'check-tls-key -o' = @('normal', 'json', 'binary') + 'cmdset-ind-id-ns --output-format' = @('normal', 'json', 'binary') 'cmdset-ind-id-ns --output-format-version' = @('1', '2') - 'cmdset-ind-id-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'compare --output-format' = @('normal', 'json', 'binary', 'tabular') + 'cmdset-ind-id-ns -o' = @('normal', 'json', 'binary') + 'compare --output-format' = @('normal', 'json', 'binary') 'compare --output-format-version' = @('1', '2') - 'compare -o' = @('normal', 'json', 'binary', 'tabular') - 'config create --output-format' = @('normal', 'json', 'binary', 'tabular') + 'compare -o' = @('normal', 'json', 'binary') + 'config convert --output-format' = @('normal', 'json', 'binary') + 'config convert --output-format-version' = @('1', '2') + 'config convert -o' = @('normal', 'json', 'binary') + 'config create --output-format' = @('normal', 'json', 'binary') 'config create --output-format-version' = @('1', '2') - 'config create -o' = @('normal', 'json', 'binary', 'tabular') - 'config show --output-format' = @('normal', 'json', 'binary', 'tabular') + 'config create -o' = @('normal', 'json', 'binary') + 'config show --output-format' = @('normal', 'json', 'binary') 'config show --output-format-version' = @('1', '2') - 'config show -o' = @('normal', 'json', 'binary', 'tabular') - 'connect --output-format' = @('normal', 'json', 'binary', 'tabular') + 'config show -o' = @('normal', 'json', 'binary') + 'config status --output-format' = @('normal', 'json', 'binary') + 'config status --output-format-version' = @('1', '2') + 'config status -o' = @('normal', 'json', 'binary') + 'config validate --output-format' = @('normal', 'json', 'binary') + 'config validate --output-format-version' = @('1', '2') + 'config validate -o' = @('normal', 'json', 'binary') + 'connect --output-format' = @('normal', 'json', 'binary') 'connect --output-format-version' = @('1', '2') - 'connect -o' = @('normal', 'json', 'binary', 'tabular') - 'connect-all --output-format' = @('normal', 'json', 'binary', 'tabular') + 'connect -o' = @('normal', 'json', 'binary') + 'connect-all --output-format' = @('normal', 'json', 'binary') 'connect-all --output-format-version' = @('1', '2') - 'connect-all -o' = @('normal', 'json', 'binary', 'tabular') - 'copy --output-format' = @('normal', 'json', 'binary', 'tabular') + 'connect-all -o' = @('normal', 'json', 'binary') + 'copy --output-format' = @('normal', 'json', 'binary') 'copy --output-format-version' = @('1', '2') - 'copy -o' = @('normal', 'json', 'binary', 'tabular') - 'create-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'copy -o' = @('normal', 'json', 'binary') + 'create-ns --output-format' = @('normal', 'json', 'binary') 'create-ns --output-format-version' = @('1', '2') - 'create-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'dapustor smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'create-ns -o' = @('normal', 'json', 'binary') + 'dapustor smart-log-add --output-format' = @('normal', 'json', 'binary') 'dapustor smart-log-add --output-format-version' = @('1', '2') - 'dapustor smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'delete-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dapustor smart-log-add -o' = @('normal', 'json', 'binary') + 'delete-ns --output-format' = @('normal', 'json', 'binary') 'delete-ns --output-format-version' = @('1', '2') - 'delete-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'dell id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'delete-ns -o' = @('normal', 'json', 'binary') + 'dell id-ctrl --output-format' = @('normal', 'json', 'binary') 'dell id-ctrl --output-format-version' = @('1', '2') - 'dell id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'dera smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dell id-ctrl -o' = @('normal', 'json', 'binary') + 'dera smart-log-add --output-format' = @('normal', 'json', 'binary') 'dera smart-log-add --output-format-version' = @('1', '2') - 'dera smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'dera stat --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dera smart-log-add -o' = @('normal', 'json', 'binary') + 'dera stat --output-format' = @('normal', 'json', 'binary') 'dera stat --output-format-version' = @('1', '2') - 'dera stat -o' = @('normal', 'json', 'binary', 'tabular') - 'detach-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dera stat -o' = @('normal', 'json', 'binary') + 'detach-ns --output-format' = @('normal', 'json', 'binary') 'detach-ns --output-format-version' = @('1', '2') - 'detach-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'device-self-test --output-format' = @('normal', 'json', 'binary', 'tabular') + 'detach-ns -o' = @('normal', 'json', 'binary') + 'device-self-test --output-format' = @('normal', 'json', 'binary') 'device-self-test --output-format-version' = @('1', '2') - 'device-self-test -o' = @('normal', 'json', 'binary', 'tabular') - 'dim --output-format' = @('normal', 'json', 'binary', 'tabular') + 'device-self-test -o' = @('normal', 'json', 'binary') + 'dim --output-format' = @('normal', 'json', 'binary') 'dim --output-format-version' = @('1', '2') - 'dim -o' = @('normal', 'json', 'binary', 'tabular') - 'dir receive --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dim -o' = @('normal', 'json', 'binary') + 'dir receive --output-format' = @('normal', 'json', 'binary') 'dir receive --output-format-version' = @('1', '2') - 'dir receive -o' = @('normal', 'json', 'binary', 'tabular') - 'dir send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dir receive -o' = @('normal', 'json', 'binary') + 'dir send --output-format' = @('normal', 'json', 'binary') 'dir send --output-format-version' = @('1', '2') - 'dir send -o' = @('normal', 'json', 'binary', 'tabular') - 'dir-receive --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dir send -o' = @('normal', 'json', 'binary') + 'dir-receive --output-format' = @('normal', 'json', 'binary') 'dir-receive --output-format-version' = @('1', '2') - 'dir-receive -o' = @('normal', 'json', 'binary', 'tabular') - 'dir-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dir-receive -o' = @('normal', 'json', 'binary') + 'dir-send --output-format' = @('normal', 'json', 'binary') 'dir-send --output-format-version' = @('1', '2') - 'dir-send -o' = @('normal', 'json', 'binary', 'tabular') - 'disconnect --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dir-send -o' = @('normal', 'json', 'binary') + 'disconnect --output-format' = @('normal', 'json', 'binary') 'disconnect --output-format-version' = @('1', '2') - 'disconnect -o' = @('normal', 'json', 'binary', 'tabular') - 'disconnect-all --output-format' = @('normal', 'json', 'binary', 'tabular') + 'disconnect -o' = @('normal', 'json', 'binary') + 'disconnect-all --output-format' = @('normal', 'json', 'binary') 'disconnect-all --output-format-version' = @('1', '2') - 'disconnect-all -o' = @('normal', 'json', 'binary', 'tabular') - 'discover --output-format' = @('normal', 'json', 'binary', 'tabular') + 'disconnect-all -o' = @('normal', 'json', 'binary') + 'discover --output-format' = @('normal', 'json', 'binary') 'discover --output-format-version' = @('1', '2') - 'discover -o' = @('normal', 'json', 'binary', 'tabular') - 'dispersed-ns-participating-nss-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'discover -o' = @('normal', 'json', 'binary') + 'dispersed-ns-participating-nss-log --output-format' = @('normal', 'json', 'binary') 'dispersed-ns-participating-nss-log --output-format-version' = @('1', '2') - 'dispersed-ns-participating-nss-log -o' = @('normal', 'json', 'binary', 'tabular') - 'dsm --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dispersed-ns-participating-nss-log -o' = @('normal', 'json', 'binary') + 'dsm --output-format' = @('normal', 'json', 'binary') 'dsm --output-format-version' = @('1', '2') - 'dsm -o' = @('normal', 'json', 'binary', 'tabular') - 'effects-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'dsm -o' = @('normal', 'json', 'binary') + 'effects-log --output-format' = @('normal', 'json', 'binary') 'effects-log --output-format-version' = @('1', '2') - 'effects-log -o' = @('normal', 'json', 'binary', 'tabular') - 'endurance-event-agg-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'effects-log -o' = @('normal', 'json', 'binary') + 'endurance-event-agg-log --output-format' = @('normal', 'json', 'binary') 'endurance-event-agg-log --output-format-version' = @('1', '2') - 'endurance-event-agg-log -o' = @('normal', 'json', 'binary', 'tabular') - 'endurance-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'endurance-event-agg-log -o' = @('normal', 'json', 'binary') + 'endurance-log --output-format' = @('normal', 'json', 'binary') 'endurance-log --output-format-version' = @('1', '2') - 'endurance-log -o' = @('normal', 'json', 'binary', 'tabular') - 'error-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'endurance-log -o' = @('normal', 'json', 'binary') + 'error-log --output-format' = @('normal', 'json', 'binary') 'error-log --output-format-version' = @('1', '2') - 'error-log -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'error-log -o' = @('normal', 'json', 'binary') + 'exclusion add --output-format' = @('normal', 'json', 'binary') 'exclusion add --output-format-version' = @('1', '2') - 'exclusion add -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion create --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion add -o' = @('normal', 'json', 'binary') + 'exclusion create --output-format' = @('normal', 'json', 'binary') 'exclusion create --output-format-version' = @('1', '2') - 'exclusion create -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion delete --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion create -o' = @('normal', 'json', 'binary') + 'exclusion delete --output-format' = @('normal', 'json', 'binary') 'exclusion delete --output-format-version' = @('1', '2') - 'exclusion delete -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion edit --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion delete -o' = @('normal', 'json', 'binary') + 'exclusion edit --output-format' = @('normal', 'json', 'binary') 'exclusion edit --output-format-version' = @('1', '2') - 'exclusion edit -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion edit -o' = @('normal', 'json', 'binary') + 'exclusion list --output-format' = @('normal', 'json', 'binary') 'exclusion list --output-format-version' = @('1', '2') - 'exclusion list -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion remove --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion list -o' = @('normal', 'json', 'binary') + 'exclusion remove --output-format' = @('normal', 'json', 'binary') 'exclusion remove --output-format-version' = @('1', '2') - 'exclusion remove -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp configs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'exclusion remove -o' = @('normal', 'json', 'binary') + 'fdp configs --output-format' = @('normal', 'json', 'binary') 'fdp configs --output-format-version' = @('1', '2') - 'fdp configs -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp events --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp configs -o' = @('normal', 'json', 'binary') + 'fdp events --output-format' = @('normal', 'json', 'binary') 'fdp events --output-format-version' = @('1', '2') - 'fdp events -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp events -o' = @('normal', 'json', 'binary') + 'fdp feature --output-format' = @('normal', 'json', 'binary') 'fdp feature --output-format-version' = @('1', '2') - 'fdp feature -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp set-events --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp feature -o' = @('normal', 'json', 'binary') + 'fdp set-events --output-format' = @('normal', 'json', 'binary') 'fdp set-events --output-format-version' = @('1', '2') - 'fdp set-events -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp set-events -o' = @('normal', 'json', 'binary') + 'fdp stats --output-format' = @('normal', 'json', 'binary') 'fdp stats --output-format-version' = @('1', '2') - 'fdp stats -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp stats -o' = @('normal', 'json', 'binary') + 'fdp status --output-format' = @('normal', 'json', 'binary') 'fdp status --output-format-version' = @('1', '2') - 'fdp status -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp update --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp status -o' = @('normal', 'json', 'binary') + 'fdp update --output-format' = @('normal', 'json', 'binary') 'fdp update --output-format-version' = @('1', '2') - 'fdp update -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp usage --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp update -o' = @('normal', 'json', 'binary') + 'fdp usage --output-format' = @('normal', 'json', 'binary') 'fdp usage --output-format-version' = @('1', '2') - 'fdp usage -o' = @('normal', 'json', 'binary', 'tabular') - 'feat arbitration --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fdp usage -o' = @('normal', 'json', 'binary') + 'feat arbitration --output-format' = @('normal', 'json', 'binary') 'feat arbitration --output-format-version' = @('1', '2') 'feat arbitration --sel' = @('0', '1', '2', '3') 'feat arbitration -S' = @('0', '1', '2', '3') - 'feat arbitration -o' = @('normal', 'json', 'binary', 'tabular') - 'feat async-event-conf --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat arbitration -o' = @('normal', 'json', 'binary') + 'feat async-event-conf --output-format' = @('normal', 'json', 'binary') 'feat async-event-conf --output-format-version' = @('1', '2') 'feat async-event-conf --sel' = @('0', '1', '2', '3') 'feat async-event-conf -S' = @('0', '1', '2', '3') - 'feat async-event-conf -o' = @('normal', 'json', 'binary', 'tabular') - 'feat err-recovery --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat async-event-conf -o' = @('normal', 'json', 'binary') + 'feat err-recovery --output-format' = @('normal', 'json', 'binary') 'feat err-recovery --output-format-version' = @('1', '2') 'feat err-recovery --sel' = @('0', '1', '2', '3') 'feat err-recovery -S' = @('0', '1', '2', '3') - 'feat err-recovery -o' = @('normal', 'json', 'binary', 'tabular') - 'feat hctm --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat err-recovery -o' = @('normal', 'json', 'binary') + 'feat hctm --output-format' = @('normal', 'json', 'binary') 'feat hctm --output-format-version' = @('1', '2') 'feat hctm --sel' = @('0', '1', '2', '3') 'feat hctm -S' = @('0', '1', '2', '3') - 'feat hctm -o' = @('normal', 'json', 'binary', 'tabular') - 'feat host-behavior-support --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat hctm -o' = @('normal', 'json', 'binary') + 'feat host-behavior-support --output-format' = @('normal', 'json', 'binary') 'feat host-behavior-support --output-format-version' = @('1', '2') 'feat host-behavior-support --sel' = @('0', '1', '2', '3') 'feat host-behavior-support -S' = @('0', '1', '2', '3') - 'feat host-behavior-support -o' = @('normal', 'json', 'binary', 'tabular') - 'feat int-coalesce --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat host-behavior-support -o' = @('normal', 'json', 'binary') + 'feat int-coalesce --output-format' = @('normal', 'json', 'binary') 'feat int-coalesce --output-format-version' = @('1', '2') 'feat int-coalesce --sel' = @('0', '1', '2', '3') 'feat int-coalesce -S' = @('0', '1', '2', '3') - 'feat int-coalesce -o' = @('normal', 'json', 'binary', 'tabular') - 'feat int-vector-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat int-coalesce -o' = @('normal', 'json', 'binary') + 'feat int-vector-config --output-format' = @('normal', 'json', 'binary') 'feat int-vector-config --output-format-version' = @('1', '2') 'feat int-vector-config --sel' = @('0', '1', '2', '3') 'feat int-vector-config -S' = @('0', '1', '2', '3') - 'feat int-vector-config -o' = @('normal', 'json', 'binary', 'tabular') - 'feat keep-alive-timer --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat int-vector-config -o' = @('normal', 'json', 'binary') + 'feat keep-alive-timer --output-format' = @('normal', 'json', 'binary') 'feat keep-alive-timer --output-format-version' = @('1', '2') 'feat keep-alive-timer --sel' = @('0', '1', '2', '3') 'feat keep-alive-timer -S' = @('0', '1', '2', '3') - 'feat keep-alive-timer -o' = @('normal', 'json', 'binary', 'tabular') - 'feat lba-range-type --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat keep-alive-timer -o' = @('normal', 'json', 'binary') + 'feat lba-range-type --output-format' = @('normal', 'json', 'binary') 'feat lba-range-type --output-format-version' = @('1', '2') 'feat lba-range-type --sel' = @('0', '1', '2', '3') 'feat lba-range-type -S' = @('0', '1', '2', '3') - 'feat lba-range-type -o' = @('normal', 'json', 'binary', 'tabular') - 'feat num-queues --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat lba-range-type -o' = @('normal', 'json', 'binary') + 'feat num-queues --output-format' = @('normal', 'json', 'binary') 'feat num-queues --output-format-version' = @('1', '2') 'feat num-queues --sel' = @('0', '1', '2', '3') 'feat num-queues -S' = @('0', '1', '2', '3') - 'feat num-queues -o' = @('normal', 'json', 'binary', 'tabular') - 'feat perf-characteristics --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat num-queues -o' = @('normal', 'json', 'binary') + 'feat perf-characteristics --output-format' = @('normal', 'json', 'binary') 'feat perf-characteristics --output-format-version' = @('1', '2') 'feat perf-characteristics --sel' = @('0', '1', '2', '3') 'feat perf-characteristics -S' = @('0', '1', '2', '3') - 'feat perf-characteristics -o' = @('normal', 'json', 'binary', 'tabular') - 'feat power-limit --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat perf-characteristics -o' = @('normal', 'json', 'binary') + 'feat power-limit --output-format' = @('normal', 'json', 'binary') 'feat power-limit --output-format-version' = @('1', '2') 'feat power-limit --sel' = @('0', '1', '2', '3') 'feat power-limit -S' = @('0', '1', '2', '3') - 'feat power-limit -o' = @('normal', 'json', 'binary', 'tabular') - 'feat power-meas --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat power-limit -o' = @('normal', 'json', 'binary') + 'feat power-meas --output-format' = @('normal', 'json', 'binary') 'feat power-meas --output-format-version' = @('1', '2') 'feat power-meas --sel' = @('0', '1', '2', '3') 'feat power-meas -S' = @('0', '1', '2', '3') - 'feat power-meas -o' = @('normal', 'json', 'binary', 'tabular') - 'feat power-mgmt --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat power-meas -o' = @('normal', 'json', 'binary') + 'feat power-mgmt --output-format' = @('normal', 'json', 'binary') 'feat power-mgmt --output-format-version' = @('1', '2') 'feat power-mgmt --sel' = @('0', '1', '2', '3') 'feat power-mgmt -S' = @('0', '1', '2', '3') - 'feat power-mgmt -o' = @('normal', 'json', 'binary', 'tabular') - 'feat power-thresh --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat power-mgmt -o' = @('normal', 'json', 'binary') + 'feat power-thresh --output-format' = @('normal', 'json', 'binary') 'feat power-thresh --output-format-version' = @('1', '2') 'feat power-thresh --sel' = @('0', '1', '2', '3') 'feat power-thresh -S' = @('0', '1', '2', '3') - 'feat power-thresh -o' = @('normal', 'json', 'binary', 'tabular') - 'feat temp-thresh --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat power-thresh -o' = @('normal', 'json', 'binary') + 'feat temp-thresh --output-format' = @('normal', 'json', 'binary') 'feat temp-thresh --output-format-version' = @('1', '2') 'feat temp-thresh --sel' = @('0', '1', '2', '3') 'feat temp-thresh -S' = @('0', '1', '2', '3') - 'feat temp-thresh -o' = @('normal', 'json', 'binary', 'tabular') - 'feat timestamp --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat temp-thresh -o' = @('normal', 'json', 'binary') + 'feat timestamp --output-format' = @('normal', 'json', 'binary') 'feat timestamp --output-format-version' = @('1', '2') 'feat timestamp --sel' = @('0', '1', '2', '3') 'feat timestamp -S' = @('0', '1', '2', '3') - 'feat timestamp -o' = @('normal', 'json', 'binary', 'tabular') - 'feat volatile-wc --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat timestamp -o' = @('normal', 'json', 'binary') + 'feat volatile-wc --output-format' = @('normal', 'json', 'binary') 'feat volatile-wc --output-format-version' = @('1', '2') 'feat volatile-wc --sel' = @('0', '1', '2', '3') 'feat volatile-wc -S' = @('0', '1', '2', '3') - 'feat volatile-wc -o' = @('normal', 'json', 'binary', 'tabular') - 'feat write-atom-normal --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat volatile-wc -o' = @('normal', 'json', 'binary') + 'feat write-atom-normal --output-format' = @('normal', 'json', 'binary') 'feat write-atom-normal --output-format-version' = @('1', '2') 'feat write-atom-normal --sel' = @('0', '1', '2', '3') 'feat write-atom-normal -S' = @('0', '1', '2', '3') - 'feat write-atom-normal -o' = @('normal', 'json', 'binary', 'tabular') - 'fid-support-effects-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat write-atom-normal -o' = @('normal', 'json', 'binary') + 'fid-support-effects-log --output-format' = @('normal', 'json', 'binary') 'fid-support-effects-log --output-format-version' = @('1', '2') - 'fid-support-effects-log -o' = @('normal', 'json', 'binary', 'tabular') - 'flush --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fid-support-effects-log -o' = @('normal', 'json', 'binary') + 'flush --output-format' = @('normal', 'json', 'binary') 'flush --output-format-version' = @('1', '2') - 'flush -o' = @('normal', 'json', 'binary', 'tabular') - 'format --output-format' = @('normal', 'json', 'binary', 'tabular') + 'flush -o' = @('normal', 'json', 'binary') + 'format --output-format' = @('normal', 'json', 'binary') 'format --output-format-version' = @('1', '2') - 'format -o' = @('normal', 'json', 'binary', 'tabular') + 'format -o' = @('normal', 'json', 'binary') 'fw activate --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw activate --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw activate --output-format' = @('normal', 'json', 'binary') 'fw activate --output-format-version' = @('1', '2') 'fw activate -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw activate -o' = @('normal', 'json', 'binary', 'tabular') + 'fw activate -o' = @('normal', 'json', 'binary') 'fw commit --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw commit --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw commit --output-format' = @('normal', 'json', 'binary') 'fw commit --output-format-version' = @('1', '2') 'fw commit -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw commit -o' = @('normal', 'json', 'binary', 'tabular') - 'fw download --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw commit -o' = @('normal', 'json', 'binary') + 'fw download --output-format' = @('normal', 'json', 'binary') 'fw download --output-format-version' = @('1', '2') - 'fw download -o' = @('normal', 'json', 'binary', 'tabular') + 'fw download -o' = @('normal', 'json', 'binary') 'fw-activate --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw-activate --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw-activate --output-format' = @('normal', 'json', 'binary') 'fw-activate --output-format-version' = @('1', '2') 'fw-activate -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw-activate -o' = @('normal', 'json', 'binary', 'tabular') + 'fw-activate -o' = @('normal', 'json', 'binary') 'fw-commit --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw-commit --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw-commit --output-format' = @('normal', 'json', 'binary') 'fw-commit --output-format-version' = @('1', '2') 'fw-commit -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') - 'fw-commit -o' = @('normal', 'json', 'binary', 'tabular') - 'fw-download --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw-commit -o' = @('normal', 'json', 'binary') + 'fw-download --output-format' = @('normal', 'json', 'binary') 'fw-download --output-format-version' = @('1', '2') - 'fw-download -o' = @('normal', 'json', 'binary', 'tabular') - 'fw-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw-download -o' = @('normal', 'json', 'binary') + 'fw-log --output-format' = @('normal', 'json', 'binary') 'fw-log --output-format-version' = @('1', '2') - 'fw-log -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-dhchap-key --output-format' = @('normal', 'json', 'binary', 'tabular') + 'fw-log -o' = @('normal', 'json', 'binary') + 'gen-dhchap-key --output-format' = @('normal', 'json', 'binary') 'gen-dhchap-key --output-format-version' = @('1', '2') - 'gen-dhchap-key -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-hostnqn --output-format' = @('normal', 'json', 'binary', 'tabular') + 'gen-dhchap-key -o' = @('normal', 'json', 'binary') + 'gen-hostnqn --output-format' = @('normal', 'json', 'binary') 'gen-hostnqn --output-format-version' = @('1', '2') - 'gen-hostnqn -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-tls-key --output-format' = @('normal', 'json', 'binary', 'tabular') + 'gen-hostnqn -o' = @('normal', 'json', 'binary') + 'gen-tls-key --output-format' = @('normal', 'json', 'binary') 'gen-tls-key --output-format-version' = @('1', '2') - 'gen-tls-key -o' = @('normal', 'json', 'binary', 'tabular') + 'gen-tls-key -o' = @('normal', 'json', 'binary') 'get-feature --feature-id' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') - 'get-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-feature --output-format' = @('normal', 'json', 'binary') 'get-feature --output-format-version' = @('1', '2') 'get-feature --sel' = @('0', '1', '2', '3') 'get-feature -f' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') - 'get-feature -o' = @('normal', 'json', 'binary', 'tabular') + 'get-feature -o' = @('normal', 'json', 'binary') 'get-feature -s' = @('0', '1', '2', '3') - 'get-lba-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-lba-status --output-format' = @('normal', 'json', 'binary') 'get-lba-status --output-format-version' = @('1', '2') - 'get-lba-status -o' = @('normal', 'json', 'binary', 'tabular') + 'get-lba-status -o' = @('normal', 'json', 'binary') 'get-log --log-id' = @('supported-log-pages', 'error', 'smart', 'fw-slot', 'changed-attached-ns', 'changed-ns', 'cmd-effects', 'device-self-test', 'telemetry-host', 'telemetry-ctrl', 'endurance-group', 'predictable-lat-nvmset', 'predictable-lat-agg', 'ana', 'persistent-event', 'lba-status', 'endurance-grp-evt', 'media-unit-status', 'supported-cap-config-list', 'fid-supported-effects', 'mi-cmd-supported-effects', 'cmd-and-feat-lockdown', 'boot-partition', 'rotational-media-info', 'dispersed-ns-participating-ns', 'mgmt-addr-list', 'phy-rx-eom', 'reachability-groups', 'reachability-associations', 'changed-alloc-ns-list', 'fdp-configs', 'fdp-ruh-usage', 'fdp-stats', 'fdp-events', 'discover', 'host-discover', 'ave-discover', 'pull-model-ddc-req', 'reservation', 'sanitize', 'zns-changed-zones') - 'get-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-log --output-format' = @('normal', 'json', 'binary') 'get-log --output-format-version' = @('1', '2') 'get-log -i' = @('supported-log-pages', 'error', 'smart', 'fw-slot', 'changed-attached-ns', 'changed-ns', 'cmd-effects', 'device-self-test', 'telemetry-host', 'telemetry-ctrl', 'endurance-group', 'predictable-lat-nvmset', 'predictable-lat-agg', 'ana', 'persistent-event', 'lba-status', 'endurance-grp-evt', 'media-unit-status', 'supported-cap-config-list', 'fid-supported-effects', 'mi-cmd-supported-effects', 'cmd-and-feat-lockdown', 'boot-partition', 'rotational-media-info', 'dispersed-ns-participating-ns', 'mgmt-addr-list', 'phy-rx-eom', 'reachability-groups', 'reachability-associations', 'changed-alloc-ns-list', 'fdp-configs', 'fdp-ruh-usage', 'fdp-stats', 'fdp-events', 'discover', 'host-discover', 'ave-discover', 'pull-model-ddc-req', 'reservation', 'sanitize', 'zns-changed-zones') - 'get-log -o' = @('normal', 'json', 'binary', 'tabular') - 'get-ns-id --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-log -o' = @('normal', 'json', 'binary') + 'get-ns-id --output-format' = @('normal', 'json', 'binary') 'get-ns-id --output-format-version' = @('1', '2') - 'get-ns-id -o' = @('normal', 'json', 'binary', 'tabular') - 'get-property --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-ns-id -o' = @('normal', 'json', 'binary') + 'get-property --output-format' = @('normal', 'json', 'binary') 'get-property --output-format-version' = @('1', '2') - 'get-property -o' = @('normal', 'json', 'binary', 'tabular') - 'get-reg --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-property -o' = @('normal', 'json', 'binary') + 'get-reg --output-format' = @('normal', 'json', 'binary') 'get-reg --output-format-version' = @('1', '2') - 'get-reg -o' = @('normal', 'json', 'binary', 'tabular') - 'host-discovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-reg -o' = @('normal', 'json', 'binary') + 'host-discovery-log --output-format' = @('normal', 'json', 'binary') 'host-discovery-log --output-format-version' = @('1', '2') - 'host-discovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'huawei id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'host-discovery-log -o' = @('normal', 'json', 'binary') + 'huawei id-ctrl --output-format' = @('normal', 'json', 'binary') 'huawei id-ctrl --output-format-version' = @('1', '2') - 'huawei id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'huawei list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'huawei id-ctrl -o' = @('normal', 'json', 'binary') + 'huawei list --output-format' = @('normal', 'json') 'huawei list --output-format-version' = @('1', '2') - 'huawei list -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm crit-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'huawei list -o' = @('normal', 'json') + 'ibm crit-log --output-format' = @('normal', 'json', 'binary') 'ibm crit-log --output-format-version' = @('1', '2') - 'ibm crit-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm persist-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ibm crit-log -o' = @('normal', 'json', 'binary') + 'ibm persist-event-log --output-format' = @('normal', 'json', 'binary') 'ibm persist-event-log --output-format-version' = @('1', '2') - 'ibm persist-event-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm vpd --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ibm persist-event-log -o' = @('normal', 'json', 'binary') + 'ibm vpd --output-format' = @('normal', 'json', 'binary') 'ibm vpd --output-format-version' = @('1', '2') - 'ibm vpd -o' = @('normal', 'json', 'binary', 'tabular') - 'id ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ibm vpd -o' = @('normal', 'json', 'binary') + 'id ctrl --output-format' = @('normal', 'json', 'binary') 'id ctrl --output-format-version' = @('1', '2') - 'id ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'id ctrl-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ctrl -o' = @('normal', 'json', 'binary') + 'id ctrl-list --output-format' = @('normal', 'json') 'id ctrl-list --output-format-version' = @('1', '2') - 'id ctrl-list -o' = @('normal', 'json', 'binary', 'tabular') - 'id domain --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ctrl-list -o' = @('normal', 'json') + 'id domain --output-format' = @('normal', 'json', 'binary') 'id domain --output-format-version' = @('1', '2') - 'id domain -o' = @('normal', 'json', 'binary', 'tabular') - 'id endgrp-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id domain -o' = @('normal', 'json', 'binary') + 'id endgrp-list --output-format' = @('normal', 'json') 'id endgrp-list --output-format-version' = @('1', '2') - 'id endgrp-list -o' = @('normal', 'json', 'binary', 'tabular') - 'id iocs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id endgrp-list -o' = @('normal', 'json') + 'id iocs --output-format' = @('normal', 'json', 'binary') 'id iocs --output-format-version' = @('1', '2') - 'id iocs -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id iocs -o' = @('normal', 'json', 'binary') + 'id ns --output-format' = @('normal', 'json', 'binary') 'id ns --output-format-version' = @('1', '2') - 'id ns -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-descs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns -o' = @('normal', 'json', 'binary') + 'id ns-descs --output-format' = @('normal', 'json', 'binary') 'id ns-descs --output-format-version' = @('1', '2') - 'id ns-descs -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-granularity --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns-descs -o' = @('normal', 'json', 'binary') + 'id ns-granularity --output-format' = @('normal', 'json', 'binary') 'id ns-granularity --output-format-version' = @('1', '2') - 'id ns-granularity -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-ind --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns-granularity -o' = @('normal', 'json', 'binary') + 'id ns-ind --output-format' = @('normal', 'json', 'binary') 'id ns-ind --output-format-version' = @('1', '2') - 'id ns-ind -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns-ind -o' = @('normal', 'json', 'binary') + 'id ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id ns-lba-format --output-format-version' = @('1', '2') - 'id ns-lba-format -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns-lba-format -o' = @('normal', 'json', 'binary') + 'id ns-list --output-format' = @('normal', 'json') 'id ns-list --output-format-version' = @('1', '2') - 'id ns-list -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id ns-list -o' = @('normal', 'json') + 'id nvm-ctrl --output-format' = @('normal', 'json', 'binary') 'id nvm-ctrl --output-format-version' = @('1', '2') - 'id nvm-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id nvm-ctrl -o' = @('normal', 'json', 'binary') + 'id nvm-ns --output-format' = @('normal', 'json', 'binary') 'id nvm-ns --output-format-version' = @('1', '2') - 'id nvm-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id nvm-ns -o' = @('normal', 'json', 'binary') + 'id nvm-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id nvm-ns-lba-format --output-format-version' = @('1', '2') - 'id nvm-ns-lba-format -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvmset --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id nvm-ns-lba-format -o' = @('normal', 'json', 'binary') + 'id nvmset --output-format' = @('normal', 'json', 'binary') 'id nvmset --output-format-version' = @('1', '2') - 'id nvmset -o' = @('normal', 'json', 'binary', 'tabular') - 'id primary-ctrl-caps --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id nvmset -o' = @('normal', 'json', 'binary') + 'id primary-ctrl-caps --output-format' = @('normal', 'json', 'binary') 'id primary-ctrl-caps --output-format-version' = @('1', '2') - 'id primary-ctrl-caps -o' = @('normal', 'json', 'binary', 'tabular') - 'id secondary-ctrl-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id primary-ctrl-caps -o' = @('normal', 'json', 'binary') + 'id secondary-ctrl-list --output-format' = @('normal', 'json', 'binary') 'id secondary-ctrl-list --output-format-version' = @('1', '2') - 'id secondary-ctrl-list -o' = @('normal', 'json', 'binary', 'tabular') - 'id uuid --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id secondary-ctrl-list -o' = @('normal', 'json', 'binary') + 'id uuid --output-format' = @('normal', 'json', 'binary') 'id uuid --output-format-version' = @('1', '2') - 'id uuid -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id uuid -o' = @('normal', 'json', 'binary') + 'id-ctrl --output-format' = @('normal', 'json', 'binary') 'id-ctrl --output-format-version' = @('1', '2') - 'id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'id-domain --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-ctrl -o' = @('normal', 'json', 'binary') + 'id-domain --output-format' = @('normal', 'json', 'binary') 'id-domain --output-format-version' = @('1', '2') - 'id-domain -o' = @('normal', 'json', 'binary', 'tabular') - 'id-iocs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-domain -o' = @('normal', 'json', 'binary') + 'id-iocs --output-format' = @('normal', 'json', 'binary') 'id-iocs --output-format-version' = @('1', '2') - 'id-iocs -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-iocs -o' = @('normal', 'json', 'binary') + 'id-ns --output-format' = @('normal', 'json', 'binary') 'id-ns --output-format-version' = @('1', '2') - 'id-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns-granularity --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-ns -o' = @('normal', 'json', 'binary') + 'id-ns-granularity --output-format' = @('normal', 'json', 'binary') 'id-ns-granularity --output-format-version' = @('1', '2') - 'id-ns-granularity -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-ns-granularity -o' = @('normal', 'json', 'binary') + 'id-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id-ns-lba-format --output-format-version' = @('1', '2') - 'id-ns-lba-format -o' = @('normal', 'json', 'binary', 'tabular') - 'id-nvmset --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-ns-lba-format -o' = @('normal', 'json', 'binary') + 'id-nvmset --output-format' = @('normal', 'json', 'binary') 'id-nvmset --output-format-version' = @('1', '2') - 'id-nvmset -o' = @('normal', 'json', 'binary', 'tabular') - 'id-uuid --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-nvmset -o' = @('normal', 'json', 'binary') + 'id-uuid --output-format' = @('normal', 'json', 'binary') 'id-uuid --output-format-version' = @('1', '2') - 'id-uuid -o' = @('normal', 'json', 'binary', 'tabular') - 'innogrit get-cdump --output-format' = @('normal', 'json', 'binary', 'tabular') + 'id-uuid -o' = @('normal', 'json', 'binary') + 'innogrit get-cdump --output-format' = @('normal', 'json', 'binary') 'innogrit get-cdump --output-format-version' = @('1', '2') - 'innogrit get-cdump -o' = @('normal', 'json', 'binary', 'tabular') - 'innogrit get-eventlog --output-format' = @('normal', 'json', 'binary', 'tabular') + 'innogrit get-cdump -o' = @('normal', 'json', 'binary') + 'innogrit get-eventlog --output-format' = @('normal', 'json', 'binary') 'innogrit get-eventlog --output-format-version' = @('1', '2') - 'innogrit get-eventlog -o' = @('normal', 'json', 'binary', 'tabular') - 'inspur nvme-vendor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'innogrit get-eventlog -o' = @('normal', 'json', 'binary') + 'inspur nvme-vendor-log --output-format' = @('normal', 'json', 'binary') 'inspur nvme-vendor-log --output-format-version' = @('1', '2') - 'inspur nvme-vendor-log -o' = @('normal', 'json', 'binary', 'tabular') - 'intel id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'inspur nvme-vendor-log -o' = @('normal', 'json', 'binary') + 'intel id-ctrl --output-format' = @('normal', 'json', 'binary') 'intel id-ctrl --output-format-version' = @('1', '2') - 'intel id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'intel internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel id-ctrl -o' = @('normal', 'json', 'binary') + 'intel internal-log --output-format' = @('normal', 'json', 'binary') 'intel internal-log --output-format-version' = @('1', '2') - 'intel internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'intel lat-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel internal-log -o' = @('normal', 'json', 'binary') + 'intel lat-stats --output-format' = @('normal', 'json', 'binary') 'intel lat-stats --output-format-version' = @('1', '2') - 'intel lat-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'intel market-name --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel lat-stats -o' = @('normal', 'json', 'binary') + 'intel lat-stats-tracking --output-format' = @('normal', 'json', 'binary') + 'intel lat-stats-tracking --output-format-version' = @('1', '2') + 'intel lat-stats-tracking -o' = @('normal', 'json', 'binary') + 'intel market-name --output-format' = @('normal', 'json', 'binary') 'intel market-name --output-format-version' = @('1', '2') - 'intel market-name -o' = @('normal', 'json', 'binary', 'tabular') - 'intel set-bucket-thresholds --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel market-name -o' = @('normal', 'json', 'binary') + 'intel set-bucket-thresholds --output-format' = @('normal', 'json', 'binary') 'intel set-bucket-thresholds --output-format-version' = @('1', '2') - 'intel set-bucket-thresholds -o' = @('normal', 'json', 'binary', 'tabular') - 'intel smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel set-bucket-thresholds -o' = @('normal', 'json', 'binary') + 'intel smart-log-add --output-format' = @('normal', 'json', 'binary') 'intel smart-log-add --output-format-version' = @('1', '2') - 'intel smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'intel temp-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel smart-log-add -o' = @('normal', 'json', 'binary') + 'intel temp-stats --output-format' = @('normal', 'json', 'binary') 'intel temp-stats --output-format-version' = @('1', '2') - 'intel temp-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'intel temp-stats -o' = @('normal', 'json', 'binary') + 'io-mgmt recv --output-format' = @('normal', 'json', 'binary') 'io-mgmt recv --output-format-version' = @('1', '2') - 'io-mgmt recv -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'io-mgmt recv -o' = @('normal', 'json', 'binary') + 'io-mgmt send --output-format' = @('normal', 'json', 'binary') 'io-mgmt send --output-format-version' = @('1', '2') - 'io-mgmt send -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'io-mgmt send -o' = @('normal', 'json', 'binary') + 'io-mgmt-recv --output-format' = @('normal', 'json', 'binary') 'io-mgmt-recv --output-format-version' = @('1', '2') - 'io-mgmt-recv -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'io-mgmt-recv -o' = @('normal', 'json', 'binary') + 'io-mgmt-send --output-format' = @('normal', 'json', 'binary') 'io-mgmt-send --output-format-version' = @('1', '2') - 'io-mgmt-send -o' = @('normal', 'json', 'binary', 'tabular') - 'io-passthru --output-format' = @('normal', 'json', 'binary', 'tabular') + 'io-mgmt-send -o' = @('normal', 'json', 'binary') + 'io-passthru --output-format' = @('normal', 'json', 'binary') 'io-passthru --output-format-version' = @('1', '2') - 'io-passthru -o' = @('normal', 'json', 'binary', 'tabular') - 'keys check-kxchap-secret --output-format' = @('normal', 'json', 'binary', 'tabular') + 'io-passthru -o' = @('normal', 'json', 'binary') + 'keys check-kxchap-secret --output-format' = @('normal', 'json', 'binary') 'keys check-kxchap-secret --output-format-version' = @('1', '2') - 'keys check-kxchap-secret -o' = @('normal', 'json', 'binary', 'tabular') - 'keys check-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys check-kxchap-secret -o' = @('normal', 'json', 'binary') + 'keys check-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys check-tls-psk --output-format-version' = @('1', '2') - 'keys check-tls-psk -o' = @('normal', 'json', 'binary', 'tabular') - 'keys export --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys check-tls-psk -o' = @('normal', 'json', 'binary') + 'keys export --output-format' = @('normal', 'json', 'binary') 'keys export --output-format-version' = @('1', '2') - 'keys export -o' = @('normal', 'json', 'binary', 'tabular') - 'keys gen-kxchap-secret --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys export -o' = @('normal', 'json', 'binary') + 'keys gen-kxchap-secret --output-format' = @('normal', 'json', 'binary') 'keys gen-kxchap-secret --output-format-version' = @('1', '2') - 'keys gen-kxchap-secret -o' = @('normal', 'json', 'binary', 'tabular') - 'keys gen-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys gen-kxchap-secret -o' = @('normal', 'json', 'binary') + 'keys gen-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys gen-tls-psk --output-format-version' = @('1', '2') - 'keys gen-tls-psk -o' = @('normal', 'json', 'binary', 'tabular') - 'keys import --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys gen-tls-psk -o' = @('normal', 'json', 'binary') + 'keys import --output-format' = @('normal', 'json', 'binary') 'keys import --output-format-version' = @('1', '2') - 'keys import -o' = @('normal', 'json', 'binary', 'tabular') - 'keys insert-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys import -o' = @('normal', 'json', 'binary') + 'keys insert-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys insert-tls-psk --output-format-version' = @('1', '2') - 'keys insert-tls-psk -o' = @('normal', 'json', 'binary', 'tabular') - 'keys revoke --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys insert-tls-psk -o' = @('normal', 'json', 'binary') + 'keys revoke --output-format' = @('normal', 'json', 'binary') 'keys revoke --output-format-version' = @('1', '2') - 'keys revoke -o' = @('normal', 'json', 'binary', 'tabular') - 'lba-status-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'keys revoke -o' = @('normal', 'json', 'binary') + 'lba-status-log --output-format' = @('normal', 'json', 'binary') 'lba-status-log --output-format-version' = @('1', '2') - 'lba-status-log -o' = @('normal', 'json', 'binary', 'tabular') - 'list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lba-status-log -o' = @('normal', 'json', 'binary') + 'list --output-format' = @('normal', 'json') 'list --output-format-version' = @('1', '2') - 'list -o' = @('normal', 'json', 'binary', 'tabular') - 'list-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list -o' = @('normal', 'json') + 'list-ctrl --output-format' = @('normal', 'json') 'list-ctrl --output-format-version' = @('1', '2') - 'list-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'list-endgrp --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list-ctrl -o' = @('normal', 'json') + 'list-endgrp --output-format' = @('normal', 'json') 'list-endgrp --output-format-version' = @('1', '2') - 'list-endgrp -o' = @('normal', 'json', 'binary', 'tabular') - 'list-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list-endgrp -o' = @('normal', 'json') + 'list-ns --output-format' = @('normal', 'json') 'list-ns --output-format-version' = @('1', '2') - 'list-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'list-secondary --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list-ns -o' = @('normal', 'json') + 'list-secondary --output-format' = @('normal', 'json', 'binary') 'list-secondary --output-format-version' = @('1', '2') - 'list-secondary -o' = @('normal', 'json', 'binary', 'tabular') - 'list-subsys --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list-secondary -o' = @('normal', 'json', 'binary') + 'list-subsys --output-format' = @('normal', 'json') 'list-subsys --output-format-version' = @('1', '2') - 'list-subsys -o' = @('normal', 'json', 'binary', 'tabular') - 'lm create-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + 'list-subsys -o' = @('normal', 'json') + 'lm create-cdq --output-format' = @('normal', 'json', 'binary') 'lm create-cdq --output-format-version' = @('1', '2') - 'lm create-cdq -o' = @('normal', 'json', 'binary', 'tabular') - 'lm delete-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm create-cdq -o' = @('normal', 'json', 'binary') + 'lm delete-cdq --output-format' = @('normal', 'json', 'binary') 'lm delete-cdq --output-format-version' = @('1', '2') - 'lm delete-cdq -o' = @('normal', 'json', 'binary', 'tabular') - 'lm get-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm delete-cdq -o' = @('normal', 'json', 'binary') + 'lm get-cdq --output-format' = @('normal', 'json', 'binary') 'lm get-cdq --output-format-version' = @('1', '2') - 'lm get-cdq -o' = @('normal', 'json', 'binary', 'tabular') - 'lm migration-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm get-cdq -o' = @('normal', 'json', 'binary') + 'lm migration-recv --output-format' = @('normal', 'json', 'binary') 'lm migration-recv --output-format-version' = @('1', '2') 'lm migration-recv --sel' = @('0', '1', '2', '3') - 'lm migration-recv -o' = @('normal', 'json', 'binary', 'tabular') + 'lm migration-recv -o' = @('normal', 'json', 'binary') 'lm migration-recv -s' = @('0', '1', '2', '3') - 'lm migration-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm migration-send --output-format' = @('normal', 'json', 'binary') 'lm migration-send --output-format-version' = @('1', '2') 'lm migration-send --sel' = @('0', '1', '2', '3') - 'lm migration-send -o' = @('normal', 'json', 'binary', 'tabular') + 'lm migration-send -o' = @('normal', 'json', 'binary') 'lm migration-send -s' = @('0', '1', '2', '3') - 'lm set-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm set-cdq --output-format' = @('normal', 'json', 'binary') 'lm set-cdq --output-format-version' = @('1', '2') - 'lm set-cdq -o' = @('normal', 'json', 'binary', 'tabular') - 'lm track-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm set-cdq -o' = @('normal', 'json', 'binary') + 'lm track-send --output-format' = @('normal', 'json', 'binary') 'lm track-send --output-format-version' = @('1', '2') 'lm track-send --sel' = @('0', '1', '2', '3') - 'lm track-send -o' = @('normal', 'json', 'binary', 'tabular') + 'lm track-send -o' = @('normal', 'json', 'binary') 'lm track-send -s' = @('0', '1', '2', '3') - 'lockdown --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lockdown --output-format' = @('normal', 'json', 'binary') 'lockdown --output-format-version' = @('1', '2') - 'lockdown -o' = @('normal', 'json', 'binary', 'tabular') - 'log ana --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lockdown -o' = @('normal', 'json', 'binary') + 'log ana --output-format' = @('normal', 'json', 'binary') 'log ana --output-format-version' = @('1', '2') - 'log ana -o' = @('normal', 'json', 'binary', 'tabular') - 'log ave-discovery --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log ana -o' = @('normal', 'json', 'binary') + 'log ave-discovery --output-format' = @('normal', 'json', 'binary') 'log ave-discovery --output-format-version' = @('1', '2') - 'log ave-discovery -o' = @('normal', 'json', 'binary', 'tabular') - 'log boot-part --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log ave-discovery -o' = @('normal', 'json', 'binary') + 'log boot-part --output-format' = @('normal', 'json', 'binary') 'log boot-part --output-format-version' = @('1', '2') - 'log boot-part -o' = @('normal', 'json', 'binary', 'tabular') - 'log changed-alloc-ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log boot-part -o' = @('normal', 'json', 'binary') + 'log changed-alloc-ns-list --output-format' = @('normal', 'json', 'binary') 'log changed-alloc-ns-list --output-format-version' = @('1', '2') - 'log changed-alloc-ns-list -o' = @('normal', 'json', 'binary', 'tabular') - 'log changed-ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log changed-alloc-ns-list -o' = @('normal', 'json', 'binary') + 'log changed-ns-list --output-format' = @('normal', 'json', 'binary') 'log changed-ns-list --output-format-version' = @('1', '2') - 'log changed-ns-list -o' = @('normal', 'json', 'binary', 'tabular') - 'log dispersed-ns-participating-nss --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log changed-ns-list -o' = @('normal', 'json', 'binary') + 'log dispersed-ns-participating-nss --output-format' = @('normal', 'json', 'binary') 'log dispersed-ns-participating-nss --output-format-version' = @('1', '2') - 'log dispersed-ns-participating-nss -o' = @('normal', 'json', 'binary', 'tabular') - 'log effects --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log dispersed-ns-participating-nss -o' = @('normal', 'json', 'binary') + 'log effects --output-format' = @('normal', 'json', 'binary') 'log effects --output-format-version' = @('1', '2') - 'log effects -o' = @('normal', 'json', 'binary', 'tabular') - 'log endurance --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log effects -o' = @('normal', 'json', 'binary') + 'log endurance --output-format' = @('normal', 'json', 'binary') 'log endurance --output-format-version' = @('1', '2') - 'log endurance -o' = @('normal', 'json', 'binary', 'tabular') - 'log endurance-event-agg --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log endurance -o' = @('normal', 'json', 'binary') + 'log endurance-event-agg --output-format' = @('normal', 'json', 'binary') 'log endurance-event-agg --output-format-version' = @('1', '2') - 'log endurance-event-agg -o' = @('normal', 'json', 'binary', 'tabular') - 'log error --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log endurance-event-agg -o' = @('normal', 'json', 'binary') + 'log error --output-format' = @('normal', 'json', 'binary') 'log error --output-format-version' = @('1', '2') - 'log error -o' = @('normal', 'json', 'binary', 'tabular') - 'log fid-support-effects --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log error -o' = @('normal', 'json', 'binary') + 'log fid-support-effects --output-format' = @('normal', 'json', 'binary') 'log fid-support-effects --output-format-version' = @('1', '2') - 'log fid-support-effects -o' = @('normal', 'json', 'binary', 'tabular') - 'log fw --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log fid-support-effects -o' = @('normal', 'json', 'binary') + 'log fw --output-format' = @('normal', 'json', 'binary') 'log fw --output-format-version' = @('1', '2') - 'log fw -o' = @('normal', 'json', 'binary', 'tabular') - 'log host-discovery --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log fw -o' = @('normal', 'json', 'binary') + 'log host-discovery --output-format' = @('normal', 'json', 'binary') 'log host-discovery --output-format-version' = @('1', '2') - 'log host-discovery -o' = @('normal', 'json', 'binary', 'tabular') - 'log lba-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log host-discovery -o' = @('normal', 'json', 'binary') + 'log lba-status --output-format' = @('normal', 'json', 'binary') 'log lba-status --output-format-version' = @('1', '2') - 'log lba-status -o' = @('normal', 'json', 'binary', 'tabular') - 'log media-unit-stat --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log lba-status -o' = @('normal', 'json', 'binary') + 'log media-unit-stat --output-format' = @('normal', 'json', 'binary') 'log media-unit-stat --output-format-version' = @('1', '2') - 'log media-unit-stat -o' = @('normal', 'json', 'binary', 'tabular') - 'log mgmt-addr-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log media-unit-stat -o' = @('normal', 'json', 'binary') + 'log mgmt-addr-list --output-format' = @('normal', 'json', 'binary') 'log mgmt-addr-list --output-format-version' = @('1', '2') - 'log mgmt-addr-list -o' = @('normal', 'json', 'binary', 'tabular') - 'log mi-cmd-support-effects --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log mgmt-addr-list -o' = @('normal', 'json', 'binary') + 'log mi-cmd-support-effects --output-format' = @('normal', 'json', 'binary') 'log mi-cmd-support-effects --output-format-version' = @('1', '2') - 'log mi-cmd-support-effects -o' = @('normal', 'json', 'binary', 'tabular') - 'log persistent-event --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log mi-cmd-support-effects -o' = @('normal', 'json', 'binary') + 'log persistent-event --output-format' = @('normal', 'json', 'binary') 'log persistent-event --output-format-version' = @('1', '2') - 'log persistent-event -o' = @('normal', 'json', 'binary', 'tabular') - 'log phy-rx-eom --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log persistent-event -o' = @('normal', 'json', 'binary') + 'log phy-rx-eom --output-format' = @('normal', 'json', 'binary') 'log phy-rx-eom --output-format-version' = @('1', '2') - 'log phy-rx-eom -o' = @('normal', 'json', 'binary', 'tabular') - 'log power-measurement --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log phy-rx-eom -o' = @('normal', 'json', 'binary') + 'log power-measurement --output-format' = @('normal', 'json', 'binary') 'log power-measurement --output-format-version' = @('1', '2') - 'log power-measurement -o' = @('normal', 'json', 'binary', 'tabular') - 'log pred-lat-event-agg --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log power-measurement -o' = @('normal', 'json', 'binary') + 'log pred-lat-event-agg --output-format' = @('normal', 'json', 'binary') 'log pred-lat-event-agg --output-format-version' = @('1', '2') - 'log pred-lat-event-agg -o' = @('normal', 'json', 'binary', 'tabular') - 'log predictable-lat --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log pred-lat-event-agg -o' = @('normal', 'json', 'binary') + 'log predictable-lat --output-format' = @('normal', 'json', 'binary') 'log predictable-lat --output-format-version' = @('1', '2') - 'log predictable-lat -o' = @('normal', 'json', 'binary', 'tabular') - 'log pull-model-ddc-req --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log predictable-lat -o' = @('normal', 'json', 'binary') + 'log pull-model-ddc-req --output-format' = @('normal', 'json', 'binary') 'log pull-model-ddc-req --output-format-version' = @('1', '2') - 'log pull-model-ddc-req -o' = @('normal', 'json', 'binary', 'tabular') - 'log reachability-associations --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log pull-model-ddc-req -o' = @('normal', 'json', 'binary') + 'log reachability-associations --output-format' = @('normal', 'json', 'binary') 'log reachability-associations --output-format-version' = @('1', '2') - 'log reachability-associations -o' = @('normal', 'json', 'binary', 'tabular') - 'log reachability-groups --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log reachability-associations -o' = @('normal', 'json', 'binary') + 'log reachability-groups --output-format' = @('normal', 'json', 'binary') 'log reachability-groups --output-format-version' = @('1', '2') - 'log reachability-groups -o' = @('normal', 'json', 'binary', 'tabular') - 'log resv-notif --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log reachability-groups -o' = @('normal', 'json', 'binary') + 'log resv-notif --output-format' = @('normal', 'json', 'binary') 'log resv-notif --output-format-version' = @('1', '2') - 'log resv-notif -o' = @('normal', 'json', 'binary', 'tabular') - 'log rotational-media-info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log resv-notif -o' = @('normal', 'json', 'binary') + 'log rotational-media-info --output-format' = @('normal', 'json', 'binary') 'log rotational-media-info --output-format-version' = @('1', '2') - 'log rotational-media-info -o' = @('normal', 'json', 'binary', 'tabular') - 'log sanitize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log rotational-media-info -o' = @('normal', 'json', 'binary') + 'log sanitize --output-format' = @('normal', 'json', 'binary') 'log sanitize --output-format-version' = @('1', '2') - 'log sanitize -o' = @('normal', 'json', 'binary', 'tabular') - 'log self-test --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log sanitize -o' = @('normal', 'json', 'binary') + 'log self-test --output-format' = @('normal', 'json', 'binary') 'log self-test --output-format-version' = @('1', '2') - 'log self-test -o' = @('normal', 'json', 'binary', 'tabular') - 'log smart --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log self-test -o' = @('normal', 'json', 'binary') + 'log smart --output-format' = @('normal', 'json', 'binary') 'log smart --output-format-version' = @('1', '2') - 'log smart -o' = @('normal', 'json', 'binary', 'tabular') - 'log supported-cap-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log smart -o' = @('normal', 'json', 'binary') + 'log supported-cap-config --output-format' = @('normal', 'json', 'binary') 'log supported-cap-config --output-format-version' = @('1', '2') - 'log supported-cap-config -o' = @('normal', 'json', 'binary', 'tabular') - 'log supported-pages --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log supported-cap-config -o' = @('normal', 'json', 'binary') + 'log supported-pages --output-format' = @('normal', 'json', 'binary') 'log supported-pages --output-format-version' = @('1', '2') - 'log supported-pages -o' = @('normal', 'json', 'binary', 'tabular') - 'log telemetry --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log supported-pages -o' = @('normal', 'json', 'binary') + 'log telemetry --output-format' = @('normal', 'json', 'binary') 'log telemetry --output-format-version' = @('1', '2') - 'log telemetry -o' = @('normal', 'json', 'binary', 'tabular') - 'mangoboost id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'log telemetry -o' = @('normal', 'json', 'binary') + 'mangoboost id-ctrl --output-format' = @('normal', 'json', 'binary') 'mangoboost id-ctrl --output-format-version' = @('1', '2') - 'mangoboost id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'media-unit-stat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'mangoboost id-ctrl -o' = @('normal', 'json', 'binary') + 'media-unit-stat-log --output-format' = @('normal', 'json', 'binary') 'media-unit-stat-log --output-format-version' = @('1', '2') - 'media-unit-stat-log -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze clear-error-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'media-unit-stat-log -o' = @('normal', 'json', 'binary') + 'memblaze clear-error-log --output-format' = @('normal', 'json', 'binary') 'memblaze clear-error-log --output-format-version' = @('1', '2') - 'memblaze clear-error-log -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze get-pm-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze clear-error-log -o' = @('normal', 'json', 'binary') + 'memblaze get-pm-status --output-format' = @('normal', 'json', 'binary') 'memblaze get-pm-status --output-format-version' = @('1', '2') - 'memblaze get-pm-status -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-get-feature-x --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze get-pm-status -o' = @('normal', 'json', 'binary') + 'memblaze lat-get-feature-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-get-feature-x --output-format-version' = @('1', '2') - 'memblaze lat-get-feature-x -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-get-feature-x -o' = @('normal', 'json', 'binary') + 'memblaze lat-log --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log --output-format-version' = @('1', '2') - 'memblaze lat-log -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log-print --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-log -o' = @('normal', 'json', 'binary') + 'memblaze lat-log-print --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log-print --output-format-version' = @('1', '2') - 'memblaze lat-log-print -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-log-print -o' = @('normal', 'json', 'binary') + 'memblaze lat-log-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log-print-x --output-format-version' = @('1', '2') - 'memblaze lat-log-print-x -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-set-feature-x --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-log-print-x -o' = @('normal', 'json', 'binary') + 'memblaze lat-set-feature-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-set-feature-x --output-format-version' = @('1', '2') - 'memblaze lat-set-feature-x -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-stats-print --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-set-feature-x -o' = @('normal', 'json', 'binary') + 'memblaze lat-stats --output-format' = @('normal', 'json', 'binary') + 'memblaze lat-stats --output-format-version' = @('1', '2') + 'memblaze lat-stats -o' = @('normal', 'json', 'binary') + 'memblaze lat-stats-print --output-format' = @('normal', 'json', 'binary') 'memblaze lat-stats-print --output-format-version' = @('1', '2') - 'memblaze lat-stats-print -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-stats-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-stats-print -o' = @('normal', 'json', 'binary') + 'memblaze lat-stats-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-stats-print-x --output-format-version' = @('1', '2') - 'memblaze lat-stats-print-x -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze perf-stats-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze lat-stats-print-x -o' = @('normal', 'json', 'binary') + 'memblaze perf-stats-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze perf-stats-print-x --output-format-version' = @('1', '2') - 'memblaze perf-stats-print-x -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze select-download --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze perf-stats-print-x -o' = @('normal', 'json', 'binary') + 'memblaze select-download --output-format' = @('normal', 'json', 'binary') 'memblaze select-download --output-format-version' = @('1', '2') - 'memblaze select-download -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze set-pm-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze select-download -o' = @('normal', 'json', 'binary') + 'memblaze set-pm-status --output-format' = @('normal', 'json', 'binary') 'memblaze set-pm-status --output-format-version' = @('1', '2') - 'memblaze set-pm-status -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze set-pm-status -o' = @('normal', 'json', 'binary') + 'memblaze smart-log-add --output-format' = @('normal', 'json', 'binary') 'memblaze smart-log-add --output-format-version' = @('1', '2') - 'memblaze smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'mgmt-addr-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'memblaze smart-log-add -o' = @('normal', 'json', 'binary') + 'memblaze smart-log-add-x --output-format' = @('normal', 'json', 'binary') + 'memblaze smart-log-add-x --output-format-version' = @('1', '2') + 'memblaze smart-log-add-x -o' = @('normal', 'json', 'binary') + 'mgmt-addr-list-log --output-format' = @('normal', 'json', 'binary') 'mgmt-addr-list-log --output-format-version' = @('1', '2') - 'mgmt-addr-list-log -o' = @('normal', 'json', 'binary', 'tabular') - 'mi-cmd-support-effects-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'mgmt-addr-list-log -o' = @('normal', 'json', 'binary') + 'mi-cmd-support-effects-log --output-format' = @('normal', 'json', 'binary') 'mi-cmd-support-effects-log --output-format-version' = @('1', '2') - 'mi-cmd-support-effects-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'mi-cmd-support-effects-log -o' = @('normal', 'json', 'binary') + 'micron clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'micron clear-fw-activate-history --output-format-version' = @('1', '2') - 'micron clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'micron clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'micron clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'micron clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'micron clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'micron cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'micron cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'micron cloud-SSD-plugin-version --output-format-version' = @('1', '2') - 'micron cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'micron cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'micron cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'micron cloud-boot-SSD-version --output-format-version' = @('1', '2') - 'micron cloud-boot-SSD-version -o' = @('normal', 'json', 'binary', 'tabular') - 'micron id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') + 'micron id-ctrl --output-format' = @('normal', 'json', 'binary') 'micron id-ctrl --output-format-version' = @('1', '2') - 'micron id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-logs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron id-ctrl -o' = @('normal', 'json', 'binary') + 'micron latency-logs --output-format' = @('normal', 'json', 'binary') 'micron latency-logs --output-format-version' = @('1', '2') - 'micron latency-logs -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron latency-logs -o' = @('normal', 'json', 'binary') + 'micron latency-stats --output-format' = @('normal', 'json', 'binary') 'micron latency-stats --output-format-version' = @('1', '2') - 'micron latency-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-tracking --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron latency-stats -o' = @('normal', 'json', 'binary') + 'micron latency-tracking --output-format' = @('normal', 'json', 'binary') 'micron latency-tracking --output-format-version' = @('1', '2') - 'micron latency-tracking -o' = @('normal', 'json', 'binary', 'tabular') - 'micron log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron latency-tracking -o' = @('normal', 'json', 'binary') + 'micron log-page-directory --output-format' = @('normal', 'json', 'binary') 'micron log-page-directory --output-format-version' = @('1', '2') - 'micron log-page-directory -o' = @('normal', 'json', 'binary', 'tabular') - 'micron plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron log-page-directory -o' = @('normal', 'json', 'binary') + 'micron plugin-version --output-format' = @('normal', 'json', 'binary') 'micron plugin-version --output-format-version' = @('1', '2') - 'micron plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'micron select-download --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron plugin-version -o' = @('normal', 'json', 'binary') + 'micron select-download --output-format' = @('normal', 'json', 'binary') 'micron select-download --output-format-version' = @('1', '2') - 'micron select-download -o' = @('normal', 'json', 'binary', 'tabular') - 'micron smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron select-download -o' = @('normal', 'json', 'binary') + 'micron smart-log --output-format' = @('normal', 'json') 'micron smart-log --output-format-version' = @('1', '2') - 'micron smart-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron smart-log -o' = @('normal', 'json') + 'micron vs-cloud-log --output-format' = @('normal', 'json') 'micron vs-cloud-log --output-format-version' = @('1', '2') - 'micron vs-cloud-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-cloud-log -o' = @('normal', 'json') + 'micron vs-device-waf --output-format' = @('normal', 'json', 'binary') 'micron vs-device-waf --output-format-version' = @('1', '2') - 'micron vs-device-waf -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-device-waf -o' = @('normal', 'json', 'binary') + 'micron vs-drive-info --output-format' = @('normal', 'json') 'micron vs-drive-info --output-format-version' = @('1', '2') - 'micron vs-drive-info -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-drive-info -o' = @('normal', 'json') + 'micron vs-fw-activate-history --output-format' = @('normal', 'json') 'micron vs-fw-activate-history --output-format-version' = @('1', '2') - 'micron vs-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-fw-activate-history -o' = @('normal', 'json') + 'micron vs-internal-log --output-format' = @('normal', 'json', 'binary') 'micron vs-internal-log --output-format-version' = @('1', '2') - 'micron vs-internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-internal-log -o' = @('normal', 'json', 'binary') + 'micron vs-nand-stats --output-format' = @('normal', 'json') 'micron vs-nand-stats --output-format-version' = @('1', '2') - 'micron vs-nand-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-nand-stats -o' = @('normal', 'json') + 'micron vs-pcie-stats --output-format' = @('normal', 'json') 'micron vs-pcie-stats --output-format-version' = @('1', '2') - 'micron vs-pcie-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-pcie-stats -o' = @('normal', 'json') + 'micron vs-smart-add-log --output-format' = @('normal', 'json') 'micron vs-smart-add-log --output-format-version' = @('1', '2') - 'micron vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smart-ext-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-smart-add-log -o' = @('normal', 'json') + 'micron vs-smart-ext-log --output-format' = @('normal', 'json') 'micron vs-smart-ext-log --output-format-version' = @('1', '2') - 'micron vs-smart-ext-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smbus-option --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-smart-ext-log -o' = @('normal', 'json') + 'micron vs-smbus-option --output-format' = @('normal', 'json', 'binary') 'micron vs-smbus-option --output-format-version' = @('1', '2') - 'micron vs-smbus-option -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-smbus-option -o' = @('normal', 'json', 'binary') + 'micron vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'micron vs-telemetry-controller-option --output-format-version' = @('1', '2') - 'micron vs-telemetry-controller-option -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') + 'micron vs-temperature-stats --output-format' = @('normal', 'json') 'micron vs-temperature-stats --output-format-version' = @('1', '2') - 'micron vs-temperature-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-vendor-telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-temperature-stats -o' = @('normal', 'json') + 'micron vs-vendor-telemetry-log --output-format' = @('normal', 'json') 'micron vs-vendor-telemetry-log --output-format-version' = @('1', '2') - 'micron vs-vendor-telemetry-log -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-work-load-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-vendor-telemetry-log -o' = @('normal', 'json') + 'micron vs-work-load-log --output-format' = @('normal', 'json') 'micron vs-work-load-log --output-format-version' = @('1', '2') - 'micron vs-work-load-log -o' = @('normal', 'json', 'binary', 'tabular') - 'nbft show --output-format' = @('normal', 'json', 'binary', 'tabular') + 'micron vs-work-load-log -o' = @('normal', 'json') + 'nbft show --output-format' = @('normal', 'json', 'binary') 'nbft show --output-format-version' = @('1', '2') - 'nbft show -o' = @('normal', 'json', 'binary', 'tabular') - 'netapp ontapdevices --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nbft show -o' = @('normal', 'json', 'binary') + 'netapp ontapdevices --output-format' = @('normal', 'column', 'json') 'netapp ontapdevices --output-format-version' = @('1', '2') - 'netapp ontapdevices -o' = @('normal', 'json', 'binary', 'tabular') - 'netapp smdevices --output-format' = @('normal', 'json', 'binary', 'tabular') + 'netapp ontapdevices -o' = @('normal', 'column', 'json') + 'netapp smdevices --output-format' = @('normal', 'column', 'json') 'netapp smdevices --output-format-version' = @('1', '2') - 'netapp smdevices -o' = @('normal', 'json', 'binary', 'tabular') - 'ns attach --output-format' = @('normal', 'json', 'binary', 'tabular') + 'netapp smdevices -o' = @('normal', 'column', 'json') + 'ns attach --output-format' = @('normal', 'json', 'binary') 'ns attach --output-format-version' = @('1', '2') - 'ns attach -o' = @('normal', 'json', 'binary', 'tabular') - 'ns create --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns attach -o' = @('normal', 'json', 'binary') + 'ns create --output-format' = @('normal', 'json', 'binary') 'ns create --output-format-version' = @('1', '2') - 'ns create -o' = @('normal', 'json', 'binary', 'tabular') - 'ns delete --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns create -o' = @('normal', 'json', 'binary') + 'ns delete --output-format' = @('normal', 'json', 'binary') 'ns delete --output-format-version' = @('1', '2') - 'ns delete -o' = @('normal', 'json', 'binary', 'tabular') - 'ns detach --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns delete -o' = @('normal', 'json', 'binary') + 'ns detach --output-format' = @('normal', 'json', 'binary') 'ns detach --output-format-version' = @('1', '2') - 'ns detach -o' = @('normal', 'json', 'binary', 'tabular') - 'ns get-id --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns detach -o' = @('normal', 'json', 'binary') + 'ns get-id --output-format' = @('normal', 'json', 'binary') 'ns get-id --output-format-version' = @('1', '2') - 'ns get-id -o' = @('normal', 'json', 'binary', 'tabular') - 'ns-descs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns get-id -o' = @('normal', 'json', 'binary') + 'ns-descs --output-format' = @('normal', 'json', 'binary') 'ns-descs --output-format-version' = @('1', '2') - 'ns-descs -o' = @('normal', 'json', 'binary', 'tabular') - 'ns-rescan --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns-descs -o' = @('normal', 'json', 'binary') + 'ns-rescan --output-format' = @('normal', 'json', 'binary') 'ns-rescan --output-format-version' = @('1', '2') - 'ns-rescan -o' = @('normal', 'json', 'binary', 'tabular') - 'nvidia id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ns-rescan -o' = @('normal', 'json', 'binary') + 'nvidia id-ctrl --output-format' = @('normal', 'json', 'binary') 'nvidia id-ctrl --output-format-version' = @('1', '2') - 'nvidia id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvidia id-ctrl -o' = @('normal', 'json', 'binary') + 'nvm-id-ctrl --output-format' = @('normal', 'json', 'binary') 'nvm-id-ctrl --output-format-version' = @('1', '2') - 'nvm-id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvm-id-ctrl -o' = @('normal', 'json', 'binary') + 'nvm-id-ns --output-format' = @('normal', 'json', 'binary') 'nvm-id-ns --output-format-version' = @('1', '2') - 'nvm-id-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvm-id-ns -o' = @('normal', 'json', 'binary') + 'nvm-id-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'nvm-id-ns-lba-format --output-format-version' = @('1', '2') - 'nvm-id-ns-lba-format -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvm-id-ns-lba-format -o' = @('normal', 'json', 'binary') + 'nvme-mi recv --output-format' = @('normal', 'json', 'binary') 'nvme-mi recv --output-format-version' = @('1', '2') - 'nvme-mi recv -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvme-mi recv -o' = @('normal', 'json', 'binary') + 'nvme-mi send --output-format' = @('normal', 'json', 'binary') 'nvme-mi send --output-format-version' = @('1', '2') - 'nvme-mi send -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvme-mi send -o' = @('normal', 'json', 'binary') + 'nvme-mi-recv --output-format' = @('normal', 'json', 'binary') 'nvme-mi-recv --output-format-version' = @('1', '2') - 'nvme-mi-recv -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvme-mi-recv -o' = @('normal', 'json', 'binary') + 'nvme-mi-send --output-format' = @('normal', 'json', 'binary') 'nvme-mi-send --output-format-version' = @('1', '2') - 'nvme-mi-send -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'nvme-mi-send -o' = @('normal', 'json', 'binary') + 'ocp clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'ocp clear-fw-activate-history --output-format-version' = @('1', '2') - 'ocp clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'ocp clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'ocp clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'ocp clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp device-capability-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'ocp device-capability-log --output-format' = @('normal', 'json', 'binary') 'ocp device-capability-log --output-format-version' = @('1', '2') - 'ocp device-capability-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp eol-plp-failure-mode --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp device-capability-log -o' = @('normal', 'json', 'binary') + 'ocp eol-plp-failure-mode --output-format' = @('normal', 'json', 'binary') 'ocp eol-plp-failure-mode --output-format-version' = @('1', '2') 'ocp eol-plp-failure-mode --sel' = @('0', '1', '2', '3') 'ocp eol-plp-failure-mode -S' = @('0', '1', '2', '3') - 'ocp eol-plp-failure-mode -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp error-recovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp eol-plp-failure-mode -o' = @('normal', 'json', 'binary') + 'ocp error-recovery-log --output-format' = @('normal', 'json', 'binary') 'ocp error-recovery-log --output-format-version' = @('1', '2') - 'ocp error-recovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp error-recovery-log -o' = @('normal', 'json', 'binary') + 'ocp fw-activate-history --output-format' = @('normal', 'json', 'binary') 'ocp fw-activate-history --output-format-version' = @('1', '2') - 'ocp fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp fw-activate-history -o' = @('normal', 'json', 'binary') + 'ocp get-clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'ocp get-clear-pcie-correctable-errors --output-format-version' = @('1', '2') 'ocp get-clear-pcie-correctable-errors --sel' = @('0', '1', '2', '3') - 'ocp get-clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'ocp get-clear-pcie-correctable-errors -s' = @('0', '1', '2', '3') - 'ocp get-dssd-async-event-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-dssd-async-event-config --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-async-event-config --output-format-version' = @('1', '2') 'ocp get-dssd-async-event-config --sel' = @('0', '1', '2', '3') 'ocp get-dssd-async-event-config -S' = @('0', '1', '2', '3') - 'ocp get-dssd-async-event-config -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-dssd-async-event-config -o' = @('normal', 'json', 'binary') + 'ocp get-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-power-state-feature --output-format-version' = @('1', '2') 'ocp get-dssd-power-state-feature --sel' = @('0', '1', '2', '3') 'ocp get-dssd-power-state-feature -S' = @('0', '1', '2', '3') - 'ocp get-dssd-power-state-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-dssd-power-state-feature -o' = @('normal', 'json', 'binary') + 'ocp get-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo --output-format-version' = @('1', '2') 'ocp get-enable-ieee1667-silo --sel' = @('0', '1', '2', '3') - 'ocp get-enable-ieee1667-silo -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-enable-ieee1667-silo -o' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo -s' = @('0', '1', '2', '3') - 'ocp get-error-injection --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-error-injection --output-format' = @('normal', 'json', 'binary') 'ocp get-error-injection --output-format-version' = @('1', '2') 'ocp get-error-injection --sel' = @('0', '1', '2', '3') - 'ocp get-error-injection -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-error-injection -o' = @('normal', 'json', 'binary') 'ocp get-error-injection -s' = @('0', '1', '2', '3') - 'ocp get-idle-wakeup-time --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-idle-wakeup-time --output-format' = @('normal', 'json', 'binary') 'ocp get-idle-wakeup-time --output-format-version' = @('1', '2') 'ocp get-idle-wakeup-time --sel' = @('0', '1', '2', '3') - 'ocp get-idle-wakeup-time -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-idle-wakeup-time -o' = @('normal', 'json', 'binary') 'ocp get-idle-wakeup-time -s' = @('0', '1', '2', '3') - 'ocp get-latency-monitor --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-latency-monitor --output-format' = @('normal', 'json', 'binary') 'ocp get-latency-monitor --output-format-version' = @('1', '2') 'ocp get-latency-monitor --sel' = @('0', '1', '2', '3') - 'ocp get-latency-monitor -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-latency-monitor -o' = @('normal', 'json', 'binary') 'ocp get-latency-monitor -s' = @('0', '1', '2', '3') - 'ocp get-plp-health-check-interval --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-plp-health-check-interval --output-format' = @('normal', 'json', 'binary') 'ocp get-plp-health-check-interval --output-format-version' = @('1', '2') 'ocp get-plp-health-check-interval --sel' = @('0', '1', '2', '3') 'ocp get-plp-health-check-interval -S' = @('0', '1', '2', '3') - 'ocp get-plp-health-check-interval -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-telemetry-profile --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-plp-health-check-interval -o' = @('normal', 'json', 'binary') + 'ocp get-telemetry-profile --output-format' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile --output-format-version' = @('1', '2') 'ocp get-telemetry-profile --sel' = @('0', '1', '2', '3') - 'ocp get-telemetry-profile -o' = @('normal', 'json', 'binary', 'tabular') + 'ocp get-telemetry-profile -o' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile -s' = @('0', '1', '2', '3') 'ocp hardware-component-log --comp-id' = @('asic', 'nand', 'dram', 'pmic', 'pcb', 'cap', 'reg', 'case', 'sn', 'country', 'hw-rev', 'born-on-date', 'vendor') - 'ocp hardware-component-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp hardware-component-log --output-format' = @('normal', 'json', 'binary') 'ocp hardware-component-log --output-format-version' = @('1', '2') 'ocp hardware-component-log -i' = @('asic', 'nand', 'dram', 'pmic', 'pcb', 'cap', 'reg', 'case', 'sn', 'country', 'hw-rev', 'born-on-date', 'vendor') - 'ocp hardware-component-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp hardware-component-log -o' = @('normal', 'json', 'binary') + 'ocp internal-log --output-format' = @('normal', 'json', 'binary') 'ocp internal-log --output-format-version' = @('1', '2') - 'ocp internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp latency-monitor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp internal-log -o' = @('normal', 'json', 'binary') + 'ocp latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'ocp latency-monitor-log --output-format-version' = @('1', '2') - 'ocp latency-monitor-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp persistent-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp latency-monitor-log -o' = @('normal', 'json', 'binary') + 'ocp persistent-event-log --output-format' = @('normal', 'json', 'binary') 'ocp persistent-event-log --output-format-version' = @('1', '2') - 'ocp persistent-event-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-dssd-async-event-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp persistent-event-log -o' = @('normal', 'json', 'binary') + 'ocp set-dssd-async-event-config --output-format' = @('normal', 'json', 'binary') 'ocp set-dssd-async-event-config --output-format-version' = @('1', '2') - 'ocp set-dssd-async-event-config -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-dssd-async-event-config -o' = @('normal', 'json', 'binary') + 'ocp set-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary') 'ocp set-dssd-power-state-feature --output-format-version' = @('1', '2') - 'ocp set-dssd-power-state-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-dssd-power-state-feature -o' = @('normal', 'json', 'binary') + 'ocp set-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary') 'ocp set-enable-ieee1667-silo --output-format-version' = @('1', '2') - 'ocp set-enable-ieee1667-silo -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-error-injection --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-enable-ieee1667-silo -o' = @('normal', 'json', 'binary') + 'ocp set-error-injection --output-format' = @('normal', 'json', 'binary') 'ocp set-error-injection --output-format-version' = @('1', '2') - 'ocp set-error-injection -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-error-injection -o' = @('normal', 'json', 'binary') + 'ocp set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'ocp set-latency-monitor-feature --output-format-version' = @('1', '2') - 'ocp set-latency-monitor-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-plp-health-check-interval --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-latency-monitor-feature -o' = @('normal', 'json', 'binary') + 'ocp set-plp-health-check-interval --output-format' = @('normal', 'json', 'binary') 'ocp set-plp-health-check-interval --output-format-version' = @('1', '2') - 'ocp set-plp-health-check-interval -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-telemetry-profile --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-plp-health-check-interval -o' = @('normal', 'json', 'binary') + 'ocp set-telemetry-profile --output-format' = @('normal', 'json', 'binary') 'ocp set-telemetry-profile --output-format-version' = @('1', '2') - 'ocp set-telemetry-profile -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp set-telemetry-profile -o' = @('normal', 'json', 'binary') + 'ocp smart-add-log --output-format' = @('normal', 'json', 'binary') 'ocp smart-add-log --output-format-version' = @('1', '2') - 'ocp smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp tcg-configuration-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp smart-add-log -o' = @('normal', 'json', 'binary') + 'ocp tcg-configuration-log --output-format' = @('normal', 'json', 'binary') 'ocp tcg-configuration-log --output-format-version' = @('1', '2') - 'ocp tcg-configuration-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp telemetry-string-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp tcg-configuration-log -o' = @('normal', 'json', 'binary') + 'ocp telemetry-string-log --output-format' = @('normal', 'json', 'binary') 'ocp telemetry-string-log --output-format-version' = @('1', '2') - 'ocp telemetry-string-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp telemetry-string-log -o' = @('normal', 'json', 'binary') + 'ocp unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'ocp unsupported-reqs-log --output-format-version' = @('1', '2') - 'ocp unsupported-reqs-log -o' = @('normal', 'json', 'binary', 'tabular') - 'persistent-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ocp unsupported-reqs-log -o' = @('normal', 'json', 'binary') + 'persistent-event-log --output-format' = @('normal', 'json', 'binary') 'persistent-event-log --output-format-version' = @('1', '2') - 'persistent-event-log -o' = @('normal', 'json', 'binary', 'tabular') - 'phy-rx-eom-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'persistent-event-log -o' = @('normal', 'json', 'binary') + 'phy-rx-eom-log --output-format' = @('normal', 'json', 'binary') 'phy-rx-eom-log --output-format-version' = @('1', '2') - 'phy-rx-eom-log -o' = @('normal', 'json', 'binary', 'tabular') - 'power-measurement-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'phy-rx-eom-log -o' = @('normal', 'json', 'binary') + 'power-measurement-log --output-format' = @('normal', 'json', 'binary') 'power-measurement-log --output-format-version' = @('1', '2') - 'power-measurement-log -o' = @('normal', 'json', 'binary', 'tabular') - 'pred-lat-event-agg-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'power-measurement-log -o' = @('normal', 'json', 'binary') + 'pred-lat-event-agg-log --output-format' = @('normal', 'json', 'binary') 'pred-lat-event-agg-log --output-format-version' = @('1', '2') - 'pred-lat-event-agg-log -o' = @('normal', 'json', 'binary', 'tabular') - 'predictable-lat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'pred-lat-event-agg-log -o' = @('normal', 'json', 'binary') + 'predictable-lat-log --output-format' = @('normal', 'json', 'binary') 'predictable-lat-log --output-format-version' = @('1', '2') - 'predictable-lat-log -o' = @('normal', 'json', 'binary', 'tabular') - 'primary-ctrl-caps --output-format' = @('normal', 'json', 'binary', 'tabular') + 'predictable-lat-log -o' = @('normal', 'json', 'binary') + 'primary-ctrl-caps --output-format' = @('normal', 'json', 'binary') 'primary-ctrl-caps --output-format-version' = @('1', '2') - 'primary-ctrl-caps -o' = @('normal', 'json', 'binary', 'tabular') - 'pull-model-ddc-req-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'primary-ctrl-caps -o' = @('normal', 'json', 'binary') + 'pull-model-ddc-req-log --output-format' = @('normal', 'json', 'binary') 'pull-model-ddc-req-log --output-format-version' = @('1', '2') - 'pull-model-ddc-req-log -o' = @('normal', 'json', 'binary', 'tabular') - 'reachability-associations-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'pull-model-ddc-req-log -o' = @('normal', 'json', 'binary') + 'reachability-associations-log --output-format' = @('normal', 'json', 'binary') 'reachability-associations-log --output-format-version' = @('1', '2') - 'reachability-associations-log -o' = @('normal', 'json', 'binary', 'tabular') - 'reachability-groups-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'reachability-associations-log -o' = @('normal', 'json', 'binary') + 'reachability-groups-log --output-format' = @('normal', 'json', 'binary') 'reachability-groups-log --output-format-version' = @('1', '2') - 'reachability-groups-log -o' = @('normal', 'json', 'binary', 'tabular') - 'read --output-format' = @('normal', 'json', 'binary', 'tabular') + 'reachability-groups-log -o' = @('normal', 'json', 'binary') + 'read --output-format' = @('normal', 'json', 'binary') 'read --output-format-version' = @('1', '2') - 'read -o' = @('normal', 'json', 'binary', 'tabular') - 'registry delete --output-format' = @('normal', 'json', 'binary', 'tabular') + 'read -o' = @('normal', 'json', 'binary') + 'registry delete --output-format' = @('normal', 'json', 'binary') 'registry delete --output-format-version' = @('1', '2') - 'registry delete -o' = @('normal', 'json', 'binary', 'tabular') - 'registry list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'registry delete -o' = @('normal', 'json', 'binary') + 'registry list --output-format' = @('normal', 'json', 'binary') 'registry list --output-format-version' = @('1', '2') - 'registry list -o' = @('normal', 'json', 'binary', 'tabular') - 'registry retrieve --output-format' = @('normal', 'json', 'binary', 'tabular') + 'registry list -o' = @('normal', 'json', 'binary') + 'registry retrieve --output-format' = @('normal', 'json', 'binary') 'registry retrieve --output-format-version' = @('1', '2') - 'registry retrieve -o' = @('normal', 'json', 'binary', 'tabular') - 'registry update --output-format' = @('normal', 'json', 'binary', 'tabular') + 'registry retrieve -o' = @('normal', 'json', 'binary') + 'registry update --output-format' = @('normal', 'json', 'binary') 'registry update --output-format-version' = @('1', '2') - 'registry update -o' = @('normal', 'json', 'binary', 'tabular') - 'reset --output-format' = @('normal', 'json', 'binary', 'tabular') + 'registry update -o' = @('normal', 'json', 'binary') + 'reset --output-format' = @('normal', 'json', 'binary') 'reset --output-format-version' = @('1', '2') - 'reset -o' = @('normal', 'json', 'binary', 'tabular') - 'resv acquire --output-format' = @('normal', 'json', 'binary', 'tabular') + 'reset -o' = @('normal', 'json', 'binary') + 'resv acquire --output-format' = @('normal', 'json', 'binary') 'resv acquire --output-format-version' = @('1', '2') - 'resv acquire -o' = @('normal', 'json', 'binary', 'tabular') - 'resv register --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv acquire -o' = @('normal', 'json', 'binary') + 'resv register --output-format' = @('normal', 'json', 'binary') 'resv register --output-format-version' = @('1', '2') - 'resv register -o' = @('normal', 'json', 'binary', 'tabular') - 'resv release --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv register -o' = @('normal', 'json', 'binary') + 'resv release --output-format' = @('normal', 'json', 'binary') 'resv release --output-format-version' = @('1', '2') - 'resv release -o' = @('normal', 'json', 'binary', 'tabular') - 'resv report --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv release -o' = @('normal', 'json', 'binary') + 'resv report --output-format' = @('normal', 'json', 'binary') 'resv report --output-format-version' = @('1', '2') - 'resv report -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-acquire --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv report -o' = @('normal', 'json', 'binary') + 'resv-acquire --output-format' = @('normal', 'json', 'binary') 'resv-acquire --output-format-version' = @('1', '2') - 'resv-acquire -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-notif-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv-acquire -o' = @('normal', 'json', 'binary') + 'resv-notif-log --output-format' = @('normal', 'json', 'binary') 'resv-notif-log --output-format-version' = @('1', '2') - 'resv-notif-log -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-register --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv-notif-log -o' = @('normal', 'json', 'binary') + 'resv-register --output-format' = @('normal', 'json', 'binary') 'resv-register --output-format-version' = @('1', '2') - 'resv-register -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-release --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv-register -o' = @('normal', 'json', 'binary') + 'resv-release --output-format' = @('normal', 'json', 'binary') 'resv-release --output-format-version' = @('1', '2') - 'resv-release -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-report --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv-release -o' = @('normal', 'json', 'binary') + 'resv-report --output-format' = @('normal', 'json', 'binary') 'resv-report --output-format-version' = @('1', '2') - 'resv-report -o' = @('normal', 'json', 'binary', 'tabular') - 'rotational-media-info-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'resv-report -o' = @('normal', 'json', 'binary') + 'rotational-media-info-log --output-format' = @('normal', 'json', 'binary') 'rotational-media-info-log --output-format-version' = @('1', '2') - 'rotational-media-info-log -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rotational-media-info-log -o' = @('normal', 'json', 'binary') + 'rpmb info --output-format' = @('normal', 'json', 'binary') 'rpmb info --output-format-version' = @('1', '2') - 'rpmb info -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb program-key --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb info -o' = @('normal', 'json', 'binary') + 'rpmb program-key --output-format' = @('normal', 'json', 'binary') 'rpmb program-key --output-format-version' = @('1', '2') - 'rpmb program-key -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb program-key -o' = @('normal', 'json', 'binary') + 'rpmb read-config --output-format' = @('normal', 'json', 'binary') 'rpmb read-config --output-format-version' = @('1', '2') - 'rpmb read-config -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-counter --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb read-config -o' = @('normal', 'json', 'binary') + 'rpmb read-counter --output-format' = @('normal', 'json', 'binary') 'rpmb read-counter --output-format-version' = @('1', '2') - 'rpmb read-counter -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-data --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb read-counter -o' = @('normal', 'json', 'binary') + 'rpmb read-data --output-format' = @('normal', 'json', 'binary') 'rpmb read-data --output-format-version' = @('1', '2') - 'rpmb read-data -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb write-config --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb read-data -o' = @('normal', 'json', 'binary') + 'rpmb write-config --output-format' = @('normal', 'json', 'binary') 'rpmb write-config --output-format-version' = @('1', '2') - 'rpmb write-config -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb write-data --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb write-config -o' = @('normal', 'json', 'binary') + 'rpmb write-data --output-format' = @('normal', 'json', 'binary') 'rpmb write-data --output-format-version' = @('1', '2') - 'rpmb write-data -o' = @('normal', 'json', 'binary', 'tabular') - 'samsung vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'rpmb write-data -o' = @('normal', 'json', 'binary') + 'samsung vs-internal-log --output-format' = @('normal') 'samsung vs-internal-log --output-format-version' = @('1', '2') - 'samsung vs-internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sanitize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'samsung vs-internal-log -o' = @('normal') + 'sanitize --output-format' = @('normal', 'json', 'binary') 'sanitize --output-format-version' = @('1', '2') 'sanitize --sanact' = @('exit-failure', 'start-block-erase', 'start-overwrite', 'start-crypto-erase', 'exit-media-verification') 'sanitize -a' = @('exit-failure', 'start-block-erase', 'start-overwrite', 'start-crypto-erase', 'exit-media-verification') - 'sanitize -o' = @('normal', 'json', 'binary', 'tabular') - 'sanitize-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sanitize -o' = @('normal', 'json', 'binary') + 'sanitize-log --output-format' = @('normal', 'json', 'binary') 'sanitize-log --output-format-version' = @('1', '2') - 'sanitize-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sanitize-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sanitize-log -o' = @('normal', 'json', 'binary') + 'sanitize-ns --output-format' = @('normal', 'json', 'binary') 'sanitize-ns --output-format-version' = @('1', '2') 'sanitize-ns --sanact' = @('exit-failure', 'start-crypto-erase', 'exit-media-verification') 'sanitize-ns -a' = @('exit-failure', 'start-crypto-erase', 'exit-media-verification') - 'sanitize-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sanitize-ns -o' = @('normal', 'json', 'binary') + 'seagate clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'seagate clear-fw-activate-history --output-format-version' = @('1', '2') - 'seagate clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'seagate clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'seagate clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'seagate clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'seagate cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'seagate cloud-SSD-plugin-version --output-format-version' = @('1', '2') - 'seagate cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate get-ctrl-tele --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'seagate get-ctrl-tele --output-format' = @('normal', 'json', 'binary') 'seagate get-ctrl-tele --output-format-version' = @('1', '2') - 'seagate get-ctrl-tele -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate get-host-tele --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate get-ctrl-tele -o' = @('normal', 'json', 'binary') + 'seagate get-host-tele --output-format' = @('normal', 'json', 'binary') 'seagate get-host-tele --output-format-version' = @('1', '2') - 'seagate get-host-tele -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate get-host-tele -o' = @('normal', 'json', 'binary') + 'seagate plugin-version --output-format' = @('normal', 'json', 'binary') 'seagate plugin-version --output-format-version' = @('1', '2') - 'seagate plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate plugin-version -o' = @('normal', 'json', 'binary') + 'seagate vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'seagate vs-fw-activate-history --output-format-version' = @('1', '2') - 'seagate vs-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'seagate vs-internal-log --output-format' = @('normal', 'json', 'binary') 'seagate vs-internal-log --output-format-version' = @('1', '2') - 'seagate vs-internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-log-page-sup --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-internal-log -o' = @('normal', 'json', 'binary') + 'seagate vs-log-page-sup --output-format' = @('normal', 'json', 'binary') 'seagate vs-log-page-sup --output-format-version' = @('1', '2') - 'seagate vs-log-page-sup -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-log-page-sup -o' = @('normal', 'json', 'binary') + 'seagate vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'seagate vs-pcie-stats --output-format-version' = @('1', '2') - 'seagate vs-pcie-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-pcie-stats -o' = @('normal', 'json', 'binary') + 'seagate vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'seagate vs-smart-add-log --output-format-version' = @('1', '2') - 'seagate vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-smart-add-log -o' = @('normal', 'json', 'binary') + 'seagate vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'seagate vs-temperature-stats --output-format-version' = @('1', '2') - 'seagate vs-temperature-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'security recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'seagate vs-temperature-stats -o' = @('normal', 'json', 'binary') + 'security recv --output-format' = @('normal', 'json', 'binary') 'security recv --output-format-version' = @('1', '2') - 'security recv -o' = @('normal', 'json', 'binary', 'tabular') - 'security send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'security recv -o' = @('normal', 'json', 'binary') + 'security send --output-format' = @('normal', 'json', 'binary') 'security send --output-format-version' = @('1', '2') - 'security send -o' = @('normal', 'json', 'binary', 'tabular') - 'security-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'security send -o' = @('normal', 'json', 'binary') + 'security-recv --output-format' = @('normal', 'json', 'binary') 'security-recv --output-format-version' = @('1', '2') - 'security-recv -o' = @('normal', 'json', 'binary', 'tabular') - 'security-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'security-recv -o' = @('normal', 'json', 'binary') + 'security-send --output-format' = @('normal', 'json', 'binary') 'security-send --output-format-version' = @('1', '2') - 'security-send -o' = @('normal', 'json', 'binary', 'tabular') - 'self-test-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'security-send -o' = @('normal', 'json', 'binary') + 'sed 1 --output-format' = @('normal', 'json', 'binary') + 'sed 1 --output-format-version' = @('1', '2') + 'sed 1 -o' = @('normal', 'json', 'binary') + 'sed discover --output-format' = @('normal', 'json', 'binary') + 'sed discover --output-format-version' = @('1', '2') + 'sed discover -o' = @('normal', 'json', 'binary') + 'sed initialize --output-format' = @('normal', 'json', 'binary') + 'sed initialize --output-format-version' = @('1', '2') + 'sed initialize -o' = @('normal', 'json', 'binary') + 'sed lock --output-format' = @('normal', 'json', 'binary') + 'sed lock --output-format-version' = @('1', '2') + 'sed lock -o' = @('normal', 'json', 'binary') + 'sed password --output-format' = @('normal', 'json', 'binary') + 'sed password --output-format-version' = @('1', '2') + 'sed password -o' = @('normal', 'json', 'binary') + 'sed revert --output-format' = @('normal', 'json', 'binary') + 'sed revert --output-format-version' = @('1', '2') + 'sed revert -o' = @('normal', 'json', 'binary') + 'sed unlock --output-format' = @('normal', 'json', 'binary') + 'sed unlock --output-format-version' = @('1', '2') + 'sed unlock -o' = @('normal', 'json', 'binary') + 'self-test-log --output-format' = @('normal', 'json', 'binary') 'self-test-log --output-format-version' = @('1', '2') - 'self-test-log -o' = @('normal', 'json', 'binary', 'tabular') + 'self-test-log -o' = @('normal', 'json', 'binary') 'set-feature --feature-id' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') - 'set-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'set-feature --output-format' = @('normal', 'json', 'binary') 'set-feature --output-format-version' = @('1', '2') 'set-feature -f' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') - 'set-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'set-property --output-format' = @('normal', 'json', 'binary', 'tabular') + 'set-feature -o' = @('normal', 'json', 'binary') + 'set-property --output-format' = @('normal', 'json', 'binary') 'set-property --output-format-version' = @('1', '2') - 'set-property -o' = @('normal', 'json', 'binary', 'tabular') - 'set-reg --output-format' = @('normal', 'json', 'binary', 'tabular') + 'set-property -o' = @('normal', 'json', 'binary') + 'set-reg --output-format' = @('normal', 'json', 'binary') 'set-reg --output-format-version' = @('1', '2') - 'set-reg -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx change-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + 'set-reg -o' = @('normal', 'json', 'binary') + 'sfx change-cap --output-format' = @('normal', 'json', 'binary') 'sfx change-cap --output-format-version' = @('1', '2') - 'sfx change-cap -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx dump-evtlog --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx change-cap -o' = @('normal', 'json', 'binary') + 'sfx dump-evtlog --output-format' = @('normal', 'json', 'binary') 'sfx dump-evtlog --output-format-version' = @('1', '2') - 'sfx dump-evtlog -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx expand-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx dump-evtlog -o' = @('normal', 'json', 'binary') + 'sfx expand-cap --output-format' = @('normal', 'json', 'binary') 'sfx expand-cap --output-format-version' = @('1', '2') - 'sfx expand-cap -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx get-bad-block --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx expand-cap -o' = @('normal', 'json', 'binary') + 'sfx get-bad-block --output-format' = @('normal', 'json', 'binary') 'sfx get-bad-block --output-format-version' = @('1', '2') - 'sfx get-bad-block -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx get-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx get-bad-block -o' = @('normal', 'json', 'binary') + 'sfx get-feature --output-format' = @('normal', 'json', 'binary') 'sfx get-feature --output-format-version' = @('1', '2') - 'sfx get-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx lat-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx get-feature -o' = @('normal', 'json', 'binary') + 'sfx lat-stats --output-format' = @('normal', 'json', 'binary') 'sfx lat-stats --output-format-version' = @('1', '2') - 'sfx lat-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx query-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx lat-stats -o' = @('normal', 'json', 'binary') + 'sfx query-cap --output-format' = @('normal', 'json', 'binary') 'sfx query-cap --output-format-version' = @('1', '2') - 'sfx query-cap -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx set-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx query-cap -o' = @('normal', 'json', 'binary') + 'sfx set-feature --output-format' = @('normal', 'json', 'binary') 'sfx set-feature --output-format-version' = @('1', '2') - 'sfx set-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx set-feature -o' = @('normal', 'json', 'binary') + 'sfx smart-log-add --output-format' = @('normal', 'json', 'binary') 'sfx smart-log-add --output-format-version' = @('1', '2') - 'sfx smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx smart-log-add -o' = @('normal', 'json', 'binary') + 'sfx status --output-format' = @('normal', 'json', 'binary') 'sfx status --output-format-version' = @('1', '2') - 'sfx status -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon get-additional-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sfx status -o' = @('normal', 'json', 'binary') + 'shannon get-additional-feature --output-format' = @('normal', 'json', 'binary') 'shannon get-additional-feature --output-format-version' = @('1', '2') 'shannon get-additional-feature --sel' = @('0', '1', '2', '3') - 'shannon get-additional-feature -o' = @('normal', 'json', 'binary', 'tabular') + 'shannon get-additional-feature -o' = @('normal', 'json', 'binary') 'shannon get-additional-feature -s' = @('0', '1', '2', '3') - 'shannon id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'shannon id-ctrl --output-format' = @('normal', 'json', 'binary') 'shannon id-ctrl --output-format-version' = @('1', '2') - 'shannon id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon set-additioal-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'shannon id-ctrl -o' = @('normal', 'json', 'binary') + 'shannon set-additioal-feature --output-format' = @('normal', 'json', 'binary') 'shannon set-additioal-feature --output-format-version' = @('1', '2') - 'shannon set-additioal-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'shannon set-additioal-feature -o' = @('normal', 'json', 'binary') + 'shannon smart-log-add --output-format' = @('normal', 'json', 'binary') 'shannon smart-log-add --output-format-version' = @('1', '2') - 'shannon smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'show-hostnqn --output-format' = @('normal', 'json', 'binary', 'tabular') + 'shannon smart-log-add -o' = @('normal', 'json', 'binary') + 'show-hostnqn --output-format' = @('normal', 'json', 'binary') 'show-hostnqn --output-format-version' = @('1', '2') - 'show-hostnqn -o' = @('normal', 'json', 'binary', 'tabular') - 'show-regs --output-format' = @('normal', 'json', 'binary', 'tabular') + 'show-hostnqn -o' = @('normal', 'json', 'binary') + 'show-regs --output-format' = @('normal', 'json', 'binary') 'show-regs --output-format-version' = @('1', '2') - 'show-regs -o' = @('normal', 'json', 'binary', 'tabular') - 'show-topology --output-format' = @('normal', 'json', 'binary', 'tabular') + 'show-regs -o' = @('normal', 'json', 'binary') + 'show-topology --output-format' = @('normal', 'json', 'tabular') 'show-topology --output-format-version' = @('1', '2') - 'show-topology -o' = @('normal', 'json', 'binary', 'tabular') - 'smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'show-topology -o' = @('normal', 'json', 'tabular') + 'smart-log --output-format' = @('normal', 'json', 'binary') 'smart-log --output-format-version' = @('1', '2') - 'smart-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk capabilities --output-format' = @('normal', 'json', 'binary', 'tabular') + 'smart-log -o' = @('normal', 'json', 'binary') + 'sndk capabilities --output-format' = @('normal', 'json', 'binary') 'sndk capabilities --output-format-version' = @('1', '2') - 'sndk capabilities -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-assert-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk capabilities -o' = @('normal', 'json', 'binary') + 'sndk clear-assert-dump --output-format' = @('normal', 'json', 'binary') 'sndk clear-assert-dump --output-format-version' = @('1', '2') - 'sndk clear-assert-dump -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk clear-assert-dump -o' = @('normal', 'json', 'binary') + 'sndk clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'sndk clear-fw-activate-history --output-format-version' = @('1', '2') - 'sndk clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'sndk clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'sndk clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'sndk clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'sndk cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'sndk cloud-SSD-plugin-version --output-format-version' = @('1', '2') - 'sndk cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'sndk cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'sndk cloud-boot-SSD-version --output-format-version' = @('1', '2') - 'sndk cloud-boot-SSD-version -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cu-smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') + 'sndk cu-smart-log --output-format' = @('normal', 'json', 'binary') 'sndk cu-smart-log --output-format-version' = @('1', '2') - 'sndk cu-smart-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk drive-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk cu-smart-log -o' = @('normal', 'json', 'binary') + 'sndk drive-resize --output-format' = @('normal', 'json', 'binary') 'sndk drive-resize --output-format-version' = @('1', '2') - 'sndk drive-resize -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk drive-resize -o' = @('normal', 'json', 'binary') + 'sndk get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary') 'sndk get-dev-capabilities-log --output-format-version' = @('1', '2') - 'sndk get-dev-capabilities-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-drive-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk get-dev-capabilities-log -o' = @('normal', 'json', 'binary') + 'sndk get-drive-status --output-format' = @('normal', 'json', 'binary') 'sndk get-drive-status --output-format-version' = @('1', '2') - 'sndk get-drive-status -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-error-recovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk get-drive-status -o' = @('normal', 'json', 'binary') + 'sndk get-error-recovery-log --output-format' = @('normal', 'json', 'binary') 'sndk get-error-recovery-log --output-format-version' = @('1', '2') - 'sndk get-error-recovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-latency-monitor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk get-error-recovery-log -o' = @('normal', 'json', 'binary') + 'sndk get-latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'sndk get-latency-monitor-log --output-format-version' = @('1', '2') - 'sndk get-latency-monitor-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk get-latency-monitor-log -o' = @('normal', 'json', 'binary') + 'sndk get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'sndk get-unsupported-reqs-log --output-format-version' = @('1', '2') - 'sndk get-unsupported-reqs-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk get-unsupported-reqs-log -o' = @('normal', 'json', 'binary') + 'sndk log-page-directory --output-format' = @('normal', 'json', 'binary') 'sndk log-page-directory --output-format-version' = @('1', '2') - 'sndk log-page-directory -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk namespace-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk log-page-directory -o' = @('normal', 'json', 'binary') + 'sndk namespace-resize --output-format' = @('normal', 'json', 'binary') 'sndk namespace-resize --output-format-version' = @('1', '2') - 'sndk namespace-resize -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk namespace-resize -o' = @('normal', 'json', 'binary') + 'sndk set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'sndk set-latency-monitor-feature --output-format-version' = @('1', '2') - 'sndk set-latency-monitor-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk set-latency-monitor-feature -o' = @('normal', 'json', 'binary') + 'sndk vs-cloud-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-cloud-log --output-format-version' = @('1', '2') - 'sndk vs-cloud-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-cloud-log -o' = @('normal', 'json', 'binary') + 'sndk vs-device-waf --output-format' = @('normal', 'json', 'binary') 'sndk vs-device-waf --output-format-version' = @('1', '2') - 'sndk vs-device-waf -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-device-waf -o' = @('normal', 'json', 'binary') + 'sndk vs-drive-info --output-format' = @('normal', 'json', 'binary') 'sndk vs-drive-info --output-format-version' = @('1', '2') - 'sndk vs-drive-info -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-drive-info -o' = @('normal', 'json', 'binary') + 'sndk vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary') 'sndk vs-error-reason-identifier --output-format-version' = @('1', '2') - 'sndk vs-error-reason-identifier -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-error-reason-identifier -o' = @('normal', 'json', 'binary') + 'sndk vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'sndk vs-fw-activate-history --output-format-version' = @('1', '2') - 'sndk vs-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-hw-rev-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'sndk vs-hw-rev-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-hw-rev-log --output-format-version' = @('1', '2') - 'sndk vs-hw-rev-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-hw-rev-log -o' = @('normal', 'json', 'binary') + 'sndk vs-internal-log --output-format' = @('normal', 'json', 'binary') + 'sndk vs-internal-log --output-format-version' = @('1', '2') + 'sndk vs-internal-log -o' = @('normal', 'json', 'binary') + 'sndk vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-nand-stats --output-format-version' = @('1', '2') - 'sndk vs-nand-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-nand-stats -o' = @('normal', 'json', 'binary') + 'sndk vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-pcie-stats --output-format-version' = @('1', '2') - 'sndk vs-pcie-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-pcie-stats -o' = @('normal', 'json', 'binary') + 'sndk vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-smart-add-log --output-format-version' = @('1', '2') - 'sndk vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-smart-add-log -o' = @('normal', 'json', 'binary') + 'sndk vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'sndk vs-telemetry-controller-option --output-format-version' = @('1', '2') - 'sndk vs-telemetry-controller-option -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') + 'sndk vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-temperature-stats --output-format-version' = @('1', '2') - 'sndk vs-temperature-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'sndk vs-temperature-stats -o' = @('normal', 'json', 'binary') + 'solidigm clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'solidigm clear-fw-activate-history --output-format-version' = @('1', '2') - 'solidigm clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'solidigm clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'solidigm clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'solidigm clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm cloud-SSDplugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'solidigm cloud-SSDplugin-version --output-format' = @('normal', 'json', 'binary') 'solidigm cloud-SSDplugin-version --output-format-version' = @('1', '2') - 'solidigm cloud-SSDplugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm garbage-collect-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm cloud-SSDplugin-version -o' = @('normal', 'json', 'binary') + 'solidigm garbage-collect-log --output-format' = @('normal', 'json', 'binary') 'solidigm garbage-collect-log --output-format-version' = @('1', '2') - 'solidigm garbage-collect-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm garbage-collect-log -o' = @('normal', 'json', 'binary') + 'solidigm id-ctrl --output-format' = @('normal', 'json', 'binary') 'solidigm id-ctrl --output-format-version' = @('1', '2') - 'solidigm id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm latency-tracking-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm id-ctrl -o' = @('normal', 'json', 'binary') + 'solidigm latency-tracking-log --output-format' = @('normal', 'json', 'binary') 'solidigm latency-tracking-log --output-format-version' = @('1', '2') - 'solidigm latency-tracking-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm latency-tracking-log -o' = @('normal', 'json', 'binary') + 'solidigm log-page-directory --output-format' = @('normal', 'json', 'binary') 'solidigm log-page-directory --output-format-version' = @('1', '2') - 'solidigm log-page-directory -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm market-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm log-page-directory -o' = @('normal', 'json', 'binary') + 'solidigm market-log --output-format' = @('normal', 'json', 'binary') 'solidigm market-log --output-format-version' = @('1', '2') - 'solidigm market-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm parse-telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm market-log -o' = @('normal', 'json', 'binary') + 'solidigm parse-telemetry-log --output-format' = @('normal', 'json', 'binary') 'solidigm parse-telemetry-log --output-format-version' = @('1', '2') - 'solidigm parse-telemetry-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm parse-telemetry-log -o' = @('normal', 'json', 'binary') + 'solidigm smart-log-add --output-format' = @('normal', 'json', 'binary') 'solidigm smart-log-add --output-format-version' = @('1', '2') - 'solidigm smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm temp-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm smart-log-add -o' = @('normal', 'json', 'binary') + 'solidigm temp-stats --output-format' = @('normal', 'json', 'binary') 'solidigm temp-stats --output-format-version' = @('1', '2') - 'solidigm temp-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm temp-stats -o' = @('normal', 'json', 'binary') + 'solidigm vs-drive-info --output-format' = @('normal', 'json') 'solidigm vs-drive-info --output-format-version' = @('1', '2') - 'solidigm vs-drive-info -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm vs-drive-info -o' = @('normal', 'json') + 'solidigm vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'solidigm vs-fw-activate-history --output-format-version' = @('1', '2') - 'solidigm vs-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'solidigm vs-internal-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-internal-log --output-format-version' = @('1', '2') - 'solidigm vs-internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm vs-internal-log -o' = @('normal', 'json', 'binary') + 'solidigm vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-smart-add-log --output-format-version' = @('1', '2') - 'solidigm vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm workload-tracker --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm vs-smart-add-log -o' = @('normal', 'json', 'binary') + 'solidigm workload-tracker --output-format' = @('normal', 'json', 'binary') 'solidigm workload-tracker --output-format-version' = @('1', '2') - 'solidigm workload-tracker -o' = @('normal', 'json', 'binary', 'tabular') - 'ssstc smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'solidigm workload-tracker -o' = @('normal', 'json', 'binary') + 'ssstc smart-log-add --output-format' = @('normal', 'json', 'binary') 'ssstc smart-log-add --output-format-version' = @('1', '2') - 'ssstc smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'subsystem-reset --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ssstc smart-log-add -o' = @('normal', 'json', 'binary') + 'subsystem-reset --output-format' = @('normal', 'json', 'binary') 'subsystem-reset --output-format-version' = @('1', '2') - 'subsystem-reset -o' = @('normal', 'json', 'binary', 'tabular') - 'supported-cap-config-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'subsystem-reset -o' = @('normal', 'json', 'binary') + 'supported-cap-config-log --output-format' = @('normal', 'json', 'binary') 'supported-cap-config-log --output-format-version' = @('1', '2') - 'supported-cap-config-log -o' = @('normal', 'json', 'binary', 'tabular') - 'supported-log-pages --output-format' = @('normal', 'json', 'binary', 'tabular') + 'supported-cap-config-log -o' = @('normal', 'json', 'binary') + 'supported-log-pages --output-format' = @('normal', 'json', 'binary') 'supported-log-pages --output-format-version' = @('1', '2') - 'supported-log-pages -o' = @('normal', 'json', 'binary', 'tabular') - 'telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'supported-log-pages -o' = @('normal', 'json', 'binary') + 'telemetry-log --output-format' = @('normal', 'json', 'binary') 'telemetry-log --output-format-version' = @('1', '2') - 'telemetry-log -o' = @('normal', 'json', 'binary', 'tabular') - 'top --output-format' = @('normal', 'json', 'binary', 'tabular') + 'telemetry-log -o' = @('normal', 'json', 'binary') + 'top --output-format' = @('normal') 'top --output-format-version' = @('1', '2') - 'top -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'top -o' = @('normal') + 'toshiba clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'toshiba clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'toshiba clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'toshiba clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'toshiba vs-internal-log --output-format' = @('normal', 'json', 'binary') 'toshiba vs-internal-log --output-format-version' = @('1', '2') - 'toshiba vs-internal-log -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'toshiba vs-internal-log -o' = @('normal', 'json', 'binary') + 'toshiba vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'toshiba vs-smart-add-log --output-format-version' = @('1', '2') - 'toshiba vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'transcend badblock --output-format' = @('normal', 'json', 'binary', 'tabular') + 'toshiba vs-smart-add-log -o' = @('normal', 'json', 'binary') + 'transcend badblock --output-format' = @('normal', 'json', 'binary') 'transcend badblock --output-format-version' = @('1', '2') - 'transcend badblock -o' = @('normal', 'json', 'binary', 'tabular') - 'transcend healthvalue --output-format' = @('normal', 'json', 'binary', 'tabular') + 'transcend badblock -o' = @('normal', 'json', 'binary') + 'transcend healthvalue --output-format' = @('normal', 'json', 'binary') 'transcend healthvalue --output-format-version' = @('1', '2') - 'transcend healthvalue -o' = @('normal', 'json', 'binary', 'tabular') - 'verify --output-format' = @('normal', 'json', 'binary', 'tabular') + 'transcend healthvalue -o' = @('normal', 'json', 'binary') + 'verify --output-format' = @('normal', 'json', 'binary') 'verify --output-format-version' = @('1', '2') - 'verify -o' = @('normal', 'json', 'binary', 'tabular') - 'virt-mgmt --output-format' = @('normal', 'json', 'binary', 'tabular') + 'verify -o' = @('normal', 'json', 'binary') + 'virt-mgmt --output-format' = @('normal', 'json', 'binary') 'virt-mgmt --output-format-version' = @('1', '2') - 'virt-mgmt -o' = @('normal', 'json', 'binary', 'tabular') - 'virtium save-smart-to-vtview-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'virt-mgmt -o' = @('normal', 'json', 'binary') + 'virtium save-smart-to-vtview-log --output-format' = @('normal', 'json', 'binary') 'virtium save-smart-to-vtview-log --output-format-version' = @('1', '2') - 'virtium save-smart-to-vtview-log -o' = @('normal', 'json', 'binary', 'tabular') - 'virtium show-identify --output-format' = @('normal', 'json', 'binary', 'tabular') + 'virtium save-smart-to-vtview-log -o' = @('normal', 'json', 'binary') + 'virtium show-identify --output-format' = @('normal', 'json', 'binary') 'virtium show-identify --output-format-version' = @('1', '2') - 'virtium show-identify -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cap-diag --output-format' = @('normal', 'json', 'binary', 'tabular') + 'virtium show-identify -o' = @('normal', 'json', 'binary') + 'wdc cap-diag --output-format' = @('normal', 'json', 'binary') 'wdc cap-diag --output-format-version' = @('1', '2') - 'wdc cap-diag -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc capabilities --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc cap-diag -o' = @('normal', 'json', 'binary') + 'wdc capabilities --output-format' = @('normal', 'json', 'binary') 'wdc capabilities --output-format-version' = @('1', '2') - 'wdc capabilities -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-assert-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc capabilities -o' = @('normal', 'json', 'binary') + 'wdc clear-assert-dump --output-format' = @('normal', 'json', 'binary') 'wdc clear-assert-dump --output-format-version' = @('1', '2') - 'wdc clear-assert-dump -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc clear-assert-dump -o' = @('normal', 'json', 'binary') + 'wdc clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'wdc clear-fw-activate-history --output-format-version' = @('1', '2') - 'wdc clear-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc clear-fw-activate-history -o' = @('normal', 'json', 'binary') + 'wdc clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'wdc clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'wdc clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') + 'wdc cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'wdc cloud-SSD-plugin-version --output-format-version' = @('1', '2') - 'wdc cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'wdc cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'wdc cloud-boot-SSD-version --output-format-version' = @('1', '2') - 'wdc cloud-boot-SSD-version -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cu-smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') + 'wdc cu-smart-log --output-format' = @('normal', 'json', 'binary') 'wdc cu-smart-log --output-format-version' = @('1', '2') - 'wdc cu-smart-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-essentials --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc cu-smart-log -o' = @('normal', 'json', 'binary') + 'wdc drive-essentials --output-format' = @('normal', 'json', 'binary') 'wdc drive-essentials --output-format-version' = @('1', '2') - 'wdc drive-essentials -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc drive-essentials -o' = @('normal', 'json', 'binary') + 'wdc drive-log --output-format' = @('normal', 'json', 'binary') 'wdc drive-log --output-format-version' = @('1', '2') - 'wdc drive-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc drive-log -o' = @('normal', 'json', 'binary') + 'wdc drive-resize --output-format' = @('normal', 'json', 'binary') 'wdc drive-resize --output-format-version' = @('1', '2') - 'wdc drive-resize -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc enc-get-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc drive-resize -o' = @('normal', 'json', 'binary') + 'wdc enc-get-log --output-format' = @('normal', 'json', 'binary') 'wdc enc-get-log --output-format-version' = @('1', '2') - 'wdc enc-get-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-crash-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc enc-get-log -o' = @('normal', 'json', 'binary') + 'wdc get-crash-dump --output-format' = @('normal', 'json', 'binary') 'wdc get-crash-dump --output-format-version' = @('1', '2') - 'wdc get-crash-dump -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-crash-dump -o' = @('normal', 'json', 'binary') + 'wdc get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary') 'wdc get-dev-capabilities-log --output-format-version' = @('1', '2') - 'wdc get-dev-capabilities-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-drive-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-dev-capabilities-log -o' = @('normal', 'json', 'binary') + 'wdc get-drive-status --output-format' = @('normal', 'json', 'binary') 'wdc get-drive-status --output-format-version' = @('1', '2') - 'wdc get-drive-status -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-error-recovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-drive-status -o' = @('normal', 'json', 'binary') + 'wdc get-error-recovery-log --output-format' = @('normal', 'json', 'binary') 'wdc get-error-recovery-log --output-format-version' = @('1', '2') - 'wdc get-error-recovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-latency-monitor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-error-recovery-log -o' = @('normal', 'json', 'binary') + 'wdc get-latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'wdc get-latency-monitor-log --output-format-version' = @('1', '2') - 'wdc get-latency-monitor-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-pfail-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-latency-monitor-log -o' = @('normal', 'json', 'binary') + 'wdc get-pfail-dump --output-format' = @('normal', 'json', 'binary') 'wdc get-pfail-dump --output-format-version' = @('1', '2') - 'wdc get-pfail-dump -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-pfail-dump -o' = @('normal', 'json', 'binary') + 'wdc get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'wdc get-unsupported-reqs-log --output-format-version' = @('1', '2') - 'wdc get-unsupported-reqs-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc get-unsupported-reqs-log -o' = @('normal', 'json', 'binary') + 'wdc id-ctrl --output-format' = @('normal', 'json', 'binary') 'wdc id-ctrl --output-format-version' = @('1', '2') - 'wdc id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc id-ctrl -o' = @('normal', 'json', 'binary') + 'wdc log-page-directory --output-format' = @('normal', 'json', 'binary') 'wdc log-page-directory --output-format-version' = @('1', '2') - 'wdc log-page-directory -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc namespace-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc log-page-directory -o' = @('normal', 'json', 'binary') + 'wdc namespace-resize --output-format' = @('normal', 'json', 'binary') 'wdc namespace-resize --output-format-version' = @('1', '2') - 'wdc namespace-resize -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc purge --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc namespace-resize -o' = @('normal', 'json', 'binary') + 'wdc purge --output-format' = @('normal', 'json', 'binary') 'wdc purge --output-format-version' = @('1', '2') - 'wdc purge -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc purge-monitor --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc purge -o' = @('normal', 'json', 'binary') + 'wdc purge-monitor --output-format' = @('normal', 'json', 'binary') 'wdc purge-monitor --output-format-version' = @('1', '2') - 'wdc purge-monitor -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc purge-monitor -o' = @('normal', 'json', 'binary') + 'wdc set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'wdc set-latency-monitor-feature --output-format-version' = @('1', '2') - 'wdc set-latency-monitor-feature -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc set-latency-monitor-feature -o' = @('normal', 'json', 'binary') + 'wdc vs-cloud-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-cloud-log --output-format-version' = @('1', '2') - 'wdc vs-cloud-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-cloud-log -o' = @('normal', 'json', 'binary') + 'wdc vs-device-waf --output-format' = @('normal', 'json', 'binary') 'wdc vs-device-waf --output-format-version' = @('1', '2') - 'wdc vs-device-waf -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-device-waf -o' = @('normal', 'json', 'binary') + 'wdc vs-drive-info --output-format' = @('normal', 'json', 'binary') 'wdc vs-drive-info --output-format-version' = @('1', '2') - 'wdc vs-drive-info -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-drive-info -o' = @('normal', 'json', 'binary') + 'wdc vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary') 'wdc vs-error-reason-identifier --output-format-version' = @('1', '2') - 'wdc vs-error-reason-identifier -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-error-reason-identifier -o' = @('normal', 'json', 'binary') + 'wdc vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'wdc vs-fw-activate-history --output-format-version' = @('1', '2') - 'wdc vs-fw-activate-history -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-hw-rev-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'wdc vs-hw-rev-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-hw-rev-log --output-format-version' = @('1', '2') - 'wdc vs-hw-rev-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-hw-rev-log -o' = @('normal', 'json', 'binary') + 'wdc vs-internal-log --output-format' = @('normal', 'json', 'binary') + 'wdc vs-internal-log --output-format-version' = @('1', '2') + 'wdc vs-internal-log -o' = @('normal', 'json', 'binary') + 'wdc vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-nand-stats --output-format-version' = @('1', '2') - 'wdc vs-nand-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-nand-stats -o' = @('normal', 'json', 'binary') + 'wdc vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-pcie-stats --output-format-version' = @('1', '2') - 'wdc vs-pcie-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-pcie-stats -o' = @('normal', 'json', 'binary') + 'wdc vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-smart-add-log --output-format-version' = @('1', '2') - 'wdc vs-smart-add-log -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-smart-add-log -o' = @('normal', 'json', 'binary') + 'wdc vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'wdc vs-telemetry-controller-option --output-format-version' = @('1', '2') - 'wdc vs-telemetry-controller-option -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') + 'wdc vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-temperature-stats --output-format-version' = @('1', '2') - 'wdc vs-temperature-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'write --output-format' = @('normal', 'json', 'binary', 'tabular') + 'wdc vs-temperature-stats -o' = @('normal', 'json', 'binary') + 'write --output-format' = @('normal', 'json', 'binary') 'write --output-format-version' = @('1', '2') - 'write -o' = @('normal', 'json', 'binary', 'tabular') - 'write-uncor --output-format' = @('normal', 'json', 'binary', 'tabular') + 'write -o' = @('normal', 'json', 'binary') + 'write-uncor --output-format' = @('normal', 'json', 'binary') 'write-uncor --output-format-version' = @('1', '2') - 'write-uncor -o' = @('normal', 'json', 'binary', 'tabular') - 'write-zeroes --output-format' = @('normal', 'json', 'binary', 'tabular') + 'write-uncor -o' = @('normal', 'json', 'binary') + 'write-zeroes --output-format' = @('normal', 'json', 'binary') 'write-zeroes --output-format-version' = @('1', '2') - 'write-zeroes -o' = @('normal', 'json', 'binary', 'tabular') - 'ymtc smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + 'write-zeroes -o' = @('normal', 'json', 'binary') + 'ymtc smart-log-add --output-format' = @('normal', 'json', 'binary') 'ymtc smart-log-add --output-format-version' = @('1', '2') - 'ymtc smart-log-add -o' = @('normal', 'json', 'binary', 'tabular') - 'zns changed-zone-list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'ymtc smart-log-add -o' = @('normal', 'json', 'binary') + 'zns changed-zone-list --output-format' = @('normal', 'json', 'binary') 'zns changed-zone-list --output-format-version' = @('1', '2') - 'zns changed-zone-list -o' = @('normal', 'json', 'binary', 'tabular') - 'zns close-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns changed-zone-list -o' = @('normal', 'json', 'binary') + 'zns close-zone --output-format' = @('normal', 'json', 'binary') 'zns close-zone --output-format-version' = @('1', '2') - 'zns close-zone -o' = @('normal', 'json', 'binary', 'tabular') - 'zns finish-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns close-zone -o' = @('normal', 'json', 'binary') + 'zns finish-zone --output-format' = @('normal', 'json', 'binary') 'zns finish-zone --output-format-version' = @('1', '2') - 'zns finish-zone -o' = @('normal', 'json', 'binary', 'tabular') - 'zns id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns finish-zone -o' = @('normal', 'json', 'binary') + 'zns id-ctrl --output-format' = @('normal', 'json', 'binary') 'zns id-ctrl --output-format-version' = @('1', '2') - 'zns id-ctrl -o' = @('normal', 'json', 'binary', 'tabular') - 'zns id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns id-ctrl -o' = @('normal', 'json', 'binary') + 'zns id-ns --output-format' = @('normal', 'json', 'binary') 'zns id-ns --output-format-version' = @('1', '2') - 'zns id-ns -o' = @('normal', 'json', 'binary', 'tabular') - 'zns list --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns id-ns -o' = @('normal', 'json', 'binary') + 'zns list --output-format' = @('normal') 'zns list --output-format-version' = @('1', '2') - 'zns list -o' = @('normal', 'json', 'binary', 'tabular') - 'zns offline-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns list -o' = @('normal') + 'zns offline-zone --output-format' = @('normal', 'json', 'binary') 'zns offline-zone --output-format-version' = @('1', '2') - 'zns offline-zone -o' = @('normal', 'json', 'binary', 'tabular') - 'zns open-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns offline-zone -o' = @('normal', 'json', 'binary') + 'zns open-zone --output-format' = @('normal', 'json', 'binary') 'zns open-zone --output-format-version' = @('1', '2') - 'zns open-zone -o' = @('normal', 'json', 'binary', 'tabular') - 'zns report-zones --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns open-zone -o' = @('normal', 'json', 'binary') + 'zns report-zones --output-format' = @('normal', 'json', 'binary') 'zns report-zones --output-format-version' = @('1', '2') - 'zns report-zones -o' = @('normal', 'json', 'binary', 'tabular') - 'zns reset-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns report-zones -o' = @('normal', 'json', 'binary') + 'zns reset-zone --output-format' = @('normal', 'json', 'binary') 'zns reset-zone --output-format-version' = @('1', '2') - 'zns reset-zone -o' = @('normal', 'json', 'binary', 'tabular') - 'zns set-zone-desc --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns reset-zone -o' = @('normal', 'json', 'binary') + 'zns set-zone-desc --output-format' = @('normal', 'json', 'binary') 'zns set-zone-desc --output-format-version' = @('1', '2') - 'zns set-zone-desc -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-append --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns set-zone-desc -o' = @('normal', 'json', 'binary') + 'zns zone-append --output-format' = @('normal', 'json', 'binary') 'zns zone-append --output-format-version' = @('1', '2') - 'zns zone-append -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-mgmt-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns zone-append -o' = @('normal', 'json', 'binary') + 'zns zone-mgmt-recv --output-format' = @('normal', 'json', 'binary') 'zns zone-mgmt-recv --output-format-version' = @('1', '2') - 'zns zone-mgmt-recv -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-mgmt-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns zone-mgmt-recv -o' = @('normal', 'json', 'binary') + 'zns zone-mgmt-send --output-format' = @('normal', 'json', 'binary') 'zns zone-mgmt-send --output-format-version' = @('1', '2') - 'zns zone-mgmt-send -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zrwa-flush-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + 'zns zone-mgmt-send -o' = @('normal', 'json', 'binary') + 'zns zrwa-flush-zone --output-format' = @('normal', 'json', 'binary') 'zns zrwa-flush-zone --output-format-version' = @('1', '2') - 'zns zrwa-flush-zone -o' = @('normal', 'json', 'binary', 'tabular') + 'zns zrwa-flush-zone -o' = @('normal', 'json', 'binary') } Register-ArgumentCompleter -Native -CommandName nvme -ScriptBlock { From b70df42c78eda0e9ce52173597af3aeb630a8182 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 16:31:33 +0000 Subject: [PATCH 061/263] NEWS: document the global options changes Every command accepts the global options and rejects an invalid --output-format, and the help and completions list the output formats of each command. Signed-off-by: Daniel Wagner --- NEWS.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/NEWS.md b/NEWS.md index 6af84b63d4..914713bbfb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,6 +13,10 @@ short options, so `-verbose` is read as `-v -e -r -b -o -s -e`. Options and arguments can still be given in any order. +* Every command rejects an invalid `--output-format` value now, before + it opens the device. Before, the commands which don't use the output + format accepted any value. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. @@ -108,6 +112,16 @@ ### nvme-cli +* The global options (`--verbose`, `--output-format`, `--dry-run`, ...) + are accepted by every command. `config validate`, `config status`, + `config convert`, `intel lat-stats-tracking`, `memblaze lat-stats`, + `memblaze smart-log-add-x`, `sndk vs-internal-log`, + `wdc vs-internal-log` and the `sed` commands did not accept them. + +* The help and the shell completions of `--output-format` list the + formats the command supports. Before, the completions offered + `tabular` everywhere, and some commands listed formats they rejected. + * `--dry-run` now also skips `nvme reset`, `nvme subsystem-reset`, `nvme ns-rescan`, the `nvme sed` operations which change the drive, and the fabrics connects and disconnects. Before, only NVMe commands From 1135a9dcbc6ee8444a3df43528e1b26a1c709a68 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 13:11:35 -0400 Subject: [PATCH 062/263] log: convert the LBA status log length from little endian get_lba_status_log() used LSLPLEN without le32_to_cpu(). On a big-endian host, a log length of 16 became 256 MiB, which was then allocated and read from the device. Signed-off-by: Martin Belanger --- plugins/log/log-plugin.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 67aa3482ef..702d4fca1a 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -1287,6 +1287,7 @@ static int get_lba_status_log(int argc, char **argv, __cleanup_libnvme_free void *lba_status = NULL; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; + __le32 lslplen_le; __u32 lslplen; int err; @@ -1307,12 +1308,13 @@ static int get_lba_status_log(int argc, char **argv, flags = nvme_args.output_flags; - nvme_init_get_log_lba_status(&cmd, 0, &lslplen, sizeof(__u32)); - err = libnvme_get_log(hdl, &cmd, false, sizeof(__u32)); + nvme_init_get_log_lba_status(&cmd, 0, &lslplen_le, sizeof(lslplen_le)); + err = libnvme_get_log(hdl, &cmd, false, sizeof(lslplen_le)); if (err) { nvme_show_err(err, "lba status log page"); return err; } + lslplen = le32_to_cpu(lslplen_le); lba_status = libnvme_alloc(lslplen); if (!lba_status) From 8a66c419fae5866d5da3440f91edc92d0222f8b6 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 13:11:35 -0400 Subject: [PATCH 063/263] nvme-print: fix the streams directive printers The JSON printer was copied from the stdout printer, but with a __u16 pointer. It kept the byte offsets, so it read every field after MSL at twice its offset. It also converted SWS with le16_to_cpu(), but SWS is a 32-bit field. With a large Open Stream Count, it read past the end of the buffer. The stdout printer did not convert the fields from little endian, so the values were wrong on big-endian hosts. Use the libnvme structures in both printers. The field sizes and offsets match Figure 712 and Figure 713 of the NVMe Base Specification 2.4. Signed-off-by: Martin Belanger --- src/nvme-print-json.c | 35 ++++++++++++++++++++++------------- src/nvme-print-stdout.c | 26 +++++++++++++------------- tests/cli/nvme_print_test.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index d7b209c3f0..dba1fb255f 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5465,32 +5465,41 @@ static void json_directive_show_fields_identify(__u8 doper, __u8 *field, struct } } -static void json_directive_show_fields_streams(__u8 doper, unsigned int result, __u16 *field, +static void json_directive_show_fields_streams(__u8 doper, + unsigned int result, __u8 *field, struct json_object *r) { + struct nvme_streams_directive_params *params = (void *)field; + struct nvme_streams_directive_status *status = (void *)field; int count; int i; char json_str[STR_LEN]; switch (doper) { case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM: - obj_add_uint(r, "Max Streams Limit (MSL)", le16_to_cpu(*field)); - obj_add_uint(r, "NVM Subsystem Streams Available (NSSA)", le16_to_cpu(*(field + 2))); - obj_add_uint(r, "NVM Subsystem Streams Open (NSSO)", le16_to_cpu(*(field + 4))); - obj_add_uint(r, "NVM Subsystem Stream Capability (NSSC)", le16_to_cpu(*(field + 6))); + obj_add_uint(r, "Max Streams Limit (MSL)", + le16_to_cpu(params->msl)); + obj_add_uint(r, "NVM Subsystem Streams Available (NSSA)", + le16_to_cpu(params->nssa)); + obj_add_uint(r, "NVM Subsystem Streams Open (NSSO)", + le16_to_cpu(params->nsso)); + obj_add_uint(r, "NVM Subsystem Stream Capability (NSSC)", + params->nssc); obj_add_uint(r, "Stream Write Size (in unit of LB size) (SWS)", - le16_to_cpu(*(__u32 *)(field + 16))); + le32_to_cpu(params->sws)); obj_add_uint(r, "Stream Granularity Size (in unit of SWS) (SGS)", - le16_to_cpu(*(field + 20))); - obj_add_uint(r, "Namespace Streams Allocated (NSA)", le16_to_cpu(*(field + 22))); - obj_add_uint(r, "Namespace Streams Open (NSO)", le16_to_cpu(*(field + 24))); + le16_to_cpu(params->sgs)); + obj_add_uint(r, "Namespace Streams Allocated (NSA)", + le16_to_cpu(params->nsa)); + obj_add_uint(r, "Namespace Streams Open (NSO)", + le16_to_cpu(params->nso)); break; case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS: - count = *field; - obj_add_uint(r, "Open Stream Count", le16_to_cpu(*field)); + count = le16_to_cpu(status->osc); + obj_add_uint(r, "Open Stream Count", count); for (i = 0; i < count; i++) { sprintf(json_str, "Stream Identifier %.6u", i + 1); - obj_add_uint(r, json_str, le16_to_cpu(*(field + (i + 1) * 2))); + obj_add_uint(r, json_str, le16_to_cpu(status->sid[i])); } break; case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE: @@ -5511,7 +5520,7 @@ static void json_directive_show_fields(__u8 dtype, __u8 doper, unsigned int resu json_directive_show_fields_identify(doper, field, r); break; case NVME_DIRECTIVE_DTYPE_STREAMS: - json_directive_show_fields_streams(doper, result, (__u16 *)field, r); + json_directive_show_fields_streams(doper, result, field, r); break; default: obj_add_str(r, "Error", "invalid directive type"); diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 2c1bc03576..3f5f2ca94f 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -2123,6 +2123,8 @@ static void stdout_host_mem_buffer(struct nvme_host_mem_buf_attrs *hmb) static void stdout_directive_show_fields(__u8 dtype, __u8 doper, unsigned int result, unsigned char *buf) { + struct nvme_streams_directive_params *params = (void *)buf; + struct nvme_streams_directive_status *status = (void *)buf; __u8 *field = buf; int count, i; struct shr_table *t; @@ -2202,47 +2204,45 @@ static void stdout_directive_show_fields(__u8 dtype, __u8 doper, return; stdout_kv_add(t, "Max Streams Limit (MSL)", "%u", - *(__u16 *)field); + le16_to_cpu(params->msl)); stdout_kv_add(t, "NVM Subsystem Streams Available (NSSA)", - "%u", *(__u16 *)(field + 2)); + "%u", le16_to_cpu(params->nssa)); stdout_kv_add(t, "NVM Subsystem Streams Open (NSSO)", - "%u", *(__u16 *)(field + 4)); + "%u", le16_to_cpu(params->nsso)); stdout_kv_add(t, "NVM Subsystem Stream Capability (NSSC)", - "%u", *(__u16 *)(field + 6)); + "%u", params->nssc); stdout_kv_add(t, "Stream Write Size (in unit of LB size) (SWS)", - "%u", *(__u32 *)(field + 16)); + "%u", le32_to_cpu(params->sws)); stdout_kv_add(t, "Stream Granularity Size (in unit of SWS) (SGS)", - "%u", *(__u16 *)(field + 20)); + "%u", le16_to_cpu(params->sgs)); stdout_kv_add(t, "Namespace Streams Allocated (NSA)", - "%u", *(__u16 *)(field + 22)); + "%u", le16_to_cpu(params->nsa)); stdout_kv_add(t, "Namespace Streams Open (NSO)", "%u", - *(__u16 *)(field + 24)); + le16_to_cpu(params->nso)); stdout_kv_table_finish(t, "directive-show"); break; case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS: - count = *(__u16 *)field; + count = le16_to_cpu(status->osc); t = stdout_kv_table_create(); if (!t) return; - stdout_kv_add(t, "Open Stream Count", "%u", - *(__u16 *)field); + stdout_kv_add(t, "Open Stream Count", "%u", count); for (i = 0; i < count; i++) { char name[32]; snprintf(name, sizeof(name), "Stream Identifier %.6u", i + 1); stdout_kv_add(t, name, "%u", - *(__u16 *)(field + - (i + 1) * 2)); + le16_to_cpu(status->sid[i])); } stdout_kv_table_finish(t, "directive-show"); diff --git a/tests/cli/nvme_print_test.py b/tests/cli/nvme_print_test.py index 35575c11b9..38d7814491 100644 --- a/tests/cli/nvme_print_test.py +++ b/tests/cli/nvme_print_test.py @@ -48,6 +48,7 @@ _OPC_IO_MGMT_RECV = 0x12 _OPC_RESV_REPORT = 0x0e _OPC_DIR_RECV = 0x1a +_DIR_STREAMS_PARAM = 0x0101 # directive type 1, operation 1 _DIR_STREAMS_STATUS = 0x0102 # directive type 1, operation 2 _LID_PERSISTENT_EVENT = 0x0d _LID_MEDIA_UNIT_STATUS = 0x10 @@ -99,6 +100,7 @@ def __init__(self, sock_path): self.header_len = {} self.logs = {} self.id_ctrl = {} + self.dir_data = {} def handle_ioctl(self, conn, fd, request, opcode, nsid, cdw10, cdw11, cdw12, cdw13, cdw14, cdw15, lpo, req_len): @@ -124,6 +126,8 @@ def handle_ioctl(self, conn, fd, request, opcode, nsid, _put(buf, ' Date: Thu, 15 Oct 2026 13:15:48 -0400 Subject: [PATCH 064/263] fdp: convert the configurations log size from little endian fdp_configs() used the Size field of the FDP Configurations log without le32_to_cpu(). On a big-endian host, a size of 88 bytes became 1.4 GiB, which was then allocated and read from the device. Signed-off-by: Martin Belanger --- plugins/fdp/fdp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index eced56d3d4..a3df3c232e 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -35,6 +35,7 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, struct nvme_fdp_config_log hdr; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; + __u32 size; int err; struct config { @@ -77,18 +78,19 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, return err; } - log = malloc(hdr.size); + size = le32_to_cpu(hdr.size); + log = malloc(size); if (!log) return -ENOMEM; - nvme_init_get_log_fdp_configurations(&cmd, cfg.egid, 0, log, hdr.size); - err = libnvme_get_log(hdl, &cmd, false, hdr.size); + nvme_init_get_log_fdp_configurations(&cmd, cfg.egid, 0, log, size); + err = libnvme_get_log(hdl, &cmd, false, size); if (err) { nvme_show_status(errno); return err; } - nvme_show_fdp_configs(log, hdr.size, flags); + nvme_show_fdp_configs(log, size, flags); return 0; } From 39a7ffb9ccc5e048c1e93b476639a1d0d1ddd57b Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 13:15:48 -0400 Subject: [PATCH 065/263] tests: run the print test on big-endian hosts The test timed out on s390x because the LBA status and FDP configurations commands requested huge transfers, and the mock device stopped answering. Both are fixed, and the test passes on s390x in 31 seconds. Signed-off-by: Martin Belanger --- tests/cli/meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 6286617c86..a1e789b6dd 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -112,9 +112,8 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) - # Not on big-endian hosts yet: the test times out on s390x. if 'id' in selected_plugins and 'log' in selected_plugins and \ - 'feat' in selected_plugins and host_machine.endian() == 'little' + 'feat' in selected_plugins print_test_env = environment() print_test_env.prepend('PYTHONPATH', meson.project_source_root()) print_test_env.set('NVME_TEST_JSON', json_c_dep.found() ? '1' : '0') From af36d33d7f804af11639560b0fb1ec3fff71749e Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Thu, 15 Oct 2026 13:30:49 -0400 Subject: [PATCH 066/263] tests: keep the mock server running after a handler error An exception in a handler stopped the mock server thread. Each later nvme command then waited 60 seconds for an answer, and the print test reached the meson timeout without naming the command that failed. Answer the request with EIO and keep the server running. Record the traceback, and fail the print test command that caused it. Signed-off-by: Martin Belanger --- tests/cli/nvme_mock_ipc.py | 12 ++++++++++++ tests/cli/nvme_print_test.py | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/tests/cli/nvme_mock_ipc.py b/tests/cli/nvme_mock_ipc.py index d21d5b93f4..3fc439433f 100644 --- a/tests/cli/nvme_mock_ipc.py +++ b/tests/cli/nvme_mock_ipc.py @@ -29,6 +29,7 @@ import subprocess import sys import threading +import traceback from pathlib import Path IPC_REQUEST_FMT = " Date: Thu, 15 Oct 2026 15:33:51 -0700 Subject: [PATCH 067/263] ocp: name spec-defined Event IDs missing from the String Log OCP 2.7 TELSLG-14 lets a drive leave Statistic and Event IDs the spec defines out of the C9h String Log if the OCP plugin can decode them. Statistics already fall back to built-in names, but events printed an empty Event String. Add the Event ID names from OCP 2.7 Section 4.9.15.2 for classes 01h-0Dh and use them when the Event String Table has no matching entry. A String Log entry still takes precedence, and reserved, obsolete and vendor unique IDs, VU Event Identifiers and classes 80h-FFh stay unnamed. Add the same names to the test layout module, expect them in the e2e event check, and cover the fallback in the event FIFO mock suite. Signed-off-by: Broc Going --- plugins/ocp/ocp-telemetry-decode.c | 147 ++++++++++++++++- .../ocp_internal_log_event_fifo_mock_test.py | 148 +++++++++++++++++- .../e2e/plugins/ocp/ocp_internal_log_test.py | 21 ++- tests/e2e/plugins/ocp/ocp_telemetry_layout.py | 114 ++++++++++++-- 4 files changed, 401 insertions(+), 29 deletions(-) diff --git a/plugins/ocp/ocp-telemetry-decode.c b/plugins/ocp/ocp-telemetry-decode.c index 4e7d0ee29d..532f59ea6f 100644 --- a/plugins/ocp/ocp-telemetry-decode.c +++ b/plugins/ocp/ocp-telemetry-decode.c @@ -126,6 +126,125 @@ struct statistic_entry statistic_identifiers_map[] = { { 0x6F, "Queue ID Context Statistic Descriptor"} }; +/* OCP 2.7 Section 4.9.15.2 Event IDs; reserved and obsolete IDs are NULL. */ +static const char * const timestamp_event_id_str[] = { + [0x0000] = "Host issued a Timestamp Set Feature Command (Feature ID Eh)", + [0x0001] = "Firmware initiated Timestamp Snapshot", +}; + +static const char * const pcie_event_id_str[] = { + [0x0000] = "Link Up", + [0x0001] = "Link Down", + [0x0002] = "PCIe Error Detected", + [0x0003] = "PERST# Asserted", + [0x0004] = "PERST# De-asserted", + [0x0005] = "Refclk Stable", + [0x0006] = "Vmain Stable", + [0x0007] = "Link Speed and Width Negotiated", +}; + +static const char * const nvme_event_id_str[] = { + [0x0000] = "CC.EN transitions from 0b to 1b", + [0x0001] = "CC.EN transitions from 1b to 0b", + [0x0002] = "CSTS.RDY transitions from 0b to 1b", + [0x0003] = "CSTS.RDY transitions from 1b to 0b", + [0x0005] = "Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", + [0x0006] = "In-band Admin Command Processed other than Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", + [0x0007] = "An Admin command returned a non-zero Status Code", + [0x0008] = "An I/O Command Returned a Non-zero Status Code", + [0x0009] = "CSTS.CFS transitioned from 0b to 1b", + [0x000A] = "Admin Submission Queue Base Address Written (AQA) or Admin Completion Queue Based Address (ACQ) written", + [0x000B] = "Controller Configuration Register (CC) Changed except for the cases that are covered in 0000h and 0001h", + [0x000C] = "Controller Status Register (CSTS) Changed except for the cases that are covered in 0002h, 0003h, and 0009h", + [0x000D] = "Delete I/O Completion Queue Command or Delete I/O Submission Queue Command Processed", + [0x000E] = "Out-of-band Command Processed", + [0x000F] = "Out-of-band Asynchronous Event Message Transmitted", + [0x0010] = "Error Reporting AEN was sent (see EREC-5)", +}; + +static const char * const reset_event_id_str[] = { + [0x0000] = "PCIe Conventional Hot Reset", + [0x0001] = "Main Power Cycle", + [0x0002] = "PERST#", + [0x0003] = "PCIe Function Level Reset", + [0x0004] = "NVM Subsystem Reset", +}; + +static const char * const boot_sequence_event_id_str[] = { + [0x0000] = "Main Firmware Boot Complete", + [0x0001] = "FTL Load from NVM Complete", + [0x0002] = "FTL Rebuild Started", + [0x0003] = "FTL Rebuild Complete", +}; + +static const char * const firmware_assert_event_id_str[] = { + [0x0000] = "Assert in NVMe Processing Code", + [0x0001] = "Assert in Media Code", + [0x0002] = "Assert in Security Code", + [0x0003] = "Assert in Background Services Code", + [0x0004] = "FTL Rebuild Failed", + [0x0005] = "FTL Data Mismatch", + [0x0006] = "Assert in Other Code", +}; + +static const char * const temperature_event_id_str[] = { + [0x0000] = "Composite Temperature decreases to (WCTEMP - 2)", + [0x0001] = "Composite Temperature increases to WCTEMP", + [0x0002] = "Composite Temperature increases to reach CCTEMP", +}; + +static const char * const media_event_id_str[] = { + [0x0000] = "XOR (or equivalent) Recovery Invoked", + [0x0001] = "Uncorrectable Media Error", + [0x0002] = "Block Marked Bad Due to Program Error", + [0x0003] = "Block Marked Bad Due to Erase Error", + [0x0004] = "Block Marked Bad Due to Read Error", + [0x0005] = "Plane Failure Event", +}; + +static const char * const media_wear_event_id_str[] = { + [0x0000] = "Media Wear", +}; + +static const char * const virtual_fifo_event_id_str[] = { + [0x0000] = "Virtual FIFO Start", + [0x0001] = "Virtual FIFO End", +}; + +static const char * const smbus_event_id_str[] = { + [0x0000] = "Timeout Errors", + [0x0001] = "PEC Errors", + [0x0002] = "Arbitration loss", + [0x0003] = "NACK error", +}; + +static const char * const mctp_event_id_str[] = { + [0x0000] = "Dropped Packet", + [0x0001] = "Dropped Message", + [0x0002] = "Discovery and addressing errors", + [0x0003] = "Error Status", +}; + +#define EVENT_IDS(t) { t, ARRAY_SIZE(t) } + +static const struct { + const char * const *str; + size_t count; +} spec_event_id_str[] = { + [TIME_STAMP_CLASS_TYPE] = EVENT_IDS(timestamp_event_id_str), + [PCIE_CLASS_TYPE] = EVENT_IDS(pcie_event_id_str), + [NVME_CLASS_TYPE] = EVENT_IDS(nvme_event_id_str), + [RESET_CLASS_TYPE] = EVENT_IDS(reset_event_id_str), + [BOOT_SEQUENCE_CLASS_TYPE] = EVENT_IDS(boot_sequence_event_id_str), + [FIRMWARE_ASSERT_CLASS_TYPE] = EVENT_IDS(firmware_assert_event_id_str), + [TEMPERATURE_CLASS_TYPE] = EVENT_IDS(temperature_event_id_str), + [MEDIA_CLASS_TYPE] = EVENT_IDS(media_event_id_str), + [MEDIA_WEAR_CLASS_TYPE] = EVENT_IDS(media_wear_event_id_str), + [VIRTUAL_FIFO_EVENT_CLASS_TYPE] = EVENT_IDS(virtual_fifo_event_id_str), + [SMBUS_I2C_I3C_EVENT_CLASS_TYPE] = EVENT_IDS(smbus_event_id_str), + [MCTP_EVENT_CLASS_TYPE] = EVENT_IDS(mctp_event_id_str), +}; + struct request_data host_log_page_header[] = { { "LogIdentifier", 1 }, { "Reserved1", 4 }, @@ -499,6 +618,25 @@ static int ocp_event_ascii_string(__le64 start, __le64 size, int identifier, return -1; } +static int ocp_spec_event_id_string(int identifier, int debug_event_class, + char *description) +{ + const char *str; + + if (debug_event_class < 0 || + debug_event_class >= (int)ARRAY_SIZE(spec_event_id_str) || + identifier < 0 || + identifier >= (int)spec_event_id_str[debug_event_class].count) + return -1; + + str = spec_event_id_str[debug_event_class].str[identifier]; + if (!str) + return -1; + + strcpy(description, str); + return 0; +} + int get_event_id_ascii_string(int identifier, int debug_event_class, char *description) { struct nvme_ocp_telemetry_string_header *pocp_ts_header = @@ -507,8 +645,13 @@ int get_event_id_ascii_string(int identifier, int debug_event_class, char *descr if (!pstring_buffer) return -1; - return ocp_event_ascii_string(pocp_ts_header->ests, pocp_ts_header->estsz, identifier, - debug_event_class, description); + if (!ocp_event_ascii_string(pocp_ts_header->ests, pocp_ts_header->estsz, + identifier, debug_event_class, description)) + return 0; + + /* TELSLG-14: the String Log may omit Event IDs the spec defines. */ + return ocp_spec_event_id_string(identifier, debug_event_class, + description); } int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description) diff --git a/tests/cli/ocp/ocp_internal_log_event_fifo_mock_test.py b/tests/cli/ocp/ocp_internal_log_event_fifo_mock_test.py index a0d94f58d8..810ba2dd0e 100644 --- a/tests/cli/ocp/ocp_internal_log_event_fifo_mock_test.py +++ b/tests/cli/ocp/ocp_internal_log_event_fifo_mock_test.py @@ -46,6 +46,11 @@ * Event String and VU Event String lookups match on (class, identifier), not identifier alone, for every class, and come from the VU table from class 80h up. + * An Event ID the spec defines for classes 01h-0Dh is named with the + spec's name when the Event String Table has no entry for it + (TELSLG-14); a string log entry wins, and reserved, obsolete and + vendor unique IDs, VU Event Identifiers and classes 80h up stay + unnamed. * A string log table or ASCII string that does not lie within the string log leaves the event unnamed rather than being read. * An event of any class that runs past the end of its FIFO being @@ -94,7 +99,12 @@ def one_fifo(*events, da=1, **kwargs): **kwargs) -def common(cls, event_id, size_dw, string=''): +def common(cls, event_id, size_dw, string=None): + """@string defaults to the spec's name for the Event ID, which the + decoder falls back to when the string log has none.""" + if string is None: + string = (layout.SPEC_EVENT_NAMES.get((cls, event_id), '') + if cls < layout.CLASS_VU_FIRST else '') return { 'Debug Event Class type': f'0x{cls:x}', 'Event Identifier': f'0x{event_id:x}', @@ -103,7 +113,7 @@ def common(cls, event_id, size_dw, string=''): } -def virtual_fifo(fifo_id, event_id=0, size_dw=1, event_string='', +def virtual_fifo(fifo_id, event_id=0, size_dw=1, event_string=None, vfifo_string='', phys_string=''): physical, virtual = layout.split_virtual_fifo_id(fifo_id) return { @@ -159,7 +169,7 @@ def vu_fields(vu_id, data, string=''): } -def smbus(event_id, event_data, data_string='', size_dw=1, event_string='', +def smbus(event_id, event_data, data_string='', size_dw=1, event_string=None, vu=None): return { **common(layout.CLASS_SMBUS_I2C_I3C, event_id, size_dw, event_string), @@ -171,7 +181,7 @@ def smbus(event_id, event_data, data_string='', size_dw=1, event_string='', def mctp(event_id, event_data, data_string='', protocol=0, protocol_string=MCTP_PROTOCOLS[0], header=None, size_dw=2, - event_string='', vu=None): + event_string=None, vu=None): """@header is the expected MCTP Transport Header, or None when the Transport Header Valid flag is clear and no header is printed.""" fields = { @@ -738,6 +748,117 @@ def test_event_string_is_keyed_on_class_and_identifier(self): 'Class Specific Data': '0000000000000000'}, ]) + @staticmethod + def _smallest_event(cls, event_id, event_string=None): + """The smallest event of @cls carrying @event_id, and its decode + with Event String @event_string.""" + if cls == layout.CLASS_VIRTUAL_FIFO: + fifo_id = layout.virtual_fifo_id(1, 0) + return (layout.virtual_fifo_event(fifo_id, event_id), + virtual_fifo(fifo_id, event_id=event_id, + event_string=event_string, + phys_string=PHYS_NAMES[1])) + if cls == layout.CLASS_SMBUS_I2C_I3C: + nack = event_id == layout.SMBUS_NACK_ERROR + return (layout.smbus_event(event_id, 0), + smbus(event_id, 0, SMBUS_NACK_DATA[0] if nack else '', + event_string=event_string)) + if cls == layout.CLASS_MCTP: + return (layout.mctp_event(event_id, 0), + mctp(event_id, 0, MCTP_EVENT_DATA.get(event_id, [''])[0], + event_string=event_string)) + size = layout.CLASS_SPECIFIC_SIZE.get(cls, 0) + data = bytes(range(0x10, 0x10 + size)) + expected = common(cls, event_id, size // 4, event_string) + if size: + expected['Class Specific Data'] = layout.hex_upper(data) + return layout.event(cls, event_id, data), expected + + def _assert_smallest_events(self, names, log=None): + """One smallest event per (class, Event ID) of @names, each + decoding with its Event String from @names.""" + pairs = [self._smallest_event(cls, event_id, name) + for (cls, event_id), name in names.items()] + self.assert_events(one_fifo(*(event for event, _ in pairs)), + log or strings(), + [expected for _, expected in pairs]) + + def test_spec_defined_event_ids_are_named_without_the_string_log(self): + """TELSLG-14 lets the string log leave out the Event IDs the spec + defines, so with no Event String Table entry every one of them is + named from the spec.""" + self._assert_smallest_events(dict(sorted( + layout.SPEC_EVENT_NAMES.items()))) + + def test_spec_names_follow_the_spec_wording(self): + """One name per class, spelled as in OCP 2.7 Section 4.9.15.2.""" + self._assert_smallest_events({ + (layout.CLASS_TIMESTAMP, 0x0001): + 'Firmware initiated Timestamp Snapshot', + (layout.CLASS_PCIE, 0x0007): 'Link Speed and Width Negotiated', + (layout.CLASS_NVME, 0x0010): + 'Error Reporting AEN was sent (see EREC-5)', + (layout.CLASS_RESET, 0x0002): 'PERST#', + (layout.CLASS_BOOT_SEQUENCE, 0x0000): + 'Main Firmware Boot Complete', + (layout.CLASS_FIRMWARE_ASSERT, 0x0006): 'Assert in Other Code', + (layout.CLASS_TEMPERATURE, 0x0000): + 'Composite Temperature decreases to (WCTEMP - 2)', + (layout.CLASS_MEDIA, 0x0005): 'Plane Failure Event', + (layout.CLASS_MEDIA_WEAR, 0x0000): 'Media Wear', + (layout.CLASS_VIRTUAL_FIFO, 0x0001): 'Virtual FIFO End', + (layout.CLASS_SMBUS_I2C_I3C, 0x0003): 'NACK error', + (layout.CLASS_MCTP, 0x0000): 'Dropped Packet', + }) + + def test_string_log_names_take_precedence_over_spec_names(self): + """An Event String Table entry names a spec-defined Event ID; the + spec's name only fills in for the IDs the table leaves out.""" + custom = {(layout.CLASS_PCIE, 0x0000): 'CUSTOM LINK UP', + (layout.CLASS_SMBUS_I2C_I3C, 0x0003): 'CUSTOM NACK'} + self._assert_smallest_events( + {**custom, (layout.CLASS_PCIE, 0x0001): 'Link Down'}, + strings(event_strings=custom)) + + def test_reserved_and_vendor_unique_event_ids_stay_unnamed(self): + """Reserved, obsolete and vendor unique Event IDs of the spec's + classes have no name to fall back to, nor does any Event ID of a + reserved or vendor unique class.""" + self._assert_smallest_events({ + (layout.CLASS_TIMESTAMP, 0x0002): '', + (layout.CLASS_PCIE, 0x0008): '', + (layout.CLASS_NVME, 0x0004): '', + (layout.CLASS_NVME, 0x0011): '', + (layout.CLASS_RESET, 0x7FFF): '', + (layout.CLASS_MEDIA, 0x8000): '', + (layout.CLASS_MEDIA_WEAR, 0x0001): '', + (layout.CLASS_VIRTUAL_FIFO, 0x0002): '', + (layout.CLASS_SMBUS_I2C_I3C, 0x0004): '', + (layout.CLASS_MCTP, 0xFFFF): '', + }) + self.assert_events( + one_fifo(layout.event(0x0E, 0x0000), layout.event(0x80, 0x0000)), + strings(), [ + {**common(0x0E, 0x0000, 0), 'Class Specific Data': ''}, + {**common(0x80, 0x0000, 0), 'VU Data': ''}, + ]) + + def test_vu_identifiers_get_no_spec_names(self): + """A VU Event Identifier or a VU Virtual FIFO Identifier of 0000h + is vendor defined, even where Event ID 0000h of the same class has + a spec name.""" + fifo_id = layout.virtual_fifo_id(0, 0) + telemetry = one_fifo( + layout.event(layout.CLASS_RESET, 0x0000, + layout.vu_part(0x0000, bytes.fromhex('1122'))), + layout.virtual_fifo_event(fifo_id, 0x0000)) + self.assert_events(telemetry, strings(), [ + {**common(layout.CLASS_RESET, 0x0000, 1, + 'PCIe Conventional Hot Reset'), + **vu_fields(0x0000, bytes.fromhex('1122'))}, + virtual_fifo(fifo_id, event_string='Virtual FIFO Start'), + ]) + def _snapshot_fifo(self): stat = layout.statistic(0x22, b'12345678', behavior=2, nsid=3, ns_valid=True) @@ -1184,6 +1305,25 @@ def test_ascii_string_running_past_the_ascii_table(self): length_offset = vu_ests * layout.DWORD + 3 self._assert_names({length_offset: bytes([200])}, vfifo_string='') + def test_unusable_entry_leaves_a_spec_event_id_its_spec_name(self): + """An entry skipped for a bad ASCII offset names nothing, so a + spec-defined Event ID still gets its spec name.""" + event_strings = {(layout.CLASS_PCIE, 0x0000): 'CUSTOM LINK UP'} + ests = struct.unpack_from('= layout.CLASS_VU_FIRST: + return tables.vu_events.get(key, "") + return tables.events.get(key, layout.SPEC_EVENT_NAMES.get(key, "")) + + class TestOCPInternalLog(TestOCP): """Verify that ocp internal-log decodes the drive's telemetry logs.""" @@ -196,10 +206,7 @@ def _event_mismatches(event, decoded, tables, names): "Debug Event Class type": f"0x{event.cls:x}", "Event Identifier": f"0x{event.event_id:x}", "Event Data Size": f"0x{event.size_dw:x}", - "Event String": (tables.vu_events - if event.cls >= layout.CLASS_VU_FIRST - else tables.events).get( - (event.cls, event.event_id), ""), + "Event String": _expected_event_string(event, tables), } if event.cls == layout.CLASS_VIRTUAL_FIFO and len(event.data) >= 2: fifo_id, = struct.unpack_from(" StringTables: """Decode the three identifier tables. Where an identifier repeats, - the first entry wins, matching a front-to-back table search.""" - ascii_start = _u(strings, STR_ASCTS, 8) * DWORD - - def text(length: int, offset_dw: int) -> str: - start = ascii_start + offset_dw * DWORD - raw = strings[start:start + min(length + 1, MAX_ASCII_LEN)] + the first usable entry wins, matching a front-to-back table search; + an entry whose string falls outside the ASCII table, or whose ASCII + table falls outside the log, is skipped as the decoder skips it.""" + ascts = _u(strings, STR_ASCTS, 8) + asctsz = _u(strings, STR_ASCTSZ, 8) + log_dw = len(strings) // DWORD + ascii_fits = ascts <= log_dw and asctsz <= log_dw - ascts + + def text(length: int, offset_dw: int) -> Optional[str]: + copy_len = min(length + 1, MAX_ASCII_LEN) + if (not ascii_fits or offset_dw > asctsz + or copy_len > (asctsz - offset_dw) * DWORD): + return None + start = (ascts + offset_dw) * DWORD + raw = strings[start:start + copy_len] return raw.split(b'\0', 1)[0].decode('ascii', 'replace') def entries(start_field, size_field, fmt): + """(first field, second field, string) of each usable entry.""" start = _u(strings, start_field, 8) * DWORD count = _u(strings, size_field, 8) * DWORD // STR_ENTRY_SIZE for i in range(count): - yield struct.unpack_from(fmt, strings, start + i * STR_ENTRY_SIZE) + first, second, length, offset_dw, _ = struct.unpack_from( + fmt, strings, start + i * STR_ENTRY_SIZE) + name = text(length, offset_dw) + if name is not None: + yield first, second, name stats: Dict[int, str] = {} - for stat_id, _, length, offset_dw, _ in entries(STR_SITS, STR_SITSZ, - _SITS_ENTRY): - stats.setdefault(stat_id, text(length, offset_dw)) + for stat_id, _, name in entries(STR_SITS, STR_SITSZ, _SITS_ENTRY): + stats.setdefault(stat_id, name) events: Dict[Tuple[int, int], str] = {} - for cls, ident, length, offset_dw, _ in entries(STR_ESTS, STR_ESTSZ, - _EST_ENTRY): - events.setdefault((cls, ident), text(length, offset_dw)) + for cls, ident, name in entries(STR_ESTS, STR_ESTSZ, _EST_ENTRY): + events.setdefault((cls, ident), name) vu_events: Dict[Tuple[int, int], str] = {} - for cls, ident, length, offset_dw, _ in entries(STR_VU_ESTS, STR_VU_ESTSZ, - _EST_ENTRY): - vu_events.setdefault((cls, ident), text(length, offset_dw)) + for cls, ident, name in entries(STR_VU_ESTS, STR_VU_ESTSZ, _EST_ENTRY): + vu_events.setdefault((cls, ident), name) return StringTables(stats, events, vu_events) From 3d6ce3e2824bdf538ae7539e3889143cef208051 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 17:38:53 +0000 Subject: [PATCH 068/263] plugin: split handle_plugin() into small functions handle_plugin() did all of the dispatching in one function: the global options, --help and --version, the command lookup with its exact and unique prefix matches, the extensions and the "-" form, with the usage setup duplicated for the two kinds of match. Split it into find_command(), run_command(), find_extension(), run_joined_command() and run_help(), so handle_plugin() reads from top to bottom. No functional change: the dispatch test passes, and 32 invocations of help, version, exact, prefix, joined and invalid commands print the same and exit the same before and after. Signed-off-by: Daniel Wagner --- src/plugin.c | 231 ++++++++++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 104 deletions(-) diff --git a/src/plugin.c b/src/plugin.c index 30c2d7060d..813c8822a9 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -388,27 +388,127 @@ void command_set_output_formats(struct command *cmd) cmd->output_format_desc); } -int handle_plugin(int argc, char **argv, struct plugin *plugin) +/* + * The command @name selects: an exact name or alias, otherwise the only + * command whose name starts with @name. + */ +static struct command *find_command(struct plugin *plugin, const char *name) +{ + struct command **cmd, *match = NULL; + size_t len = strlen(name); + bool unique = false; + + for (cmd = plugin->commands; *cmd; cmd++) { + if (!strcmp(name, (*cmd)->name) || + ((*cmd)->alias && !strcmp(name, (*cmd)->alias))) + return *cmd; + } + + for (cmd = plugin->commands; *cmd; cmd++) { + if (strncmp(name, (*cmd)->name, len)) + continue; + unique = !match; + match = *cmd; + } + + return unique ? match : NULL; +} + +static int run_command(int argc, char **argv, struct plugin *plugin, + struct command *cmd) { char use[0x100]; + + build_command_usage(use, sizeof(use), plugin->parent, plugin, cmd); + argconfig_append_usage(use); + command_set_output_formats(cmd); + + return cmd->fn(argc, argv, cmd, plugin); +} + +static struct plugin *find_extension(struct plugin *plugin, const char *name) +{ struct plugin *extension; - struct program *prog = plugin->parent; - struct command **cmd = plugin->commands; - struct command *cr = NULL; - bool opt_help = false, opt_version = false; - bool cr_valid = false; + + for (extension = plugin->next; extension; extension = extension->next) { + if (!strcmp(name, extension->name)) + return extension; + } + + return NULL; +} + +/* + * "nvme -": argv[0] starts with the name of an extension. + * Run the rest as a command of that extension. + */ +static int run_joined_command(int argc, char **argv, struct plugin *plugin) +{ + __cleanup_free char **sub_argv = NULL; + __cleanup_free char *name = NULL; + struct plugin *extension; + + for (extension = plugin->next; extension; extension = extension->next) { + if (!strncmp(argv[0], extension->name, strlen(extension->name))) + break; + } + if (!extension) { + fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", argv[0]); + return -ENOTTY; + } + + sub_argv = malloc((argc + 1) * sizeof(*sub_argv)); + name = strdup(extension->name); + if (!sub_argv || !name) + return -ENOMEM; + + argv[0] += strlen(extension->name); + while (*argv[0] == '-') + argv[0]++; + + sub_argv[0] = name; + memcpy(&sub_argv[1], argv, argc * sizeof(*argv)); + + return handle_plugin(argc + 1, sub_argv, extension); +} + +/* "nvme --help " is "nvme help " */ +static int run_help(int argc, char **argv, struct plugin *plugin) +{ + __cleanup_free char **help_argv = NULL; + __cleanup_free char *help_name = NULL; + + if (argc <= 0) { + general_help(plugin, NULL); + return 0; + } + + help_argv = malloc((argc + 1) * sizeof(*help_argv)); + help_name = strdup("help"); + if (!help_argv || !help_name) + return -ENOMEM; + + help_argv[0] = help_name; + memcpy(&help_argv[1], argv, argc * sizeof(*argv)); + + return help(argc + 1, help_argv, plugin); +} + +/* + * argv[0] is the program or plugin name. The global options come first, + * then the command of @plugin, or an extension and its command. + */ +int handle_plugin(int argc, char **argv, struct plugin *plugin) +{ + struct plugin *extension; + struct command *cmd; int err, argind; - char *str; if (!argc) { general_help(plugin, NULL); return 0; } - /* - * look for global options before the sub command parser and - * pre-fill the global nvme_args variable. - */ /* * -h and --help show the help of the command which follows, if any, * -V and --version replace the command. @@ -418,35 +518,13 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) general_help(plugin, NULL); return err; } - opt_help = err & ARGCONFIG_HELP; - opt_version = err & ARGCONFIG_VERSION; argc -= argind; argv += argind; - if (opt_help) { - __cleanup_free char **help_argv = NULL; - __cleanup_free char *help_name = NULL; - - if (argc <= 0) { - general_help(plugin, NULL); - return 0; - } - - help_argv = malloc((argc + 1) * sizeof(*help_argv)); - if (!help_argv) - return -ENOMEM; - - help_name = strdup("help"); - if (!help_name) - return -ENOMEM; - - help_argv[0] = help_name; - memcpy(&help_argv[1], argv, argc * sizeof(*argv)); - return help(argc + 1, help_argv, plugin); - } - - if (opt_version) + if (err & ARGCONFIG_HELP) + return run_help(argc, argv, plugin); + if (err & ARGCONFIG_VERSION) return version_cmd(plugin); if (!argc) { @@ -454,81 +532,26 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) return 0; } - str = argv[0]; - - if (!strcmp(str, "help")) + if (!strcmp(argv[0], "help")) return help(argc, argv, plugin); - if (!strcmp(str, "version")) + if (!strcmp(argv[0], "version")) return version_cmd(plugin); - while (*cmd) { - if (!strcmp(str, (*cmd)->name) || - ((*cmd)->alias && !strcmp(str, (*cmd)->alias))) { - build_command_usage(use, sizeof(use), prog, plugin, *cmd); - argconfig_append_usage(use); - command_set_output_formats(*cmd); - return (*cmd)->fn(argc, argv, *cmd, plugin); - } - if (!strncmp(str, (*cmd)->name, strlen(str))) { - if (cr) { - cr_valid = false; - } else { - cr = *cmd; - cr_valid = true; - } - } - cmd++; - } - - if (cr && cr_valid) { - build_command_usage(use, sizeof(use), prog, plugin, cr); - argconfig_append_usage(use); - command_set_output_formats(cr); - return cr->fn(argc, argv, cr, plugin); - } + cmd = find_command(plugin, argv[0]); + if (cmd) + return run_command(argc, argv, plugin, cmd); - /* Check extensions only if this is running the built-in plugin */ + /* Only the built-in plugin has extensions */ if (plugin->name) { - fprintf(stderr, "ERROR: Invalid sub-command '%s' for plugin %s\n", str, plugin->name); + fprintf(stderr, + "ERROR: Invalid sub-command '%s' for plugin %s\n", + argv[0], plugin->name); return -ENOTTY; } - extension = plugin->next; - while (extension) { - if (!strcmp(str, extension->name)) - return handle_plugin(argc, argv, extension); - extension = extension->next; - } + extension = find_extension(plugin, argv[0]); + if (extension) + return handle_plugin(argc, argv, extension); - /* - * If the command is executed with the extension name and - * command together ("plugin-command"), run the plug in - */ - extension = plugin->next; - while (extension) { - if (!strncmp(str, extension->name, strlen(extension->name))) { - __cleanup_free char **sub_argv = NULL; - __cleanup_free char *name_copy = NULL; - - sub_argv = malloc((argc + 1) * sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - argv[0] += strlen(extension->name); - while (*argv[0] == '-') - argv[0]++; - - name_copy = strdup(extension->name); - if (!name_copy) - return -ENOMEM; - - sub_argv[0] = name_copy; - memcpy(&sub_argv[1], argv, argc * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, extension); - } - extension = extension->next; - } - fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", str); - return -ENOTTY; + return run_joined_command(argc, argv, plugin); } From 74e35e10c08a924861bb95091863c9aa957d7784 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 17:40:24 +0000 Subject: [PATCH 069/263] plugins: return the result of the option parsing unchanged Most commands return what parse_args() or parse_and_open() returned. Some don't: - zns returns errno instead, so the exit status of a failed parse depends on whatever errno happens to be; - seagate, transcend, toshiba, virtium and ymtc print a message of their own and some replace the error with -1 or -EINVAL. The parser or parse_and_open() already reported what went wrong, so this adds a second message, and "Device not found" is wrong for an unknown option; - ibm, netapp and wdc only return a negative result and would carry on with a positive one. Return the result unchanged like the other commands. Signed-off-by: Daniel Wagner --- plugins/ibm/ibm-nvme.c | 2 +- plugins/netapp/netapp-nvme.c | 4 ++-- plugins/seagate/seagate-nvme.c | 36 ++++++++++-------------------- plugins/toshiba/toshiba-nvme.c | 18 +++++---------- plugins/transcend/transcend-nvme.c | 12 ++++------ plugins/virtium/virtium-nvme.c | 8 ++----- plugins/wdc/wdc-nvme.c | 2 +- plugins/ymtc/ymtc-nvme.c | 4 +--- plugins/zns/zns.c | 20 ++++++++--------- 9 files changed, 39 insertions(+), 67 deletions(-) diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 3fdfed8dd4..7c34414530 100644 --- a/plugins/ibm/ibm-nvme.c +++ b/plugins/ibm/ibm-nvme.c @@ -375,7 +375,7 @@ static int get_ibm_vpd_log(int argc, char **argv, struct command *cmd, struct pl OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err < 0) + if (err) return err; memset(&vpd_log, 0, sizeof(vpd_log)); diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 0c8d9fbce9..568c98917b 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -972,7 +972,7 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, NVME_ARGS(opts); ret = parse_args(argc, argv, desc, opts); - if (ret < 0) + if (ret) return ret; ret = nvme_create_global_ctx(&ctx); @@ -1069,7 +1069,7 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, NVME_ARGS(opts); ret = parse_args(argc, argv, desc, opts); - if (ret < 0) + if (ret) return ret; ret = nvme_create_global_ctx(&ctx); diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 615684d44c..066bea719f 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -910,10 +910,8 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; flags = nvme_args.output_flags; @@ -1086,10 +1084,8 @@ static int temp_stats(int argc, char **argv, struct command *acmd, struct plugin NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; flags = nvme_args.output_flags; @@ -1245,10 +1241,8 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *acmd, struct NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; flags = nvme_args.output_flags; @@ -1378,10 +1372,8 @@ static int stx_vs_fw_activate_history(int argc, char **argv, struct command *acm NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err < 0) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; flags = nvme_args.output_flags; @@ -1435,10 +1427,8 @@ static int clear_fw_activate_history(int argc, char **argv, struct command *acmd OPT_FLAG("save", 's', &cfg.save, save)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err < 0) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; nvme_init_identify_ctrl(&cmd, &ctrl); err = libnvme_exec_admin_passthru(hdl, &cmd); @@ -1495,10 +1485,8 @@ static int vs_clr_pcie_correctable_errs(int argc, char **argv, struct command *a OPT_FLAG("save", 's', &cfg.save, save)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; nvme_init_identify_ctrl(&cmd, &ctrl); diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index b4b6be9622..7ccd3fc1ac 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -434,10 +434,8 @@ static int vendor_log(int argc, char **argv, struct command *acmd, struct plugin OPT_UINT("log", 'l', &cfg.log, log)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("%s: failed to parse arguments", __func__); - return -EINVAL; - } + if (err) + return err; if ((cfg.log != 0xC0) && (cfg.log != 0xCA)) { nvme_show_error("%s: invalid log page 0x%x - should be 0xC0 or 0xCA", __func__, cfg.log); @@ -480,10 +478,8 @@ static int internal_log(int argc, char **argv, struct command *acmd, struct plug OPT_FLAG("prev-log", 'p', &cfg.prev_log, prev_log)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("%s: failed to parse arguments", __func__); - return -EINVAL; - } + if (err) + return err; if (cfg.prev_log) printf("Getting previous log\n"); @@ -517,10 +513,8 @@ static int clear_correctable_errors(int argc, char **argv, struct command *acmd, NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("%s: failed to parse arguments", __func__); - return -EINVAL; - } + if (err) + return err; /* Check device supported */ err = nvme_get_sct_status(hdl, MASK_0 | MASK_1); diff --git a/plugins/transcend/transcend-nvme.c b/plugins/transcend/transcend-nvme.c index 6a8bcb8655..a6edb2f6d5 100644 --- a/plugins/transcend/transcend-nvme.c +++ b/plugins/transcend/transcend-nvme.c @@ -32,10 +32,8 @@ static int getHealthValue(int argc, char **argv, struct command *acmd, struct pl NVME_ARGS(opts); result = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (result) { - nvme_show_error("Device not found"); - return -1; - } + if (result) + return result; result = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log); if (!result) { printf("Transcend NVME heath value: "); @@ -65,10 +63,8 @@ static int getBadblock(int argc, char **argv, struct command *acmd, struct plugi NVME_ARGS(opts); result = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (result) { - nvme_show_error("Device not found"); - return -1; - } + if (result) + return result; memset(&nvmecmd, 0, sizeof(nvmecmd)); nvmecmd.opcode = OP_BAD_BLOCK; diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index f9db4c15ca..b4a0b26f11 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -984,10 +984,8 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, } err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Error parse and open (err = %d)", err); + if (err) return err; - } nvme_show_verbose_info("Running..."); nvme_show_verbose_info("Collecting data for device %s", path); @@ -1043,10 +1041,8 @@ static int vt_show_identify(int argc, char **argv, struct command *acmd, struct NVME_ARGS(opts); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Error parse and open (err = %d)", err); + if (err) return err; - } nvme_init_identify_ctrl(&cmd, &ctrl); ret = libnvme_exec_admin_passthru(hdl, &cmd); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 530d846e01..59d2af7fb3 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -12835,7 +12835,7 @@ int wdc_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (ret < 0) + if (ret) return ret; /* get capabilities */ diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index be02b86206..2dcca852c0 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -132,10 +132,8 @@ static int ymtc_parse_options(struct libnvme_global_ctx **ctx, int err; err = parse_and_open(ctx, hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("open"); + if (err) return err; - } if (model) { if (sscanf(argconfig_operand(0), "/dev/nvme%d", &idx) != 1) diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 88f761f7e1..b04ac86e18 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -152,7 +152,7 @@ static int id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *p err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; flags = nvme_args.output_flags; @@ -197,7 +197,7 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; flags = nvme_args.output_flags; if (cfg.vendor_specific) @@ -354,7 +354,7 @@ static int zone_mgmt_send(int argc, char **argv, struct command *acmd, struct pl err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -467,7 +467,7 @@ static int open_zone(int argc, char **argv, struct command *acmd, struct plugin err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -535,7 +535,7 @@ static int set_zone_desc(int argc, char **argv, struct command *acmd, struct plu err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -615,7 +615,7 @@ static int zrwa_flush_zone(int argc, char **argv, struct command *acmd, struct p err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -673,7 +673,7 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *acmd, struct pl err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.namespace_id) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); @@ -765,7 +765,7 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; flags = nvme_args.output_flags; if (cfg.verbose) @@ -934,7 +934,7 @@ static int zone_append(int argc, char **argv, struct command *acmd, struct plugi err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; if (!cfg.data_size) { nvme_show_error("Append size not provided"); @@ -1084,7 +1084,7 @@ static int changed_zone_list(int argc, char **argv, struct command *acmd, struct err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) - return errno; + return err; flags = nvme_args.output_flags; From 40a5c31ccc644d0bb584f97cfc25a1a00540cdf8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 15 Oct 2026 17:44:31 +0000 Subject: [PATCH 070/263] argconfig: print the requested help to stdout and exit 0 -h, --help and -? printed the help of a command to stderr and failed with -EINVAL, so 'nvme --help' exited 1, like a usage error. Print the help that was asked for to stdout, return ARGCONFIG_HELP so the command stops, and let main() exit 0. It checks argconfig_help_requested() rather than the return value, which a command may also use for an NVMe status. The help after a malformed option still goes to stderr with exit status 1. The headings are bold only on a terminal. The getopt reference of the differential test reported the help as an error; the test maps ARGCONFIG_HELP to that, and keeps its report apart from the help on stdout. Signed-off-by: Daniel Wagner --- src/argconfig.c | 69 ++++++++++++++++++++++------- src/argconfig.h | 1 + src/nvme.c | 4 ++ tests/cli/meson.build | 7 +++ tests/cli/nvme_usage_test.py | 48 ++++++++++++++++++++ tests/nvme_test.py | 5 ++- tests/unit/c/test-argconfig-diff.c | 40 ++++++++++++----- tests/unit/c/test-argconfig-parse.c | 10 ++--- 8 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 tests/cli/nvme_usage_test.py diff --git a/src/argconfig.c b/src/argconfig.c index cc5e79e25b..b6cbd69a85 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -81,7 +82,8 @@ void argconfig_append_usage(const char *str) append_usage_str = str; } -static void show_option(const struct argconfig_commandline_options *option) +static void show_option(FILE *out, + const struct argconfig_commandline_options *option) { char buffer[0x1000]; char *b = buffer; @@ -105,16 +107,26 @@ static void show_option(const struct argconfig_commandline_options *option) } b += sprintf(b, " ] "); - fprintf(stderr, "%s", buffer); + fprintf(out, "%s", buffer); if (option->help) { - shr_print_word_wrapped("--- ", 40, b - buffer, stderr); - shr_print_word_wrapped(option->help, 44, 44, stderr); + shr_print_word_wrapped("--- ", 40, b - buffer, out); + shr_print_word_wrapped(option->help, 44, 44, out); } - fprintf(stderr, "\n"); + fprintf(out, "\n"); +} + +/* A heading, in bold on a terminal */ +static void print_heading(FILE *out, const char *fmt, const char *text) +{ + bool tty = isatty(fileno(out)); + + fputs(tty ? "\033[1m" : "", out); + fprintf(out, fmt, text); + fputs(tty ? "\033[0m" : "", out); } /* A group header is printed before the first option which follows it */ -static void print_options(struct argconfig_commandline_options *s, +static void print_options(FILE *out, struct argconfig_commandline_options *s, const char *header) { bool header_printed = false; @@ -126,25 +138,42 @@ static void print_options(struct argconfig_commandline_options *s, continue; } if (!header_printed) { - fprintf(stderr, "\n\033[1m%s:\033[0m\n", header); + fputc('\n', out); + print_heading(out, "%s:", header); + fputc('\n', out); header_printed = true; } if (!s->hidden) - show_option(s); + show_option(out, s); } } +static void show_help(FILE *out, const char *program_desc, + struct argconfig_commandline_options *s) +{ + print_heading(out, "Usage: %s", append_usage_str); + fputs("\n\n", out); + + shr_print_word_wrapped(program_desc, 0, 0, out); + fputc('\n', out); + + print_options(out, s, "Options"); + print_options(out, argconfig_globals(s), "Global options"); +} + +/* The help after a usage error, on stderr */ void argconfig_print_help(const char *program_desc, struct argconfig_commandline_options *s) { - fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n", - append_usage_str); + show_help(stderr, program_desc, s); +} - shr_print_word_wrapped(program_desc, 0, 0, stderr); - fprintf(stderr, "\n"); +static bool help_requested; - print_options(s, "Options"); - print_options(argconfig_globals(s), "Global options"); +/* True once argconfig_parse() printed the help -h or --help asked for */ +bool argconfig_help_requested(void) +{ + return help_requested; } static int argconfig_error(char *type, const char *opt, const char *arg) @@ -630,8 +659,16 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, ret = argconfig_parse_args(&p); if (ret) { - /* A malformed option prints the help, a bad value does not */ - if (ret > 0) { + /* + * The help that was asked for goes to stdout, the one after a + * malformed option to stderr. A bad value prints no help. + * The command stops either way: it gets ARGCONFIG_HELP or an + * error. + */ + if (ret == ARGCONFIG_HELP) { + show_help(stdout, program_desc, options); + help_requested = true; + } else if (ret > 0) { argconfig_print_help(program_desc, options); ret = -EINVAL; } diff --git a/src/argconfig.h b/src/argconfig.h index 4c233f62ae..7a61048f8b 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -223,6 +223,7 @@ struct argconfig_commandline_options { void argconfig_append_usage(const char *str); void argconfig_print_help(const char *program_desc, struct argconfig_commandline_options *options); +bool argconfig_help_requested(void); int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_commandline_options *options); /* argconfig_parse_global(): -h or --help, -V or --version was given */ diff --git a/src/nvme.c b/src/nvme.c index d187faf022..8f9f028386 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -194,5 +194,9 @@ int main(int argc, char **argv) nvme_show_finish(); + /* the command stopped after printing the help it was asked for */ + if (argconfig_help_requested()) + return 0; + return err ? 1 : 0; } diff --git a/tests/cli/meson.build b/tests/cli/meson.build index a1e789b6dd..62f2f6fa3e 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -106,6 +106,13 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) + if 'id' in selected_plugins + test('nvme-cli - usage-cli', + python3, + args: [files('nvme_usage_test.py'), nvme_exe], + depends: nvme_exe) + endif + test('nvme-cli - dry-run-cli', python3, args: [files('nvme_dry_run_test.py'), nvme_exe, mock_nvme_lib], diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py new file mode 100644 index 0000000000..22de2e74fb --- /dev/null +++ b/tests/cli/nvme_usage_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE LLC +# +# Authors: Daniel Wagner +"""Tests for the help and the usage errors of the commands. They need no +device: the parsing stops before one would be opened. + +Usage: python3 nvme_usage_test.py +""" +import subprocess +import sys +import unittest + +_NVME_BIN = sys.argv[1] if len(sys.argv) > 1 else 'nvme' + + +def run(*args): + return subprocess.run([_NVME_BIN] + list(args), capture_output=True, + text=True, stdin=subprocess.DEVNULL, check=False) + + +class HelpTest(unittest.TestCase): + + def test_help_on_stdout_and_success(self): + for opt in ('--help', '-h', '-?'): + with self.subTest(opt=opt): + result = run('id', 'ctrl', opt) + self.assertEqual(result.returncode, 0) + self.assertIn('Usage: nvme id ctrl ', result.stdout) + self.assertNotIn('Usage:', result.stderr) + + def test_usage_error_on_stderr_and_failure(self): + result = run('id', 'ctrl', '--bogus') + self.assertEqual(result.returncode, 1) + self.assertIn("unrecognized option '--bogus'", result.stderr) + self.assertIn('Usage: nvme id ctrl ', result.stderr) + self.assertEqual(result.stdout, '') + + def test_no_escape_sequences_when_not_a_terminal(self): + result = run('id', 'ctrl', '--help') + self.assertNotIn('\033[', result.stdout) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]]) diff --git a/tests/nvme_test.py b/tests/nvme_test.py index aff98e45d7..711cc22e2b 100644 --- a/tests/nvme_test.py +++ b/tests/nvme_test.py @@ -69,8 +69,9 @@ def _probe_command(nvme_bin, command): probe = subprocess.run(f"{nvme_bin} {command} --help", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - # --help always exits non-zero whether or not the command exists, - # so check for the error message instead of returncode. nvme-cli + # Before 3.2, --help exited non-zero whether or not the command + # existed, so check for the error message instead of returncode. + # nvme-cli # prints it to stderr on current builds but to stdout on 2.16 and # earlier, so capture both into one stream rather than picking one. supported = b"Invalid sub-command" not in probe.stdout diff --git a/tests/unit/c/test-argconfig-diff.c b/tests/unit/c/test-argconfig-diff.c index 63935d994e..b2082049bb 100644 --- a/tests/unit/c/test-argconfig-diff.c +++ b/tests/unit/c/test-argconfig-diff.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,9 @@ struct global_cfg { uint32_t timeout; }; +/* The report; stdout gets the help argconfig prints */ +static FILE *report; + #define MAX_OPTS 32 #define MAX_ARGS 8 @@ -227,8 +231,8 @@ static bool filter_test(void) if (unsupported(tests[i].arg, tests[i].global) == tests[i].unsupported) continue; - printf("ERROR: filter: '%s' %s taken as unsupported\n", - tests[i].arg, tests[i].unsupported ? "not" : "wrongly"); + fprintf(report, "ERROR: filter: '%s' %s taken as unsupported\n", + tests[i].arg, tests[i].unsupported ? "not" : "wrongly"); ok = false; } @@ -280,6 +284,9 @@ static void run(char **args, int nargs, bool ref, bool global, r->operands[i] = operands[i]; } else { r->ret = argconfig_parse(nargs + 1, argv, "diff", opts); + /* the getopt parser reported the help as an error */ + if (r->ret == ARGCONFIG_HELP) + r->ret = -EINVAL; r->nr_operands = argconfig_nr_operands(); for (i = 0; !r->ret && i < r->nr_operands; i++) r->operands[i] = argconfig_operand(i); @@ -310,12 +317,13 @@ static bool compare(char **args, int nargs, bool global) if (!memcmp(&ref, &cur, sizeof(ref))) return true; - printf("ERROR: %s parse differs for:", global ? "global" : "command"); + fprintf(report, "ERROR: %s parse differs for:", + global ? "global" : "command"); for (i = 0; i < nargs; i++) - printf(" '%s'", args[i]); - printf("\n ret %d/%d argind %d/%d operands %d/%d\n", - ref.ret, cur.ret, ref.argind, cur.argind, - ref.nr_operands, cur.nr_operands); + fprintf(report, " '%s'", args[i]); + fprintf(report, "\n ret %d/%d argind %d/%d operands %d/%d\n", + ref.ret, cur.ret, ref.argind, cur.argind, + ref.nr_operands, cur.nr_operands); return false; } @@ -417,10 +425,16 @@ int main(void) setlocale(LC_NUMERIC, "C"); unsetenv("POSIXLY_CORRECT"); + report = fdopen(dup(STDOUT_FILENO), "w"); + if (!report || !freopen(shr_dev_null(), "w", stdout)) { + printf("ERROR: redirecting stdout failed: %s\n", + libnvme_strerror(errno)); + return EXIT_FAILURE; + } f = freopen(shr_dev_null(), "w", stderr); if (!f) { - printf("ERROR: reopening stderr failed: %s\n", - libnvme_strerror(errno)); + fprintf(report, "ERROR: reopening stderr failed: %s\n", + libnvme_strerror(errno)); return EXIT_FAILURE; } @@ -432,10 +446,12 @@ int main(void) ok &= random_cases(false, &nr_random); ok &= random_cases(true, &nr_random); - printf("%lu cases compared, %lu skipped\n", nr_cases, nr_skipped); - printf("three words: %lu of %u, random: %lu\n", - nr_three, 2 * n * n * n, nr_random); + fprintf(report, "%lu cases compared, %lu skipped\n", nr_cases, + nr_skipped); + fprintf(report, "three words: %lu of %u, random: %lu\n", + nr_three, 2 * n * n * n, nr_random); fclose(f); + fclose(report); return ok ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 78b8614dd4..9b3874f3ff 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -141,12 +141,12 @@ static struct toval_test toval_tests[] = { VAL_TEST("-b7", byte, 7, true, 0), VAL_TEST("-tstr", string, "str", false, 0), - /* Help requests short-circuit parsing and report failure so the - * caller doesn't proceed as if real options were parsed. + /* Help requests short-circuit parsing and return ARGCONFIG_HELP so + * the caller doesn't proceed as if real options were parsed. */ - { "--help", &cfg.flag, { .flag = false }, 0, -EINVAL }, - { "-h", &cfg.flag, { .flag = false }, 0, -EINVAL }, - { "-?", &cfg.flag, { .flag = false }, 0, -EINVAL }, + { "--help", &cfg.flag, { .flag = false }, 0, ARGCONFIG_HELP }, + { "-h", &cfg.flag, { .flag = false }, 0, ARGCONFIG_HELP }, + { "-?", &cfg.flag, { .flag = false }, 0, ARGCONFIG_HELP }, /* required_argument option with no value attached and no further * argv token to supply one. From 661e4779b900757567c54cdf696fc5651633b0be Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:25:11 +0000 Subject: [PATCH 071/263] nvme: check the device operand of every command Each command found its device itself: parse_and_open() took the first operand and failed with "Invalid argument" without one, a few commands read an optional one with their own checks, and every other operand was silently ignored. A command declared only whether its usage shows (no_device). Declare the operand in struct command instead: a device (the default), an optional device or none. parse_args() checks it, so a missing device and an extra operand are usage errors with the same message for every command, and keeps the device in nvme_args.device, which the commands use. The usage shows [] for an optional one. netapp smdevices and ontapdevices, list-subsys, show-topology and solidigm parse-telemetry-log take an optional device; the commands marked no_device take none. Their tests expected an extra operand to be ignored: micron plugin-version with a device, and discover with "--persistent force", which ran with mode "auto"; both are errors now. Signed-off-by: Daniel Wagner --- plugins/config/config-nvme.c | 10 +++--- plugins/exclusion/exclusion-nvme.c | 12 +++---- plugins/huawei/huawei-nvme.c | 2 +- plugins/keys/keys-plugin.c | 16 ++++----- plugins/micron/micron-nvme.c | 10 +++--- plugins/nbft/nbft-plugin.c | 2 +- plugins/netapp/netapp-nvme.c | 12 +++---- plugins/registry/registry-nvme.c | 6 ++-- plugins/seagate/seagate-nvme.c | 4 +-- plugins/solidigm/solidigm-nvme.c | 3 +- plugins/solidigm/solidigm-telemetry.c | 2 +- plugins/utils/command-metadata.c | 2 +- plugins/utils/utils.c | 2 +- plugins/ymtc/ymtc-nvme.c | 2 +- plugins/zns/zns.c | 2 +- src/args.c | 33 +++++++++++++++++++ src/args.h | 4 +++ src/global-ctx.c | 4 +-- src/nvme-cmds-deprecated.c | 10 +++--- src/nvme-cmds-enumerate.c | 16 ++++----- src/nvme-cmds-fabrics.c | 16 ++++----- src/nvme-cmds-io.c | 2 +- src/nvme-cmds-sanitize.c | 2 +- src/plugin.c | 22 ++++++++----- src/plugin.h | 12 +++++-- .../cli/micron/micron_plugin_version_test.py | 22 ++++++------- tests/cli/nvme_fabrics_mock_test.py | 15 +++++---- tests/cli/nvme_usage_test.py | 30 +++++++++++++++++ 28 files changed, 176 insertions(+), 99 deletions(-) diff --git a/plugins/config/config-nvme.c b/plugins/config/config-nvme.c index dee526e26b..92dab5e299 100644 --- a/plugins/config/config-nvme.c +++ b/plugins/config/config-nvme.c @@ -56,35 +56,35 @@ static struct command config_validate_cmd_cmd = { .name = "validate", .help = "Validate an NVMeoF connection configuration", .fn = config_validate_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command config_show_cmd_cmd = { .name = "show", .help = "Show the resolved NVMeoF connection configuration", .fn = config_show_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command config_status_cmd_cmd = { .name = "status", .help = "Report config status", .fn = config_status_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command config_convert_cmd_cmd = { .name = "convert", .help = "Convert config.json/discovery.conf to INI", .fn = config_convert_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command config_create_cmd_cmd = { .name = "create", .help = "Create an NVMeoF connection entry in the INI configuration", .fn = config_create_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/plugins/exclusion/exclusion-nvme.c b/plugins/exclusion/exclusion-nvme.c index f55c398428..a214409c85 100644 --- a/plugins/exclusion/exclusion-nvme.c +++ b/plugins/exclusion/exclusion-nvme.c @@ -613,42 +613,42 @@ static struct command excl_create_cmd = { .name = "create", .help = "Create an exclusion list", .fn = excl_create, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command excl_delete_cmd = { .name = "delete", .help = "Delete an exclusion list", .fn = excl_delete, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command excl_edit_cmd = { .name = "edit", .help = "Edit an exclusion list interactively", .fn = excl_edit, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command excl_list_cmd = { .name = "list", .help = "List exclusion lists or entries", .fn = excl_list, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command excl_add_cmd = { .name = "add", .help = "Add an entry to an exclusion list", .fn = excl_add, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command excl_remove_cmd = { .name = "remove", .help = "Remove an entry from an exclusion list", .fn = excl_remove, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index dcc2df63f5..4348820b2d 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -407,7 +407,7 @@ static struct command huawei_list_cmd = { .help = "List all Huawei NVMe devices and namespaces on machine", .fn = huawei_list, .output_formats = NORMAL | JSON, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command huawei_id_ctrl_cmd = { diff --git a/plugins/keys/keys-plugin.c b/plugins/keys/keys-plugin.c index a6187b85c6..9b58b736d2 100644 --- a/plugins/keys/keys-plugin.c +++ b/plugins/keys/keys-plugin.c @@ -1097,56 +1097,56 @@ static struct command gen_kxchap_cmd = { .name = "gen-kxchap-secret", .help = "Generate NVMeoF KX-HMAC-CHAP host secret", .fn = gen_kxchap, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command check_kxchap_cmd = { .name = "check-kxchap-secret", .help = "Validate NVMeoF KX-HMAC-CHAP host secret format or check if loaded", .fn = check_kxchap, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command gen_tls_cmd = { .name = "gen-tls-psk", .help = "Generate NVMeoF TLS PSK", .fn = gen_tls, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command check_tls_cmd = { .name = "check-tls-psk", .help = "Validate NVMeoF TLS PSK format or check if loaded", .fn = check_tls, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command insert_tls_cmd = { .name = "insert-tls-psk", .help = "Insert NVMeoF TLS PSK into a keyring", .fn = insert_tls, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command key_import_cmd = { .name = "import", .help = "Import NVMeoF TLS PSKs and KX-HMAC-CHAP secrets into a keyring", .fn = key_import, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command key_export_cmd = { .name = "export", .help = "Export NVMeoF TLS PSKs from a keyring", .fn = key_export, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command key_revoke_cmd = { .name = "revoke", .help = "Revoke an NVMeoF TLS PSK from a keyring", .fn = key_revoke, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index f8ea2eecfa..7d52c16156 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -4435,11 +4435,9 @@ static int micron_health_info(int argc, char **argv, struct command *acmd, } if (is_json) - print_micron_health_log_json(&smart_log, - argconfig_operand(0)); + print_micron_health_log_json(&smart_log, nvme_args.device); else - print_micron_health_log_normal(&smart_log, - argconfig_operand(0)); + print_micron_health_log_normal(&smart_log, nvme_args.device); return 0; } @@ -4572,14 +4570,14 @@ static struct command plugin_version_cmd = { .name = "plugin-version", .help = "Display plugin version info", .fn = micron_plugin_version, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command cloud_ssd_plugin_version_cmd = { .name = "cloud-SSD-plugin-version", .help = "Display plugin version info", .fn = micron_cloud_ssd_plugin_version, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command log_page_directory_cmd = { diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 4e100a8456..544c100ea8 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -701,7 +701,7 @@ static struct command show_nbft_cmd = { .name = "show", .help = "Show contents of ACPI NBFT tables", .fn = show_nbft, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 568c98917b..c4d24f855f 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -992,8 +992,8 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, return devs.num; } - if (argconfig_nr_operands()) - devname = basename(argconfig_operand(0)); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_num, nsid; @@ -1083,8 +1083,8 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, return -EINVAL; } - if (argconfig_nr_operands()) - devname = basename(argconfig_operand(0)); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_num, nsid; @@ -1156,7 +1156,7 @@ static struct command netapp_smdevices_cmd = { .fn = netapp_smdevices, .output_formats = OUTPUT_FORMATS_CUSTOM, .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, - .no_device = true, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command netapp_ontapdevices_cmd = { @@ -1165,7 +1165,7 @@ static struct command netapp_ontapdevices_cmd = { .fn = netapp_ontapdevices, .output_formats = OUTPUT_FORMATS_CUSTOM, .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, - .no_device = true, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command *commands[] = { diff --git a/plugins/registry/registry-nvme.c b/plugins/registry/registry-nvme.c index cfafce8088..8a9c028648 100644 --- a/plugins/registry/registry-nvme.c +++ b/plugins/registry/registry-nvme.c @@ -37,10 +37,10 @@ static char *get_device(int argc, char **argv) { char *device; - if (!argconfig_nr_operands()) + if (!nvme_args.device) return NULL; - device = argconfig_operand(0); + device = nvme_args.device; strip_dev_prefix(&device); return device; } @@ -256,7 +256,7 @@ static struct command registry_list_cmd = { .name = "list", .help = "List live registry entries", .fn = registry_list, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command registry_retrieve_cmd = { diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 066bea719f..d821b49464 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -1940,14 +1940,14 @@ static struct command seagate_plugin_version_cmd = { .name = "plugin-version", .help = "Shows Seagate plugin's version information ", .fn = seagate_plugin_version, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command stx_ocp_plugin_version_cmd = { .name = "cloud-SSD-plugin-version", .help = "Shows OCP Seagate plugin's version information ", .fn = stx_ocp_plugin_version, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c index c2dd0811b5..c619df4406 100644 --- a/plugins/solidigm/solidigm-nvme.c +++ b/plugins/solidigm/solidigm-nvme.c @@ -162,6 +162,7 @@ static struct command get_telemetry_log_cmd = { .name = "parse-telemetry-log", .help = "Parse Telemetry Log binary", .fn = get_telemetry_log, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command clear_pcie_correctable_error_counters_cmd = { @@ -205,7 +206,7 @@ static struct command get_cloud_SSDplugin_version_cmd = { .name = "cloud-SSDplugin-version", .help = "Prints plug-in OCP version", .fn = get_cloud_SSDplugin_version, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command get_workload_tracker_cmd = { diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 931d3db246..7750361296 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -89,7 +89,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *acmd, stru has_binary_file = argconfig_parse_seen(opts, "source-file"); if (has_binary_file) { // If a binary file is provided, we don't want to open a device. - if (argconfig_nr_operands()) { + if (nvme_args.device) { nvme_show_error( "Device path not allowed when using --source-file"); return -EINVAL; diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index a7a527e2dd..634d407b40 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -419,7 +419,7 @@ static int capture_command(struct command_metadata_command *mc, struct command * command_metadata_capture_error = 0; command_metadata_cur_command = mc; capture_current_command = cmd->name; - command_set_output_formats(cmd); + command_prepare(cmd); (void)cmd->fn(2, argv, cmd, plugin); capture_current_command = NULL; command_metadata_cur_command = NULL; diff --git a/plugins/utils/utils.c b/plugins/utils/utils.c index 472d4110a0..0820d11485 100644 --- a/plugins/utils/utils.c +++ b/plugins/utils/utils.c @@ -22,7 +22,7 @@ static struct command dump_command_metadata_cmd_cmd = { .name = "dump-command-metadata", .help = "Dump all commands and their options as JSON", .fn = dump_command_metadata_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; #endif /* CONFIG_JSONC */ diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index 2dcca852c0..8380070860 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -136,7 +136,7 @@ static int ymtc_parse_options(struct libnvme_global_ctx **ctx, return err; if (model) { - if (sscanf(argconfig_operand(0), "/dev/nvme%d", &idx) != 1) + if (sscanf(nvme_args.device, "/dev/nvme%d", &idx) != 1) idx = 0; *model = GetSSDModel(idx); } diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index b04ac86e18..e325b51982 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -1112,7 +1112,7 @@ static struct command list_cmd = { .help = "List all NVMe devices with Zoned Namespace Command Set support", .fn = list, .output_formats = OUTPUT_FORMATS_NORMAL, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command id_ctrl_cmd = { diff --git a/src/args.c b/src/args.c index 1d1dfc4b93..b165ecb4e4 100644 --- a/src/args.c +++ b/src/args.c @@ -14,6 +14,7 @@ #include "argconfig.h" #include "args.h" #include "nvme-print.h" +#include "plugin.h" struct nvme_args nvme_args = { .output_format = "normal", @@ -25,6 +26,34 @@ struct nvme_args nvme_args = { const char *uuid_index = "UUID index"; const char *namespace_id_desired = "identifier of desired namespace"; +/* + * The operand of the current command: the device, or nothing. A missing + * or an extra operand is a usage error. + */ +static int check_device(char *argv[], const char *desc, + struct argconfig_commandline_options *opts) +{ + int max = nvme_args.device_operand == COMMAND_NO_DEVICE ? 0 : 1; + int nr = argconfig_nr_operands(); + + if (nvme_args.device_operand == COMMAND_DEVICE && !nr) { + nvme_show_error("%s: missing ", argv[0]); + goto usage; + } + if (nr > max) { + nvme_show_error("%s: unexpected argument '%s'", argv[0], + argconfig_operand(max)); + goto usage; + } + + nvme_args.device = argconfig_operand(0); + return 0; + +usage: + argconfig_print_help(desc, opts); + return -EINVAL; +} + int parse_args(int argc, char *argv[], const char *desc, struct argconfig_commandline_options *opts) { @@ -34,6 +63,10 @@ int parse_args(int argc, char *argv[], const char *desc, if (ret) return ret; + ret = check_device(argv, desc, opts); + if (ret) + return ret; + if (nvme_args.supported_output_formats & OUTPUT_FORMATS_CUSTOM) { nvme_args.output_flags = NORMAL; } else { diff --git a/src/args.h b/src/args.h index 327fdcd7c9..d84b6c6188 100644 --- a/src/args.h +++ b/src/args.h @@ -28,6 +28,10 @@ struct nvme_args { /* --output-format of the current command, set by parse_args() */ nvme_print_flags_t output_flags; const char *set_options; + /* the operand of the current command, enum command_device */ + int device_operand; + /* the device it was given, set by parse_args(), or NULL */ + char *device; }; extern struct nvme_args nvme_args; diff --git a/src/global-ctx.c b/src/global-ctx.c index 4c6adf998b..dec21f88dd 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -27,7 +27,7 @@ static int check_arg_dev(int argc, char **argv) { - if (!argconfig_nr_operands()) { + if (!nvme_args.device) { errno = EINVAL; nvme_show_perror(argv[0]); return -EINVAL; @@ -46,7 +46,7 @@ static int get_transport_handle(struct libnvme_global_ctx *ctx, int argc, if (ret) return ret; - devname = argconfig_operand(0); + devname = nvme_args.device; ret = libnvme_open(ctx, devname, flags, hdl); if (ret) diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index d3734b5691..0384be9284 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -1482,7 +1482,7 @@ static struct command gen_dhchap_key_cmd = { .help = "Generate NVMeoF DH-HMAC-CHAP host secret (deprecated, use 'nvme keys gen-kxchap-secret')", .fn = gen_dhchap_key, .deprecated = true, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command check_dhchap_key_cmd = { @@ -1491,7 +1491,7 @@ static struct command check_dhchap_key_cmd = { "check-kxchap-secret')", .fn = check_dhchap_key, .deprecated = true, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command gen_tls_key_cmd = { @@ -1499,7 +1499,7 @@ static struct command gen_tls_key_cmd = { .help = "Generate NVMeoF TLS PSK (deprecated, use 'nvme keys gen-tls-psk')", .fn = gen_tls_key, .deprecated = true, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command check_tls_key_cmd = { @@ -1507,7 +1507,7 @@ static struct command check_tls_key_cmd = { .help = "Validate NVMeoF TLS PSK (deprecated, use 'nvme keys check-tls-psk')", .fn = check_tls_key, .deprecated = true, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command tls_key_cmd = { @@ -1515,7 +1515,7 @@ static struct command tls_key_cmd = { .help = "Manage NVMeoF TLS PSKs (deprecated, use 'nvme keys import/export/revoke')", .fn = tls_key, .deprecated = true, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; #endif /* CONFIG_FABRICS */ diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 6b5bd169f8..87ad494046 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -56,8 +56,8 @@ static int list_subsys(int argc, char **argv, struct command *acmd, return err; devname = NULL; - if (argconfig_nr_operands()) - devname = basename(argconfig_operand(0)); + if (nvme_args.device) + devname = basename(nvme_args.device); flags = nvme_args.output_flags; @@ -139,8 +139,8 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (argconfig_nr_operands()) - devname = basename(argconfig_operand(0)); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_id, nsid; @@ -244,7 +244,7 @@ static struct command list_cmd = { .help = "List all NVMe devices and namespaces on machine", .fn = list, .output_formats = NORMAL | JSON, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command list_subsys_cmd = { @@ -252,7 +252,7 @@ static struct command list_subsys_cmd = { .help = "List nvme subsystems", .fn = list_subsys, .output_formats = NORMAL | JSON, - .no_device = true, + .device = COMMAND_DEVICE_OPTIONAL, }; #ifdef CONFIG_TOP @@ -262,7 +262,7 @@ static struct command top_cmd = { .help = "nvme top", .fn = top, .output_formats = OUTPUT_FORMATS_NORMAL, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; #endif /* CONFIG_TOP */ @@ -272,7 +272,7 @@ static struct command show_topology_cmd_cmd = { .help = "Show the topology", .fn = show_topology_cmd, .output_formats = TOPOLOGY_OUTPUT_FORMATS, - .no_device = true, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command *commands[] = { diff --git a/src/nvme-cmds-fabrics.c b/src/nvme-cmds-fabrics.c index edf4f7629d..1b0d5840d6 100644 --- a/src/nvme-cmds-fabrics.c +++ b/src/nvme-cmds-fabrics.c @@ -170,56 +170,56 @@ static struct command discover_cmd_cmd = { .name = "discover", .help = "Discover NVMeoF subsystems", .fn = discover_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command connect_all_cmd_cmd = { .name = "connect-all", .help = "Discover and Connect to NVMeoF subsystems", .fn = connect_all_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command connect_cmd_cmd = { .name = "connect", .help = "Connect to NVMeoF subsystem", .fn = connect_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command disconnect_cmd_cmd = { .name = "disconnect", .help = "Disconnect from NVMeoF subsystem", .fn = disconnect_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command disconnect_all_cmd_cmd = { .name = "disconnect-all", .help = "Disconnect from all connected NVMeoF subsystems", .fn = disconnect_all_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command dim_cmd_cmd = { .name = "dim", .help = "Send Discovery Information Management command to a Discovery Controller", .fn = dim_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command gen_hostnqn_cmd_cmd = { .name = "gen-hostnqn", .help = "Generate NVMeoF host NQN", .fn = gen_hostnqn_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command show_hostnqn_cmd_cmd = { .name = "show-hostnqn", .help = "Show NVMeoF host NQN", .fn = show_hostnqn_cmd, - .no_device = true, + .device = COMMAND_NO_DEVICE, }; static struct command *commands[] = { diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 9988d76051..1c67d92678 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -1055,7 +1055,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char if (err) { if (err == -EBUSY) { nvme_show_error("Failed to open %s.", - basename(argconfig_operand(0))); + basename(nvme_args.device)); nvme_show_error("Namespace is currently busy."); if (!cfg.force) nvme_show_error( diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 5e6ed0bcd0..fff5bc0538 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -773,7 +773,7 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin if (err) { if (-err == EBUSY) { nvme_show_error("Failed to open %s.", - basename(argconfig_operand(0))); + basename(nvme_args.device)); nvme_show_error("Namespace is currently busy."); if (!cfg.force) nvme_show_error("Use the force [--force] option to ignore that."); diff --git a/src/plugin.c b/src/plugin.c index 813c8822a9..3a706a7508 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -137,13 +137,18 @@ static void usage_cmd(struct plugin *plugin) static void build_command_usage(char *use, size_t size, struct program *prog, struct plugin *plugin, struct command *cmd) { - const char *device = cmd->no_device ? "" : " "; + static const char * const device[] = { + [COMMAND_DEVICE] = " ", + [COMMAND_DEVICE_OPTIONAL] = " []", + [COMMAND_NO_DEVICE] = "", + }; if (!plugin->name) - snprintf(use, size, "%s %s%s [OPTIONS]", prog->name, cmd->name, device); + snprintf(use, size, "%s %s%s [OPTIONS]", prog->name, cmd->name, + device[cmd->device]); else - snprintf(use, size, "%s %s %s%s [OPTIONS]", prog->name, plugin->name, - cmd->name, device); + snprintf(use, size, "%s %s %s%s [OPTIONS]", prog->name, + plugin->name, cmd->name, device[cmd->device]); } void plugin_add_group(struct plugin *plugin, const char *title, @@ -199,7 +204,7 @@ void general_help(struct plugin *plugin, char *str) bool needs_device = false; for (i = 0; plugin->commands[i]; i++) { - if (!plugin->commands[i]->no_device) { + if (plugin->commands[i]->device != COMMAND_NO_DEVICE) { needs_device = true; break; } @@ -379,13 +384,14 @@ void general_help(struct plugin *plugin, char *str) } } -/* Apply the output formats a command supports before it runs */ -void command_set_output_formats(struct command *cmd) +/* Apply the output formats and the operand of a command before it runs */ +void command_prepare(struct command *cmd) { nvme_args_set_output_formats(cmd->output_formats ? cmd->output_formats : DEFAULT_OUTPUT_FORMATS, cmd->output_format_desc); + nvme_args.device_operand = cmd->device; } /* @@ -421,7 +427,7 @@ static int run_command(int argc, char **argv, struct plugin *plugin, build_command_usage(use, sizeof(use), plugin->parent, plugin, cmd); argconfig_append_usage(use); - command_set_output_formats(cmd); + command_prepare(cmd); return cmd->fn(argc, argv, cmd, plugin); } diff --git a/src/plugin.h b/src/plugin.h index e46aabbc76..f8d10fd18c 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -40,14 +40,20 @@ struct plugin { const char *group; }; +/* The operand of a command */ +enum command_device { + COMMAND_DEVICE, /* , the default */ + COMMAND_DEVICE_OPTIONAL, /* [] */ + COMMAND_NO_DEVICE, /* no operand */ +}; + struct command { char *name; char *help; int (*fn)(int argc, char **argv, struct command *acmd, struct plugin *plugin); char *alias; bool deprecated; - /* Set for commands that don't take a positional argument. */ - bool no_device; + enum command_device device; /* * The values --output-format accepts (enum nvme_print_flags), 0 for * the default ones, OUTPUT_FORMATS_NORMAL for only the normal output. @@ -68,7 +74,7 @@ struct command { extern struct plugin builtin; void general_help(struct plugin *plugin, char *str); -void command_set_output_formats(struct command *cmd); +void command_prepare(struct command *cmd); int handle_plugin(int argc, char **argv, struct plugin *plugin); void register_extension(struct plugin *plugin); diff --git a/tests/cli/micron/micron_plugin_version_test.py b/tests/cli/micron/micron_plugin_version_test.py index 93e9b86627..9387d8ecec 100644 --- a/tests/cli/micron/micron_plugin_version_test.py +++ b/tests/cli/micron/micron_plugin_version_test.py @@ -66,15 +66,13 @@ def run_version_check(self, command, device=None, args=()): f"stderr={result.stderr!r}") return result - def check_device_argument_ignored(self, command): - """A device argument must not change the output.""" - without = self.run_version_check(command).stdout - with_device = self.run_version_check(command, - device="/dev/nvme0").stdout + def check_device_argument_rejected(self, command): + """The command takes no device, a device argument is an error.""" + result = self.run_version(command, device="/dev/nvme0") - self.assertEqual(with_device, without, - f"micron {command} output changed when a device was " - f"supplied") + self.assertNotEqual(result.returncode, 0, + f"micron {command} accepted a device argument") + self.assertIn("unexpected argument '/dev/nvme0'", result.stderr) def check_help_omits_device(self, command): """--help must advertise no operand.""" @@ -118,11 +116,11 @@ def test_cloud_version_matches_the_plugin_version_prefix(self): self.assertEqual(cloud.group(1, 2), plugin.group(1, 2)) - def test_plugin_version_ignores_a_device_argument(self): - self.check_device_argument_ignored(_PLUGIN_VERSION) + def test_plugin_version_rejects_a_device_argument(self): + self.check_device_argument_rejected(_PLUGIN_VERSION) - def test_cloud_plugin_version_ignores_a_device_argument(self): - self.check_device_argument_ignored(_CLOUD_VERSION) + def test_cloud_plugin_version_rejects_a_device_argument(self): + self.check_device_argument_rejected(_CLOUD_VERSION) def test_plugin_version_help_omits_the_device_operand(self): self.check_help_omits_device(_PLUGIN_VERSION) diff --git a/tests/cli/nvme_fabrics_mock_test.py b/tests/cli/nvme_fabrics_mock_test.py index af9a7ce385..e365d5b1d1 100644 --- a/tests/cli/nvme_fabrics_mock_test.py +++ b/tests/cli/nvme_fabrics_mock_test.py @@ -781,16 +781,17 @@ def test_discover_persistent_force_ignores_epcsd(self): self._run('discover', '-t', 'tcp', '-a', addr, '--persistent=force') self._assert_persisted(self._DISCOVERY_INSTANCE) - def test_discover_persistent_separate_value_arg_is_dropped(self): - """A value given as a separate argv token (not glued with '=') is silently - dropped; --persistent is treated as bare (mode "auto"), not "force".""" + def test_discover_persistent_separate_value_arg_is_rejected(self): + """The optional value of --persistent has to be glued with '='. A + separate argv token is an operand, which discover does not take, so + it fails instead of silently running with mode "auto".""" addr = '192.168.10.7' self.server.discovery_entries = [self._self_entry(addr, eflags=0)] - # If "force" were actually parsed as the option's value this would persist; - # since it's dropped and auto degrades on EPCSD=0, it must disconnect. - self._run('discover', '-t', 'tcp', '-a', addr, '--persistent', 'force') - self._assert_disconnected(self._DISCOVERY_INSTANCE) + res = self._run('discover', '-t', 'tcp', '-a', addr, + '--persistent', 'force', expect_fail=True) + self.assertIn("unexpected argument 'force'", res.stderr) + self.assertEqual(self.server.controllers, {}) def test_connect_all_persistent_auto_epcsd_referral_hop(self): """Referral (non-self) Discovery Controller entries decide persistence diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 22de2e74fb..ab8d44eee2 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -44,5 +44,35 @@ def test_no_escape_sequences_when_not_a_terminal(self): self.assertNotIn('\033[', result.stdout) +class OperandTest(unittest.TestCase): + """A command takes a device, an optional one or none. A missing + device and extra operands are usage errors.""" + + def assert_usage_error(self, result, message): + self.assertEqual(result.returncode, 1) + self.assertIn(message, result.stderr) + self.assertIn('Usage:', result.stderr) + + def test_missing_device(self): + self.assert_usage_error(run('id', 'ctrl'), 'missing ') + + def test_extra_operand(self): + self.assert_usage_error(run('id', 'ctrl', '/dev/nvme0', 'extra'), + "unexpected argument 'extra'") + + def test_no_device_command(self): + result = run('list', '--help') + self.assertIn('Usage: nvme list [OPTIONS]', result.stdout) + self.assert_usage_error(run('list', 'extra'), + "unexpected argument 'extra'") + + def test_optional_device_command(self): + result = run('list-subsys', '--help') + self.assertIn('Usage: nvme list-subsys [] [OPTIONS]', + result.stdout) + self.assert_usage_error(run('list-subsys', 'nvme0', 'extra'), + "unexpected argument 'extra'") + + if __name__ == '__main__': unittest.main(argv=[sys.argv[0]]) From 06e6b942d46d687f1154ba929f8b10e855cdd44b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:28:44 +0000 Subject: [PATCH 072/263] utils: report the device operand in the command metadata The command metadata did not say whether a command takes a device, so the generated completions offered /dev/nvme* for every command. Add the "device" of every command, "required", "optional" or "none", as schema version 2. The completion generator requires version 2 and offers a device only to commands which take one. The test fixture has a command without a device, which the bash and zsh tests check. Signed-off-by: Daniel Wagner --- completions/generate-completions.py | 33 ++++++++++++++++--- completions/test-bash-completion.sh | 5 +++ completions/test-command-metadata.json | 8 ++++- completions/test-zsh-completion.sh | 5 +++ plugins/utils/command-metadata-schema.json | 7 +++- plugins/utils/command-metadata.c | 11 ++++++- plugins/utils/command-metadata.h | 1 + tests/unit/py/test_command_metadata_schema.py | 13 ++++++-- .../unit/py/test_generate_completions_args.py | 2 +- 9 files changed, 74 insertions(+), 11 deletions(-) diff --git a/completions/generate-completions.py b/completions/generate-completions.py index c468ed3b29..1ea645b426 100755 --- a/completions/generate-completions.py +++ b/completions/generate-completions.py @@ -20,7 +20,7 @@ # Metadata schema version this generator understands; reject anything else. -SUPPORTED_SCHEMA_VERSION = 1 +SUPPORTED_SCHEMA_VERSION = 2 # --------------------------------------------------------------------------- @@ -226,6 +226,7 @@ def sorted_options(opts): \tlocal completing_value=0 \tlocal wantfiles=0 +\tlocal nodevice=0 \t_nvme_detect_value_completion ''' @@ -269,6 +270,7 @@ def sorted_options(opts): \t\t\t[[ ${words[i]} == /dev/nvme* ]] && has_device=1 \t\tdone \t\tif [[ $nonopt_args -ge $2 ]] && [[ $has_device -eq 0 ]] && \\ +\t\t [[ $nodevice -eq 0 ]] && \\ \t\t [[ "$1" != "help" ]] && [[ "$1" != "version" ]]; then \t\t\topts="/dev/nvme* $opts" \t\tfi @@ -389,6 +391,11 @@ def bash_option_body(opts): return out +def takes_device(cmd): + """Whether the command takes a device operand, required or optional.""" + return cmd.get("device", "required") != "none" + + def emit_bash_command_group(out, commands, func, device_argpos): out.write(BASH_FUNC_PREAMBLE.format(func=func)) @@ -403,6 +410,8 @@ def emit_bash_command_group(out, commands, func, device_argpos): clauses = [] for c in sorted_commands(commands): clause_body = bash_option_body(sorted_options(command_options(c))) + if not takes_device(c): + clause_body += '\t\t\tnodevice=1\n' if not clause_body: continue label = "|".join(f'"{n}"' for n in cmd_names(c)) @@ -641,9 +650,12 @@ def emit_zsh_command_clause(out, tab, path, cmd): out.write(f'{tab}({"|".join(names)})\n') opts = sorted_options(command_options(cmd)) + device = f"{body}_nvme_has_device || compadd -- /dev/nvme*(N)\n" + if not takes_device(cmd): + device = "" if not opts: # No options to offer, but still complete a device argument. - out.write(f"{body}_nvme_has_device || compadd -- /dev/nvme*(N)\n") + out.write(device) out.write(f"{body};;\n\n") return @@ -665,7 +677,7 @@ def emit_zsh_command_clause(out, tab, path, cmd): out.write(f"{body}_arguments '*:: :->subcmds'\n") plabel = f"nvme {path + ' ' if path else ''}{name} options" out.write(f'{body}_nvme_describe "{plabel}" {var}\n') - out.write(f"{body}_nvme_has_device || compadd -- /dev/nvme*(N)\n") + out.write(device) out.write(f"{body};;\n\n") @@ -858,8 +870,9 @@ def generate_zsh(model, out): $results = @($script:NvmeOptions[$key] | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterName', $_) }) # Offer a device-path hint for commands that take a device argument - # (everything except help/version, which are handled above). - if ('/dev/nvme' -like "$wordToComplete*") { + # (help/version are handled above). + if ('/dev/nvme' -like "$wordToComplete*" -and + -not $script:NvmeNoDevice.ContainsKey($key)) { $results += [System.Management.Automation.CompletionResult]::new( '/dev/nvme', '/dev/nvme', 'ParameterValue', 'NVMe device (e.g. /dev/nvme0, /dev/nvme0n1)') } @@ -919,6 +932,16 @@ def generate_powershell(model, out): out.write(f" '{key}' = @({opts})\n") out.write("}\n") + out.write("\n$script:NvmeNoDevice = @{\n") + no_device = [ps_escape(n) for c in builtin_commands(model) + if not takes_device(c) for n in cmd_names(c)] + no_device += [ps_escape("%s %s" % (p["name"], n)) for p in plugins(model) + for c in p["commands"] if not takes_device(c) + for n in cmd_names(c)] + for key in sorted(no_device): + out.write(f" '{key}' = $true\n") + out.write("}\n") + out.write("\n$script:NvmeFileOptions = @{\n") file_entries = [] for c in builtin_commands(model): diff --git a/completions/test-bash-completion.sh b/completions/test-bash-completion.sh index 66120cb57c..1ef49da3f2 100755 --- a/completions/test-bash-completion.sh +++ b/completions/test-bash-completion.sh @@ -373,6 +373,11 @@ expect_device \ "an option-less plugin sub-command still injects a device" \ "nvme zeta-plug no-opt-sub " +# A command whose metadata says it takes no device offers none. +expect_no_device \ + "a command without a device operand offers none" \ + "nvme beta-plug local-sub " + expect_no_device \ "help takes no device" \ "nvme help " diff --git a/completions/test-command-metadata.json b/completions/test-command-metadata.json index bc5e81ea3c..82cca1df9c 100644 --- a/completions/test-command-metadata.json +++ b/completions/test-command-metadata.json @@ -1,11 +1,12 @@ { - "schema_version": 1, + "schema_version": 2, "name": "nvme", "version": "0.0-fixture", "description": "Synthetic fixture for the completion generator tests.", "commands": [ { "name": "zulu-cmd", + "device": "required", "alias": "alpha-alias", "description": "zulu builtin command", "options": [ @@ -47,11 +48,13 @@ }, { "name": "no-opt-cmd", + "device": "required", "description": "builtin command with no options", "options": [] }, { "name": "alpha-cmd", + "device": "optional", "description": "alpha builtin command", "options": [ { @@ -100,6 +103,7 @@ "commands": [ { "name": "report", + "device": "required", "alias": "rpt", "description": "report a thing", "options": [ @@ -136,6 +140,7 @@ }, { "name": "no-opt-sub", + "device": "required", "description": "sub-command with no options", "options": [] } @@ -147,6 +152,7 @@ "commands": [ { "name": "local-sub", + "device": "none", "description": "sub-command with local options but no globals", "options": [ { diff --git a/completions/test-zsh-completion.sh b/completions/test-zsh-completion.sh index 40c6a294c4..0d6c98cdd5 100755 --- a/completions/test-zsh-completion.sh +++ b/completions/test-zsh-completion.sh @@ -399,6 +399,11 @@ expect_device \ "nvme zeta-plug no-opt-sub " # help and version never offer a device. +# A command whose metadata says it takes no device offers none. +expect_no_device \ + "a command without a device operand offers none" \ + "nvme beta-plug local-sub " + expect_no_device \ "help takes no device" \ "nvme help " diff --git a/plugins/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index b1e3ea74eb..f3413eb09d 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -58,12 +58,17 @@ "command": { "type": "object", "additionalProperties": false, - "required": ["name", "options"], + "required": ["name", "device", "options"], "properties": { "name": { "type": "string", "description": "Command name as typed on the command line." }, + "device": { + "type": "string", + "enum": ["required", "optional", "none"], + "description": "Whether the command takes a device operand (since schema version 2)." + }, "alias": { "type": "string", "description": "Alternate name accepted for the command. Omitted if none." diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 634d407b40..f80afa76a7 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -56,7 +56,7 @@ * changes that keep existing keys stable do not require a bump. Consumers * should reject a major version they do not understand. */ -#define COMMAND_METADATA_SCHEMA_VERSION 1 +#define COMMAND_METADATA_SCHEMA_VERSION 2 /* * The command currently being captured; set by capture_command() @@ -406,6 +406,7 @@ static int capture_command(struct command_metadata_command *mc, struct command * mc->name = cmd->name; mc->alias = cmd->alias; mc->help = cmd->help; + mc->device = cmd->device; mc->captured = false; /* @@ -672,6 +673,13 @@ static void json_option(struct json_object *arr, const struct command_metadata_o } /* Build one command as a json object: name, alias, description, and options. */ +/* The device operand of a command, see enum command_device */ +static const char * const device_names[] = { + [COMMAND_DEVICE] = "required", + [COMMAND_DEVICE_OPTIONAL] = "optional", + [COMMAND_NO_DEVICE] = "none", +}; + static struct json_object *json_command(const struct command_metadata_command *c) { struct json_object *jc, *opts; @@ -684,6 +692,7 @@ static struct json_object *json_command(const struct command_metadata_command *c json_object_add_value_string(jc, "alias", c->alias); if (c->help) json_object_add_value_string(jc, "description", c->help); + json_object_add_value_string(jc, "device", device_names[c->device]); opts = json_create_array(); for (i = 0; i < c->num_options; i++) { diff --git a/plugins/utils/command-metadata.h b/plugins/utils/command-metadata.h index e24629b75b..4a480d9327 100644 --- a/plugins/utils/command-metadata.h +++ b/plugins/utils/command-metadata.h @@ -37,6 +37,7 @@ struct command_metadata_command { struct command_metadata_option *options; /* heap array */ size_t num_options; bool captured; /* hook fired -> options valid */ + int device; /* enum command_device */ }; struct command_metadata_plugin { diff --git a/tests/unit/py/test_command_metadata_schema.py b/tests/unit/py/test_command_metadata_schema.py index a47825ce31..bd02aa59d7 100644 --- a/tests/unit/py/test_command_metadata_schema.py +++ b/tests/unit/py/test_command_metadata_schema.py @@ -95,6 +95,8 @@ def test_schema_rejects_malformed(self): lambda d: d["commands"][0]["options"][0].pop("long"), lambda d: d["commands"][0]["options"][0].update(argument="bogus"), lambda d: d["commands"][0]["options"][0].update(surprise=1), + lambda d: d["commands"][0].pop("device"), + lambda d: d["commands"][0].update(device="bogus"), ] for mutate in cases: bad = copy.deepcopy(self.data) @@ -138,8 +140,15 @@ def test_output_is_deterministic(self): self.assertEqual(runs[0], runs[1]) def test_schema_version(self): - """schema_version is the expected 1; a change must be conscious.""" - self.assertEqual(self.data["schema_version"], 1) + """schema_version is the expected 2; a change must be conscious.""" + self.assertEqual(self.data["schema_version"], 2) + + def test_device_operand(self): + """Each command reports whether it takes a device.""" + device = {c["name"]: c["device"] for c in self.data["commands"]} + self.assertEqual(device["list"], "none") + self.assertEqual(device["list-subsys"], "optional") + self.assertEqual(device["format"], "required") def test_global_output_format_option(self): """The shared output-format option carries its constrained value set.""" diff --git a/tests/unit/py/test_generate_completions_args.py b/tests/unit/py/test_generate_completions_args.py index c9486158b7..977ab03ebe 100644 --- a/tests/unit/py/test_generate_completions_args.py +++ b/tests/unit/py/test_generate_completions_args.py @@ -27,7 +27,7 @@ # A model that passes the schema-version check; with no commands or plugins the # generator still emits a valid (if command-less) script. -MINIMAL_MODEL = json.dumps({"schema_version": 1}) +MINIMAL_MODEL = json.dumps({"schema_version": 2}) class GenerateCompletionsArgsTest(unittest.TestCase): From 650dc95363eb34424d3aa9cfcfd33b8d88a195f5 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:30:01 +0000 Subject: [PATCH 073/263] plugins/virtium: take the device from the parsed operand save-smart-to-vtview-log copied argv[1] as the device name before parsing the options, so with an option first it logged the option as the device, e.g. "--run-time=1". Use the device parse_args() found, which also drops the length check and the strcpy() of the copy. Signed-off-by: Daniel Wagner --- plugins/virtium/virtium-nvme.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index b4a0b26f11..e97ec0bfdb 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -943,7 +943,6 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, long remain_time = 0; long start_time = 0; long end_time = 0; - char path[256] = ""; char *desc = "Save SMART data into log file with format that is easy to analyze (comma delimited). Maximum log file will be 4K.\n\n" "Typical usages:\n\n" "Temperature characterization:\n" @@ -975,24 +974,17 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, vt_generate_vtview_log_file_name(vt_default_log_file_name); - if (argc >= 2) { - if (strlen(argv[1]) > sizeof(path) - 1) { - nvme_show_error("Filename too long"); - return -1; - } - strcpy(path, argv[1]); - } - err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; nvme_show_verbose_info("Running..."); - nvme_show_verbose_info("Collecting data for device %s", path); + nvme_show_verbose_info("Collecting data for device %s", + nvme_args.device); nvme_show_verbose_info("Running for %lf hour(s)", cfg.run_time_hrs); nvme_show_verbose_info("Logging SMART data for every %lf hour(s)", cfg.log_record_frequency_hrs); - ret = vt_update_vtview_log_header(hdl, path, &cfg); + ret = vt_update_vtview_log_header(hdl, nvme_args.device, &cfg); if (ret) return ret; @@ -1012,7 +1004,7 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, if (cur_time >= end_time) break; - ret = vt_add_entry_to_log(hdl, path, &cfg); + ret = vt_add_entry_to_log(hdl, nvme_args.device, &cfg); if (ret) { nvme_show_error("Cannot update driver log"); break; From 202fb598105b4c5229139ff19e2f73041b696849 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:31:54 +0000 Subject: [PATCH 074/263] plugin: let help find the command like the dispatcher "nvme help " showed the man page only for an exact command name, while "nvme " also runs a command by a unique prefix, and "nvme help " never found the command of a plugin. Look the command up with find_command() and descend into an extension like handle_plugin() does. Signed-off-by: Daniel Wagner --- src/plugin.c | 106 ++++++++++++++++++----------------- tests/cli/nvme_usage_test.py | 39 +++++++++++++ 2 files changed, 93 insertions(+), 52 deletions(-) diff --git a/src/plugin.c b/src/plugin.c index 3a706a7508..0540e97415 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -31,37 +31,77 @@ static int version_cmd(struct plugin *plugin) return 0; } +/* + * The command @name selects: an exact name or alias, otherwise the only + * command whose name starts with @name. + */ +static struct command *find_command(struct plugin *plugin, const char *name) +{ + struct command **cmd, *match = NULL; + size_t len = strlen(name); + bool unique = false; + + for (cmd = plugin->commands; *cmd; cmd++) { + if (!strcmp(name, (*cmd)->name) || + ((*cmd)->alias && !strcmp(name, (*cmd)->alias))) + return *cmd; + } + + for (cmd = plugin->commands; *cmd; cmd++) { + if (strncmp(name, (*cmd)->name, len)) + continue; + unique = !match; + match = *cmd; + } + + return unique ? match : NULL; +} + +static struct plugin *find_extension(struct plugin *plugin, const char *name) +{ + struct plugin *extension; + + for (extension = plugin->next; extension; extension = extension->next) { + if (!strcmp(name, extension->name)) + return extension; + } + + return NULL; +} + +/* + * "nvme help " shows the man page of the command "nvme " + * would run, also for "nvme help ". + */ static int help(int argc, char **argv, struct plugin *plugin) { char man[0x100]; struct program *prog = plugin->parent; - char *str; - int i; + struct plugin *extension; + struct command *cmd; if (argc == 1) { general_help(plugin, NULL); return 0; } - str = argv[1]; - for (i = 0; plugin->commands[i]; i++) { - struct command *command = plugin->commands[i]; - - if (strcmp(str, command->name)) - if (!command->alias || - (command->alias && strcmp(str, command->alias))) - continue; - + cmd = find_command(plugin, argv[1]); + if (cmd) { if (plugin->name) snprintf(man, sizeof(man), "%s-%s-%s", prog->name, - plugin->name, command->name); + plugin->name, cmd->name); else - snprintf(man, sizeof(man), "%s-%s", prog->name, command->name); + snprintf(man, sizeof(man), "%s-%s", prog->name, + cmd->name); if (execlp("man", "man", man, (char *)NULL)) perror(argv[1]); + } else if (!plugin->name) { + extension = find_extension(plugin, argv[1]); + if (extension) + return help(argc - 1, argv + 1, extension); } - general_help(plugin, str); + general_help(plugin, argv[1]); return 0; } @@ -394,32 +434,6 @@ void command_prepare(struct command *cmd) nvme_args.device_operand = cmd->device; } -/* - * The command @name selects: an exact name or alias, otherwise the only - * command whose name starts with @name. - */ -static struct command *find_command(struct plugin *plugin, const char *name) -{ - struct command **cmd, *match = NULL; - size_t len = strlen(name); - bool unique = false; - - for (cmd = plugin->commands; *cmd; cmd++) { - if (!strcmp(name, (*cmd)->name) || - ((*cmd)->alias && !strcmp(name, (*cmd)->alias))) - return *cmd; - } - - for (cmd = plugin->commands; *cmd; cmd++) { - if (strncmp(name, (*cmd)->name, len)) - continue; - unique = !match; - match = *cmd; - } - - return unique ? match : NULL; -} - static int run_command(int argc, char **argv, struct plugin *plugin, struct command *cmd) { @@ -432,18 +446,6 @@ static int run_command(int argc, char **argv, struct plugin *plugin, return cmd->fn(argc, argv, cmd, plugin); } -static struct plugin *find_extension(struct plugin *plugin, const char *name) -{ - struct plugin *extension; - - for (extension = plugin->next; extension; extension = extension->next) { - if (!strcmp(name, extension->name)) - return extension; - } - - return NULL; -} - /* * "nvme -": argv[0] starts with the name of an extension. * Run the rest as a command of that extension. diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index ab8d44eee2..f4e8090faa 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -10,8 +10,10 @@ Usage: python3 nvme_usage_test.py """ +import os import subprocess import sys +import tempfile import unittest _NVME_BIN = sys.argv[1] if len(sys.argv) > 1 else 'nvme' @@ -74,5 +76,42 @@ def test_optional_device_command(self): "unexpected argument 'extra'") +class HelpCommandTest(unittest.TestCase): + """'nvme help ' finds the command like the dispatcher does and + shows its man page. A fake man prints which page it was asked for.""" + + def setUp(self): + self.dir = tempfile.TemporaryDirectory() + man = os.path.join(self.dir.name, 'man') + with open(man, 'w', encoding='utf-8') as f: + f.write('#!/bin/sh\necho "MAN: $1"\n') + os.chmod(man, 0o755) + self.env = dict(os.environ) + self.env['PATH'] = self.dir.name + os.pathsep + self.env['PATH'] + + def tearDown(self): + self.dir.cleanup() + + def man_page(self, *args): + result = subprocess.run([_NVME_BIN] + list(args), capture_output=True, + text=True, stdin=subprocess.DEVNULL, + env=self.env, check=False) + return result.stdout.strip() + + def test_builtin_command(self): + self.assertEqual(self.man_page('help', 'list-subsys'), + 'MAN: nvme-list-subsys') + + def test_unique_prefix(self): + self.assertEqual(self.man_page('help', 'list-sub'), + 'MAN: nvme-list-subsys') + + def test_plugin_command(self): + self.assertEqual(self.man_page('help', 'id', 'ctrl'), + 'MAN: nvme-id-ctrl') + self.assertEqual(self.man_page('--help', 'id', 'ctrl'), + 'MAN: nvme-id-ctrl') + + if __name__ == '__main__': unittest.main(argv=[sys.argv[0]]) From 0b447fc126c62ed3664e33fab0af1dc63d1d35ec Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:32:23 +0000 Subject: [PATCH 075/263] completions: update the shell completions Regenerate the completions from schema version 2 of the command metadata: commands without a device operand no longer offer one. Signed-off-by: Daniel Wagner --- completions/_nvme | 44 ------------ completions/bash-nvme-completion.sh | 101 ++++++++++++++++++++++++++++ completions/nvme-completion.ps1 | 52 +++++++++++++- 3 files changed, 151 insertions(+), 46 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 0b3ddc6013..89aaff17c9 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -400,7 +400,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config convert options" _config_convert - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (create) @@ -480,7 +479,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config create options" _config_create - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (show) @@ -507,7 +505,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config show options" _config_show - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (status) @@ -531,7 +528,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config status options" _config_status - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (validate) @@ -558,7 +554,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config validate options" _config_validate - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -841,7 +836,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion add options" _exclusion_add - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (create) @@ -867,7 +861,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion create options" _exclusion_create - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (delete) @@ -893,7 +886,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion delete options" _exclusion_delete - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (edit) @@ -919,7 +911,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion edit options" _exclusion_edit - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (list) @@ -945,7 +936,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion list options" _exclusion_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (remove) @@ -971,7 +961,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion remove options" _exclusion_remove - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -2089,7 +2078,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme huawei list options" _huawei_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -3221,7 +3209,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys check-kxchap-secret options" _keys_check_kxchap_secret - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (check-tls-psk) @@ -3259,7 +3246,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys check-tls-psk options" _keys_check_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (export) @@ -3287,7 +3273,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys export options" _keys_export - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-kxchap-secret) @@ -3317,7 +3302,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys gen-kxchap-secret options" _keys_gen_kxchap_secret - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-tls-psk) @@ -3361,7 +3345,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys gen-tls-psk options" _keys_gen_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (import) @@ -3393,7 +3376,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys import options" _keys_import - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (insert-tls-psk) @@ -3433,7 +3415,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys insert-tls-psk options" _keys_insert_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (revoke) @@ -3463,7 +3444,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys revoke options" _keys_revoke - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -5277,7 +5257,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme micron cloud-SSD-plugin-version options" _micron_cloud_SSD_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (cloud-boot-SSD-version) @@ -5453,7 +5432,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme micron plugin-version options" _micron_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (select-download) @@ -5907,7 +5885,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme nbft show options" _nbft_show - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -7313,7 +7290,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme registry list options" _registry_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (retrieve) @@ -7919,7 +7895,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme seagate cloud-SSD-plugin-version options" _seagate_cloud_SSD_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (get-ctrl-tele) @@ -8001,7 +7976,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme seagate plugin-version options" _seagate_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (vs-fw-activate-history) @@ -9799,7 +9773,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme solidigm cloud-SSDplugin-version options" _solidigm_cloud_SSDplugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (garbage-collect-log) @@ -10411,7 +10384,6 @@ _nvme () { case ${words[2]} in (dump-command-metadata) - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -11768,7 +11740,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme zns list options" _zns_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (offline-zone) @@ -12381,7 +12352,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme check-dhchap-key options" _check_dhchap_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (check-tls-key) @@ -12419,7 +12389,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme check-tls-key options" _check_tls_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (cmdset-ind-id-ns) @@ -12599,7 +12568,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme connect options" _connect - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (connect-all) @@ -12690,7 +12658,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme connect-all options" _connect_all - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (copy) @@ -12940,7 +12907,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme dim options" _dim - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (dir-receive) @@ -13100,7 +13066,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme disconnect options" _disconnect - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (disconnect-all) @@ -13128,7 +13093,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme disconnect-all options" _disconnect_all - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (discover) @@ -13219,7 +13183,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme discover options" _discover - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (dispersed-ns-participating-nss-log) @@ -13635,7 +13598,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-dhchap-key options" _gen_dhchap_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-hostnqn) @@ -13659,7 +13621,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-hostnqn options" _gen_hostnqn - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-tls-key) @@ -13703,7 +13664,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-tls-key options" _gen_tls_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (get-feature) @@ -14371,7 +14331,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme list options" _list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (list-ctrl) @@ -15716,7 +15675,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme show-hostnqn options" _show_hostnqn - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (show-regs) @@ -15912,7 +15870,6 @@ _nvme () { ;; (tls-key) - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (top) @@ -15938,7 +15895,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme top options" _top - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (verify) diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index d420505cfd..7414439308 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -116,6 +116,7 @@ _nvme_finish_completion () { [[ ${words[i]} == /dev/nvme* ]] && has_device=1 done if [[ $nonopt_args -ge $2 ]] && [[ $has_device -eq 0 ]] && \ + [[ $nodevice -eq 0 ]] && \ [[ "$1" != "help" ]] && [[ "$1" != "version" ]]; then opts="/dev/nvme* $opts" fi @@ -154,6 +155,7 @@ nvme_list_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "admin-passthru") @@ -228,6 +230,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "check-tls-key") @@ -236,6 +239,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "cmdset-ind-id-ns") @@ -262,6 +266,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--devid-file" + nodevice=1 ;; "connect-all") @@ -271,6 +276,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--raw -r" + nodevice=1 ;; "copy") @@ -319,6 +325,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "dir-receive") @@ -344,6 +351,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "disconnect-all") @@ -352,6 +360,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "discover") @@ -361,6 +370,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--raw -r" + nodevice=1 ;; "dispersed-ns-participating-nss-log") @@ -467,6 +477,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "gen-hostnqn") @@ -475,6 +486,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "gen-tls-key") @@ -483,6 +495,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "get-feature") @@ -649,6 +662,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" + nodevice=1 ;; "list-ctrl") @@ -1001,6 +1015,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "show-regs") @@ -1060,12 +1075,17 @@ nvme_list_opts () { _nvme_opt_files "--output-file -O" ;; + "tls-key") + nodevice=1 + ;; + "top") opts+=" --delay= -d --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --delay -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" + nodevice=1 ;; "verify") @@ -1124,6 +1144,7 @@ plugin_amzn_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -1157,6 +1178,7 @@ plugin_config_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "convert") @@ -1166,6 +1188,7 @@ plugin_config_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" "--output -o" + nodevice=1 ;; "create") @@ -1175,6 +1198,7 @@ plugin_config_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--output" + nodevice=1 ;; "show") @@ -1184,6 +1208,7 @@ plugin_config_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" + nodevice=1 ;; "status") @@ -1192,6 +1217,7 @@ plugin_config_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "validate") @@ -1201,6 +1227,7 @@ plugin_config_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" _nvme_opt_files "--config -J" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1218,6 +1245,7 @@ plugin_dapustor_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "smart-log-add") @@ -1243,6 +1271,7 @@ plugin_dell_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -1268,6 +1297,7 @@ plugin_dera_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "smart-log-add"|"stat") @@ -1293,6 +1323,7 @@ plugin_dir_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "receive") @@ -1327,6 +1358,7 @@ plugin_exclusion_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "add") @@ -1335,6 +1367,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "create") @@ -1343,6 +1376,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "delete") @@ -1351,6 +1385,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "edit") @@ -1359,6 +1394,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "list") @@ -1367,6 +1403,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "remove") @@ -1375,6 +1412,7 @@ plugin_exclusion_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1392,6 +1430,7 @@ plugin_fdp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "configs") @@ -1473,6 +1512,7 @@ plugin_feat_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "arbitration") @@ -1662,6 +1702,7 @@ plugin_fw_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "commit"|"activate") @@ -1697,6 +1738,7 @@ plugin_huawei_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -1713,6 +1755,7 @@ plugin_huawei_opts () { _nvme_opt_vals "--output-format -o" "normal json" \ "--output-format-version" "1 2" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1730,6 +1773,7 @@ plugin_ibm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "crit-log") @@ -1771,6 +1815,7 @@ plugin_id_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ctrl") @@ -1932,6 +1977,7 @@ plugin_innogrit_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "get-cdump") @@ -1965,6 +2011,7 @@ plugin_inspur_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "nvme-vendor-log") @@ -1990,6 +2037,7 @@ plugin_intel_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -2072,6 +2120,7 @@ plugin_io_mgmt_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "recv") @@ -2107,6 +2156,7 @@ plugin_keys_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "check-kxchap-secret") @@ -2115,6 +2165,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "check-tls-psk") @@ -2123,6 +2174,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "export") @@ -2131,6 +2183,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "gen-kxchap-secret") @@ -2139,6 +2192,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "gen-tls-psk") @@ -2147,6 +2201,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "import") @@ -2155,6 +2210,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "insert-tls-psk") @@ -2163,6 +2219,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "revoke") @@ -2171,6 +2228,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -2188,6 +2246,7 @@ plugin_lm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "create-cdq") @@ -2266,6 +2325,7 @@ plugin_log_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ana") @@ -2549,6 +2609,7 @@ plugin_mangoboost_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -2574,6 +2635,7 @@ plugin_memblaze_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-error-log") @@ -2712,6 +2774,7 @@ plugin_micron_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") @@ -2736,6 +2799,7 @@ plugin_micron_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "cloud-boot-SSD-version") @@ -2792,6 +2856,7 @@ plugin_micron_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "select-download") @@ -2939,6 +3004,7 @@ plugin_nbft_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "show") @@ -2947,6 +3013,7 @@ plugin_nbft_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -2964,6 +3031,7 @@ plugin_netapp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ontapdevices") @@ -2997,6 +3065,7 @@ plugin_ns_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "attach") @@ -3054,6 +3123,7 @@ plugin_nvidia_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") @@ -3079,6 +3149,7 @@ plugin_nvme_mi_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "recv") @@ -3114,6 +3185,7 @@ plugin_ocp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") @@ -3385,6 +3457,7 @@ plugin_registry_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "delete") @@ -3401,6 +3474,7 @@ plugin_registry_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "retrieve") @@ -3434,6 +3508,7 @@ plugin_resv_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "acquire") @@ -3483,6 +3558,7 @@ plugin_rpmb_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "info") @@ -3561,6 +3637,7 @@ plugin_samsung_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "vs-internal-log") @@ -3587,6 +3664,7 @@ plugin_seagate_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") @@ -3611,6 +3689,7 @@ plugin_seagate_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "get-ctrl-tele") @@ -3635,6 +3714,7 @@ plugin_seagate_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "vs-fw-activate-history") @@ -3701,6 +3781,7 @@ plugin_security_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "recv") @@ -3735,6 +3816,7 @@ plugin_sed_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "discover"|"1") @@ -3800,6 +3882,7 @@ plugin_sfx_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "change-cap") @@ -3898,6 +3981,7 @@ plugin_shannon_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "get-additional-feature") @@ -3949,6 +4033,7 @@ plugin_sndk_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "capabilities") @@ -4192,6 +4277,7 @@ plugin_solidigm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") @@ -4216,6 +4302,7 @@ plugin_solidigm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" + nodevice=1 ;; "garbage-collect-log") @@ -4339,6 +4426,7 @@ plugin_ssstc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "smart-log-add") @@ -4364,6 +4452,7 @@ plugin_toshiba_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-pcie-correctable-errors") @@ -4407,6 +4496,7 @@ plugin_transcend_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "badblock") @@ -4440,7 +4530,13 @@ plugin_utils_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion + case "$1" in + "dump-command-metadata") + nodevice=1 + ;; + esac _nvme_finish_completion "$1" 3 } @@ -4456,6 +4552,7 @@ plugin_virtium_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "save-smart-to-vtview-log") @@ -4490,6 +4587,7 @@ plugin_wdc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "cap-diag") @@ -4811,6 +4909,7 @@ plugin_ymtc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "smart-log-add") @@ -4836,6 +4935,7 @@ plugin_zns_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "changed-zone-list") @@ -4884,6 +4984,7 @@ plugin_zns_opts () { _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" + nodevice=1 ;; "offline-zone") diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index a77a04dc94..9c2f208ac0 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -728,6 +728,53 @@ $script:NvmeOptions = @{ 'zns zrwa-flush-zone' = @('--dry-run', '--lba', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') } +$script:NvmeNoDevice = @{ + 'check-dhchap-key' = $true + 'check-tls-key' = $true + 'config convert' = $true + 'config create' = $true + 'config show' = $true + 'config status' = $true + 'config validate' = $true + 'connect' = $true + 'connect-all' = $true + 'dim' = $true + 'disconnect' = $true + 'disconnect-all' = $true + 'discover' = $true + 'exclusion add' = $true + 'exclusion create' = $true + 'exclusion delete' = $true + 'exclusion edit' = $true + 'exclusion list' = $true + 'exclusion remove' = $true + 'gen-dhchap-key' = $true + 'gen-hostnqn' = $true + 'gen-tls-key' = $true + 'huawei list' = $true + 'keys check-kxchap-secret' = $true + 'keys check-tls-psk' = $true + 'keys export' = $true + 'keys gen-kxchap-secret' = $true + 'keys gen-tls-psk' = $true + 'keys import' = $true + 'keys insert-tls-psk' = $true + 'keys revoke' = $true + 'list' = $true + 'micron cloud-SSD-plugin-version' = $true + 'micron plugin-version' = $true + 'nbft show' = $true + 'registry list' = $true + 'seagate cloud-SSD-plugin-version' = $true + 'seagate plugin-version' = $true + 'show-hostnqn' = $true + 'solidigm cloud-SSDplugin-version' = $true + 'tls-key' = $true + 'top' = $true + 'utils dump-command-metadata' = $true + 'zns list' = $true +} + $script:NvmeFileOptions = @{ 'admin-passthru --input-file' = $true 'admin-passthru --metadata' = $true @@ -2553,8 +2600,9 @@ Register-ArgumentCompleter -Native -CommandName nvme -ScriptBlock { $results = @($script:NvmeOptions[$key] | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterName', $_) }) # Offer a device-path hint for commands that take a device argument - # (everything except help/version, which are handled above). - if ('/dev/nvme' -like "$wordToComplete*") { + # (help/version are handled above). + if ('/dev/nvme' -like "$wordToComplete*" -and + -not $script:NvmeNoDevice.ContainsKey($key)) { $results += [System.Management.Automation.CompletionResult]::new( '/dev/nvme', '/dev/nvme', 'ParameterValue', 'NVMe device (e.g. /dev/nvme0, /dev/nvme0n1)') } From d6391391e9b3bde5bdd4ce226c13674c1e6d3259 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 07:32:32 +0000 Subject: [PATCH 076/263] NEWS: document the help, operand and completion changes --help exits 0, every command checks its device operand, help finds commands like the dispatcher, and the completions offer a device only where a command takes one. Signed-off-by: Daniel Wagner --- NEWS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NEWS.md b/NEWS.md index 914713bbfb..37b97085e0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,16 @@ it opens the device. Before, the commands which don't use the output format accepted any value. +* `nvme --help` (also `-h` and `-?`) prints the help to stdout + and exits with status 0. Before, it printed to stderr and exited + with 1, like a usage error, which still does. + +* Every command checks its operand: a command which takes a device + fails without one, and an extra operand is an error. Before, extra + operands were silently ignored, e.g. a device given to a command which + takes none, or the value of `--persistent force`, which has to be + given as `--persistent=force`. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. @@ -112,6 +122,14 @@ ### nvme-cli +* `nvme help ` finds the command like `nvme ` does, + also by a unique prefix, and `nvme help ` shows the + man page of a plugin's command. + +* The shell completions offer a device only to commands which take + one. The command metadata (`nvme utils dump-command-metadata`) reports + the device operand of each command as schema version 2. + * The global options (`--verbose`, `--output-format`, `--dry-run`, ...) are accepted by every command. `config validate`, `config status`, `config convert`, `intel lat-stats-tracking`, `memblaze lat-stats`, From 406071d6b8894974c0681973d351f6a6b251a1f1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:42:19 +0000 Subject: [PATCH 077/263] scripts: drop line length warnings for the generated completions The shell completions are generated and their lines are as long as the generator makes them. A new option value can move existing text to a longer continuation line, which checkpatch then reports as added. Signed-off-by: Daniel Wagner --- scripts/checkpatch-filter.pl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/checkpatch-filter.pl b/scripts/checkpatch-filter.pl index 3c55d346b1..cb674129b1 100755 --- a/scripts/checkpatch-filter.pl +++ b/scripts/checkpatch-filter.pl @@ -12,6 +12,10 @@ # suggests __func__. With __func__, checkpatch reports "Unnecessary # ftrace-like logging". No form passes, so drop the first warning for # files in a tests/ directory. +# +# The shell completions in completions/ are generated. Their lines are +# as long as the generator makes them, so drop line length warnings for +# them. use strict; use warnings; @@ -35,6 +39,14 @@ next if $file =~ m{^#\d+: FILE: (?:.*/)?tests/}; $line .= $file . $code . $blank; } + if ($line =~ /^WARNING: line length of \d+ exceeds/) { + my $file = // ''; + my $code = // ''; + my $blank = // ''; + + next if $file =~ m{^#\d+: FILE: completions/}; + $line .= $file . $code . $blank; + } $found = 1 if $line =~ /^(ERROR|WARNING):/; print $line; } From e6ac541f6503e2bc16d51dd42c3a0e720d175710 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:05:56 +0000 Subject: [PATCH 078/263] argconfig: look up a given option by its variable argconfig_parse_seen() finds an option by its long name. A misspelled name compiles and silently reports "not given", and an alias of the option, which stores to the same variable, is not seen. Add argconfig_given(), which takes the variable the option stores to. A misspelled variable does not compile, and any option bound to it counts, also among the global options. Signed-off-by: Daniel Wagner --- src/argconfig.c | 21 ++++++++++++++++ src/argconfig.h | 2 ++ tests/unit/c/test-argconfig-parse.c | 38 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/src/argconfig.c b/src/argconfig.c index b6cbd69a85..9d37603a84 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -720,6 +720,27 @@ int argconfig_parse_global(int argc, char *argv[], (p.version ? ARGCONFIG_VERSION : 0); } +/* + * True if an option which stores to @value was given, in the table or in + * the global options. Aliases share the variable, so any of them counts. + */ +bool argconfig_given(struct argconfig_commandline_options *s, + const void *value) +{ + struct argconfig_commandline_options *g = argconfig_globals(s); + bool given = false; + + if (!value) + return false; + + for (; s && s->option; s++) + given |= s->default_value == value && s->seen; + for (; g && g->option; g++) + given |= g->default_value == value && g->seen; + + return given; +} + bool argconfig_parse_seen(struct argconfig_commandline_options *s, const char *option) { diff --git a/src/argconfig.h b/src/argconfig.h index 7a61048f8b..8728817146 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -244,5 +244,7 @@ typedef int (*argconfig_parse_hook_fn)(int argc, char *argv[], struct argconfig_commandline_options *options); void argconfig_set_parse_hook(argconfig_parse_hook_fn hook); +bool argconfig_given(struct argconfig_commandline_options *options, + const void *value); bool argconfig_parse_seen(struct argconfig_commandline_options *options, const char *option); diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 9b3874f3ff..d75a4ad9f4 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -443,6 +443,43 @@ static void global_options_test(void) argconfig_set_global_options(NULL); } +/* + * argconfig_given() finds an option by the variable it stores to, also an + * alias of it or a global option, and NULL is never given. + */ +static void given_test(void) +{ + const char *desc = "Test given"; + int verbose = 0; + bool alias = false, quiet = false; + char **argv = (char *[]){ "prog", "--old-flag", "-v", NULL }; + int ret; + + OPT_ARGS(globals) = { + OPT_INCR("verbose", 'v', &verbose, "verbose"), + OPT_FLAG("quiet", 'q', &quiet, "quiet"), + OPT_END() + }; + OPT_ARGS(opts) = { + OPT_FLAG("flag", 'f', &alias, "flag"), + OPT_FLAG("old-flag", 0, &alias, NULL, NULL, true), + OPT_UINT("uint", 'u', &cfg.uint, "uint"), + OPT_FLAG("seen-only", 0, NULL, "seen only"), + OPT_END() + }; + + argconfig_set_global_options(globals); + ret = argconfig_parse(argv_len(argv), argv, desc, opts); + if (ret || !argconfig_given(opts, &alias) || + argconfig_given(opts, &cfg.uint) || + !argconfig_given(opts, &verbose) || + argconfig_given(opts, &quiet) || argconfig_given(opts, NULL)) { + printf("ERROR: given: unexpected result\n"); + test_rc = 1; + } + argconfig_set_global_options(NULL); +} + #define COMMA_SEP_ARRAY_MAX_VALUES 4 struct comma_sep_array_test { @@ -834,6 +871,7 @@ int main(void) combined_opts_test(); operands_test(); global_options_test(); + given_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) comma_sep_array_test(&comma_sep_array_tests[i]); From 92987a277d6030eac9935e3a23cc8186a6ea5a27 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:05:56 +0000 Subject: [PATCH 079/263] treewide: ask whether an option was given by its variable Replace argconfig_parse_seen(opts, "name") with argconfig_given(opts, &variable). Most calls were converted with a script which looks the name up in the option table of the function, the NVMF_ARGS and FEAT_ARGS tables or the global options. Options which were only ever checked with argconfig_parse_seen() had no variable. They get one: the --save flag of the feat commands a field in their configuration, the ocp flags (--no-uuid, --all, --all-ns, --save, --enable) a local bool, and a flag's value is then simply its variable. Helpers which got the option table only to check a few options get the result instead: num_queues_set(), enable_ieee1667_silo_set() and nvmf_resolve_persistent_arg(); the register helpers take the configuration. Signed-off-by: Daniel Wagner --- plugins/fdp/fdp.c | 2 +- plugins/feat/feat-nvme.c | 166 +++++++++++-------- plugins/ns/ns-plugin.c | 2 +- plugins/nvme-mi/nvme-mi-plugin.c | 2 +- plugins/ocp/ocp-clear-features.c | 10 +- plugins/ocp/ocp-nvme.c | 132 +++++++++------ plugins/ocp/ocp-smart-extended-log.c | 5 +- plugins/solidigm/solidigm-telemetry.c | 8 +- plugins/solidigm/solidigm-workload-tracker.c | 2 +- src/config-create.c | 5 +- src/fabrics.c | 12 +- src/fabrics.h | 8 +- src/nvme-cmds-deprecated.c | 10 +- src/nvme-cmds-feature.c | 6 +- src/nvme-cmds-io.c | 4 +- src/nvme-cmds-passthru.c | 2 +- src/nvme-cmds-registers.c | 66 ++++---- 17 files changed, 247 insertions(+), 195 deletions(-) diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index a3df3c232e..aabf8bad22 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -525,7 +525,7 @@ static int fdp_feature(int argc, char **argv, struct command *acmd, struct plugi if (err) return err; - enabling_conf_idx = argconfig_parse_seen(opts, "enable-conf-idx"); + enabling_conf_idx = argconfig_given(opts, &cfg.fdpcidx); if (enabling_conf_idx && cfg.disable) { nvme_show_error("Cannot enable and disable at the same time"); return -EINVAL; diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index ac0c8ca2da..6d96e5b12d 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -39,7 +39,7 @@ #define FEAT_ARGS_SAVE(n, save_short, ...) \ NVME_ARGS(n, ##__VA_ARGS__, \ - OPT_FLAG("save", save_short, NULL, save), \ + OPT_FLAG("save", save_short, &cfg.save, save), \ OPT_BYTE("sel", 'S', &cfg.sel, sel)) #define FEAT_ARGS(n, ...) FEAT_ARGS_SAVE(n, 's', ##__VA_ARGS__) @@ -56,6 +56,7 @@ struct perfc_config { __u16 attrl; char *vs_data; __u8 sel; + bool save; }; struct temp_thresh_config { @@ -64,6 +65,7 @@ struct temp_thresh_config { __u8 thsel; __u8 tmpthh; __u8 sel; + bool save; }; struct arbitration_config { @@ -72,12 +74,14 @@ struct arbitration_config { __u8 mpw; __u8 hpw; __u8 sel; + bool save; }; struct err_recovery_config { __u8 tler; __u8 dulbe; __u8 sel; + bool save; }; struct host_behavior_config { @@ -87,6 +91,7 @@ struct host_behavior_config { __u8 hdisns; __u16 cdfe; __u8 sel; + bool save; }; struct lba_range_type_config { @@ -99,28 +104,33 @@ struct lba_range_type_config { char *nlbs; char *guids; __u8 sel; + bool save; }; struct int_coalesce_config { __u8 thr; __u8 time; __u8 sel; + bool save; }; struct int_vec_conf { __u16 iv; bool cd; __u8 sel; + bool save; }; struct write_atom_normal_config { bool dn; __u8 sel; + bool save; }; struct keep_alive_timer_config { __u32 kato; __u8 sel; + bool save; }; static const char *power_mgmt_feat = "power management feature"; @@ -224,6 +234,7 @@ static int feat_power_mgmt(int argc, char **argv, struct command *acmd, struct p __u8 ps; __u8 wh; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -236,9 +247,9 @@ static int feat_power_mgmt(int argc, char **argv, struct command *acmd, struct p if (err) return err; - if (argconfig_parse_seen(opts, "ps")) + if (argconfig_given(opts, &cfg.ps)) err = power_mgmt_set(hdl, fid, cfg.ps, cfg.wh, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, power_mgmt_feat); @@ -330,11 +341,11 @@ static int feat_perfc(int argc, char **argv, struct command *acmd, struct plugin cdw11 = NVME_SET(cfg.attri, FEAT_PERFC_ATTRI) | NVME_SET(cfg.rvspa, FEAT_PERFC_RVSPA); - if (argconfig_parse_seen(opts, "rvspa") || - argconfig_parse_seen(opts, "r4karl") || - argconfig_parse_seen(opts, "paid")) + if (argconfig_given(opts, &cfg.rvspa) || + argconfig_given(opts, &cfg.r4karl) || + argconfig_given(opts, &cfg.paid)) err = perfc_set(hdl, fid, cdw11, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, cdw11, cfg.sel, 0, perfc_feat); @@ -375,6 +386,7 @@ static int feat_hctm(int argc, char **argv, struct command *acmd, struct plugin __u16 tmt1; __u16 tmt2; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -387,10 +399,10 @@ static int feat_hctm(int argc, char **argv, struct command *acmd, struct plugin if (err) return err; - if (argconfig_parse_seen(opts, "tmt1") || - argconfig_parse_seen(opts, "tmt2")) + if (argconfig_given(opts, &cfg.tmt1) || + argconfig_given(opts, &cfg.tmt2)) err = hctm_set(hdl, fid, cfg.tmt1, cfg.tmt2, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, hctm_feat); @@ -433,6 +445,7 @@ static int feat_timestamp(int argc, char **argv, struct command *acmd, struct pl struct config { __u64 tstmp; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -443,9 +456,9 @@ static int feat_timestamp(int argc, char **argv, struct command *acmd, struct pl if (err) return err; - if (argconfig_parse_seen(opts, "tstmp")) + if (argconfig_given(opts, &cfg.tstmp)) err = timestamp_set(hdl, fid, cfg.tstmp, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, timestamp_feat); @@ -465,7 +478,7 @@ static int temp_thresh_set(struct libnvme_transport_handle *hdl, const __u8 fid, int err; bool sv; - sv = argconfig_parse_seen(opts, "save"); + sv = cfg->save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -474,9 +487,9 @@ static int temp_thresh_set(struct libnvme_transport_handle *hdl, const __u8 fid, if (!err) { nvme_feature_decode_temp_threshold(cmd.result, &tmpth, &tmpsel, &thsel, &tmpthh); - if (!argconfig_parse_seen(opts, "tmpth")) + if (!argconfig_given(opts, &cfg->tmpth)) cfg->tmpth = tmpth; - if (!argconfig_parse_seen(opts, "tmpthh")) + if (!argconfig_given(opts, &cfg->tmpthh)) cfg->tmpthh = tmpthh; } @@ -522,8 +535,8 @@ static int feat_temp_thresh(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (argconfig_parse_seen(opts, "tmpth") || - argconfig_parse_seen(opts, "tmpthh")) + if (argconfig_given(opts, &cfg.tmpth) || + argconfig_given(opts, &cfg.tmpthh)) err = temp_thresh_set(hdl, fid, opts, &cfg); else err = feat_get(hdl, fid, NVME_SET(cfg.tmpsel, FEAT_TT_TMPSEL) | @@ -543,7 +556,7 @@ static int arbitration_set(struct libnvme_transport_handle *hdl, const __u8 fid, bool sv; int err; - sv = argconfig_parse_seen(opts, "save"); + sv = cfg->save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -552,13 +565,13 @@ static int arbitration_set(struct libnvme_transport_handle *hdl, const __u8 fid, if (!err) { nvme_feature_decode_arbitration(cmd.result, &ab, &lpw, &mpw, &hpw); - if (!argconfig_parse_seen(opts, "ab")) + if (!argconfig_given(opts, &cfg->ab)) cfg->ab = ab; - if (!argconfig_parse_seen(opts, "lpw")) + if (!argconfig_given(opts, &cfg->lpw)) cfg->lpw = lpw; - if (!argconfig_parse_seen(opts, "mpw")) + if (!argconfig_given(opts, &cfg->mpw)) cfg->mpw = mpw; - if (!argconfig_parse_seen(opts, "hpw")) + if (!argconfig_given(opts, &cfg->hpw)) cfg->hpw = hpw; } @@ -606,7 +619,7 @@ static int feat_arbitration(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (argc == 2 || argconfig_parse_seen(opts, "sel")) + if (argc == 2 || argconfig_given(opts, &cfg.sel)) return feat_get(hdl, fid, 0, cfg.sel, 0, "arbitration feature"); return arbitration_set(hdl, fid, opts, &cfg); @@ -645,6 +658,7 @@ static int feat_volatile_wc(int argc, char **argv, struct command *acmd, struct struct config { bool wce; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -655,9 +669,9 @@ static int feat_volatile_wc(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (argconfig_parse_seen(opts, "wce")) + if (argconfig_given(opts, &cfg.wce)) err = volatile_wc_set(hdl, fid, cfg.wce, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, volatile_wc_feat); @@ -704,6 +718,7 @@ static int feat_power_limit(int argc, char **argv, struct command *acmd, __u8 pls; __u8 uidx; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -717,9 +732,9 @@ static int feat_power_limit(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "plv")) + if (argconfig_given(opts, &cfg.plv)) err = power_limit_set(hdl, fid, cfg.plv, cfg.pls, cfg.uidx, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, cfg.uidx, power_limit_feat); @@ -774,6 +789,7 @@ static int feat_power_thresh(int argc, char **argv, struct command *acmd, __u8 ept; __u8 uidx; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -789,13 +805,13 @@ static int feat_power_thresh(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "ptv") || - argconfig_parse_seen(opts, "pts") || - argconfig_parse_seen(opts, "pmts") || - argconfig_parse_seen(opts, "ept")) + if (argconfig_given(opts, &cfg.ptv) || + argconfig_given(opts, &cfg.pts) || + argconfig_given(opts, &cfg.pmts) || + argconfig_given(opts, &cfg.ept)) err = power_thresh_set(hdl, fid, cfg.ptv, cfg.pts, cfg.pmts, cfg.ept, cfg.uidx, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, cfg.uidx, power_thresh_feat); @@ -846,6 +862,7 @@ static int feat_power_meas(int argc, char **argv, struct command *cmd, __u16 smt; __u8 uidx; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -860,12 +877,12 @@ static int feat_power_meas(int argc, char **argv, struct command *cmd, if (err) return err; - if (argconfig_parse_seen(opts, "act") || - argconfig_parse_seen(opts, "pmts") || - argconfig_parse_seen(opts, "smt")) + if (argconfig_given(opts, &cfg.act) || + argconfig_given(opts, &cfg.pmts) || + argconfig_given(opts, &cfg.smt)) err = power_meas_set(hdl, fid, cfg.act, cfg.pmts, cfg.smt, cfg.uidx, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, power_meas_feat); @@ -913,6 +930,7 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, __u16 tler; bool dulbe; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -934,10 +952,10 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, } } - if (argconfig_parse_seen(opts, "tler") || - argconfig_parse_seen(opts, "dulbe")) + if (argconfig_given(opts, &cfg.tler) || + argconfig_given(opts, &cfg.dulbe)) err = err_recovery_set(hdl, fid, cfg.nsid, cfg.tler, cfg.dulbe, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.sel, 0, err_recovery_feat); @@ -946,8 +964,8 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, } static int num_queues_set(struct libnvme_transport_handle *hdl, const __u8 fid, - __u16 nsqr, __u16 ncqr, bool sv, - struct argconfig_commandline_options *opts) + __u16 nsqr, __u16 ncqr, bool sv, bool nsqr_given, + bool ncqr_given) { enum nvme_get_features_sel sel = NVME_GET_FEATURES_SEL_CURRENT; __u32 cdw11 = NVME_SET(nsqr, FEAT_NRQS_NSQR) | @@ -963,9 +981,9 @@ static int num_queues_set(struct libnvme_transport_handle *hdl, const __u8 fid, err = libnvme_exec_admin_passthru(hdl, &cmd); if (!err) { nvme_feature_decode_number_of_queues(cmd.result, &nsqr, &ncqr); - if (!argconfig_parse_seen(opts, "nsqr")) + if (!nsqr_given) cdw11 |= NVME_SET(nsqr, FEAT_NRQS_NSQR); - if (!argconfig_parse_seen(opts, "ncqr")) + if (!ncqr_given) cdw11 |= NVME_SET(ncqr, FEAT_NRQS_NSQR); } @@ -999,6 +1017,7 @@ static int feat_num_queues(int argc, char **argv, struct command *acmd, __u16 nsqr; __u16 ncqr; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -1011,10 +1030,12 @@ static int feat_num_queues(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "nsqr") || - argconfig_parse_seen(opts, "ncqr")) + if (argconfig_given(opts, &cfg.nsqr) || + argconfig_given(opts, &cfg.ncqr)) err = num_queues_set(hdl, fid, cfg.nsqr, cfg.ncqr, - argconfig_parse_seen(opts, "save"), opts); + cfg.save, + argconfig_given(opts, &cfg.nsqr), + argconfig_given(opts, &cfg.ncqr)); else err = feat_get(hdl, fid, 0, cfg.sel, 0, num_queues_feat); @@ -1040,15 +1061,15 @@ static int host_behavior_set(struct libnvme_transport_handle *hdl, __u8 fid, return err; } - if (argconfig_parse_seen(opts, "acre")) + if (argconfig_given(opts, &cfg->acre)) data.acre = cfg->acre; - if (argconfig_parse_seen(opts, "etdas")) + if (argconfig_given(opts, &cfg->etdas)) data.etdas = cfg->etdas; - if (argconfig_parse_seen(opts, "lbafee")) + if (argconfig_given(opts, &cfg->lbafee)) data.lbafee = cfg->lbafee; - if (argconfig_parse_seen(opts, "hdisns")) + if (argconfig_given(opts, &cfg->hdisns)) data.hdisns = cfg->hdisns; - if (argconfig_parse_seen(opts, "cdfe")) + if (argconfig_given(opts, &cfg->cdfe)) data.cdfe = cpu_to_le16(cfg->cdfe); nvme_init_set_features_host_behavior(&cmd, sv, &data); @@ -1092,13 +1113,13 @@ static int feat_host_behavior_support(int argc, char **argv, struct command *acm if (err) return err; - if (argconfig_parse_seen(opts, "acre") || - argconfig_parse_seen(opts, "etdas") || - argconfig_parse_seen(opts, "lbafee") || - argconfig_parse_seen(opts, "hdisns") || - argconfig_parse_seen(opts, "cdfe")) + if (argconfig_given(opts, &cfg.acre) || + argconfig_given(opts, &cfg.etdas) || + argconfig_given(opts, &cfg.lbafee) || + argconfig_given(opts, &cfg.hdisns) || + argconfig_given(opts, &cfg.cdfe)) err = host_behavior_set(hdl, fid, opts, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, host_behavior_feat); @@ -1172,7 +1193,7 @@ static int lba_range_type_set(struct libnvme_transport_handle *hdl, __u8 fid, return -EINVAL; } #else /* NVME_HAVE_INT128 */ - if (argconfig_parse_seen(opts, "guids")) { + if (argconfig_given(opts, &cfg->guids)) { nvme_show_error("guid unsupported without __int128 available"); return -EINVAL; } @@ -1245,9 +1266,9 @@ static int feat_lba_range_type(int argc, char **argv, struct command *acmd, } } - if (argconfig_parse_seen(opts, "num")) + if (argconfig_given(opts, &cfg.num)) err = lba_range_type_set(hdl, fid, opts, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.sel, 0, lba_range_feat); @@ -1298,10 +1319,10 @@ static int feat_int_coalesce(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "thr") || - argconfig_parse_seen(opts, "time")) + if (argconfig_given(opts, &cfg.thr) || + argconfig_given(opts, &cfg.time)) err = int_coalesce_set(hdl, fid, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, int_coalesce_feat); @@ -1350,10 +1371,10 @@ static int feat_int_vec_conf(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "iv") || - argconfig_parse_seen(opts, "cd")) + if (argconfig_given(opts, &cfg.iv) || + argconfig_given(opts, &cfg.cd)) err = int_vec_conf_set(hdl, fid, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, int_vec_conf_feat); @@ -1400,9 +1421,9 @@ static int feat_write_atom_normal(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "dn")) + if (argconfig_given(opts, &cfg.dn)) err = write_atom_normal_set(hdl, fid, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, write_atom_normal_feat); @@ -1442,6 +1463,7 @@ static int feat_async_event_conf(int argc, char **argv, struct command *acmd, struct config { __u32 result; __u8 sel; + bool save; }; struct config cfg = { 0 }; @@ -1453,9 +1475,9 @@ static int feat_async_event_conf(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "result")) + if (argconfig_given(opts, &cfg.result)) err = async_event_conf_set(hdl, fid, cfg.result, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, async_event_conf_feat); @@ -1501,9 +1523,9 @@ static int feat_keep_alive_timer(int argc, char **argv, struct command *acmd, if (err) return err; - if (argconfig_parse_seen(opts, "kato")) + if (argconfig_given(opts, &cfg.kato)) err = keep_alive_timer_set(hdl, fid, &cfg, - argconfig_parse_seen(opts, "save")); + cfg.save); else err = feat_get(hdl, fid, 0, cfg.sel, 0, keep_alive_timer_feat); diff --git a/plugins/ns/ns-plugin.c b/plugins/ns/ns-plugin.c index b5efc534f9..a2b631c85b 100644 --- a/plugins/ns/ns-plugin.c +++ b/plugins/ns/ns-plugin.c @@ -172,7 +172,7 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s if (!cntlist) return -ENOMEM; - if (argconfig_parse_seen(opts, "controllers")) { + if (argconfig_given(opts, &cfg.cntlist)) { nvme_init_ctrl_list(cntlist, num, list); } else { struct nvme_id_ctrl ctrl = { 0 }; diff --git a/plugins/nvme-mi/nvme-mi-plugin.c b/plugins/nvme-mi/nvme-mi-plugin.c index 95631fe354..7687d66d67 100644 --- a/plugins/nvme-mi/nvme-mi-plugin.c +++ b/plugins/nvme-mi/nvme-mi-plugin.c @@ -82,7 +82,7 @@ static int libnvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc if (err) return err; - if (!argconfig_parse_seen(opts, "opcode")) { + if (!argconfig_given(opts, &cfg.opcode)) { nvme_show_error("%s: opcode parameter required", *argv); return -EINVAL; } diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c index 5c0a30e07a..878b99b171 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -20,6 +20,7 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid) { + bool no_uuid_flag = false; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __u64 result = 0; @@ -29,14 +30,14 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 int err; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, "Skip UUID index search (UUID index not required for OCP 1.0)")); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (argconfig_parse_seen(opts, "no-uuid")) + if (no_uuid_flag) uuid = false; if (uuid) { @@ -64,6 +65,7 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 int get_ocp_error_counters(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Issue Get Feature command (FID: 0xC3) Clear PCIe Correctable Error Counters"; const char *sel = "[0-3]: current/default/saved/supported/"; const char *nsid = "Byte[04-07]: Namespace Identifier Valid/Invalid/Inactive"; @@ -89,13 +91,13 @@ int get_ocp_error_counters(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - uuid = !argconfig_parse_seen(opts, "no-uuid"); + uuid = !no_uuid_flag; if (uuid) { /* OCP 2.0 requires UUID index support */ diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 881dfbc6e5..282b420696 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -266,20 +266,21 @@ static int ocp_latency_monitor_log(int argc, char **argv, struct command *command, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve latency monitor log data."; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) return ret; ret = get_c3_log_page(hdl, nvme_args.output_format, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error( "ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n", @@ -418,6 +419,7 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, static int ocp_get_latency_monitor_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Issue Get Feature command (FID: 0xC5) Latency Monitor"; const char *sel = "[0-3]: current/default/saved/supported/"; const char *nsid = "Byte[04-07]: Namespace Identifier Valid/Invalid/Inactive"; @@ -443,13 +445,13 @@ static int ocp_get_latency_monitor_feature(int argc, char **argv, struct command NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - uuid = !argconfig_parse_seen(opts, "no-uuid"); + uuid = !no_uuid_flag; if (uuid) { /* OCP 2.0 requires UUID index support */ @@ -562,6 +564,7 @@ static int eol_plp_failure_mode_set(struct libnvme_transport_handle *hdl, const static int eol_plp_failure_mode(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Define EOL or PLP circuitry failure mode.\n" "No argument prints current mode."; const char *mode = "[0-3]: default/rom/wtm/normal"; @@ -587,19 +590,19 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *acmd, OPT_BYTE("mode", 'm', &cfg.mode, mode), OPT_FLAG("save", 's', &cfg.save, save), OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (argconfig_parse_seen(opts, "mode")) + if (argconfig_given(opts, &cfg.mode)) err = eol_plp_failure_mode_set(hdl, nsid, fid, cfg.mode, cfg.save, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); else err = eol_plp_failure_mode_get(hdl, nsid, fid, cfg.sel, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); return err; } @@ -951,6 +954,7 @@ int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options) static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve and parse OCP Telemetry log."; const char *telemetry_log = "Telemetry log binary;\n 'host.bin' or 'controller.bin'"; const char *string_log = "String log binary; 'C9.bin'"; @@ -990,7 +994,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct OPT_INT("data-area", 'a', &opt.data_area, data_area), OPT_STR("telemetry-type", 't', &opt.telemetry_type, telemetry_type), OPT_UINT("host-generate", 'g', &host_gen, hgen), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1098,7 +1102,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct sprintf(file_path_string, "%s-%s", opt.output_file, string_suffix); err = get_c9_log_page_data(hdl, 0, 1, (const char *)file_path_string, NULL, NULL, NULL, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (err) { nvme_show_error("Failed to fetch string-log from the drive."); goto out; @@ -1107,7 +1111,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct opt.string_log = file_path_string; } - if (argconfig_parse_seen(opts, "output-format")) + if (argconfig_given(opts, &nvme_args.output_format)) opt.output_format = nvme_args.output_format; if (!opt.output_format) { @@ -1216,6 +1220,7 @@ static int get_c5_log_page(struct libnvme_transport_handle *hdl, char *format, b static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve unsupported requirements log data."; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1230,14 +1235,14 @@ static int ocp_unsupported_requirements_log(int argc, char **argv, struct comman }; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) return ret; ret = get_c5_log_page(hdl, cfg.output_format, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C5 Log Page, ret = %d", ret); @@ -1318,6 +1323,7 @@ static int get_c1_log_page(struct libnvme_transport_handle *hdl, char *format, b static int ocp_error_recovery_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve C1h Error Recovery Log data."; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1332,14 +1338,14 @@ static int ocp_error_recovery_log(int argc, char **argv, struct command *acmd, s }; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) return ret; ret = get_c1_log_page(hdl, cfg.output_format, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C1h Log Page, ret = %d", ret); @@ -1419,20 +1425,21 @@ static int get_c4_log_page(struct libnvme_transport_handle *hdl, char *format, b static int ocp_device_capabilities_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve C4h Device Capabilities Log data."; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) return ret; ret = get_c4_log_page(hdl, nvme_args.output_format, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C4h Log Page, ret = %d", ret); @@ -1494,7 +1501,7 @@ static int ocp_set_telemetry_profile_feature(int argc, char **argv, struct comma if (err) return err; - if (argconfig_parse_seen(opts, "telemetry-profile-select")) + if (argconfig_given(opts, &cfg.tps)) err = ocp_set_telemetry_profile(hdl, cfg.tps); else nvme_show_error("Telemetry Profile Select is a required argument"); @@ -1510,6 +1517,7 @@ static int ocp_set_telemetry_profile_feature(int argc, char **argv, struct comma static int ocp_get_telemetry_profile_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Issue Get Feature command (FID: 0xC8) Telemetry Profile"; const char *sel = "[0-3]: current/default/saved/supported/"; const char *nsid = "Byte[04-07]: Namespace Identifier Valid/Invalid/Inactive"; @@ -1535,13 +1543,13 @@ static int ocp_get_telemetry_profile_feature(int argc, char **argv, struct comma NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - uuid = !argconfig_parse_seen(opts, "no-uuid"); + uuid = !no_uuid_flag; if (uuid) { /* OCP 2.0 requires UUID index support */ @@ -1608,6 +1616,7 @@ set_dssd_power_state(struct libnvme_transport_handle *hdl, static int set_dssd_power_state_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Define DSSD Power State (Feature Identifier C7h) Set Feature."; const char *power_state = "DSSD Power State to set in watts"; const char *save = "Specifies that the controller shall save the attribute"; @@ -1629,15 +1638,15 @@ static int set_dssd_power_state_feature(int argc, char **argv, struct command *a NVME_ARGS(opts, OPT_BYTE("power-state", 'p', &cfg.power_state, power_state), OPT_FLAG("save", 's', &cfg.save, save), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (argconfig_parse_seen(opts, "power-state")) + if (argconfig_given(opts, &cfg.power_state)) err = set_dssd_power_state(hdl, nsid, OCP_FID_DSSDPS, cfg.power_state, cfg.save, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); return err; } @@ -1681,6 +1690,8 @@ static int get_dssd_power_state(struct libnvme_transport_handle *hdl, const __u3 static int get_dssd_power_state_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool all_flag = false; + bool no_uuid_flag = false; const char *desc = "Define DSSD Power State (Feature Identifier C7h) Get Feature."; const char *all = "Print out all 3 values at once - Current, Default, and Saved"; const char *sel = "[0-3]: current/default/saved/supported/"; @@ -1702,23 +1713,23 @@ static int get_dssd_power_state_feature(int argc, char **argv, struct command *a NVME_ARGS(opts, OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_FLAG("all", 'a', NULL, all), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("all", 'a', &all_flag, all), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (argconfig_parse_seen(opts, "all")) { + if (all_flag) { for (i = 0; i < 3; i++) { err = get_dssd_power_state(hdl, nsid, fid, i, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (err) break; } - } else if (argconfig_parse_seen(opts, "sel")) + } else if (argconfig_given(opts, &cfg.sel)) err = get_dssd_power_state(hdl, nsid, fid, cfg.sel, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); else nvme_show_error("Required to have --sel as an argument, or pass the --all flag."); @@ -1734,6 +1745,7 @@ static int get_dssd_power_state_feature(int argc, char **argv, struct command *a static int set_plp_health_check_interval(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Issue Set Feature command (FID: 0xC6) PLP Health Check Interval"; const char *plp_health_interval = "[31:16]:PLP Health Check Interval"; @@ -1759,14 +1771,14 @@ static int set_plp_health_check_interval(int argc, char **argv, struct command * OPT_SHRT("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), OPT_FLAG("save", 's', &cfg.sv, sv), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (!argconfig_parse_seen(opts, "no-uuid")) { + if (!no_uuid_flag) { /* OCP 2.0 requires UUID index support */ err = ocp_get_uuid_index(hdl, &uidx); if (err || !uidx) { @@ -1970,6 +1982,7 @@ static int get_c9_log_page(struct libnvme_transport_handle *hdl, static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; @@ -1992,7 +2005,7 @@ static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *a NVME_ARGS(opts, OPT_FILE("output-file", 'f', &cfg.output_file, output_file), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -2004,7 +2017,7 @@ static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *a sprintf(file_path, "%s", DEFAULT_STRING_BIN); ret = get_c9_log_page(hdl, cfg.output_format, file_path, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C9 Log Page, ret = %d", ret); @@ -2088,6 +2101,7 @@ static int get_c7_log_page(struct libnvme_transport_handle *hdl, char *format, b static int ocp_tcg_configuration_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve TCG Configuration Log Page Data"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2102,14 +2116,14 @@ static int ocp_tcg_configuration_log(int argc, char **argv, struct command *acmd }; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) return ret; ret = get_c7_log_page(hdl, cfg.output_format, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C7 Log Page, ret = %d", ret); @@ -2204,6 +2218,8 @@ static int error_injection_get(struct libnvme_transport_handle *hdl, const __u8 static int get_error_injection(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; + bool all_ns_flag = false; const char *desc = "Get Error Injection Feature"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2217,8 +2233,8 @@ static int get_error_injection(int argc, char **argv, struct command *acmd, stru NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid), - OPT_FLAG("all-ns", 'a', NULL, all_ns)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2230,9 +2246,9 @@ static int get_error_injection(int argc, char **argv, struct command *acmd, stru * OCP v2.0r21 - NSID: Shall be set to FFFFFFFFh. * OCP v2.5 - NSID: The host should either clear this to zero or set this to FFFFFFFFh */ - nsid = argconfig_parse_seen(opts, "all-ns") ? NVME_NSID_ALL : 0; + nsid = all_ns_flag ? NVME_NSID_ALL : 0; - return error_injection_get(hdl, cfg.sel, !argconfig_parse_seen(opts, "no-uuid"), nsid); + return error_injection_get(hdl, cfg.sel, !no_uuid_flag, nsid); } static int error_injection_set(struct libnvme_transport_handle *hdl, struct erri_config *cfg, bool uuid, __u32 nsid) @@ -2298,6 +2314,8 @@ static int error_injection_set(struct libnvme_transport_handle *hdl, struct erri static int set_error_injection(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; + bool all_ns_flag = false; const char *desc = "Inject error conditions"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2311,8 +2329,8 @@ static int set_error_injection(int argc, char **argv, struct command *acmd, stru NVME_ARGS(opts, OPT_FILE("data", 'd', &cfg.file, data), OPT_BYTE("number", 'n', &cfg.number, number), - OPT_FLAG("no-uuid", 'N', NULL, no_uuid), - OPT_FLAG("all-ns", 'a', NULL, all_ns), + OPT_FLAG("no-uuid", 'N', &no_uuid_flag, no_uuid), + OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns), OPT_SHRT("type", 't', &cfg.type, type), OPT_SHRT("nrtdp", 'r', &cfg.nrtdp, nrtdp)); @@ -2326,8 +2344,8 @@ static int set_error_injection(int argc, char **argv, struct command *acmd, stru * OCP v2.0r21 - NSID: Shall be set to FFFFFFFFh. * OCP v2.5 - NSID: The host should either clear this to zero or set this to FFFFFFFFh */ - nsid = argconfig_parse_seen(opts, "all-ns") ? NVME_NSID_ALL : 0; - return error_injection_set(hdl, &cfg, !argconfig_parse_seen(opts, "no-uuid"), nsid); + nsid = all_ns_flag ? NVME_NSID_ALL : 0; + return error_injection_set(hdl, &cfg, !no_uuid_flag, nsid); } static int enable_ieee1667_silo_get(struct libnvme_transport_handle *hdl, const __u8 sel, bool uuid) @@ -2366,6 +2384,7 @@ static int enable_ieee1667_silo_get(struct libnvme_transport_handle *hdl, const static int get_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Get Enable IEEE1667 Silo Feature"; int err; struct config { @@ -2378,20 +2397,18 @@ static int get_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - return enable_ieee1667_silo_get(hdl, cfg.sel, !argconfig_parse_seen(opts, "no-uuid")); + return enable_ieee1667_silo_get(hdl, cfg.sel, !no_uuid_flag); } static int enable_ieee1667_silo_set(struct libnvme_transport_handle *hdl, - struct argconfig_commandline_options *opts) + bool enable, bool save, bool uuid) { - bool enable = argconfig_parse_seen(opts, "enable"); - bool save = argconfig_parse_seen(opts, "save"); struct ieee1667_get_cq_entry cq_entry; const __u8 fid = OCP_FID_1667; __u64 result; @@ -2399,7 +2416,7 @@ static int enable_ieee1667_silo_set(struct libnvme_transport_handle *hdl, __u8 uidx = 0; int err; - if (!argconfig_parse_seen(opts, "no-uuid")) { + if (uuid) { /* OCP 2.0 requires UUID index support */ err = ocp_get_uuid_index(hdl, &uidx); if (err || !uidx) { @@ -2426,21 +2443,25 @@ static int enable_ieee1667_silo_set(struct libnvme_transport_handle *hdl, static int set_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool enable_flag = false; + bool save_flag = false; + bool no_uuid_flag = false; int err; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; NVME_ARGS(opts, - OPT_FLAG("enable", 'e', NULL, no_uuid), - OPT_FLAG("save", 's', NULL, save), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("enable", 'e', &enable_flag, no_uuid), + OPT_FLAG("save", 's', &save_flag, save), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, enable_ieee1667_silo, opts); if (err) return err; - return enable_ieee1667_silo_set(hdl, opts); + return enable_ieee1667_silo_set(hdl, enable_flag, save_flag, + !no_uuid_flag); } static int hwcomp_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) @@ -2575,6 +2596,7 @@ static int ocp_get_idle_wakeup_time_config_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Issue Get Feature command (FID: 0xCA) IWUT"; const char *sel = "[0-3]: current/default/saved/supported/"; const char *nsid = "Byte[04-07]: NSID Valid/Invalid/Inactive"; @@ -2601,13 +2623,13 @@ static int ocp_get_idle_wakeup_time_config_feature(int argc, char **argv, NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - uuid = !argconfig_parse_seen(opts, "no-uuid"); + uuid = !no_uuid_flag; if (uuid) { /* OCP 2.0 requires UUID index support */ diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index c826ff47db..6c8b2630f1 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -114,6 +114,7 @@ static int get_c0_log_page(struct libnvme_transport_handle *hdl, char *format, int ocp_smart_add_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + bool no_uuid_flag = false; const char *desc = "Retrieve the extended SMART health data."; const char *no_uuid = "Skip UUID index search (UUID index not required for OCP 1.0)"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -121,7 +122,7 @@ int ocp_smart_add_log(int argc, char **argv, struct command *acmd, int ret = 0; NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -129,7 +130,7 @@ int ocp_smart_add_log(int argc, char **argv, struct command *acmd, ret = get_c0_log_page(hdl, nvme_args.output_format, nvme_args.output_format_ver, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (ret) nvme_show_error("ERROR : OCP : Failure reading the C0 Log Page, ret = %d", ret); diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 7750361296..f610ec0cb4 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -78,15 +78,15 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *acmd, stru return err; } /* When not selected on the command line, get minimum data area required */ - if (!argconfig_parse_seen(opts, "data-area")) - cfg.data_area = argconfig_parse_seen(opts, "config-file") ? 3 : 1; + if (!argconfig_given(opts, &cfg.data_area)) + cfg.data_area = argconfig_given(opts, &cfg.cfg_file) ? 3 : 1; if (cfg.data_area < 1 || cfg.data_area > 4) { nvme_show_error("data-area = '%d'", cfg.data_area); return -EINVAL; } - has_binary_file = argconfig_parse_seen(opts, "source-file"); + has_binary_file = argconfig_given(opts, &cfg.binary_file); if (has_binary_file) { // If a binary file is provided, we don't want to open a device. if (nvme_args.device) { @@ -113,7 +113,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *acmd, stru return -EINVAL; } - if (argconfig_parse_seen(opts, "config-file")) { + if (argconfig_given(opts, &cfg.cfg_file)) { __cleanup_free char *conf_str = NULL; enum json_tokener_error jerr; diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c index bdf9fdbde7..364d3a5801 100644 --- a/plugins/solidigm/solidigm-workload-tracker.c +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -593,7 +593,7 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *acmd, stru return -EINVAL; } we.contentGroup = opt; - if (argconfig_parse_seen(opts, "trigger-field")) { + if (argconfig_given(opts, &cfg.trigger_field)) { int field_pos = find_field(group_fields[opt], cfg.trigger_field); int field_offset = 0; diff --git a/src/config-create.c b/src/config-create.c index 4951d8fe63..1e54091eb8 100644 --- a/src/config-create.c +++ b/src/config-create.c @@ -206,9 +206,10 @@ int nvme_config_create(const char *desc, int argc, char **argv) if (err) return err; - persistent = nvmf_resolve_persistent_arg(opts, persistent_arg); + persistent = nvmf_resolve_persistent_arg( + argconfig_given(opts, &persistent_arg), persistent_arg); - if (argconfig_parse_seen(opts, "persistent") && !discovery) { + if (argconfig_given(opts, &persistent_arg) && !discovery) { nvme_show_error("--persistent requires --discovery"); return -EINVAL; } diff --git a/src/fabrics.c b/src/fabrics.c index a6ac8237f3..3ffa82a58b 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -555,10 +555,9 @@ static void consume_conn(const struct libnvmf_config_conn *conn, } } -const char *nvmf_resolve_persistent_arg( - struct argconfig_commandline_options *opts, const char *arg) +const char *nvmf_resolve_persistent_arg(bool given, const char *arg) { - if (!argconfig_parse_seen(opts, "persistent")) + if (!given) return "auto"; return arg ? arg : "auto"; @@ -621,7 +620,9 @@ int nvmf_convert_discovery_line(struct libnvmf_config_emitter *emitter, return 0; return nvme_config_convert_discovery_args(emitter, &fa, - nvmf_resolve_persistent_arg(opts, persistent_arg)); + nvmf_resolve_persistent_arg( + argconfig_given(opts, &persistent_arg), + persistent_arg)); } static int setup_common_context(struct libnvmf_context *fctx, @@ -901,7 +902,8 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) flags = nvme_args.output_flags; - persistent = nvmf_resolve_persistent_arg(opts, persistent_arg); + persistent = nvmf_resolve_persistent_arg( + argconfig_given(opts, &persistent_arg), persistent_arg); if (!strcmp(config_file, "none")) config_file = NULL; diff --git a/src/fabrics.h b/src/fabrics.h index cab015f873..96cb3ef817 100644 --- a/src/fabrics.h +++ b/src/fabrics.h @@ -117,11 +117,11 @@ struct nvmf_args { * to the string libnvmf_context_set_persistent()/libnvmf_params_set() * expect: "auto" when the option is absent or given bare, or the value as * typed otherwise. Never NULL -- persistence is on by default, and - * "--persistent=no" is the opt-out. A caller that has to tell "absent" - * from "given" apart needs argconfig_parse_seen() itself. + * "--persistent=no" is the opt-out. @given is whether the option was + * given, @arg its value. A caller that has to tell "absent" from "given" + * apart uses argconfig_given() itself. */ -const char *nvmf_resolve_persistent_arg( - struct argconfig_commandline_options *opts, const char *arg); +const char *nvmf_resolve_persistent_arg(bool given, const char *arg); int fabrics_discover(const char *desc, int argc, char **argv, bool connect); int fabrics_connect(const char *desc, int argc, char **argv); diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index 0384be9284..268837fa67 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -138,20 +138,20 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl if (err) return err; - if (argconfig_parse_seen(opts, "nqn")) + if (argconfig_given(opts, &cfg.nqn)) fprintf(stderr, "WARNING: '--nqn' is ignored, the DHHC-1 string carries the secret itself, which no NQN takes part in deriving\n"); - if (argconfig_parse_seen(opts, "secret")) { + if (argconfig_given(opts, &cfg.secret)) { args[nargs++] = "--secret"; args[nargs++] = cfg.secret; } - if (argconfig_parse_seen(opts, "key-length")) { + if (argconfig_given(opts, &cfg.key_len)) { snprintf(key_len_buf, sizeof(key_len_buf), "%u", cfg.key_len); args[nargs++] = "--secret-length"; args[nargs++] = key_len_buf; } - if (argconfig_parse_seen(opts, "hmac")) { + if (argconfig_given(opts, &cfg.hmac)) { snprintf(hmac_buf, sizeof(hmac_buf), "%u", cfg.hmac); args[nargs++] = "--hmac"; args[nargs++] = hmac_buf; @@ -190,7 +190,7 @@ static int check_dhchap_key(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (argconfig_parse_seen(opts, "key")) { + if (argconfig_given(opts, &cfg.key)) { args[nargs++] = "--keydata"; args[nargs++] = cfg.key; } diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index c00f3a2108..944ce9558f 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -277,7 +277,7 @@ static int get_feature(int argc, char **argv, struct command *acmd, flags = nvme_args.output_flags; - if (!argconfig_parse_seen(opts, "namespace-id")) { + if (!argconfig_given(opts, &cfg.namespace_id)) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { if (errno != ENOTTY) { @@ -368,7 +368,7 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi if (err) return err; - if (!argconfig_parse_seen(opts, "namespace-id")) { + if (!argconfig_given(opts, &cfg.nsid)) { err = libnvme_get_nsid(hdl, &cfg.nsid); if (err < 0) { if (errno != ENOTTY) { @@ -408,7 +408,7 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi * length. */ if (cfg.fid == NVME_FEAT_FID_TIMESTAMP && - argconfig_parse_seen(opts, "value")) { + argconfig_given(opts, &cfg.value)) { memcpy(buf, &cfg.value, NVME_FEAT_TIMESTAMP_DATA_SIZE); } else { if (strlen(cfg.file)) diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 1c67d92678..a3137f1931 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -624,7 +624,7 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi nb = shr_parse_csv_u32(cfg.blocks, nlbs, ARRAY_SIZE(nlbs)); ns = shr_parse_csv_u64(cfg.slbas, slbas, ARRAY_SIZE(slbas)); if ((nb != ns) || - (argconfig_parse_seen(opts, "ctx-attrs") && nb != nc)) { + (argconfig_given(opts, &cfg.ctx_attrs) && nb != nc)) { nvme_show_error("No valid range definition provided"); return -EINVAL; } @@ -1158,7 +1158,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char else buffer_size = cfg.data_size; - if (argconfig_parse_seen(opts, "block-count")) { + if (argconfig_given(opts, &cfg.block_count)) { /* Use the value provided */ nblocks = cfg.block_count; } else { diff --git a/src/nvme-cmds-passthru.c b/src/nvme-cmds-passthru.c index 71605c36a1..589f533748 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -167,7 +167,7 @@ static int passthru(int argc, char **argv, bool admin, if (err) return err; - if (!argconfig_parse_seen(opts, "opcode")) { + if (!argconfig_given(opts, &cfg.opcode)) { nvme_show_error("%s: opcode parameter required", acmd->name); return -EINVAL; } diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index bc2c124d79..399862fce9 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -500,63 +500,65 @@ static int nvme_set_register(struct libnvme_transport_handle *hdl, int offset, return 0; } -static inline int set_register_names_check(struct argconfig_commandline_options *opts, int offset) +static inline int +set_register_names_check(struct argconfig_commandline_options *opts, + struct set_reg_config *cfg, int offset) { switch (offset) { case NVME_REG_INTMS: - if (argconfig_parse_seen(opts, "intms")) + if (argconfig_given(opts, &cfg->intms)) return -EINVAL; break; case NVME_REG_INTMC: - if (argconfig_parse_seen(opts, "intmc")) + if (argconfig_given(opts, &cfg->intmc)) return -EINVAL; break; case NVME_REG_CC: - if (argconfig_parse_seen(opts, "cc")) + if (argconfig_given(opts, &cfg->cc)) return -EINVAL; break; case NVME_REG_CSTS: - if (argconfig_parse_seen(opts, "csts")) + if (argconfig_given(opts, &cfg->csts)) return -EINVAL; break; case NVME_REG_NSSR: - if (argconfig_parse_seen(opts, "nssr")) + if (argconfig_given(opts, &cfg->nssr)) return -EINVAL; break; case NVME_REG_AQA: - if (argconfig_parse_seen(opts, "aqa")) + if (argconfig_given(opts, &cfg->aqa)) return -EINVAL; break; case NVME_REG_ASQ: - if (argconfig_parse_seen(opts, "asq")) + if (argconfig_given(opts, &cfg->asq)) return -EINVAL; break; case NVME_REG_ACQ: - if (argconfig_parse_seen(opts, "acq")) + if (argconfig_given(opts, &cfg->acq)) return -EINVAL; break; case NVME_REG_BPRSEL: - if (argconfig_parse_seen(opts, "bprsel")) + if (argconfig_given(opts, &cfg->bprsel)) return -EINVAL; break; case NVME_REG_CMBMSC: - if (argconfig_parse_seen(opts, "cmbmsc")) + if (argconfig_given(opts, &cfg->cmbmsc)) return -EINVAL; break; case NVME_REG_NSSD: - if (argconfig_parse_seen(opts, "nssd")) + if (argconfig_given(opts, &cfg->nssd)) return -EINVAL; break; case NVME_REG_PMRCTL: - if (argconfig_parse_seen(opts, "pmrctl")) + if (argconfig_given(opts, &cfg->pmrctl)) return -EINVAL; break; case NVME_REG_PMRMSCL: - if (argconfig_parse_seen(opts, "pmrmscl")) + if (argconfig_given(opts, &cfg->pmrmscl)) return -EINVAL; break; case NVME_REG_PMRMSCU: - if (argconfig_parse_seen(opts, "pmrmscu")) + if (argconfig_given(opts, &cfg->pmrmscu)) return -EINVAL; break; default: @@ -572,12 +574,12 @@ static int set_register_offset(struct libnvme_transport_handle *hdl, { int err; - if (!argconfig_parse_seen(opts, "value")) { + if (!argconfig_given(opts, &cfg->value)) { nvme_show_error("value required param"); return -EINVAL; } - err = set_register_names_check(opts, cfg->offset); + err = set_register_names_check(opts, cfg, cfg->offset); if (err) { nvme_show_error("offset duplicated param"); return err; @@ -596,98 +598,98 @@ static int set_register_names(struct libnvme_transport_handle *hdl, { int err; - if (argconfig_parse_seen(opts, "intms")) { + if (argconfig_given(opts, &cfg->intms)) { err = nvme_set_register(hdl, NVME_REG_INTMS, cfg->intms, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "intmc")) { + if (argconfig_given(opts, &cfg->intmc)) { err = nvme_set_register(hdl, NVME_REG_INTMC, cfg->intmc, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "cc")) { + if (argconfig_given(opts, &cfg->cc)) { err = nvme_set_register(hdl, NVME_REG_CC, cfg->cc, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "csts")) { + if (argconfig_given(opts, &cfg->csts)) { err = nvme_set_register(hdl, NVME_REG_CSTS, cfg->csts, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "nssr")) { + if (argconfig_given(opts, &cfg->nssr)) { err = nvme_set_register(hdl, NVME_REG_NSSR, cfg->nssr, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "aqa")) { + if (argconfig_given(opts, &cfg->aqa)) { err = nvme_set_register(hdl, NVME_REG_AQA, cfg->aqa, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "asq")) { + if (argconfig_given(opts, &cfg->asq)) { err = nvme_set_register(hdl, NVME_REG_ASQ, cfg->asq, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "acq")) { + if (argconfig_given(opts, &cfg->acq)) { err = nvme_set_register(hdl, NVME_REG_ACQ, cfg->acq, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "bprsel")) { + if (argconfig_given(opts, &cfg->bprsel)) { err = nvme_set_register(hdl, NVME_REG_BPRSEL, cfg->bprsel, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "cmbmsc")) { + if (argconfig_given(opts, &cfg->cmbmsc)) { err = nvme_set_register(hdl, NVME_REG_CMBMSC, cfg->cmbmsc, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "nssd")) { + if (argconfig_given(opts, &cfg->nssd)) { err = nvme_set_register(hdl, NVME_REG_NSSD, cfg->nssd, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "pmrctl")) { + if (argconfig_given(opts, &cfg->pmrctl)) { err = nvme_set_register(hdl, NVME_REG_PMRCTL, cfg->pmrctl, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "pmrmscl")) { + if (argconfig_given(opts, &cfg->pmrmscl)) { err = nvme_set_register(hdl, NVME_REG_PMRMSCL, cfg->pmrmscl, cfg->mmio32); if (err) return err; } - if (argconfig_parse_seen(opts, "pmrmscu")) { + if (argconfig_given(opts, &cfg->pmrmscu)) { err = nvme_set_register(hdl, NVME_REG_PMRMSCU, cfg->pmrmscu, cfg->mmio32); if (err) @@ -740,7 +742,7 @@ static int set_register(int argc, char **argv, struct command *acmd, struct plug return -EINVAL; } - if (argconfig_parse_seen(opts, "offset")) + if (argconfig_given(opts, &cfg.offset)) err = set_register_offset(hdl, opts, &cfg); if (!err) From 50755e242bb13c9adacf04ecdd405c34159687e1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:08:04 +0000 Subject: [PATCH 080/263] argconfig: drop argconfig_parse_seen() Nothing looks options up by name anymore. Remove argconfig_parse_seen(), and since an option without a variable can't be asked about at all now, let the option macros reject a NULL variable. The differential and the argconfig tests use argconfig_given() and give their flags a variable. Signed-off-by: Daniel Wagner --- src/argconfig.c | 26 -------------------------- src/argconfig.h | 14 ++++++-------- tests/unit/c/test-argconfig-diff.c | 6 ++++-- tests/unit/c/test-argconfig-parse.c | 23 +++++++++++------------ 4 files changed, 21 insertions(+), 48 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 9d37603a84..fb9aed7b9a 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -330,24 +330,6 @@ static int argconfig_parse_val(struct argconfig_commandline_options *s, return 0; } -static struct argconfig_commandline_options * -argconfig_find_option(struct argconfig_commandline_options *s, - const char *option) -{ - struct argconfig_commandline_options *g = argconfig_globals(s); - - for (; s && s->option; s++) { - if (!strcmp(s->option, option)) - return s; - } - for (; g && g->option; g++) { - if (!strcmp(g->option, option)) - return g; - } - - return NULL; -} - /* --verbose counts, a command may have a flag of the same name */ static bool argconfig_check_verbose(struct argconfig_commandline_options *s) { @@ -740,11 +722,3 @@ bool argconfig_given(struct argconfig_commandline_options *s, return given; } - -bool argconfig_parse_seen(struct argconfig_commandline_options *s, - const char *option) -{ - s = argconfig_find_option(s, option); - - return s && s->seen; -} diff --git a/src/argconfig.h b/src/argconfig.h index 8728817146..f9084ec09c 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -59,25 +59,25 @@ enum argconfig_types { /* * The option macros check the type of the variable they store to, so a * mismatch is a compile error instead of a write of the wrong size. The - * signed variant of a type is accepted as well. NULL is accepted for - * options which are only checked with argconfig_parse_seen(). + * signed variant of a type is accepted as well. Every option needs a + * variable, which argconfig_given() takes. */ -#define OPT_CHECK(v, T, S) _Generic(v, T : (v), S : (v), void * : (v)) +#define OPT_CHECK(v, T, S) _Generic(v, T : (v), S : (v)) /* * uint64_t is unsigned long long on some ABIs only, and listing a type * twice is an error. The inner selection falls back to a void expression, * which only fails if it is selected. */ #define OPT_CHECK_64(v) \ - _Generic(v, uint64_t * : (v), int64_t * : (v), void * : (v), \ + _Generic(v, uint64_t * : (v), int64_t * : (v), \ default : \ _Generic(v, unsigned long long * : (v), \ long long * : (v), \ default : \ (void)0)) #define OPT_CHECK_STR(v) OPT_CHECK(v, char **, const char **) -#define OPT_CHECK_BOOL(v) _Generic(v, bool * : (v), void * : (v)) -#define OPT_CHECK_DOUBLE(v) _Generic(v, double * : (v), void * : (v)) +#define OPT_CHECK_BOOL(v) _Generic(v, bool * : (v)) +#define OPT_CHECK_DOUBLE(v) _Generic(v, double * : (v)) #define OPT_END() { NULL } @@ -246,5 +246,3 @@ void argconfig_set_parse_hook(argconfig_parse_hook_fn hook); bool argconfig_given(struct argconfig_commandline_options *options, const void *value); -bool argconfig_parse_seen(struct argconfig_commandline_options *options, - const char *option); diff --git a/tests/unit/c/test-argconfig-diff.c b/tests/unit/c/test-argconfig-diff.c index b2082049bb..2ecf85b63f 100644 --- a/tests/unit/c/test-argconfig-diff.c +++ b/tests/unit/c/test-argconfig-diff.c @@ -50,6 +50,7 @@ struct diff_cfg { uint32_t longonly; bool dup1; bool dup2; + bool noval; }; struct global_cfg { @@ -57,6 +58,7 @@ struct global_cfg { bool dry_run; char *format; uint32_t timeout; + bool help; }; /* The report; stdout gets the help argconfig prints */ @@ -124,7 +126,7 @@ static void diff_opts(struct argconfig_commandline_options *opts, OPT_STR("str", 'r', &cfg->str, "str"), OPT_STRING_OPTIONAL("optstr", 'O', "STR", &cfg->optstr, "optstr"), - OPT_FLAG("noval", 'n', NULL, "noval"), + OPT_FLAG("noval", 'n', &cfg->noval, "noval"), OPT_UINT("longonly", 0, &cfg->longonly, "longonly"), OPT_FLAG("dup1", 'D', &cfg->dup1, "dup1"), OPT_FLAG("dup2", 'D', &cfg->dup2, "dup2"), @@ -142,7 +144,7 @@ static void global_opts(struct argconfig_commandline_options *opts, OPT_FLAG("dry-run", 0, &cfg->dry_run, "dry-run"), OPT_FMT("format", 'o', &cfg->format, "format"), OPT_UINT("timeout", 0, &cfg->timeout, "timeout"), - OPT_FLAG("help", 'h', NULL, "help"), + OPT_FLAG("help", 'h', &cfg->help, "help"), OPT_END() }; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index d75a4ad9f4..063a3fc18a 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -266,7 +266,7 @@ static void optional_string_test(void) * Real command lines pass several options together; the per-option tests * above each parse exactly one token, so exercise a realistic multi-flag * invocation here, mixed with an OPT_GROUP() separator (as real commands - * do) and a check of argconfig_parse_seen(), the public "was this option + * do) and a check of argconfig_given(), the public "was this option * given" query used throughout the plugins. */ static void combined_opts_test(void) @@ -301,11 +301,11 @@ static void combined_opts_test(void) test_rc = 1; } - if (!argconfig_parse_seen(opts, "flag") || - !argconfig_parse_seen(opts, "suffix") || - !argconfig_parse_seen(opts, "uint") || - argconfig_parse_seen(opts, "int")) { - printf("ERROR: combined options: argconfig_parse_seen() mismatch\n"); + if (!argconfig_given(opts, &cfg.flag) || + !argconfig_given(opts, &cfg.suffix) || + !argconfig_given(opts, &cfg.uint) || + argconfig_given(opts, &cfg.int_val)) { + printf("ERROR: combined options: argconfig_given() mismatch\n"); test_rc = 1; } } @@ -419,9 +419,9 @@ static void global_options_test(void) printf("ERROR: global options: command parse failed\n"); test_rc = 1; } - if (!argconfig_parse_seen(opts, "output-format") || - !argconfig_parse_seen(opts, "verbose") || - argconfig_parse_seen(opts, "dry-run")) { + if (!argconfig_given(opts, &format) || + !argconfig_given(opts, &verbose) || + argconfig_given(opts, &dry_run)) { printf("ERROR: global options: seen mismatch\n"); test_rc = 1; } @@ -433,8 +433,8 @@ static void global_options_test(void) ret = argconfig_parse(argv_len(argv), argv, desc, local); if (ret || !dry_run || - !argconfig_parse_seen(local, "output-format") || - !argconfig_parse_seen(local, "dry-run")) { + !argconfig_given(local, &format) || + !argconfig_given(local, &dry_run)) { printf("ERROR: global options: seen not kept\n"); test_rc = 1; } @@ -464,7 +464,6 @@ static void given_test(void) OPT_FLAG("flag", 'f', &alias, "flag"), OPT_FLAG("old-flag", 0, &alias, NULL, NULL, true), OPT_UINT("uint", 'u', &cfg.uint, "uint"), - OPT_FLAG("seen-only", 0, NULL, "seen only"), OPT_END() }; From c057a5809ad26e904f27b442cfd06d79fb578350 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:10:59 +0000 Subject: [PATCH 081/263] argconfig: reject negative numbers for unsigned options strtoul() accepts a sign and negates the value, so "--namespace-id=-1" became 0xffffffff and "--byte=-1" was rejected only because it ended up too large. A word was parsed with strtoul() into a uint32_t, so on 64-bit systems 4294967296 silently became 0. Reject a negative number for the unsigned option types and a word above UINT32_MAX. Names in the value table of an option still take precedence, so an option can accept "-1" as a named value. The signed options (OPT_INT, e.g. --ctrl-loss-tmo=-1) are unchanged. The value conversion of the getopt reference in the differential test follows the same rules. Signed-off-by: Daniel Wagner --- src/argconfig.c | 31 ++++++++++++++++++++++++-- tests/unit/c/argconfig-getopt-ref.c | 34 ++++++++++++++++++++++++----- tests/unit/c/test-argconfig-parse.c | 10 +++++++++ 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index fb9aed7b9a..dd26ed6eac 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -29,6 +29,7 @@ * */ +#include #include #include #include @@ -182,6 +183,18 @@ static int argconfig_error(char *type, const char *opt, const char *arg) return -EINVAL; } +/* + * strtoul() accepts a sign and negates the result, so "-1" would become the + * largest value. The value tables of an option can name such a value. + */ +static bool argconfig_negative(const char *arg) +{ + while (isspace((unsigned char)*arg)) + arg++; + + return *arg == '-'; +} + static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { @@ -191,6 +204,20 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, errno = 0; /* To distinguish success/failure after strtol/stroul call */ + switch (s->config_type) { + case CFG_BYTE: + case CFG_SHORT: + case CFG_POSITIVE: + case CFG_LONG: + case CFG_LONG_SUFFIX: + if (argconfig_negative(arg)) + return argconfig_error("non-negative number", s->option, + arg); + break; + default: + break; + } + switch (s->config_type) { case CFG_STRING: *(char **)value = arg; @@ -219,9 +246,9 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; } case CFG_POSITIVE: { - uint32_t tmp = strtoul(arg, &endptr, 0); + unsigned long long tmp = strtoull(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || tmp > UINT32_MAX || arg == endptr) ret = argconfig_error("word", s->option, arg); else *(uint32_t *)value = tmp; diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 0ae4437b25..695cc57131 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -2,11 +2,13 @@ /* * A frozen copy of the getopt based argconfig parser, as it was before * argconfig got its own parser. test-argconfig-diff compares both, so any - * change in behaviour shows up. Only the value conversion is shared in - * spirit: it is copied unchanged, help output and the locale side effect - * are left out. + * change in behaviour shows up. Help output and the locale side effect + * are left out. The test compares the parsing of the command line, so the + * value conversion follows the rules of argconfig: unsigned options reject + * negative numbers, and a word is at most UINT32_MAX. */ +#include #include #include #include @@ -34,6 +36,14 @@ static int argconfig_error(char *type, const char *opt, const char *arg) return -EINVAL; } +static bool argconfig_negative(const char *arg) +{ + while (isspace((unsigned char)*arg)) + arg++; + + return *arg == '-'; +} + static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { @@ -44,6 +54,20 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, /* To distinguish success/failure after strtol/stroul call */ errno = 0; + switch (s->config_type) { + case CFG_BYTE: + case CFG_SHORT: + case CFG_POSITIVE: + case CFG_LONG: + case CFG_LONG_SUFFIX: + if (argconfig_negative(arg)) + return argconfig_error("non-negative number", s->option, + arg); + break; + default: + break; + } + switch (s->config_type) { case CFG_STRING: *(char **)value = arg; @@ -72,9 +96,9 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; } case CFG_POSITIVE: { - uint32_t tmp = strtoul(arg, &endptr, 0); + unsigned long long tmp = strtoull(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || tmp > UINT32_MAX || arg == endptr) ret = argconfig_error("word", s->option, arg); else *(uint32_t *)value = tmp; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 063a3fc18a..384ef02876 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -105,6 +105,16 @@ static struct toval_test toval_tests[] = { VAL_TEST("--suffix=34.9Ki", suffix, 0, true, -EINVAL), VAL_TEST("--suffix=32Gii", suffix, 0, true, -EINVAL), VAL_TEST("--uint=1", uint, 1, true, 0), + VAL_TEST("--uint=4294967295", uint, 4294967295, true, 0), + VAL_TEST("--uint=4294967296", uint, 0, true, -EINVAL), + /* unsigned options reject negative numbers, signed ones don't */ + VAL_TEST("--uint=-1", uint, 0, true, -EINVAL), + VAL_TEST("--uint= -1", uint, 0, true, -EINVAL), + VAL_TEST("--byte=-1", byte, 0, true, -EINVAL), + VAL_TEST("--shrt=-1", shrt, 0, true, -EINVAL), + VAL_TEST("--long=-1", long_val, 0, true, -EINVAL), + VAL_TEST("--suffix=-1", suffix, 0, true, -EINVAL), + VAL_TEST("--int=-1", int_val, -1, true, 0), VAL_TEST("--int=1", int_val, 1, true, 0), VAL_TEST("--long=1", long_val, 1, true, 0), VAL_TEST("--long=0x123456789abcdef0", long_val, 0x123456789abcdef0, From ca5796cf371feb9a856081a602029f2b9ce84dfd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:18:12 +0000 Subject: [PATCH 082/263] nvme: accept names for the special namespace id and timeout values Several options take a value that has a special meaning: the namespace id 0xffffffff addresses all namespaces and a timeout of 0xffffffff waits forever. So far users had to know the number or type '-1', which only worked because the number was converted without a sign check. With unsigned options rejecting negative numbers, give these values names instead. Every namespace id option accepts 'all' and the global timeout option accepts 'infinite'. Both still accept '-1' as a name, so existing scripts keep working. The help lists the names of an option after its description. Signed-off-by: Daniel Wagner --- Documentation/global-options.txt | 1 + Documentation/nvme-admin-passthru.txt | 1 + Documentation/nvme-copy.txt | 1 + Documentation/nvme-dapustor-smart-log-add.txt | 1 + Documentation/nvme-device-self-test.txt | 1 + Documentation/nvme-dir-receive.txt | 1 + Documentation/nvme-dir-send.txt | 1 + Documentation/nvme-dsm.txt | 1 + Documentation/nvme-fdp-set-events.txt | 1 + Documentation/nvme-fdp-status.txt | 1 + Documentation/nvme-fdp-update.txt | 1 + Documentation/nvme-feat-err-recovery.txt | 1 + Documentation/nvme-feat-lba-range-type.txt | 1 + Documentation/nvme-flush.txt | 1 + Documentation/nvme-format.txt | 1 + Documentation/nvme-get-feature.txt | 1 + Documentation/nvme-get-lba-status.txt | 1 + Documentation/nvme-get-log.txt | 1 + Documentation/nvme-id-ctrl-list.txt | 1 + Documentation/nvme-id-ns-descs.txt | 1 + Documentation/nvme-id-ns-ind.txt | 1 + Documentation/nvme-id-ns-list.txt | 1 + Documentation/nvme-id-ns.txt | 1 + Documentation/nvme-id-nvm-ns.txt | 1 + Documentation/nvme-intel-internal-log.txt | 1 + Documentation/nvme-intel-smart-log-add.txt | 1 + Documentation/nvme-io-mgmt-recv.txt | 1 + Documentation/nvme-io-mgmt-send.txt | 1 + Documentation/nvme-io-passthru.txt | 1 + ...vme-log-dispersed-ns-participating-nss.txt | 1 + Documentation/nvme-log-error.txt | 1 + Documentation/nvme-log-smart.txt | 1 + Documentation/nvme-ns-attach.txt | 1 + Documentation/nvme-ns-delete.txt | 1 + Documentation/nvme-ns-detach.txt | 1 + Documentation/nvme-nvme-mi-recv.txt | 1 + Documentation/nvme-nvme-mi-send.txt | 1 + ...-ocp-get-clear-pcie-correctable-errors.txt | 1 + .../nvme-ocp-get-idle-wakeup-time.txt | 1 + .../nvme-ocp-get-latency-monitor.txt | 1 + .../nvme-ocp-get-telemetry-profile.txt | 1 + Documentation/nvme-resv-acquire.txt | 1 + Documentation/nvme-resv-register.txt | 1 + Documentation/nvme-resv-release.txt | 1 + Documentation/nvme-resv-report.txt | 1 + Documentation/nvme-seagate-get-ctrl-tele.txt | 1 + Documentation/nvme-seagate-get-host-tele.txt | 1 + .../nvme-seagate-vs-internal-log.txt | 1 + Documentation/nvme-security-recv.txt | 1 + Documentation/nvme-security-send.txt | 1 + Documentation/nvme-set-feature.txt | 1 + Documentation/nvme-sndk-namespace-resize.txt | 1 + Documentation/nvme-sndk-vs-cloud-log.txt | 1 + Documentation/nvme-sndk-vs-device-waf.txt | 1 + Documentation/nvme-sndk-vs-hw-rev-log.txt | 1 + Documentation/nvme-sndk-vs-smart-add-log.txt | 1 + Documentation/nvme-solidigm-smart-log-add.txt | 1 + .../nvme-toshiba-vs-smart-add-log.txt | 1 + Documentation/nvme-verify.txt | 1 + Documentation/nvme-wdc-namespace-resize.txt | 1 + Documentation/nvme-wdc-vs-cloud-log.txt | 1 + Documentation/nvme-wdc-vs-device-waf.txt | 1 + Documentation/nvme-wdc-vs-hw-rev-log.txt | 1 + Documentation/nvme-wdc-vs-smart-add-log.txt | 1 + Documentation/nvme-write-uncor.txt | 1 + Documentation/nvme-write-zeroes.txt | 1 + Documentation/nvme-zns-close-zone.txt | 1 + Documentation/nvme-zns-finish-zone.txt | 1 + Documentation/nvme-zns-id-ns.txt | 1 + Documentation/nvme-zns-offline-zone.txt | 1 + Documentation/nvme-zns-open-zone.txt | 1 + Documentation/nvme-zns-report-zones.txt | 1 + Documentation/nvme-zns-reset-zone.txt | 1 + Documentation/nvme-zns-set-zone-desc.txt | 1 + Documentation/nvme-zns-zone-append.txt | 1 + Documentation/nvme-zns-zone-mgmt-recv.txt | 1 + Documentation/nvme-zns-zone-mgmt-send.txt | 1 + Documentation/nvme-zns-zrwa-flush-zone.txt | 1 + plugins/dapustor/dapustor-nvme.c | 3 +- plugins/dir/dir-plugin.c | 6 ++- plugins/fdp/fdp.c | 9 ++-- plugins/feat/feat-nvme.c | 9 ++-- plugins/id/id-plugin.c | 18 ++++--- plugins/intel/intel-nvme.c | 6 ++- plugins/io-mgmt/io-mgmt-plugin.c | 6 ++- plugins/log/log-plugin.c | 9 ++-- plugins/memblaze/memblaze-nvme.c | 3 +- plugins/ns/ns-plugin.c | 6 ++- plugins/nvme-mi/nvme-mi-plugin.c | 3 +- plugins/ocp/ocp-clear-features.c | 3 +- plugins/ocp/ocp-nvme.c | 9 ++-- plugins/resv/resv-plugin.c | 12 +++-- plugins/sandisk/sandisk-nvme.c | 3 +- plugins/scaleflux/sfx-nvme.c | 9 ++-- plugins/seagate/seagate-nvme.c | 9 ++-- plugins/security/security-plugin.c | 6 ++- plugins/shannon/shannon-nvme.c | 9 ++-- plugins/solidigm/solidigm-smart.c | 3 +- plugins/ssstc/ssstc-nvme.c | 3 +- plugins/toshiba/toshiba-nvme.c | 3 +- plugins/utils/command-metadata-schema.json | 2 +- plugins/utils/command-metadata.c | 6 +-- plugins/wdc/wdc-nvme.c | 18 ++++--- plugins/ymtc/ymtc-nvme.c | 3 +- plugins/zns/zns.c | 30 ++++++++---- src/argconfig.c | 49 +++++++++++++++++-- src/argconfig.h | 4 +- src/args.h | 2 + src/global-config.c | 4 ++ src/global-options.c | 18 ++++++- src/nvme-cmds-discovery.c | 3 +- src/nvme-cmds-feature.c | 6 ++- src/nvme-cmds-io.c | 24 ++++++--- src/nvme-cmds-passthru.c | 3 +- src/nvme-cmds-sanitize.c | 6 ++- tests/cli/nvme_usage_test.py | 8 +++ tests/unit/c/test-argconfig-parse.c | 44 +++++++++++++++++ tests/unit/c/test-global-config.c | 26 ++++++++++ 118 files changed, 390 insertions(+), 91 deletions(-) diff --git a/Documentation/global-options.txt b/Documentation/global-options.txt index 20584042d3..0fdfd79de6 100644 --- a/Documentation/global-options.txt +++ b/Documentation/global-options.txt @@ -35,6 +35,7 @@ and are available to this subcommand: --timeout=:: Set the timeout for the command in milliseconds. + 'infinite' or '-1' waits without a time limit (0xffffffff). -v:: --verbose:: diff --git a/Documentation/nvme-admin-passthru.txt b/Documentation/nvme-admin-passthru.txt index 7f4b0392cb..78d6884ee7 100644 --- a/Documentation/nvme-admin-passthru.txt +++ b/Documentation/nvme-admin-passthru.txt @@ -64,6 +64,7 @@ OPTIONS -n :: --namespace-id=:: The value for the ns-id in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -[2-9] :: --cdw[2-3,10-15]=:: diff --git a/Documentation/nvme-copy.txt b/Documentation/nvme-copy.txt index 134b918e77..690aae7266 100644 --- a/Documentation/nvme-copy.txt +++ b/Documentation/nvme-copy.txt @@ -47,6 +47,7 @@ OPTIONS --namespace-id=:: Sends the command with the requested nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -d :: --sdlba=:: diff --git a/Documentation/nvme-dapustor-smart-log-add.txt b/Documentation/nvme-dapustor-smart-log-add.txt index 22212a1097..b03e3319d8 100644 --- a/Documentation/nvme-dapustor-smart-log-add.txt +++ b/Documentation/nvme-dapustor-smart-log-add.txt @@ -36,6 +36,7 @@ OPTIONS to provide this log on a per-namespace basis (see the NVMe Identify Controller for this capability). The default nsid to use is 0xffffffff for the device global SMART log. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-device-self-test.txt b/Documentation/nvme-device-self-test.txt index 61e2442de1..4d957e70d7 100644 --- a/Documentation/nvme-device-self-test.txt +++ b/Documentation/nvme-device-self-test.txt @@ -29,6 +29,7 @@ OPTIONS -n :: --namespace-id=:: Indicate the namespace in which the device self-test has to be carried out + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --self-test-code=:: diff --git a/Documentation/nvme-dir-receive.txt b/Documentation/nvme-dir-receive.txt index 763a06fe49..3ff271b6e5 100644 --- a/Documentation/nvme-dir-receive.txt +++ b/Documentation/nvme-dir-receive.txt @@ -41,6 +41,7 @@ OPTIONS --namespace-id=:: Retrieve the feature for the given nsid. This is optional and most features do not use this value. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -D :: --dir-type=:: diff --git a/Documentation/nvme-dir-send.txt b/Documentation/nvme-dir-send.txt index ae2abbca6d..f0eee5edb0 100644 --- a/Documentation/nvme-dir-send.txt +++ b/Documentation/nvme-dir-send.txt @@ -42,6 +42,7 @@ OPTIONS --namespace-id=:: Retrieve the feature for the given nsid. This is optional and most features do not use this value. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -D :: --dir-type=:: diff --git a/Documentation/nvme-dsm.txt b/Documentation/nvme-dsm.txt index dda8ffdb00..595682567a 100644 --- a/Documentation/nvme-dsm.txt +++ b/Documentation/nvme-dsm.txt @@ -43,6 +43,7 @@ OPTIONS --namespace-id=:: Sends the command with the requested nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -a :: --ctx-attrs=:: diff --git a/Documentation/nvme-fdp-set-events.txt b/Documentation/nvme-fdp-set-events.txt index 8fceac9970..805e9439e3 100644 --- a/Documentation/nvme-fdp-set-events.txt +++ b/Documentation/nvme-fdp-set-events.txt @@ -25,6 +25,7 @@ OPTIONS -n :: --namespace-id=:: Namespace identifier. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-fdp-status.txt b/Documentation/nvme-fdp-status.txt index 8ea86211c2..da934f2586 100644 --- a/Documentation/nvme-fdp-status.txt +++ b/Documentation/nvme-fdp-status.txt @@ -22,6 +22,7 @@ OPTIONS -n :: --namespace-id=:: Namespace identifier. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-fdp-update.txt b/Documentation/nvme-fdp-update.txt index eaebe6a7ec..8150791aff 100644 --- a/Documentation/nvme-fdp-update.txt +++ b/Documentation/nvme-fdp-update.txt @@ -22,6 +22,7 @@ OPTIONS -n :: --namespace-id=:: Namespace identifier. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -p :: --pids=:: diff --git a/Documentation/nvme-feat-err-recovery.txt b/Documentation/nvme-feat-err-recovery.txt index fc31be94ce..100023f34a 100644 --- a/Documentation/nvme-feat-err-recovery.txt +++ b/Documentation/nvme-feat-err-recovery.txt @@ -37,6 +37,7 @@ OPTIONS --nsid=:: Sends the command with the requested nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -t :: --tler=:: diff --git a/Documentation/nvme-feat-lba-range-type.txt b/Documentation/nvme-feat-lba-range-type.txt index a7770b7102..96cb57fdd0 100644 --- a/Documentation/nvme-feat-lba-range-type.txt +++ b/Documentation/nvme-feat-lba-range-type.txt @@ -36,6 +36,7 @@ OPTIONS --nsid=:: Sends the command with the requested nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -N :: --num=:: diff --git a/Documentation/nvme-flush.txt b/Documentation/nvme-flush.txt index 88151dc212..2189992443 100644 --- a/Documentation/nvme-flush.txt +++ b/Documentation/nvme-flush.txt @@ -24,6 +24,7 @@ OPTIONS --namespace-id=:: Specify the optional namespace id for this command. Defaults to 0xffffffff, indicating flush for all namespaces. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-format.txt b/Documentation/nvme-format.txt index 021104806e..b6522c8cd5 100644 --- a/Documentation/nvme-format.txt +++ b/Documentation/nvme-format.txt @@ -51,6 +51,7 @@ OPTIONS Send the format command for the specified nsid. This can be used to override the default value for either character device (unspecified) or the block device (result from NVME_IOCTL_ID). + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -l :: --lbaf=:: diff --git a/Documentation/nvme-get-feature.txt b/Documentation/nvme-get-feature.txt index 2d2f93c579..1fb696cdea 100644 --- a/Documentation/nvme-get-feature.txt +++ b/Documentation/nvme-get-feature.txt @@ -37,6 +37,7 @@ OPTIONS --namespace-id=:: Retrieve the feature for the given nsid. This is optional and most features do not use this value. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -f :: --feature-id=:: diff --git a/Documentation/nvme-get-lba-status.txt b/Documentation/nvme-get-lba-status.txt index 2124a6883b..1e8a4c7981 100644 --- a/Documentation/nvme-get-lba-status.txt +++ b/Documentation/nvme-get-lba-status.txt @@ -35,6 +35,7 @@ OPTIONS Send the Get LBA Status command for the specified nsid. This can be used to override the default value for either character device (unspecified) or the block device (result from NVME_IOCTL_ID). + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-get-log.txt b/Documentation/nvme-get-log.txt index 13d469168f..618267c68c 100644 --- a/Documentation/nvme-get-log.txt +++ b/Documentation/nvme-get-log.txt @@ -103,6 +103,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-id-ctrl-list.txt b/Documentation/nvme-id-ctrl-list.txt index 434a9ab9a8..a3ecb819b3 100644 --- a/Documentation/nvme-id-ctrl-list.txt +++ b/Documentation/nvme-id-ctrl-list.txt @@ -37,6 +37,7 @@ OPTIONS specified namespace. If no namespace is given, or set to 0, the command requests the controller list for the entire subsystem, whether or not they are attached to namespace(s). + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-id-ns-descs.txt b/Documentation/nvme-id-ns-descs.txt index bd262d14f4..54d7f217f7 100644 --- a/Documentation/nvme-id-ns-descs.txt +++ b/Documentation/nvme-id-ns-descs.txt @@ -37,6 +37,7 @@ OPTIONS Retrieve the identify namespace identification descriptor structure for the given nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-id-ns-ind.txt b/Documentation/nvme-id-ns-ind.txt index e7ebf8a465..311d1f475c 100644 --- a/Documentation/nvme-id-ns-ind.txt +++ b/Documentation/nvme-id-ns-ind.txt @@ -35,6 +35,7 @@ OPTIONS Retrieve the identify namespace structure for the given nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-id-ns-list.txt b/Documentation/nvme-id-ns-list.txt index e2972721c9..1978b75230 100644 --- a/Documentation/nvme-id-ns-list.txt +++ b/Documentation/nvme-id-ns-list.txt @@ -31,6 +31,7 @@ OPTIONS -n :: --namespace-id=:: Retrieve the identify list structure starting with the given nsid. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -y :: --csi=:: diff --git a/Documentation/nvme-id-ns.txt b/Documentation/nvme-id-ns.txt index 9002203ea0..86ea3b20b8 100644 --- a/Documentation/nvme-id-ns.txt +++ b/Documentation/nvme-id-ns.txt @@ -44,6 +44,7 @@ OPTIONS and 0xFFFFFFFF is given, then the controller returns the identify namespace structure that specifies common capabilities across namespaces for the controller. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. --force:: Request controller return the identify namespace structure even diff --git a/Documentation/nvme-id-nvm-ns.txt b/Documentation/nvme-id-nvm-ns.txt index 24868dff83..24aae5eb55 100644 --- a/Documentation/nvme-id-nvm-ns.txt +++ b/Documentation/nvme-id-nvm-ns.txt @@ -31,6 +31,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -U :: --uuid-index=:: diff --git a/Documentation/nvme-intel-internal-log.txt b/Documentation/nvme-intel-internal-log.txt index 5312ac7c62..9278a4228a 100644 --- a/Documentation/nvme-intel-internal-log.txt +++ b/Documentation/nvme-intel-internal-log.txt @@ -38,6 +38,7 @@ OPTIONS -n :: --namespace-id=:: Namespace to use. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -O :: --output-file=:: diff --git a/Documentation/nvme-intel-smart-log-add.txt b/Documentation/nvme-intel-smart-log-add.txt index 2b72d1fc8f..ebccfe05dc 100644 --- a/Documentation/nvme-intel-smart-log-add.txt +++ b/Documentation/nvme-intel-smart-log-add.txt @@ -36,6 +36,7 @@ OPTIONS to provide this log on a per-namespace basis (see the NVMe Identify Controller for this capability). The default nsid to use is 0xffffffff for the device global SMART log. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-io-mgmt-recv.txt b/Documentation/nvme-io-mgmt-recv.txt index f454ef5183..35a107723c 100644 --- a/Documentation/nvme-io-mgmt-recv.txt +++ b/Documentation/nvme-io-mgmt-recv.txt @@ -36,6 +36,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -d :: --data=:: diff --git a/Documentation/nvme-io-mgmt-send.txt b/Documentation/nvme-io-mgmt-send.txt index f5f22c9085..bdf06cf93d 100644 --- a/Documentation/nvme-io-mgmt-send.txt +++ b/Documentation/nvme-io-mgmt-send.txt @@ -35,6 +35,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -d :: --data=:: diff --git a/Documentation/nvme-io-passthru.txt b/Documentation/nvme-io-passthru.txt index 375701d9cb..a2f2deb298 100644 --- a/Documentation/nvme-io-passthru.txt +++ b/Documentation/nvme-io-passthru.txt @@ -64,6 +64,7 @@ OPTIONS -n :: --namespace-id=:: The value for the ns-id in the command. Defaults to '0'. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -[2-9] :: --cdw[2-3,10-15]=:: diff --git a/Documentation/nvme-log-dispersed-ns-participating-nss.txt b/Documentation/nvme-log-dispersed-ns-participating-nss.txt index 6746bde76a..f7cbfc3844 100644 --- a/Documentation/nvme-log-dispersed-ns-participating-nss.txt +++ b/Documentation/nvme-log-dispersed-ns-participating-nss.txt @@ -27,6 +27,7 @@ OPTIONS The namespace identifier to delete. Retrieve the dispersed namespace participating NVM subsystems log for the given nsid. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-log-error.txt b/Documentation/nvme-log-error.txt index 22308ff5c2..df8a1b1973 100644 --- a/Documentation/nvme-log-error.txt +++ b/Documentation/nvme-log-error.txt @@ -65,6 +65,7 @@ OPTIONS -n :: --namespace-id=:: Output specified NSID entry only. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -t :: --trtype=:: diff --git a/Documentation/nvme-log-smart.txt b/Documentation/nvme-log-smart.txt index c5498fb09d..0b22a30106 100644 --- a/Documentation/nvme-log-smart.txt +++ b/Documentation/nvme-log-smart.txt @@ -33,6 +33,7 @@ OPTIONS provide this log on a per-namespace basis (see the NVMe Identify Controller for this capability). The default nsid to use is 0xffffffff for the device global SMART log. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-ns-attach.txt b/Documentation/nvme-ns-attach.txt index b0895d5ba1..4f7c084c7c 100644 --- a/Documentation/nvme-ns-attach.txt +++ b/Documentation/nvme-ns-attach.txt @@ -26,6 +26,7 @@ OPTIONS -n :: --namespace-id=:: The namespace identifier to attach but not attached already. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -c :: --controllers=:: diff --git a/Documentation/nvme-ns-delete.txt b/Documentation/nvme-ns-delete.txt index bcc2e2633a..aebc19ccdf 100644 --- a/Documentation/nvme-ns-delete.txt +++ b/Documentation/nvme-ns-delete.txt @@ -25,6 +25,7 @@ OPTIONS -n :: --namespace-id=:: The namespace identifier to delete. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-ns-detach.txt b/Documentation/nvme-ns-detach.txt index eaea37d8e6..ebcf480230 100644 --- a/Documentation/nvme-ns-detach.txt +++ b/Documentation/nvme-ns-detach.txt @@ -23,6 +23,7 @@ OPTIONS -n :: --namespace-id=:: The namespace identifier to detach. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -c :: --controllers=:: diff --git a/Documentation/nvme-nvme-mi-recv.txt b/Documentation/nvme-nvme-mi-recv.txt index 9a0e6925b7..aa16b83380 100644 --- a/Documentation/nvme-nvme-mi-recv.txt +++ b/Documentation/nvme-nvme-mi-recv.txt @@ -40,6 +40,7 @@ OPTIONS -n :: --namespace-id=:: The value for the ns-id in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -l :: --data-len=:: diff --git a/Documentation/nvme-nvme-mi-send.txt b/Documentation/nvme-nvme-mi-send.txt index 4bbdd7b40e..658757922c 100644 --- a/Documentation/nvme-nvme-mi-send.txt +++ b/Documentation/nvme-nvme-mi-send.txt @@ -40,6 +40,7 @@ OPTIONS -n :: --namespace-id=:: The value for the ns-id in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -l :: --data-len=:: diff --git a/Documentation/nvme-ocp-get-clear-pcie-correctable-errors.txt b/Documentation/nvme-ocp-get-clear-pcie-correctable-errors.txt index 2c4365ce84..76fbd50683 100644 --- a/Documentation/nvme-ocp-get-clear-pcie-correctable-errors.txt +++ b/Documentation/nvme-ocp-get-clear-pcie-correctable-errors.txt @@ -30,6 +30,7 @@ OPTIONS NSID: Assign the different kind of nsid value(like active, inactive and invalid nsids) and check the get feature command response: + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: diff --git a/Documentation/nvme-ocp-get-idle-wakeup-time.txt b/Documentation/nvme-ocp-get-idle-wakeup-time.txt index 2b087f60ba..a422d4d8cd 100644 --- a/Documentation/nvme-ocp-get-idle-wakeup-time.txt +++ b/Documentation/nvme-ocp-get-idle-wakeup-time.txt @@ -47,6 +47,7 @@ OPTIONS |3|Supported capabilities |4-7|Reserved |================== + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-ocp-get-latency-monitor.txt b/Documentation/nvme-ocp-get-latency-monitor.txt index 111d0996e4..82f8ec7fdc 100644 --- a/Documentation/nvme-ocp-get-latency-monitor.txt +++ b/Documentation/nvme-ocp-get-latency-monitor.txt @@ -30,6 +30,7 @@ OPTIONS NSID: Assign the different kind of nsid value(like active, inactive and invalid nsids) and check the get feature command response: + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: diff --git a/Documentation/nvme-ocp-get-telemetry-profile.txt b/Documentation/nvme-ocp-get-telemetry-profile.txt index 9cc40e1885..7b0ceea43d 100644 --- a/Documentation/nvme-ocp-get-telemetry-profile.txt +++ b/Documentation/nvme-ocp-get-telemetry-profile.txt @@ -30,6 +30,7 @@ OPTIONS NSID: Assign the different kind of nsid value(like active, inactive and invalid nsids) and check the get feature command response: + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: diff --git a/Documentation/nvme-resv-acquire.txt b/Documentation/nvme-resv-acquire.txt index 6c0d61bd80..9d0df48a66 100644 --- a/Documentation/nvme-resv-acquire.txt +++ b/Documentation/nvme-resv-acquire.txt @@ -32,6 +32,7 @@ OPTIONS --namespace-id=:: Override the nsid field. If using the admin character device, this parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -c :: --crkey=:: diff --git a/Documentation/nvme-resv-register.txt b/Documentation/nvme-resv-register.txt index 5d290e3f7e..ff42ba898c 100644 --- a/Documentation/nvme-resv-register.txt +++ b/Documentation/nvme-resv-register.txt @@ -31,6 +31,7 @@ OPTIONS --namespace-id=:: Override the nsid field. If using the admin character device, this parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -c :: --crkey=:: diff --git a/Documentation/nvme-resv-release.txt b/Documentation/nvme-resv-release.txt index b63378b69a..e4f06c83a8 100644 --- a/Documentation/nvme-resv-release.txt +++ b/Documentation/nvme-resv-release.txt @@ -30,6 +30,7 @@ OPTIONS --namespace-id=:: Override the nsid field. If using the admin character device, this parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -c :: --crkey=:: diff --git a/Documentation/nvme-resv-report.txt b/Documentation/nvme-resv-report.txt index 98b1913324..278cb884f3 100644 --- a/Documentation/nvme-resv-report.txt +++ b/Documentation/nvme-resv-report.txt @@ -41,6 +41,7 @@ OPTIONS Retrieve the reservation report structure for the given nsid. This is required for the character devices, or overrides the block nsid if given. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -d :: --numd=:: diff --git a/Documentation/nvme-seagate-get-ctrl-tele.txt b/Documentation/nvme-seagate-get-ctrl-tele.txt index 58d9fcecc1..fbaffc4ba9 100644 --- a/Documentation/nvme-seagate-get-ctrl-tele.txt +++ b/Documentation/nvme-seagate-get-ctrl-tele.txt @@ -24,6 +24,7 @@ OPTIONS -n :: --namespace-id=:: Desired namespace + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -b:: --raw-binary:: diff --git a/Documentation/nvme-seagate-get-host-tele.txt b/Documentation/nvme-seagate-get-host-tele.txt index e1ae02dbf7..af8eb548a4 100644 --- a/Documentation/nvme-seagate-get-host-tele.txt +++ b/Documentation/nvme-seagate-get-host-tele.txt @@ -24,6 +24,7 @@ OPTIONS -n :: --namespace-id=:: desired namespace + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -i :: --log_specific=:: diff --git a/Documentation/nvme-seagate-vs-internal-log.txt b/Documentation/nvme-seagate-vs-internal-log.txt index 65797702ac..33f0cd0b05 100644 --- a/Documentation/nvme-seagate-vs-internal-log.txt +++ b/Documentation/nvme-seagate-vs-internal-log.txt @@ -24,6 +24,7 @@ OPTIONS -n :: --namespace-id=:: Desired namespace + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -f :: --dump-file=:: diff --git a/Documentation/nvme-security-recv.txt b/Documentation/nvme-security-recv.txt index 5435611dd1..c187eb37b9 100644 --- a/Documentation/nvme-security-recv.txt +++ b/Documentation/nvme-security-recv.txt @@ -41,6 +41,7 @@ OPTIONS -n :: --namespace-id=:: Target a specific namespace for this security command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -N :: --nssf=:: diff --git a/Documentation/nvme-security-send.txt b/Documentation/nvme-security-send.txt index e15534bbfe..15c4cff334 100644 --- a/Documentation/nvme-security-send.txt +++ b/Documentation/nvme-security-send.txt @@ -38,6 +38,7 @@ OPTIONS -n :: --namespace-id=:: Target a specific namespace for this security command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -N :: --nssf=:: diff --git a/Documentation/nvme-set-feature.txt b/Documentation/nvme-set-feature.txt index ddacc53ee9..c75eed96b6 100644 --- a/Documentation/nvme-set-feature.txt +++ b/Documentation/nvme-set-feature.txt @@ -35,6 +35,7 @@ OPTIONS --namespace-id=:: Sets the feature for the given nsid. This is optional and most features do not use this value. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -f :: --feature-id=:: diff --git a/Documentation/nvme-sndk-namespace-resize.txt b/Documentation/nvme-sndk-namespace-resize.txt index bfce941b59..bf42da72af 100644 --- a/Documentation/nvme-sndk-namespace-resize.txt +++ b/Documentation/nvme-sndk-namespace-resize.txt @@ -28,6 +28,7 @@ OPTIONS -n :: --namespace-id=:: Namespace ID; ID of the namespace to resize + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -O :: --op-option=:: diff --git a/Documentation/nvme-sndk-vs-cloud-log.txt b/Documentation/nvme-sndk-vs-cloud-log.txt index cba3133e82..fcaaab5d68 100644 --- a/Documentation/nvme-sndk-vs-cloud-log.txt +++ b/Documentation/nvme-sndk-vs-cloud-log.txt @@ -32,6 +32,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-sndk-vs-device-waf.txt b/Documentation/nvme-sndk-vs-device-waf.txt index 5b2b90baaf..6651d607f6 100644 --- a/Documentation/nvme-sndk-vs-device-waf.txt +++ b/Documentation/nvme-sndk-vs-device-waf.txt @@ -33,6 +33,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-sndk-vs-hw-rev-log.txt b/Documentation/nvme-sndk-vs-hw-rev-log.txt index 81e4400dfe..8e2c2f7664 100644 --- a/Documentation/nvme-sndk-vs-hw-rev-log.txt +++ b/Documentation/nvme-sndk-vs-hw-rev-log.txt @@ -31,6 +31,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-sndk-vs-smart-add-log.txt b/Documentation/nvme-sndk-vs-smart-add-log.txt index d21bf4b4a4..42eaf922e6 100644 --- a/Documentation/nvme-sndk-vs-smart-add-log.txt +++ b/Documentation/nvme-sndk-vs-smart-add-log.txt @@ -54,6 +54,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. Valid Interval values and description :- diff --git a/Documentation/nvme-solidigm-smart-log-add.txt b/Documentation/nvme-solidigm-smart-log-add.txt index bec6847c99..aea7c73493 100644 --- a/Documentation/nvme-solidigm-smart-log-add.txt +++ b/Documentation/nvme-solidigm-smart-log-add.txt @@ -25,6 +25,7 @@ OPTIONS --namespace-id=:: Retrieve the log for the given nsid. If not specified, the log will be retrieved for all namespaces. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-toshiba-vs-smart-add-log.txt b/Documentation/nvme-toshiba-vs-smart-add-log.txt index ba84def70a..1370cabf00 100644 --- a/Documentation/nvme-toshiba-vs-smart-add-log.txt +++ b/Documentation/nvme-toshiba-vs-smart-add-log.txt @@ -39,6 +39,7 @@ OPTIONS -n :: --namespace-id=:: + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -O :: --output-file=:: diff --git a/Documentation/nvme-verify.txt b/Documentation/nvme-verify.txt index aaf1c6adba..fcf4b31738 100644 --- a/Documentation/nvme-verify.txt +++ b/Documentation/nvme-verify.txt @@ -30,6 +30,7 @@ OPTIONS -n :: --namespace-id=:: Namespace ID use in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-block=:: diff --git a/Documentation/nvme-wdc-namespace-resize.txt b/Documentation/nvme-wdc-namespace-resize.txt index 76cc592cd6..44e25b4bd6 100644 --- a/Documentation/nvme-wdc-namespace-resize.txt +++ b/Documentation/nvme-wdc-namespace-resize.txt @@ -28,6 +28,7 @@ OPTIONS -n :: --namespace-id=:: Namespace ID; ID of the namespace to resize + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -O :: --op-option=:: diff --git a/Documentation/nvme-wdc-vs-cloud-log.txt b/Documentation/nvme-wdc-vs-cloud-log.txt index 8e72b161f8..eab2bff5ae 100644 --- a/Documentation/nvme-wdc-vs-cloud-log.txt +++ b/Documentation/nvme-wdc-vs-cloud-log.txt @@ -32,6 +32,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-wdc-vs-device-waf.txt b/Documentation/nvme-wdc-vs-device-waf.txt index 71d0a13f63..e9b0abf74d 100644 --- a/Documentation/nvme-wdc-vs-device-waf.txt +++ b/Documentation/nvme-wdc-vs-device-waf.txt @@ -33,6 +33,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-wdc-vs-hw-rev-log.txt b/Documentation/nvme-wdc-vs-hw-rev-log.txt index 061c3913fe..0f5126d682 100644 --- a/Documentation/nvme-wdc-vs-hw-rev-log.txt +++ b/Documentation/nvme-wdc-vs-hw-rev-log.txt @@ -31,6 +31,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-wdc-vs-smart-add-log.txt b/Documentation/nvme-wdc-vs-smart-add-log.txt index 2c1735d6bc..e7aae07d80 100644 --- a/Documentation/nvme-wdc-vs-smart-add-log.txt +++ b/Documentation/nvme-wdc-vs-smart-add-log.txt @@ -54,6 +54,7 @@ OPTIONS 0xffffffff if not given. This option may not affect anything depending on the log page, which may or may not be specific to a namespace. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. Valid Interval values and description :- diff --git a/Documentation/nvme-write-uncor.txt b/Documentation/nvme-write-uncor.txt index c777423f8f..91b12df18b 100644 --- a/Documentation/nvme-write-uncor.txt +++ b/Documentation/nvme-write-uncor.txt @@ -33,6 +33,7 @@ OPTIONS -n :: --namespace-id=:: Namespace ID use in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -T :: --dir-type=:: diff --git a/Documentation/nvme-write-zeroes.txt b/Documentation/nvme-write-zeroes.txt index e18c0d596b..3e6dff6407 100644 --- a/Documentation/nvme-write-zeroes.txt +++ b/Documentation/nvme-write-zeroes.txt @@ -81,6 +81,7 @@ metadata is passes. -n :: --namespace-id=:: Namespace ID use in the command. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -S :: --storage-tag=:: diff --git a/Documentation/nvme-zns-close-zone.txt b/Documentation/nvme-zns-close-zone.txt index 5ec4de55aa..3dc4d1c13f 100644 --- a/Documentation/nvme-zns-close-zone.txt +++ b/Documentation/nvme-zns-close-zone.txt @@ -28,6 +28,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-finish-zone.txt b/Documentation/nvme-zns-finish-zone.txt index 4414869fa2..9a6eef589e 100644 --- a/Documentation/nvme-zns-finish-zone.txt +++ b/Documentation/nvme-zns-finish-zone.txt @@ -29,6 +29,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-id-ns.txt b/Documentation/nvme-zns-id-ns.txt index 02533bf3b3..c3efedfa34 100644 --- a/Documentation/nvme-zns-id-ns.txt +++ b/Documentation/nvme-zns-id-ns.txt @@ -29,6 +29,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. include::global-options.txt[] diff --git a/Documentation/nvme-zns-offline-zone.txt b/Documentation/nvme-zns-offline-zone.txt index ca40f962c2..2d7bc31163 100644 --- a/Documentation/nvme-zns-offline-zone.txt +++ b/Documentation/nvme-zns-offline-zone.txt @@ -28,6 +28,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-open-zone.txt b/Documentation/nvme-zns-open-zone.txt index 4f218460af..ebdf57c6c6 100644 --- a/Documentation/nvme-zns-open-zone.txt +++ b/Documentation/nvme-zns-open-zone.txt @@ -29,6 +29,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-report-zones.txt b/Documentation/nvme-zns-report-zones.txt index 0abcb0d2c0..47bd8b029d 100644 --- a/Documentation/nvme-zns-report-zones.txt +++ b/Documentation/nvme-zns-report-zones.txt @@ -35,6 +35,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-reset-zone.txt b/Documentation/nvme-zns-reset-zone.txt index 238db59d66..91aa408842 100644 --- a/Documentation/nvme-zns-reset-zone.txt +++ b/Documentation/nvme-zns-reset-zone.txt @@ -29,6 +29,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-set-zone-desc.txt b/Documentation/nvme-zns-set-zone-desc.txt index b96cd5dd37..3fb8bc38d0 100644 --- a/Documentation/nvme-zns-set-zone-desc.txt +++ b/Documentation/nvme-zns-set-zone-desc.txt @@ -28,6 +28,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-zone-append.txt b/Documentation/nvme-zns-zone-append.txt index a387a9be73..469fdf94aa 100644 --- a/Documentation/nvme-zns-zone-append.txt +++ b/Documentation/nvme-zns-zone-append.txt @@ -38,6 +38,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --zslba=:: diff --git a/Documentation/nvme-zns-zone-mgmt-recv.txt b/Documentation/nvme-zns-zone-mgmt-recv.txt index 9bd916d046..dda65646c3 100644 --- a/Documentation/nvme-zns-zone-mgmt-recv.txt +++ b/Documentation/nvme-zns-zone-mgmt-recv.txt @@ -33,6 +33,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-zone-mgmt-send.txt b/Documentation/nvme-zns-zone-mgmt-send.txt index 6ec6ce10b4..9290e44bfb 100644 --- a/Documentation/nvme-zns-zone-mgmt-send.txt +++ b/Documentation/nvme-zns-zone-mgmt-send.txt @@ -31,6 +31,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -s :: --start-lba=:: diff --git a/Documentation/nvme-zns-zrwa-flush-zone.txt b/Documentation/nvme-zns-zrwa-flush-zone.txt index ab2f21c0ea..6950f58ff2 100644 --- a/Documentation/nvme-zns-zrwa-flush-zone.txt +++ b/Documentation/nvme-zns-zrwa-flush-zone.txt @@ -28,6 +28,7 @@ OPTIONS Use the provided namespace id for the command. If not provided, the namespace id of the block device will be used. If the command is issued to a non-block device, the parameter is required. + The broadcast value 0xffffffff can also be given as 'all' or '-1'. -l :: --lba=:: diff --git a/plugins/dapustor/dapustor-nvme.c b/plugins/dapustor/dapustor-nvme.c index 57b4246e7b..578e587168 100644 --- a/plugins/dapustor/dapustor-nvme.c +++ b/plugins/dapustor/dapustor-nvme.c @@ -527,7 +527,8 @@ static int dapustor_additional_smart_log(int argc, char **argv, struct command * }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG_JSON("json", 'j', &cfg.json, json)); diff --git a/plugins/dir/dir-plugin.c b/plugins/dir/dir-plugin.c index 723b3c8c53..cedc0f9134 100644 --- a/plugins/dir/dir-plugin.c +++ b/plugins/dir/dir-plugin.c @@ -72,7 +72,8 @@ static int dir_send(int argc, char **argv, struct command *acmd, struct plugin * }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), OPT_BYTE("target-dir", 'T', &cfg.ttype, ttype), @@ -198,7 +199,8 @@ static int dir_receive(int argc, char **argv, struct command *acmd, struct plugi }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_directive), OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index aabf8bad22..97b8850750 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -302,7 +302,8 @@ static int fdp_status(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -372,7 +373,8 @@ static int fdp_update(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + nvme_nsid_values), OPT_LIST("pids", 'p', &cfg.pids, _pids)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -441,7 +443,8 @@ static int fdp_set_events(int argc, char **argv, struct command *acmd, struct pl }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + nvme_nsid_values), OPT_SHRT("placement-handle", 'p', &cfg.ph, ph), OPT_FLAG("enable", 'e', &cfg.enable, enable), OPT_FLAG("save", 's', &cfg.sv, sv), diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 6d96e5b12d..5aa5b8e6b4 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -327,7 +327,8 @@ static int feat_perfc(int argc, char **argv, struct command *acmd, struct plugin struct perfc_config cfg = { 0 }; FEAT_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_optional), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_optional, nvme_nsid_values), OPT_BYTE("attri", 'a', &cfg.attri, attri), OPT_FLAG("rvspa", 'r', &cfg.rvspa, rvspa), OPT_BYTE("r4karl", 'R', &cfg.r4karl, r4karl), @@ -936,7 +937,8 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, struct config cfg = { 0 }; FEAT_ARGS(opts, - OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), OPT_SHRT("tler", 't', &cfg.tler, tler), OPT_FLAG("dulbe", 'd', &cfg.dulbe, dulbe)); @@ -1245,7 +1247,8 @@ static int feat_lba_range_type(int argc, char **argv, struct command *acmd, /* -s is --slbas and -h is help, --save and --hlbars are long only */ FEAT_ARGS_SAVE(opts, 0, - OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), OPT_BYTE("num", 'N', &cfg.num, num_desc), OPT_LIST("types", 't', &cfg.types, types_desc), OPT_LIST("lbaros", 'l', &cfg.lbaros, lbaros_desc), diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index ce36735292..0a207260e1 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -52,7 +52,8 @@ static int list_ctrl(int argc, char **argv, struct command *acmd, struct plugin NVME_ARGS(opts, OPT_SHRT("cntid", 'c', &cfg.cntid, controller), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_optional)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_optional, nvme_nsid_values)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -110,7 +111,8 @@ static int list_ns(int argc, char **argv, struct command *acmd, struct plugin *p }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_INT("csi", 'y', &cfg.csi, csi), OPT_FLAG("all", 'a', &cfg.all, all)); @@ -329,7 +331,8 @@ static int nvm_id_ns(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -470,7 +473,8 @@ static int ns_descs(int argc, char **argv, struct command *acmd, struct plugin * }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -540,7 +544,8 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_FLAG("force", 0, &cfg.force, force), OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify)); @@ -615,7 +620,8 @@ static int cmd_set_independent_id_ns(int argc, char **argv, struct command *acmd }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 56d1d387b0..4fdd617baf 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -363,7 +363,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG_JSON("json", 'j', &cfg.json, json)); @@ -1379,7 +1380,8 @@ static int get_internal_log(int argc, char **argv, struct command *acmd, OPT_UINT("log", 'l', &cfg.log, log), OPT_INT("region", 'r', &cfg.core, core), OPT_INT("nlognum", 'm', &cfg.lnum, nlognum), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_FILE("output-file", 'O', &cfg.file, file), OPT_FLAG("verbose-nlog", 'V', &cfg.verbose, verbose)); diff --git a/plugins/io-mgmt/io-mgmt-plugin.c b/plugins/io-mgmt/io-mgmt-plugin.c index 38c283298c..557fc6a567 100644 --- a/plugins/io-mgmt/io-mgmt-plugin.c +++ b/plugins/io-mgmt/io-mgmt-plugin.c @@ -57,7 +57,8 @@ static int io_mgmt_send(int argc, char **argv, struct command *acmd, struct plug }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_id_desired, nvme_nsid_values), OPT_SHRT("mos", 's', &cfg.mos, mos), OPT_BYTE("mo", 'm', &cfg.mo, mo), OPT_FILE("data", 'd', &cfg.file, data), @@ -137,7 +138,8 @@ static int io_mgmt_recv(int argc, char **argv, struct command *acmd, struct plug }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_id_desired, nvme_nsid_values), OPT_SHRT("mos", 's', &cfg.mos, mos), OPT_BYTE("mo", 'm', &cfg.mo, mo), OPT_FILE("data", 'd', &cfg.file, data), diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 702d4fca1a..7a525e3cdf 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -137,7 +137,8 @@ static int get_smart_log(int argc, char **argv, struct command *acmd, struct plu }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -780,7 +781,8 @@ static int get_error_log(int argc, char **argv, struct command *acmd, struct plu OPT_SHRT("sqid", 'S', &cfg.flt.sqid, sqid), OPT_SHRT("status", 's', &cfg.flt.status, status), OPT_SUFFIX("lba", 'l', &cfg.flt.lba, lba), - OPT_UINT("namespace-id", 'n', &cfg.flt.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.flt.nsid, nsid, + nvme_nsid_values), OPT_BYTE("trtype", 't', &cfg.flt.trtype, trtype), OPT_BYTE("csi", 'c', &cfg.flt.csi, csi), OPT_BYTE("opcode", 'O', &cfg.flt.opcode, opcode)); @@ -2106,7 +2108,8 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct .namespace_id = 1, }; - NVME_ARGS(opts, OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired)); + NVME_ARGS(opts, OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 91cd74404e..0ea107be02 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -439,7 +439,8 @@ static int mb_get_additional_smart_log(int argc, char **argv, struct command *ac }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/ns/ns-plugin.c b/plugins/ns/ns-plugin.c index a2b631c85b..92cd10295b 100644 --- a/plugins/ns/ns-plugin.c +++ b/plugins/ns/ns-plugin.c @@ -90,7 +90,8 @@ static int delete_ns(int argc, char **argv, struct command *acmd, struct plugin NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -144,7 +145,8 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + nvme_nsid_values), OPT_LIST("controllers", 'c', &cfg.cntlist, cont)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/nvme-mi/nvme-mi-plugin.c b/plugins/nvme-mi/nvme-mi-plugin.c index 7687d66d67..61f625e38d 100644 --- a/plugins/nvme-mi/nvme-mi-plugin.c +++ b/plugins/nvme-mi/nvme-mi-plugin.c @@ -71,7 +71,8 @@ static int libnvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc NVME_ARGS(opts, OPT_BYTE("opcode", 'O', &cfg.opcode, opcode), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_UINT("nmimt", 'm', &cfg.nmimt, nmimt), OPT_UINT("nmd0", '0', &cfg.nmd0, nmd0), diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c index 878b99b171..ea024dad77 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -90,7 +90,8 @@ int get_ocp_error_counters(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 282b420696..39f82f7006 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -444,7 +444,8 @@ static int ocp_get_latency_monitor_feature(int argc, char **argv, struct command NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1542,7 +1543,8 @@ static int ocp_get_telemetry_profile_feature(int argc, char **argv, struct comma NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -2622,7 +2624,8 @@ static int ocp_get_idle_wakeup_time_config_feature(int argc, char **argv, NVME_ARGS(opts, OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid), + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/resv/resv-plugin.c b/plugins/resv/resv-plugin.c index 71cabb6c48..88af121ce6 100644 --- a/plugins/resv/resv-plugin.c +++ b/plugins/resv/resv-plugin.c @@ -62,7 +62,8 @@ static int resv_acquire(int argc, char **argv, struct command *acmd, struct plug }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), OPT_SUFFIX("prkey", 'p', &cfg.prkey, prkey), OPT_BYTE("rtype", 't', &cfg.rtype, rtype), @@ -131,7 +132,8 @@ static int resv_register(int argc, char **argv, struct command *acmd, struct plu }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), OPT_SUFFIX("nrkey", 'k', &cfg.nrkey, nrkey), OPT_BYTE("rrega", 'r', &cfg.rrega, rrega), @@ -208,7 +210,8 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_desired, nvme_nsid_values), OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), OPT_BYTE("rtype", 't', &cfg.rtype, rtype), OPT_BYTE("rrela", 'a', &cfg.rrela, rrela), @@ -299,7 +302,8 @@ static int resv_report(int argc, char **argv, struct command *acmd, struct plugi }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_id_desired, nvme_nsid_values), OPT_UINT("numd", 'd', &cfg.numd, numd), OPT_FLAG("eds", 'e', &cfg.eds, eds), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump)); diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index de5d5f353a..d91fa1e2de 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -623,7 +623,8 @@ static int sndk_vs_smart_add_log(int argc, char **argv, OPT_BYTE("interval", 'i', &cfg.interval, interval), OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index ed465493ff..96b8807b38 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -348,7 +348,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG_JSON("json", 'j', &cfg.json, json)); @@ -908,7 +909,8 @@ static int sfx_set_feature(int argc, char **argv, struct command *acmd, struct p }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), OPT_UINT("value", 'V', &cfg.value, value), OPT_FLAG("force", 's', &cfg.force, force)); @@ -995,7 +997,8 @@ static int sfx_get_feature(int argc, char **argv, struct command *acmd, struct p }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index d821b49464..ab87714231 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -1545,7 +1545,8 @@ static int get_host_tele(int argc, char **argv, struct command *acmd, struct plu }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_UINT("log_specific", 'i', &cfg.log_id, log_specific), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); @@ -1650,7 +1651,8 @@ static int get_ctrl_tele(int argc, char **argv, struct command *acmd, struct plu }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1763,7 +1765,8 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, struct p }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_FILE("dump-file", 'f', &cfg.file, file)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/security/security-plugin.c b/plugins/security/security-plugin.c index d4bd656722..07e1ecfc8d 100644 --- a/plugins/security/security-plugin.c +++ b/plugins/security/security-plugin.c @@ -73,7 +73,8 @@ static int sec_send(int argc, char **argv, struct command *acmd, struct plugin * NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_FILE("file", 'f', &cfg.file, file), OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), OPT_BYTE("secp", 'p', &cfg.secp, secp), @@ -192,7 +193,8 @@ static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin * NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_UINT("size", 'x', &cfg.size, size), OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), OPT_BYTE("secp", 'p', &cfg.secp, secp), diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index dac9d6bcdd..e854836f45 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -135,7 +135,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -203,7 +204,8 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), @@ -282,7 +284,8 @@ static int set_additional_feature(int argc, char **argv, struct command *acmd, s }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), OPT_UINT("value", 'V', &cfg.value, value), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 3f85fd490e..52f8ef0529 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -271,7 +271,8 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *acm NVME_ARGS(opts, OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - "(optional) desired namespace")); + "(optional) desired namespace", + nvme_nsid_values)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/ssstc/ssstc-nvme.c b/plugins/ssstc/ssstc-nvme.c index 81bce480f7..32ee0b93ef 100644 --- a/plugins/ssstc/ssstc-nvme.c +++ b/plugins/ssstc/ssstc-nvme.c @@ -404,7 +404,8 @@ int ssstc_get_add_smart_log(int argc, char **argv, struct command *acmd, struct }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG_JSON("json", 'j', &cfg.json, json)); diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index 7ccd3fc1ac..44ae58517e 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -429,7 +429,8 @@ static int vendor_log(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FILE("output-file", 'O', &cfg.output_file, output_file), OPT_UINT("log", 'l', &cfg.log, log)); diff --git a/plugins/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index f3413eb09d..5ff1b0fc27 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -124,7 +124,7 @@ }, "values": { "type": "array", - "description": "The set of values the parser enforces for this option, when it constrains them to a fixed set. Absent when the value is unconstrained.", + "description": "The names the option accepts as values, e.g. the output formats or named special values such as \"all\" for a namespace identifier. A numeric option also takes a number. Absent when the option has no named values.", "items": { "type": "string" }, "minItems": 1 } diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index f80afa76a7..25ca8235ac 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -608,9 +608,9 @@ static bool opt_is_emittable(const struct command_metadata_option *o) * output-format is special-cased because its values are not represented via an * opt_val table. Its help text lists the formats the command supports * ("Output format: normal|json"), see nvme_args_set_output_formats(). Every - * other value set comes from the option's opt_val table, which is the set the - * parser actually enforces; options whose value is unconstrained (e.g. any - * OPT_UINT such as output-format-version) have no values array. + * other value set comes from the option's opt_val table: the names it + * accepts, a numeric option also takes a number. Options without names + * (e.g. output-format-version) have no values array. */ static struct json_object *json_option_values(const struct command_metadata_option *o) { diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 59d2af7fb3..d48cc6d870 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -8458,7 +8458,8 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *acmd, OPT_BYTE("interval", 'i', &cfg.interval, interval), OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8691,7 +8692,8 @@ static int wdc_vs_cloud_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8760,7 +8762,8 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8850,7 +8853,8 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -10684,7 +10688,8 @@ static int wdc_namespace_resize(int argc, char **argv, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_UINT("op-option", 'O', &cfg.op_option, op_option)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -12447,7 +12452,8 @@ static int wdc_cloud_boot_SSD_version(int argc, char **argv, struct command *acm }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values)); ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index 8380070860..b92b43752e 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -413,7 +413,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = ymtc_parse_options(&ctx, &hdl, argc, argv, desc, opts,&model); diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index e325b51982..7bbbfd6209 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -191,7 +191,8 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu struct config cfg = { }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); @@ -250,7 +251,8 @@ static int zns_mgmt_send(int argc, char **argv, struct command *acmd, struct plu struct config cfg = {}; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_FLAG("select-all", 'a', &cfg.select_all, select_all)); @@ -344,7 +346,8 @@ static int zone_mgmt_send(int argc, char **argv, struct command *acmd, struct pl struct config cfg = {}; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_FLAG("zsaso", 'O', &cfg.zsaso, zsaso), OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), @@ -460,7 +463,8 @@ static int open_zone(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), OPT_FLAG("select-all", 'a', &cfg.select_all, select_all)); @@ -528,7 +532,8 @@ static int set_zone_desc(int argc, char **argv, struct command *acmd, struct plu struct config cfg = {}; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), OPT_FILE("data", 'd', &cfg.file, data)); @@ -610,7 +615,8 @@ static int zrwa_flush_zone(int argc, char **argv, struct command *acmd, struct p struct config cfg = {}; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("lba", 'l', &cfg.lba, slba)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -664,7 +670,8 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *acmd, struct pl struct config cfg = { }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_BYTE("zra", 'z', &cfg.zra, zra), OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), @@ -755,7 +762,8 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), OPT_UINT("state", 'S', &cfg.state, state), @@ -920,7 +928,8 @@ static int zone_append(int argc, char **argv, struct command *acmd, struct plugi struct config cfg = {}; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), @@ -1079,7 +1088,8 @@ static int changed_zone_list(int argc, char **argv, struct command *acmd, struct struct config cfg = { }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), OPT_FLAG("rae", 'r', &cfg.rae, rae)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/src/argconfig.c b/src/argconfig.c index dd26ed6eac..3716ffc044 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -83,6 +83,31 @@ void argconfig_append_usage(const char *str) append_usage_str = str; } +/* The names an option accepts, below its description */ +static void show_value_names(FILE *out, const struct argconfig_opt_val *v) +{ + __cleanup_free char *names = NULL; + size_t len = sizeof("\nNames:"); + const struct argconfig_opt_val *n; + char *p; + + if (!v || !v->str) + return; + + for (n = v; n->str; n++) + len += strlen(n->str) + 2; + + names = malloc(len); + if (!names) + return; + + p = names + sprintf(names, "\nNames:"); + for (n = v; n->str; n++) + p += sprintf(p, " %s%s", n->str, n[1].str ? "," : ""); + + shr_print_word_wrapped(names, 44, 44, out); +} + static void show_option(FILE *out, const struct argconfig_commandline_options *option) { @@ -112,6 +137,7 @@ static void show_option(FILE *out, if (option->help) { shr_print_word_wrapped("--- ", 40, b - buffer, out); shr_print_word_wrapped(option->help, 44, 44, out); + show_value_names(out, option->opt_val); } fprintf(out, "\n"); } @@ -343,18 +369,31 @@ argconfig_match_val(struct argconfig_opt_val *v, const char *str) return match; } -static int argconfig_parse_val(struct argconfig_commandline_options *s, - char *arg) +/* + * Store the value which @str names in the value table of @s. False if + * the option has no value of that name. + */ +bool argconfig_set_value_name(struct argconfig_commandline_options *s, + const char *str) { struct argconfig_opt_val *v = s->opt_val; if (v) - v = argconfig_match_val(v, arg); + v = argconfig_match_val(v, str); if (!v) - return argconfig_parse_type(s, arg); + return false; argconfig_set_opt_val(v->type, &v->val, s->default_value); - return 0; + return true; +} + +static int argconfig_parse_val(struct argconfig_commandline_options *s, + char *arg) +{ + if (argconfig_set_value_name(s, arg)) + return 0; + + return argconfig_parse_type(s, arg); } /* --verbose counts, a command may have a flag of the same name */ diff --git a/src/argconfig.h b/src/argconfig.h index f9084ec09c..3c60bcf955 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -163,7 +163,7 @@ enum argconfig_types { {s, CFG_LONG_SUFFIX, .val.long_suffix = v} #define VAL_UINT(s, v) \ - {s, CFG_POSITIVE, v} + {s, CFG_POSITIVE, .val.positive = v} #define VAL_INT(s, v) \ {s, CFG_INT, .val.int_val = v} @@ -246,3 +246,5 @@ void argconfig_set_parse_hook(argconfig_parse_hook_fn hook); bool argconfig_given(struct argconfig_commandline_options *options, const void *value); +bool argconfig_set_value_name(struct argconfig_commandline_options *option, + const char *str); diff --git a/src/args.h b/src/args.h index d84b6c6188..3d183bbd18 100644 --- a/src/args.h +++ b/src/args.h @@ -77,6 +77,8 @@ enum nvme_print_flags { OPT_END() \ } +extern struct argconfig_opt_val nvme_nsid_values[]; +extern struct argconfig_opt_val nvme_timeout_values[]; extern const char *uuid_index; extern const char *namespace_id_desired; diff --git a/src/global-config.c b/src/global-config.c index 761d6f6dd7..9d486fe798 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -76,6 +76,10 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, return 0; } + /* 'timeout = infinite' like on the command line */ + if (argconfig_set_value_name(s, value)) + return 0; + switch (s->config_type) { case CFG_FLAG: err = shr_parse_bool(value, &bval); diff --git a/src/global-options.c b/src/global-options.c index eb5ea095f9..c2323d3257 100644 --- a/src/global-options.c +++ b/src/global-options.c @@ -10,9 +10,25 @@ #include +#include + #include "argconfig.h" #include "args.h" +/* The broadcast namespace identifier, see the NVM Express specification */ +struct argconfig_opt_val nvme_nsid_values[] = { + VAL_UINT("all", NVME_NSID_ALL), + VAL_UINT("-1", NVME_NSID_ALL), + VAL_END() +}; + +/* No timeout: the largest timeout the kernel takes */ +struct argconfig_opt_val nvme_timeout_values[] = { + VAL_UINT("infinite", UINT32_MAX), + VAL_UINT("-1", UINT32_MAX), + VAL_END() +}; + static char output_format_help[64]; static struct argconfig_commandline_options global_options[] = { @@ -23,7 +39,7 @@ static struct argconfig_commandline_options global_options[] = { OPT_FMT("output-format", 'o', &nvme_args.output_format, output_format_help), OPT_UINT("timeout", 0, &nvme_args.timeout, - "timeout value, in milliseconds"), + "timeout value, in milliseconds", nvme_timeout_values), OPT_FLAG("dry-run", 0, &nvme_args.dry_run, "show command instead of executing"), OPT_FLAG("no-retries", 0, &nvme_args.no_retries, diff --git a/src/nvme-cmds-discovery.c b/src/nvme-cmds-discovery.c index baeca3fc76..4798e18c04 100644 --- a/src/nvme-cmds-discovery.c +++ b/src/nvme-cmds-discovery.c @@ -137,7 +137,8 @@ static int get_log(int argc, char **argv, struct command *acmd, struct plugin *p NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_BYTE("log-id", 'i', &cfg.log_id, log_id, log_name), OPT_UINT("log-len", 'l', &cfg.log_len, log_len), OPT_UINT("aen", 'a', &cfg.aen, aen), diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index 944ce9558f..0021d05825 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -263,7 +263,8 @@ static int get_feature(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id, feature_name), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), @@ -355,7 +356,8 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_desired, nvme_nsid_values), OPT_BYTE("feature-id", 'f', &cfg.fid, fid, feature_name), OPT_SUFFIX("value", 'V', &cfg.value, value), OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12), diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index a3137f1931..02425844cf 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -128,7 +128,8 @@ static int write_uncor(int argc, char **argv, struct command *acmd, struct plugi }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), @@ -480,7 +481,8 @@ static int write_zeroes(int argc, char **argv, }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_desired, nvme_nsid_values), OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), @@ -603,7 +605,8 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_LIST("ctx-attrs", 'a', &cfg.ctx_attrs, context_attrs), OPT_LIST("blocks", 'b', &cfg.blocks, blocks), OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks), @@ -762,7 +765,8 @@ static int copy_cmd(int argc, char **argv, struct command *acmd, struct plugin * }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_id_desired, nvme_nsid_values), OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba), OPT_LIST("slbs", 's', &cfg.slbas, d_slbas), OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs), @@ -900,7 +904,8 @@ static int flush_cmd(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired)); + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1020,7 +1025,8 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_id_desired, nvme_nsid_values), OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block_addr), OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_SHRT("block-size", 'b', &cfg.block_size, block_size), @@ -1344,7 +1350,8 @@ static int verify_cmd(int argc, char **argv, struct command *acmd, struct plugin }; NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.nsid, + namespace_desired, nvme_nsid_values), OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), @@ -1441,7 +1448,8 @@ static int get_lba_status(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_SUFFIX("start-lba", 's', &cfg.slba, slba), OPT_UINT("max-dw", 'm', &cfg.mndw, mndw), OPT_BYTE("action", 'a', &cfg.atype, atype), diff --git a/src/nvme-cmds-passthru.c b/src/nvme-cmds-passthru.c index 589f533748..be2a4c0369 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -144,7 +144,8 @@ static int passthru(int argc, char **argv, bool admin, OPT_BYTE("flags", 'f', &cfg.flags, cflags), OPT_BYTE("prefill", 'p', &cfg.prefill, prefill), OPT_SHRT("rsvd", 'R', &cfg.rsvd, rsvd), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_UINT("metadata-len", 'm', &cfg.metadata_len, metadata_len), OPT_UINT("cdw2", '2', &cfg.cdw2, cdw2), diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index fff5bc0538..150ff61e06 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -200,7 +200,8 @@ static int device_self_test(int argc, char **argv, struct command *acmd, struct NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id, nvme_nsid_values), OPT_BYTE("self-test-code", 's', &cfg.stc, self_test_code), OPT_FLAG("wait", 'w', &cfg.wait, wait)); @@ -752,7 +753,8 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin NVME_ARGS(opts, OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), OPT_BYTE("lbaf", 'l', &cfg.lbaf, lbaf), OPT_BYTE("ses", 's', &cfg.ses, ses), OPT_BYTE("pi", 'i', &cfg.pi, pi), diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index f4e8090faa..d7e8c37886 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -46,6 +46,14 @@ def test_no_escape_sequences_when_not_a_terminal(self): self.assertNotIn('\033[', result.stdout) +class SpecialValuesTest(unittest.TestCase): + + def test_help_lists_the_names(self): + result = run('id', 'ns', '--help') + self.assertIn('Names: all, -1', result.stdout) + self.assertIn('Names: infinite, -1', result.stdout) + + class OperandTest(unittest.TestCase): """A command takes a device, an optional one or none. A missing device and extra operands are usage errors.""" diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 384ef02876..5a1c143a5a 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -489,6 +489,49 @@ static void given_test(void) argconfig_set_global_options(NULL); } +/* + * A value table names special values of a numeric option, also negative + * ones, while other negative numbers are rejected. + */ +static void special_values_test(void) +{ + static const struct { + const char *arg; + int ret; + __u32 value; + } tests[] = { + { "--nsid=all", 0, 0xffffffff }, + { "--nsid=-1", 0, 0xffffffff }, + { "--nsid=a", 0, 0xffffffff }, + { "--nsid=7", 0, 7 }, + { "--nsid=-2", -EINVAL, 0 }, + }; + OPT_VALS(nsid_values) = { + VAL_UINT("all", 0xffffffff), + VAL_UINT("-1", 0xffffffff), + VAL_END() + }; + __u32 nsid; + unsigned int i; + int ret; + + OPT_ARGS(opts) = { + OPT_UINT("nsid", 'n', &nsid, "nsid", nsid_values), + OPT_END() + }; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + char **argv = (char *[]){ "prog", (char *)tests[i].arg, NULL }; + + nsid = 0; + ret = argconfig_parse(argv_len(argv), argv, "test", opts); + if (ret != tests[i].ret || (!ret && nsid != tests[i].value)) { + printf("ERROR: special values: %s\n", tests[i].arg); + test_rc = 1; + } + } +} + #define COMMA_SEP_ARRAY_MAX_VALUES 4 struct comma_sep_array_test { @@ -881,6 +924,7 @@ int main(void) operands_test(); global_options_test(); given_test(); + special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) comma_sep_array_test(&comma_sep_array_tests[i]); diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index 7b96f0edc0..c03d498fe1 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -113,6 +113,31 @@ static bool test_unknown_key_ignored(void) return pass; } +static bool test_value_names(void) +{ + char *path = write_temp( + "[Global]\n" + "timeout = infinite\n"); + bool pass = true; + + printf("test_value_names:\n"); + + nvme_args.timeout = 5000; + nvme_load_global_config_from(path); + unlink(path); + free(path); + + /* The names the command line takes work in the file too */ + if (nvme_args.timeout != UINT32_MAX) { + printf(" - timeout = infinite [FAIL]\n"); + pass = false; + } else { + printf(" - timeout = infinite [PASS]\n"); + } + + return pass; +} + static bool test_malformed_value_skipped(void) { char *path; @@ -226,6 +251,7 @@ int main(void) pass &= test_valid_keys(); pass &= test_unknown_key_ignored(); + pass &= test_value_names(); pass &= test_malformed_value_skipped(); pass &= test_hidden_and_out_of_range(); pass &= test_keys_outside_global_ignored(); From 373c0889128f91571a44690eea45c4f757348b6b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:23:49 +0000 Subject: [PATCH 083/263] treewide: ask whether an option was given instead of sentinels A few commands start an option with a value which they treat as 'not given': -1 for the lm '--sel' and '--tpt', the zns report-zones '--descs', the micron vs-internal-log '--data_area' and the register and property '--offset' and '--value', 0xff for the telemetry-log '--mcda'. Unsigned options reject negative numbers now, but the -1 sentinels still sit in signed variables behind unsigned options, and the sentinel values can't be given: set-property can't write a value of all ones and '--mcda=255' is ignored. Store these options in unsigned variables and ask argconfig_given() instead. The register helpers which need to know whether an offset was given take the option table for that, as the set-reg helpers already do. lm migration-recv required no '--sel' so far and sent the reserved select value 0xff. It now requires one, like track-send and migration-send. Signed-off-by: Daniel Wagner --- plugins/lm/lm-nvme.c | 26 +++++++++++++++--------- plugins/log/log-plugin.c | 4 ++-- plugins/micron/micron-nvme.c | 8 ++++---- plugins/zns/zns.c | 8 ++++---- src/nvme-cmds-common.h | 4 ++-- src/nvme-cmds-registers.c | 39 ++++++++++++++++++++++-------------- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/plugins/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index 99d1ac6e33..6b4c363726 100644 --- a/plugins/lm/lm-nvme.c +++ b/plugins/lm/lm-nvme.c @@ -171,7 +171,7 @@ static int lm_track_send(int argc, char **argv, struct command *acmd, struct plu int err = -1; struct config { - __s8 sel; + __u8 sel; __u8 mos; __u16 cdqid; bool start; @@ -179,7 +179,7 @@ static int lm_track_send(int argc, char **argv, struct command *acmd, struct plu }; struct config cfg = { - .sel = -1, + .sel = 0, .mos = 0, .cdqid = 0, .start = false, @@ -197,7 +197,7 @@ static int lm_track_send(int argc, char **argv, struct command *acmd, struct plu if (err) return err; - if (cfg.sel == -1) { + if (!argconfig_given(opts, &cfg.sel)) { nvme_show_error("Select field required"); return -EINVAL; } @@ -272,7 +272,7 @@ static int lm_migration_send(int argc, char **argv, struct command *acmd, struct int err = -1; struct config { - __s8 sel; + __u8 sel; __u16 cntlid; __u8 stype; __u8 seqind; @@ -286,7 +286,7 @@ static int lm_migration_send(int argc, char **argv, struct command *acmd, struct }; struct config cfg = { - .sel = -1, + .sel = 0, .cntlid = 0, .stype = 0, .seqind = 0, @@ -316,7 +316,7 @@ static int lm_migration_send(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (cfg.sel == -1) { + if (!argconfig_given(opts, &cfg.sel)) { nvme_show_error("Select field required"); return -EINVAL; } @@ -416,7 +416,7 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct }; struct config cfg = { - .sel = -1, + .sel = 0, .cntlid = 0, .csuuidi = 0, .csvi = 0, @@ -442,6 +442,11 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct if (err) return err; + if (!argconfig_given(opts, &cfg.sel)) { + nvme_show_error("Select field required"); + return -EINVAL; + } + flags = nvme_args.output_flags; if (nvme_args.output_format && cfg.offset != 0 && !(flags & BINARY)) { @@ -515,13 +520,13 @@ static int lm_set_cdq(int argc, char **argv, struct command *acmd, struct plugin struct config { __u16 cdqid; __u32 hp; - __s32 tpt; + __u32 tpt; }; struct config cfg = { .cdqid = 0, .hp = 0, - .tpt = -1, + .tpt = 0, }; NVME_ARGS(opts, @@ -534,7 +539,8 @@ static int lm_set_cdq(int argc, char **argv, struct command *acmd, struct plugin return err; err = nvme_set_features(hdl, 0, lm_cdq_feature_id, 0, cfg.cdqid | - ((cfg.tpt >= 0) ? NVME_SET(1, LM_CTRL_DATA_QUEUE_ETPT) : 0), + (argconfig_given(opts, &cfg.tpt) ? + NVME_SET(1, LM_CTRL_DATA_QUEUE_ETPT) : 0), cfg.hp, cfg.tpt, 0, 0, NULL, 0, NULL); if (err < 0) nvme_show_error("ERROR: nvme_set_features() failed %s", libnvme_strerror(errno)); diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 7a525e3cdf..34261d6f94 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -473,7 +473,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, .ctrl_init = false, .data_area = 3, .rae = false, - .mcda = 0xff, + .mcda = 0, }; NVME_ARGS(opts, @@ -496,7 +496,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, cfg.host_gen = !!cfg.host_gen; - if (cfg.mcda != 0xff) { + if (argconfig_given(opts, &cfg.mcda)) { if (cfg.ctrl_init || !cfg.host_gen) { nvme_show_error("mcda allowed for Host-init Creation!"); return -EINVAL; diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 7d52c16156..4197c5ab63 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -3692,14 +3692,14 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, struct config { char *type; char *package; - int data_area; + __u32 data_area; int log; }; struct config cfg = { .type = "", .package = "", - .data_area = -1, + .data_area = 0, .log = 0x07, }; @@ -3722,12 +3722,12 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, goto out; } - if (cfg.data_area <= 0 || cfg.data_area > 4) { + if (!cfg.data_area || cfg.data_area > 4) { nvme_show_error("data area must be selected using -d option ie --d=1,2,3,4"); goto out; } telemetry_option = 1; - } else if (cfg.data_area >= 0) { + } else if (argconfig_given(opts, &cfg.data_area)) { nvme_show_error( "data area option is valid only for telemetry option (i.e --type=host|controller)"); goto out; diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 7bbbfd6209..2324bd58b2 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -750,15 +750,15 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug struct config { __u64 zslba; __u32 namespace_id; - int num_descs; - int state; + __u32 num_descs; + __u32 state; bool verbose; bool extended; bool partial; }; struct config cfg = { - .num_descs = -1, + .num_descs = 0, }; NVME_ARGS(opts, @@ -827,7 +827,7 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug total_nr_zones = le64_to_cpu(buff->nr_zones); - if (cfg.num_descs == -1) + if (!argconfig_given(opts, &cfg.num_descs)) cfg.num_descs = total_nr_zones; nr_zones = cfg.num_descs; diff --git a/src/nvme-cmds-common.h b/src/nvme-cmds-common.h index 527bb0a2a0..e37932b92f 100644 --- a/src/nvme-cmds-common.h +++ b/src/nvme-cmds-common.h @@ -51,7 +51,7 @@ struct passthru_config { }; struct get_reg_config { - int offset; + __u32 offset; bool cap; bool vs; bool intms; @@ -84,7 +84,7 @@ struct get_reg_config { }; struct set_reg_config { - int offset; + __u32 offset; bool mmio32; __u64 value; __u32 intms; diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index 399862fce9..a631c44d07 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -252,11 +252,14 @@ static bool is_reg_selected(struct get_reg_config *cfg, int offset) return false; } -static int get_register_properties(struct libnvme_transport_handle *hdl, void **pbar, struct get_reg_config *cfg) +static int get_register_properties(struct libnvme_transport_handle *hdl, + struct argconfig_commandline_options *opts, + void **pbar, struct get_reg_config *cfg) { struct libnvme_passthru_cmd cmd; int offset = NVME_REG_CRTO; __u64 value; + bool given = argconfig_given(opts, &cfg->offset); int size; int err; void *bar; @@ -267,7 +270,8 @@ static int get_register_properties(struct libnvme_transport_handle *hdl, void ** return -ENOMEM; for (offset = NVME_REG_CAP; offset <= NVME_REG_CRTO; offset += get_reg_size(offset)) { - if ((cfg->offset != offset && !is_reg_selected(cfg, offset)) || + if ((!(given && cfg->offset == offset) && + !is_reg_selected(cfg, offset)) || !nvme_is_fabrics_reg(offset)) continue; @@ -333,17 +337,22 @@ bool nvme_is_ctrl_reg(int offset) return false; } -static bool get_register_offset(void *bar, bool fabrics, struct get_reg_config *cfg, +static bool get_register_offset(struct argconfig_commandline_options *opts, + void *bar, bool fabrics, + struct get_reg_config *cfg, nvme_print_flags_t flags) { - bool offset_matched = cfg->offset >= 0; + bool given = argconfig_given(opts, &cfg->offset); + bool offset_matched = given; int offset; if (offset_matched) nvme_show_ctrl_register(bar, fabrics, cfg->offset, flags); for (offset = NVME_REG_CAP; offset <= NVME_REG_PMRMSCU; offset += get_reg_size(offset)) { - if (!nvme_is_ctrl_reg(offset) || offset == cfg->offset || !is_reg_selected(cfg, offset)) + if (!nvme_is_ctrl_reg(offset) || + (given && offset == cfg->offset) || + !is_reg_selected(cfg, offset)) continue; nvme_show_ctrl_register(bar, fabrics, offset, flags); if (!offset_matched) @@ -387,7 +396,7 @@ static int get_register(int argc, char **argv, struct command *acmd, struct plug void *bar; struct get_reg_config cfg = { - .offset = -1, + .offset = 0, }; NVME_ARGS(opts, @@ -437,13 +446,13 @@ static int get_register(int argc, char **argv, struct command *acmd, struct plug bar = read_registers(hdl); if (!bar) { - err = get_register_properties(hdl, &bar, &cfg); + err = get_register_properties(hdl, opts, &bar, &cfg); if (err) return err; fabrics = true; } - if (!get_register_offset(bar, fabrics, &cfg, flags)) { + if (!get_register_offset(opts, bar, fabrics, &cfg, flags)) { nvme_show_error("offset required param"); err = -EINVAL; } @@ -710,7 +719,7 @@ static int set_register(int argc, char **argv, struct command *acmd, struct plug int err; struct set_reg_config cfg = { - .offset = -1, + .offset = 0, }; NVME_ARGS(opts, @@ -765,7 +774,7 @@ static int get_property(int argc, char **argv, struct command *acmd, struct plug nvme_print_flags_t flags = NORMAL; struct get_reg_config cfg = { - .offset = -1, + .offset = 0, .fabrics = true, }; @@ -778,7 +787,7 @@ static int get_property(int argc, char **argv, struct command *acmd, struct plug flags = nvme_args.output_flags; - if (cfg.offset == -1) { + if (!argconfig_given(opts, &cfg.offset)) { nvme_show_error("offset required param"); return -EINVAL; } @@ -805,8 +814,8 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug int err; struct set_reg_config cfg = { - .offset = -1, - .value = -1, + .offset = 0, + .value = 0, }; NVME_ARGS(opts, @@ -817,11 +826,11 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug if (err) return err; - if (cfg.offset == -1) { + if (!argconfig_given(opts, &cfg.offset)) { nvme_show_error("offset required param"); return -EINVAL; } - if (cfg.value == -1) { + if (!argconfig_given(opts, &cfg.value)) { nvme_show_error("value required param"); return -EINVAL; } From 5ec0dfb14ac3bccfcb3180ed2604615fe730ced1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:26:54 +0000 Subject: [PATCH 084/263] argconfig: check the signedness of an option variable The option macros accept the signed variant of the variable type as well, so an unsigned option could store into an int. With negative numbers rejected that variable never sees a negative value, but the code using it may still test for one, as the removed -1 sentinels did. Accept only the type the option converts to. OPT_INT and OPT_INCR take an int, the other number options an unsigned type of their size. Change the few plugin variables which were declared signed. Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 2 +- plugins/memblaze/memblaze-nvme.c | 2 +- plugins/micron/micron-nvme.c | 6 +++--- plugins/sandisk/sandisk-nvme.c | 2 +- plugins/toshiba/toshiba-nvme.c | 2 +- plugins/wdc/wdc-nvme.c | 6 +++--- plugins/zns/zns.c | 2 +- src/argconfig.h | 24 ++++++++++++------------ 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 34261d6f94..63b1c7a201 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -462,7 +462,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, char *file_name; __u32 host_gen; bool ctrl_init; - int data_area; + __u32 data_area; bool rae; __u8 mcda; bool force; diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 0ea107be02..3e35655fdb 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -1796,7 +1796,7 @@ static int mb_get_performance_stats(int argc, char **argv, struct command *acmd, // Get the configuration struct config { - int duration; + __u32 duration; bool raw_binary; }; diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 4197c5ab63..76d3e9e3d5 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -710,8 +710,8 @@ static int micron_smbus_option(int argc, char **argv, struct { char *option; - int value; - int save; + __u32 value; + __u32 save; int status; } opt = { .option = "disable", @@ -3399,7 +3399,7 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, struct { char *option; - int select; + __u32 select; } opt = { .option = "disable", .select = 0, diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index d91fa1e2de..c22f1b5511 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -414,7 +414,7 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, struct config { char *file; __u32 xfer_size; - int data_area; + __u32 data_area; __u64 file_size; __u64 offset; char *type; diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index 44ae58517e..8c20ea9a25 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -419,7 +419,7 @@ static int vendor_log(int argc, char **argv, struct command *acmd, struct plugin struct config { __u32 namespace_id; const char *output_file; - int log; + __u32 log; }; struct config cfg = { diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index d48cc6d870..ca7dac6618 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -4431,7 +4431,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, struct config { char *file; __u32 xfer_size; - int data_area; + __u32 data_area; __u64 file_size; __u64 offset; char *type; @@ -8577,7 +8577,7 @@ static int wdc_cu_smart_log(int argc, char **argv, struct command *acmd, __u8 *data; struct config { - int uuid_index; + __u32 uuid_index; }; struct config cfg = { @@ -10740,7 +10740,7 @@ static int wdc_reason_identifier(int argc, char **argv, struct config { - int log_id; + __u32 log_id; char *file; }; struct config cfg = { diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 2324bd58b2..05c6c0a9d0 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -339,7 +339,7 @@ static int zone_mgmt_send(int argc, char **argv, struct command *acmd, struct pl bool zsaso; bool select_all; __u8 zsa; - int data_len; + __u32 data_len; char *file; }; diff --git a/src/argconfig.h b/src/argconfig.h index 3c60bcf955..0a573de2b7 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -58,24 +58,24 @@ enum argconfig_types { /* * The option macros check the type of the variable they store to, so a - * mismatch is a compile error instead of a write of the wrong size. The - * signed variant of a type is accepted as well. Every option needs a - * variable, which argconfig_given() takes. + * mismatch is a compile error instead of a write of the wrong size or + * signedness. Every option needs a variable, which argconfig_given() + * takes. */ -#define OPT_CHECK(v, T, S) _Generic(v, T : (v), S : (v)) +#define OPT_CHECK(v, T) _Generic(v, T : (v)) +#define OPT_CHECK_2(v, T, U) _Generic(v, T : (v), U : (v)) /* * uint64_t is unsigned long long on some ABIs only, and listing a type * twice is an error. The inner selection falls back to a void expression, * which only fails if it is selected. */ #define OPT_CHECK_64(v) \ - _Generic(v, uint64_t * : (v), int64_t * : (v), \ + _Generic(v, uint64_t * : (v), \ default : \ _Generic(v, unsigned long long * : (v), \ - long long * : (v), \ default : \ (void)0)) -#define OPT_CHECK_STR(v) OPT_CHECK(v, char **, const char **) +#define OPT_CHECK_STR(v) OPT_CHECK_2(v, char **, const char **) #define OPT_CHECK_BOOL(v) _Generic(v, bool * : (v)) #define OPT_CHECK_DOUBLE(v) _Generic(v, double * : (v)) @@ -93,11 +93,11 @@ enum argconfig_types { required_argument, d, false, __VA_ARGS__} #define OPT_UINT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_POSITIVE, OPT_CHECK(v, uint32_t *, int32_t *), \ + {l, s, "NUM", CFG_POSITIVE, OPT_CHECK(v, uint32_t *), \ required_argument, d, false, __VA_ARGS__} #define OPT_INT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_INT, OPT_CHECK(v, int *, unsigned int *), \ + {l, s, "NUM", CFG_INT, OPT_CHECK(v, int *), \ required_argument, d, false, __VA_ARGS__} #define OPT_LONG(l, s, v, d, ...) \ @@ -109,15 +109,15 @@ enum argconfig_types { required_argument, d, false, __VA_ARGS__} #define OPT_BYTE(l, s, v, d, ...) \ - {l, s, "NUM", CFG_BYTE, OPT_CHECK(v, uint8_t *, int8_t *), \ + {l, s, "NUM", CFG_BYTE, OPT_CHECK(v, uint8_t *), \ required_argument, d, false, __VA_ARGS__} #define OPT_SHRT(l, s, v, d, ...) \ - {l, s, "NUM", CFG_SHORT, OPT_CHECK(v, uint16_t *, int16_t *), \ + {l, s, "NUM", CFG_SHORT, OPT_CHECK(v, uint16_t *), \ required_argument, d, false, __VA_ARGS__} #define OPT_INCR(l, s, v, d, ...) \ - {l, s, "NUM", CFG_INCREMENT, OPT_CHECK(v, int *, unsigned int *), \ + {l, s, "NUM", CFG_INCREMENT, OPT_CHECK(v, int *), \ no_argument, d, false, __VA_ARGS__} #define OPT_STRING(l, s, m, v, d, ...) \ From d4ce4cc271a7c911fb3041604c9d4b7b40a6cf90 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:27:49 +0000 Subject: [PATCH 085/263] completions: update the shell completions Offer the names of the namespace id and the timeout values. Signed-off-by: Daniel Wagner --- completions/_nvme | 1802 ++++++++++++++++++--------- completions/bash-nvme-completion.sh | 1800 +++++++++++++++++--------- completions/nvme-completion.ps1 | 723 +++++++++++ 3 files changed, 3126 insertions(+), 1199 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 89aaff17c9..7637705c41 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -297,7 +297,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _amzn_id_ctrl _amzn_id_ctrl=( --dry-run':show command instead of executing' @@ -325,7 +326,8 @@ _nvme () { (stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _amzn_stats _amzn_stats=( --details':Detail IO histogram of each block size ranges' @@ -374,7 +376,8 @@ _nvme () { case ${words[2]} in (convert) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ "--output -o" && return local _config_convert @@ -404,7 +407,8 @@ _nvme () { (create) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output" && return local _config_create _config_create=( @@ -483,7 +487,8 @@ _nvme () { (show) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" && return local _config_show _config_show=( @@ -509,7 +514,8 @@ _nvme () { (status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _config_status _config_status=( --dry-run':show command instead of executing' @@ -532,7 +538,8 @@ _nvme () { (validate) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" && return local _config_validate _config_validate=( @@ -574,8 +581,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dapustor_smart_log_add _dapustor_smart_log_add=( --dry-run':show command instead of executing' @@ -622,7 +631,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dell_id_ctrl _dell_id_ctrl=( --dry-run':show command instead of executing' @@ -668,7 +678,8 @@ _nvme () { case ${words[2]} in (smart-log-add|stat) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dera_smart_log_add _dera_smart_log_add=( --dry-run':show command instead of executing' @@ -709,8 +720,10 @@ _nvme () { case ${words[2]} in (receive) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dir_receive _dir_receive=( --data-len=':buffer len (if) data is sent or received' @@ -747,8 +760,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _dir_send _dir_send=( @@ -813,7 +828,8 @@ _nvme () { case ${words[2]} in (add) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_add _exclusion_add=( --dry-run':show command instead of executing' @@ -840,7 +856,8 @@ _nvme () { (create) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_create _exclusion_create=( --dry-run':show command instead of executing' @@ -865,7 +882,8 @@ _nvme () { (delete) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_delete _exclusion_delete=( --dry-run':show command instead of executing' @@ -890,7 +908,8 @@ _nvme () { (edit) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_edit _exclusion_edit=( --dry-run':show command instead of executing' @@ -915,7 +934,8 @@ _nvme () { (list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_list _exclusion_list=( --dry-run':show command instead of executing' @@ -940,7 +960,8 @@ _nvme () { (remove) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_remove _exclusion_remove=( --dry-run':show command instead of executing' @@ -989,7 +1010,8 @@ _nvme () { case ${words[2]} in (configs) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_configs _fdp_configs=( --dry-run':show command instead of executing' @@ -1019,7 +1041,8 @@ _nvme () { (events) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_events _fdp_events=( --dry-run':show command instead of executing' @@ -1049,7 +1072,8 @@ _nvme () { (feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_feature _fdp_feature=( --disable':Disable current FDP configuration' @@ -1078,8 +1102,10 @@ _nvme () { ;; (set-events) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_set_events _fdp_set_events=( --dry-run':show command instead of executing' @@ -1113,7 +1139,8 @@ _nvme () { (stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_stats _fdp_stats=( --dry-run':show command instead of executing' @@ -1140,8 +1167,10 @@ _nvme () { ;; (status) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_status _fdp_status=( --dry-run':show command instead of executing' @@ -1168,8 +1197,10 @@ _nvme () { ;; (update) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_update _fdp_update=( --dry-run':show command instead of executing' @@ -1197,7 +1228,8 @@ _nvme () { (usage) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_usage _fdp_usage=( --dry-run':show command instead of executing' @@ -1261,7 +1293,8 @@ _nvme () { (arbitration) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_arbitration _feat_arbitration=( --ab=':arbitration burst' @@ -1298,7 +1331,8 @@ _nvme () { (async-event-conf) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_async_event_conf _feat_async_event_conf=( --dry-run':show command instead of executing' @@ -1327,9 +1361,11 @@ _nvme () { ;; (err-recovery) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--nsid -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_err_recovery _feat_err_recovery=( --dry-run':show command instead of executing' @@ -1364,7 +1400,8 @@ _nvme () { (hctm) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_hctm _feat_hctm=( --dry-run':show command instead of executing' @@ -1397,7 +1434,8 @@ _nvme () { (host-behavior-support) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_host_behavior_support _feat_host_behavior_support=( --acre=':Advanced Command Retry Enable (0 or 1)' @@ -1436,7 +1474,8 @@ _nvme () { (int-coalesce) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_int_coalesce _feat_int_coalesce=( --dry-run':show command instead of executing' @@ -1469,7 +1508,8 @@ _nvme () { (int-vector-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_int_vector_config _feat_int_vector_config=( --cd':coalescing disable' @@ -1502,7 +1542,8 @@ _nvme () { (keep-alive-timer) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_keep_alive_timer _feat_keep_alive_timer=( --dry-run':show command instead of executing' @@ -1531,9 +1572,11 @@ _nvme () { ;; (lba-range-type) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--nsid -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_lba_range_type _feat_lba_range_type=( --dry-run':show command instead of executing' @@ -1576,7 +1619,8 @@ _nvme () { (num-queues) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_num_queues _feat_num_queues=( --dry-run':show command instead of executing' @@ -1607,9 +1651,11 @@ _nvme () { ;; (perf-characteristics) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--vs-data -V" && return local _feat_perf_characteristics _feat_perf_characteristics=( @@ -1653,7 +1699,8 @@ _nvme () { (power-limit) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_power_limit _feat_power_limit=( --dry-run':show command instead of executing' @@ -1688,7 +1735,8 @@ _nvme () { (power-meas) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_power_meas _feat_power_meas=( --act=':action [0-1]: stop|start' @@ -1722,7 +1770,8 @@ _nvme () { (power-mgmt) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_power_mgmt _feat_power_mgmt=( --dry-run':show command instead of executing' @@ -1755,7 +1804,8 @@ _nvme () { (power-thresh) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_power_thresh _feat_power_thresh=( --dry-run':show command instead of executing' @@ -1794,7 +1844,8 @@ _nvme () { (temp-thresh) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_temp_thresh _feat_temp_thresh=( --dry-run':show command instead of executing' @@ -1831,7 +1882,8 @@ _nvme () { (timestamp) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_timestamp _feat_timestamp=( --dry-run':show command instead of executing' @@ -1862,7 +1914,8 @@ _nvme () { (volatile-wc) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_volatile_wc _feat_volatile_wc=( --dry-run':show command instead of executing' @@ -1893,7 +1946,8 @@ _nvme () { (write-atom-normal) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _feat_write_atom_normal _feat_write_atom_normal=( --dn':disable normal' @@ -1943,7 +1997,8 @@ _nvme () { (commit|activate) _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fw_commit _fw_commit=( --action=':[0-7]: commit action: 0 = replace, 1 = replace and activate, 2 = set active, 3 = replace and activate immediate, 6 = replace boot partition, 7 = activate boot partition' @@ -1975,7 +2030,8 @@ _nvme () { (download) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _fw_download _fw_download=( @@ -2031,7 +2087,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _huawei_id_ctrl _huawei_id_ctrl=( --dry-run':show command instead of executing' @@ -2059,7 +2116,8 @@ _nvme () { (list) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _huawei_list _huawei_list=( --dry-run':show command instead of executing' @@ -2101,7 +2159,8 @@ _nvme () { case ${words[2]} in (crit-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ibm_crit_log _ibm_crit_log=( --dry-run':show command instead of executing' @@ -2127,7 +2186,8 @@ _nvme () { (persist-event-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ibm_persist_event_log _ibm_persist_event_log=( --action=':action the controller shall take during processing this persistent log page command.' @@ -2157,7 +2217,8 @@ _nvme () { (vpd) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ibm_vpd _ibm_vpd=( --dry-run':show command instead of executing' @@ -2217,7 +2278,8 @@ _nvme () { case ${words[2]} in (ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ctrl _id_ctrl=( --dry-run':show command instead of executing' @@ -2244,8 +2306,10 @@ _nvme () { ;; (ctrl-list) - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ctrl_list _id_ctrl_list=( --cntid=':controller to display' @@ -2273,7 +2337,8 @@ _nvme () { (domain) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_domain _id_domain=( --dom-id=':identifier of desired domain' @@ -2299,7 +2364,8 @@ _nvme () { (endgrp-list) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_endgrp_list _id_endgrp_list=( --dry-run':show command instead of executing' @@ -2325,7 +2391,8 @@ _nvme () { (iocs) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_iocs _id_iocs=( --controller-id=':identifier of desired controller' @@ -2350,8 +2417,10 @@ _nvme () { ;; (ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns _id_ns=( --dry-run':show command instead of executing' @@ -2381,8 +2450,10 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_descs _id_ns_descs=( --dry-run':show command instead of executing' @@ -2410,7 +2481,8 @@ _nvme () { (ns-granularity) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_granularity _id_ns_granularity=( --dry-run':show command instead of executing' @@ -2433,8 +2505,10 @@ _nvme () { ;; (ns-ind) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_ind _id_ns_ind=( --dry-run':show command instead of executing' @@ -2462,7 +2536,8 @@ _nvme () { (ns-lba-format) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_lba_format _id_ns_lba_format=( --dry-run':show command instead of executing' @@ -2489,8 +2564,10 @@ _nvme () { ;; (ns-list) - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_list _id_ns_list=( --all':show all namespaces in the subsystem, whether attached or inactive' @@ -2520,7 +2597,8 @@ _nvme () { (nvm-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvm_ctrl _id_nvm_ctrl=( --dry-run':show command instead of executing' @@ -2543,8 +2621,10 @@ _nvme () { ;; (nvm-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvm_ns _id_nvm_ns=( --dry-run':show command instead of executing' @@ -2572,7 +2652,8 @@ _nvme () { (nvm-ns-lba-format) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvm_ns_lba_format _id_nvm_ns_lba_format=( --dry-run':show command instead of executing' @@ -2600,7 +2681,8 @@ _nvme () { (nvmset) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvmset _id_nvmset=( --dry-run':show command instead of executing' @@ -2626,7 +2708,8 @@ _nvme () { (primary-ctrl-caps) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_primary_ctrl_caps _id_primary_ctrl_caps=( --cntlid=':Controller ID' @@ -2652,7 +2735,8 @@ _nvme () { (secondary-ctrl-list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_secondary_ctrl_list _id_secondary_ctrl_list=( --cntid=':lowest controller identifier to display' @@ -2680,7 +2764,8 @@ _nvme () { (uuid) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_uuid _id_uuid=( --dry-run':show command instead of executing' @@ -2724,7 +2809,8 @@ _nvme () { case ${words[2]} in (get-cdump) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _innogrit_get_cdump _innogrit_get_cdump=( --dry-run':show command instead of executing' @@ -2748,7 +2834,8 @@ _nvme () { (get-eventlog) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _innogrit_get_eventlog _innogrit_get_eventlog=( --dry-run':show command instead of executing' @@ -2789,7 +2876,8 @@ _nvme () { case ${words[2]} in (nvme-vendor-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _inspur_nvme_vendor_log _inspur_nvme_vendor_log=( --dry-run':show command instead of executing' @@ -2837,7 +2925,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_id_ctrl _intel_id_ctrl=( --dry-run':show command instead of executing' @@ -2864,8 +2953,10 @@ _nvme () { ;; (internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _intel_internal_log _intel_internal_log=( @@ -2902,7 +2993,8 @@ _nvme () { (lat-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_lat_stats _intel_lat_stats=( --dry-run':show command instead of executing' @@ -2932,7 +3024,8 @@ _nvme () { (lat-stats-tracking) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_lat_stats_tracking _intel_lat_stats_tracking=( --disable':Disable LST' @@ -2960,7 +3053,8 @@ _nvme () { (market-name) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_market_name _intel_market_name=( --dry-run':show command instead of executing' @@ -2986,7 +3080,8 @@ _nvme () { (set-bucket-thresholds) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_set_bucket_thresholds _intel_set_bucket_thresholds=( --bucket-thresholds=':Bucket Threshold List, comma separated list: 0, 10, 20 ...' @@ -3013,8 +3108,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_smart_log_add _intel_smart_log_add=( --dry-run':show command instead of executing' @@ -3044,7 +3141,8 @@ _nvme () { (temp-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_temp_stats _intel_temp_stats=( --dry-run':show command instead of executing' @@ -3087,8 +3185,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _io_mgmt_recv _io_mgmt_recv=( @@ -3122,8 +3222,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _io_mgmt_send _io_mgmt_send=( @@ -3182,7 +3284,8 @@ _nvme () { case ${words[2]} in (check-kxchap-secret) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_check_kxchap_secret _keys_check_kxchap_secret=( --dry-run':show command instead of executing' @@ -3213,7 +3316,8 @@ _nvme () { (check-tls-psk) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_check_tls_psk _keys_check_tls_psk=( --compat':Use non-RFC 8446 compliant algorithm for deriving the TLS PSK identity for older implementations.' @@ -3250,7 +3354,8 @@ _nvme () { (export) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_export _keys_export=( --dry-run':show command instead of executing' @@ -3277,7 +3382,8 @@ _nvme () { (gen-kxchap-secret) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_gen_kxchap_secret _keys_gen_kxchap_secret=( --dry-run':show command instead of executing' @@ -3306,7 +3412,8 @@ _nvme () { (gen-tls-psk) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_gen_tls_psk _keys_gen_tls_psk=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -3349,7 +3456,8 @@ _nvme () { (import) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_import _keys_import=( --dry-run':show command instead of executing' @@ -3380,7 +3488,8 @@ _nvme () { (insert-tls-psk) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_insert_tls_psk _keys_insert_tls_psk=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -3419,7 +3528,8 @@ _nvme () { (revoke) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_revoke _keys_revoke=( --dry-run':show command instead of executing' @@ -3471,7 +3581,8 @@ _nvme () { case ${words[2]} in (create-cdq) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_create_cdq _lm_create_cdq=( --cntlid=':Controller ID' @@ -3502,7 +3613,8 @@ _nvme () { (delete-cdq) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_delete_cdq _lm_delete_cdq=( --cdqid=':Controller Data Queue ID' @@ -3528,7 +3640,8 @@ _nvme () { (get-cdq) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_get_cdq _lm_get_cdq=( --cdqid=':Controller Data Queue ID' @@ -3555,7 +3668,8 @@ _nvme () { (migration-recv) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _lm_migration_recv _lm_migration_recv=( @@ -3599,7 +3713,8 @@ _nvme () { (migration-send) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -f" && return local _lm_migration_send _lm_migration_send=( @@ -3646,7 +3761,8 @@ _nvme () { (set-cdq) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_set_cdq _lm_set_cdq=( --cdqid=':Controller Data Queue ID' @@ -3677,7 +3793,8 @@ _nvme () { (track-send) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _lm_track_send _lm_track_send=( --cdqid=':Controller Data Queue ID' @@ -3758,7 +3875,8 @@ _nvme () { case ${words[2]} in (ana) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_ana _log_ana=( --dry-run':show command instead of executing' @@ -3784,7 +3902,8 @@ _nvme () { (ave-discovery) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_ave_discovery _log_ave_discovery=( --dry-run':show command instead of executing' @@ -3810,7 +3929,8 @@ _nvme () { (boot-part) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _log_boot_part _log_boot_part=( @@ -3839,7 +3959,8 @@ _nvme () { (changed-alloc-ns-list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_changed_alloc_ns_list _log_changed_alloc_ns_list=( --dry-run':show command instead of executing' @@ -3865,7 +3986,8 @@ _nvme () { (changed-ns-list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_changed_ns_list _log_changed_ns_list=( --dry-run':show command instead of executing' @@ -3890,8 +4012,10 @@ _nvme () { ;; (dispersed-ns-participating-nss) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_dispersed_ns_participating_nss _log_dispersed_ns_participating_nss=( --dry-run':show command instead of executing' @@ -3917,7 +4041,8 @@ _nvme () { (effects) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_effects _log_effects=( --csi=':command set identifier' @@ -3945,7 +4070,8 @@ _nvme () { (endurance) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_endurance _log_endurance=( --dry-run':show command instead of executing' @@ -3971,7 +4097,8 @@ _nvme () { (endurance-event-agg) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_endurance_event_agg _log_endurance_event_agg=( --dry-run':show command instead of executing' @@ -4000,8 +4127,10 @@ _nvme () { ;; (error) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_error _log_error=( --csi=':output specified CSI entry only' @@ -4045,7 +4174,8 @@ _nvme () { (fid-support-effects) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_fid_support_effects _log_fid_support_effects=( --dry-run':show command instead of executing' @@ -4069,7 +4199,8 @@ _nvme () { (fw) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_fw _log_fw=( --dry-run':show command instead of executing' @@ -4095,7 +4226,8 @@ _nvme () { (host-discovery) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_host_discovery _log_host_discovery=( --all-host-entries':All Host Entries' @@ -4123,7 +4255,8 @@ _nvme () { (lba-status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_lba_status _log_lba_status=( --dry-run':show command instead of executing' @@ -4149,7 +4282,8 @@ _nvme () { (media-unit-stat) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_media_unit_stat _log_media_unit_stat=( --domain-id=':Domain Identifier' @@ -4177,7 +4311,8 @@ _nvme () { (mgmt-addr-list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_mgmt_addr_list _log_mgmt_addr_list=( --dry-run':show command instead of executing' @@ -4201,7 +4336,8 @@ _nvme () { (mi-cmd-support-effects) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_mi_cmd_support_effects _log_mi_cmd_support_effects=( --dry-run':show command instead of executing' @@ -4225,7 +4361,8 @@ _nvme () { (persistent-event) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_persistent_event _log_persistent_event=( --action=':action the controller shall take during processing this persistent log page command.' @@ -4255,7 +4392,8 @@ _nvme () { (phy-rx-eom) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_phy_rx_eom _log_phy_rx_eom=( --controller=':Target Controller ID.' @@ -4283,7 +4421,8 @@ _nvme () { (power-measurement) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_power_measurement _log_power_measurement=( --dry-run':show command instead of executing' @@ -4309,7 +4448,8 @@ _nvme () { (pred-lat-event-agg) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_pred_lat_event_agg _log_pred_lat_event_agg=( --dry-run':show command instead of executing' @@ -4339,7 +4479,8 @@ _nvme () { (predictable-lat) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_predictable_lat _log_predictable_lat=( --dry-run':show command instead of executing' @@ -4367,7 +4508,8 @@ _nvme () { (pull-model-ddc-req) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_pull_model_ddc_req _log_pull_model_ddc_req=( --dry-run':show command instead of executing' @@ -4393,7 +4535,8 @@ _nvme () { (reachability-associations) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_reachability_associations _log_reachability_associations=( --associations-only':Return Associations Only' @@ -4421,7 +4564,8 @@ _nvme () { (reachability-groups) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_reachability_groups _log_reachability_groups=( --dry-run':show command instead of executing' @@ -4449,7 +4593,8 @@ _nvme () { (resv-notif) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_resv_notif _log_resv_notif=( --dry-run':show command instead of executing' @@ -4473,7 +4618,8 @@ _nvme () { (rotational-media-info) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_rotational_media_info _log_rotational_media_info=( --dry-run':show command instead of executing' @@ -4499,7 +4645,8 @@ _nvme () { (sanitize) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_sanitize _log_sanitize=( --dry-run':show command instead of executing' @@ -4527,7 +4674,8 @@ _nvme () { (self-test) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_self_test _log_self_test=( --dry-run':show command instead of executing' @@ -4552,8 +4700,10 @@ _nvme () { ;; (smart) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_smart _log_smart=( --dry-run':show command instead of executing' @@ -4581,7 +4731,8 @@ _nvme () { (supported-cap-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_supported_cap_config _log_supported_cap_config=( --domain-id=':Domain Identifier' @@ -4609,7 +4760,8 @@ _nvme () { (supported-pages) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_supported_pages _log_supported_pages=( --dry-run':show command instead of executing' @@ -4633,7 +4785,8 @@ _nvme () { (telemetry) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _log_telemetry _log_telemetry=( @@ -4688,7 +4841,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _mangoboost_id_ctrl _mangoboost_id_ctrl=( --dry-run':show command instead of executing' @@ -4747,7 +4901,8 @@ _nvme () { case ${words[2]} in (clear-error-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_clear_error_log _memblaze_clear_error_log=( --dry-run':show command instead of executing' @@ -4771,7 +4926,8 @@ _nvme () { (get-pm-status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_get_pm_status _memblaze_get_pm_status=( --dry-run':show command instead of executing' @@ -4795,7 +4951,8 @@ _nvme () { (lat-get-feature-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_get_feature_x _memblaze_lat_get_feature_x=( --dry-run':show command instead of executing' @@ -4819,7 +4976,8 @@ _nvme () { (lat-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_log _memblaze_lat_log=( --dry-run':show command instead of executing' @@ -4845,7 +5003,8 @@ _nvme () { (lat-log-print) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_log_print _memblaze_lat_log_print=( --dry-run':show command instead of executing' @@ -4869,7 +5028,8 @@ _nvme () { (lat-log-print-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_log_print_x _memblaze_lat_log_print_x=( --dry-run':show command instead of executing' @@ -4895,7 +5055,8 @@ _nvme () { (lat-set-feature-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_set_feature_x _memblaze_lat_set_feature_x=( --dry-run':show command instead of executing' @@ -4929,7 +5090,8 @@ _nvme () { (lat-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_stats _memblaze_lat_stats=( --disable':Disable LST' @@ -4957,7 +5119,8 @@ _nvme () { (lat-stats-print) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_stats_print _memblaze_lat_stats_print=( --dry-run':show command instead of executing' @@ -4983,7 +5146,8 @@ _nvme () { (lat-stats-print-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_stats_print_x _memblaze_lat_stats_print_x=( --dry-run':show command instead of executing' @@ -5009,7 +5173,8 @@ _nvme () { (perf-stats-print-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_perf_stats_print_x _memblaze_perf_stats_print_x=( --dry-run':show command instead of executing' @@ -5037,7 +5202,8 @@ _nvme () { (select-download) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _memblaze_select_download _memblaze_select_download=( @@ -5066,7 +5232,8 @@ _nvme () { (set-pm-status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_set_pm_status _memblaze_set_pm_status=( --dry-run':show command instead of executing' @@ -5093,8 +5260,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_smart_log_add _memblaze_smart_log_add=( --dry-run':show command instead of executing' @@ -5122,7 +5291,8 @@ _nvme () { (smart-log-add-x) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_smart_log_add_x _memblaze_smart_log_add_x=( --dry-run':show command instead of executing' @@ -5190,7 +5360,8 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_clear_fw_activate_history _micron_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -5214,7 +5385,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_clear_pcie_correctable_errors _micron_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -5238,7 +5410,8 @@ _nvme () { (cloud-SSD-plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_cloud_SSD_plugin_version _micron_cloud_SSD_plugin_version=( --dry-run':show command instead of executing' @@ -5261,7 +5434,8 @@ _nvme () { (cloud-boot-SSD-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_cloud_boot_SSD_version _micron_cloud_boot_SSD_version=( --dry-run':show command instead of executing' @@ -5285,7 +5459,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_id_ctrl _micron_id_ctrl=( --dry-run':show command instead of executing' @@ -5309,7 +5484,8 @@ _nvme () { (latency-logs) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_latency_logs _micron_latency_logs=( --dry-run':show command instead of executing' @@ -5333,7 +5509,8 @@ _nvme () { (latency-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_latency_stats _micron_latency_stats=( --command=':command to display stats - all|read|write|trim, default is all' @@ -5359,7 +5536,8 @@ _nvme () { (latency-tracking) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_latency_tracking _micron_latency_tracking=( --command=':commands to monitor for - all|read|write|trim, default is all i.e, enabled for all commands' @@ -5389,7 +5567,8 @@ _nvme () { (log-page-directory) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_log_page_directory _micron_log_page_directory=( --dry-run':show command instead of executing' @@ -5413,7 +5592,8 @@ _nvme () { (plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_plugin_version _micron_plugin_version=( --dry-run':show command instead of executing' @@ -5436,7 +5616,8 @@ _nvme () { (select-download) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _micron_select_download _micron_select_download=( @@ -5465,7 +5646,8 @@ _nvme () { (smart-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_smart_log _micron_smart_log=( --dry-run':show command instead of executing' @@ -5489,7 +5671,8 @@ _nvme () { (vs-cloud-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_cloud_log _micron_vs_cloud_log=( --dry-run':show command instead of executing' @@ -5513,7 +5696,8 @@ _nvme () { (vs-device-waf) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_device_waf _micron_vs_device_waf=( --dry-run':show command instead of executing' @@ -5537,7 +5721,8 @@ _nvme () { (vs-drive-info) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_drive_info _micron_vs_drive_info=( --dry-run':show command instead of executing' @@ -5561,7 +5746,8 @@ _nvme () { (vs-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_fw_activate_history _micron_vs_fw_activate_history=( --dry-run':show command instead of executing' @@ -5585,7 +5771,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--package -p" && return local _micron_vs_internal_log _micron_vs_internal_log=( @@ -5616,7 +5803,8 @@ _nvme () { (vs-nand-stats) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_nand_stats _micron_vs_nand_stats=( --dry-run':show command instead of executing' @@ -5640,7 +5828,8 @@ _nvme () { (vs-pcie-stats) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_pcie_stats _micron_vs_pcie_stats=( --dry-run':show command instead of executing' @@ -5664,7 +5853,8 @@ _nvme () { (vs-smart-add-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_smart_add_log _micron_vs_smart_add_log=( --dry-run':show command instead of executing' @@ -5688,7 +5878,8 @@ _nvme () { (vs-smart-ext-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_smart_ext_log _micron_vs_smart_ext_log=( --dry-run':show command instead of executing' @@ -5712,7 +5903,8 @@ _nvme () { (vs-smbus-option) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_smbus_option _micron_vs_smbus_option=( --dry-run':show command instead of executing' @@ -5742,7 +5934,8 @@ _nvme () { (vs-telemetry-controller-option) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_telemetry_controller_option _micron_vs_telemetry_controller_option=( --dry-run':show command instead of executing' @@ -5770,7 +5963,8 @@ _nvme () { (vs-temperature-stats) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_temperature_stats _micron_vs_temperature_stats=( --dry-run':show command instead of executing' @@ -5794,7 +5988,8 @@ _nvme () { (vs-vendor-telemetry-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_vendor_telemetry_log _micron_vs_vendor_telemetry_log=( --dry-run':show command instead of executing' @@ -5818,7 +6013,8 @@ _nvme () { (vs-work-load-log) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_vs_work_load_log _micron_vs_work_load_log=( --dry-run':show command instead of executing' @@ -5859,7 +6055,8 @@ _nvme () { case ${words[2]} in (show) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nbft_show _nbft_show=( --discovery':show NBFT discovery controllers' @@ -5907,7 +6104,8 @@ _nvme () { case ${words[2]} in (ontapdevices) _nvme_opt_vals "--output-format -o" "normal column json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _netapp_ontapdevices _netapp_ontapdevices=( --dry-run':show command instead of executing' @@ -5931,7 +6129,8 @@ _nvme () { (smdevices) _nvme_opt_vals "--output-format -o" "normal column json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _netapp_smdevices _netapp_smdevices=( --dry-run':show command instead of executing' @@ -5975,8 +6174,10 @@ _nvme () { case ${words[2]} in (attach) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_attach _ns_attach=( --controllers=':optional comma-sep controller id list' @@ -6006,7 +6207,8 @@ _nvme () { (create) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_create _ns_create=( --anagrp-id=':ANA Group Identifier (ANAGRPID)' @@ -6069,8 +6271,10 @@ _nvme () { ;; (delete) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_delete _ns_delete=( --dry-run':show command instead of executing' @@ -6097,8 +6301,10 @@ _nvme () { ;; (detach) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_detach _ns_detach=( --controllers=':optional comma-sep controller id list' @@ -6128,7 +6334,8 @@ _nvme () { (get-id) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_get_id _ns_get_id=( --dry-run':show command instead of executing' @@ -6169,7 +6376,8 @@ _nvme () { case ${words[2]} in (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvidia_id_ctrl _nvidia_id_ctrl=( --dry-run':show command instead of executing' @@ -6214,8 +6422,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_recv _nvme_mi_recv=( @@ -6253,8 +6463,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_send _nvme_mi_send=( @@ -6339,7 +6551,8 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_clear_fw_activate_history _ocp_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -6365,7 +6578,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_clear_pcie_correctable_errors _ocp_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -6391,7 +6605,8 @@ _nvme () { (device-capability-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_device_capability_log _ocp_device_capability_log=( --dry-run':show command instead of executing' @@ -6418,7 +6633,8 @@ _nvme () { (eol-plp-failure-mode) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_eol_plp_failure_mode _ocp_eol_plp_failure_mode=( --dry-run':show command instead of executing' @@ -6450,7 +6666,8 @@ _nvme () { (error-recovery-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_error_recovery_log _ocp_error_recovery_log=( --dry-run':show command instead of executing' @@ -6476,7 +6693,8 @@ _nvme () { (fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_fw_activate_history _ocp_fw_activate_history=( --dry-run':show command instead of executing' @@ -6499,9 +6717,11 @@ _nvme () { ;; (get-clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_clear_pcie_correctable_errors _ocp_get_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -6532,7 +6752,8 @@ _nvme () { (get-dssd-async-event-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_dssd_async_event_config _ocp_get_dssd_async_event_config=( --dry-run':show command instead of executing' @@ -6559,7 +6780,8 @@ _nvme () { (get-dssd-power-state-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_dssd_power_state_feature _ocp_get_dssd_power_state_feature=( --all':Print out all 3 values at once - Current, Default, and Saved' @@ -6590,7 +6812,8 @@ _nvme () { (get-enable-ieee1667-silo) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_enable_ieee1667_silo _ocp_get_enable_ieee1667_silo=( --dry-run':show command instead of executing' @@ -6619,7 +6842,8 @@ _nvme () { (get-error-injection) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_error_injection _ocp_get_error_injection=( --all-ns':Apply to all namespaces' @@ -6648,9 +6872,11 @@ _nvme () { ;; (get-idle-wakeup-time) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_idle_wakeup_time _ocp_get_idle_wakeup_time=( --dry-run':show command instead of executing' @@ -6679,9 +6905,11 @@ _nvme () { ;; (get-latency-monitor) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_latency_monitor _ocp_get_latency_monitor=( --dry-run':show command instead of executing' @@ -6712,7 +6940,8 @@ _nvme () { (get-plp-health-check-interval) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" && return + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_plp_health_check_interval _ocp_get_plp_health_check_interval=( --dry-run':show command instead of executing' @@ -6737,9 +6966,11 @@ _nvme () { ;; (get-telemetry-profile) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _ocp_get_telemetry_profile _ocp_get_telemetry_profile=( --dry-run':show command instead of executing' @@ -6770,7 +7001,8 @@ _nvme () { (hardware-component-log) _nvme_opt_vals "--comp-id -i" "asic nand dram pmic pcb cap reg case sn country hw-rev born-on-date vendor" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_hardware_component_log _ocp_hardware_component_log=( --comp-id=':component identifier' @@ -6798,7 +7030,8 @@ _nvme () { (internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _ocp_internal_log _ocp_internal_log=( @@ -6837,7 +7070,8 @@ _nvme () { (latency-monitor-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_latency_monitor_log _ocp_latency_monitor_log=( --dry-run':show command instead of executing' @@ -6863,7 +7097,8 @@ _nvme () { (persistent-event-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_persistent_event_log _ocp_persistent_event_log=( --action=':action the controller shall take during processing this persistent log page command.' @@ -6893,7 +7128,8 @@ _nvme () { (set-dssd-async-event-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_dssd_async_event_config _ocp_set_dssd_async_event_config=( --dry-run':show command instead of executing' @@ -6921,7 +7157,8 @@ _nvme () { (set-dssd-power-state-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_dssd_power_state_feature _ocp_set_dssd_power_state_feature=( --dry-run':show command instead of executing' @@ -6951,7 +7188,8 @@ _nvme () { (set-enable-ieee1667-silo) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_enable_ieee1667_silo _ocp_set_enable_ieee1667_silo=( --dry-run':show command instead of executing' @@ -6981,7 +7219,8 @@ _nvme () { (set-error-injection) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _ocp_set_error_injection _ocp_set_error_injection=( @@ -7018,7 +7257,8 @@ _nvme () { (set-latency-monitor-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_latency_monitor_feature _ocp_set_latency_monitor_feature=( --active_bucket_timer_threshold=':This is the value that loads the Active Bucket Timer Threshold.' @@ -7062,7 +7302,8 @@ _nvme () { (set-plp-health-check-interval) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_plp_health_check_interval _ocp_set_plp_health_check_interval=( --dry-run':show command instead of executing' @@ -7092,7 +7333,8 @@ _nvme () { (set-telemetry-profile) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_set_telemetry_profile _ocp_set_telemetry_profile=( --dry-run':show command instead of executing' @@ -7118,7 +7360,8 @@ _nvme () { (smart-add-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_smart_add_log _ocp_smart_add_log=( --dry-run':show command instead of executing' @@ -7144,7 +7387,8 @@ _nvme () { (tcg-configuration-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_tcg_configuration_log _ocp_tcg_configuration_log=( --dry-run':show command instead of executing' @@ -7170,7 +7414,8 @@ _nvme () { (telemetry-string-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _ocp_telemetry_string_log _ocp_telemetry_string_log=( @@ -7199,7 +7444,8 @@ _nvme () { (unsupported-reqs-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_unsupported_reqs_log _ocp_unsupported_reqs_log=( --dry-run':show command instead of executing' @@ -7245,7 +7491,8 @@ _nvme () { case ${words[2]} in (delete) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_delete _registry_delete=( --attr=':attribute to remove (default: whole entry)' @@ -7271,7 +7518,8 @@ _nvme () { (list) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_list _registry_list=( --dry-run':show command instead of executing' @@ -7294,7 +7542,8 @@ _nvme () { (retrieve) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_retrieve _registry_retrieve=( --attr=':attribute name (e.g. owner, note)' @@ -7320,7 +7569,8 @@ _nvme () { (update) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_update _registry_update=( --attr=':attribute name (e.g. note, owner)' @@ -7367,8 +7617,10 @@ _nvme () { case ${words[2]} in (acquire) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_acquire _resv_acquire=( --crkey=':current reservation key' @@ -7403,8 +7655,10 @@ _nvme () { ;; (register) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_register _resv_register=( --cptpl=':change persistence through power loss setting' @@ -7439,9 +7693,11 @@ _nvme () { ;; (release) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return - local _resv_release + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return + local _resv_release _resv_release=( --crkey=':current reservation key' -c':alias for --crkey' @@ -7473,8 +7729,10 @@ _nvme () { ;; (report) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_report _resv_report=( --dry-run':show command instead of executing' @@ -7529,7 +7787,8 @@ _nvme () { case ${words[2]} in (info) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _rpmb_info _rpmb_info=( --dry-run':show command instead of executing' @@ -7553,7 +7812,8 @@ _nvme () { (program-key) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" && return local _rpmb_program_key _rpmb_program_key=( @@ -7584,7 +7844,8 @@ _nvme () { (read-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--msgfile -f" && return local _rpmb_read_config _rpmb_read_config=( @@ -7611,7 +7872,8 @@ _nvme () { (read-counter) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _rpmb_read_counter _rpmb_read_counter=( --dry-run':show command instead of executing' @@ -7637,7 +7899,8 @@ _nvme () { (read-data) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--msgfile -f" && return local _rpmb_read_data _rpmb_read_data=( @@ -7670,7 +7933,8 @@ _nvme () { (write-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return local _rpmb_write_config @@ -7704,7 +7968,8 @@ _nvme () { (write-data) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return local _rpmb_write_data @@ -7761,7 +8026,8 @@ _nvme () { case ${words[2]} in (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _samsung_vs_internal_log _samsung_vs_internal_log=( @@ -7824,7 +8090,8 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_clear_fw_activate_history _seagate_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -7850,7 +8117,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_clear_pcie_correctable_errors _seagate_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -7876,7 +8144,8 @@ _nvme () { (cloud-SSD-plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_cloud_SSD_plugin_version _seagate_cloud_SSD_plugin_version=( --dry-run':show command instead of executing' @@ -7898,8 +8167,10 @@ _nvme () { ;; (get-ctrl-tele) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_get_ctrl_tele _seagate_get_ctrl_tele=( --dry-run':show command instead of executing' @@ -7926,8 +8197,10 @@ _nvme () { ;; (get-host-tele) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_get_host_tele _seagate_get_host_tele=( --dry-run':show command instead of executing' @@ -7957,7 +8230,8 @@ _nvme () { (plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_plugin_version _seagate_plugin_version=( --dry-run':show command instead of executing' @@ -7980,7 +8254,8 @@ _nvme () { (vs-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_vs_fw_activate_history _seagate_vs_fw_activate_history=( --dry-run':show command instead of executing' @@ -8003,8 +8278,10 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--dump-file -f" && return local _seagate_vs_internal_log _seagate_vs_internal_log=( @@ -8033,7 +8310,8 @@ _nvme () { (vs-log-page-sup) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_vs_log_page_sup _seagate_vs_log_page_sup=( --dry-run':show command instead of executing' @@ -8057,7 +8335,8 @@ _nvme () { (vs-pcie-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_vs_pcie_stats _seagate_vs_pcie_stats=( --dry-run':show command instead of executing' @@ -8081,7 +8360,8 @@ _nvme () { (vs-smart-add-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_vs_smart_add_log _seagate_vs_smart_add_log=( --dry-run':show command instead of executing' @@ -8105,7 +8385,8 @@ _nvme () { (vs-temperature-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_vs_temperature_stats _seagate_vs_temperature_stats=( --dry-run':show command instead of executing' @@ -8146,8 +8427,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _security_recv _security_recv=( --al=':allocation length (cf. SPC-4)' @@ -8186,8 +8469,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -f" && return local _security_send _security_send=( @@ -8249,7 +8534,8 @@ _nvme () { case ${words[2]} in (discover|1) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_discover _sed_discover=( --dry-run':show command instead of executing' @@ -8277,7 +8563,8 @@ _nvme () { (initialize) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_initialize _sed_initialize=( --dry-run':show command instead of executing' @@ -8303,7 +8590,8 @@ _nvme () { (lock) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_lock _sed_lock=( --ask-key':prompt for SED authentication key' @@ -8331,7 +8619,8 @@ _nvme () { (password) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_password _sed_password=( --dry-run':show command instead of executing' @@ -8355,7 +8644,8 @@ _nvme () { (revert) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_revert _sed_revert=( --destructive':destructive revert' @@ -8383,7 +8673,8 @@ _nvme () { (unlock) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sed_unlock _sed_unlock=( --ask-key':prompt for SED authentication key' @@ -8437,7 +8728,8 @@ _nvme () { case ${words[2]} in (change-cap) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_change_cap _sfx_change_cap=( --cap=':cap size in GB' @@ -8467,7 +8759,8 @@ _nvme () { (dump-evtlog) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -f" \ "--output -O" && return local _sfx_dump_evtlog @@ -8503,7 +8796,8 @@ _nvme () { (expand-cap) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_expand_cap _sfx_expand_cap=( --dry-run':show command instead of executing' @@ -8537,7 +8831,8 @@ _nvme () { (get-bad-block) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_get_bad_block _sfx_get_bad_block=( --dry-run':show command instead of executing' @@ -8560,8 +8855,10 @@ _nvme () { ;; (get-feature) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_get_feature _sfx_get_feature=( --dry-run':show command instead of executing' @@ -8589,7 +8886,8 @@ _nvme () { (lat-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_lat_stats _sfx_lat_stats=( --dry-run':show command instead of executing' @@ -8617,7 +8915,8 @@ _nvme () { (query-cap) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_query_cap _sfx_query_cap=( --dry-run':show command instead of executing' @@ -8642,8 +8941,10 @@ _nvme () { ;; (set-feature) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_set_feature _sfx_set_feature=( --dry-run':show command instead of executing' @@ -8674,8 +8975,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_smart_log_add _sfx_smart_log_add=( --dry-run':show command instead of executing' @@ -8705,7 +9008,8 @@ _nvme () { (status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_status _sfx_status=( --dry-run':show command instead of executing' @@ -8750,9 +9054,11 @@ _nvme () { case ${words[2]} in (get-additional-feature) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _shannon_get_additional_feature _shannon_get_additional_feature=( --cdw11=':dword 11 for interrupt vector config' @@ -8790,7 +9096,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _shannon_id_ctrl _shannon_id_ctrl=( --dry-run':show command instead of executing' @@ -8817,8 +9124,10 @@ _nvme () { ;; (set-additioal-feature) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _shannon_set_additioal_feature _shannon_set_additioal_feature=( @@ -8854,8 +9163,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _shannon_smart_log_add _shannon_smart_log_add=( --dry-run':show command instead of executing' @@ -8927,7 +9238,8 @@ _nvme () { case ${words[2]} in (capabilities) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_capabilities _sndk_capabilities=( --dry-run':show command instead of executing' @@ -8951,7 +9263,8 @@ _nvme () { (clear-assert-dump) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_clear_assert_dump _sndk_clear_assert_dump=( --dry-run':show command instead of executing' @@ -8975,7 +9288,8 @@ _nvme () { (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_clear_fw_activate_history _sndk_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -8999,7 +9313,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_clear_pcie_correctable_errors _sndk_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -9023,7 +9338,8 @@ _nvme () { (cloud-SSD-plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_cloud_SSD_plugin_version _sndk_cloud_SSD_plugin_version=( --dry-run':show command instead of executing' @@ -9046,8 +9362,10 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_cloud_boot_SSD_version _sndk_cloud_boot_SSD_version=( --dry-run':show command instead of executing' @@ -9073,7 +9391,8 @@ _nvme () { (cu-smart-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_cu_smart_log _sndk_cu_smart_log=( --dry-run':show command instead of executing' @@ -9099,7 +9418,8 @@ _nvme () { (drive-resize) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_drive_resize _sndk_drive_resize=( --dry-run':show command instead of executing' @@ -9125,7 +9445,8 @@ _nvme () { (get-dev-capabilities-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_get_dev_capabilities_log _sndk_get_dev_capabilities_log=( --dry-run':show command instead of executing' @@ -9149,7 +9470,8 @@ _nvme () { (get-drive-status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_get_drive_status _sndk_get_drive_status=( --dry-run':show command instead of executing' @@ -9173,7 +9495,8 @@ _nvme () { (get-error-recovery-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_get_error_recovery_log _sndk_get_error_recovery_log=( --dry-run':show command instead of executing' @@ -9197,7 +9520,8 @@ _nvme () { (get-latency-monitor-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_get_latency_monitor_log _sndk_get_latency_monitor_log=( --dry-run':show command instead of executing' @@ -9221,7 +9545,8 @@ _nvme () { (get-unsupported-reqs-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_get_unsupported_reqs_log _sndk_get_unsupported_reqs_log=( --dry-run':show command instead of executing' @@ -9245,7 +9570,8 @@ _nvme () { (log-page-directory) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_log_page_directory _sndk_log_page_directory=( --dry-run':show command instead of executing' @@ -9268,8 +9594,10 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_namespace_resize _sndk_namespace_resize=( --dry-run':show command instead of executing' @@ -9297,7 +9625,8 @@ _nvme () { (set-latency-monitor-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_set_latency_monitor_feature _sndk_set_latency_monitor_feature=( --active_bucket_timer_threshold=':This is the value that loads the Active Bucket Timer Threshold.' @@ -9340,8 +9669,10 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_cloud_log _sndk_vs_cloud_log=( --dry-run':show command instead of executing' @@ -9366,8 +9697,10 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_device_waf _sndk_vs_device_waf=( --dry-run':show command instead of executing' @@ -9393,7 +9726,8 @@ _nvme () { (vs-drive-info) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_drive_info _sndk_vs_drive_info=( --dry-run':show command instead of executing' @@ -9417,7 +9751,8 @@ _nvme () { (vs-error-reason-identifier) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -O" && return local _sndk_vs_error_reason_identifier _sndk_vs_error_reason_identifier=( @@ -9446,7 +9781,8 @@ _nvme () { (vs-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_fw_activate_history _sndk_vs_fw_activate_history=( --dry-run':show command instead of executing' @@ -9469,8 +9805,10 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_hw_rev_log _sndk_vs_hw_rev_log=( --dry-run':show command instead of executing' @@ -9496,7 +9834,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" \ "--type -t" && return local _sndk_vs_internal_log @@ -9536,7 +9875,8 @@ _nvme () { (vs-nand-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_nand_stats _sndk_vs_nand_stats=( --dry-run':show command instead of executing' @@ -9560,7 +9900,8 @@ _nvme () { (vs-pcie-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_pcie_stats _sndk_vs_pcie_stats=( --dry-run':show command instead of executing' @@ -9583,8 +9924,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_smart_add_log _sndk_vs_smart_add_log=( --dry-run':show command instead of executing' @@ -9616,7 +9959,8 @@ _nvme () { (vs-telemetry-controller-option) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_telemetry_controller_option _sndk_vs_telemetry_controller_option=( --disable':Disable controller option of the telemetry log page.' @@ -9646,7 +9990,8 @@ _nvme () { (vs-temperature-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_vs_temperature_stats _sndk_vs_temperature_stats=( --dry-run':show command instead of executing' @@ -9702,7 +10047,8 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_clear_fw_activate_history _solidigm_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -9728,7 +10074,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_clear_pcie_correctable_errors _solidigm_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -9754,7 +10101,8 @@ _nvme () { (cloud-SSDplugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_cloud_SSDplugin_version _solidigm_cloud_SSDplugin_version=( --dry-run':show command instead of executing' @@ -9777,7 +10125,8 @@ _nvme () { (garbage-collect-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_garbage_collect_log _solidigm_garbage_collect_log=( --dry-run':show command instead of executing' @@ -9801,7 +10150,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_id_ctrl _solidigm_id_ctrl=( --dry-run':show command instead of executing' @@ -9829,7 +10179,8 @@ _nvme () { (latency-tracking-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_latency_tracking_log _solidigm_latency_tracking_log=( --disable':Disable Latency Tracking' @@ -9863,7 +10214,8 @@ _nvme () { (log-page-directory) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_log_page_directory _solidigm_log_page_directory=( --dry-run':show command instead of executing' @@ -9887,7 +10239,8 @@ _nvme () { (market-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_market_log _solidigm_market_log=( --dry-run':show command instead of executing' @@ -9913,7 +10266,8 @@ _nvme () { (parse-telemetry-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config-file -j" \ "--source-file -s" && return local _solidigm_parse_telemetry_log @@ -9948,8 +10302,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_smart_log_add _solidigm_smart_log_add=( --dry-run':show command instead of executing' @@ -9975,7 +10331,8 @@ _nvme () { (temp-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_temp_stats _solidigm_temp_stats=( --dry-run':show command instead of executing' @@ -10001,7 +10358,8 @@ _nvme () { (vs-drive-info) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_vs_drive_info _solidigm_vs_drive_info=( --dry-run':show command instead of executing' @@ -10025,7 +10383,8 @@ _nvme () { (vs-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_vs_fw_activate_history _solidigm_vs_fw_activate_history=( --dry-run':show command instead of executing' @@ -10049,7 +10408,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--dir-name -d" && return local _solidigm_vs_internal_log _solidigm_vs_internal_log=( @@ -10078,7 +10438,8 @@ _nvme () { (vs-smart-add-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_vs_smart_add_log _solidigm_vs_smart_add_log=( --dry-run':show command instead of executing' @@ -10104,7 +10465,8 @@ _nvme () { (workload-tracker) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_workload_tracker _solidigm_workload_tracker=( --disable':tracker disable' @@ -10168,8 +10530,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ssstc_smart_log_add _ssstc_smart_log_add=( --dry-run':show command instead of executing' @@ -10218,7 +10582,8 @@ _nvme () { case ${words[2]} in (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _toshiba_clear_pcie_correctable_errors _toshiba_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -10242,7 +10607,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _toshiba_vs_internal_log _toshiba_vs_internal_log=( @@ -10270,8 +10636,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _toshiba_vs_smart_add_log _toshiba_vs_smart_add_log=( @@ -10320,7 +10688,8 @@ _nvme () { case ${words[2]} in (badblock) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _transcend_badblock _transcend_badblock=( --dry-run':show command instead of executing' @@ -10344,7 +10713,8 @@ _nvme () { (healthvalue) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _transcend_healthvalue _transcend_healthvalue=( --dry-run':show command instead of executing' @@ -10406,7 +10776,8 @@ _nvme () { case ${words[2]} in (save-smart-to-vtview-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _virtium_save_smart_to_vtview_log _virtium_save_smart_to_vtview_log=( @@ -10439,7 +10810,8 @@ _nvme () { (show-identify) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _virtium_show_identify _virtium_show_identify=( --dry-run':show command instead of executing' @@ -10516,7 +10888,8 @@ _nvme () { case ${words[2]} in (cap-diag) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_cap_diag _wdc_cap_diag=( @@ -10545,7 +10918,8 @@ _nvme () { (capabilities) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_capabilities _wdc_capabilities=( --dry-run':show command instead of executing' @@ -10569,7 +10943,8 @@ _nvme () { (clear-assert-dump) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_clear_assert_dump _wdc_clear_assert_dump=( --dry-run':show command instead of executing' @@ -10593,7 +10968,8 @@ _nvme () { (clear-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_clear_fw_activate_history _wdc_clear_fw_activate_history=( --dry-run':show command instead of executing' @@ -10617,7 +10993,8 @@ _nvme () { (clear-pcie-correctable-errors) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_clear_pcie_correctable_errors _wdc_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -10641,7 +11018,8 @@ _nvme () { (cloud-SSD-plugin-version) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_cloud_SSD_plugin_version _wdc_cloud_SSD_plugin_version=( --dry-run':show command instead of executing' @@ -10664,8 +11042,10 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_cloud_boot_SSD_version _wdc_cloud_boot_SSD_version=( --dry-run':show command instead of executing' @@ -10691,7 +11071,8 @@ _nvme () { (cu-smart-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_cu_smart_log _wdc_cu_smart_log=( --dry-run':show command instead of executing' @@ -10717,7 +11098,8 @@ _nvme () { (drive-essentials) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--dir-name -d" && return local _wdc_drive_essentials _wdc_drive_essentials=( @@ -10744,7 +11126,8 @@ _nvme () { (drive-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_drive_log _wdc_drive_log=( @@ -10771,7 +11154,8 @@ _nvme () { (drive-resize) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_drive_resize _wdc_drive_resize=( --dry-run':show command instead of executing' @@ -10797,7 +11181,8 @@ _nvme () { (enc-get-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_enc_get_log _wdc_enc_get_log=( @@ -10828,7 +11213,8 @@ _nvme () { (get-crash-dump) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_get_crash_dump _wdc_get_crash_dump=( @@ -10855,7 +11241,8 @@ _nvme () { (get-dev-capabilities-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_get_dev_capabilities_log _wdc_get_dev_capabilities_log=( --dry-run':show command instead of executing' @@ -10879,7 +11266,8 @@ _nvme () { (get-drive-status) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_get_drive_status _wdc_get_drive_status=( --dry-run':show command instead of executing' @@ -10903,7 +11291,8 @@ _nvme () { (get-error-recovery-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_get_error_recovery_log _wdc_get_error_recovery_log=( --dry-run':show command instead of executing' @@ -10927,7 +11316,8 @@ _nvme () { (get-latency-monitor-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_get_latency_monitor_log _wdc_get_latency_monitor_log=( --dry-run':show command instead of executing' @@ -10951,7 +11341,8 @@ _nvme () { (get-pfail-dump) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_get_pfail_dump _wdc_get_pfail_dump=( @@ -10978,7 +11369,8 @@ _nvme () { (get-unsupported-reqs-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_get_unsupported_reqs_log _wdc_get_unsupported_reqs_log=( --dry-run':show command instead of executing' @@ -11002,7 +11394,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_id_ctrl _wdc_id_ctrl=( --dry-run':show command instead of executing' @@ -11030,7 +11423,8 @@ _nvme () { (log-page-directory) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_log_page_directory _wdc_log_page_directory=( --dry-run':show command instead of executing' @@ -11053,8 +11447,10 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_namespace_resize _wdc_namespace_resize=( --dry-run':show command instead of executing' @@ -11082,7 +11478,8 @@ _nvme () { (purge) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_purge _wdc_purge=( --dry-run':show command instead of executing' @@ -11106,7 +11503,8 @@ _nvme () { (purge-monitor) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_purge_monitor _wdc_purge_monitor=( --dry-run':show command instead of executing' @@ -11130,7 +11528,8 @@ _nvme () { (set-latency-monitor-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_set_latency_monitor_feature _wdc_set_latency_monitor_feature=( --active_bucket_timer_threshold=':This is the value that loads the Active Bucket Timer Threshold.' @@ -11173,8 +11572,10 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_cloud_log _wdc_vs_cloud_log=( --dry-run':show command instead of executing' @@ -11199,8 +11600,10 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_device_waf _wdc_vs_device_waf=( --dry-run':show command instead of executing' @@ -11226,7 +11629,8 @@ _nvme () { (vs-drive-info) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_drive_info _wdc_vs_drive_info=( --dry-run':show command instead of executing' @@ -11250,7 +11654,8 @@ _nvme () { (vs-error-reason-identifier) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -O" && return local _wdc_vs_error_reason_identifier _wdc_vs_error_reason_identifier=( @@ -11279,7 +11684,8 @@ _nvme () { (vs-fw-activate-history) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_fw_activate_history _wdc_vs_fw_activate_history=( --dry-run':show command instead of executing' @@ -11302,8 +11708,10 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_hw_rev_log _wdc_vs_hw_rev_log=( --dry-run':show command instead of executing' @@ -11329,7 +11737,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" \ "--type -t" && return local _wdc_vs_internal_log @@ -11369,7 +11778,8 @@ _nvme () { (vs-nand-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_nand_stats _wdc_vs_nand_stats=( --dry-run':show command instead of executing' @@ -11393,7 +11803,8 @@ _nvme () { (vs-pcie-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_pcie_stats _wdc_vs_pcie_stats=( --dry-run':show command instead of executing' @@ -11416,8 +11827,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_smart_add_log _wdc_vs_smart_add_log=( --dry-run':show command instead of executing' @@ -11449,7 +11862,8 @@ _nvme () { (vs-telemetry-controller-option) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_telemetry_controller_option _wdc_vs_telemetry_controller_option=( --disable':Disable controller option of the telemetry log page.' @@ -11479,7 +11893,8 @@ _nvme () { (vs-temperature-stats) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_vs_temperature_stats _wdc_vs_temperature_stats=( --dry-run':show command instead of executing' @@ -11519,8 +11934,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ymtc_smart_log_add _ymtc_smart_log_add=( --dry-run':show command instead of executing' @@ -11578,8 +11995,10 @@ _nvme () { case ${words[2]} in (changed-zone-list) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_changed_zone_list _zns_changed_zone_list=( --dry-run':show command instead of executing' @@ -11606,8 +12025,10 @@ _nvme () { ;; (close-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_close_zone _zns_close_zone=( --dry-run':show command instead of executing' @@ -11636,8 +12057,10 @@ _nvme () { ;; (finish-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_finish_zone _zns_finish_zone=( --dry-run':show command instead of executing' @@ -11667,7 +12090,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_id_ctrl _zns_id_ctrl=( --dry-run':show command instead of executing' @@ -11690,8 +12114,10 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_id_ns _zns_id_ns=( --dry-run':show command instead of executing' @@ -11721,7 +12147,8 @@ _nvme () { (list) _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_list _zns_list=( --dry-run':show command instead of executing' @@ -11743,8 +12170,10 @@ _nvme () { ;; (offline-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_offline_zone _zns_offline_zone=( --dry-run':show command instead of executing' @@ -11773,8 +12202,10 @@ _nvme () { ;; (open-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_open_zone _zns_open_zone=( --dry-run':show command instead of executing' @@ -11805,8 +12236,10 @@ _nvme () { ;; (report-zones) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_report_zones _zns_report_zones=( --descs=':number of descriptors to retrieve (default: all of them)' @@ -11843,8 +12276,10 @@ _nvme () { ;; (reset-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_reset_zone _zns_reset_zone=( --dry-run':show command instead of executing' @@ -11873,8 +12308,10 @@ _nvme () { ;; (set-zone-desc) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _zns_set_zone_desc _zns_set_zone_desc=( @@ -11906,8 +12343,10 @@ _nvme () { ;; (zone-append) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return local _zns_zone_append @@ -11954,8 +12393,10 @@ _nvme () { ;; (zone-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_zone_mgmt_recv _zns_zone_mgmt_recv=( --data-len=':length of data in bytes' @@ -11990,8 +12431,10 @@ _nvme () { ;; (zone-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _zns_zone_mgmt_send _zns_zone_mgmt_send=( @@ -12029,8 +12472,10 @@ _nvme () { ;; (zrwa-flush-zone) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_zrwa_flush_zone _zns_zrwa_flush_zone=( --dry-run':show command instead of executing' @@ -12063,8 +12508,10 @@ _nvme () { ;; (admin-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" \ "--metadata -M" && return local _admin_passthru @@ -12134,7 +12581,8 @@ _nvme () { (ana-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ana_log _ana_log=( --dry-run':show command instead of executing' @@ -12159,8 +12607,10 @@ _nvme () { ;; (attach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _attach_ns _attach_ns=( --controllers=':optional comma-sep controller id list' @@ -12190,7 +12640,8 @@ _nvme () { (ave-discovery-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ave_discovery_log _ave_discovery_log=( --dry-run':show command instead of executing' @@ -12216,7 +12667,8 @@ _nvme () { (boot-part-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _boot_part_log _boot_part_log=( @@ -12245,7 +12697,8 @@ _nvme () { (capacity-mgmt) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _capacity_mgmt _capacity_mgmt=( --cap-lower=':Least significant 32 bits of the capacity in bytes of the Endurance Group or NVM Set to be created' @@ -12279,7 +12732,8 @@ _nvme () { (changed-alloc-ns-list-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _changed_alloc_ns_list_log _changed_alloc_ns_list_log=( --dry-run':show command instead of executing' @@ -12305,7 +12759,8 @@ _nvme () { (changed-ns-list-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _changed_ns_list_log _changed_ns_list_log=( --dry-run':show command instead of executing' @@ -12331,7 +12786,8 @@ _nvme () { (check-dhchap-key) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _check_dhchap_key _check_dhchap_key=( --dry-run':show command instead of executing' @@ -12356,7 +12812,8 @@ _nvme () { (check-tls-key) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _check_tls_key _check_tls_key=( --compat':Use non-RFC 8446 compliant algorithm for deriving the TLS PSK identity for older implementations.' @@ -12392,8 +12849,10 @@ _nvme () { ;; (cmdset-ind-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _cmdset_ind_id_ns _cmdset_ind_id_ns=( --dry-run':show command instead of executing' @@ -12420,8 +12879,10 @@ _nvme () { ;; (compare) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return local _compare @@ -12490,7 +12951,8 @@ _nvme () { (connect) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ "--devid-file" && return local _connect @@ -12572,7 +13034,8 @@ _nvme () { (connect-all) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ "--raw -r" && return local _connect_all @@ -12661,8 +13124,10 @@ _nvme () { ;; (copy) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _copy _copy=( --app-tag=':lba application tag (write part)' @@ -12728,7 +13193,8 @@ _nvme () { (create-ns) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _create_ns _create_ns=( --anagrp-id=':ANA Group Identifier (ANAGRPID)' @@ -12791,8 +13257,10 @@ _nvme () { ;; (delete-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _delete_ns _delete_ns=( --dry-run':show command instead of executing' @@ -12819,8 +13287,10 @@ _nvme () { ;; (detach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _detach_ns _detach_ns=( --controllers=':optional comma-sep controller id list' @@ -12849,8 +13319,10 @@ _nvme () { ;; (device-self-test) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _device_self_test _device_self_test=( --dry-run':show command instead of executing' @@ -12882,7 +13354,8 @@ _nvme () { (dim) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dim _dim=( --device=':Comma-separated list of DC nvme device handle.' @@ -12910,8 +13383,10 @@ _nvme () { ;; (dir-receive) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dir_receive _dir_receive=( --data-len=':buffer len (if) data is sent or received' @@ -12948,8 +13423,10 @@ _nvme () { ;; (dir-send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _dir_send _dir_send=( @@ -12992,7 +13469,8 @@ _nvme () { (disconnect) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _disconnect _disconnect=( --concat':enable secure concatenation' @@ -13070,7 +13548,8 @@ _nvme () { (disconnect-all) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _disconnect_all _disconnect_all=( --dry-run':show command instead of executing' @@ -13097,7 +13576,8 @@ _nvme () { (discover) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ "--raw -r" && return local _discover @@ -13186,8 +13666,10 @@ _nvme () { ;; (dispersed-ns-participating-nss-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dispersed_ns_participating_nss_log _dispersed_ns_participating_nss_log=( --dry-run':show command instead of executing' @@ -13212,8 +13694,10 @@ _nvme () { ;; (dsm) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dsm _dsm=( --ad':Attribute Deallocate' @@ -13253,7 +13737,8 @@ _nvme () { (effects-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _effects_log _effects_log=( --csi=':command set identifier' @@ -13281,7 +13766,8 @@ _nvme () { (endurance-event-agg-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _endurance_event_agg_log _endurance_event_agg_log=( --dry-run':show command instead of executing' @@ -13311,7 +13797,8 @@ _nvme () { (endurance-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _endurance_log _endurance_log=( --dry-run':show command instead of executing' @@ -13336,8 +13823,10 @@ _nvme () { ;; (error-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _error_log _error_log=( --csi=':output specified CSI entry only' @@ -13381,7 +13870,8 @@ _nvme () { (fid-support-effects-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fid_support_effects_log _fid_support_effects_log=( --dry-run':show command instead of executing' @@ -13404,8 +13894,10 @@ _nvme () { ;; (flush) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _flush _flush=( --dry-run':show command instead of executing' @@ -13430,8 +13922,10 @@ _nvme () { ;; (format) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _format _format=( --block-size=':target block size' @@ -13475,7 +13969,8 @@ _nvme () { (fw-commit|fw-activate) _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fw_commit _fw_commit=( --action=':[0-7]: commit action: 0 = replace, 1 = replace and activate, 2 = set active, 3 = replace and activate immediate, 6 = replace boot partition, 7 = activate boot partition' @@ -13507,7 +14002,8 @@ _nvme () { (fw-download) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _fw_download _fw_download=( @@ -13545,7 +14041,8 @@ _nvme () { (fw-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fw_log _fw_log=( --dry-run':show command instead of executing' @@ -13571,7 +14068,8 @@ _nvme () { (gen-dhchap-key) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _gen_dhchap_key _gen_dhchap_key=( --dry-run':show command instead of executing' @@ -13602,7 +14100,8 @@ _nvme () { (gen-hostnqn) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _gen_hostnqn _gen_hostnqn=( --dry-run':show command instead of executing' @@ -13625,7 +14124,8 @@ _nvme () { (gen-tls-key) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _gen_tls_key _gen_tls_key=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -13668,9 +14168,11 @@ _nvme () { (get-feature) _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" && return + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" && return local _get_feature _get_feature=( --cdw11=':feature specific dword 11' @@ -13709,8 +14211,10 @@ _nvme () { ;; (get-lba-status) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_lba_status _get_lba_status=( --action=':Action Type(ATYPE) specifies the mechanism the controller uses in determining the LBA Status Descriptors to return.' @@ -13746,8 +14250,10 @@ _nvme () { (get-log) _nvme_opt_vals "--log-id -i" "supported-log-pages error smart fw-slot changed-attached-ns changed-ns cmd-effects device-self-test telemetry-host telemetry-ctrl endurance-group predictable-lat-nvmset predictable-lat-agg ana persistent-event lba-status endurance-grp-evt media-unit-status supported-cap-config-list fid-supported-effects mi-cmd-supported-effects cmd-and-feat-lockdown boot-partition rotational-media-info dispersed-ns-participating-ns mgmt-addr-list phy-rx-eom reachability-groups reachability-associations changed-alloc-ns-list fdp-configs fdp-ruh-usage fdp-stats fdp-events discover host-discover ave-discover pull-model-ddc-req reservation sanitize zns-changed-zones" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_log _get_log=( --aen=':result of the aen, use to override log id' @@ -13799,7 +14305,8 @@ _nvme () { (get-ns-id) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_ns_id _get_ns_id=( --dry-run':show command instead of executing' @@ -13823,7 +14330,8 @@ _nvme () { (get-property) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_property _get_property=( --dry-run':show command instead of executing' @@ -13849,7 +14357,8 @@ _nvme () { (get-reg) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_reg _get_reg=( --acq':ACQ=0x30 register offset' @@ -13903,7 +14412,8 @@ _nvme () { (host-discovery-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _host_discovery_log _host_discovery_log=( --all-host-entries':All Host Entries' @@ -13931,7 +14441,8 @@ _nvme () { (id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ctrl _id_ctrl=( --dry-run':show command instead of executing' @@ -13959,7 +14470,8 @@ _nvme () { (id-domain) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_domain _id_domain=( --dom-id=':identifier of desired domain' @@ -13985,7 +14497,8 @@ _nvme () { (id-iocs) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_iocs _id_iocs=( --controller-id=':identifier of desired controller' @@ -14010,8 +14523,10 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns _id_ns=( --dry-run':show command instead of executing' @@ -14042,7 +14557,8 @@ _nvme () { (id-ns-granularity) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_granularity _id_ns_granularity=( --dry-run':show command instead of executing' @@ -14066,7 +14582,8 @@ _nvme () { (id-ns-lba-format) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_lba_format _id_ns_lba_format=( --dry-run':show command instead of executing' @@ -14094,7 +14611,8 @@ _nvme () { (id-nvmset) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvmset _id_nvmset=( --dry-run':show command instead of executing' @@ -14120,7 +14638,8 @@ _nvme () { (id-uuid) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_uuid _id_uuid=( --dry-run':show command instead of executing' @@ -14145,8 +14664,10 @@ _nvme () { ;; (io-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _io_mgmt_recv _io_mgmt_recv=( @@ -14180,8 +14701,10 @@ _nvme () { ;; (io-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _io_mgmt_send _io_mgmt_send=( @@ -14215,8 +14738,10 @@ _nvme () { ;; (io-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" \ "--metadata -M" && return local _io_passthru @@ -14286,7 +14811,8 @@ _nvme () { (lba-status-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lba_status_log _lba_status_log=( --dry-run':show command instead of executing' @@ -14312,7 +14838,8 @@ _nvme () { (list) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list _list=( --dry-run':show command instead of executing' @@ -14334,8 +14861,10 @@ _nvme () { ;; (list-ctrl) - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_ctrl _list_ctrl=( --cntid=':controller to display' @@ -14363,7 +14892,8 @@ _nvme () { (list-endgrp) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_endgrp _list_endgrp=( --dry-run':show command instead of executing' @@ -14388,8 +14918,10 @@ _nvme () { ;; (list-ns) - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_ns _list_ns=( --all':show all namespaces in the subsystem, whether attached or inactive' @@ -14419,7 +14951,8 @@ _nvme () { (list-secondary) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_secondary _list_secondary=( --cntid=':lowest controller identifier to display' @@ -14447,7 +14980,8 @@ _nvme () { (list-subsys) _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_subsys _list_subsys=( --dry-run':show command instead of executing' @@ -14471,7 +15005,8 @@ _nvme () { (lockdown) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lockdown _lockdown=( --csel=':[0,1,2,15] Controller Select (CSEL) specifies the controller(s) affected by this command: 0 = NVM subsystem, 1 = specific controller (see --css), 2 = secondary controllers for a specific primary controller (see --css), 15 = vendor specific.' @@ -14509,7 +15044,8 @@ _nvme () { (media-unit-stat-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _media_unit_stat_log _media_unit_stat_log=( --domain-id=':Domain Identifier' @@ -14537,7 +15073,8 @@ _nvme () { (mgmt-addr-list-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _mgmt_addr_list_log _mgmt_addr_list_log=( --dry-run':show command instead of executing' @@ -14561,7 +15098,8 @@ _nvme () { (mi-cmd-support-effects-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _mi_cmd_support_effects_log _mi_cmd_support_effects_log=( --dry-run':show command instead of executing' @@ -14584,8 +15122,10 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_descs _ns_descs=( --dry-run':show command instead of executing' @@ -14613,7 +15153,8 @@ _nvme () { (ns-rescan) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_rescan _ns_rescan=( --dry-run':show command instead of executing' @@ -14637,7 +15178,8 @@ _nvme () { (nvm-id-ctrl) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvm_id_ctrl _nvm_id_ctrl=( --dry-run':show command instead of executing' @@ -14660,8 +15202,10 @@ _nvme () { ;; (nvm-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvm_id_ns _nvm_id_ns=( --dry-run':show command instead of executing' @@ -14689,7 +15233,8 @@ _nvme () { (nvm-id-ns-lba-format) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvm_id_ns_lba_format _nvm_id_ns_lba_format=( --dry-run':show command instead of executing' @@ -14716,8 +15261,10 @@ _nvme () { ;; (nvme-mi-recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_recv _nvme_mi_recv=( @@ -14755,8 +15302,10 @@ _nvme () { ;; (nvme-mi-send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -i" && return local _nvme_mi_send _nvme_mi_send=( @@ -14795,7 +15344,8 @@ _nvme () { (persistent-event-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _persistent_event_log _persistent_event_log=( --action=':action the controller shall take during processing this persistent log page command.' @@ -14825,7 +15375,8 @@ _nvme () { (phy-rx-eom-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _phy_rx_eom_log _phy_rx_eom_log=( --controller=':Target Controller ID.' @@ -14853,7 +15404,8 @@ _nvme () { (power-measurement-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _power_measurement_log _power_measurement_log=( --dry-run':show command instead of executing' @@ -14879,7 +15431,8 @@ _nvme () { (pred-lat-event-agg-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _pred_lat_event_agg_log _pred_lat_event_agg_log=( --dry-run':show command instead of executing' @@ -14909,7 +15462,8 @@ _nvme () { (predictable-lat-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _predictable_lat_log _predictable_lat_log=( --dry-run':show command instead of executing' @@ -14937,7 +15491,8 @@ _nvme () { (primary-ctrl-caps) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _primary_ctrl_caps _primary_ctrl_caps=( --cntlid=':Controller ID' @@ -14963,7 +15518,8 @@ _nvme () { (pull-model-ddc-req-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _pull_model_ddc_req_log _pull_model_ddc_req_log=( --dry-run':show command instead of executing' @@ -14989,7 +15545,8 @@ _nvme () { (reachability-associations-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reachability_associations_log _reachability_associations_log=( --associations-only':Return Associations Only' @@ -15017,7 +15574,8 @@ _nvme () { (reachability-groups-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reachability_groups_log _reachability_groups_log=( --dry-run':show command instead of executing' @@ -15044,8 +15602,10 @@ _nvme () { ;; (read) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return local _read @@ -15114,7 +15674,8 @@ _nvme () { (reset) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reset _reset=( --dry-run':show command instead of executing' @@ -15137,8 +15698,10 @@ _nvme () { ;; (resv-acquire) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_acquire _resv_acquire=( --crkey=':current reservation key' @@ -15174,7 +15737,8 @@ _nvme () { (resv-notif-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_notif_log _resv_notif_log=( --dry-run':show command instead of executing' @@ -15197,8 +15761,10 @@ _nvme () { ;; (resv-register) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_register _resv_register=( --cptpl=':change persistence through power loss setting' @@ -15233,8 +15799,10 @@ _nvme () { ;; (resv-release) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_release _resv_release=( --crkey=':current reservation key' @@ -15267,8 +15835,10 @@ _nvme () { ;; (resv-report) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_report _resv_report=( --dry-run':show command instead of executing' @@ -15300,7 +15870,8 @@ _nvme () { (rotational-media-info-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _rotational_media_info_log _rotational_media_info_log=( --dry-run':show command instead of executing' @@ -15327,7 +15898,8 @@ _nvme () { (sanitize) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" && return + "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" && return local _sanitize _sanitize=( --ause':Allow unrestricted sanitize exit.' @@ -15373,7 +15945,8 @@ _nvme () { (sanitize-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sanitize_log _sanitize_log=( --dry-run':show command instead of executing' @@ -15402,7 +15975,8 @@ _nvme () { (sanitize-ns) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" && return + "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" && return local _sanitize_ns _sanitize_ns=( --ause':Allow unrestricted sanitize exit.' @@ -15435,8 +16009,10 @@ _nvme () { ;; (security-recv) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _security_recv _security_recv=( --al=':allocation length (cf. SPC-4)' @@ -15475,8 +16051,10 @@ _nvme () { ;; (security-send) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -f" && return local _security_send _security_send=( @@ -15515,7 +16093,8 @@ _nvme () { (self-test-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _self_test_log _self_test_log=( --dry-run':show command instead of executing' @@ -15541,8 +16120,10 @@ _nvme () { (set-feature) _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _set_feature _set_feature=( @@ -15583,7 +16164,8 @@ _nvme () { (set-property) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _set_property _set_property=( --dry-run':show command instead of executing' @@ -15611,7 +16193,8 @@ _nvme () { (set-reg) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _set_reg _set_reg=( --acq=':ACQ=0x30 register offset' @@ -15656,7 +16239,8 @@ _nvme () { (show-hostnqn) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _show_hostnqn _show_hostnqn=( --dry-run':show command instead of executing' @@ -15679,7 +16263,8 @@ _nvme () { (show-regs) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _show_regs _show_regs=( --dry-run':show command instead of executing' @@ -15703,7 +16288,8 @@ _nvme () { (show-topology) _nvme_opt_vals "--output-format -o" "normal json tabular" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _show_topology _show_topology=( --dry-run':show command instead of executing' @@ -15728,8 +16314,10 @@ _nvme () { ;; (smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _smart_log _smart_log=( --dry-run':show command instead of executing' @@ -15757,7 +16345,8 @@ _nvme () { (subsystem-reset) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _subsystem_reset _subsystem_reset=( --dry-run':show command instead of executing' @@ -15781,7 +16370,8 @@ _nvme () { (supported-cap-config-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _supported_cap_config_log _supported_cap_config_log=( --domain-id=':Domain Identifier' @@ -15809,7 +16399,8 @@ _nvme () { (supported-log-pages) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _supported_log_pages _supported_log_pages=( --dry-run':show command instead of executing' @@ -15833,7 +16424,8 @@ _nvme () { (telemetry-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _telemetry_log _telemetry_log=( @@ -15874,7 +16466,8 @@ _nvme () { (top) _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _top _top=( --delay=':refresh interval in seconds' @@ -15898,8 +16491,10 @@ _nvme () { ;; (verify) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _verify _verify=( --app-tag=':app tag for end-to-end PI' @@ -15945,7 +16540,8 @@ _nvme () { (virt-mgmt) _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _virt_mgmt _virt_mgmt=( --act=':Action(ACT): [1,7,8,9] 1h: Primary Flexible 7h: Secondary Offline 8h: Secondary Assign 9h: Secondary Online' @@ -15976,8 +16572,10 @@ _nvme () { ;; (write) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" \ "--metadata -M" && return local _write @@ -16045,8 +16643,10 @@ _nvme () { ;; (write-uncor) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _write_uncor _write_uncor=( --block-count=':number of blocks (zeroes based) on device to access' @@ -16079,8 +16679,10 @@ _nvme () { ;; (write-zeroes) - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _write_zeroes _write_zeroes=( --app-tag=':app tag for end-to-end PI' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index 7414439308..d2a1acb696 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -162,8 +162,10 @@ nvme_list_opts () { opts+=" --cdw10= -4 --cdw11= -5 --cdw12= -6 --cdw13= -7 --cdw14= -8 --cdw15= -9 --cdw2= -2 --cdw3= -3 --data-len= -l --dry-run --flags= -f --input-file= -i --latency -T --metadata= -M --metadata-len= -m --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --prefill= -p --quiet --raw-binary -b --read -r --rsvd= -R --set-options= --show-command -s --timeout= --verbose -v --write -w" valopts+=" --cdw10 -4 --cdw11 -5 --cdw12 -6 --cdw13 -7 --cdw14 -8 --cdw15 -9 --cdw2 -2 --cdw3 -3 --data-len -l --flags -f --input-file -i --metadata -M --metadata-len -m --namespace-id -n --opcode -O --output-format -o --output-format-version --prefill -p --rsvd -R --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" "--metadata -M" ;; @@ -172,15 +174,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "attach-ns") opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ave-discovery-log") @@ -188,7 +193,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "boot-part-log") @@ -196,7 +202,8 @@ nvme_list_opts () { valopts+=" --lsp -s --output-file -f --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -205,7 +212,8 @@ nvme_list_opts () { valopts+=" --cap-lower -l --cap-upper -u --element-id -i --operation -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-alloc-ns-list-log") @@ -213,7 +221,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-ns-list-log") @@ -221,7 +230,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "check-dhchap-key") @@ -229,7 +239,8 @@ nvme_list_opts () { valopts+=" --key -k --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -238,7 +249,8 @@ nvme_list_opts () { valopts+=" --hostnqn -n --identity -I --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -246,16 +258,20 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "compare") opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -264,7 +280,8 @@ nvme_list_opts () { valopts+=" --config -J --ctrl-loss-tmo -l --devid-file --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" "--devid-file" nodevice=1 ;; @@ -274,7 +291,8 @@ nvme_list_opts () { valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" "--raw -r" nodevice=1 ;; @@ -283,8 +301,10 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --blocks= -b --dir-spec= -S --dir-type= -T --dry-run --expected-app-tag-masks= -M --expected-app-tags= -A --expected-ref-tags= -R --force-unit-access -f --format= -F --limited-retry -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfor= -P --prinfow= -p --quiet --ref-tag= -r --sdlba= -d --set-options= --slbs= -s --snsids= -N --sopts= -O --storage-tag= -t --storage-tag-check -c --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --blocks -b --dir-spec -S --dir-type -T --expected-app-tag-masks -M --expected-app-tags -A --expected-ref-tags -R --format -F --namespace-id -n --output-format -o --output-format-version --prinfor -P --prinfow -p --ref-tag -r --sdlba -d --set-options --slbs -s --snsids -N --sopts -O --storage-tag -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "create-ns") @@ -292,31 +312,38 @@ nvme_list_opts () { valopts+=" --anagrp-id -a --block-size -b --csi -y --dps -d --endg-id -e --flbas -f --lbstm -l --ncap -c --ncap-si -C --nmic -m --nphndls -n --nsze -s --nsze-si -S --nvmset-id -i --output-format -o --output-format-version --phndls -p --rar -r --rnumzrwa -u --ror -O --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "delete-ns") opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "detach-ns") opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "device-self-test") opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --self-test-code= -s --set-options= --timeout= --verbose -v --wait -w" valopts+=" --namespace-id -n --output-format -o --output-format-version --self-test-code -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dim") @@ -324,7 +351,8 @@ nvme_list_opts () { valopts+=" --device -d --nqn -n --output-format -o --output-format-version --set-options --task -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -332,16 +360,20 @@ nvme_list_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --req-resource= -r --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --namespace-id -n --output-format -o --output-format-version --req-resource -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dir-send") opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --endir= -e --input-file= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --target-dir= -T --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --endir -e --input-file -i --namespace-id -n --output-format -o --output-format-version --set-options --target-dir -T --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; @@ -350,7 +382,8 @@ nvme_list_opts () { valopts+=" --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -359,7 +392,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --owner --set-options --timeout --transport -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -368,7 +402,8 @@ nvme_list_opts () { valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" "--raw -r" nodevice=1 ;; @@ -377,16 +412,20 @@ nvme_list_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dsm") opts+=" --ad -d --blocks= -b --cdw11= -c --ctx-attrs= -a --dry-run --idr -r --idw -w --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --slbs= -s --timeout= --verbose -v" valopts+=" --blocks -b --cdw11 -c --ctx-attrs -a --namespace-id -n --output-format -o --output-format-version --set-options --slbs -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "effects-log") @@ -394,7 +433,8 @@ nvme_list_opts () { valopts+=" --csi -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-event-agg-log") @@ -402,7 +442,8 @@ nvme_list_opts () { valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-log") @@ -410,15 +451,18 @@ nvme_list_opts () { valopts+=" --group-id -g --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "error-log") opts+=" --csi= -c --dry-run --lba= -l --log-entries= -e --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --sqid= -S --status= -s --timeout= --trtype= -t --valid-entry -V --verbose -v" valopts+=" --csi -c --lba -l --log-entries -e --namespace-id -n --opcode -O --output-format -o --output-format-version --set-options --sqid -S --status -s --timeout --trtype -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fid-support-effects-log") @@ -426,23 +470,28 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "flush") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "format") opts+=" --block-size= -b --dry-run --force --ish -I --lbaf= -l --ms= -m --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pi= -i --pil= -p --quiet --reset -r --ses= -s --set-options= --timeout= --verbose -v" valopts+=" --block-size -b --lbaf -l --ms -m --namespace-id -n --output-format -o --output-format-version --pi -i --pil -p --ses -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw-commit"|"fw-activate") @@ -451,7 +500,8 @@ nvme_list_opts () { _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw-download") @@ -459,7 +509,8 @@ nvme_list_opts () { valopts+=" --fw -f --offset -O --output-format -o --output-format-version --set-options --timeout --xfer -x" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -468,7 +519,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "gen-dhchap-key") @@ -476,7 +528,8 @@ nvme_list_opts () { valopts+=" --hmac -m --key-length -l --nqn -n --output-format -o --output-format-version --secret -s --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -485,7 +538,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -494,7 +548,8 @@ nvme_list_opts () { valopts+=" --hmac -m --hostnqn -n --identity -I --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --secret -s --set-options --subsysnqn -c --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -503,17 +558,21 @@ nvme_list_opts () { valopts+=" --cdw11 -c --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout --uuid-index -U" _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-lba-status") opts+=" --action= -a --dry-run --ish -I --max-dw= -m --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --range-len= -l --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --action -a --max-dw -m --namespace-id -n --output-format -o --output-format-version --range-len -l --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-log") @@ -521,8 +580,10 @@ nvme_list_opts () { valopts+=" --aen -a --csi -y --log-id -i --log-len -l --lpo -L --lsi -S --lsp -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U --xfer-len -x" _nvme_opt_vals "--log-id -i" "supported-log-pages error smart fw-slot changed-attached-ns changed-ns cmd-effects device-self-test telemetry-host telemetry-ctrl endurance-group predictable-lat-nvmset predictable-lat-agg ana persistent-event lba-status endurance-grp-evt media-unit-status supported-cap-config-list fid-supported-effects mi-cmd-supported-effects cmd-and-feat-lockdown boot-partition rotational-media-info dispersed-ns-participating-ns mgmt-addr-list phy-rx-eom reachability-groups reachability-associations changed-alloc-ns-list fdp-configs fdp-ruh-usage fdp-stats fdp-events discover host-discover ave-discover pull-model-ddc-req reservation sanitize zns-changed-zones" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-ns-id") @@ -530,7 +591,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-property") @@ -538,7 +600,8 @@ nvme_list_opts () { valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-reg") @@ -546,7 +609,8 @@ nvme_list_opts () { valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "host-discovery-log") @@ -554,7 +618,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -562,7 +627,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-domain") @@ -570,7 +636,8 @@ nvme_list_opts () { valopts+=" --dom-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-iocs") @@ -578,15 +645,18 @@ nvme_list_opts () { valopts+=" --controller-id -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ns") opts+=" --dry-run --force --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ns-granularity") @@ -594,7 +664,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ns-lba-format") @@ -602,7 +673,8 @@ nvme_list_opts () { valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-nvmset") @@ -610,7 +682,8 @@ nvme_list_opts () { valopts+=" --nvmset_id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-uuid") @@ -618,15 +691,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "io-mgmt-recv") opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -634,8 +710,10 @@ nvme_list_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -643,8 +721,10 @@ nvme_list_opts () { opts+=" --cdw10= -4 --cdw11= -5 --cdw12= -6 --cdw13= -7 --cdw14= -8 --cdw15= -9 --cdw2= -2 --cdw3= -3 --data-len= -l --dry-run --flags= -f --input-file= -i --latency -T --metadata= -M --metadata-len= -m --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --prefill= -p --quiet --raw-binary -b --read -r --rsvd= -R --set-options= --show-command -s --timeout= --verbose -v --write -w" valopts+=" --cdw10 -4 --cdw11 -5 --cdw12 -6 --cdw13 -7 --cdw14 -8 --cdw15 -9 --cdw2 -2 --cdw3 -3 --data-len -l --flags -f --input-file -i --metadata -M --metadata-len -m --namespace-id -n --opcode -O --output-format -o --output-format-version --prefill -p --rsvd -R --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" "--metadata -M" ;; @@ -653,7 +733,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") @@ -661,7 +742,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -669,8 +751,10 @@ nvme_list_opts () { opts+=" --cntid= -c --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list-endgrp") @@ -678,15 +762,18 @@ nvme_list_opts () { valopts+=" --endgrp-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list-ns") opts+=" --all -a --csi= -y --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --csi -y --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list-secondary") @@ -694,7 +781,8 @@ nvme_list_opts () { valopts+=" --cntid -c --num-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list-subsys") @@ -702,7 +790,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lockdown") @@ -710,7 +799,8 @@ nvme_list_opts () { valopts+=" --csel -c --css -C --ifc -f --ofi -O --output-format -o --output-format-version --prhbt -p --scp -s --set-options --timeout --uuid -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "media-unit-stat-log") @@ -718,7 +808,8 @@ nvme_list_opts () { valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mgmt-addr-list-log") @@ -726,7 +817,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mi-cmd-support-effects-log") @@ -734,15 +826,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-descs") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-rescan") @@ -750,7 +845,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-id-ctrl") @@ -758,15 +854,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-id-ns") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-id-ns-lba-format") @@ -774,15 +873,18 @@ nvme_list_opts () { valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvme-mi-recv") opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; @@ -790,8 +892,10 @@ nvme_list_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; @@ -800,7 +904,8 @@ nvme_list_opts () { valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "phy-rx-eom-log") @@ -808,7 +913,8 @@ nvme_list_opts () { valopts+=" --controller -c --lsp -s --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "power-measurement-log") @@ -816,7 +922,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pred-lat-event-agg-log") @@ -824,7 +931,8 @@ nvme_list_opts () { valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "predictable-lat-log") @@ -832,7 +940,8 @@ nvme_list_opts () { valopts+=" --nvmset-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "primary-ctrl-caps") @@ -840,7 +949,8 @@ nvme_list_opts () { valopts+=" --cntlid -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pull-model-ddc-req-log") @@ -848,7 +958,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-associations-log") @@ -856,7 +967,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-groups-log") @@ -864,15 +976,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "read") opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -881,15 +996,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-acquire") opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prkey= -p --quiet --racqa= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --prkey -p --racqa -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-notif-log") @@ -897,31 +1015,38 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-register") opts+=" --cptpl= -p --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --nrkey= -k --output-format= -o --output-format-version= --quiet --rrega= -r --set-options= --timeout= --verbose -v" valopts+=" --cptpl -p --crkey -c --namespace-id -n --nrkey -k --output-format -o --output-format-version --rrega -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-release") opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rrela= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --rrela -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-report") opts+=" --dry-run --eds -e --namespace-id= -n --no-ioctl-probing --no-retries --numd= -d --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --numd -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "rotational-media-info-log") @@ -929,7 +1054,8 @@ nvme_list_opts () { valopts+=" --endg-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize") @@ -938,7 +1064,8 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" + "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" ;; "sanitize-log") @@ -946,7 +1073,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize-ns") @@ -955,23 +1083,28 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" + "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" ;; "security-recv") opts+=" --al= -t --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --raw-binary -b --secp= -p --set-options= --size= -x --spsp= -s --timeout= --verbose -v" valopts+=" --al -t --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --size -x --spsp -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "security-send") opts+=" --dry-run --file= -f --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --secp= -p --set-options= --spsp= -s --timeout= --tl= -t --verbose -v" valopts+=" --file -f --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --spsp -s --timeout --tl -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -f" ;; @@ -980,7 +1113,8 @@ nvme_list_opts () { valopts+=" --dst-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-feature") @@ -988,8 +1122,10 @@ nvme_list_opts () { valopts+=" --cdw12 -c --data -d --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U --value -V" _nvme_opt_vals "--feature-id -f" "arbitration power-mgmt lba-range temp-thresh err-recovery volatile-wc num-queues irq-coalesce irq-config write-atomic async-event auto-pst host-mem-buf timestamp kato hctm nopsc rrl plm-config plm-window lba-sts-interval host-behavior sanitize endurance-evt-cfg iocs-profile spinup-control power-loss-signal perf-characteristics fdp fdp-events ns-admin-label key-value ctrl-data-queue emb-mgmt-ctrl-addr host-mgmt-agent-addr enh-ctrl-metadata ctrl-metadata ns-metadata sw-progress host-id resv-nf-mask resv-persist write-protect bp-write-protect" \ + "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -998,7 +1134,8 @@ nvme_list_opts () { valopts+=" --offset -O --output-format -o --output-format-version --set-options --timeout --value -V" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-reg") @@ -1006,7 +1143,8 @@ nvme_list_opts () { valopts+=" --acq --aqa --asq --bpmbl --bprsel --cc --cmbmsc --csts --intmc --intms --nssd --nssr --offset -O --output-format -o --output-format-version --pmrctl --pmrmscl --pmrmscu --set-options --timeout --value -V" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "show-hostnqn") @@ -1014,7 +1152,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1023,7 +1162,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "show-topology") @@ -1031,15 +1171,18 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --ranking -r --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json tabular" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-log") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "subsystem-reset") @@ -1047,7 +1190,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-cap-config-log") @@ -1055,7 +1199,8 @@ nvme_list_opts () { valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-log-pages") @@ -1063,7 +1208,8 @@ nvme_list_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry-log") @@ -1071,7 +1217,8 @@ nvme_list_opts () { valopts+=" --data-area -d --host-generate -g --mcda -m --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -1084,7 +1231,8 @@ nvme_list_opts () { valopts+=" --delay -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1092,8 +1240,10 @@ nvme_list_opts () { opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --dry-run --force-unit-access -f --limited-retry -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --start-block= -s --storage-tag= -S --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "virt-mgmt") @@ -1101,15 +1251,18 @@ nvme_list_opts () { valopts+=" --act -a --cntlid -c --nr -n --output-format -o --output-format-version --rt -r --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "write") opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --block-size= -b --data= -d --data-size= -z --dir-spec= -S --dir-type= -T --dry-run --dsm= -D --force --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --show-command -V --start-block= -s --storage-tag= -g --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --block-size -b --data -d --data-size -z --dir-spec -S --dir-type -T --dsm -D --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -g --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -1117,16 +1270,20 @@ nvme_list_opts () { opts+=" --block-count= -c --dir-spec= -S --dir-type= -T --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --start-block= -s --timeout= --verbose -v" valopts+=" --block-count -c --dir-spec -S --dir-type -T --namespace-id -n --output-format -o --output-format-version --set-options --start-block -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "write-zeroes") opts+=" --app-tag= -a --app-tag-mask= -m --block-count= -c --deac -d --dir-spec= -D --dir-type= -T --dry-run --force-unit-access -f --limited-retry -l --namespace-id= -n --namespace-zeroes -Z --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prinfo= -p --quiet --ref-tag= -r --set-options= --start-block= -s --storage-tag= -S --storage-tag-check -C --timeout= --verbose -v" valopts+=" --app-tag -a --app-tag-mask -m --block-count -c --dir-spec -D --dir-type -T --namespace-id -n --output-format -o --output-format-version --prinfo -p --ref-tag -r --set-options --start-block -s --storage-tag -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 2 @@ -1152,7 +1309,8 @@ plugin_amzn_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "stats") @@ -1160,7 +1318,8 @@ plugin_amzn_opts () { valopts+=" --interval -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1186,7 +1345,8 @@ plugin_config_opts () { valopts+=" --config -J --output -o --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" "--output -o" nodevice=1 ;; @@ -1196,7 +1356,8 @@ plugin_config_opts () { valopts+=" --ctrl-loss-tmo -l --fast-io-fail-tmo -F --host-iface -f --host-symname --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output --output-format -o --output-format-version --persistent --queue-size -Q --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output" nodevice=1 ;; @@ -1206,7 +1367,8 @@ plugin_config_opts () { valopts+=" --config -J --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" nodevice=1 ;; @@ -1216,7 +1378,8 @@ plugin_config_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1225,7 +1388,8 @@ plugin_config_opts () { valopts+=" --config -J --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" nodevice=1 ;; @@ -1252,8 +1416,10 @@ plugin_dapustor_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1279,7 +1445,8 @@ plugin_dell_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1305,7 +1472,8 @@ plugin_dera_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1330,16 +1498,20 @@ plugin_dir_opts () { opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --req-resource= -r --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --namespace-id -n --output-format -o --output-format-version --req-resource -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "send") opts+=" --data-len= -l --dir-oper= -O --dir-spec= -S --dir-type= -D --dry-run --endir= -e --input-file= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --target-dir= -T --timeout= --verbose -v" valopts+=" --data-len -l --dir-oper -O --dir-spec -S --dir-type -D --endir -e --input-file -i --namespace-id -n --output-format -o --output-format-version --set-options --target-dir -T --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; esac @@ -1366,7 +1538,8 @@ plugin_exclusion_opts () { valopts+=" --entry -e --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1375,7 +1548,8 @@ plugin_exclusion_opts () { valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1384,7 +1558,8 @@ plugin_exclusion_opts () { valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1393,7 +1568,8 @@ plugin_exclusion_opts () { valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1402,7 +1578,8 @@ plugin_exclusion_opts () { valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -1411,7 +1588,8 @@ plugin_exclusion_opts () { valopts+=" --name -N --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; esac @@ -1438,7 +1616,8 @@ plugin_fdp_opts () { valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "events") @@ -1446,7 +1625,8 @@ plugin_fdp_opts () { valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "feature") @@ -1454,15 +1634,18 @@ plugin_fdp_opts () { valopts+=" --enable-conf-idx -c --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-events") opts+=" --dry-run --enable -e --event-types= -t --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --placement-handle= -p --quiet --save -s --set-options= --timeout= --verbose -v" valopts+=" --event-types -t --namespace-id -n --output-format -o --output-format-version --placement-handle -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "stats") @@ -1470,23 +1653,28 @@ plugin_fdp_opts () { valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "status") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "update") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --pids= -p --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --pids -p --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "usage") @@ -1494,7 +1682,8 @@ plugin_fdp_opts () { valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1521,7 +1710,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "async-event-conf") @@ -1530,16 +1720,19 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "err-recovery") opts+=" --dry-run --dulbe -d --no-ioctl-probing --no-retries --nsid= -n --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --tler= -t --verbose -v" valopts+=" --nsid -n --output-format -o --output-format-version --sel -S --set-options --timeout --tler -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--nsid -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "hctm") @@ -1548,7 +1741,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "host-behavior-support") @@ -1557,7 +1751,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "int-coalesce") @@ -1566,7 +1761,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "int-vector-config") @@ -1575,7 +1771,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "keep-alive-timer") @@ -1584,16 +1781,19 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "lba-range-type") opts+=" --dry-run --guids= -g --hlbars= --lbaros= -l --nlbs= -b --no-ioctl-probing --no-retries --nsid= -n --num= -N --output-format= -o --output-format-version= --quiet --save --sel= -S --set-options= --slbas= -s --timeout= --types= -t --verbose -v" valopts+=" --guids -g --hlbars --lbaros -l --nlbs -b --nsid -n --num -N --output-format -o --output-format-version --sel -S --set-options --slbas -s --timeout --types -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--nsid -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "num-queues") @@ -1602,16 +1802,19 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "perf-characteristics") opts+=" --attri= -a --attrl= -A --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --paid= -p --quiet --r4karl= -R --rvspa -r --save -s --sel= -S --set-options= --timeout= --verbose -v --vs-data= -V" valopts+=" --attri -a --attrl -A --namespace-id -n --output-format -o --output-format-version --paid -p --r4karl -R --sel -S --set-options --timeout --vs-data -V" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" _nvme_opt_files "--vs-data -V" ;; @@ -1621,7 +1824,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "power-meas") @@ -1630,7 +1834,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "power-mgmt") @@ -1639,7 +1844,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "power-thresh") @@ -1648,7 +1854,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "temp-thresh") @@ -1657,7 +1864,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "timestamp") @@ -1666,7 +1874,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "volatile-wc") @@ -1675,7 +1884,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "write-atom-normal") @@ -1684,7 +1894,8 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1711,7 +1922,8 @@ plugin_fw_opts () { _nvme_opt_vals "--action -a" "replace replace-and-activate set-active replace-and-activate-immediate replace-boot-partition activate-boot-partition" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "download") @@ -1719,7 +1931,8 @@ plugin_fw_opts () { valopts+=" --fw -f --offset -O --output-format -o --output-format-version --set-options --timeout --xfer -x" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; esac @@ -1746,7 +1959,8 @@ plugin_huawei_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") @@ -1754,7 +1968,8 @@ plugin_huawei_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; esac @@ -1781,7 +1996,8 @@ plugin_ibm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persist-event-log") @@ -1789,7 +2005,8 @@ plugin_ibm_opts () { valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vpd") @@ -1797,7 +2014,8 @@ plugin_ibm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1823,15 +2041,18 @@ plugin_id_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ctrl-list") opts+=" --cntid= -c --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --cntid -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "domain") @@ -1839,7 +2060,8 @@ plugin_id_opts () { valopts+=" --dom-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endgrp-list") @@ -1847,7 +2069,8 @@ plugin_id_opts () { valopts+=" --endgrp-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "iocs") @@ -1855,23 +2078,28 @@ plugin_id_opts () { valopts+=" --controller-id -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns") opts+=" --dry-run --force --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-descs") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-granularity") @@ -1879,15 +2107,18 @@ plugin_id_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-ind") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-lba-format") @@ -1895,15 +2126,18 @@ plugin_id_opts () { valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ns-list") opts+=" --all -a --csi= -y --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --csi -y --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-ctrl") @@ -1911,15 +2145,18 @@ plugin_id_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-ns") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --uuid-index= -U --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout --uuid-index -U" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-ns-lba-format") @@ -1927,7 +2164,8 @@ plugin_id_opts () { valopts+=" --lba-format-index -i --output-format -o --output-format-version --set-options --timeout --uuid-index -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvmset") @@ -1935,7 +2173,8 @@ plugin_id_opts () { valopts+=" --nvmset_id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "primary-ctrl-caps") @@ -1943,7 +2182,8 @@ plugin_id_opts () { valopts+=" --cntlid -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "secondary-ctrl-list") @@ -1951,7 +2191,8 @@ plugin_id_opts () { valopts+=" --cntid -c --num-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "uuid") @@ -1959,7 +2200,8 @@ plugin_id_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1985,7 +2227,8 @@ plugin_innogrit_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-eventlog") @@ -1993,7 +2236,8 @@ plugin_innogrit_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2019,7 +2263,8 @@ plugin_inspur_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2045,15 +2290,18 @@ plugin_intel_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "internal-log") opts+=" --dry-run --log= -l --namespace-id= -n --nlognum= -m --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --region= -r --set-options= --timeout= --verbose -v --verbose-nlog -V" valopts+=" --log -l --namespace-id -n --nlognum -m --output-file -O --output-format -o --output-format-version --region -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -2062,7 +2310,8 @@ plugin_intel_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats-tracking") @@ -2070,7 +2319,8 @@ plugin_intel_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "market-name") @@ -2078,7 +2328,8 @@ plugin_intel_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-bucket-thresholds") @@ -2086,15 +2337,18 @@ plugin_intel_opts () { valopts+=" --bucket-thresholds -t --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-log-add") opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "temp-stats") @@ -2102,7 +2356,8 @@ plugin_intel_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2127,8 +2382,10 @@ plugin_io_mgmt_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -2136,8 +2393,10 @@ plugin_io_mgmt_opts () { opts+=" --data= -d --data-len= -l --dry-run --mo= -m --mos= -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data -d --data-len -l --mo -m --mos -s --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; esac @@ -2164,7 +2423,8 @@ plugin_keys_opts () { valopts+=" --identity -i --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2173,7 +2433,8 @@ plugin_keys_opts () { valopts+=" --hostnqn -n --identity -I --keydata -d --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2182,7 +2443,8 @@ plugin_keys_opts () { valopts+=" --keyfile -f --keyring -k --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2191,7 +2453,8 @@ plugin_keys_opts () { valopts+=" --hmac -m --output-format -o --output-format-version --secret -s --secret-length -l --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2200,7 +2463,8 @@ plugin_keys_opts () { valopts+=" --hmac -m --hostnqn -n --identity -I --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --secret -s --set-options --subsysnqn -c --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2209,7 +2473,8 @@ plugin_keys_opts () { valopts+=" --identity -i --keydata -d --keyfile -f --keyring -k --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2218,7 +2483,8 @@ plugin_keys_opts () { valopts+=" --hostnqn -n --identity -I --keydata -d --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --set-options --subsysnqn -c --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2227,7 +2493,8 @@ plugin_keys_opts () { valopts+=" --identity -i --keyring -k --keytype -t --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; esac @@ -2254,7 +2521,8 @@ plugin_lm_opts () { valopts+=" --cntlid -c --output-format -o --output-format-version --queue-type -q --set-options --size -s --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "delete-cdq") @@ -2262,7 +2530,8 @@ plugin_lm_opts () { valopts+=" --cdqid -C --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-cdq") @@ -2270,7 +2539,8 @@ plugin_lm_opts () { valopts+=" --cdqid -C --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "migration-recv") @@ -2279,7 +2549,8 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -2289,7 +2560,8 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -f" ;; @@ -2298,7 +2570,8 @@ plugin_lm_opts () { valopts+=" --cdqid -C --hp -H --output-format -o --output-format-version --set-options --timeout --tpt -T" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "track-send") @@ -2307,7 +2580,8 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2333,7 +2607,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ave-discovery") @@ -2341,7 +2616,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "boot-part") @@ -2349,7 +2625,8 @@ plugin_log_opts () { valopts+=" --lsp -s --output-file -f --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -2358,7 +2635,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-ns-list") @@ -2366,15 +2644,18 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dispersed-ns-participating-nss") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "effects") @@ -2382,7 +2663,8 @@ plugin_log_opts () { valopts+=" --csi -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance") @@ -2390,7 +2672,8 @@ plugin_log_opts () { valopts+=" --group-id -g --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-event-agg") @@ -2398,15 +2681,18 @@ plugin_log_opts () { valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "error") opts+=" --csi= -c --dry-run --lba= -l --log-entries= -e --namespace-id= -n --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --sqid= -S --status= -s --timeout= --trtype= -t --valid-entry -V --verbose -v" valopts+=" --csi -c --lba -l --log-entries -e --namespace-id -n --opcode -O --output-format -o --output-format-version --set-options --sqid -S --status -s --timeout --trtype -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fid-support-effects") @@ -2414,7 +2700,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw") @@ -2422,7 +2709,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "host-discovery") @@ -2430,7 +2718,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lba-status") @@ -2438,7 +2727,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "media-unit-stat") @@ -2446,7 +2736,8 @@ plugin_log_opts () { valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mgmt-addr-list") @@ -2454,7 +2745,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mi-cmd-support-effects") @@ -2462,7 +2754,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persistent-event") @@ -2470,7 +2763,8 @@ plugin_log_opts () { valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "phy-rx-eom") @@ -2478,7 +2772,8 @@ plugin_log_opts () { valopts+=" --controller -c --lsp -s --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "power-measurement") @@ -2486,7 +2781,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pred-lat-event-agg") @@ -2494,7 +2790,8 @@ plugin_log_opts () { valopts+=" --log-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "predictable-lat") @@ -2502,7 +2799,8 @@ plugin_log_opts () { valopts+=" --nvmset-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pull-model-ddc-req") @@ -2510,7 +2808,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-associations") @@ -2518,7 +2817,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-groups") @@ -2526,7 +2826,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-notif") @@ -2534,7 +2835,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "rotational-media-info") @@ -2542,7 +2844,8 @@ plugin_log_opts () { valopts+=" --endg-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize") @@ -2550,7 +2853,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "self-test") @@ -2558,15 +2862,18 @@ plugin_log_opts () { valopts+=" --dst-entries -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-cap-config") @@ -2574,7 +2881,8 @@ plugin_log_opts () { valopts+=" --domain-id -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-pages") @@ -2582,7 +2890,8 @@ plugin_log_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry") @@ -2590,7 +2899,8 @@ plugin_log_opts () { valopts+=" --data-area -d --host-generate -g --mcda -m --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; esac @@ -2617,7 +2927,8 @@ plugin_mangoboost_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2643,7 +2954,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-pm-status") @@ -2651,7 +2963,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-get-feature-x") @@ -2659,7 +2972,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log") @@ -2667,7 +2981,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --param -p --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log-print") @@ -2675,7 +2990,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log-print-x") @@ -2683,7 +2999,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-set-feature-x") @@ -2691,7 +3008,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --sel-perf-log -s --set-commands-mask -m --set-options --set-read-threshold -r --set-trim-threshold -t --set-write-threshold -w --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats") @@ -2699,7 +3017,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats-print") @@ -2707,7 +3026,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats-print-x") @@ -2715,7 +3035,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "perf-stats-print-x") @@ -2723,7 +3044,8 @@ plugin_memblaze_opts () { valopts+=" --duration -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "select-download") @@ -2731,7 +3053,8 @@ plugin_memblaze_opts () { valopts+=" --fw -f --output-format -o --output-format-version --select -s --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -2740,15 +3063,18 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout --value -V" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-log-add") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-log-add-x") @@ -2756,7 +3082,8 @@ plugin_memblaze_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2782,7 +3109,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -2790,7 +3118,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") @@ -2798,7 +3127,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2807,7 +3137,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -2815,7 +3146,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-logs") @@ -2823,7 +3155,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-stats") @@ -2831,7 +3164,8 @@ plugin_micron_opts () { valopts+=" --command -c --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-tracking") @@ -2839,7 +3173,8 @@ plugin_micron_opts () { valopts+=" --command -c --option -O --output-format -o --output-format-version --set-options --threshold -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") @@ -2847,7 +3182,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "plugin-version") @@ -2855,7 +3191,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -2864,7 +3201,8 @@ plugin_micron_opts () { valopts+=" --fw -f --output-format -o --output-format-version --select -s --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -2873,7 +3211,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-cloud-log") @@ -2881,7 +3220,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-device-waf") @@ -2889,7 +3229,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") @@ -2897,7 +3238,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-fw-activate-history") @@ -2905,7 +3247,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") @@ -2913,7 +3256,8 @@ plugin_micron_opts () { valopts+=" --data_area -d --output-format -o --output-format-version --package -p --set-options --timeout --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--package -p" ;; @@ -2922,7 +3266,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") @@ -2930,7 +3275,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") @@ -2938,7 +3284,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-ext-log") @@ -2946,7 +3293,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smbus-option") @@ -2954,7 +3302,8 @@ plugin_micron_opts () { valopts+=" --option -O --output-format -o --output-format-version --save -s --set-options --timeout --value -V" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-telemetry-controller-option") @@ -2962,7 +3311,8 @@ plugin_micron_opts () { valopts+=" --option -O --output-format -o --output-format-version --select -s --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") @@ -2970,7 +3320,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-vendor-telemetry-log") @@ -2978,7 +3329,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-work-load-log") @@ -2986,7 +3338,8 @@ plugin_micron_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3012,7 +3365,8 @@ plugin_nbft_opts () { valopts+=" --nbft-path --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; esac @@ -3039,7 +3393,8 @@ plugin_netapp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal column json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smdevices") @@ -3047,7 +3402,8 @@ plugin_netapp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal column json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3072,8 +3428,10 @@ plugin_ns_opts () { opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "create") @@ -3081,23 +3439,28 @@ plugin_ns_opts () { valopts+=" --anagrp-id -a --block-size -b --csi -y --dps -d --endg-id -e --flbas -f --lbstm -l --ncap -c --ncap-si -C --nmic -m --nphndls -n --nsze -s --nsze-si -S --nvmset-id -i --output-format -o --output-format-version --phndls -p --rar -r --rnumzrwa -u --ror -O --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "delete") opts+=" --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "detach") opts+=" --controllers= -c --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --controllers -c --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-id") @@ -3105,7 +3468,8 @@ plugin_ns_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3131,7 +3495,8 @@ plugin_nvidia_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3156,8 +3521,10 @@ plugin_nvme_mi_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; @@ -3165,8 +3532,10 @@ plugin_nvme_mi_opts () { opts+=" --data-len= -l --dry-run --input-file= -i --namespace-id= -n --nmd0= -0 --nmd1= -1 --nmimt= -m --no-ioctl-probing --no-retries --opcode= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --data-len -l --input-file -i --namespace-id -n --nmd0 -0 --nmd1 -1 --nmimt -m --opcode -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--input-file -i" ;; esac @@ -3193,7 +3562,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -3201,7 +3571,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "device-capability-log") @@ -3209,7 +3580,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "eol-plp-failure-mode") @@ -3218,7 +3590,8 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "error-recovery-log") @@ -3226,7 +3599,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw-activate-history") @@ -3234,16 +3608,19 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-clear-pcie-correctable-errors") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-dssd-async-event-config") @@ -3252,7 +3629,8 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-dssd-power-state-feature") @@ -3261,7 +3639,8 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-enable-ieee1667-silo") @@ -3270,7 +3649,8 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-error-injection") @@ -3279,25 +3659,30 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-idle-wakeup-time") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-latency-monitor") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-plp-health-check-interval") @@ -3306,16 +3691,19 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" + "--sel -S" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "get-telemetry-profile") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --no-uuid -u --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "hardware-component-log") @@ -3324,7 +3712,8 @@ plugin_ocp_opts () { _nvme_opt_vals "--comp-id -i" "asic nand dram pmic pcb cap reg case sn country hw-rev born-on-date vendor" \ "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "internal-log") @@ -3332,7 +3721,8 @@ plugin_ocp_opts () { valopts+=" --data-area -a --host-generate -g --output-file -f --output-format -o --output-format-version --set-options --string-log -s --telemetry-log -l --telemetry-type -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -3341,7 +3731,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persistent-event-log") @@ -3349,7 +3740,8 @@ plugin_ocp_opts () { valopts+=" --action -a --log_len -l --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-dssd-async-event-config") @@ -3357,7 +3749,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-dssd-power-state-feature") @@ -3365,7 +3758,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --power-state -p --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-enable-ieee1667-silo") @@ -3373,7 +3767,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-error-injection") @@ -3381,7 +3776,8 @@ plugin_ocp_opts () { valopts+=" --data -d --nrtdp -r --number -n --output-format -o --output-format-version --set-options --timeout --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -3390,7 +3786,8 @@ plugin_ocp_opts () { valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-plp-health-check-interval") @@ -3398,7 +3795,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --plp_health_interval -p --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-telemetry-profile") @@ -3406,7 +3804,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --telemetry-profile-select -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-add-log") @@ -3414,7 +3813,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "tcg-configuration-log") @@ -3422,7 +3822,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry-string-log") @@ -3430,7 +3831,8 @@ plugin_ocp_opts () { valopts+=" --output-file -f --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -3439,7 +3841,8 @@ plugin_ocp_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3465,7 +3868,8 @@ plugin_registry_opts () { valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") @@ -3473,7 +3877,8 @@ plugin_registry_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -3482,7 +3887,8 @@ plugin_registry_opts () { valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "update") @@ -3490,7 +3896,8 @@ plugin_registry_opts () { valopts+=" --attr -a --output-format -o --output-format-version --set-options --timeout --value -V" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3515,32 +3922,40 @@ plugin_resv_opts () { opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --prkey= -p --quiet --racqa= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --prkey -p --racqa -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "register") opts+=" --cptpl= -p --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --nrkey= -k --output-format= -o --output-format-version= --quiet --rrega= -r --set-options= --timeout= --verbose -v" valopts+=" --cptpl -p --crkey -c --namespace-id -n --nrkey -k --output-format -o --output-format-version --rrega -r --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "release") opts+=" --crkey= -c --dry-run --iekey -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rrela= -a --rtype= -t --set-options= --timeout= --verbose -v" valopts+=" --crkey -c --namespace-id -n --output-format -o --output-format-version --rrela -a --rtype -t --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "report") opts+=" --dry-run --eds -e --namespace-id= -n --no-ioctl-probing --no-retries --numd= -d --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --numd -d --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3566,7 +3981,8 @@ plugin_rpmb_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "program-key") @@ -3574,7 +3990,8 @@ plugin_rpmb_opts () { valopts+=" --key -k --keyfile -g --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" ;; @@ -3583,7 +4000,8 @@ plugin_rpmb_opts () { valopts+=" --msgfile -f --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--msgfile -f" ;; @@ -3592,7 +4010,8 @@ plugin_rpmb_opts () { valopts+=" --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "read-data") @@ -3600,7 +4019,8 @@ plugin_rpmb_opts () { valopts+=" --address -o --blocks -b --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--msgfile -f" ;; @@ -3609,7 +4029,8 @@ plugin_rpmb_opts () { valopts+=" --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; @@ -3618,7 +4039,8 @@ plugin_rpmb_opts () { valopts+=" --address -o --blocks -b --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; esac @@ -3645,7 +4067,8 @@ plugin_samsung_opts () { valopts+=" --dump-type -t --output-file -O --output-format -o --output-format-version --set-options --telemetry-data-area -a --timeout" _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; esac @@ -3672,7 +4095,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -3680,7 +4104,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") @@ -3688,7 +4113,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -3696,16 +4122,20 @@ plugin_seagate_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-host-tele") opts+=" --dry-run --log_specific= -i --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --log_specific -i --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "plugin-version") @@ -3713,7 +4143,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -3722,15 +4153,18 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") opts+=" --dry-run --dump-file= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --dump-file -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--dump-file -f" ;; @@ -3739,7 +4173,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") @@ -3747,7 +4182,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") @@ -3755,7 +4191,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") @@ -3763,7 +4200,8 @@ plugin_seagate_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3788,16 +4226,20 @@ plugin_security_opts () { opts+=" --al= -t --dry-run --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --raw-binary -b --secp= -p --set-options= --size= -x --spsp= -s --timeout= --verbose -v" valopts+=" --al -t --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --size -x --spsp -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "send") opts+=" --dry-run --file= -f --ish -I --namespace-id= -n --no-ioctl-probing --no-retries --nssf= -N --output-format= -o --output-format-version= --quiet --secp= -p --set-options= --spsp= -s --timeout= --tl= -t --verbose -v" valopts+=" --file -f --namespace-id -n --nssf -N --output-format -o --output-format-version --secp -p --set-options --spsp -s --timeout --tl -t" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -f" ;; esac @@ -3824,7 +4266,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "initialize") @@ -3832,7 +4275,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lock") @@ -3840,7 +4284,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "password") @@ -3848,7 +4293,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "revert") @@ -3856,7 +4302,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "unlock") @@ -3864,7 +4311,8 @@ plugin_sed_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3890,7 +4338,8 @@ plugin_sfx_opts () { valopts+=" --cap -c --cap-byte -z --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dump-evtlog") @@ -3898,7 +4347,8 @@ plugin_sfx_opts () { valopts+=" --file -f --namespace_id -n --output -O --output-format -o --output-format-version --set-options --storage_medium -s --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -f" "--output -O" ;; @@ -3907,7 +4357,8 @@ plugin_sfx_opts () { valopts+=" --lbaf -l --namespace_cap -c --namespace_id -n --namespace_size -s --output-format -o --output-format-version --set-options --timeout --units -u" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-bad-block") @@ -3915,15 +4366,18 @@ plugin_sfx_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-feature") opts+=" --dry-run --feature-id= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats") @@ -3931,7 +4385,8 @@ plugin_sfx_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "query-cap") @@ -3939,23 +4394,28 @@ plugin_sfx_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-feature") opts+=" --dry-run --feature-id= -f --force -s --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --value= -V --verbose -v" valopts+=" --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-log-add") opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "status") @@ -3963,7 +4423,8 @@ plugin_sfx_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3988,9 +4449,11 @@ plugin_shannon_opts () { opts+=" --cdw11= -c --data-len= -l --dry-run --feature-id= -f --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --cdw11 -c --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" + "--sel -s" "0 1 2 3" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -3998,15 +4461,18 @@ plugin_shannon_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-additioal-feature") opts+=" --data= -d --data-len= -l --dry-run --feature-id= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --set-options= --timeout= --value= -V --verbose -v" valopts+=" --data -d --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -4014,8 +4480,10 @@ plugin_shannon_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4041,7 +4509,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-assert-dump") @@ -4049,7 +4518,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-fw-activate-history") @@ -4057,7 +4527,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -4065,7 +4536,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") @@ -4073,15 +4545,18 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-boot-SSD-version") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cu-smart-log") @@ -4089,7 +4564,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout --uuid-index -u" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "drive-resize") @@ -4097,7 +4573,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --size -s --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-dev-capabilities-log") @@ -4105,7 +4582,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-drive-status") @@ -4113,7 +4591,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-error-recovery-log") @@ -4121,7 +4600,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-latency-monitor-log") @@ -4129,7 +4609,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-unsupported-reqs-log") @@ -4137,7 +4618,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") @@ -4145,15 +4627,18 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "namespace-resize") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --op-option= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --op-option -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-latency-monitor-feature") @@ -4161,23 +4646,28 @@ plugin_sndk_opts () { valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-cloud-log") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-device-waf") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") @@ -4185,7 +4675,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-error-reason-identifier") @@ -4193,7 +4684,8 @@ plugin_sndk_opts () { valopts+=" --file -O --log-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -O" ;; @@ -4202,15 +4694,18 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-hw-rev-log") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") @@ -4218,7 +4713,8 @@ plugin_sndk_opts () { valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" "--type -t" ;; @@ -4227,7 +4723,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") @@ -4235,15 +4732,18 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") opts+=" --dry-run --interval= -i --log-page-mask= -p --log-page-version= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --interval -i --log-page-mask -p --log-page-version -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-telemetry-controller-option") @@ -4251,7 +4751,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") @@ -4259,7 +4760,8 @@ plugin_sndk_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4285,7 +4787,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -4293,7 +4796,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSDplugin-version") @@ -4301,7 +4805,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -4310,7 +4815,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -4318,7 +4824,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-tracking-log") @@ -4326,7 +4833,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") @@ -4334,7 +4842,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "market-log") @@ -4342,7 +4851,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "parse-telemetry-log") @@ -4350,7 +4860,8 @@ plugin_solidigm_opts () { valopts+=" --config-file -j --data-area -d --host-generate -g --output-format -o --output-format-version --set-options --source-file -s --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config-file -j" "--source-file -s" ;; @@ -4358,8 +4869,10 @@ plugin_solidigm_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "temp-stats") @@ -4367,7 +4880,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") @@ -4375,7 +4889,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-fw-activate-history") @@ -4383,7 +4898,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") @@ -4391,7 +4907,8 @@ plugin_solidigm_opts () { valopts+=" --dir-name -d --output-format -o --output-format-version --set-options --timeout --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--dir-name -d" ;; @@ -4400,7 +4917,8 @@ plugin_solidigm_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "workload-tracker") @@ -4408,7 +4926,8 @@ plugin_solidigm_opts () { valopts+=" --flush-freq -f --output-format -o --output-format-version --run-time -r --sample-time -s --set-options --timeout --trigger-field -T --trigger-threshold -V --type -t --uuid-index -U" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4433,8 +4952,10 @@ plugin_ssstc_opts () { opts+=" --dry-run --json -j --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4460,7 +4981,8 @@ plugin_toshiba_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") @@ -4468,7 +4990,8 @@ plugin_toshiba_opts () { valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4476,8 +4999,10 @@ plugin_toshiba_opts () { opts+=" --dry-run --log= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --log -l --namespace-id -n --output-file -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; esac @@ -4504,7 +5029,8 @@ plugin_transcend_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "healthvalue") @@ -4512,7 +5038,8 @@ plugin_transcend_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4560,7 +5087,8 @@ plugin_virtium_opts () { valopts+=" --freq -f --output-file -O --output-format -o --output-format-version --run-time -r --set-options --test-name -n --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4569,7 +5097,8 @@ plugin_virtium_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4595,7 +5124,8 @@ plugin_wdc_opts () { valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4604,7 +5134,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-assert-dump") @@ -4612,7 +5143,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-fw-activate-history") @@ -4620,7 +5152,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") @@ -4628,7 +5161,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") @@ -4636,15 +5170,18 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-boot-SSD-version") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cu-smart-log") @@ -4652,7 +5189,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout --uuid-index -u" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "drive-essentials") @@ -4660,7 +5198,8 @@ plugin_wdc_opts () { valopts+=" --dir-name -d --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--dir-name -d" ;; @@ -4669,7 +5208,8 @@ plugin_wdc_opts () { valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4678,7 +5218,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --size -s --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "enc-get-log") @@ -4686,7 +5227,8 @@ plugin_wdc_opts () { valopts+=" --log-id -l --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4695,7 +5237,8 @@ plugin_wdc_opts () { valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4704,7 +5247,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-drive-status") @@ -4712,7 +5256,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-error-recovery-log") @@ -4720,7 +5265,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-latency-monitor-log") @@ -4728,7 +5274,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-pfail-dump") @@ -4736,7 +5283,8 @@ plugin_wdc_opts () { valopts+=" --output-file -O --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4745,7 +5293,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -4753,7 +5302,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") @@ -4761,15 +5311,18 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "namespace-resize") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --op-option= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --op-option -O --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "purge") @@ -4777,7 +5330,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "purge-monitor") @@ -4785,7 +5339,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-latency-monitor-feature") @@ -4793,23 +5348,28 @@ plugin_wdc_opts () { valopts+=" --active_bucket_timer_threshold -t --active_latency_config -f --active_latency_minimum_window -w --active_threshold_a -a --active_threshold_b -b --active_threshold_c -c --active_threshold_d -d --debug_log_trigger_enable -r --discard_debug_log -l --latency_monitor_feature_enable -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-cloud-log") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-device-waf") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") @@ -4817,7 +5377,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-error-reason-identifier") @@ -4825,7 +5386,8 @@ plugin_wdc_opts () { valopts+=" --file -O --log-id -i --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -O" ;; @@ -4834,15 +5396,18 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-hw-rev-log") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") @@ -4850,7 +5415,8 @@ plugin_wdc_opts () { valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" "--type -t" ;; @@ -4859,7 +5425,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") @@ -4867,15 +5434,18 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") opts+=" --dry-run --interval= -i --log-page-mask= -p --log-page-version= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --interval -i --log-page-mask -p --log-page-version -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-telemetry-controller-option") @@ -4883,7 +5453,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") @@ -4891,7 +5462,8 @@ plugin_wdc_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4916,8 +5488,10 @@ plugin_ymtc_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4942,24 +5516,30 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --rae -r --set-options= --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "close-zone") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "finish-zone") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") @@ -4967,15 +5547,18 @@ plugin_zns_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ns") opts+=" --dry-run --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") @@ -4983,7 +5566,8 @@ plugin_zns_opts () { valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal" \ - "--output-format-version" "1 2" + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; @@ -4991,40 +5575,50 @@ plugin_zns_opts () { opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "open-zone") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v --zrwaa -r" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "report-zones") opts+=" --descs= -d --dry-run --extended -e --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --state= -S --timeout= --verbose -V --verbose -v" valopts+=" --descs -d --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --state -S --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reset-zone") opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-zone-desc") opts+=" --data= -d --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --start-lba= -s --timeout= --verbose -v --zrwaa -r" valopts+=" --data -d --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -5032,8 +5626,10 @@ plugin_zns_opts () { opts+=" --data= -d --data-size= -z --dry-run --force-unit-access -f --latency -t --limited-retry -l --metadata= -M --metadata-size= -y --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --piremap -P --prinfo= -p --quiet --set-options= --timeout= --verbose -v --zslba= -s" valopts+=" --data -d --data-size -z --metadata -M --metadata-size -y --namespace-id -n --output-format -o --output-format-version --prinfo -p --set-options --timeout --zslba -s" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" "--metadata -M" ;; @@ -5041,16 +5637,20 @@ plugin_zns_opts () { opts+=" --data-len= -l --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --timeout= --verbose -v --zra= -z --zrasf= -S" valopts+=" --data-len -l --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout --zra -z --zrasf -S" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "zone-mgmt-send") opts+=" --data= -d --data-len= -l --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --select-all -a --set-options= --start-lba= -s --timeout= --verbose -v --zsa= -z --zsaso -O" valopts+=" --data -d --data-len -l --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --timeout --zsa -z" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -5058,8 +5658,10 @@ plugin_zns_opts () { opts+=" --dry-run --lba= -l --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --lba -l --namespace-id -n --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 9c2f208ac0..655cf9b251 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -945,1560 +945,2283 @@ $script:NvmeFileOptions = @{ } $script:NvmeOptionValues = @{ + 'admin-passthru --namespace-id' = @('all', '-1') 'admin-passthru --output-format' = @('normal', 'json', 'binary') 'admin-passthru --output-format-version' = @('1', '2') + 'admin-passthru --timeout' = @('infinite', '-1') + 'admin-passthru -n' = @('all', '-1') 'admin-passthru -o' = @('normal', 'json', 'binary') 'amzn id-ctrl --output-format' = @('normal', 'json', 'binary') 'amzn id-ctrl --output-format-version' = @('1', '2') + 'amzn id-ctrl --timeout' = @('infinite', '-1') 'amzn id-ctrl -o' = @('normal', 'json', 'binary') 'amzn stats --output-format' = @('normal', 'json', 'binary') 'amzn stats --output-format-version' = @('1', '2') + 'amzn stats --timeout' = @('infinite', '-1') 'amzn stats -o' = @('normal', 'json', 'binary') 'ana-log --output-format' = @('normal', 'json', 'binary') 'ana-log --output-format-version' = @('1', '2') + 'ana-log --timeout' = @('infinite', '-1') 'ana-log -o' = @('normal', 'json', 'binary') + 'attach-ns --namespace-id' = @('all', '-1') 'attach-ns --output-format' = @('normal', 'json', 'binary') 'attach-ns --output-format-version' = @('1', '2') + 'attach-ns --timeout' = @('infinite', '-1') + 'attach-ns -n' = @('all', '-1') 'attach-ns -o' = @('normal', 'json', 'binary') 'ave-discovery-log --output-format' = @('normal', 'json', 'binary') 'ave-discovery-log --output-format-version' = @('1', '2') + 'ave-discovery-log --timeout' = @('infinite', '-1') 'ave-discovery-log -o' = @('normal', 'json', 'binary') 'boot-part-log --output-format' = @('normal', 'json', 'binary') 'boot-part-log --output-format-version' = @('1', '2') + 'boot-part-log --timeout' = @('infinite', '-1') 'boot-part-log -o' = @('normal', 'json', 'binary') 'capacity-mgmt --output-format' = @('normal', 'json', 'binary') 'capacity-mgmt --output-format-version' = @('1', '2') + 'capacity-mgmt --timeout' = @('infinite', '-1') 'capacity-mgmt -o' = @('normal', 'json', 'binary') 'changed-alloc-ns-list-log --output-format' = @('normal', 'json', 'binary') 'changed-alloc-ns-list-log --output-format-version' = @('1', '2') + 'changed-alloc-ns-list-log --timeout' = @('infinite', '-1') 'changed-alloc-ns-list-log -o' = @('normal', 'json', 'binary') 'changed-ns-list-log --output-format' = @('normal', 'json', 'binary') 'changed-ns-list-log --output-format-version' = @('1', '2') + 'changed-ns-list-log --timeout' = @('infinite', '-1') 'changed-ns-list-log -o' = @('normal', 'json', 'binary') 'check-dhchap-key --output-format' = @('normal', 'json', 'binary') 'check-dhchap-key --output-format-version' = @('1', '2') + 'check-dhchap-key --timeout' = @('infinite', '-1') 'check-dhchap-key -o' = @('normal', 'json', 'binary') 'check-tls-key --output-format' = @('normal', 'json', 'binary') 'check-tls-key --output-format-version' = @('1', '2') + 'check-tls-key --timeout' = @('infinite', '-1') 'check-tls-key -o' = @('normal', 'json', 'binary') + 'cmdset-ind-id-ns --namespace-id' = @('all', '-1') 'cmdset-ind-id-ns --output-format' = @('normal', 'json', 'binary') 'cmdset-ind-id-ns --output-format-version' = @('1', '2') + 'cmdset-ind-id-ns --timeout' = @('infinite', '-1') + 'cmdset-ind-id-ns -n' = @('all', '-1') 'cmdset-ind-id-ns -o' = @('normal', 'json', 'binary') + 'compare --namespace-id' = @('all', '-1') 'compare --output-format' = @('normal', 'json', 'binary') 'compare --output-format-version' = @('1', '2') + 'compare --timeout' = @('infinite', '-1') + 'compare -n' = @('all', '-1') 'compare -o' = @('normal', 'json', 'binary') 'config convert --output-format' = @('normal', 'json', 'binary') 'config convert --output-format-version' = @('1', '2') + 'config convert --timeout' = @('infinite', '-1') 'config convert -o' = @('normal', 'json', 'binary') 'config create --output-format' = @('normal', 'json', 'binary') 'config create --output-format-version' = @('1', '2') + 'config create --timeout' = @('infinite', '-1') 'config create -o' = @('normal', 'json', 'binary') 'config show --output-format' = @('normal', 'json', 'binary') 'config show --output-format-version' = @('1', '2') + 'config show --timeout' = @('infinite', '-1') 'config show -o' = @('normal', 'json', 'binary') 'config status --output-format' = @('normal', 'json', 'binary') 'config status --output-format-version' = @('1', '2') + 'config status --timeout' = @('infinite', '-1') 'config status -o' = @('normal', 'json', 'binary') 'config validate --output-format' = @('normal', 'json', 'binary') 'config validate --output-format-version' = @('1', '2') + 'config validate --timeout' = @('infinite', '-1') 'config validate -o' = @('normal', 'json', 'binary') 'connect --output-format' = @('normal', 'json', 'binary') 'connect --output-format-version' = @('1', '2') + 'connect --timeout' = @('infinite', '-1') 'connect -o' = @('normal', 'json', 'binary') 'connect-all --output-format' = @('normal', 'json', 'binary') 'connect-all --output-format-version' = @('1', '2') + 'connect-all --timeout' = @('infinite', '-1') 'connect-all -o' = @('normal', 'json', 'binary') + 'copy --namespace-id' = @('all', '-1') 'copy --output-format' = @('normal', 'json', 'binary') 'copy --output-format-version' = @('1', '2') + 'copy --timeout' = @('infinite', '-1') + 'copy -n' = @('all', '-1') 'copy -o' = @('normal', 'json', 'binary') 'create-ns --output-format' = @('normal', 'json', 'binary') 'create-ns --output-format-version' = @('1', '2') + 'create-ns --timeout' = @('infinite', '-1') 'create-ns -o' = @('normal', 'json', 'binary') + 'dapustor smart-log-add --namespace-id' = @('all', '-1') 'dapustor smart-log-add --output-format' = @('normal', 'json', 'binary') 'dapustor smart-log-add --output-format-version' = @('1', '2') + 'dapustor smart-log-add --timeout' = @('infinite', '-1') + 'dapustor smart-log-add -n' = @('all', '-1') 'dapustor smart-log-add -o' = @('normal', 'json', 'binary') + 'delete-ns --namespace-id' = @('all', '-1') 'delete-ns --output-format' = @('normal', 'json', 'binary') 'delete-ns --output-format-version' = @('1', '2') + 'delete-ns --timeout' = @('infinite', '-1') + 'delete-ns -n' = @('all', '-1') 'delete-ns -o' = @('normal', 'json', 'binary') 'dell id-ctrl --output-format' = @('normal', 'json', 'binary') 'dell id-ctrl --output-format-version' = @('1', '2') + 'dell id-ctrl --timeout' = @('infinite', '-1') 'dell id-ctrl -o' = @('normal', 'json', 'binary') 'dera smart-log-add --output-format' = @('normal', 'json', 'binary') 'dera smart-log-add --output-format-version' = @('1', '2') + 'dera smart-log-add --timeout' = @('infinite', '-1') 'dera smart-log-add -o' = @('normal', 'json', 'binary') 'dera stat --output-format' = @('normal', 'json', 'binary') 'dera stat --output-format-version' = @('1', '2') + 'dera stat --timeout' = @('infinite', '-1') 'dera stat -o' = @('normal', 'json', 'binary') + 'detach-ns --namespace-id' = @('all', '-1') 'detach-ns --output-format' = @('normal', 'json', 'binary') 'detach-ns --output-format-version' = @('1', '2') + 'detach-ns --timeout' = @('infinite', '-1') + 'detach-ns -n' = @('all', '-1') 'detach-ns -o' = @('normal', 'json', 'binary') + 'device-self-test --namespace-id' = @('all', '-1') 'device-self-test --output-format' = @('normal', 'json', 'binary') 'device-self-test --output-format-version' = @('1', '2') + 'device-self-test --timeout' = @('infinite', '-1') + 'device-self-test -n' = @('all', '-1') 'device-self-test -o' = @('normal', 'json', 'binary') 'dim --output-format' = @('normal', 'json', 'binary') 'dim --output-format-version' = @('1', '2') + 'dim --timeout' = @('infinite', '-1') 'dim -o' = @('normal', 'json', 'binary') + 'dir receive --namespace-id' = @('all', '-1') 'dir receive --output-format' = @('normal', 'json', 'binary') 'dir receive --output-format-version' = @('1', '2') + 'dir receive --timeout' = @('infinite', '-1') + 'dir receive -n' = @('all', '-1') 'dir receive -o' = @('normal', 'json', 'binary') + 'dir send --namespace-id' = @('all', '-1') 'dir send --output-format' = @('normal', 'json', 'binary') 'dir send --output-format-version' = @('1', '2') + 'dir send --timeout' = @('infinite', '-1') + 'dir send -n' = @('all', '-1') 'dir send -o' = @('normal', 'json', 'binary') + 'dir-receive --namespace-id' = @('all', '-1') 'dir-receive --output-format' = @('normal', 'json', 'binary') 'dir-receive --output-format-version' = @('1', '2') + 'dir-receive --timeout' = @('infinite', '-1') + 'dir-receive -n' = @('all', '-1') 'dir-receive -o' = @('normal', 'json', 'binary') + 'dir-send --namespace-id' = @('all', '-1') 'dir-send --output-format' = @('normal', 'json', 'binary') 'dir-send --output-format-version' = @('1', '2') + 'dir-send --timeout' = @('infinite', '-1') + 'dir-send -n' = @('all', '-1') 'dir-send -o' = @('normal', 'json', 'binary') 'disconnect --output-format' = @('normal', 'json', 'binary') 'disconnect --output-format-version' = @('1', '2') + 'disconnect --timeout' = @('infinite', '-1') 'disconnect -o' = @('normal', 'json', 'binary') 'disconnect-all --output-format' = @('normal', 'json', 'binary') 'disconnect-all --output-format-version' = @('1', '2') + 'disconnect-all --timeout' = @('infinite', '-1') 'disconnect-all -o' = @('normal', 'json', 'binary') 'discover --output-format' = @('normal', 'json', 'binary') 'discover --output-format-version' = @('1', '2') + 'discover --timeout' = @('infinite', '-1') 'discover -o' = @('normal', 'json', 'binary') + 'dispersed-ns-participating-nss-log --namespace-id' = @('all', '-1') 'dispersed-ns-participating-nss-log --output-format' = @('normal', 'json', 'binary') 'dispersed-ns-participating-nss-log --output-format-version' = @('1', '2') + 'dispersed-ns-participating-nss-log --timeout' = @('infinite', '-1') + 'dispersed-ns-participating-nss-log -n' = @('all', '-1') 'dispersed-ns-participating-nss-log -o' = @('normal', 'json', 'binary') + 'dsm --namespace-id' = @('all', '-1') 'dsm --output-format' = @('normal', 'json', 'binary') 'dsm --output-format-version' = @('1', '2') + 'dsm --timeout' = @('infinite', '-1') + 'dsm -n' = @('all', '-1') 'dsm -o' = @('normal', 'json', 'binary') 'effects-log --output-format' = @('normal', 'json', 'binary') 'effects-log --output-format-version' = @('1', '2') + 'effects-log --timeout' = @('infinite', '-1') 'effects-log -o' = @('normal', 'json', 'binary') 'endurance-event-agg-log --output-format' = @('normal', 'json', 'binary') 'endurance-event-agg-log --output-format-version' = @('1', '2') + 'endurance-event-agg-log --timeout' = @('infinite', '-1') 'endurance-event-agg-log -o' = @('normal', 'json', 'binary') 'endurance-log --output-format' = @('normal', 'json', 'binary') 'endurance-log --output-format-version' = @('1', '2') + 'endurance-log --timeout' = @('infinite', '-1') 'endurance-log -o' = @('normal', 'json', 'binary') + 'error-log --namespace-id' = @('all', '-1') 'error-log --output-format' = @('normal', 'json', 'binary') 'error-log --output-format-version' = @('1', '2') + 'error-log --timeout' = @('infinite', '-1') + 'error-log -n' = @('all', '-1') 'error-log -o' = @('normal', 'json', 'binary') 'exclusion add --output-format' = @('normal', 'json', 'binary') 'exclusion add --output-format-version' = @('1', '2') + 'exclusion add --timeout' = @('infinite', '-1') 'exclusion add -o' = @('normal', 'json', 'binary') 'exclusion create --output-format' = @('normal', 'json', 'binary') 'exclusion create --output-format-version' = @('1', '2') + 'exclusion create --timeout' = @('infinite', '-1') 'exclusion create -o' = @('normal', 'json', 'binary') 'exclusion delete --output-format' = @('normal', 'json', 'binary') 'exclusion delete --output-format-version' = @('1', '2') + 'exclusion delete --timeout' = @('infinite', '-1') 'exclusion delete -o' = @('normal', 'json', 'binary') 'exclusion edit --output-format' = @('normal', 'json', 'binary') 'exclusion edit --output-format-version' = @('1', '2') + 'exclusion edit --timeout' = @('infinite', '-1') 'exclusion edit -o' = @('normal', 'json', 'binary') 'exclusion list --output-format' = @('normal', 'json', 'binary') 'exclusion list --output-format-version' = @('1', '2') + 'exclusion list --timeout' = @('infinite', '-1') 'exclusion list -o' = @('normal', 'json', 'binary') 'exclusion remove --output-format' = @('normal', 'json', 'binary') 'exclusion remove --output-format-version' = @('1', '2') + 'exclusion remove --timeout' = @('infinite', '-1') 'exclusion remove -o' = @('normal', 'json', 'binary') 'fdp configs --output-format' = @('normal', 'json', 'binary') 'fdp configs --output-format-version' = @('1', '2') + 'fdp configs --timeout' = @('infinite', '-1') 'fdp configs -o' = @('normal', 'json', 'binary') 'fdp events --output-format' = @('normal', 'json', 'binary') 'fdp events --output-format-version' = @('1', '2') + 'fdp events --timeout' = @('infinite', '-1') 'fdp events -o' = @('normal', 'json', 'binary') 'fdp feature --output-format' = @('normal', 'json', 'binary') 'fdp feature --output-format-version' = @('1', '2') + 'fdp feature --timeout' = @('infinite', '-1') 'fdp feature -o' = @('normal', 'json', 'binary') + 'fdp set-events --namespace-id' = @('all', '-1') 'fdp set-events --output-format' = @('normal', 'json', 'binary') 'fdp set-events --output-format-version' = @('1', '2') + 'fdp set-events --timeout' = @('infinite', '-1') + 'fdp set-events -n' = @('all', '-1') 'fdp set-events -o' = @('normal', 'json', 'binary') 'fdp stats --output-format' = @('normal', 'json', 'binary') 'fdp stats --output-format-version' = @('1', '2') + 'fdp stats --timeout' = @('infinite', '-1') 'fdp stats -o' = @('normal', 'json', 'binary') + 'fdp status --namespace-id' = @('all', '-1') 'fdp status --output-format' = @('normal', 'json', 'binary') 'fdp status --output-format-version' = @('1', '2') + 'fdp status --timeout' = @('infinite', '-1') + 'fdp status -n' = @('all', '-1') 'fdp status -o' = @('normal', 'json', 'binary') + 'fdp update --namespace-id' = @('all', '-1') 'fdp update --output-format' = @('normal', 'json', 'binary') 'fdp update --output-format-version' = @('1', '2') + 'fdp update --timeout' = @('infinite', '-1') + 'fdp update -n' = @('all', '-1') 'fdp update -o' = @('normal', 'json', 'binary') 'fdp usage --output-format' = @('normal', 'json', 'binary') 'fdp usage --output-format-version' = @('1', '2') + 'fdp usage --timeout' = @('infinite', '-1') 'fdp usage -o' = @('normal', 'json', 'binary') 'feat arbitration --output-format' = @('normal', 'json', 'binary') 'feat arbitration --output-format-version' = @('1', '2') 'feat arbitration --sel' = @('0', '1', '2', '3') + 'feat arbitration --timeout' = @('infinite', '-1') 'feat arbitration -S' = @('0', '1', '2', '3') 'feat arbitration -o' = @('normal', 'json', 'binary') 'feat async-event-conf --output-format' = @('normal', 'json', 'binary') 'feat async-event-conf --output-format-version' = @('1', '2') 'feat async-event-conf --sel' = @('0', '1', '2', '3') + 'feat async-event-conf --timeout' = @('infinite', '-1') 'feat async-event-conf -S' = @('0', '1', '2', '3') 'feat async-event-conf -o' = @('normal', 'json', 'binary') + 'feat err-recovery --nsid' = @('all', '-1') 'feat err-recovery --output-format' = @('normal', 'json', 'binary') 'feat err-recovery --output-format-version' = @('1', '2') 'feat err-recovery --sel' = @('0', '1', '2', '3') + 'feat err-recovery --timeout' = @('infinite', '-1') 'feat err-recovery -S' = @('0', '1', '2', '3') + 'feat err-recovery -n' = @('all', '-1') 'feat err-recovery -o' = @('normal', 'json', 'binary') 'feat hctm --output-format' = @('normal', 'json', 'binary') 'feat hctm --output-format-version' = @('1', '2') 'feat hctm --sel' = @('0', '1', '2', '3') + 'feat hctm --timeout' = @('infinite', '-1') 'feat hctm -S' = @('0', '1', '2', '3') 'feat hctm -o' = @('normal', 'json', 'binary') 'feat host-behavior-support --output-format' = @('normal', 'json', 'binary') 'feat host-behavior-support --output-format-version' = @('1', '2') 'feat host-behavior-support --sel' = @('0', '1', '2', '3') + 'feat host-behavior-support --timeout' = @('infinite', '-1') 'feat host-behavior-support -S' = @('0', '1', '2', '3') 'feat host-behavior-support -o' = @('normal', 'json', 'binary') 'feat int-coalesce --output-format' = @('normal', 'json', 'binary') 'feat int-coalesce --output-format-version' = @('1', '2') 'feat int-coalesce --sel' = @('0', '1', '2', '3') + 'feat int-coalesce --timeout' = @('infinite', '-1') 'feat int-coalesce -S' = @('0', '1', '2', '3') 'feat int-coalesce -o' = @('normal', 'json', 'binary') 'feat int-vector-config --output-format' = @('normal', 'json', 'binary') 'feat int-vector-config --output-format-version' = @('1', '2') 'feat int-vector-config --sel' = @('0', '1', '2', '3') + 'feat int-vector-config --timeout' = @('infinite', '-1') 'feat int-vector-config -S' = @('0', '1', '2', '3') 'feat int-vector-config -o' = @('normal', 'json', 'binary') 'feat keep-alive-timer --output-format' = @('normal', 'json', 'binary') 'feat keep-alive-timer --output-format-version' = @('1', '2') 'feat keep-alive-timer --sel' = @('0', '1', '2', '3') + 'feat keep-alive-timer --timeout' = @('infinite', '-1') 'feat keep-alive-timer -S' = @('0', '1', '2', '3') 'feat keep-alive-timer -o' = @('normal', 'json', 'binary') + 'feat lba-range-type --nsid' = @('all', '-1') 'feat lba-range-type --output-format' = @('normal', 'json', 'binary') 'feat lba-range-type --output-format-version' = @('1', '2') 'feat lba-range-type --sel' = @('0', '1', '2', '3') + 'feat lba-range-type --timeout' = @('infinite', '-1') 'feat lba-range-type -S' = @('0', '1', '2', '3') + 'feat lba-range-type -n' = @('all', '-1') 'feat lba-range-type -o' = @('normal', 'json', 'binary') 'feat num-queues --output-format' = @('normal', 'json', 'binary') 'feat num-queues --output-format-version' = @('1', '2') 'feat num-queues --sel' = @('0', '1', '2', '3') + 'feat num-queues --timeout' = @('infinite', '-1') 'feat num-queues -S' = @('0', '1', '2', '3') 'feat num-queues -o' = @('normal', 'json', 'binary') + 'feat perf-characteristics --namespace-id' = @('all', '-1') 'feat perf-characteristics --output-format' = @('normal', 'json', 'binary') 'feat perf-characteristics --output-format-version' = @('1', '2') 'feat perf-characteristics --sel' = @('0', '1', '2', '3') + 'feat perf-characteristics --timeout' = @('infinite', '-1') 'feat perf-characteristics -S' = @('0', '1', '2', '3') + 'feat perf-characteristics -n' = @('all', '-1') 'feat perf-characteristics -o' = @('normal', 'json', 'binary') 'feat power-limit --output-format' = @('normal', 'json', 'binary') 'feat power-limit --output-format-version' = @('1', '2') 'feat power-limit --sel' = @('0', '1', '2', '3') + 'feat power-limit --timeout' = @('infinite', '-1') 'feat power-limit -S' = @('0', '1', '2', '3') 'feat power-limit -o' = @('normal', 'json', 'binary') 'feat power-meas --output-format' = @('normal', 'json', 'binary') 'feat power-meas --output-format-version' = @('1', '2') 'feat power-meas --sel' = @('0', '1', '2', '3') + 'feat power-meas --timeout' = @('infinite', '-1') 'feat power-meas -S' = @('0', '1', '2', '3') 'feat power-meas -o' = @('normal', 'json', 'binary') 'feat power-mgmt --output-format' = @('normal', 'json', 'binary') 'feat power-mgmt --output-format-version' = @('1', '2') 'feat power-mgmt --sel' = @('0', '1', '2', '3') + 'feat power-mgmt --timeout' = @('infinite', '-1') 'feat power-mgmt -S' = @('0', '1', '2', '3') 'feat power-mgmt -o' = @('normal', 'json', 'binary') 'feat power-thresh --output-format' = @('normal', 'json', 'binary') 'feat power-thresh --output-format-version' = @('1', '2') 'feat power-thresh --sel' = @('0', '1', '2', '3') + 'feat power-thresh --timeout' = @('infinite', '-1') 'feat power-thresh -S' = @('0', '1', '2', '3') 'feat power-thresh -o' = @('normal', 'json', 'binary') 'feat temp-thresh --output-format' = @('normal', 'json', 'binary') 'feat temp-thresh --output-format-version' = @('1', '2') 'feat temp-thresh --sel' = @('0', '1', '2', '3') + 'feat temp-thresh --timeout' = @('infinite', '-1') 'feat temp-thresh -S' = @('0', '1', '2', '3') 'feat temp-thresh -o' = @('normal', 'json', 'binary') 'feat timestamp --output-format' = @('normal', 'json', 'binary') 'feat timestamp --output-format-version' = @('1', '2') 'feat timestamp --sel' = @('0', '1', '2', '3') + 'feat timestamp --timeout' = @('infinite', '-1') 'feat timestamp -S' = @('0', '1', '2', '3') 'feat timestamp -o' = @('normal', 'json', 'binary') 'feat volatile-wc --output-format' = @('normal', 'json', 'binary') 'feat volatile-wc --output-format-version' = @('1', '2') 'feat volatile-wc --sel' = @('0', '1', '2', '3') + 'feat volatile-wc --timeout' = @('infinite', '-1') 'feat volatile-wc -S' = @('0', '1', '2', '3') 'feat volatile-wc -o' = @('normal', 'json', 'binary') 'feat write-atom-normal --output-format' = @('normal', 'json', 'binary') 'feat write-atom-normal --output-format-version' = @('1', '2') 'feat write-atom-normal --sel' = @('0', '1', '2', '3') + 'feat write-atom-normal --timeout' = @('infinite', '-1') 'feat write-atom-normal -S' = @('0', '1', '2', '3') 'feat write-atom-normal -o' = @('normal', 'json', 'binary') 'fid-support-effects-log --output-format' = @('normal', 'json', 'binary') 'fid-support-effects-log --output-format-version' = @('1', '2') + 'fid-support-effects-log --timeout' = @('infinite', '-1') 'fid-support-effects-log -o' = @('normal', 'json', 'binary') + 'flush --namespace-id' = @('all', '-1') 'flush --output-format' = @('normal', 'json', 'binary') 'flush --output-format-version' = @('1', '2') + 'flush --timeout' = @('infinite', '-1') + 'flush -n' = @('all', '-1') 'flush -o' = @('normal', 'json', 'binary') + 'format --namespace-id' = @('all', '-1') 'format --output-format' = @('normal', 'json', 'binary') 'format --output-format-version' = @('1', '2') + 'format --timeout' = @('infinite', '-1') + 'format -n' = @('all', '-1') 'format -o' = @('normal', 'json', 'binary') 'fw activate --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw activate --output-format' = @('normal', 'json', 'binary') 'fw activate --output-format-version' = @('1', '2') + 'fw activate --timeout' = @('infinite', '-1') 'fw activate -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw activate -o' = @('normal', 'json', 'binary') 'fw commit --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw commit --output-format' = @('normal', 'json', 'binary') 'fw commit --output-format-version' = @('1', '2') + 'fw commit --timeout' = @('infinite', '-1') 'fw commit -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw commit -o' = @('normal', 'json', 'binary') 'fw download --output-format' = @('normal', 'json', 'binary') 'fw download --output-format-version' = @('1', '2') + 'fw download --timeout' = @('infinite', '-1') 'fw download -o' = @('normal', 'json', 'binary') 'fw-activate --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw-activate --output-format' = @('normal', 'json', 'binary') 'fw-activate --output-format-version' = @('1', '2') + 'fw-activate --timeout' = @('infinite', '-1') 'fw-activate -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw-activate -o' = @('normal', 'json', 'binary') 'fw-commit --action' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw-commit --output-format' = @('normal', 'json', 'binary') 'fw-commit --output-format-version' = @('1', '2') + 'fw-commit --timeout' = @('infinite', '-1') 'fw-commit -a' = @('replace', 'replace-and-activate', 'set-active', 'replace-and-activate-immediate', 'replace-boot-partition', 'activate-boot-partition') 'fw-commit -o' = @('normal', 'json', 'binary') 'fw-download --output-format' = @('normal', 'json', 'binary') 'fw-download --output-format-version' = @('1', '2') + 'fw-download --timeout' = @('infinite', '-1') 'fw-download -o' = @('normal', 'json', 'binary') 'fw-log --output-format' = @('normal', 'json', 'binary') 'fw-log --output-format-version' = @('1', '2') + 'fw-log --timeout' = @('infinite', '-1') 'fw-log -o' = @('normal', 'json', 'binary') 'gen-dhchap-key --output-format' = @('normal', 'json', 'binary') 'gen-dhchap-key --output-format-version' = @('1', '2') + 'gen-dhchap-key --timeout' = @('infinite', '-1') 'gen-dhchap-key -o' = @('normal', 'json', 'binary') 'gen-hostnqn --output-format' = @('normal', 'json', 'binary') 'gen-hostnqn --output-format-version' = @('1', '2') + 'gen-hostnqn --timeout' = @('infinite', '-1') 'gen-hostnqn -o' = @('normal', 'json', 'binary') 'gen-tls-key --output-format' = @('normal', 'json', 'binary') 'gen-tls-key --output-format-version' = @('1', '2') + 'gen-tls-key --timeout' = @('infinite', '-1') 'gen-tls-key -o' = @('normal', 'json', 'binary') 'get-feature --feature-id' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') + 'get-feature --namespace-id' = @('all', '-1') 'get-feature --output-format' = @('normal', 'json', 'binary') 'get-feature --output-format-version' = @('1', '2') 'get-feature --sel' = @('0', '1', '2', '3') + 'get-feature --timeout' = @('infinite', '-1') 'get-feature -f' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') + 'get-feature -n' = @('all', '-1') 'get-feature -o' = @('normal', 'json', 'binary') 'get-feature -s' = @('0', '1', '2', '3') + 'get-lba-status --namespace-id' = @('all', '-1') 'get-lba-status --output-format' = @('normal', 'json', 'binary') 'get-lba-status --output-format-version' = @('1', '2') + 'get-lba-status --timeout' = @('infinite', '-1') + 'get-lba-status -n' = @('all', '-1') 'get-lba-status -o' = @('normal', 'json', 'binary') 'get-log --log-id' = @('supported-log-pages', 'error', 'smart', 'fw-slot', 'changed-attached-ns', 'changed-ns', 'cmd-effects', 'device-self-test', 'telemetry-host', 'telemetry-ctrl', 'endurance-group', 'predictable-lat-nvmset', 'predictable-lat-agg', 'ana', 'persistent-event', 'lba-status', 'endurance-grp-evt', 'media-unit-status', 'supported-cap-config-list', 'fid-supported-effects', 'mi-cmd-supported-effects', 'cmd-and-feat-lockdown', 'boot-partition', 'rotational-media-info', 'dispersed-ns-participating-ns', 'mgmt-addr-list', 'phy-rx-eom', 'reachability-groups', 'reachability-associations', 'changed-alloc-ns-list', 'fdp-configs', 'fdp-ruh-usage', 'fdp-stats', 'fdp-events', 'discover', 'host-discover', 'ave-discover', 'pull-model-ddc-req', 'reservation', 'sanitize', 'zns-changed-zones') + 'get-log --namespace-id' = @('all', '-1') 'get-log --output-format' = @('normal', 'json', 'binary') 'get-log --output-format-version' = @('1', '2') + 'get-log --timeout' = @('infinite', '-1') 'get-log -i' = @('supported-log-pages', 'error', 'smart', 'fw-slot', 'changed-attached-ns', 'changed-ns', 'cmd-effects', 'device-self-test', 'telemetry-host', 'telemetry-ctrl', 'endurance-group', 'predictable-lat-nvmset', 'predictable-lat-agg', 'ana', 'persistent-event', 'lba-status', 'endurance-grp-evt', 'media-unit-status', 'supported-cap-config-list', 'fid-supported-effects', 'mi-cmd-supported-effects', 'cmd-and-feat-lockdown', 'boot-partition', 'rotational-media-info', 'dispersed-ns-participating-ns', 'mgmt-addr-list', 'phy-rx-eom', 'reachability-groups', 'reachability-associations', 'changed-alloc-ns-list', 'fdp-configs', 'fdp-ruh-usage', 'fdp-stats', 'fdp-events', 'discover', 'host-discover', 'ave-discover', 'pull-model-ddc-req', 'reservation', 'sanitize', 'zns-changed-zones') + 'get-log -n' = @('all', '-1') 'get-log -o' = @('normal', 'json', 'binary') 'get-ns-id --output-format' = @('normal', 'json', 'binary') 'get-ns-id --output-format-version' = @('1', '2') + 'get-ns-id --timeout' = @('infinite', '-1') 'get-ns-id -o' = @('normal', 'json', 'binary') 'get-property --output-format' = @('normal', 'json', 'binary') 'get-property --output-format-version' = @('1', '2') + 'get-property --timeout' = @('infinite', '-1') 'get-property -o' = @('normal', 'json', 'binary') 'get-reg --output-format' = @('normal', 'json', 'binary') 'get-reg --output-format-version' = @('1', '2') + 'get-reg --timeout' = @('infinite', '-1') 'get-reg -o' = @('normal', 'json', 'binary') 'host-discovery-log --output-format' = @('normal', 'json', 'binary') 'host-discovery-log --output-format-version' = @('1', '2') + 'host-discovery-log --timeout' = @('infinite', '-1') 'host-discovery-log -o' = @('normal', 'json', 'binary') 'huawei id-ctrl --output-format' = @('normal', 'json', 'binary') 'huawei id-ctrl --output-format-version' = @('1', '2') + 'huawei id-ctrl --timeout' = @('infinite', '-1') 'huawei id-ctrl -o' = @('normal', 'json', 'binary') 'huawei list --output-format' = @('normal', 'json') 'huawei list --output-format-version' = @('1', '2') + 'huawei list --timeout' = @('infinite', '-1') 'huawei list -o' = @('normal', 'json') 'ibm crit-log --output-format' = @('normal', 'json', 'binary') 'ibm crit-log --output-format-version' = @('1', '2') + 'ibm crit-log --timeout' = @('infinite', '-1') 'ibm crit-log -o' = @('normal', 'json', 'binary') 'ibm persist-event-log --output-format' = @('normal', 'json', 'binary') 'ibm persist-event-log --output-format-version' = @('1', '2') + 'ibm persist-event-log --timeout' = @('infinite', '-1') 'ibm persist-event-log -o' = @('normal', 'json', 'binary') 'ibm vpd --output-format' = @('normal', 'json', 'binary') 'ibm vpd --output-format-version' = @('1', '2') + 'ibm vpd --timeout' = @('infinite', '-1') 'ibm vpd -o' = @('normal', 'json', 'binary') 'id ctrl --output-format' = @('normal', 'json', 'binary') 'id ctrl --output-format-version' = @('1', '2') + 'id ctrl --timeout' = @('infinite', '-1') 'id ctrl -o' = @('normal', 'json', 'binary') + 'id ctrl-list --namespace-id' = @('all', '-1') 'id ctrl-list --output-format' = @('normal', 'json') 'id ctrl-list --output-format-version' = @('1', '2') + 'id ctrl-list --timeout' = @('infinite', '-1') + 'id ctrl-list -n' = @('all', '-1') 'id ctrl-list -o' = @('normal', 'json') 'id domain --output-format' = @('normal', 'json', 'binary') 'id domain --output-format-version' = @('1', '2') + 'id domain --timeout' = @('infinite', '-1') 'id domain -o' = @('normal', 'json', 'binary') 'id endgrp-list --output-format' = @('normal', 'json') 'id endgrp-list --output-format-version' = @('1', '2') + 'id endgrp-list --timeout' = @('infinite', '-1') 'id endgrp-list -o' = @('normal', 'json') 'id iocs --output-format' = @('normal', 'json', 'binary') 'id iocs --output-format-version' = @('1', '2') + 'id iocs --timeout' = @('infinite', '-1') 'id iocs -o' = @('normal', 'json', 'binary') + 'id ns --namespace-id' = @('all', '-1') 'id ns --output-format' = @('normal', 'json', 'binary') 'id ns --output-format-version' = @('1', '2') + 'id ns --timeout' = @('infinite', '-1') + 'id ns -n' = @('all', '-1') 'id ns -o' = @('normal', 'json', 'binary') + 'id ns-descs --namespace-id' = @('all', '-1') 'id ns-descs --output-format' = @('normal', 'json', 'binary') 'id ns-descs --output-format-version' = @('1', '2') + 'id ns-descs --timeout' = @('infinite', '-1') + 'id ns-descs -n' = @('all', '-1') 'id ns-descs -o' = @('normal', 'json', 'binary') 'id ns-granularity --output-format' = @('normal', 'json', 'binary') 'id ns-granularity --output-format-version' = @('1', '2') + 'id ns-granularity --timeout' = @('infinite', '-1') 'id ns-granularity -o' = @('normal', 'json', 'binary') + 'id ns-ind --namespace-id' = @('all', '-1') 'id ns-ind --output-format' = @('normal', 'json', 'binary') 'id ns-ind --output-format-version' = @('1', '2') + 'id ns-ind --timeout' = @('infinite', '-1') + 'id ns-ind -n' = @('all', '-1') 'id ns-ind -o' = @('normal', 'json', 'binary') 'id ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id ns-lba-format --output-format-version' = @('1', '2') + 'id ns-lba-format --timeout' = @('infinite', '-1') 'id ns-lba-format -o' = @('normal', 'json', 'binary') + 'id ns-list --namespace-id' = @('all', '-1') 'id ns-list --output-format' = @('normal', 'json') 'id ns-list --output-format-version' = @('1', '2') + 'id ns-list --timeout' = @('infinite', '-1') + 'id ns-list -n' = @('all', '-1') 'id ns-list -o' = @('normal', 'json') 'id nvm-ctrl --output-format' = @('normal', 'json', 'binary') 'id nvm-ctrl --output-format-version' = @('1', '2') + 'id nvm-ctrl --timeout' = @('infinite', '-1') 'id nvm-ctrl -o' = @('normal', 'json', 'binary') + 'id nvm-ns --namespace-id' = @('all', '-1') 'id nvm-ns --output-format' = @('normal', 'json', 'binary') 'id nvm-ns --output-format-version' = @('1', '2') + 'id nvm-ns --timeout' = @('infinite', '-1') + 'id nvm-ns -n' = @('all', '-1') 'id nvm-ns -o' = @('normal', 'json', 'binary') 'id nvm-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id nvm-ns-lba-format --output-format-version' = @('1', '2') + 'id nvm-ns-lba-format --timeout' = @('infinite', '-1') 'id nvm-ns-lba-format -o' = @('normal', 'json', 'binary') 'id nvmset --output-format' = @('normal', 'json', 'binary') 'id nvmset --output-format-version' = @('1', '2') + 'id nvmset --timeout' = @('infinite', '-1') 'id nvmset -o' = @('normal', 'json', 'binary') 'id primary-ctrl-caps --output-format' = @('normal', 'json', 'binary') 'id primary-ctrl-caps --output-format-version' = @('1', '2') + 'id primary-ctrl-caps --timeout' = @('infinite', '-1') 'id primary-ctrl-caps -o' = @('normal', 'json', 'binary') 'id secondary-ctrl-list --output-format' = @('normal', 'json', 'binary') 'id secondary-ctrl-list --output-format-version' = @('1', '2') + 'id secondary-ctrl-list --timeout' = @('infinite', '-1') 'id secondary-ctrl-list -o' = @('normal', 'json', 'binary') 'id uuid --output-format' = @('normal', 'json', 'binary') 'id uuid --output-format-version' = @('1', '2') + 'id uuid --timeout' = @('infinite', '-1') 'id uuid -o' = @('normal', 'json', 'binary') 'id-ctrl --output-format' = @('normal', 'json', 'binary') 'id-ctrl --output-format-version' = @('1', '2') + 'id-ctrl --timeout' = @('infinite', '-1') 'id-ctrl -o' = @('normal', 'json', 'binary') 'id-domain --output-format' = @('normal', 'json', 'binary') 'id-domain --output-format-version' = @('1', '2') + 'id-domain --timeout' = @('infinite', '-1') 'id-domain -o' = @('normal', 'json', 'binary') 'id-iocs --output-format' = @('normal', 'json', 'binary') 'id-iocs --output-format-version' = @('1', '2') + 'id-iocs --timeout' = @('infinite', '-1') 'id-iocs -o' = @('normal', 'json', 'binary') + 'id-ns --namespace-id' = @('all', '-1') 'id-ns --output-format' = @('normal', 'json', 'binary') 'id-ns --output-format-version' = @('1', '2') + 'id-ns --timeout' = @('infinite', '-1') + 'id-ns -n' = @('all', '-1') 'id-ns -o' = @('normal', 'json', 'binary') 'id-ns-granularity --output-format' = @('normal', 'json', 'binary') 'id-ns-granularity --output-format-version' = @('1', '2') + 'id-ns-granularity --timeout' = @('infinite', '-1') 'id-ns-granularity -o' = @('normal', 'json', 'binary') 'id-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'id-ns-lba-format --output-format-version' = @('1', '2') + 'id-ns-lba-format --timeout' = @('infinite', '-1') 'id-ns-lba-format -o' = @('normal', 'json', 'binary') 'id-nvmset --output-format' = @('normal', 'json', 'binary') 'id-nvmset --output-format-version' = @('1', '2') + 'id-nvmset --timeout' = @('infinite', '-1') 'id-nvmset -o' = @('normal', 'json', 'binary') 'id-uuid --output-format' = @('normal', 'json', 'binary') 'id-uuid --output-format-version' = @('1', '2') + 'id-uuid --timeout' = @('infinite', '-1') 'id-uuid -o' = @('normal', 'json', 'binary') 'innogrit get-cdump --output-format' = @('normal', 'json', 'binary') 'innogrit get-cdump --output-format-version' = @('1', '2') + 'innogrit get-cdump --timeout' = @('infinite', '-1') 'innogrit get-cdump -o' = @('normal', 'json', 'binary') 'innogrit get-eventlog --output-format' = @('normal', 'json', 'binary') 'innogrit get-eventlog --output-format-version' = @('1', '2') + 'innogrit get-eventlog --timeout' = @('infinite', '-1') 'innogrit get-eventlog -o' = @('normal', 'json', 'binary') 'inspur nvme-vendor-log --output-format' = @('normal', 'json', 'binary') 'inspur nvme-vendor-log --output-format-version' = @('1', '2') + 'inspur nvme-vendor-log --timeout' = @('infinite', '-1') 'inspur nvme-vendor-log -o' = @('normal', 'json', 'binary') 'intel id-ctrl --output-format' = @('normal', 'json', 'binary') 'intel id-ctrl --output-format-version' = @('1', '2') + 'intel id-ctrl --timeout' = @('infinite', '-1') 'intel id-ctrl -o' = @('normal', 'json', 'binary') + 'intel internal-log --namespace-id' = @('all', '-1') 'intel internal-log --output-format' = @('normal', 'json', 'binary') 'intel internal-log --output-format-version' = @('1', '2') + 'intel internal-log --timeout' = @('infinite', '-1') + 'intel internal-log -n' = @('all', '-1') 'intel internal-log -o' = @('normal', 'json', 'binary') 'intel lat-stats --output-format' = @('normal', 'json', 'binary') 'intel lat-stats --output-format-version' = @('1', '2') + 'intel lat-stats --timeout' = @('infinite', '-1') 'intel lat-stats -o' = @('normal', 'json', 'binary') 'intel lat-stats-tracking --output-format' = @('normal', 'json', 'binary') 'intel lat-stats-tracking --output-format-version' = @('1', '2') + 'intel lat-stats-tracking --timeout' = @('infinite', '-1') 'intel lat-stats-tracking -o' = @('normal', 'json', 'binary') 'intel market-name --output-format' = @('normal', 'json', 'binary') 'intel market-name --output-format-version' = @('1', '2') + 'intel market-name --timeout' = @('infinite', '-1') 'intel market-name -o' = @('normal', 'json', 'binary') 'intel set-bucket-thresholds --output-format' = @('normal', 'json', 'binary') 'intel set-bucket-thresholds --output-format-version' = @('1', '2') + 'intel set-bucket-thresholds --timeout' = @('infinite', '-1') 'intel set-bucket-thresholds -o' = @('normal', 'json', 'binary') + 'intel smart-log-add --namespace-id' = @('all', '-1') 'intel smart-log-add --output-format' = @('normal', 'json', 'binary') 'intel smart-log-add --output-format-version' = @('1', '2') + 'intel smart-log-add --timeout' = @('infinite', '-1') + 'intel smart-log-add -n' = @('all', '-1') 'intel smart-log-add -o' = @('normal', 'json', 'binary') 'intel temp-stats --output-format' = @('normal', 'json', 'binary') 'intel temp-stats --output-format-version' = @('1', '2') + 'intel temp-stats --timeout' = @('infinite', '-1') 'intel temp-stats -o' = @('normal', 'json', 'binary') + 'io-mgmt recv --namespace-id' = @('all', '-1') 'io-mgmt recv --output-format' = @('normal', 'json', 'binary') 'io-mgmt recv --output-format-version' = @('1', '2') + 'io-mgmt recv --timeout' = @('infinite', '-1') + 'io-mgmt recv -n' = @('all', '-1') 'io-mgmt recv -o' = @('normal', 'json', 'binary') + 'io-mgmt send --namespace-id' = @('all', '-1') 'io-mgmt send --output-format' = @('normal', 'json', 'binary') 'io-mgmt send --output-format-version' = @('1', '2') + 'io-mgmt send --timeout' = @('infinite', '-1') + 'io-mgmt send -n' = @('all', '-1') 'io-mgmt send -o' = @('normal', 'json', 'binary') + 'io-mgmt-recv --namespace-id' = @('all', '-1') 'io-mgmt-recv --output-format' = @('normal', 'json', 'binary') 'io-mgmt-recv --output-format-version' = @('1', '2') + 'io-mgmt-recv --timeout' = @('infinite', '-1') + 'io-mgmt-recv -n' = @('all', '-1') 'io-mgmt-recv -o' = @('normal', 'json', 'binary') + 'io-mgmt-send --namespace-id' = @('all', '-1') 'io-mgmt-send --output-format' = @('normal', 'json', 'binary') 'io-mgmt-send --output-format-version' = @('1', '2') + 'io-mgmt-send --timeout' = @('infinite', '-1') + 'io-mgmt-send -n' = @('all', '-1') 'io-mgmt-send -o' = @('normal', 'json', 'binary') + 'io-passthru --namespace-id' = @('all', '-1') 'io-passthru --output-format' = @('normal', 'json', 'binary') 'io-passthru --output-format-version' = @('1', '2') + 'io-passthru --timeout' = @('infinite', '-1') + 'io-passthru -n' = @('all', '-1') 'io-passthru -o' = @('normal', 'json', 'binary') 'keys check-kxchap-secret --output-format' = @('normal', 'json', 'binary') 'keys check-kxchap-secret --output-format-version' = @('1', '2') + 'keys check-kxchap-secret --timeout' = @('infinite', '-1') 'keys check-kxchap-secret -o' = @('normal', 'json', 'binary') 'keys check-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys check-tls-psk --output-format-version' = @('1', '2') + 'keys check-tls-psk --timeout' = @('infinite', '-1') 'keys check-tls-psk -o' = @('normal', 'json', 'binary') 'keys export --output-format' = @('normal', 'json', 'binary') 'keys export --output-format-version' = @('1', '2') + 'keys export --timeout' = @('infinite', '-1') 'keys export -o' = @('normal', 'json', 'binary') 'keys gen-kxchap-secret --output-format' = @('normal', 'json', 'binary') 'keys gen-kxchap-secret --output-format-version' = @('1', '2') + 'keys gen-kxchap-secret --timeout' = @('infinite', '-1') 'keys gen-kxchap-secret -o' = @('normal', 'json', 'binary') 'keys gen-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys gen-tls-psk --output-format-version' = @('1', '2') + 'keys gen-tls-psk --timeout' = @('infinite', '-1') 'keys gen-tls-psk -o' = @('normal', 'json', 'binary') 'keys import --output-format' = @('normal', 'json', 'binary') 'keys import --output-format-version' = @('1', '2') + 'keys import --timeout' = @('infinite', '-1') 'keys import -o' = @('normal', 'json', 'binary') 'keys insert-tls-psk --output-format' = @('normal', 'json', 'binary') 'keys insert-tls-psk --output-format-version' = @('1', '2') + 'keys insert-tls-psk --timeout' = @('infinite', '-1') 'keys insert-tls-psk -o' = @('normal', 'json', 'binary') 'keys revoke --output-format' = @('normal', 'json', 'binary') 'keys revoke --output-format-version' = @('1', '2') + 'keys revoke --timeout' = @('infinite', '-1') 'keys revoke -o' = @('normal', 'json', 'binary') 'lba-status-log --output-format' = @('normal', 'json', 'binary') 'lba-status-log --output-format-version' = @('1', '2') + 'lba-status-log --timeout' = @('infinite', '-1') 'lba-status-log -o' = @('normal', 'json', 'binary') 'list --output-format' = @('normal', 'json') 'list --output-format-version' = @('1', '2') + 'list --timeout' = @('infinite', '-1') 'list -o' = @('normal', 'json') + 'list-ctrl --namespace-id' = @('all', '-1') 'list-ctrl --output-format' = @('normal', 'json') 'list-ctrl --output-format-version' = @('1', '2') + 'list-ctrl --timeout' = @('infinite', '-1') + 'list-ctrl -n' = @('all', '-1') 'list-ctrl -o' = @('normal', 'json') 'list-endgrp --output-format' = @('normal', 'json') 'list-endgrp --output-format-version' = @('1', '2') + 'list-endgrp --timeout' = @('infinite', '-1') 'list-endgrp -o' = @('normal', 'json') + 'list-ns --namespace-id' = @('all', '-1') 'list-ns --output-format' = @('normal', 'json') 'list-ns --output-format-version' = @('1', '2') + 'list-ns --timeout' = @('infinite', '-1') + 'list-ns -n' = @('all', '-1') 'list-ns -o' = @('normal', 'json') 'list-secondary --output-format' = @('normal', 'json', 'binary') 'list-secondary --output-format-version' = @('1', '2') + 'list-secondary --timeout' = @('infinite', '-1') 'list-secondary -o' = @('normal', 'json', 'binary') 'list-subsys --output-format' = @('normal', 'json') 'list-subsys --output-format-version' = @('1', '2') + 'list-subsys --timeout' = @('infinite', '-1') 'list-subsys -o' = @('normal', 'json') 'lm create-cdq --output-format' = @('normal', 'json', 'binary') 'lm create-cdq --output-format-version' = @('1', '2') + 'lm create-cdq --timeout' = @('infinite', '-1') 'lm create-cdq -o' = @('normal', 'json', 'binary') 'lm delete-cdq --output-format' = @('normal', 'json', 'binary') 'lm delete-cdq --output-format-version' = @('1', '2') + 'lm delete-cdq --timeout' = @('infinite', '-1') 'lm delete-cdq -o' = @('normal', 'json', 'binary') 'lm get-cdq --output-format' = @('normal', 'json', 'binary') 'lm get-cdq --output-format-version' = @('1', '2') + 'lm get-cdq --timeout' = @('infinite', '-1') 'lm get-cdq -o' = @('normal', 'json', 'binary') 'lm migration-recv --output-format' = @('normal', 'json', 'binary') 'lm migration-recv --output-format-version' = @('1', '2') 'lm migration-recv --sel' = @('0', '1', '2', '3') + 'lm migration-recv --timeout' = @('infinite', '-1') 'lm migration-recv -o' = @('normal', 'json', 'binary') 'lm migration-recv -s' = @('0', '1', '2', '3') 'lm migration-send --output-format' = @('normal', 'json', 'binary') 'lm migration-send --output-format-version' = @('1', '2') 'lm migration-send --sel' = @('0', '1', '2', '3') + 'lm migration-send --timeout' = @('infinite', '-1') 'lm migration-send -o' = @('normal', 'json', 'binary') 'lm migration-send -s' = @('0', '1', '2', '3') 'lm set-cdq --output-format' = @('normal', 'json', 'binary') 'lm set-cdq --output-format-version' = @('1', '2') + 'lm set-cdq --timeout' = @('infinite', '-1') 'lm set-cdq -o' = @('normal', 'json', 'binary') 'lm track-send --output-format' = @('normal', 'json', 'binary') 'lm track-send --output-format-version' = @('1', '2') 'lm track-send --sel' = @('0', '1', '2', '3') + 'lm track-send --timeout' = @('infinite', '-1') 'lm track-send -o' = @('normal', 'json', 'binary') 'lm track-send -s' = @('0', '1', '2', '3') 'lockdown --output-format' = @('normal', 'json', 'binary') 'lockdown --output-format-version' = @('1', '2') + 'lockdown --timeout' = @('infinite', '-1') 'lockdown -o' = @('normal', 'json', 'binary') 'log ana --output-format' = @('normal', 'json', 'binary') 'log ana --output-format-version' = @('1', '2') + 'log ana --timeout' = @('infinite', '-1') 'log ana -o' = @('normal', 'json', 'binary') 'log ave-discovery --output-format' = @('normal', 'json', 'binary') 'log ave-discovery --output-format-version' = @('1', '2') + 'log ave-discovery --timeout' = @('infinite', '-1') 'log ave-discovery -o' = @('normal', 'json', 'binary') 'log boot-part --output-format' = @('normal', 'json', 'binary') 'log boot-part --output-format-version' = @('1', '2') + 'log boot-part --timeout' = @('infinite', '-1') 'log boot-part -o' = @('normal', 'json', 'binary') 'log changed-alloc-ns-list --output-format' = @('normal', 'json', 'binary') 'log changed-alloc-ns-list --output-format-version' = @('1', '2') + 'log changed-alloc-ns-list --timeout' = @('infinite', '-1') 'log changed-alloc-ns-list -o' = @('normal', 'json', 'binary') 'log changed-ns-list --output-format' = @('normal', 'json', 'binary') 'log changed-ns-list --output-format-version' = @('1', '2') + 'log changed-ns-list --timeout' = @('infinite', '-1') 'log changed-ns-list -o' = @('normal', 'json', 'binary') + 'log dispersed-ns-participating-nss --namespace-id' = @('all', '-1') 'log dispersed-ns-participating-nss --output-format' = @('normal', 'json', 'binary') 'log dispersed-ns-participating-nss --output-format-version' = @('1', '2') + 'log dispersed-ns-participating-nss --timeout' = @('infinite', '-1') + 'log dispersed-ns-participating-nss -n' = @('all', '-1') 'log dispersed-ns-participating-nss -o' = @('normal', 'json', 'binary') 'log effects --output-format' = @('normal', 'json', 'binary') 'log effects --output-format-version' = @('1', '2') + 'log effects --timeout' = @('infinite', '-1') 'log effects -o' = @('normal', 'json', 'binary') 'log endurance --output-format' = @('normal', 'json', 'binary') 'log endurance --output-format-version' = @('1', '2') + 'log endurance --timeout' = @('infinite', '-1') 'log endurance -o' = @('normal', 'json', 'binary') 'log endurance-event-agg --output-format' = @('normal', 'json', 'binary') 'log endurance-event-agg --output-format-version' = @('1', '2') + 'log endurance-event-agg --timeout' = @('infinite', '-1') 'log endurance-event-agg -o' = @('normal', 'json', 'binary') + 'log error --namespace-id' = @('all', '-1') 'log error --output-format' = @('normal', 'json', 'binary') 'log error --output-format-version' = @('1', '2') + 'log error --timeout' = @('infinite', '-1') + 'log error -n' = @('all', '-1') 'log error -o' = @('normal', 'json', 'binary') 'log fid-support-effects --output-format' = @('normal', 'json', 'binary') 'log fid-support-effects --output-format-version' = @('1', '2') + 'log fid-support-effects --timeout' = @('infinite', '-1') 'log fid-support-effects -o' = @('normal', 'json', 'binary') 'log fw --output-format' = @('normal', 'json', 'binary') 'log fw --output-format-version' = @('1', '2') + 'log fw --timeout' = @('infinite', '-1') 'log fw -o' = @('normal', 'json', 'binary') 'log host-discovery --output-format' = @('normal', 'json', 'binary') 'log host-discovery --output-format-version' = @('1', '2') + 'log host-discovery --timeout' = @('infinite', '-1') 'log host-discovery -o' = @('normal', 'json', 'binary') 'log lba-status --output-format' = @('normal', 'json', 'binary') 'log lba-status --output-format-version' = @('1', '2') + 'log lba-status --timeout' = @('infinite', '-1') 'log lba-status -o' = @('normal', 'json', 'binary') 'log media-unit-stat --output-format' = @('normal', 'json', 'binary') 'log media-unit-stat --output-format-version' = @('1', '2') + 'log media-unit-stat --timeout' = @('infinite', '-1') 'log media-unit-stat -o' = @('normal', 'json', 'binary') 'log mgmt-addr-list --output-format' = @('normal', 'json', 'binary') 'log mgmt-addr-list --output-format-version' = @('1', '2') + 'log mgmt-addr-list --timeout' = @('infinite', '-1') 'log mgmt-addr-list -o' = @('normal', 'json', 'binary') 'log mi-cmd-support-effects --output-format' = @('normal', 'json', 'binary') 'log mi-cmd-support-effects --output-format-version' = @('1', '2') + 'log mi-cmd-support-effects --timeout' = @('infinite', '-1') 'log mi-cmd-support-effects -o' = @('normal', 'json', 'binary') 'log persistent-event --output-format' = @('normal', 'json', 'binary') 'log persistent-event --output-format-version' = @('1', '2') + 'log persistent-event --timeout' = @('infinite', '-1') 'log persistent-event -o' = @('normal', 'json', 'binary') 'log phy-rx-eom --output-format' = @('normal', 'json', 'binary') 'log phy-rx-eom --output-format-version' = @('1', '2') + 'log phy-rx-eom --timeout' = @('infinite', '-1') 'log phy-rx-eom -o' = @('normal', 'json', 'binary') 'log power-measurement --output-format' = @('normal', 'json', 'binary') 'log power-measurement --output-format-version' = @('1', '2') + 'log power-measurement --timeout' = @('infinite', '-1') 'log power-measurement -o' = @('normal', 'json', 'binary') 'log pred-lat-event-agg --output-format' = @('normal', 'json', 'binary') 'log pred-lat-event-agg --output-format-version' = @('1', '2') + 'log pred-lat-event-agg --timeout' = @('infinite', '-1') 'log pred-lat-event-agg -o' = @('normal', 'json', 'binary') 'log predictable-lat --output-format' = @('normal', 'json', 'binary') 'log predictable-lat --output-format-version' = @('1', '2') + 'log predictable-lat --timeout' = @('infinite', '-1') 'log predictable-lat -o' = @('normal', 'json', 'binary') 'log pull-model-ddc-req --output-format' = @('normal', 'json', 'binary') 'log pull-model-ddc-req --output-format-version' = @('1', '2') + 'log pull-model-ddc-req --timeout' = @('infinite', '-1') 'log pull-model-ddc-req -o' = @('normal', 'json', 'binary') 'log reachability-associations --output-format' = @('normal', 'json', 'binary') 'log reachability-associations --output-format-version' = @('1', '2') + 'log reachability-associations --timeout' = @('infinite', '-1') 'log reachability-associations -o' = @('normal', 'json', 'binary') 'log reachability-groups --output-format' = @('normal', 'json', 'binary') 'log reachability-groups --output-format-version' = @('1', '2') + 'log reachability-groups --timeout' = @('infinite', '-1') 'log reachability-groups -o' = @('normal', 'json', 'binary') 'log resv-notif --output-format' = @('normal', 'json', 'binary') 'log resv-notif --output-format-version' = @('1', '2') + 'log resv-notif --timeout' = @('infinite', '-1') 'log resv-notif -o' = @('normal', 'json', 'binary') 'log rotational-media-info --output-format' = @('normal', 'json', 'binary') 'log rotational-media-info --output-format-version' = @('1', '2') + 'log rotational-media-info --timeout' = @('infinite', '-1') 'log rotational-media-info -o' = @('normal', 'json', 'binary') 'log sanitize --output-format' = @('normal', 'json', 'binary') 'log sanitize --output-format-version' = @('1', '2') + 'log sanitize --timeout' = @('infinite', '-1') 'log sanitize -o' = @('normal', 'json', 'binary') 'log self-test --output-format' = @('normal', 'json', 'binary') 'log self-test --output-format-version' = @('1', '2') + 'log self-test --timeout' = @('infinite', '-1') 'log self-test -o' = @('normal', 'json', 'binary') + 'log smart --namespace-id' = @('all', '-1') 'log smart --output-format' = @('normal', 'json', 'binary') 'log smart --output-format-version' = @('1', '2') + 'log smart --timeout' = @('infinite', '-1') + 'log smart -n' = @('all', '-1') 'log smart -o' = @('normal', 'json', 'binary') 'log supported-cap-config --output-format' = @('normal', 'json', 'binary') 'log supported-cap-config --output-format-version' = @('1', '2') + 'log supported-cap-config --timeout' = @('infinite', '-1') 'log supported-cap-config -o' = @('normal', 'json', 'binary') 'log supported-pages --output-format' = @('normal', 'json', 'binary') 'log supported-pages --output-format-version' = @('1', '2') + 'log supported-pages --timeout' = @('infinite', '-1') 'log supported-pages -o' = @('normal', 'json', 'binary') 'log telemetry --output-format' = @('normal', 'json', 'binary') 'log telemetry --output-format-version' = @('1', '2') + 'log telemetry --timeout' = @('infinite', '-1') 'log telemetry -o' = @('normal', 'json', 'binary') 'mangoboost id-ctrl --output-format' = @('normal', 'json', 'binary') 'mangoboost id-ctrl --output-format-version' = @('1', '2') + 'mangoboost id-ctrl --timeout' = @('infinite', '-1') 'mangoboost id-ctrl -o' = @('normal', 'json', 'binary') 'media-unit-stat-log --output-format' = @('normal', 'json', 'binary') 'media-unit-stat-log --output-format-version' = @('1', '2') + 'media-unit-stat-log --timeout' = @('infinite', '-1') 'media-unit-stat-log -o' = @('normal', 'json', 'binary') 'memblaze clear-error-log --output-format' = @('normal', 'json', 'binary') 'memblaze clear-error-log --output-format-version' = @('1', '2') + 'memblaze clear-error-log --timeout' = @('infinite', '-1') 'memblaze clear-error-log -o' = @('normal', 'json', 'binary') 'memblaze get-pm-status --output-format' = @('normal', 'json', 'binary') 'memblaze get-pm-status --output-format-version' = @('1', '2') + 'memblaze get-pm-status --timeout' = @('infinite', '-1') 'memblaze get-pm-status -o' = @('normal', 'json', 'binary') 'memblaze lat-get-feature-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-get-feature-x --output-format-version' = @('1', '2') + 'memblaze lat-get-feature-x --timeout' = @('infinite', '-1') 'memblaze lat-get-feature-x -o' = @('normal', 'json', 'binary') 'memblaze lat-log --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log --output-format-version' = @('1', '2') + 'memblaze lat-log --timeout' = @('infinite', '-1') 'memblaze lat-log -o' = @('normal', 'json', 'binary') 'memblaze lat-log-print --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log-print --output-format-version' = @('1', '2') + 'memblaze lat-log-print --timeout' = @('infinite', '-1') 'memblaze lat-log-print -o' = @('normal', 'json', 'binary') 'memblaze lat-log-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-log-print-x --output-format-version' = @('1', '2') + 'memblaze lat-log-print-x --timeout' = @('infinite', '-1') 'memblaze lat-log-print-x -o' = @('normal', 'json', 'binary') 'memblaze lat-set-feature-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-set-feature-x --output-format-version' = @('1', '2') + 'memblaze lat-set-feature-x --timeout' = @('infinite', '-1') 'memblaze lat-set-feature-x -o' = @('normal', 'json', 'binary') 'memblaze lat-stats --output-format' = @('normal', 'json', 'binary') 'memblaze lat-stats --output-format-version' = @('1', '2') + 'memblaze lat-stats --timeout' = @('infinite', '-1') 'memblaze lat-stats -o' = @('normal', 'json', 'binary') 'memblaze lat-stats-print --output-format' = @('normal', 'json', 'binary') 'memblaze lat-stats-print --output-format-version' = @('1', '2') + 'memblaze lat-stats-print --timeout' = @('infinite', '-1') 'memblaze lat-stats-print -o' = @('normal', 'json', 'binary') 'memblaze lat-stats-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze lat-stats-print-x --output-format-version' = @('1', '2') + 'memblaze lat-stats-print-x --timeout' = @('infinite', '-1') 'memblaze lat-stats-print-x -o' = @('normal', 'json', 'binary') 'memblaze perf-stats-print-x --output-format' = @('normal', 'json', 'binary') 'memblaze perf-stats-print-x --output-format-version' = @('1', '2') + 'memblaze perf-stats-print-x --timeout' = @('infinite', '-1') 'memblaze perf-stats-print-x -o' = @('normal', 'json', 'binary') 'memblaze select-download --output-format' = @('normal', 'json', 'binary') 'memblaze select-download --output-format-version' = @('1', '2') + 'memblaze select-download --timeout' = @('infinite', '-1') 'memblaze select-download -o' = @('normal', 'json', 'binary') 'memblaze set-pm-status --output-format' = @('normal', 'json', 'binary') 'memblaze set-pm-status --output-format-version' = @('1', '2') + 'memblaze set-pm-status --timeout' = @('infinite', '-1') 'memblaze set-pm-status -o' = @('normal', 'json', 'binary') + 'memblaze smart-log-add --namespace-id' = @('all', '-1') 'memblaze smart-log-add --output-format' = @('normal', 'json', 'binary') 'memblaze smart-log-add --output-format-version' = @('1', '2') + 'memblaze smart-log-add --timeout' = @('infinite', '-1') + 'memblaze smart-log-add -n' = @('all', '-1') 'memblaze smart-log-add -o' = @('normal', 'json', 'binary') 'memblaze smart-log-add-x --output-format' = @('normal', 'json', 'binary') 'memblaze smart-log-add-x --output-format-version' = @('1', '2') + 'memblaze smart-log-add-x --timeout' = @('infinite', '-1') 'memblaze smart-log-add-x -o' = @('normal', 'json', 'binary') 'mgmt-addr-list-log --output-format' = @('normal', 'json', 'binary') 'mgmt-addr-list-log --output-format-version' = @('1', '2') + 'mgmt-addr-list-log --timeout' = @('infinite', '-1') 'mgmt-addr-list-log -o' = @('normal', 'json', 'binary') 'mi-cmd-support-effects-log --output-format' = @('normal', 'json', 'binary') 'mi-cmd-support-effects-log --output-format-version' = @('1', '2') + 'mi-cmd-support-effects-log --timeout' = @('infinite', '-1') 'mi-cmd-support-effects-log -o' = @('normal', 'json', 'binary') 'micron clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'micron clear-fw-activate-history --output-format-version' = @('1', '2') + 'micron clear-fw-activate-history --timeout' = @('infinite', '-1') 'micron clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'micron clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'micron clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'micron clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'micron clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'micron cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'micron cloud-SSD-plugin-version --output-format-version' = @('1', '2') + 'micron cloud-SSD-plugin-version --timeout' = @('infinite', '-1') 'micron cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') 'micron cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'micron cloud-boot-SSD-version --output-format-version' = @('1', '2') + 'micron cloud-boot-SSD-version --timeout' = @('infinite', '-1') 'micron cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') 'micron id-ctrl --output-format' = @('normal', 'json', 'binary') 'micron id-ctrl --output-format-version' = @('1', '2') + 'micron id-ctrl --timeout' = @('infinite', '-1') 'micron id-ctrl -o' = @('normal', 'json', 'binary') 'micron latency-logs --output-format' = @('normal', 'json', 'binary') 'micron latency-logs --output-format-version' = @('1', '2') + 'micron latency-logs --timeout' = @('infinite', '-1') 'micron latency-logs -o' = @('normal', 'json', 'binary') 'micron latency-stats --output-format' = @('normal', 'json', 'binary') 'micron latency-stats --output-format-version' = @('1', '2') + 'micron latency-stats --timeout' = @('infinite', '-1') 'micron latency-stats -o' = @('normal', 'json', 'binary') 'micron latency-tracking --output-format' = @('normal', 'json', 'binary') 'micron latency-tracking --output-format-version' = @('1', '2') + 'micron latency-tracking --timeout' = @('infinite', '-1') 'micron latency-tracking -o' = @('normal', 'json', 'binary') 'micron log-page-directory --output-format' = @('normal', 'json', 'binary') 'micron log-page-directory --output-format-version' = @('1', '2') + 'micron log-page-directory --timeout' = @('infinite', '-1') 'micron log-page-directory -o' = @('normal', 'json', 'binary') 'micron plugin-version --output-format' = @('normal', 'json', 'binary') 'micron plugin-version --output-format-version' = @('1', '2') + 'micron plugin-version --timeout' = @('infinite', '-1') 'micron plugin-version -o' = @('normal', 'json', 'binary') 'micron select-download --output-format' = @('normal', 'json', 'binary') 'micron select-download --output-format-version' = @('1', '2') + 'micron select-download --timeout' = @('infinite', '-1') 'micron select-download -o' = @('normal', 'json', 'binary') 'micron smart-log --output-format' = @('normal', 'json') 'micron smart-log --output-format-version' = @('1', '2') + 'micron smart-log --timeout' = @('infinite', '-1') 'micron smart-log -o' = @('normal', 'json') 'micron vs-cloud-log --output-format' = @('normal', 'json') 'micron vs-cloud-log --output-format-version' = @('1', '2') + 'micron vs-cloud-log --timeout' = @('infinite', '-1') 'micron vs-cloud-log -o' = @('normal', 'json') 'micron vs-device-waf --output-format' = @('normal', 'json', 'binary') 'micron vs-device-waf --output-format-version' = @('1', '2') + 'micron vs-device-waf --timeout' = @('infinite', '-1') 'micron vs-device-waf -o' = @('normal', 'json', 'binary') 'micron vs-drive-info --output-format' = @('normal', 'json') 'micron vs-drive-info --output-format-version' = @('1', '2') + 'micron vs-drive-info --timeout' = @('infinite', '-1') 'micron vs-drive-info -o' = @('normal', 'json') 'micron vs-fw-activate-history --output-format' = @('normal', 'json') 'micron vs-fw-activate-history --output-format-version' = @('1', '2') + 'micron vs-fw-activate-history --timeout' = @('infinite', '-1') 'micron vs-fw-activate-history -o' = @('normal', 'json') 'micron vs-internal-log --output-format' = @('normal', 'json', 'binary') 'micron vs-internal-log --output-format-version' = @('1', '2') + 'micron vs-internal-log --timeout' = @('infinite', '-1') 'micron vs-internal-log -o' = @('normal', 'json', 'binary') 'micron vs-nand-stats --output-format' = @('normal', 'json') 'micron vs-nand-stats --output-format-version' = @('1', '2') + 'micron vs-nand-stats --timeout' = @('infinite', '-1') 'micron vs-nand-stats -o' = @('normal', 'json') 'micron vs-pcie-stats --output-format' = @('normal', 'json') 'micron vs-pcie-stats --output-format-version' = @('1', '2') + 'micron vs-pcie-stats --timeout' = @('infinite', '-1') 'micron vs-pcie-stats -o' = @('normal', 'json') 'micron vs-smart-add-log --output-format' = @('normal', 'json') 'micron vs-smart-add-log --output-format-version' = @('1', '2') + 'micron vs-smart-add-log --timeout' = @('infinite', '-1') 'micron vs-smart-add-log -o' = @('normal', 'json') 'micron vs-smart-ext-log --output-format' = @('normal', 'json') 'micron vs-smart-ext-log --output-format-version' = @('1', '2') + 'micron vs-smart-ext-log --timeout' = @('infinite', '-1') 'micron vs-smart-ext-log -o' = @('normal', 'json') 'micron vs-smbus-option --output-format' = @('normal', 'json', 'binary') 'micron vs-smbus-option --output-format-version' = @('1', '2') + 'micron vs-smbus-option --timeout' = @('infinite', '-1') 'micron vs-smbus-option -o' = @('normal', 'json', 'binary') 'micron vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'micron vs-telemetry-controller-option --output-format-version' = @('1', '2') + 'micron vs-telemetry-controller-option --timeout' = @('infinite', '-1') 'micron vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') 'micron vs-temperature-stats --output-format' = @('normal', 'json') 'micron vs-temperature-stats --output-format-version' = @('1', '2') + 'micron vs-temperature-stats --timeout' = @('infinite', '-1') 'micron vs-temperature-stats -o' = @('normal', 'json') 'micron vs-vendor-telemetry-log --output-format' = @('normal', 'json') 'micron vs-vendor-telemetry-log --output-format-version' = @('1', '2') + 'micron vs-vendor-telemetry-log --timeout' = @('infinite', '-1') 'micron vs-vendor-telemetry-log -o' = @('normal', 'json') 'micron vs-work-load-log --output-format' = @('normal', 'json') 'micron vs-work-load-log --output-format-version' = @('1', '2') + 'micron vs-work-load-log --timeout' = @('infinite', '-1') 'micron vs-work-load-log -o' = @('normal', 'json') 'nbft show --output-format' = @('normal', 'json', 'binary') 'nbft show --output-format-version' = @('1', '2') + 'nbft show --timeout' = @('infinite', '-1') 'nbft show -o' = @('normal', 'json', 'binary') 'netapp ontapdevices --output-format' = @('normal', 'column', 'json') 'netapp ontapdevices --output-format-version' = @('1', '2') + 'netapp ontapdevices --timeout' = @('infinite', '-1') 'netapp ontapdevices -o' = @('normal', 'column', 'json') 'netapp smdevices --output-format' = @('normal', 'column', 'json') 'netapp smdevices --output-format-version' = @('1', '2') + 'netapp smdevices --timeout' = @('infinite', '-1') 'netapp smdevices -o' = @('normal', 'column', 'json') + 'ns attach --namespace-id' = @('all', '-1') 'ns attach --output-format' = @('normal', 'json', 'binary') 'ns attach --output-format-version' = @('1', '2') + 'ns attach --timeout' = @('infinite', '-1') + 'ns attach -n' = @('all', '-1') 'ns attach -o' = @('normal', 'json', 'binary') 'ns create --output-format' = @('normal', 'json', 'binary') 'ns create --output-format-version' = @('1', '2') + 'ns create --timeout' = @('infinite', '-1') 'ns create -o' = @('normal', 'json', 'binary') + 'ns delete --namespace-id' = @('all', '-1') 'ns delete --output-format' = @('normal', 'json', 'binary') 'ns delete --output-format-version' = @('1', '2') + 'ns delete --timeout' = @('infinite', '-1') + 'ns delete -n' = @('all', '-1') 'ns delete -o' = @('normal', 'json', 'binary') + 'ns detach --namespace-id' = @('all', '-1') 'ns detach --output-format' = @('normal', 'json', 'binary') 'ns detach --output-format-version' = @('1', '2') + 'ns detach --timeout' = @('infinite', '-1') + 'ns detach -n' = @('all', '-1') 'ns detach -o' = @('normal', 'json', 'binary') 'ns get-id --output-format' = @('normal', 'json', 'binary') 'ns get-id --output-format-version' = @('1', '2') + 'ns get-id --timeout' = @('infinite', '-1') 'ns get-id -o' = @('normal', 'json', 'binary') + 'ns-descs --namespace-id' = @('all', '-1') 'ns-descs --output-format' = @('normal', 'json', 'binary') 'ns-descs --output-format-version' = @('1', '2') + 'ns-descs --timeout' = @('infinite', '-1') + 'ns-descs -n' = @('all', '-1') 'ns-descs -o' = @('normal', 'json', 'binary') 'ns-rescan --output-format' = @('normal', 'json', 'binary') 'ns-rescan --output-format-version' = @('1', '2') + 'ns-rescan --timeout' = @('infinite', '-1') 'ns-rescan -o' = @('normal', 'json', 'binary') 'nvidia id-ctrl --output-format' = @('normal', 'json', 'binary') 'nvidia id-ctrl --output-format-version' = @('1', '2') + 'nvidia id-ctrl --timeout' = @('infinite', '-1') 'nvidia id-ctrl -o' = @('normal', 'json', 'binary') 'nvm-id-ctrl --output-format' = @('normal', 'json', 'binary') 'nvm-id-ctrl --output-format-version' = @('1', '2') + 'nvm-id-ctrl --timeout' = @('infinite', '-1') 'nvm-id-ctrl -o' = @('normal', 'json', 'binary') + 'nvm-id-ns --namespace-id' = @('all', '-1') 'nvm-id-ns --output-format' = @('normal', 'json', 'binary') 'nvm-id-ns --output-format-version' = @('1', '2') + 'nvm-id-ns --timeout' = @('infinite', '-1') + 'nvm-id-ns -n' = @('all', '-1') 'nvm-id-ns -o' = @('normal', 'json', 'binary') 'nvm-id-ns-lba-format --output-format' = @('normal', 'json', 'binary') 'nvm-id-ns-lba-format --output-format-version' = @('1', '2') + 'nvm-id-ns-lba-format --timeout' = @('infinite', '-1') 'nvm-id-ns-lba-format -o' = @('normal', 'json', 'binary') + 'nvme-mi recv --namespace-id' = @('all', '-1') 'nvme-mi recv --output-format' = @('normal', 'json', 'binary') 'nvme-mi recv --output-format-version' = @('1', '2') + 'nvme-mi recv --timeout' = @('infinite', '-1') + 'nvme-mi recv -n' = @('all', '-1') 'nvme-mi recv -o' = @('normal', 'json', 'binary') + 'nvme-mi send --namespace-id' = @('all', '-1') 'nvme-mi send --output-format' = @('normal', 'json', 'binary') 'nvme-mi send --output-format-version' = @('1', '2') + 'nvme-mi send --timeout' = @('infinite', '-1') + 'nvme-mi send -n' = @('all', '-1') 'nvme-mi send -o' = @('normal', 'json', 'binary') + 'nvme-mi-recv --namespace-id' = @('all', '-1') 'nvme-mi-recv --output-format' = @('normal', 'json', 'binary') 'nvme-mi-recv --output-format-version' = @('1', '2') + 'nvme-mi-recv --timeout' = @('infinite', '-1') + 'nvme-mi-recv -n' = @('all', '-1') 'nvme-mi-recv -o' = @('normal', 'json', 'binary') + 'nvme-mi-send --namespace-id' = @('all', '-1') 'nvme-mi-send --output-format' = @('normal', 'json', 'binary') 'nvme-mi-send --output-format-version' = @('1', '2') + 'nvme-mi-send --timeout' = @('infinite', '-1') + 'nvme-mi-send -n' = @('all', '-1') 'nvme-mi-send -o' = @('normal', 'json', 'binary') 'ocp clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'ocp clear-fw-activate-history --output-format-version' = @('1', '2') + 'ocp clear-fw-activate-history --timeout' = @('infinite', '-1') 'ocp clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'ocp clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'ocp clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'ocp clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'ocp clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'ocp device-capability-log --output-format' = @('normal', 'json', 'binary') 'ocp device-capability-log --output-format-version' = @('1', '2') + 'ocp device-capability-log --timeout' = @('infinite', '-1') 'ocp device-capability-log -o' = @('normal', 'json', 'binary') 'ocp eol-plp-failure-mode --output-format' = @('normal', 'json', 'binary') 'ocp eol-plp-failure-mode --output-format-version' = @('1', '2') 'ocp eol-plp-failure-mode --sel' = @('0', '1', '2', '3') + 'ocp eol-plp-failure-mode --timeout' = @('infinite', '-1') 'ocp eol-plp-failure-mode -S' = @('0', '1', '2', '3') 'ocp eol-plp-failure-mode -o' = @('normal', 'json', 'binary') 'ocp error-recovery-log --output-format' = @('normal', 'json', 'binary') 'ocp error-recovery-log --output-format-version' = @('1', '2') + 'ocp error-recovery-log --timeout' = @('infinite', '-1') 'ocp error-recovery-log -o' = @('normal', 'json', 'binary') 'ocp fw-activate-history --output-format' = @('normal', 'json', 'binary') 'ocp fw-activate-history --output-format-version' = @('1', '2') + 'ocp fw-activate-history --timeout' = @('infinite', '-1') 'ocp fw-activate-history -o' = @('normal', 'json', 'binary') + 'ocp get-clear-pcie-correctable-errors --namespace-id' = @('all', '-1') 'ocp get-clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'ocp get-clear-pcie-correctable-errors --output-format-version' = @('1', '2') 'ocp get-clear-pcie-correctable-errors --sel' = @('0', '1', '2', '3') + 'ocp get-clear-pcie-correctable-errors --timeout' = @('infinite', '-1') + 'ocp get-clear-pcie-correctable-errors -n' = @('all', '-1') 'ocp get-clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'ocp get-clear-pcie-correctable-errors -s' = @('0', '1', '2', '3') 'ocp get-dssd-async-event-config --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-async-event-config --output-format-version' = @('1', '2') 'ocp get-dssd-async-event-config --sel' = @('0', '1', '2', '3') + 'ocp get-dssd-async-event-config --timeout' = @('infinite', '-1') 'ocp get-dssd-async-event-config -S' = @('0', '1', '2', '3') 'ocp get-dssd-async-event-config -o' = @('normal', 'json', 'binary') 'ocp get-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-power-state-feature --output-format-version' = @('1', '2') 'ocp get-dssd-power-state-feature --sel' = @('0', '1', '2', '3') + 'ocp get-dssd-power-state-feature --timeout' = @('infinite', '-1') 'ocp get-dssd-power-state-feature -S' = @('0', '1', '2', '3') 'ocp get-dssd-power-state-feature -o' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo --output-format-version' = @('1', '2') 'ocp get-enable-ieee1667-silo --sel' = @('0', '1', '2', '3') + 'ocp get-enable-ieee1667-silo --timeout' = @('infinite', '-1') 'ocp get-enable-ieee1667-silo -o' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo -s' = @('0', '1', '2', '3') 'ocp get-error-injection --output-format' = @('normal', 'json', 'binary') 'ocp get-error-injection --output-format-version' = @('1', '2') 'ocp get-error-injection --sel' = @('0', '1', '2', '3') + 'ocp get-error-injection --timeout' = @('infinite', '-1') 'ocp get-error-injection -o' = @('normal', 'json', 'binary') 'ocp get-error-injection -s' = @('0', '1', '2', '3') + 'ocp get-idle-wakeup-time --namespace-id' = @('all', '-1') 'ocp get-idle-wakeup-time --output-format' = @('normal', 'json', 'binary') 'ocp get-idle-wakeup-time --output-format-version' = @('1', '2') 'ocp get-idle-wakeup-time --sel' = @('0', '1', '2', '3') + 'ocp get-idle-wakeup-time --timeout' = @('infinite', '-1') + 'ocp get-idle-wakeup-time -n' = @('all', '-1') 'ocp get-idle-wakeup-time -o' = @('normal', 'json', 'binary') 'ocp get-idle-wakeup-time -s' = @('0', '1', '2', '3') + 'ocp get-latency-monitor --namespace-id' = @('all', '-1') 'ocp get-latency-monitor --output-format' = @('normal', 'json', 'binary') 'ocp get-latency-monitor --output-format-version' = @('1', '2') 'ocp get-latency-monitor --sel' = @('0', '1', '2', '3') + 'ocp get-latency-monitor --timeout' = @('infinite', '-1') + 'ocp get-latency-monitor -n' = @('all', '-1') 'ocp get-latency-monitor -o' = @('normal', 'json', 'binary') 'ocp get-latency-monitor -s' = @('0', '1', '2', '3') 'ocp get-plp-health-check-interval --output-format' = @('normal', 'json', 'binary') 'ocp get-plp-health-check-interval --output-format-version' = @('1', '2') 'ocp get-plp-health-check-interval --sel' = @('0', '1', '2', '3') + 'ocp get-plp-health-check-interval --timeout' = @('infinite', '-1') 'ocp get-plp-health-check-interval -S' = @('0', '1', '2', '3') 'ocp get-plp-health-check-interval -o' = @('normal', 'json', 'binary') + 'ocp get-telemetry-profile --namespace-id' = @('all', '-1') 'ocp get-telemetry-profile --output-format' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile --output-format-version' = @('1', '2') 'ocp get-telemetry-profile --sel' = @('0', '1', '2', '3') + 'ocp get-telemetry-profile --timeout' = @('infinite', '-1') + 'ocp get-telemetry-profile -n' = @('all', '-1') 'ocp get-telemetry-profile -o' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile -s' = @('0', '1', '2', '3') 'ocp hardware-component-log --comp-id' = @('asic', 'nand', 'dram', 'pmic', 'pcb', 'cap', 'reg', 'case', 'sn', 'country', 'hw-rev', 'born-on-date', 'vendor') 'ocp hardware-component-log --output-format' = @('normal', 'json', 'binary') 'ocp hardware-component-log --output-format-version' = @('1', '2') + 'ocp hardware-component-log --timeout' = @('infinite', '-1') 'ocp hardware-component-log -i' = @('asic', 'nand', 'dram', 'pmic', 'pcb', 'cap', 'reg', 'case', 'sn', 'country', 'hw-rev', 'born-on-date', 'vendor') 'ocp hardware-component-log -o' = @('normal', 'json', 'binary') 'ocp internal-log --output-format' = @('normal', 'json', 'binary') 'ocp internal-log --output-format-version' = @('1', '2') + 'ocp internal-log --timeout' = @('infinite', '-1') 'ocp internal-log -o' = @('normal', 'json', 'binary') 'ocp latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'ocp latency-monitor-log --output-format-version' = @('1', '2') + 'ocp latency-monitor-log --timeout' = @('infinite', '-1') 'ocp latency-monitor-log -o' = @('normal', 'json', 'binary') 'ocp persistent-event-log --output-format' = @('normal', 'json', 'binary') 'ocp persistent-event-log --output-format-version' = @('1', '2') + 'ocp persistent-event-log --timeout' = @('infinite', '-1') 'ocp persistent-event-log -o' = @('normal', 'json', 'binary') 'ocp set-dssd-async-event-config --output-format' = @('normal', 'json', 'binary') 'ocp set-dssd-async-event-config --output-format-version' = @('1', '2') + 'ocp set-dssd-async-event-config --timeout' = @('infinite', '-1') 'ocp set-dssd-async-event-config -o' = @('normal', 'json', 'binary') 'ocp set-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary') 'ocp set-dssd-power-state-feature --output-format-version' = @('1', '2') + 'ocp set-dssd-power-state-feature --timeout' = @('infinite', '-1') 'ocp set-dssd-power-state-feature -o' = @('normal', 'json', 'binary') 'ocp set-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary') 'ocp set-enable-ieee1667-silo --output-format-version' = @('1', '2') + 'ocp set-enable-ieee1667-silo --timeout' = @('infinite', '-1') 'ocp set-enable-ieee1667-silo -o' = @('normal', 'json', 'binary') 'ocp set-error-injection --output-format' = @('normal', 'json', 'binary') 'ocp set-error-injection --output-format-version' = @('1', '2') + 'ocp set-error-injection --timeout' = @('infinite', '-1') 'ocp set-error-injection -o' = @('normal', 'json', 'binary') 'ocp set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'ocp set-latency-monitor-feature --output-format-version' = @('1', '2') + 'ocp set-latency-monitor-feature --timeout' = @('infinite', '-1') 'ocp set-latency-monitor-feature -o' = @('normal', 'json', 'binary') 'ocp set-plp-health-check-interval --output-format' = @('normal', 'json', 'binary') 'ocp set-plp-health-check-interval --output-format-version' = @('1', '2') + 'ocp set-plp-health-check-interval --timeout' = @('infinite', '-1') 'ocp set-plp-health-check-interval -o' = @('normal', 'json', 'binary') 'ocp set-telemetry-profile --output-format' = @('normal', 'json', 'binary') 'ocp set-telemetry-profile --output-format-version' = @('1', '2') + 'ocp set-telemetry-profile --timeout' = @('infinite', '-1') 'ocp set-telemetry-profile -o' = @('normal', 'json', 'binary') 'ocp smart-add-log --output-format' = @('normal', 'json', 'binary') 'ocp smart-add-log --output-format-version' = @('1', '2') + 'ocp smart-add-log --timeout' = @('infinite', '-1') 'ocp smart-add-log -o' = @('normal', 'json', 'binary') 'ocp tcg-configuration-log --output-format' = @('normal', 'json', 'binary') 'ocp tcg-configuration-log --output-format-version' = @('1', '2') + 'ocp tcg-configuration-log --timeout' = @('infinite', '-1') 'ocp tcg-configuration-log -o' = @('normal', 'json', 'binary') 'ocp telemetry-string-log --output-format' = @('normal', 'json', 'binary') 'ocp telemetry-string-log --output-format-version' = @('1', '2') + 'ocp telemetry-string-log --timeout' = @('infinite', '-1') 'ocp telemetry-string-log -o' = @('normal', 'json', 'binary') 'ocp unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'ocp unsupported-reqs-log --output-format-version' = @('1', '2') + 'ocp unsupported-reqs-log --timeout' = @('infinite', '-1') 'ocp unsupported-reqs-log -o' = @('normal', 'json', 'binary') 'persistent-event-log --output-format' = @('normal', 'json', 'binary') 'persistent-event-log --output-format-version' = @('1', '2') + 'persistent-event-log --timeout' = @('infinite', '-1') 'persistent-event-log -o' = @('normal', 'json', 'binary') 'phy-rx-eom-log --output-format' = @('normal', 'json', 'binary') 'phy-rx-eom-log --output-format-version' = @('1', '2') + 'phy-rx-eom-log --timeout' = @('infinite', '-1') 'phy-rx-eom-log -o' = @('normal', 'json', 'binary') 'power-measurement-log --output-format' = @('normal', 'json', 'binary') 'power-measurement-log --output-format-version' = @('1', '2') + 'power-measurement-log --timeout' = @('infinite', '-1') 'power-measurement-log -o' = @('normal', 'json', 'binary') 'pred-lat-event-agg-log --output-format' = @('normal', 'json', 'binary') 'pred-lat-event-agg-log --output-format-version' = @('1', '2') + 'pred-lat-event-agg-log --timeout' = @('infinite', '-1') 'pred-lat-event-agg-log -o' = @('normal', 'json', 'binary') 'predictable-lat-log --output-format' = @('normal', 'json', 'binary') 'predictable-lat-log --output-format-version' = @('1', '2') + 'predictable-lat-log --timeout' = @('infinite', '-1') 'predictable-lat-log -o' = @('normal', 'json', 'binary') 'primary-ctrl-caps --output-format' = @('normal', 'json', 'binary') 'primary-ctrl-caps --output-format-version' = @('1', '2') + 'primary-ctrl-caps --timeout' = @('infinite', '-1') 'primary-ctrl-caps -o' = @('normal', 'json', 'binary') 'pull-model-ddc-req-log --output-format' = @('normal', 'json', 'binary') 'pull-model-ddc-req-log --output-format-version' = @('1', '2') + 'pull-model-ddc-req-log --timeout' = @('infinite', '-1') 'pull-model-ddc-req-log -o' = @('normal', 'json', 'binary') 'reachability-associations-log --output-format' = @('normal', 'json', 'binary') 'reachability-associations-log --output-format-version' = @('1', '2') + 'reachability-associations-log --timeout' = @('infinite', '-1') 'reachability-associations-log -o' = @('normal', 'json', 'binary') 'reachability-groups-log --output-format' = @('normal', 'json', 'binary') 'reachability-groups-log --output-format-version' = @('1', '2') + 'reachability-groups-log --timeout' = @('infinite', '-1') 'reachability-groups-log -o' = @('normal', 'json', 'binary') + 'read --namespace-id' = @('all', '-1') 'read --output-format' = @('normal', 'json', 'binary') 'read --output-format-version' = @('1', '2') + 'read --timeout' = @('infinite', '-1') + 'read -n' = @('all', '-1') 'read -o' = @('normal', 'json', 'binary') 'registry delete --output-format' = @('normal', 'json', 'binary') 'registry delete --output-format-version' = @('1', '2') + 'registry delete --timeout' = @('infinite', '-1') 'registry delete -o' = @('normal', 'json', 'binary') 'registry list --output-format' = @('normal', 'json', 'binary') 'registry list --output-format-version' = @('1', '2') + 'registry list --timeout' = @('infinite', '-1') 'registry list -o' = @('normal', 'json', 'binary') 'registry retrieve --output-format' = @('normal', 'json', 'binary') 'registry retrieve --output-format-version' = @('1', '2') + 'registry retrieve --timeout' = @('infinite', '-1') 'registry retrieve -o' = @('normal', 'json', 'binary') 'registry update --output-format' = @('normal', 'json', 'binary') 'registry update --output-format-version' = @('1', '2') + 'registry update --timeout' = @('infinite', '-1') 'registry update -o' = @('normal', 'json', 'binary') 'reset --output-format' = @('normal', 'json', 'binary') 'reset --output-format-version' = @('1', '2') + 'reset --timeout' = @('infinite', '-1') 'reset -o' = @('normal', 'json', 'binary') + 'resv acquire --namespace-id' = @('all', '-1') 'resv acquire --output-format' = @('normal', 'json', 'binary') 'resv acquire --output-format-version' = @('1', '2') + 'resv acquire --timeout' = @('infinite', '-1') + 'resv acquire -n' = @('all', '-1') 'resv acquire -o' = @('normal', 'json', 'binary') + 'resv register --namespace-id' = @('all', '-1') 'resv register --output-format' = @('normal', 'json', 'binary') 'resv register --output-format-version' = @('1', '2') + 'resv register --timeout' = @('infinite', '-1') + 'resv register -n' = @('all', '-1') 'resv register -o' = @('normal', 'json', 'binary') + 'resv release --namespace-id' = @('all', '-1') 'resv release --output-format' = @('normal', 'json', 'binary') 'resv release --output-format-version' = @('1', '2') + 'resv release --timeout' = @('infinite', '-1') + 'resv release -n' = @('all', '-1') 'resv release -o' = @('normal', 'json', 'binary') + 'resv report --namespace-id' = @('all', '-1') 'resv report --output-format' = @('normal', 'json', 'binary') 'resv report --output-format-version' = @('1', '2') + 'resv report --timeout' = @('infinite', '-1') + 'resv report -n' = @('all', '-1') 'resv report -o' = @('normal', 'json', 'binary') + 'resv-acquire --namespace-id' = @('all', '-1') 'resv-acquire --output-format' = @('normal', 'json', 'binary') 'resv-acquire --output-format-version' = @('1', '2') + 'resv-acquire --timeout' = @('infinite', '-1') + 'resv-acquire -n' = @('all', '-1') 'resv-acquire -o' = @('normal', 'json', 'binary') 'resv-notif-log --output-format' = @('normal', 'json', 'binary') 'resv-notif-log --output-format-version' = @('1', '2') + 'resv-notif-log --timeout' = @('infinite', '-1') 'resv-notif-log -o' = @('normal', 'json', 'binary') + 'resv-register --namespace-id' = @('all', '-1') 'resv-register --output-format' = @('normal', 'json', 'binary') 'resv-register --output-format-version' = @('1', '2') + 'resv-register --timeout' = @('infinite', '-1') + 'resv-register -n' = @('all', '-1') 'resv-register -o' = @('normal', 'json', 'binary') + 'resv-release --namespace-id' = @('all', '-1') 'resv-release --output-format' = @('normal', 'json', 'binary') 'resv-release --output-format-version' = @('1', '2') + 'resv-release --timeout' = @('infinite', '-1') + 'resv-release -n' = @('all', '-1') 'resv-release -o' = @('normal', 'json', 'binary') + 'resv-report --namespace-id' = @('all', '-1') 'resv-report --output-format' = @('normal', 'json', 'binary') 'resv-report --output-format-version' = @('1', '2') + 'resv-report --timeout' = @('infinite', '-1') + 'resv-report -n' = @('all', '-1') 'resv-report -o' = @('normal', 'json', 'binary') 'rotational-media-info-log --output-format' = @('normal', 'json', 'binary') 'rotational-media-info-log --output-format-version' = @('1', '2') + 'rotational-media-info-log --timeout' = @('infinite', '-1') 'rotational-media-info-log -o' = @('normal', 'json', 'binary') 'rpmb info --output-format' = @('normal', 'json', 'binary') 'rpmb info --output-format-version' = @('1', '2') + 'rpmb info --timeout' = @('infinite', '-1') 'rpmb info -o' = @('normal', 'json', 'binary') 'rpmb program-key --output-format' = @('normal', 'json', 'binary') 'rpmb program-key --output-format-version' = @('1', '2') + 'rpmb program-key --timeout' = @('infinite', '-1') 'rpmb program-key -o' = @('normal', 'json', 'binary') 'rpmb read-config --output-format' = @('normal', 'json', 'binary') 'rpmb read-config --output-format-version' = @('1', '2') + 'rpmb read-config --timeout' = @('infinite', '-1') 'rpmb read-config -o' = @('normal', 'json', 'binary') 'rpmb read-counter --output-format' = @('normal', 'json', 'binary') 'rpmb read-counter --output-format-version' = @('1', '2') + 'rpmb read-counter --timeout' = @('infinite', '-1') 'rpmb read-counter -o' = @('normal', 'json', 'binary') 'rpmb read-data --output-format' = @('normal', 'json', 'binary') 'rpmb read-data --output-format-version' = @('1', '2') + 'rpmb read-data --timeout' = @('infinite', '-1') 'rpmb read-data -o' = @('normal', 'json', 'binary') 'rpmb write-config --output-format' = @('normal', 'json', 'binary') 'rpmb write-config --output-format-version' = @('1', '2') + 'rpmb write-config --timeout' = @('infinite', '-1') 'rpmb write-config -o' = @('normal', 'json', 'binary') 'rpmb write-data --output-format' = @('normal', 'json', 'binary') 'rpmb write-data --output-format-version' = @('1', '2') + 'rpmb write-data --timeout' = @('infinite', '-1') 'rpmb write-data -o' = @('normal', 'json', 'binary') 'samsung vs-internal-log --output-format' = @('normal') 'samsung vs-internal-log --output-format-version' = @('1', '2') + 'samsung vs-internal-log --timeout' = @('infinite', '-1') 'samsung vs-internal-log -o' = @('normal') 'sanitize --output-format' = @('normal', 'json', 'binary') 'sanitize --output-format-version' = @('1', '2') 'sanitize --sanact' = @('exit-failure', 'start-block-erase', 'start-overwrite', 'start-crypto-erase', 'exit-media-verification') + 'sanitize --timeout' = @('infinite', '-1') 'sanitize -a' = @('exit-failure', 'start-block-erase', 'start-overwrite', 'start-crypto-erase', 'exit-media-verification') 'sanitize -o' = @('normal', 'json', 'binary') 'sanitize-log --output-format' = @('normal', 'json', 'binary') 'sanitize-log --output-format-version' = @('1', '2') + 'sanitize-log --timeout' = @('infinite', '-1') 'sanitize-log -o' = @('normal', 'json', 'binary') 'sanitize-ns --output-format' = @('normal', 'json', 'binary') 'sanitize-ns --output-format-version' = @('1', '2') 'sanitize-ns --sanact' = @('exit-failure', 'start-crypto-erase', 'exit-media-verification') + 'sanitize-ns --timeout' = @('infinite', '-1') 'sanitize-ns -a' = @('exit-failure', 'start-crypto-erase', 'exit-media-verification') 'sanitize-ns -o' = @('normal', 'json', 'binary') 'seagate clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'seagate clear-fw-activate-history --output-format-version' = @('1', '2') + 'seagate clear-fw-activate-history --timeout' = @('infinite', '-1') 'seagate clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'seagate clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'seagate clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'seagate clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'seagate clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'seagate cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'seagate cloud-SSD-plugin-version --output-format-version' = @('1', '2') + 'seagate cloud-SSD-plugin-version --timeout' = @('infinite', '-1') 'seagate cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'seagate get-ctrl-tele --namespace-id' = @('all', '-1') 'seagate get-ctrl-tele --output-format' = @('normal', 'json', 'binary') 'seagate get-ctrl-tele --output-format-version' = @('1', '2') + 'seagate get-ctrl-tele --timeout' = @('infinite', '-1') + 'seagate get-ctrl-tele -n' = @('all', '-1') 'seagate get-ctrl-tele -o' = @('normal', 'json', 'binary') + 'seagate get-host-tele --namespace-id' = @('all', '-1') 'seagate get-host-tele --output-format' = @('normal', 'json', 'binary') 'seagate get-host-tele --output-format-version' = @('1', '2') + 'seagate get-host-tele --timeout' = @('infinite', '-1') + 'seagate get-host-tele -n' = @('all', '-1') 'seagate get-host-tele -o' = @('normal', 'json', 'binary') 'seagate plugin-version --output-format' = @('normal', 'json', 'binary') 'seagate plugin-version --output-format-version' = @('1', '2') + 'seagate plugin-version --timeout' = @('infinite', '-1') 'seagate plugin-version -o' = @('normal', 'json', 'binary') 'seagate vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'seagate vs-fw-activate-history --output-format-version' = @('1', '2') + 'seagate vs-fw-activate-history --timeout' = @('infinite', '-1') 'seagate vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'seagate vs-internal-log --namespace-id' = @('all', '-1') 'seagate vs-internal-log --output-format' = @('normal', 'json', 'binary') 'seagate vs-internal-log --output-format-version' = @('1', '2') + 'seagate vs-internal-log --timeout' = @('infinite', '-1') + 'seagate vs-internal-log -n' = @('all', '-1') 'seagate vs-internal-log -o' = @('normal', 'json', 'binary') 'seagate vs-log-page-sup --output-format' = @('normal', 'json', 'binary') 'seagate vs-log-page-sup --output-format-version' = @('1', '2') + 'seagate vs-log-page-sup --timeout' = @('infinite', '-1') 'seagate vs-log-page-sup -o' = @('normal', 'json', 'binary') 'seagate vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'seagate vs-pcie-stats --output-format-version' = @('1', '2') + 'seagate vs-pcie-stats --timeout' = @('infinite', '-1') 'seagate vs-pcie-stats -o' = @('normal', 'json', 'binary') 'seagate vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'seagate vs-smart-add-log --output-format-version' = @('1', '2') + 'seagate vs-smart-add-log --timeout' = @('infinite', '-1') 'seagate vs-smart-add-log -o' = @('normal', 'json', 'binary') 'seagate vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'seagate vs-temperature-stats --output-format-version' = @('1', '2') + 'seagate vs-temperature-stats --timeout' = @('infinite', '-1') 'seagate vs-temperature-stats -o' = @('normal', 'json', 'binary') + 'security recv --namespace-id' = @('all', '-1') 'security recv --output-format' = @('normal', 'json', 'binary') 'security recv --output-format-version' = @('1', '2') + 'security recv --timeout' = @('infinite', '-1') + 'security recv -n' = @('all', '-1') 'security recv -o' = @('normal', 'json', 'binary') + 'security send --namespace-id' = @('all', '-1') 'security send --output-format' = @('normal', 'json', 'binary') 'security send --output-format-version' = @('1', '2') + 'security send --timeout' = @('infinite', '-1') + 'security send -n' = @('all', '-1') 'security send -o' = @('normal', 'json', 'binary') + 'security-recv --namespace-id' = @('all', '-1') 'security-recv --output-format' = @('normal', 'json', 'binary') 'security-recv --output-format-version' = @('1', '2') + 'security-recv --timeout' = @('infinite', '-1') + 'security-recv -n' = @('all', '-1') 'security-recv -o' = @('normal', 'json', 'binary') + 'security-send --namespace-id' = @('all', '-1') 'security-send --output-format' = @('normal', 'json', 'binary') 'security-send --output-format-version' = @('1', '2') + 'security-send --timeout' = @('infinite', '-1') + 'security-send -n' = @('all', '-1') 'security-send -o' = @('normal', 'json', 'binary') 'sed 1 --output-format' = @('normal', 'json', 'binary') 'sed 1 --output-format-version' = @('1', '2') + 'sed 1 --timeout' = @('infinite', '-1') 'sed 1 -o' = @('normal', 'json', 'binary') 'sed discover --output-format' = @('normal', 'json', 'binary') 'sed discover --output-format-version' = @('1', '2') + 'sed discover --timeout' = @('infinite', '-1') 'sed discover -o' = @('normal', 'json', 'binary') 'sed initialize --output-format' = @('normal', 'json', 'binary') 'sed initialize --output-format-version' = @('1', '2') + 'sed initialize --timeout' = @('infinite', '-1') 'sed initialize -o' = @('normal', 'json', 'binary') 'sed lock --output-format' = @('normal', 'json', 'binary') 'sed lock --output-format-version' = @('1', '2') + 'sed lock --timeout' = @('infinite', '-1') 'sed lock -o' = @('normal', 'json', 'binary') 'sed password --output-format' = @('normal', 'json', 'binary') 'sed password --output-format-version' = @('1', '2') + 'sed password --timeout' = @('infinite', '-1') 'sed password -o' = @('normal', 'json', 'binary') 'sed revert --output-format' = @('normal', 'json', 'binary') 'sed revert --output-format-version' = @('1', '2') + 'sed revert --timeout' = @('infinite', '-1') 'sed revert -o' = @('normal', 'json', 'binary') 'sed unlock --output-format' = @('normal', 'json', 'binary') 'sed unlock --output-format-version' = @('1', '2') + 'sed unlock --timeout' = @('infinite', '-1') 'sed unlock -o' = @('normal', 'json', 'binary') 'self-test-log --output-format' = @('normal', 'json', 'binary') 'self-test-log --output-format-version' = @('1', '2') + 'self-test-log --timeout' = @('infinite', '-1') 'self-test-log -o' = @('normal', 'json', 'binary') 'set-feature --feature-id' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') + 'set-feature --namespace-id' = @('all', '-1') 'set-feature --output-format' = @('normal', 'json', 'binary') 'set-feature --output-format-version' = @('1', '2') + 'set-feature --timeout' = @('infinite', '-1') 'set-feature -f' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') + 'set-feature -n' = @('all', '-1') 'set-feature -o' = @('normal', 'json', 'binary') 'set-property --output-format' = @('normal', 'json', 'binary') 'set-property --output-format-version' = @('1', '2') + 'set-property --timeout' = @('infinite', '-1') 'set-property -o' = @('normal', 'json', 'binary') 'set-reg --output-format' = @('normal', 'json', 'binary') 'set-reg --output-format-version' = @('1', '2') + 'set-reg --timeout' = @('infinite', '-1') 'set-reg -o' = @('normal', 'json', 'binary') 'sfx change-cap --output-format' = @('normal', 'json', 'binary') 'sfx change-cap --output-format-version' = @('1', '2') + 'sfx change-cap --timeout' = @('infinite', '-1') 'sfx change-cap -o' = @('normal', 'json', 'binary') 'sfx dump-evtlog --output-format' = @('normal', 'json', 'binary') 'sfx dump-evtlog --output-format-version' = @('1', '2') + 'sfx dump-evtlog --timeout' = @('infinite', '-1') 'sfx dump-evtlog -o' = @('normal', 'json', 'binary') 'sfx expand-cap --output-format' = @('normal', 'json', 'binary') 'sfx expand-cap --output-format-version' = @('1', '2') + 'sfx expand-cap --timeout' = @('infinite', '-1') 'sfx expand-cap -o' = @('normal', 'json', 'binary') 'sfx get-bad-block --output-format' = @('normal', 'json', 'binary') 'sfx get-bad-block --output-format-version' = @('1', '2') + 'sfx get-bad-block --timeout' = @('infinite', '-1') 'sfx get-bad-block -o' = @('normal', 'json', 'binary') + 'sfx get-feature --namespace-id' = @('all', '-1') 'sfx get-feature --output-format' = @('normal', 'json', 'binary') 'sfx get-feature --output-format-version' = @('1', '2') + 'sfx get-feature --timeout' = @('infinite', '-1') + 'sfx get-feature -n' = @('all', '-1') 'sfx get-feature -o' = @('normal', 'json', 'binary') 'sfx lat-stats --output-format' = @('normal', 'json', 'binary') 'sfx lat-stats --output-format-version' = @('1', '2') + 'sfx lat-stats --timeout' = @('infinite', '-1') 'sfx lat-stats -o' = @('normal', 'json', 'binary') 'sfx query-cap --output-format' = @('normal', 'json', 'binary') 'sfx query-cap --output-format-version' = @('1', '2') + 'sfx query-cap --timeout' = @('infinite', '-1') 'sfx query-cap -o' = @('normal', 'json', 'binary') + 'sfx set-feature --namespace-id' = @('all', '-1') 'sfx set-feature --output-format' = @('normal', 'json', 'binary') 'sfx set-feature --output-format-version' = @('1', '2') + 'sfx set-feature --timeout' = @('infinite', '-1') + 'sfx set-feature -n' = @('all', '-1') 'sfx set-feature -o' = @('normal', 'json', 'binary') + 'sfx smart-log-add --namespace-id' = @('all', '-1') 'sfx smart-log-add --output-format' = @('normal', 'json', 'binary') 'sfx smart-log-add --output-format-version' = @('1', '2') + 'sfx smart-log-add --timeout' = @('infinite', '-1') + 'sfx smart-log-add -n' = @('all', '-1') 'sfx smart-log-add -o' = @('normal', 'json', 'binary') 'sfx status --output-format' = @('normal', 'json', 'binary') 'sfx status --output-format-version' = @('1', '2') + 'sfx status --timeout' = @('infinite', '-1') 'sfx status -o' = @('normal', 'json', 'binary') + 'shannon get-additional-feature --namespace-id' = @('all', '-1') 'shannon get-additional-feature --output-format' = @('normal', 'json', 'binary') 'shannon get-additional-feature --output-format-version' = @('1', '2') 'shannon get-additional-feature --sel' = @('0', '1', '2', '3') + 'shannon get-additional-feature --timeout' = @('infinite', '-1') + 'shannon get-additional-feature -n' = @('all', '-1') 'shannon get-additional-feature -o' = @('normal', 'json', 'binary') 'shannon get-additional-feature -s' = @('0', '1', '2', '3') 'shannon id-ctrl --output-format' = @('normal', 'json', 'binary') 'shannon id-ctrl --output-format-version' = @('1', '2') + 'shannon id-ctrl --timeout' = @('infinite', '-1') 'shannon id-ctrl -o' = @('normal', 'json', 'binary') + 'shannon set-additioal-feature --namespace-id' = @('all', '-1') 'shannon set-additioal-feature --output-format' = @('normal', 'json', 'binary') 'shannon set-additioal-feature --output-format-version' = @('1', '2') + 'shannon set-additioal-feature --timeout' = @('infinite', '-1') + 'shannon set-additioal-feature -n' = @('all', '-1') 'shannon set-additioal-feature -o' = @('normal', 'json', 'binary') + 'shannon smart-log-add --namespace-id' = @('all', '-1') 'shannon smart-log-add --output-format' = @('normal', 'json', 'binary') 'shannon smart-log-add --output-format-version' = @('1', '2') + 'shannon smart-log-add --timeout' = @('infinite', '-1') + 'shannon smart-log-add -n' = @('all', '-1') 'shannon smart-log-add -o' = @('normal', 'json', 'binary') 'show-hostnqn --output-format' = @('normal', 'json', 'binary') 'show-hostnqn --output-format-version' = @('1', '2') + 'show-hostnqn --timeout' = @('infinite', '-1') 'show-hostnqn -o' = @('normal', 'json', 'binary') 'show-regs --output-format' = @('normal', 'json', 'binary') 'show-regs --output-format-version' = @('1', '2') + 'show-regs --timeout' = @('infinite', '-1') 'show-regs -o' = @('normal', 'json', 'binary') 'show-topology --output-format' = @('normal', 'json', 'tabular') 'show-topology --output-format-version' = @('1', '2') + 'show-topology --timeout' = @('infinite', '-1') 'show-topology -o' = @('normal', 'json', 'tabular') + 'smart-log --namespace-id' = @('all', '-1') 'smart-log --output-format' = @('normal', 'json', 'binary') 'smart-log --output-format-version' = @('1', '2') + 'smart-log --timeout' = @('infinite', '-1') + 'smart-log -n' = @('all', '-1') 'smart-log -o' = @('normal', 'json', 'binary') 'sndk capabilities --output-format' = @('normal', 'json', 'binary') 'sndk capabilities --output-format-version' = @('1', '2') + 'sndk capabilities --timeout' = @('infinite', '-1') 'sndk capabilities -o' = @('normal', 'json', 'binary') 'sndk clear-assert-dump --output-format' = @('normal', 'json', 'binary') 'sndk clear-assert-dump --output-format-version' = @('1', '2') + 'sndk clear-assert-dump --timeout' = @('infinite', '-1') 'sndk clear-assert-dump -o' = @('normal', 'json', 'binary') 'sndk clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'sndk clear-fw-activate-history --output-format-version' = @('1', '2') + 'sndk clear-fw-activate-history --timeout' = @('infinite', '-1') 'sndk clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'sndk clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'sndk clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'sndk clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'sndk clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'sndk cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'sndk cloud-SSD-plugin-version --output-format-version' = @('1', '2') + 'sndk cloud-SSD-plugin-version --timeout' = @('infinite', '-1') 'sndk cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'sndk cloud-boot-SSD-version --namespace-id' = @('all', '-1') 'sndk cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'sndk cloud-boot-SSD-version --output-format-version' = @('1', '2') + 'sndk cloud-boot-SSD-version --timeout' = @('infinite', '-1') + 'sndk cloud-boot-SSD-version -n' = @('all', '-1') 'sndk cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') 'sndk cu-smart-log --output-format' = @('normal', 'json', 'binary') 'sndk cu-smart-log --output-format-version' = @('1', '2') + 'sndk cu-smart-log --timeout' = @('infinite', '-1') 'sndk cu-smart-log -o' = @('normal', 'json', 'binary') 'sndk drive-resize --output-format' = @('normal', 'json', 'binary') 'sndk drive-resize --output-format-version' = @('1', '2') + 'sndk drive-resize --timeout' = @('infinite', '-1') 'sndk drive-resize -o' = @('normal', 'json', 'binary') 'sndk get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary') 'sndk get-dev-capabilities-log --output-format-version' = @('1', '2') + 'sndk get-dev-capabilities-log --timeout' = @('infinite', '-1') 'sndk get-dev-capabilities-log -o' = @('normal', 'json', 'binary') 'sndk get-drive-status --output-format' = @('normal', 'json', 'binary') 'sndk get-drive-status --output-format-version' = @('1', '2') + 'sndk get-drive-status --timeout' = @('infinite', '-1') 'sndk get-drive-status -o' = @('normal', 'json', 'binary') 'sndk get-error-recovery-log --output-format' = @('normal', 'json', 'binary') 'sndk get-error-recovery-log --output-format-version' = @('1', '2') + 'sndk get-error-recovery-log --timeout' = @('infinite', '-1') 'sndk get-error-recovery-log -o' = @('normal', 'json', 'binary') 'sndk get-latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'sndk get-latency-monitor-log --output-format-version' = @('1', '2') + 'sndk get-latency-monitor-log --timeout' = @('infinite', '-1') 'sndk get-latency-monitor-log -o' = @('normal', 'json', 'binary') 'sndk get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'sndk get-unsupported-reqs-log --output-format-version' = @('1', '2') + 'sndk get-unsupported-reqs-log --timeout' = @('infinite', '-1') 'sndk get-unsupported-reqs-log -o' = @('normal', 'json', 'binary') 'sndk log-page-directory --output-format' = @('normal', 'json', 'binary') 'sndk log-page-directory --output-format-version' = @('1', '2') + 'sndk log-page-directory --timeout' = @('infinite', '-1') 'sndk log-page-directory -o' = @('normal', 'json', 'binary') + 'sndk namespace-resize --namespace-id' = @('all', '-1') 'sndk namespace-resize --output-format' = @('normal', 'json', 'binary') 'sndk namespace-resize --output-format-version' = @('1', '2') + 'sndk namespace-resize --timeout' = @('infinite', '-1') + 'sndk namespace-resize -n' = @('all', '-1') 'sndk namespace-resize -o' = @('normal', 'json', 'binary') 'sndk set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'sndk set-latency-monitor-feature --output-format-version' = @('1', '2') + 'sndk set-latency-monitor-feature --timeout' = @('infinite', '-1') 'sndk set-latency-monitor-feature -o' = @('normal', 'json', 'binary') + 'sndk vs-cloud-log --namespace-id' = @('all', '-1') 'sndk vs-cloud-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-cloud-log --output-format-version' = @('1', '2') + 'sndk vs-cloud-log --timeout' = @('infinite', '-1') + 'sndk vs-cloud-log -n' = @('all', '-1') 'sndk vs-cloud-log -o' = @('normal', 'json', 'binary') + 'sndk vs-device-waf --namespace-id' = @('all', '-1') 'sndk vs-device-waf --output-format' = @('normal', 'json', 'binary') 'sndk vs-device-waf --output-format-version' = @('1', '2') + 'sndk vs-device-waf --timeout' = @('infinite', '-1') + 'sndk vs-device-waf -n' = @('all', '-1') 'sndk vs-device-waf -o' = @('normal', 'json', 'binary') 'sndk vs-drive-info --output-format' = @('normal', 'json', 'binary') 'sndk vs-drive-info --output-format-version' = @('1', '2') + 'sndk vs-drive-info --timeout' = @('infinite', '-1') 'sndk vs-drive-info -o' = @('normal', 'json', 'binary') 'sndk vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary') 'sndk vs-error-reason-identifier --output-format-version' = @('1', '2') + 'sndk vs-error-reason-identifier --timeout' = @('infinite', '-1') 'sndk vs-error-reason-identifier -o' = @('normal', 'json', 'binary') 'sndk vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'sndk vs-fw-activate-history --output-format-version' = @('1', '2') + 'sndk vs-fw-activate-history --timeout' = @('infinite', '-1') 'sndk vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'sndk vs-hw-rev-log --namespace-id' = @('all', '-1') 'sndk vs-hw-rev-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-hw-rev-log --output-format-version' = @('1', '2') + 'sndk vs-hw-rev-log --timeout' = @('infinite', '-1') + 'sndk vs-hw-rev-log -n' = @('all', '-1') 'sndk vs-hw-rev-log -o' = @('normal', 'json', 'binary') 'sndk vs-internal-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-internal-log --output-format-version' = @('1', '2') + 'sndk vs-internal-log --timeout' = @('infinite', '-1') 'sndk vs-internal-log -o' = @('normal', 'json', 'binary') 'sndk vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-nand-stats --output-format-version' = @('1', '2') + 'sndk vs-nand-stats --timeout' = @('infinite', '-1') 'sndk vs-nand-stats -o' = @('normal', 'json', 'binary') 'sndk vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-pcie-stats --output-format-version' = @('1', '2') + 'sndk vs-pcie-stats --timeout' = @('infinite', '-1') 'sndk vs-pcie-stats -o' = @('normal', 'json', 'binary') + 'sndk vs-smart-add-log --namespace-id' = @('all', '-1') 'sndk vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-smart-add-log --output-format-version' = @('1', '2') + 'sndk vs-smart-add-log --timeout' = @('infinite', '-1') + 'sndk vs-smart-add-log -n' = @('all', '-1') 'sndk vs-smart-add-log -o' = @('normal', 'json', 'binary') 'sndk vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'sndk vs-telemetry-controller-option --output-format-version' = @('1', '2') + 'sndk vs-telemetry-controller-option --timeout' = @('infinite', '-1') 'sndk vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') 'sndk vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-temperature-stats --output-format-version' = @('1', '2') + 'sndk vs-temperature-stats --timeout' = @('infinite', '-1') 'sndk vs-temperature-stats -o' = @('normal', 'json', 'binary') 'solidigm clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'solidigm clear-fw-activate-history --output-format-version' = @('1', '2') + 'solidigm clear-fw-activate-history --timeout' = @('infinite', '-1') 'solidigm clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'solidigm clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'solidigm clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'solidigm clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'solidigm clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'solidigm cloud-SSDplugin-version --output-format' = @('normal', 'json', 'binary') 'solidigm cloud-SSDplugin-version --output-format-version' = @('1', '2') + 'solidigm cloud-SSDplugin-version --timeout' = @('infinite', '-1') 'solidigm cloud-SSDplugin-version -o' = @('normal', 'json', 'binary') 'solidigm garbage-collect-log --output-format' = @('normal', 'json', 'binary') 'solidigm garbage-collect-log --output-format-version' = @('1', '2') + 'solidigm garbage-collect-log --timeout' = @('infinite', '-1') 'solidigm garbage-collect-log -o' = @('normal', 'json', 'binary') 'solidigm id-ctrl --output-format' = @('normal', 'json', 'binary') 'solidigm id-ctrl --output-format-version' = @('1', '2') + 'solidigm id-ctrl --timeout' = @('infinite', '-1') 'solidigm id-ctrl -o' = @('normal', 'json', 'binary') 'solidigm latency-tracking-log --output-format' = @('normal', 'json', 'binary') 'solidigm latency-tracking-log --output-format-version' = @('1', '2') + 'solidigm latency-tracking-log --timeout' = @('infinite', '-1') 'solidigm latency-tracking-log -o' = @('normal', 'json', 'binary') 'solidigm log-page-directory --output-format' = @('normal', 'json', 'binary') 'solidigm log-page-directory --output-format-version' = @('1', '2') + 'solidigm log-page-directory --timeout' = @('infinite', '-1') 'solidigm log-page-directory -o' = @('normal', 'json', 'binary') 'solidigm market-log --output-format' = @('normal', 'json', 'binary') 'solidigm market-log --output-format-version' = @('1', '2') + 'solidigm market-log --timeout' = @('infinite', '-1') 'solidigm market-log -o' = @('normal', 'json', 'binary') 'solidigm parse-telemetry-log --output-format' = @('normal', 'json', 'binary') 'solidigm parse-telemetry-log --output-format-version' = @('1', '2') + 'solidigm parse-telemetry-log --timeout' = @('infinite', '-1') 'solidigm parse-telemetry-log -o' = @('normal', 'json', 'binary') + 'solidigm smart-log-add --namespace-id' = @('all', '-1') 'solidigm smart-log-add --output-format' = @('normal', 'json', 'binary') 'solidigm smart-log-add --output-format-version' = @('1', '2') + 'solidigm smart-log-add --timeout' = @('infinite', '-1') + 'solidigm smart-log-add -n' = @('all', '-1') 'solidigm smart-log-add -o' = @('normal', 'json', 'binary') 'solidigm temp-stats --output-format' = @('normal', 'json', 'binary') 'solidigm temp-stats --output-format-version' = @('1', '2') + 'solidigm temp-stats --timeout' = @('infinite', '-1') 'solidigm temp-stats -o' = @('normal', 'json', 'binary') 'solidigm vs-drive-info --output-format' = @('normal', 'json') 'solidigm vs-drive-info --output-format-version' = @('1', '2') + 'solidigm vs-drive-info --timeout' = @('infinite', '-1') 'solidigm vs-drive-info -o' = @('normal', 'json') 'solidigm vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'solidigm vs-fw-activate-history --output-format-version' = @('1', '2') + 'solidigm vs-fw-activate-history --timeout' = @('infinite', '-1') 'solidigm vs-fw-activate-history -o' = @('normal', 'json', 'binary') 'solidigm vs-internal-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-internal-log --output-format-version' = @('1', '2') + 'solidigm vs-internal-log --timeout' = @('infinite', '-1') 'solidigm vs-internal-log -o' = @('normal', 'json', 'binary') 'solidigm vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-smart-add-log --output-format-version' = @('1', '2') + 'solidigm vs-smart-add-log --timeout' = @('infinite', '-1') 'solidigm vs-smart-add-log -o' = @('normal', 'json', 'binary') 'solidigm workload-tracker --output-format' = @('normal', 'json', 'binary') 'solidigm workload-tracker --output-format-version' = @('1', '2') + 'solidigm workload-tracker --timeout' = @('infinite', '-1') 'solidigm workload-tracker -o' = @('normal', 'json', 'binary') + 'ssstc smart-log-add --namespace-id' = @('all', '-1') 'ssstc smart-log-add --output-format' = @('normal', 'json', 'binary') 'ssstc smart-log-add --output-format-version' = @('1', '2') + 'ssstc smart-log-add --timeout' = @('infinite', '-1') + 'ssstc smart-log-add -n' = @('all', '-1') 'ssstc smart-log-add -o' = @('normal', 'json', 'binary') 'subsystem-reset --output-format' = @('normal', 'json', 'binary') 'subsystem-reset --output-format-version' = @('1', '2') + 'subsystem-reset --timeout' = @('infinite', '-1') 'subsystem-reset -o' = @('normal', 'json', 'binary') 'supported-cap-config-log --output-format' = @('normal', 'json', 'binary') 'supported-cap-config-log --output-format-version' = @('1', '2') + 'supported-cap-config-log --timeout' = @('infinite', '-1') 'supported-cap-config-log -o' = @('normal', 'json', 'binary') 'supported-log-pages --output-format' = @('normal', 'json', 'binary') 'supported-log-pages --output-format-version' = @('1', '2') + 'supported-log-pages --timeout' = @('infinite', '-1') 'supported-log-pages -o' = @('normal', 'json', 'binary') 'telemetry-log --output-format' = @('normal', 'json', 'binary') 'telemetry-log --output-format-version' = @('1', '2') + 'telemetry-log --timeout' = @('infinite', '-1') 'telemetry-log -o' = @('normal', 'json', 'binary') 'top --output-format' = @('normal') 'top --output-format-version' = @('1', '2') + 'top --timeout' = @('infinite', '-1') 'top -o' = @('normal') 'toshiba clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'toshiba clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'toshiba clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'toshiba clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'toshiba vs-internal-log --output-format' = @('normal', 'json', 'binary') 'toshiba vs-internal-log --output-format-version' = @('1', '2') + 'toshiba vs-internal-log --timeout' = @('infinite', '-1') 'toshiba vs-internal-log -o' = @('normal', 'json', 'binary') + 'toshiba vs-smart-add-log --namespace-id' = @('all', '-1') 'toshiba vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'toshiba vs-smart-add-log --output-format-version' = @('1', '2') + 'toshiba vs-smart-add-log --timeout' = @('infinite', '-1') + 'toshiba vs-smart-add-log -n' = @('all', '-1') 'toshiba vs-smart-add-log -o' = @('normal', 'json', 'binary') 'transcend badblock --output-format' = @('normal', 'json', 'binary') 'transcend badblock --output-format-version' = @('1', '2') + 'transcend badblock --timeout' = @('infinite', '-1') 'transcend badblock -o' = @('normal', 'json', 'binary') 'transcend healthvalue --output-format' = @('normal', 'json', 'binary') 'transcend healthvalue --output-format-version' = @('1', '2') + 'transcend healthvalue --timeout' = @('infinite', '-1') 'transcend healthvalue -o' = @('normal', 'json', 'binary') + 'verify --namespace-id' = @('all', '-1') 'verify --output-format' = @('normal', 'json', 'binary') 'verify --output-format-version' = @('1', '2') + 'verify --timeout' = @('infinite', '-1') + 'verify -n' = @('all', '-1') 'verify -o' = @('normal', 'json', 'binary') 'virt-mgmt --output-format' = @('normal', 'json', 'binary') 'virt-mgmt --output-format-version' = @('1', '2') + 'virt-mgmt --timeout' = @('infinite', '-1') 'virt-mgmt -o' = @('normal', 'json', 'binary') 'virtium save-smart-to-vtview-log --output-format' = @('normal', 'json', 'binary') 'virtium save-smart-to-vtview-log --output-format-version' = @('1', '2') + 'virtium save-smart-to-vtview-log --timeout' = @('infinite', '-1') 'virtium save-smart-to-vtview-log -o' = @('normal', 'json', 'binary') 'virtium show-identify --output-format' = @('normal', 'json', 'binary') 'virtium show-identify --output-format-version' = @('1', '2') + 'virtium show-identify --timeout' = @('infinite', '-1') 'virtium show-identify -o' = @('normal', 'json', 'binary') 'wdc cap-diag --output-format' = @('normal', 'json', 'binary') 'wdc cap-diag --output-format-version' = @('1', '2') + 'wdc cap-diag --timeout' = @('infinite', '-1') 'wdc cap-diag -o' = @('normal', 'json', 'binary') 'wdc capabilities --output-format' = @('normal', 'json', 'binary') 'wdc capabilities --output-format-version' = @('1', '2') + 'wdc capabilities --timeout' = @('infinite', '-1') 'wdc capabilities -o' = @('normal', 'json', 'binary') 'wdc clear-assert-dump --output-format' = @('normal', 'json', 'binary') 'wdc clear-assert-dump --output-format-version' = @('1', '2') + 'wdc clear-assert-dump --timeout' = @('infinite', '-1') 'wdc clear-assert-dump -o' = @('normal', 'json', 'binary') 'wdc clear-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'wdc clear-fw-activate-history --output-format-version' = @('1', '2') + 'wdc clear-fw-activate-history --timeout' = @('infinite', '-1') 'wdc clear-fw-activate-history -o' = @('normal', 'json', 'binary') 'wdc clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'wdc clear-pcie-correctable-errors --output-format-version' = @('1', '2') + 'wdc clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'wdc clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') 'wdc cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary') 'wdc cloud-SSD-plugin-version --output-format-version' = @('1', '2') + 'wdc cloud-SSD-plugin-version --timeout' = @('infinite', '-1') 'wdc cloud-SSD-plugin-version -o' = @('normal', 'json', 'binary') + 'wdc cloud-boot-SSD-version --namespace-id' = @('all', '-1') 'wdc cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary') 'wdc cloud-boot-SSD-version --output-format-version' = @('1', '2') + 'wdc cloud-boot-SSD-version --timeout' = @('infinite', '-1') + 'wdc cloud-boot-SSD-version -n' = @('all', '-1') 'wdc cloud-boot-SSD-version -o' = @('normal', 'json', 'binary') 'wdc cu-smart-log --output-format' = @('normal', 'json', 'binary') 'wdc cu-smart-log --output-format-version' = @('1', '2') + 'wdc cu-smart-log --timeout' = @('infinite', '-1') 'wdc cu-smart-log -o' = @('normal', 'json', 'binary') 'wdc drive-essentials --output-format' = @('normal', 'json', 'binary') 'wdc drive-essentials --output-format-version' = @('1', '2') + 'wdc drive-essentials --timeout' = @('infinite', '-1') 'wdc drive-essentials -o' = @('normal', 'json', 'binary') 'wdc drive-log --output-format' = @('normal', 'json', 'binary') 'wdc drive-log --output-format-version' = @('1', '2') + 'wdc drive-log --timeout' = @('infinite', '-1') 'wdc drive-log -o' = @('normal', 'json', 'binary') 'wdc drive-resize --output-format' = @('normal', 'json', 'binary') 'wdc drive-resize --output-format-version' = @('1', '2') + 'wdc drive-resize --timeout' = @('infinite', '-1') 'wdc drive-resize -o' = @('normal', 'json', 'binary') 'wdc enc-get-log --output-format' = @('normal', 'json', 'binary') 'wdc enc-get-log --output-format-version' = @('1', '2') + 'wdc enc-get-log --timeout' = @('infinite', '-1') 'wdc enc-get-log -o' = @('normal', 'json', 'binary') 'wdc get-crash-dump --output-format' = @('normal', 'json', 'binary') 'wdc get-crash-dump --output-format-version' = @('1', '2') + 'wdc get-crash-dump --timeout' = @('infinite', '-1') 'wdc get-crash-dump -o' = @('normal', 'json', 'binary') 'wdc get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary') 'wdc get-dev-capabilities-log --output-format-version' = @('1', '2') + 'wdc get-dev-capabilities-log --timeout' = @('infinite', '-1') 'wdc get-dev-capabilities-log -o' = @('normal', 'json', 'binary') 'wdc get-drive-status --output-format' = @('normal', 'json', 'binary') 'wdc get-drive-status --output-format-version' = @('1', '2') + 'wdc get-drive-status --timeout' = @('infinite', '-1') 'wdc get-drive-status -o' = @('normal', 'json', 'binary') 'wdc get-error-recovery-log --output-format' = @('normal', 'json', 'binary') 'wdc get-error-recovery-log --output-format-version' = @('1', '2') + 'wdc get-error-recovery-log --timeout' = @('infinite', '-1') 'wdc get-error-recovery-log -o' = @('normal', 'json', 'binary') 'wdc get-latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'wdc get-latency-monitor-log --output-format-version' = @('1', '2') + 'wdc get-latency-monitor-log --timeout' = @('infinite', '-1') 'wdc get-latency-monitor-log -o' = @('normal', 'json', 'binary') 'wdc get-pfail-dump --output-format' = @('normal', 'json', 'binary') 'wdc get-pfail-dump --output-format-version' = @('1', '2') + 'wdc get-pfail-dump --timeout' = @('infinite', '-1') 'wdc get-pfail-dump -o' = @('normal', 'json', 'binary') 'wdc get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary') 'wdc get-unsupported-reqs-log --output-format-version' = @('1', '2') + 'wdc get-unsupported-reqs-log --timeout' = @('infinite', '-1') 'wdc get-unsupported-reqs-log -o' = @('normal', 'json', 'binary') 'wdc id-ctrl --output-format' = @('normal', 'json', 'binary') 'wdc id-ctrl --output-format-version' = @('1', '2') + 'wdc id-ctrl --timeout' = @('infinite', '-1') 'wdc id-ctrl -o' = @('normal', 'json', 'binary') 'wdc log-page-directory --output-format' = @('normal', 'json', 'binary') 'wdc log-page-directory --output-format-version' = @('1', '2') + 'wdc log-page-directory --timeout' = @('infinite', '-1') 'wdc log-page-directory -o' = @('normal', 'json', 'binary') + 'wdc namespace-resize --namespace-id' = @('all', '-1') 'wdc namespace-resize --output-format' = @('normal', 'json', 'binary') 'wdc namespace-resize --output-format-version' = @('1', '2') + 'wdc namespace-resize --timeout' = @('infinite', '-1') + 'wdc namespace-resize -n' = @('all', '-1') 'wdc namespace-resize -o' = @('normal', 'json', 'binary') 'wdc purge --output-format' = @('normal', 'json', 'binary') 'wdc purge --output-format-version' = @('1', '2') + 'wdc purge --timeout' = @('infinite', '-1') 'wdc purge -o' = @('normal', 'json', 'binary') 'wdc purge-monitor --output-format' = @('normal', 'json', 'binary') 'wdc purge-monitor --output-format-version' = @('1', '2') + 'wdc purge-monitor --timeout' = @('infinite', '-1') 'wdc purge-monitor -o' = @('normal', 'json', 'binary') 'wdc set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary') 'wdc set-latency-monitor-feature --output-format-version' = @('1', '2') + 'wdc set-latency-monitor-feature --timeout' = @('infinite', '-1') 'wdc set-latency-monitor-feature -o' = @('normal', 'json', 'binary') + 'wdc vs-cloud-log --namespace-id' = @('all', '-1') 'wdc vs-cloud-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-cloud-log --output-format-version' = @('1', '2') + 'wdc vs-cloud-log --timeout' = @('infinite', '-1') + 'wdc vs-cloud-log -n' = @('all', '-1') 'wdc vs-cloud-log -o' = @('normal', 'json', 'binary') + 'wdc vs-device-waf --namespace-id' = @('all', '-1') 'wdc vs-device-waf --output-format' = @('normal', 'json', 'binary') 'wdc vs-device-waf --output-format-version' = @('1', '2') + 'wdc vs-device-waf --timeout' = @('infinite', '-1') + 'wdc vs-device-waf -n' = @('all', '-1') 'wdc vs-device-waf -o' = @('normal', 'json', 'binary') 'wdc vs-drive-info --output-format' = @('normal', 'json', 'binary') 'wdc vs-drive-info --output-format-version' = @('1', '2') + 'wdc vs-drive-info --timeout' = @('infinite', '-1') 'wdc vs-drive-info -o' = @('normal', 'json', 'binary') 'wdc vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary') 'wdc vs-error-reason-identifier --output-format-version' = @('1', '2') + 'wdc vs-error-reason-identifier --timeout' = @('infinite', '-1') 'wdc vs-error-reason-identifier -o' = @('normal', 'json', 'binary') 'wdc vs-fw-activate-history --output-format' = @('normal', 'json', 'binary') 'wdc vs-fw-activate-history --output-format-version' = @('1', '2') + 'wdc vs-fw-activate-history --timeout' = @('infinite', '-1') 'wdc vs-fw-activate-history -o' = @('normal', 'json', 'binary') + 'wdc vs-hw-rev-log --namespace-id' = @('all', '-1') 'wdc vs-hw-rev-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-hw-rev-log --output-format-version' = @('1', '2') + 'wdc vs-hw-rev-log --timeout' = @('infinite', '-1') + 'wdc vs-hw-rev-log -n' = @('all', '-1') 'wdc vs-hw-rev-log -o' = @('normal', 'json', 'binary') 'wdc vs-internal-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-internal-log --output-format-version' = @('1', '2') + 'wdc vs-internal-log --timeout' = @('infinite', '-1') 'wdc vs-internal-log -o' = @('normal', 'json', 'binary') 'wdc vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-nand-stats --output-format-version' = @('1', '2') + 'wdc vs-nand-stats --timeout' = @('infinite', '-1') 'wdc vs-nand-stats -o' = @('normal', 'json', 'binary') 'wdc vs-pcie-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-pcie-stats --output-format-version' = @('1', '2') + 'wdc vs-pcie-stats --timeout' = @('infinite', '-1') 'wdc vs-pcie-stats -o' = @('normal', 'json', 'binary') + 'wdc vs-smart-add-log --namespace-id' = @('all', '-1') 'wdc vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-smart-add-log --output-format-version' = @('1', '2') + 'wdc vs-smart-add-log --timeout' = @('infinite', '-1') + 'wdc vs-smart-add-log -n' = @('all', '-1') 'wdc vs-smart-add-log -o' = @('normal', 'json', 'binary') 'wdc vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'wdc vs-telemetry-controller-option --output-format-version' = @('1', '2') + 'wdc vs-telemetry-controller-option --timeout' = @('infinite', '-1') 'wdc vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') 'wdc vs-temperature-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-temperature-stats --output-format-version' = @('1', '2') + 'wdc vs-temperature-stats --timeout' = @('infinite', '-1') 'wdc vs-temperature-stats -o' = @('normal', 'json', 'binary') + 'write --namespace-id' = @('all', '-1') 'write --output-format' = @('normal', 'json', 'binary') 'write --output-format-version' = @('1', '2') + 'write --timeout' = @('infinite', '-1') + 'write -n' = @('all', '-1') 'write -o' = @('normal', 'json', 'binary') + 'write-uncor --namespace-id' = @('all', '-1') 'write-uncor --output-format' = @('normal', 'json', 'binary') 'write-uncor --output-format-version' = @('1', '2') + 'write-uncor --timeout' = @('infinite', '-1') + 'write-uncor -n' = @('all', '-1') 'write-uncor -o' = @('normal', 'json', 'binary') + 'write-zeroes --namespace-id' = @('all', '-1') 'write-zeroes --output-format' = @('normal', 'json', 'binary') 'write-zeroes --output-format-version' = @('1', '2') + 'write-zeroes --timeout' = @('infinite', '-1') + 'write-zeroes -n' = @('all', '-1') 'write-zeroes -o' = @('normal', 'json', 'binary') + 'ymtc smart-log-add --namespace-id' = @('all', '-1') 'ymtc smart-log-add --output-format' = @('normal', 'json', 'binary') 'ymtc smart-log-add --output-format-version' = @('1', '2') + 'ymtc smart-log-add --timeout' = @('infinite', '-1') + 'ymtc smart-log-add -n' = @('all', '-1') 'ymtc smart-log-add -o' = @('normal', 'json', 'binary') + 'zns changed-zone-list --namespace-id' = @('all', '-1') 'zns changed-zone-list --output-format' = @('normal', 'json', 'binary') 'zns changed-zone-list --output-format-version' = @('1', '2') + 'zns changed-zone-list --timeout' = @('infinite', '-1') + 'zns changed-zone-list -n' = @('all', '-1') 'zns changed-zone-list -o' = @('normal', 'json', 'binary') + 'zns close-zone --namespace-id' = @('all', '-1') 'zns close-zone --output-format' = @('normal', 'json', 'binary') 'zns close-zone --output-format-version' = @('1', '2') + 'zns close-zone --timeout' = @('infinite', '-1') + 'zns close-zone -n' = @('all', '-1') 'zns close-zone -o' = @('normal', 'json', 'binary') + 'zns finish-zone --namespace-id' = @('all', '-1') 'zns finish-zone --output-format' = @('normal', 'json', 'binary') 'zns finish-zone --output-format-version' = @('1', '2') + 'zns finish-zone --timeout' = @('infinite', '-1') + 'zns finish-zone -n' = @('all', '-1') 'zns finish-zone -o' = @('normal', 'json', 'binary') 'zns id-ctrl --output-format' = @('normal', 'json', 'binary') 'zns id-ctrl --output-format-version' = @('1', '2') + 'zns id-ctrl --timeout' = @('infinite', '-1') 'zns id-ctrl -o' = @('normal', 'json', 'binary') + 'zns id-ns --namespace-id' = @('all', '-1') 'zns id-ns --output-format' = @('normal', 'json', 'binary') 'zns id-ns --output-format-version' = @('1', '2') + 'zns id-ns --timeout' = @('infinite', '-1') + 'zns id-ns -n' = @('all', '-1') 'zns id-ns -o' = @('normal', 'json', 'binary') 'zns list --output-format' = @('normal') 'zns list --output-format-version' = @('1', '2') + 'zns list --timeout' = @('infinite', '-1') 'zns list -o' = @('normal') + 'zns offline-zone --namespace-id' = @('all', '-1') 'zns offline-zone --output-format' = @('normal', 'json', 'binary') 'zns offline-zone --output-format-version' = @('1', '2') + 'zns offline-zone --timeout' = @('infinite', '-1') + 'zns offline-zone -n' = @('all', '-1') 'zns offline-zone -o' = @('normal', 'json', 'binary') + 'zns open-zone --namespace-id' = @('all', '-1') 'zns open-zone --output-format' = @('normal', 'json', 'binary') 'zns open-zone --output-format-version' = @('1', '2') + 'zns open-zone --timeout' = @('infinite', '-1') + 'zns open-zone -n' = @('all', '-1') 'zns open-zone -o' = @('normal', 'json', 'binary') + 'zns report-zones --namespace-id' = @('all', '-1') 'zns report-zones --output-format' = @('normal', 'json', 'binary') 'zns report-zones --output-format-version' = @('1', '2') + 'zns report-zones --timeout' = @('infinite', '-1') + 'zns report-zones -n' = @('all', '-1') 'zns report-zones -o' = @('normal', 'json', 'binary') + 'zns reset-zone --namespace-id' = @('all', '-1') 'zns reset-zone --output-format' = @('normal', 'json', 'binary') 'zns reset-zone --output-format-version' = @('1', '2') + 'zns reset-zone --timeout' = @('infinite', '-1') + 'zns reset-zone -n' = @('all', '-1') 'zns reset-zone -o' = @('normal', 'json', 'binary') + 'zns set-zone-desc --namespace-id' = @('all', '-1') 'zns set-zone-desc --output-format' = @('normal', 'json', 'binary') 'zns set-zone-desc --output-format-version' = @('1', '2') + 'zns set-zone-desc --timeout' = @('infinite', '-1') + 'zns set-zone-desc -n' = @('all', '-1') 'zns set-zone-desc -o' = @('normal', 'json', 'binary') + 'zns zone-append --namespace-id' = @('all', '-1') 'zns zone-append --output-format' = @('normal', 'json', 'binary') 'zns zone-append --output-format-version' = @('1', '2') + 'zns zone-append --timeout' = @('infinite', '-1') + 'zns zone-append -n' = @('all', '-1') 'zns zone-append -o' = @('normal', 'json', 'binary') + 'zns zone-mgmt-recv --namespace-id' = @('all', '-1') 'zns zone-mgmt-recv --output-format' = @('normal', 'json', 'binary') 'zns zone-mgmt-recv --output-format-version' = @('1', '2') + 'zns zone-mgmt-recv --timeout' = @('infinite', '-1') + 'zns zone-mgmt-recv -n' = @('all', '-1') 'zns zone-mgmt-recv -o' = @('normal', 'json', 'binary') + 'zns zone-mgmt-send --namespace-id' = @('all', '-1') 'zns zone-mgmt-send --output-format' = @('normal', 'json', 'binary') 'zns zone-mgmt-send --output-format-version' = @('1', '2') + 'zns zone-mgmt-send --timeout' = @('infinite', '-1') + 'zns zone-mgmt-send -n' = @('all', '-1') 'zns zone-mgmt-send -o' = @('normal', 'json', 'binary') + 'zns zrwa-flush-zone --namespace-id' = @('all', '-1') 'zns zrwa-flush-zone --output-format' = @('normal', 'json', 'binary') 'zns zrwa-flush-zone --output-format-version' = @('1', '2') + 'zns zrwa-flush-zone --timeout' = @('infinite', '-1') + 'zns zrwa-flush-zone -n' = @('all', '-1') 'zns zrwa-flush-zone -o' = @('normal', 'json', 'binary') } From f9b135bb6ee1647e57e541b20ed7a6a35172d1e8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:28:05 +0000 Subject: [PATCH 086/263] NEWS: document the negative number and value name changes Unsigned options reject negative numbers and the special namespace id and timeout values have names. Signed-off-by: Daniel Wagner --- NEWS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS.md b/NEWS.md index 37b97085e0..56b4b36746 100644 --- a/NEWS.md +++ b/NEWS.md @@ -27,6 +27,14 @@ takes none, or the value of `--persistent force`, which has to be given as `--persistent=force`. +* Options which take an unsigned number reject negative numbers now. + Before, `-1` wrapped around to the largest value. The values which + have a special meaning got names instead: every namespace id option + accepts `all` for 0xffffffff and `--timeout` (also in + `nvme-cli.conf`) accepts `infinite`. Both still accept `-1`. The + help lists the names of an option. `lm migration-recv` requires + `--sel` now, like the other lm commands. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. From 4283fd2b2a8ce2f1b77473e08190b5af39ee9590 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:53:14 +0000 Subject: [PATCH 087/263] plugins/feat: keep the completion queue count in num-queues When only '--nsqr' is given, num-queues keeps the current number of completion queues but ORs it into the submission queue field. The completion queue field stays 0 and the command asks for one completion queue and a wrong number of submission queues. Set the current count into the completion queue field. Fixes: 4bf149f22b8b ("feat: add num-queues command") Signed-off-by: Daniel Wagner --- plugins/feat/feat-nvme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 5aa5b8e6b4..aed3d7b973 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -986,7 +986,7 @@ static int num_queues_set(struct libnvme_transport_handle *hdl, const __u8 fid, if (!nsqr_given) cdw11 |= NVME_SET(nsqr, FEAT_NRQS_NSQR); if (!ncqr_given) - cdw11 |= NVME_SET(ncqr, FEAT_NRQS_NSQR); + cdw11 |= NVME_SET(ncqr, FEAT_NRQS_NCQR); } err = nvme_set_features(hdl, 0, fid, sv, cdw11, 0, 0, 0, 0, NULL, 0, From 52974054bdbce753eccf803f49ac1c685af82e53 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:53:14 +0000 Subject: [PATCH 088/263] plugins/ocp: describe the --enable option of set-enable-ieee1667-silo The help shows the description of '--no-uuid' for '--enable'. Give it its own. Fixes: 7d6cf879a8d1 ("ocp: add set-enable-ieee1667-silo command") Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-nvme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 39f82f7006..4b2138696d 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -2445,6 +2445,7 @@ static int enable_ieee1667_silo_set(struct libnvme_transport_handle *hdl, static int set_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + const char *enable = "enable the silo, disable it if not given"; bool enable_flag = false; bool save_flag = false; bool no_uuid_flag = false; @@ -2454,7 +2455,7 @@ static int set_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; NVME_ARGS(opts, - OPT_FLAG("enable", 'e', &enable_flag, no_uuid), + OPT_FLAG("enable", 'e', &enable_flag, enable), OPT_FLAG("save", 's', &save_flag, save), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); From d4321dcb6c814340c88dd23252f646e2e621ebe2 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:53:58 +0000 Subject: [PATCH 089/263] completions: update the shell completions Pick up the new description of the ocp set-enable-ieee1667-silo --enable option. Signed-off-by: Daniel Wagner --- completions/_nvme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completions/_nvme b/completions/_nvme index 7637705c41..7540f7ce5a 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -7193,7 +7193,7 @@ _nvme () { local _ocp_set_enable_ieee1667_silo _ocp_set_enable_ieee1667_silo=( --dry-run':show command instead of executing' - --enable':Skip UUID index search (UUID index not required for OCP 1.0)' + --enable':enable the silo, disable it if not given' -e':alias for --enable' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' From 6d95baf0b8a3b106afcfada9b9e8e95224927f72 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:53:58 +0000 Subject: [PATCH 090/263] NEWS: document the num-queues fix num-queues kept the current completion queue count in the wrong field. Signed-off-by: Daniel Wagner --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 56b4b36746..00a1d41942 100644 --- a/NEWS.md +++ b/NEWS.md @@ -200,6 +200,10 @@ ### Plugins +* `nvme feat num-queues --nsqr=N` without `--ncqr` keeps the current + number of completion queues now. Before, it requested a single + completion queue and a wrong number of submission queues. + * Fixed root command injection in WDC (`vs-internal-log`, `drive-essentials`, SN730 capture) and SanDisk (telemetry capture): a caller-controlled string reached a shell (`system()`), so a From 501b6231438dd3419847de127e1383c7b269af83 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:57:23 +0000 Subject: [PATCH 091/263] argconfig: reject trailing text after a number The number options take what strtol() and friends convert and ignore the rest, so '--timeout=10s' sets a timeout of 10 ms and '-n 1x' the namespace 1. Make the whole argument a number, as the size options which take a suffix already do. Units for an option belong in its parser, not into ignored text. Signed-off-by: Daniel Wagner --- src/argconfig.c | 12 ++++++------ tests/unit/c/argconfig-getopt-ref.c | 12 ++++++------ tests/unit/c/test-argconfig-parse.c | 9 +++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 3716ffc044..a5a8ee2306 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -250,13 +250,13 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_INT: *(int *)value = strtol(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("integer", s->option, arg); break; case CFG_BYTE: { unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 8 || arg == endptr) + if (errno || tmp >= 1 << 8 || arg == endptr || *endptr) ret = argconfig_error("byte", s->option, arg); else *(uint8_t *)value = tmp; @@ -265,7 +265,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_SHORT: { unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 16 || arg == endptr) + if (errno || tmp >= 1 << 16 || arg == endptr || *endptr) ret = argconfig_error("short", s->option, arg); else *(uint16_t *)value = tmp; @@ -274,7 +274,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_POSITIVE: { unsigned long long tmp = strtoull(arg, &endptr, 0); - if (errno || tmp > UINT32_MAX || arg == endptr) + if (errno || tmp > UINT32_MAX || arg == endptr || *endptr) ret = argconfig_error("word", s->option, arg); else *(uint32_t *)value = tmp; @@ -285,7 +285,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_LONG: *(uint64_t *)value = strtoull(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("long integer", s->option, arg); break; case CFG_LONG_SUFFIX: @@ -296,7 +296,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_DOUBLE: *(double *)value = strtod(arg, &endptr); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("float", s->option, arg); break; case CFG_FLAG: diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 695cc57131..63cca76ca9 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -74,13 +74,13 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_INT: *(int *)value = strtol(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("integer", s->option, arg); break; case CFG_BYTE: { unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 8 || arg == endptr) + if (errno || tmp >= 1 << 8 || arg == endptr || *endptr) ret = argconfig_error("byte", s->option, arg); else *(uint8_t *)value = tmp; @@ -89,7 +89,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_SHORT: { unsigned long tmp = strtoul(arg, &endptr, 0); - if (errno || tmp >= 1 << 16 || arg == endptr) + if (errno || tmp >= 1 << 16 || arg == endptr || *endptr) ret = argconfig_error("short", s->option, arg); else *(uint16_t *)value = tmp; @@ -98,7 +98,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_POSITIVE: { unsigned long long tmp = strtoull(arg, &endptr, 0); - if (errno || tmp > UINT32_MAX || arg == endptr) + if (errno || tmp > UINT32_MAX || arg == endptr || *endptr) ret = argconfig_error("word", s->option, arg); else *(uint32_t *)value = tmp; @@ -109,7 +109,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_LONG: *(uint64_t *)value = strtoull(arg, &endptr, 0); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("long integer", s->option, arg); break; case CFG_LONG_SUFFIX: @@ -120,7 +120,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; case CFG_DOUBLE: *(double *)value = strtod(arg, &endptr); - if (errno || arg == endptr) + if (errno || arg == endptr || *endptr) ret = argconfig_error("float", s->option, arg); break; case CFG_FLAG: diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 5a1c143a5a..1a7d3b57ab 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -114,6 +114,15 @@ static struct toval_test toval_tests[] = { VAL_TEST("--shrt=-1", shrt, 0, true, -EINVAL), VAL_TEST("--long=-1", long_val, 0, true, -EINVAL), VAL_TEST("--suffix=-1", suffix, 0, true, -EINVAL), + VAL_TEST("--uint=10s", uint, 0, true, -EINVAL), + VAL_TEST("--uint=1 ", uint, 0, true, -EINVAL), + VAL_TEST("--byte=1x", byte, 0, true, -EINVAL), + VAL_TEST("--shrt=0x10g", shrt, 0, true, -EINVAL), + VAL_TEST("--long=5k", long_val, 0, true, -EINVAL), + VAL_TEST("--int=1x", int_val, 0, true, -EINVAL), + VAL_TEST("--double=1.5x", double_val, 0, true, -EINVAL), + VAL_TEST("--double=1.5", double_val, 1.5, true, 0), + VAL_TEST("--val=12x", val, 0, true, -EINVAL), VAL_TEST("--int=-1", int_val, -1, true, 0), VAL_TEST("--int=1", int_val, 1, true, 0), VAL_TEST("--long=1", long_val, 1, true, 0), From 4092b463848e53a3e22eee640b8b260e10a40156 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 08:57:23 +0000 Subject: [PATCH 092/263] NEWS: document the rejected trailing text after numbers Number options reject text after the number now. Signed-off-by: Daniel Wagner --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index 00a1d41942..aa8090775e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -35,6 +35,9 @@ help lists the names of an option. `lm migration-recv` requires `--sel` now, like the other lm commands. +* Number options reject text after the number. Before, the rest was + ignored: `--timeout=10s` set a timeout of 10 milliseconds. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. From 728d2da7c6b23a2c0c51984b42f932a413ba0f23 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:08:06 +0000 Subject: [PATCH 093/263] nvme: run deprecated commands from a table Every deprecated command had a function which called one of ten copies of a forward helper, one per plugin, and its own struct command whose help repeated the replacement. 72 of the 75 commands do nothing else. Name the replacement in struct command and forward with one function, run_replacement(). A table row gives the name, the help and the replacement of a deprecated command, and the help suffix which names the replacement comes from the row. The commands which translate their options keep their function and forward with run_replacement_command(). The help and the command metadata stay the same. Signed-off-by: Daniel Wagner --- src/nvme-cmds-deprecated.c | 1641 +++++------------------------------- src/plugin.c | 47 ++ src/plugin.h | 9 + 3 files changed, 281 insertions(+), 1416 deletions(-) diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index 268837fa67..3eefe00ef7 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -7,91 +7,25 @@ * Written by Keith Busch */ -/** - * This program uses NVMe IOCTLs to run native nvme commands to a device. +/* + * The deprecated commands, which run the commands that replace them. */ -#include #include -#include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include - -#include -#include -#include -#include - -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include "argconfig.h" +#include "args.h" #include "cleanup.h" -#include "nvme-print.h" #include "plugin.h" #ifdef CONFIG_DEPRECATED_CMDS #ifdef CONFIG_FABRICS -static struct plugin *find_keys_plugin(void) -{ - struct plugin *keys = builtin.next; - - while (keys && (!keys->name || strcmp(keys->name, "keys"))) - keys = keys->next; - - return keys; -} - -static int forward_to_keys_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *keys = find_keys_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!keys) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'keys' plugin, which is not available in this build; use 'nvme keys %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme keys %s' instead\n", - old_name, subcmd); - - /* - * handle_plugin() expects argv[0] to be a throwaway name (its own - * global-option parsing skips it like a program name) and argv[1] - * to be the subcommand it dispatches on, so forwarding into the - * 'keys' plugin needs both slots, not just a renamed argv[0]. - */ - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)keys->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, keys); -} - /* * gen-kxchap-secret dropped --nqn with the transform that used it. Take * it here anyway, warn, and drop it, so a 2.x command line still works. @@ -157,7 +91,8 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl args[nargs++] = hmac_buf; } - return forward_to_keys_plugin("gen-dhchap-key", "gen-kxchap-secret", nargs, args); + return run_replacement_command(plugin, acmd, acmd->replacement.command, + nargs, args); } /* @@ -195,17 +130,8 @@ static int check_dhchap_key(int argc, char **argv, struct command *acmd, struct args[nargs++] = cfg.key; } - return forward_to_keys_plugin("check-dhchap-key", "check-kxchap-secret", nargs, args); -} - -static int gen_tls_key(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_keys_plugin("gen-tls-key", "gen-tls-psk", argc, argv); -} - -static int check_tls_key(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_keys_plugin("check-tls-key", "check-tls-psk", argc, argv); + return run_replacement_command(plugin, acmd, acmd->replacement.command, + nargs, args); } /* @@ -233,7 +159,6 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p __cleanup_free char **sub_argv = NULL; const char *keyring = NULL, *keytype = NULL, *keyfile = NULL, *revoke = NULL; const char *subcmd = NULL; - struct plugin *keys; int nsub = 0, c; optind = 1; @@ -274,19 +199,11 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p return -EINVAL; } - keys = find_keys_plugin(); - if (!keys) { - fprintf(stderr, "ERROR: 'tls-key' is deprecated and requires the 'keys' plugin, which is not available in this build; use 'nvme keys %s'\n", - subcmd); - return -ENOTTY; - } - - sub_argv = calloc(6, sizeof(*sub_argv)); + sub_argv = calloc(5, sizeof(*sub_argv)); if (!sub_argv) return -ENOMEM; - sub_argv[nsub++] = (char *)keys->name; - sub_argv[nsub++] = (char *)subcmd; + sub_argv[nsub++] = argv[0]; if (keyring) { if (asprintf(&keyring_opt, "--keyring=%s", keyring) < 0) @@ -309,1337 +226,229 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p sub_argv[nsub++] = keyfile_opt; } - fprintf(stderr, "WARNING: 'tls-key' is deprecated and will be removed in the next major version, use 'nvme keys %s' instead\n", - subcmd); - - return handle_plugin(nsub, sub_argv, keys); + return run_replacement_command(plugin, acmd, subcmd, nsub, sub_argv); } #endif /* CONFIG_FABRICS */ -static struct plugin *find_log_plugin(void) -{ - struct plugin *log = builtin.next; - - while (log && (!log->name || strcmp(log->name, "log"))) - log = log->next; - - return log; -} - -static int forward_to_log_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *log = find_log_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!log) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'log' plugin, which is not available in this build; use 'nvme log %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme log %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)log->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, log); -} - -static int get_supported_log_pages(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("supported-log-pages", "supported-pages", argc, argv); -} - -static int get_telemetry_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("telemetry-log", "telemetry", argc, argv); -} - -static int get_fw_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("fw-log", "fw", argc, argv); -} - -static int get_changed_attach_ns_list_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("changed-ns-list-log", "changed-ns-list", argc, argv); -} - -static int get_smart_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("smart-log", "smart", argc, argv); -} - -static int get_ana_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("ana-log", "ana", argc, argv); -} - -static int get_error_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("error-log", "error", argc, argv); -} - -static int get_effects_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("effects-log", "effects", argc, argv); -} - -static int get_endurance_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("endurance-log", "endurance", argc, argv); -} - -static int get_pred_lat_per_nvmset_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("predictable-lat-log", "predictable-lat", argc, argv); -} - -static int get_pred_lat_event_agg_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("pred-lat-event-agg-log", "pred-lat-event-agg", argc, argv); -} - -static int get_persistent_event_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("persistent-event-log", "persistent-event", argc, argv); -} - -static int get_endurance_event_agg_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("endurance-event-agg-log", "endurance-event-agg", argc, argv); -} - -static int get_lba_status_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("lba-status-log", "lba-status", argc, argv); -} - -static int get_resv_notif_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("resv-notif-log", "resv-notif", argc, argv); -} - -static int get_boot_part_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("boot-part-log", "boot-part", argc, argv); -} - -static int get_phy_rx_eom_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("phy-rx-eom-log", "phy-rx-eom", argc, argv); -} - -static int self_test_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("self-test-log", "self-test", argc, argv); -} - -static int get_fid_support_effects_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("fid-support-effects-log", "fid-support-effects", argc, argv); -} - -static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("mi-cmd-support-effects-log", "mi-cmd-support-effects", argc, argv); -} - -static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("media-unit-stat-log", "media-unit-stat", argc, argv); -} - -static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("supported-cap-config-log", "supported-cap-config", argc, argv); -} - -static int get_mgmt_addr_list_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("mgmt-addr-list-log", "mgmt-addr-list", argc, argv); -} - -static int get_rotational_media_info_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("rotational-media-info-log", "rotational-media-info", argc, argv); -} - -static int get_changed_alloc_ns_list_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("changed-alloc-ns-list-log", "changed-alloc-ns-list", argc, argv); -} - -static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("dispersed-ns-participating-nss-log", "dispersed-ns-participating-nss", argc, argv); -} - -static int get_reachability_groups_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("reachability-groups-log", "reachability-groups", argc, argv); -} - -static int get_reachability_associations_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("reachability-associations-log", "reachability-associations", argc, argv); -} - -static int get_host_discovery_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("host-discovery-log", "host-discovery", argc, argv); -} - -static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("ave-discovery-log", "ave-discovery", argc, argv); -} - -static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("pull-model-ddc-req-log", "pull-model-ddc-req", argc, argv); -} - -static int get_power_measurement_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("power-measurement-log", "power-measurement", argc, argv); -} - -static int sanitize_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_log_plugin("sanitize-log", "sanitize", argc, argv); -} - - -static struct plugin *find_id_plugin(void) -{ - struct plugin *id = builtin.next; - - while (id && (!id->name || strcmp(id->name, "id"))) - id = id->next; - - return id; -} - -static int forward_to_id_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *id = find_id_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!id) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'id' plugin, which is not available in this build; use 'nvme id %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme id %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)id->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, id); -} - -static int id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-ctrl", "ctrl", argc, argv); -} - -static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-ns", "ns", argc, argv); -} - -static int id_ns_granularity(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-ns-granularity", "ns-granularity", argc, argv); -} - -static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-ns-lba-format", "ns-lba-format", argc, argv); -} - -static int list_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("list-ns", "ns-list", argc, argv); -} - -static int list_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("list-ctrl", "ctrl-list", argc, argv); -} - -static int nvm_id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("nvm-id-ctrl", "nvm-ctrl", argc, argv); -} - -static int nvm_id_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("nvm-id-ns", "nvm-ns", argc, argv); -} - -static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("nvm-id-ns-lba-format", "nvm-ns-lba-format", argc, argv); -} - -static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("primary-ctrl-caps", "primary-ctrl-caps", argc, argv); -} - -static int list_secondary_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("list-secondary", "secondary-ctrl-list", argc, argv); -} - -static int cmd_set_independent_id_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("cmdset-ind-id-ns", "ns-ind", argc, argv); -} - -static int ns_descs(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("ns-descs", "ns-descs", argc, argv); -} - -static int id_nvmset(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-nvmset", "nvmset", argc, argv); -} - -static int id_uuid(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-uuid", "uuid", argc, argv); -} - -static int id_iocs(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-iocs", "iocs", argc, argv); -} - -static int id_domain(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("id-domain", "domain", argc, argv); -} - -static int id_endurance_grp_list(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_id_plugin("list-endgrp", "endgrp-list", argc, argv); -} - - -static struct plugin *find_ns_plugin(void) -{ - struct plugin *ns = builtin.next; - - while (ns && (!ns->name || strcmp(ns->name, "ns"))) - ns = ns->next; - - return ns; -} - -static int forward_to_ns_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *ns = find_ns_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!ns) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'ns' plugin, which is not available in this build; use 'nvme ns %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme ns %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)ns->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, ns); -} - -static int create_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_ns_plugin("create-ns", "create", argc, argv); -} - -static int delete_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_ns_plugin("delete-ns", "delete", argc, argv); -} - -static int attach_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_ns_plugin("attach-ns", "attach", argc, argv); -} - -static int detach_ns(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_ns_plugin("detach-ns", "detach", argc, argv); -} - -static int get_ns_id(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_ns_plugin("get-ns-id", "get-id", argc, argv); -} - - -static struct plugin *find_resv_plugin(void) -{ - struct plugin *resv = builtin.next; - - while (resv && (!resv->name || strcmp(resv->name, "resv"))) - resv = resv->next; - - return resv; -} - -static int forward_to_resv_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *resv = find_resv_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!resv) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'resv' plugin, which is not available in this build; use 'nvme resv %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme resv %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)resv->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, resv); -} - -static int resv_acquire(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_resv_plugin("resv-acquire", "acquire", argc, argv); -} - -static int resv_register(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_resv_plugin("resv-register", "register", argc, argv); -} - -static int resv_release(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_resv_plugin("resv-release", "release", argc, argv); -} - -static int resv_report(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_resv_plugin("resv-report", "report", argc, argv); -} - - -static struct plugin *find_nvme_mi_plugin(void) -{ - struct plugin *nvme_mi = builtin.next; - - while (nvme_mi && (!nvme_mi->name || strcmp(nvme_mi->name, "nvme-mi"))) - nvme_mi = nvme_mi->next; - - return nvme_mi; -} - -static int forward_to_nvme_mi_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *nvme_mi = find_nvme_mi_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!nvme_mi) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'nvme-mi' plugin, which is not available in this build; use 'nvme nvme-mi %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme nvme-mi %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)nvme_mi->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, nvme_mi); -} - -static int nmi_recv(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_nvme_mi_plugin("nvme-mi-recv", "recv", argc, argv); -} - -static int nmi_send(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_nvme_mi_plugin("nvme-mi-send", "send", argc, argv); -} - - -static struct plugin *find_io_mgmt_plugin(void) -{ - struct plugin *io_mgmt = builtin.next; - - while (io_mgmt && (!io_mgmt->name || strcmp(io_mgmt->name, "io-mgmt"))) - io_mgmt = io_mgmt->next; - - return io_mgmt; -} - -static int forward_to_io_mgmt_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *io_mgmt = find_io_mgmt_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!io_mgmt) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'io-mgmt' plugin, which is not available in this build; use 'nvme io-mgmt %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme io-mgmt %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)io_mgmt->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, io_mgmt); -} - -static int io_mgmt_recv(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_io_mgmt_plugin("io-mgmt-recv", "recv", argc, argv); -} - -static int io_mgmt_send(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_io_mgmt_plugin("io-mgmt-send", "send", argc, argv); -} - - -static struct plugin *find_dir_plugin(void) -{ - struct plugin *dir = builtin.next; - - while (dir && (!dir->name || strcmp(dir->name, "dir"))) - dir = dir->next; - - return dir; -} - -static int forward_to_dir_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *dir = find_dir_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!dir) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'dir' plugin, which is not available in this build; use 'nvme dir %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme dir %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)dir->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, dir); -} - -static int dir_receive(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_dir_plugin("dir-receive", "receive", argc, argv); -} - -static int dir_send(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_dir_plugin("dir-send", "send", argc, argv); -} - - -static struct plugin *find_security_plugin(void) -{ - struct plugin *security = builtin.next; - - while (security && (!security->name || strcmp(security->name, "security"))) - security = security->next; - - return security; -} - -static int forward_to_security_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *security = find_security_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!security) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'security' plugin, which is not available in this build; use 'nvme security %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme security %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)security->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, security); -} - -static int sec_send(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_security_plugin("security-send", "send", argc, argv); -} - -static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_security_plugin("security-recv", "recv", argc, argv); -} - - -static struct plugin *find_fw_plugin(void) -{ - struct plugin *fw = builtin.next; - - while (fw && (!fw->name || strcmp(fw->name, "fw"))) - fw = fw->next; - - return fw; -} - -static int forward_to_fw_plugin(const char *old_name, const char *subcmd, - int argc, char **argv) -{ - struct plugin *fw = find_fw_plugin(); - __cleanup_free char **sub_argv = NULL; - - if (!fw) { - fprintf(stderr, "ERROR: '%s' is deprecated and requires the 'fw' plugin, which is not available in this build; use 'nvme fw %s'\n", - old_name, subcmd); - return -ENOTTY; - } - - fprintf(stderr, "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme fw %s' instead\n", - old_name, subcmd); - - sub_argv = calloc(argc + 1, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[0] = (char *)fw->name; - sub_argv[1] = (char *)subcmd; - memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); - - return handle_plugin(argc + 1, sub_argv, fw); -} - -static int fw_commit(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_fw_plugin("fw-commit", "commit", argc, argv); -} - -static int fw_download(int argc, char **argv, struct command *acmd, struct plugin *plugin) -{ - return forward_to_fw_plugin("fw-download", "download", argc, argv); -} - -static struct command id_ctrl_cmd = { - .name = "id-ctrl", - .help = "Send NVMe Identify Controller (deprecated, use 'nvme id ctrl')", - .fn = id_ctrl, - .deprecated = true, -}; - -static struct command id_ns_cmd = { - .name = "id-ns", - .help = "Send NVMe Identify Namespace, display structure (deprecated, use 'nvme id ns')", - .fn = id_ns, - .deprecated = true, -}; - -static struct command id_ns_granularity_cmd = { - .name = "id-ns-granularity", - .help = "Send NVMe Identify Namespace Granularity List, display structure (deprecated, use 'nvme " - "id ns-granularity')", - .fn = id_ns_granularity, - .deprecated = true, -}; - -static struct command id_ns_lba_format_cmd = { - .name = "id-ns-lba-format", - .help = "Send NVMe Identify Namespace for the specified LBA Format index, display structure " - "(deprecated, use 'nvme id ns-lba-format')", - .fn = id_ns_lba_format, - .deprecated = true, -}; - -static struct command list_ns_cmd = { - .name = "list-ns", - .help = "Send NVMe Identify List, display structure (deprecated, use 'nvme id ns-list')", - .fn = list_ns, - .deprecated = true, -}; - -static struct command list_ctrl_cmd = { - .name = "list-ctrl", - .help = "Send NVMe Identify Controller List, display structure (deprecated, use 'nvme id " - "ctrl-list')", - .fn = list_ctrl, - .deprecated = true, -}; - -static struct command nvm_id_ctrl_cmd = { - .name = "nvm-id-ctrl", - .help = "Send NVMe Identify Controller NVM Command Set, display structure (deprecated, use 'nvme " - "id nvm-ctrl')", - .fn = nvm_id_ctrl, - .deprecated = true, -}; - -static struct command nvm_id_ns_cmd = { - .name = "nvm-id-ns", - .help = "Send NVMe Identify Namespace NVM Command Set, display structure (deprecated, use 'nvme " - "id nvm-ns')", - .fn = nvm_id_ns, - .deprecated = true, -}; - -static struct command nvm_id_ns_lba_format_cmd = { - .name = "nvm-id-ns-lba-format", - .help = "Send NVMe Identify Namespace NVM Command Set for the specified LBA Format index, display " - "structure (deprecated, use 'nvme id nvm-ns-lba-format')", - .fn = nvm_id_ns_lba_format, - .deprecated = true, -}; - -static struct command primary_ctrl_caps_cmd = { - .name = "primary-ctrl-caps", - .help = "Send NVMe Identify Primary Controller Capabilities (deprecated, use 'nvme id " - "primary-ctrl-caps')", - .fn = primary_ctrl_caps, - .deprecated = true, -}; - -static struct command list_secondary_ctrl_cmd = { - .name = "list-secondary", - .help = "List Secondary Controllers associated with a Primary Controller (deprecated, use 'nvme " - "id secondary-ctrl-list')", - .fn = list_secondary_ctrl, - .deprecated = true, -}; - -static struct command cmd_set_independent_id_ns_cmd = { - .name = "cmdset-ind-id-ns", - .help = "I/O Command Set Independent Identify Namespace (deprecated, use 'nvme id ns-ind')", - .fn = cmd_set_independent_id_ns, - .deprecated = true, -}; - -static struct command ns_descs_cmd = { - .name = "ns-descs", - .help = "Send NVMe Namespace Descriptor List, display structure (deprecated, use 'nvme id " - "ns-descs')", - .fn = ns_descs, - .deprecated = true, -}; - -static struct command id_nvmset_cmd = { - .name = "id-nvmset", - .help = "Send NVMe Identify NVM Set List, display structure (deprecated, use 'nvme id nvmset')", - .fn = id_nvmset, - .deprecated = true, -}; - -static struct command id_uuid_cmd = { - .name = "id-uuid", - .help = "Send NVMe Identify UUID List, display structure (deprecated, use 'nvme id uuid')", - .fn = id_uuid, - .deprecated = true, -}; - -static struct command id_iocs_cmd = { - .name = "id-iocs", - .help = "Send NVMe Identify I/O Command Set, display structure (deprecated, use 'nvme id iocs')", - .fn = id_iocs, - .deprecated = true, -}; - -static struct command id_domain_cmd = { - .name = "id-domain", - .help = "Send NVMe Identify Domain List, display structure (deprecated, use 'nvme id domain')", - .fn = id_domain, - .deprecated = true, -}; - -static struct command id_endurance_grp_list_cmd = { - .name = "list-endgrp", - .help = "Send NVMe Identify Endurance Group List, display structure (deprecated, use 'nvme id " - "endgrp-list')", - .fn = id_endurance_grp_list, - .deprecated = true, -}; - -static struct command create_ns_cmd = { - .name = "create-ns", - .help = "Creates a namespace with the provided parameters (deprecated, use 'nvme ns create')", - .fn = create_ns, - .deprecated = true, -}; - -static struct command delete_ns_cmd = { - .name = "delete-ns", - .help = "Deletes a namespace from the controller (deprecated, use 'nvme ns delete')", - .fn = delete_ns, - .deprecated = true, -}; - -static struct command attach_ns_cmd = { - .name = "attach-ns", - .help = "Attaches a namespace to requested controller(s) (deprecated, use 'nvme ns attach')", - .fn = attach_ns, - .deprecated = true, -}; - -static struct command detach_ns_cmd = { - .name = "detach-ns", - .help = "Detaches a namespace from requested controller(s) (deprecated, use 'nvme ns detach')", - .fn = detach_ns, - .deprecated = true, -}; - -static struct command get_ns_id_cmd = { - .name = "get-ns-id", - .help = "Retrieve the namespace ID of opened block device (deprecated, use 'nvme ns get-id')", - .fn = get_ns_id, - .deprecated = true, -}; - -static struct command get_supported_log_pages_cmd = { - .name = "supported-log-pages", - .help = "Retrieve the Supported Log pages details, show it (deprecated, use 'nvme log " - "supported-pages')", - .fn = get_supported_log_pages, - .deprecated = true, -}; - -static struct command get_telemetry_log_cmd = { - .name = "telemetry-log", - .help = "Retrieve FW Telemetry log write to file (deprecated, use 'nvme log telemetry')", - .fn = get_telemetry_log, - .deprecated = true, -}; - -static struct command get_fw_log_cmd = { - .name = "fw-log", - .help = "Retrieve FW Log, show it (deprecated, use 'nvme log fw')", - .fn = get_fw_log, - .deprecated = true, -}; - -static struct command get_changed_attach_ns_list_log_cmd = { - .name = "changed-ns-list-log", - .help = "Retrieve Changed Attached Namespace List, show it (deprecated, use 'nvme log " - "changed-ns-list')", - .fn = get_changed_attach_ns_list_log, - .deprecated = true, -}; - -static struct command get_smart_log_cmd = { - .name = "smart-log", - .help = "Retrieve SMART Log, show it (deprecated, use 'nvme log smart')", - .fn = get_smart_log, - .deprecated = true, -}; - -static struct command get_ana_log_cmd = { - .name = "ana-log", - .help = "Retrieve ANA Log, show it (deprecated, use 'nvme log ana')", - .fn = get_ana_log, - .deprecated = true, -}; - -static struct command get_error_log_cmd = { - .name = "error-log", - .help = "Retrieve Error Log, show it (deprecated, use 'nvme log error')", - .fn = get_error_log, - .deprecated = true, -}; - -static struct command get_effects_log_cmd = { - .name = "effects-log", - .help = "Retrieve Command Effects Log, show it (deprecated, use 'nvme log effects')", - .fn = get_effects_log, - .deprecated = true, -}; - -static struct command get_endurance_log_cmd = { - .name = "endurance-log", - .help = "Retrieve Endurance Group Log, show it (deprecated, use 'nvme log endurance')", - .fn = get_endurance_log, - .deprecated = true, -}; - -static struct command get_pred_lat_per_nvmset_log_cmd = { - .name = "predictable-lat-log", - .help = "Retrieve Predictable Latency per Nvmset Log, show it (deprecated, use 'nvme log " - "predictable-lat')", - .fn = get_pred_lat_per_nvmset_log, - .deprecated = true, -}; - -static struct command get_pred_lat_event_agg_log_cmd = { - .name = "pred-lat-event-agg-log", - .help = "Retrieve Predictable Latency Event Aggregate Log, show it (deprecated, use 'nvme log " - "pred-lat-event-agg')", - .fn = get_pred_lat_event_agg_log, - .deprecated = true, -}; - -static struct command get_persistent_event_log_cmd = { - .name = "persistent-event-log", - .help = "Retrieve Persistent Event Log, show it (deprecated, use 'nvme log persistent-event')", - .fn = get_persistent_event_log, - .deprecated = true, -}; - -static struct command get_endurance_event_agg_log_cmd = { - .name = "endurance-event-agg-log", - .help = "Retrieve Endurance Group Event Aggregate Log, show it (deprecated, use 'nvme log " - "endurance-event-agg')", - .fn = get_endurance_event_agg_log, - .deprecated = true, -}; - -static struct command get_lba_status_log_cmd = { - .name = "lba-status-log", - .help = "Retrieve LBA Status Information Log, show it (deprecated, use 'nvme log lba-status')", - .fn = get_lba_status_log, - .deprecated = true, -}; - -static struct command get_resv_notif_log_cmd = { - .name = "resv-notif-log", - .help = "Retrieve Reservation Notification Log, show it (deprecated, use 'nvme log resv-notif')", - .fn = get_resv_notif_log, - .deprecated = true, -}; - -static struct command get_boot_part_log_cmd = { - .name = "boot-part-log", - .help = "Retrieve Boot Partition Log, show it (deprecated, use 'nvme log boot-part')", - .fn = get_boot_part_log, - .deprecated = true, -}; - -static struct command get_phy_rx_eom_log_cmd = { - .name = "phy-rx-eom-log", - .help = "Retrieve Physical Interface Receiver Eye Opening Measurement, show it (deprecated, use " - "'nvme log phy-rx-eom')", - .fn = get_phy_rx_eom_log, - .deprecated = true, -}; - -static struct command self_test_log_cmd = { - .name = "self-test-log", - .help = "Retrieve the SELF-TEST Log, show it (deprecated, use 'nvme log self-test')", - .fn = self_test_log, - .deprecated = true, -}; - -static struct command get_fid_support_effects_log_cmd = { - .name = "fid-support-effects-log", - .help = "Retrieve FID Support and Effects log and show it (deprecated, use 'nvme log " - "fid-support-effects')", - .fn = get_fid_support_effects_log, - .deprecated = true, -}; - -static struct command get_mi_cmd_support_effects_log_cmd = { - .name = "mi-cmd-support-effects-log", - .help = "Retrieve MI Command Support and Effects log and show it (deprecated, use 'nvme log " - "mi-cmd-support-effects')", - .fn = get_mi_cmd_support_effects_log, - .deprecated = true, -}; - -static struct command get_media_unit_stat_log_cmd = { - .name = "media-unit-stat-log", - .help = "Retrieve the configuration and wear of media units, show it (deprecated, use 'nvme log " - "media-unit-stat')", - .fn = get_media_unit_stat_log, - .deprecated = true, -}; - -static struct command get_supp_cap_config_log_cmd = { - .name = "supported-cap-config-log", - .help = "Retrieve the list of Supported Capacity Configuration Descriptors (deprecated, use 'nvme " - "log supported-cap-config')", - .fn = get_supp_cap_config_log, - .deprecated = true, -}; - -static struct command get_mgmt_addr_list_log_cmd = { - .name = "mgmt-addr-list-log", - .help = "Retrieve Management Address List Log, show it (deprecated, use 'nvme log " - "mgmt-addr-list')", - .fn = get_mgmt_addr_list_log, - .deprecated = true, -}; - -static struct command get_rotational_media_info_log_cmd = { - .name = "rotational-media-info-log", - .help = "Retrieve Rotational Media Information Log, show it (deprecated, use 'nvme log " - "rotational-media-info')", - .fn = get_rotational_media_info_log, - .deprecated = true, -}; - -static struct command get_changed_alloc_ns_list_log_cmd = { - .name = "changed-alloc-ns-list-log", - .help = "Retrieve Changed Allocated Namespace List, show it (deprecated, use 'nvme log " - "changed-alloc-ns-list')", - .fn = get_changed_alloc_ns_list_log, - .deprecated = true, -}; - -static struct command get_dispersed_ns_participating_nss_log_cmd = { - .name = "dispersed-ns-participating-nss-log", - .help = "Retrieve Dispersed Namespace Participating NVM Subsystems Log, show it (deprecated, use " - "'nvme log dispersed-ns-participating-nss')", - .fn = get_dispersed_ns_participating_nss_log, - .deprecated = true, -}; - -static struct command get_reachability_groups_log_cmd = { - .name = "reachability-groups-log", - .help = "Retrieve Reachability Groups Log, show it (deprecated, use 'nvme log " - "reachability-groups')", - .fn = get_reachability_groups_log, - .deprecated = true, -}; - -static struct command get_reachability_associations_log_cmd = { - .name = "reachability-associations-log", - .help = "Retrieve Reachability Associations Log, show it (deprecated, use 'nvme log " - "reachability-associations')", - .fn = get_reachability_associations_log, - .deprecated = true, -}; - -static struct command get_host_discovery_log_cmd = { - .name = "host-discovery-log", - .help = "Retrieve Host Discovery Log, show it (deprecated, use 'nvme log host-discovery')", - .fn = get_host_discovery_log, - .deprecated = true, -}; - -static struct command get_ave_discovery_log_cmd = { - .name = "ave-discovery-log", - .help = "Retrieve AVE Discovery Log, show it (deprecated, use 'nvme log ave-discovery')", - .fn = get_ave_discovery_log, - .deprecated = true, -}; - -static struct command get_pull_model_ddc_req_log_cmd = { - .name = "pull-model-ddc-req-log", - .help = "Retrieve Pull Model DDC Request Log, show it (deprecated, use 'nvme log " - "pull-model-ddc-req')", - .fn = get_pull_model_ddc_req_log, - .deprecated = true, -}; - -static struct command get_power_measurement_log_cmd = { - .name = "power-measurement-log", - .help = "Retrieve Power Measurement Log, show it (deprecated, use 'nvme log power-measurement')", - .fn = get_power_measurement_log, - .deprecated = true, -}; - -static struct command fw_commit_cmd = { - .name = "fw-commit", - .help = "Verify and commit firmware to a specific slot (fw-activate in old version < 1.2) " - "(deprecated, use 'nvme fw commit')", - .fn = fw_commit, - .alias = "fw-activate", - .deprecated = true, -}; - -static struct command fw_download_cmd = { - .name = "fw-download", - .help = "Download new firmware (deprecated, use 'nvme fw download')", - .fn = fw_download, - .deprecated = true, -}; - -static struct command sec_send_cmd = { - .name = "security-send", - .help = "Submit a Security Send command, return results (deprecated, use 'nvme security send')", - .fn = sec_send, - .deprecated = true, -}; - -static struct command sec_recv_cmd = { - .name = "security-recv", - .help = "Submit a Security Receive command, return results (deprecated, use 'nvme security recv')", - .fn = sec_recv, - .deprecated = true, -}; - -static struct command resv_acquire_cmd = { - .name = "resv-acquire", - .help = "Submit a Reservation Acquire, return results (deprecated, use 'nvme resv acquire')", - .fn = resv_acquire, - .deprecated = true, -}; - -static struct command resv_register_cmd = { - .name = "resv-register", - .help = "Submit a Reservation Register, return results (deprecated, use 'nvme resv register')", - .fn = resv_register, - .deprecated = true, -}; - -static struct command resv_release_cmd = { - .name = "resv-release", - .help = "Submit a Reservation Release, return results (deprecated, use 'nvme resv release')", - .fn = resv_release, - .deprecated = true, -}; - -static struct command resv_report_cmd = { - .name = "resv-report", - .help = "Submit a Reservation Report, return results (deprecated, use 'nvme resv report')", - .fn = resv_report, - .deprecated = true, -}; - -static struct command sanitize_log_cmd = { - .name = "sanitize-log", - .help = "Retrieve sanitize log, show it (deprecated, use 'nvme log sanitize')", - .fn = sanitize_log, - .deprecated = true, -}; - -#ifdef CONFIG_FABRICS - -static struct command gen_dhchap_key_cmd = { - .name = "gen-dhchap-key", - .help = "Generate NVMeoF DH-HMAC-CHAP host secret (deprecated, use 'nvme keys gen-kxchap-secret')", - .fn = gen_dhchap_key, - .deprecated = true, - .device = COMMAND_NO_DEVICE, -}; - -static struct command check_dhchap_key_cmd = { - .name = "check-dhchap-key", - .help = "Validate NVMeoF DH-HMAC-CHAP host secret (deprecated, use 'nvme keys " - "check-kxchap-secret')", - .fn = check_dhchap_key, - .deprecated = true, - .device = COMMAND_NO_DEVICE, -}; - -static struct command gen_tls_key_cmd = { - .name = "gen-tls-key", - .help = "Generate NVMeoF TLS PSK (deprecated, use 'nvme keys gen-tls-psk')", - .fn = gen_tls_key, - .deprecated = true, - .device = COMMAND_NO_DEVICE, -}; - -static struct command check_tls_key_cmd = { - .name = "check-tls-key", - .help = "Validate NVMeoF TLS PSK (deprecated, use 'nvme keys check-tls-psk')", - .fn = check_tls_key, - .deprecated = true, - .device = COMMAND_NO_DEVICE, -}; - -static struct command tls_key_cmd = { - .name = "tls-key", - .help = "Manage NVMeoF TLS PSKs (deprecated, use 'nvme keys import/export/revoke')", - .fn = tls_key, - .deprecated = true, - .device = COMMAND_NO_DEVICE, -}; - -#endif /* CONFIG_FABRICS */ - -static struct command dir_receive_cmd = { - .name = "dir-receive", - .help = "Submit a Directive Receive command, return results (deprecated, use 'nvme dir receive')", - .fn = dir_receive, - .deprecated = true, -}; - -static struct command dir_send_cmd = { - .name = "dir-send", - .help = "Submit a Directive Send command, return results (deprecated, use 'nvme dir send')", - .fn = dir_send, - .deprecated = true, -}; - -static struct command io_mgmt_recv_cmd = { - .name = "io-mgmt-recv", - .help = "I/O Management Receive (deprecated, use 'nvme io-mgmt recv')", - .fn = io_mgmt_recv, - .deprecated = true, -}; - -static struct command io_mgmt_send_cmd = { - .name = "io-mgmt-send", - .help = "I/O Management Send (deprecated, use 'nvme io-mgmt send')", - .fn = io_mgmt_send, - .deprecated = true, -}; - -static struct command nmi_recv_cmd = { - .name = "nvme-mi-recv", - .help = "Submit a NVMe-MI Receive command, return results (deprecated, use 'nvme nvme-mi recv')", - .fn = nmi_recv, - .deprecated = true, -}; - -static struct command nmi_send_cmd = { - .name = "nvme-mi-send", - .help = "Submit a NVMe-MI Send command, return results (deprecated, use 'nvme nvme-mi send')", - .fn = nmi_send, - .deprecated = true, -}; +/* + * A deprecated command which runs "nvme

" instead, with function + * f. The help names the replacement. + */ +#define DEPRECATED_FN(n, h, p, c, f, ...) \ + (&(struct command){ \ + .name = n, \ + .help = h " (deprecated, use 'nvme " p " " c "')", \ + .fn = f, \ + .deprecated = true, \ + .replacement = { p, c }, \ + __VA_ARGS__ \ + }) + +/* A deprecated command which only forwards to "nvme

" */ +#define DEPRECATED(n, h, p, c) \ + DEPRECATED_FN(n, h, p, c, run_replacement) static struct command *commands[] = { - &id_ctrl_cmd, - &id_ns_cmd, - &id_ns_granularity_cmd, - &id_ns_lba_format_cmd, - &list_ns_cmd, - &list_ctrl_cmd, - &nvm_id_ctrl_cmd, - &nvm_id_ns_cmd, - &nvm_id_ns_lba_format_cmd, - &primary_ctrl_caps_cmd, - &list_secondary_ctrl_cmd, - &cmd_set_independent_id_ns_cmd, - &ns_descs_cmd, - &id_nvmset_cmd, - &id_uuid_cmd, - &id_iocs_cmd, - &id_domain_cmd, - &id_endurance_grp_list_cmd, - &create_ns_cmd, - &delete_ns_cmd, - &attach_ns_cmd, - &detach_ns_cmd, - &get_ns_id_cmd, - &get_supported_log_pages_cmd, - &get_telemetry_log_cmd, - &get_fw_log_cmd, - &get_changed_attach_ns_list_log_cmd, - &get_smart_log_cmd, - &get_ana_log_cmd, - &get_error_log_cmd, - &get_effects_log_cmd, - &get_endurance_log_cmd, - &get_pred_lat_per_nvmset_log_cmd, - &get_pred_lat_event_agg_log_cmd, - &get_persistent_event_log_cmd, - &get_endurance_event_agg_log_cmd, - &get_lba_status_log_cmd, - &get_resv_notif_log_cmd, - &get_boot_part_log_cmd, - &get_phy_rx_eom_log_cmd, - &self_test_log_cmd, - &get_fid_support_effects_log_cmd, - &get_mi_cmd_support_effects_log_cmd, - &get_media_unit_stat_log_cmd, - &get_supp_cap_config_log_cmd, - &get_mgmt_addr_list_log_cmd, - &get_rotational_media_info_log_cmd, - &get_changed_alloc_ns_list_log_cmd, - &get_dispersed_ns_participating_nss_log_cmd, - &get_reachability_groups_log_cmd, - &get_reachability_associations_log_cmd, - &get_host_discovery_log_cmd, - &get_ave_discovery_log_cmd, - &get_pull_model_ddc_req_log_cmd, - &get_power_measurement_log_cmd, - &fw_commit_cmd, - &fw_download_cmd, - &sec_send_cmd, - &sec_recv_cmd, - &resv_acquire_cmd, - &resv_register_cmd, - &resv_release_cmd, - &resv_report_cmd, - &sanitize_log_cmd, + DEPRECATED("id-ctrl", "Send NVMe Identify Controller", "id", "ctrl"), + DEPRECATED("id-ns", "Send NVMe Identify Namespace, display structure", + "id", "ns"), + DEPRECATED("id-ns-granularity", + "Send NVMe Identify Namespace Granularity List, display structure", + "id", "ns-granularity"), + DEPRECATED("id-ns-lba-format", + "Send NVMe Identify Namespace for the specified LBA Format index, display structure", + "id", "ns-lba-format"), + DEPRECATED("list-ns", "Send NVMe Identify List, display structure", + "id", "ns-list"), + DEPRECATED("list-ctrl", + "Send NVMe Identify Controller List, display structure", + "id", "ctrl-list"), + DEPRECATED("nvm-id-ctrl", + "Send NVMe Identify Controller NVM Command Set, display structure", + "id", "nvm-ctrl"), + DEPRECATED("nvm-id-ns", + "Send NVMe Identify Namespace NVM Command Set, display structure", + "id", "nvm-ns"), + DEPRECATED("nvm-id-ns-lba-format", + "Send NVMe Identify Namespace NVM Command Set for the specified LBA Format index, display structure", + "id", "nvm-ns-lba-format"), + DEPRECATED("primary-ctrl-caps", + "Send NVMe Identify Primary Controller Capabilities", + "id", "primary-ctrl-caps"), + DEPRECATED("list-secondary", + "List Secondary Controllers associated with a Primary Controller", + "id", "secondary-ctrl-list"), + DEPRECATED("cmdset-ind-id-ns", + "I/O Command Set Independent Identify Namespace", + "id", "ns-ind"), + DEPRECATED("ns-descs", + "Send NVMe Namespace Descriptor List, display structure", + "id", "ns-descs"), + DEPRECATED("id-nvmset", + "Send NVMe Identify NVM Set List, display structure", + "id", "nvmset"), + DEPRECATED("id-uuid", "Send NVMe Identify UUID List, display structure", + "id", "uuid"), + DEPRECATED("id-iocs", + "Send NVMe Identify I/O Command Set, display structure", + "id", "iocs"), + DEPRECATED("id-domain", + "Send NVMe Identify Domain List, display structure", + "id", "domain"), + DEPRECATED("list-endgrp", + "Send NVMe Identify Endurance Group List, display structure", + "id", "endgrp-list"), + DEPRECATED("create-ns", + "Creates a namespace with the provided parameters", + "ns", "create"), + DEPRECATED("delete-ns", "Deletes a namespace from the controller", + "ns", "delete"), + DEPRECATED("attach-ns", + "Attaches a namespace to requested controller(s)", + "ns", "attach"), + DEPRECATED("detach-ns", + "Detaches a namespace from requested controller(s)", + "ns", "detach"), + DEPRECATED("get-ns-id", + "Retrieve the namespace ID of opened block device", + "ns", "get-id"), + DEPRECATED("supported-log-pages", + "Retrieve the Supported Log pages details, show it", + "log", "supported-pages"), + DEPRECATED("telemetry-log", "Retrieve FW Telemetry log write to file", + "log", "telemetry"), + DEPRECATED("fw-log", "Retrieve FW Log, show it", "log", "fw"), + DEPRECATED("changed-ns-list-log", + "Retrieve Changed Attached Namespace List, show it", + "log", "changed-ns-list"), + DEPRECATED("smart-log", "Retrieve SMART Log, show it", "log", "smart"), + DEPRECATED("ana-log", "Retrieve ANA Log, show it", "log", "ana"), + DEPRECATED("error-log", "Retrieve Error Log, show it", "log", "error"), + DEPRECATED("effects-log", "Retrieve Command Effects Log, show it", + "log", "effects"), + DEPRECATED("endurance-log", "Retrieve Endurance Group Log, show it", + "log", "endurance"), + DEPRECATED("predictable-lat-log", + "Retrieve Predictable Latency per Nvmset Log, show it", + "log", "predictable-lat"), + DEPRECATED("pred-lat-event-agg-log", + "Retrieve Predictable Latency Event Aggregate Log, show it", + "log", "pred-lat-event-agg"), + DEPRECATED("persistent-event-log", + "Retrieve Persistent Event Log, show it", + "log", "persistent-event"), + DEPRECATED("endurance-event-agg-log", + "Retrieve Endurance Group Event Aggregate Log, show it", + "log", "endurance-event-agg"), + DEPRECATED("lba-status-log", + "Retrieve LBA Status Information Log, show it", + "log", "lba-status"), + DEPRECATED("resv-notif-log", + "Retrieve Reservation Notification Log, show it", + "log", "resv-notif"), + DEPRECATED("boot-part-log", "Retrieve Boot Partition Log, show it", + "log", "boot-part"), + DEPRECATED("phy-rx-eom-log", + "Retrieve Physical Interface Receiver Eye Opening Measurement, show it", + "log", "phy-rx-eom"), + DEPRECATED("self-test-log", "Retrieve the SELF-TEST Log, show it", + "log", "self-test"), + DEPRECATED("fid-support-effects-log", + "Retrieve FID Support and Effects log and show it", + "log", "fid-support-effects"), + DEPRECATED("mi-cmd-support-effects-log", + "Retrieve MI Command Support and Effects log and show it", + "log", "mi-cmd-support-effects"), + DEPRECATED("media-unit-stat-log", + "Retrieve the configuration and wear of media units, show it", + "log", "media-unit-stat"), + DEPRECATED("supported-cap-config-log", + "Retrieve the list of Supported Capacity Configuration Descriptors", + "log", "supported-cap-config"), + DEPRECATED("mgmt-addr-list-log", + "Retrieve Management Address List Log, show it", + "log", "mgmt-addr-list"), + DEPRECATED("rotational-media-info-log", + "Retrieve Rotational Media Information Log, show it", + "log", "rotational-media-info"), + DEPRECATED("changed-alloc-ns-list-log", + "Retrieve Changed Allocated Namespace List, show it", + "log", "changed-alloc-ns-list"), + DEPRECATED("dispersed-ns-participating-nss-log", + "Retrieve Dispersed Namespace Participating NVM Subsystems Log, show it", + "log", "dispersed-ns-participating-nss"), + DEPRECATED("reachability-groups-log", + "Retrieve Reachability Groups Log, show it", + "log", "reachability-groups"), + DEPRECATED("reachability-associations-log", + "Retrieve Reachability Associations Log, show it", + "log", "reachability-associations"), + DEPRECATED("host-discovery-log", "Retrieve Host Discovery Log, show it", + "log", "host-discovery"), + DEPRECATED("ave-discovery-log", "Retrieve AVE Discovery Log, show it", + "log", "ave-discovery"), + DEPRECATED("pull-model-ddc-req-log", + "Retrieve Pull Model DDC Request Log, show it", + "log", "pull-model-ddc-req"), + DEPRECATED("power-measurement-log", + "Retrieve Power Measurement Log, show it", + "log", "power-measurement"), + DEPRECATED_FN("fw-commit", + "Verify and commit firmware to a specific slot (fw-activate in old version < 1.2)", + "fw", "commit", run_replacement, .alias = "fw-activate"), + DEPRECATED("fw-download", "Download new firmware", "fw", "download"), + DEPRECATED("security-send", + "Submit a Security Send command, return results", + "security", "send"), + DEPRECATED("security-recv", + "Submit a Security Receive command, return results", + "security", "recv"), + DEPRECATED("resv-acquire", + "Submit a Reservation Acquire, return results", + "resv", "acquire"), + DEPRECATED("resv-register", + "Submit a Reservation Register, return results", + "resv", "register"), + DEPRECATED("resv-release", + "Submit a Reservation Release, return results", + "resv", "release"), + DEPRECATED("resv-report", "Submit a Reservation Report, return results", + "resv", "report"), + DEPRECATED("sanitize-log", "Retrieve sanitize log, show it", + "log", "sanitize"), #ifdef CONFIG_FABRICS - &gen_dhchap_key_cmd, - &check_dhchap_key_cmd, - &gen_tls_key_cmd, - &check_tls_key_cmd, - &tls_key_cmd, + DEPRECATED_FN("gen-dhchap-key", + "Generate NVMeoF DH-HMAC-CHAP host secret", + "keys", "gen-kxchap-secret", gen_dhchap_key, + .device = COMMAND_NO_DEVICE), + DEPRECATED_FN("check-dhchap-key", + "Validate NVMeoF DH-HMAC-CHAP host secret", + "keys", "check-kxchap-secret", check_dhchap_key, + .device = COMMAND_NO_DEVICE), + DEPRECATED_FN("gen-tls-key", "Generate NVMeoF TLS PSK", + "keys", "gen-tls-psk", run_replacement, + .device = COMMAND_NO_DEVICE), + DEPRECATED_FN("check-tls-key", "Validate NVMeoF TLS PSK", + "keys", "check-tls-psk", run_replacement, + .device = COMMAND_NO_DEVICE), + DEPRECATED_FN("tls-key", "Manage NVMeoF TLS PSKs", + "keys", "import/export/revoke", tls_key, + .device = COMMAND_NO_DEVICE), #endif /* CONFIG_FABRICS */ - &dir_receive_cmd, - &dir_send_cmd, - &io_mgmt_recv_cmd, - &io_mgmt_send_cmd, - &nmi_recv_cmd, - &nmi_send_cmd, + DEPRECATED("dir-receive", + "Submit a Directive Receive command, return results", + "dir", "receive"), + DEPRECATED("dir-send", + "Submit a Directive Send command, return results", + "dir", "send"), + DEPRECATED("io-mgmt-recv", "I/O Management Receive", "io-mgmt", "recv"), + DEPRECATED("io-mgmt-send", "I/O Management Send", "io-mgmt", "send"), + DEPRECATED("nvme-mi-recv", + "Submit a NVMe-MI Receive command, return results", + "nvme-mi", "recv"), + DEPRECATED("nvme-mi-send", + "Submit a NVMe-MI Send command, return results", + "nvme-mi", "send"), NULL, }; diff --git a/src/plugin.c b/src/plugin.c index 0540e97415..b4f3b5c41e 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -480,6 +480,53 @@ static int run_joined_command(int argc, char **argv, struct plugin *plugin) return handle_plugin(argc + 1, sub_argv, extension); } +/* + * Warn that @cmd of @plugin is deprecated and run "nvme + * @command" with the arguments after argv[0] instead, the extension of + * @cmd's replacement. + */ +int run_replacement_command(struct plugin *plugin, struct command *cmd, + const char *command, int argc, char **argv) +{ + const char *name = cmd->replacement.plugin; + __cleanup_free char **sub_argv = NULL; + struct plugin *extension; + + extension = find_extension(plugin, name); + if (!extension) { + fprintf(stderr, + "ERROR: '%s' is deprecated and requires the '%s' plugin, which is not available in this build; use 'nvme %s %s'\n", + cmd->name, name, name, command); + return -ENOTTY; + } + + fprintf(stderr, + "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme %s %s' instead\n", + cmd->name, name, command); + + /* + * handle_plugin() skips argv[0] like a program name and dispatches + * on argv[1], so the arguments move one slot to the right. + */ + sub_argv = calloc(argc + 2, sizeof(*sub_argv)); + if (!sub_argv) + return -ENOMEM; + + sub_argv[0] = (char *)extension->name; + sub_argv[1] = (char *)command; + memcpy(&sub_argv[2], &argv[1], (argc - 1) * sizeof(*argv)); + + return handle_plugin(argc + 1, sub_argv, extension); +} + +/* The function of a deprecated command which only forwards */ +int run_replacement(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return run_replacement_command(plugin, cmd, cmd->replacement.command, + argc, argv); +} + /* "nvme --help " is "nvme help " */ static int run_help(int argc, char **argv, struct plugin *plugin) { diff --git a/src/plugin.h b/src/plugin.h index f8d10fd18c..bb2e7a9f80 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -53,6 +53,11 @@ struct command { int (*fn)(int argc, char **argv, struct command *acmd, struct plugin *plugin); char *alias; bool deprecated; + /* "nvme " which replaces a deprecated command */ + struct { + const char *plugin; + const char *command; + } replacement; enum command_device device; /* * The values --output-format accepts (enum nvme_print_flags), 0 for @@ -76,6 +81,10 @@ extern struct plugin builtin; void general_help(struct plugin *plugin, char *str); void command_prepare(struct command *cmd); int handle_plugin(int argc, char **argv, struct plugin *plugin); +int run_replacement(int argc, char **argv, struct command *cmd, + struct plugin *plugin); +int run_replacement_command(struct plugin *plugin, struct command *cmd, + const char *command, int argc, char **argv); void register_extension(struct plugin *plugin); From 51ddf32f7a9a432006d975da8b985e07a217432f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:08:43 +0000 Subject: [PATCH 094/263] nvme: check the operands of gen-dhchap-key and check-dhchap-key The two deprecated commands translate their options for the command which replaces them and pass only those on. An extra operand got dropped there instead of failing like for every other command. Split the operand check of parse_args() off into parse_cmdline(), which parses a command line without applying the output format, and use it for the two commands. Signed-off-by: Daniel Wagner --- src/args.c | 14 +++++++++++--- src/args.h | 7 +++++++ src/nvme-cmds-deprecated.c | 4 ++-- tests/cli/nvme_usage_test.py | 9 +++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/args.c b/src/args.c index b165ecb4e4..3af1742fe3 100644 --- a/src/args.c +++ b/src/args.c @@ -54,8 +54,8 @@ static int check_device(char *argv[], const char *desc, return -EINVAL; } -int parse_args(int argc, char *argv[], const char *desc, - struct argconfig_commandline_options *opts) +int parse_cmdline(int argc, char *argv[], const char *desc, + struct argconfig_commandline_options *opts) { int ret; @@ -63,7 +63,15 @@ int parse_args(int argc, char *argv[], const char *desc, if (ret) return ret; - ret = check_device(argv, desc, opts); + return check_device(argv, desc, opts); +} + +int parse_args(int argc, char *argv[], const char *desc, + struct argconfig_commandline_options *opts) +{ + int ret; + + ret = parse_cmdline(argc, argv, desc, opts); if (ret) return ret; diff --git a/src/args.h b/src/args.h index 3d183bbd18..25af17c00a 100644 --- a/src/args.h +++ b/src/args.h @@ -85,6 +85,13 @@ extern const char *namespace_id_desired; void nvme_args_register(void); void nvme_args_set_output_formats(nvme_print_flags_t formats, const char *custom_desc); +/* + * The options and the operand of a command. parse_args() also applies + * the output format, parse_cmdline() is for a command which passes its + * arguments on to another one. + */ +int parse_cmdline(int argc, char *argv[], const char *desc, + struct argconfig_commandline_options *opts); int parse_args(int argc, char *argv[], const char *desc, struct argconfig_commandline_options *opts); int validate_output_format(const char *format, nvme_print_flags_t *flags); diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index 3eefe00ef7..2d1c9728a9 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -68,7 +68,7 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl OPT_STR("nqn", 'n', &cfg.nqn, nqn), OPT_UINT("hmac", 'm', &cfg.hmac, hmac)); - err = argconfig_parse(argc, argv, desc, opts); + err = parse_cmdline(argc, argv, desc, opts); if (err) return err; @@ -121,7 +121,7 @@ static int check_dhchap_key(int argc, char **argv, struct command *acmd, struct NVME_ARGS(opts, OPT_STR("key", 'k', &cfg.key, key)); - err = argconfig_parse(argc, argv, desc, opts); + err = parse_cmdline(argc, argv, desc, opts); if (err) return err; diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index d7e8c37886..da2ca10d63 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -76,6 +76,15 @@ def test_no_device_command(self): self.assert_usage_error(run('list', 'extra'), "unexpected argument 'extra'") + def test_deprecated_command_which_translates(self): + # gen-dhchap-key rebuilds the arguments for its replacement + if run('gen-dhchap-key', '--help').returncode: + self.skipTest('no deprecated fabrics commands in this build') + self.assert_usage_error(run('gen-dhchap-key', 'extra'), + "unexpected argument 'extra'") + self.assert_usage_error(run('check-dhchap-key', 'extra'), + "unexpected argument 'extra'") + def test_optional_device_command(self): result = run('list-subsys', '--help') self.assertIn('Usage: nvme list-subsys [] [OPTIONS]', From 5bf2dd55a756baa59edf7bf2aee48817fc0f5522 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:10:23 +0000 Subject: [PATCH 095/263] nvme: parse the options of tls-key with argconfig tls-key was the last command which parsed its options with getopt. So it had no help, accepted abbreviated options, and dropped an extra operand when it translated its options for the keys command which replaces it. Parse them like every other command does. Signed-off-by: Daniel Wagner --- src/nvme-cmds-deprecated.c | 129 ++++++++++++++++------------------- tests/cli/nvme_usage_test.py | 14 ++++ 2 files changed, 72 insertions(+), 71 deletions(-) diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index 2d1c9728a9..a3e09976fc 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -11,7 +11,6 @@ * The deprecated commands, which run the commands that replace them. */ #include -#include #include #include #include @@ -143,90 +142,78 @@ static int check_dhchap_key(int argc, char **argv, struct command *acmd, struct */ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *plugin) { - static const struct option opts[] = { - { "keyring", required_argument, NULL, 'k' }, - { "keytype", required_argument, NULL, 't' }, - { "keyfile", required_argument, NULL, 'f' }, - { "import", no_argument, NULL, 'i' }, - { "export", no_argument, NULL, 'e' }, - { "revoke", required_argument, NULL, 'r' }, - { NULL, 0, NULL, 0 }, - }; - __cleanup_free char *keyring_opt = NULL; - __cleanup_free char *keytype_opt = NULL; - __cleanup_free char *keyfile_opt = NULL; - __cleanup_free char *identity_opt = NULL; - __cleanup_free char **sub_argv = NULL; - const char *keyring = NULL, *keytype = NULL, *keyfile = NULL, *revoke = NULL; + const char *desc = + "Import, export or revoke NVMe TLS PSKs.\n" + "Deprecated; use 'nvme keys import', 'export' or 'revoke' instead."; + const char *keyring = "Keyring of the PSKs."; + const char *keytype = "Key type of the PSK to revoke."; + const char *keyfile = "File to import the PSKs from or export them to."; + const char *import = "Import the PSKs from the file."; + const char *export = "Export the PSKs to the file."; + const char *revoke = "Revoke the PSK of this identity."; + + char *args[7] = { argv[0] }; const char *subcmd = NULL; - int nsub = 0, c; - - optind = 1; - while ((c = getopt_long(argc, argv, "k:t:f:ier:", opts, NULL)) != -1) { - switch (c) { - case 'k': - keyring = optarg; - break; - case 't': - keytype = optarg; - break; - case 'f': - keyfile = optarg; - break; - case 'i': - case 'e': - if (subcmd) { - fprintf(stderr, "ERROR: only one of --import, --export, or --revoke may be given\n"); - return -EINVAL; - } - subcmd = c == 'i' ? "import" : "export"; - break; - case 'r': - if (subcmd) { - fprintf(stderr, "ERROR: only one of --import, --export, or --revoke may be given\n"); - return -EINVAL; - } - subcmd = "revoke"; - revoke = optarg; - break; - default: - return -EINVAL; - } + int nargs = 1, err; + + struct config { + char *keyring; + char *keytype; + char *keyfile; + bool import; + bool export; + char *revoke; + }; + + struct config cfg = { 0 }; + + NVME_ARGS(opts, + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FLAG("import", 'i', &cfg.import, import), + OPT_FLAG("export", 'e', &cfg.export, export), + OPT_STR("revoke", 'r', &cfg.revoke, revoke)); + + err = parse_cmdline(argc, argv, desc, opts); + if (err) + return err; + + if (cfg.import + cfg.export + argconfig_given(opts, &cfg.revoke) > 1) { + fprintf(stderr, + "ERROR: only one of --import, --export, or --revoke may be given\n"); + return -EINVAL; } + if (cfg.import) + subcmd = "import"; + else if (cfg.export) + subcmd = "export"; + else if (argconfig_given(opts, &cfg.revoke)) + subcmd = "revoke"; if (!subcmd) { fprintf(stderr, "ERROR: 'tls-key' requires one of --import, --export, or --revoke\n"); return -EINVAL; } - sub_argv = calloc(5, sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[nsub++] = argv[0]; - - if (keyring) { - if (asprintf(&keyring_opt, "--keyring=%s", keyring) < 0) - return -ENOMEM; - sub_argv[nsub++] = keyring_opt; + if (argconfig_given(opts, &cfg.keyring)) { + args[nargs++] = "--keyring"; + args[nargs++] = cfg.keyring; } if (!strcmp(subcmd, "revoke")) { - if (keytype) { - if (asprintf(&keytype_opt, "--keytype=%s", keytype) < 0) - return -ENOMEM; - sub_argv[nsub++] = keytype_opt; + if (argconfig_given(opts, &cfg.keytype)) { + args[nargs++] = "--keytype"; + args[nargs++] = cfg.keytype; } - if (asprintf(&identity_opt, "--identity=%s", revoke) < 0) - return -ENOMEM; - sub_argv[nsub++] = identity_opt; - } else if (keyfile) { - if (asprintf(&keyfile_opt, "--keyfile=%s", keyfile) < 0) - return -ENOMEM; - sub_argv[nsub++] = keyfile_opt; + args[nargs++] = "--identity"; + args[nargs++] = cfg.revoke; + } else if (argconfig_given(opts, &cfg.keyfile)) { + args[nargs++] = "--keyfile"; + args[nargs++] = cfg.keyfile; } - return run_replacement_command(plugin, acmd, subcmd, nsub, sub_argv); + return run_replacement_command(plugin, acmd, subcmd, nargs, args); } #endif /* CONFIG_FABRICS */ diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index da2ca10d63..1d37908074 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -84,6 +84,20 @@ def test_deprecated_command_which_translates(self): "unexpected argument 'extra'") self.assert_usage_error(run('check-dhchap-key', 'extra'), "unexpected argument 'extra'") + self.assert_usage_error(run('tls-key', '--export', 'extra'), + "unexpected argument 'extra'") + + def test_tls_key(self): + # tls-key picks the command which replaces it from its options + if run('gen-dhchap-key', '--help').returncode: + self.skipTest('no deprecated fabrics commands in this build') + result = run('tls-key', '--help') + self.assertIn('Usage: nvme tls-key [OPTIONS]', result.stdout) + self.assertIn('--revoke', result.stdout) + result = run('tls-key', '--import', '--export') + self.assertEqual(result.returncode, 1) + self.assertIn('only one of --import, --export, or --revoke', + result.stderr) def test_optional_device_command(self): result = run('list-subsys', '--help') From c18dcf81d917a6fcf7b0b1b538c48ab119acbe4b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:14:15 +0000 Subject: [PATCH 096/263] plugins: declare the latency tracking options with OPT_FLAG() The intel and memblaze latency statistics tracking commands spell out their option entries. Use NVME_ARGS() and OPT_FLAG() like the other commands, which also checks the type of the variables. Signed-off-by: Daniel Wagner --- plugins/intel/intel-nvme.c | 8 +++----- plugins/memblaze/memblaze-nvme.c | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 4fdd617baf..c5e738fb48 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -1558,11 +1558,9 @@ static int enable_lat_stats_tracking(int argc, char **argv, .disable = false, }; - struct argconfig_commandline_options opts[] = { - {"enable", 'e', "", CFG_FLAG, &cfg.enable, no_argument, enable_desc}, - {"disable", 'd', "", CFG_FLAG, &cfg.disable, no_argument, disable_desc}, - {NULL} - }; + NVME_ARGS(opts, + OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), + OPT_FLAG("disable", 'd', &cfg.disable, disable_desc)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 3e35655fdb..b1023cd635 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -1065,11 +1065,9 @@ static int mb_set_lat_stats(int argc, char **argv, struct command *acmd, struct .disable = false, }; - struct argconfig_commandline_options opts[] = { - {"enable", 'e', "", CFG_FLAG, &cfg.enable, no_argument, enable_desc}, - {"disable", 'd', "", CFG_FLAG, &cfg.disable, no_argument, disable_desc}, - {NULL} - }; + NVME_ARGS(opts, + OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), + OPT_FLAG("disable", 'd', &cfg.disable, disable_desc)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); From b61346400d748fb0f51f9f42a09cf3992aed624b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:15:56 +0000 Subject: [PATCH 097/263] argconfig: drop the getopt header argconfig parses the command line itself, but took the values of no_argument, required_argument and optional_argument from getopt.h, and so did every file which included it. Give argconfig its own enum for them. The getopt reference of the differential test maps it to getopt's values. Drop the includes of getopt.h which nothing needs any more. Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 1 - plugins/utils/command-metadata.c | 7 +++--- plugins/utils/command-metadata.h | 2 +- src/argconfig.c | 22 +++++++++--------- src/argconfig.h | 35 +++++++++++++++++------------ src/fabrics.c | 1 - src/global-ctx.c | 1 - src/nvme-cmds-enumerate.c | 1 - src/nvme-cmds-fabrics.c | 1 - src/nvme-cmds-feature.c | 1 - src/nvme-cmds-io.c | 1 - src/nvme-cmds-passthru.c | 1 - src/nvme-cmds-registers.c | 1 - src/nvme-cmds-sanitize.c | 1 - src/nvme.c | 1 - tests/unit/c/argconfig-getopt-ref.c | 29 +++++++++++++++++------- 16 files changed, 57 insertions(+), 49 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 63b1c7a201..7780310b3c 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 25ca8235ac..e05660a06b 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -571,9 +570,9 @@ static bool opt_is_global_separator(const struct command_metadata_option *o) static const char *opt_argument(const struct command_metadata_option *o) { switch (o->argument_type) { - case optional_argument: + case ARGCONFIG_ARG_OPTIONAL: return "optional"; - case no_argument: + case ARGCONFIG_ARG_NONE: return "none"; default: return "required"; @@ -582,7 +581,7 @@ static const char *opt_argument(const struct command_metadata_option *o) static bool opt_takes_value(const struct command_metadata_option *o) { - return o->argument_type != no_argument; + return o->argument_type != ARGCONFIG_ARG_NONE; } /* diff --git a/plugins/utils/command-metadata.h b/plugins/utils/command-metadata.h index 4a480d9327..6f4e80ed30 100644 --- a/plugins/utils/command-metadata.h +++ b/plugins/utils/command-metadata.h @@ -24,7 +24,7 @@ struct command_metadata_option { char short_option; /* 0 if none */ const char *meta; /* metavar / value placeholder ("NUM"/"FMT"/...) or NULL */ enum argconfig_types config_type; - int argument_type; /* no_/required_/optional_argument */ + enum argconfig_argument argument_type; const char *help; const struct argconfig_opt_val *opt_val; /* NULL or .str==NULL terminated */ bool hidden; /* not shown in help/completion */ diff --git a/src/argconfig.c b/src/argconfig.c index a5a8ee2306..d78a4bec58 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -117,18 +117,18 @@ static void show_option(FILE *out, b += sprintf(b, " [ "); if (option->option) { b += sprintf(b, " --%s", option->option); - if (option->argument_type == optional_argument) + if (option->argument_type == ARGCONFIG_ARG_OPTIONAL) b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == required_argument) + if (option->argument_type == ARGCONFIG_ARG_REQUIRED) b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg"); if (option->short_option) b += sprintf(b, ","); } if (option->short_option) { b += sprintf(b, " -%c", option->short_option); - if (option->argument_type == optional_argument) + if (option->argument_type == ARGCONFIG_ARG_OPTIONAL) b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == required_argument) + if (option->argument_type == ARGCONFIG_ARG_REQUIRED) b += sprintf(b, " <%s>", option->meta ? option->meta : "arg"); } b += sprintf(b, " ] "); @@ -558,20 +558,20 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) } switch (s->argument_type) { - case no_argument: + case ARGCONFIG_ARG_NONE: if (eq) return argconfig_parse_error(p, "option '--%s' doesn't allow an argument", s->option); break; - case required_argument: + case ARGCONFIG_ARG_REQUIRED: value = eq ? eq + 1 : argconfig_next_arg(p); if (!value) return argconfig_parse_error(p, "option '--%s' requires an argument", s->option); break; - case optional_argument: + case ARGCONFIG_ARG_OPTIONAL: /* Only an attached value, "--name value" is an operand */ if (eq) value = eq + 1; @@ -607,23 +607,23 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) value = NULL; switch (s->argument_type) { - case no_argument: + case ARGCONFIG_ARG_NONE: break; - case required_argument: + case ARGCONFIG_ARG_REQUIRED: value = c[1] ? c + 1 : argconfig_next_arg(p); if (!value) return argconfig_parse_error(p, "option requires an argument -- '%c'", *c); break; - case optional_argument: + case ARGCONFIG_ARG_OPTIONAL: if (c[1]) value = c + 1; break; } ret = argconfig_apply(s, value); - if (ret || s->argument_type != no_argument) + if (ret || s->argument_type != ARGCONFIG_ARG_NONE) return ret; } diff --git a/src/argconfig.h b/src/argconfig.h index 0a573de2b7..9e0d2fcd7b 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -32,7 +32,6 @@ */ #pragma once -#include #include #include #include @@ -82,47 +81,48 @@ enum argconfig_types { #define OPT_END() { NULL } #define OPT_GROUP(d) \ - {"", 0, NULL, CFG_GROUP_SEPARATOR, NULL, no_argument, d, false, NULL} + {"", 0, NULL, CFG_GROUP_SEPARATOR, NULL, ARGCONFIG_ARG_NONE, d, false, \ + NULL} #define OPT_FLAG(l, s, v, d, ...) \ {l, s, NULL, CFG_FLAG, OPT_CHECK_BOOL(v), \ - no_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_NONE, d, false, __VA_ARGS__} #define OPT_SUFFIX(l, s, v, d, ...) \ {l, s, "IONUM", CFG_LONG_SUFFIX, OPT_CHECK_64(v), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_UINT(l, s, v, d, ...) \ {l, s, "NUM", CFG_POSITIVE, OPT_CHECK(v, uint32_t *), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_INT(l, s, v, d, ...) \ {l, s, "NUM", CFG_INT, OPT_CHECK(v, int *), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_LONG(l, s, v, d, ...) \ {l, s, "NUM", CFG_LONG, OPT_CHECK_64(v), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_DOUBLE(l, s, v, d, ...) \ {l, s, "NUM", CFG_DOUBLE, OPT_CHECK_DOUBLE(v), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_BYTE(l, s, v, d, ...) \ {l, s, "NUM", CFG_BYTE, OPT_CHECK(v, uint8_t *), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_SHRT(l, s, v, d, ...) \ {l, s, "NUM", CFG_SHORT, OPT_CHECK(v, uint16_t *), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} #define OPT_INCR(l, s, v, d, ...) \ {l, s, "NUM", CFG_INCREMENT, OPT_CHECK(v, int *), \ - no_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_NONE, d, false, __VA_ARGS__} #define OPT_STRING(l, s, m, v, d, ...) \ {l, s, m, CFG_STRING, OPT_CHECK_STR(v), \ - required_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} /* * Like OPT_STRING(), but the value is optional. getopt_long() only @@ -133,7 +133,7 @@ enum argconfig_types { */ #define OPT_STRING_OPTIONAL(l, s, m, v, d, ...) \ {l, s, m, CFG_STRING, OPT_CHECK_STR(v), \ - optional_argument, d, false, __VA_ARGS__} + ARGCONFIG_ARG_OPTIONAL, d, false, __VA_ARGS__} #define OPT_FMT(l, s, v, d, ...) OPT_STRING(l, s, "FMT", v, d, __VA_ARGS__) #define OPT_FILE(l, s, v, d, ...) OPT_STRING(l, s, "FILE", v, d, __VA_ARGS__) @@ -207,13 +207,20 @@ struct argconfig_opt_val { union argconfig_val val; }; +/* Whether an option takes a value */ +enum argconfig_argument { + ARGCONFIG_ARG_NONE, + ARGCONFIG_ARG_REQUIRED, + ARGCONFIG_ARG_OPTIONAL, +}; + struct argconfig_commandline_options { const char *option; const char short_option; const char *meta; enum argconfig_types config_type; void *default_value; - int argument_type; + enum argconfig_argument argument_type; const char *help; bool seen; struct argconfig_opt_val *opt_val; diff --git a/src/fabrics.c b/src/fabrics.c index 3ffa82a58b..0ea43b0ead 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/src/global-ctx.c b/src/global-ctx.c index dec21f88dd..f45d1b850c 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -7,7 +7,6 @@ */ #include #include -#include #include #include #include diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 87ad494046..3e45856d26 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -13,7 +13,6 @@ * than sending a specific NVMe command to a controller. */ #include -#include #include #include #include diff --git a/src/nvme-cmds-fabrics.c b/src/nvme-cmds-fabrics.c index 1b0d5840d6..c202dbbfc9 100644 --- a/src/nvme-cmds-fabrics.c +++ b/src/nvme-cmds-fabrics.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index 0021d05825..67834ca59a 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 02425844cf..018f88c50c 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme-cmds-passthru.c b/src/nvme-cmds-passthru.c index be2a4c0369..23e20d6f1f 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index a631c44d07..c2f7833635 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 150ff61e06..6d290d7e57 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/src/nvme.c b/src/nvme.c index 8f9f028386..c5a2a09645 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 63cca76ca9..43678efd42 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -24,6 +24,19 @@ #include "../../../src/cleanup.h" #include "argconfig-getopt-ref.h" +/* getopt's has_arg for an option */ +static int has_arg(struct argconfig_commandline_options *s) +{ + switch (s->argument_type) { + case ARGCONFIG_ARG_REQUIRED: + return required_argument; + case ARGCONFIG_ARG_OPTIONAL: + return optional_argument; + default: + return no_argument; + } +} + static bool is_null_or_empty(const char *s) { return !s || !*s; @@ -240,15 +253,15 @@ int ref_argconfig_parse(int argc, char *argv[], continue; if (s->short_option) { short_opts[short_index++] = s->short_option; - if (s->argument_type == required_argument || - s->argument_type == optional_argument) + if (s->argument_type == ARGCONFIG_ARG_REQUIRED || + s->argument_type == ARGCONFIG_ARG_OPTIONAL) short_opts[short_index++] = ':'; - if (s->argument_type == optional_argument) + if (s->argument_type == ARGCONFIG_ARG_OPTIONAL) short_opts[short_index++] = ':'; } if (!is_null_or_empty(s->option)) { long_opts[long_opt_index].name = s->option; - long_opts[long_opt_index].has_arg = s->argument_type; + long_opts[long_opt_index].has_arg = has_arg(s); long_opt_map[long_opt_index] = opt_index; long_opt_index++; } @@ -332,15 +345,15 @@ int ref_argconfig_parse_global(int argc, char *argv[], continue; if (s->short_option) { short_opts[short_index++] = s->short_option; - if (s->argument_type == required_argument || - s->argument_type == optional_argument) + if (s->argument_type == ARGCONFIG_ARG_REQUIRED || + s->argument_type == ARGCONFIG_ARG_OPTIONAL) short_opts[short_index++] = ':'; - if (s->argument_type == optional_argument) + if (s->argument_type == ARGCONFIG_ARG_OPTIONAL) short_opts[short_index++] = ':'; } if (!is_null_or_empty(s->option)) { long_opts[long_opt_index].name = s->option; - long_opts[long_opt_index].has_arg = s->argument_type; + long_opts[long_opt_index].has_arg = has_arg(s); long_opt_map[long_opt_index] = opt_index; long_opt_index++; } From 4b99182450c1d0833994b7a79517579a2ac50618 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:16:08 +0000 Subject: [PATCH 098/263] completions: update the shell completions Offer the options of tls-key, which now has them in its help. Signed-off-by: Daniel Wagner --- completions/_nvme | 33 +++++++++++++++++++++++++++++ completions/bash-nvme-completion.sh | 6 ++++++ completions/nvme-completion.ps1 | 6 +++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/completions/_nvme b/completions/_nvme index 7540f7ce5a..95b91ba57f 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -16462,6 +16462,39 @@ _nvme () { ;; (tls-key) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return + local _tls_key + _tls_key=( + --dry-run':show command instead of executing' + --export':Export the PSKs to the file.' + -e':alias for --export' + --import':Import the PSKs from the file.' + -i':alias for --import' + --keyfile=':File to import the PSKs from or export them to.' + -f':alias for --keyfile' + --keyring=':Keyring of the PSKs.' + -k':alias for --keyring' + --keytype=':Key type of the PSK to revoke.' + -t':alias for --keytype' + --no-ioctl-probing':disable 64-bit IOCTL support probing' + --no-retries':disable retry logic on errors' + --output-format=':Output format: normal|json|binary' + -o':alias for --output-format' + --output-format-version=':output format version: 1|2' + --quiet':suppress informational output' + --revoke=':Revoke the PSK of this identity.' + -r':alias for --revoke' + --set-options=':set a libnvme library option (key=value[,key=value,...]);' + --timeout=':timeout value, in milliseconds' + --verbose':Increase output verbosity' + -v':alias for --verbose' + --help':show help' + -h':alias for --help' + ) + _arguments '*:: :->subcmds' + _nvme_describe "nvme tls-key options" _tls_key ;; (top) diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index d2a1acb696..f8b146c3ca 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -1223,6 +1223,12 @@ nvme_list_opts () { ;; "tls-key") + opts+=" --dry-run --export -e --import -i --keyfile= -f --keyring= -k --keytype= -t --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --revoke= -r --set-options= --timeout= --verbose -v" + valopts+=" --keyfile -f --keyring -k --keytype -t --output-format -o --output-format-version --revoke -r --set-options --timeout" + + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" nodevice=1 ;; diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 655cf9b251..f412e0ffe7 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -658,7 +658,7 @@ $script:NvmeOptions = @{ 'supported-cap-config-log' = @('--domain-id', '-d', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'supported-log-pages' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'telemetry-log' = @('--controller-init', '-c', '--data-area', '-d', '--dry-run', '--force', '--host-generate', '-g', '--mcda', '-m', '--no-ioctl-probing', '--no-retries', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--rae', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'tls-key' = @('--help', '-h') + 'tls-key' = @('--dry-run', '--export', '-e', '--import', '-i', '--keyfile', '-f', '--keyring', '-k', '--keytype', '-t', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--revoke', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'top' = @('--delay', '-d', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'toshiba clear-pcie-correctable-errors' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'toshiba vs-internal-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--prev-log', '-p', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -2909,6 +2909,10 @@ $script:NvmeOptionValues = @{ 'telemetry-log --output-format-version' = @('1', '2') 'telemetry-log --timeout' = @('infinite', '-1') 'telemetry-log -o' = @('normal', 'json', 'binary') + 'tls-key --output-format' = @('normal', 'json', 'binary') + 'tls-key --output-format-version' = @('1', '2') + 'tls-key --timeout' = @('infinite', '-1') + 'tls-key -o' = @('normal', 'json', 'binary') 'top --output-format' = @('normal') 'top --output-format-version' = @('1', '2') 'top --timeout' = @('infinite', '-1') From 372661686594de17e25940ef7f3b12d86c314407 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 09:16:08 +0000 Subject: [PATCH 099/263] NEWS: document the tls-key and dhchap key command changes tls-key has a help now and rejects abbreviations, and the three commands which translate their options reject extra operands. Signed-off-by: Daniel Wagner --- NEWS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index aa8090775e..2e5e488933 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,11 @@ * Number options reject text after the number. Before, the rest was ignored: `--timeout=10s` set a timeout of 10 milliseconds. +* The deprecated `tls-key` command parses its options like every other + command: it has a help, and abbreviated options such as `--keyr` are + no longer accepted. It, `gen-dhchap-key` and `check-dhchap-key` + reject extra operands now instead of dropping them. + * The human-readable stdout output of `id-ctrl`, `id-ns`, and most other identify/log-page/feature commands is now rendered through a computed-width table instead of hand-aligned `printf()` calls. From 333675a1c75e1a44ff9aa76a744a1aaf2ec56a58 Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Fri, 16 Oct 2026 07:01:11 -0400 Subject: [PATCH 100/263] nvme-discoverd: tear down the host-name test target The wringer created port 11 and the host-name subsystems, but nvmet_teardown() did not remove them. They stayed in configfs after every run. Signed-off-by: Martin Belanger --- discoverd/tests/wringer.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/discoverd/tests/wringer.sh b/discoverd/tests/wringer.sh index 2e0727fa1a..6dac34cdcc 100755 --- a/discoverd/tests/wringer.sh +++ b/discoverd/tests/wringer.sh @@ -341,13 +341,14 @@ nvmet_teardown() { for id in "${DISC_PORT_ID}" "${FOREIGN_PORT_ID}" "${CONF_PORT_ID}" \ "${V6_PORT_ID}" "${LL_PORT_ID}" "${REL_PORT_ID}" \ "${MDNS_PORT_ID}" "${NL_PORT_ID}" "${MAP_PORT_ID}" \ - "${REF_PORT_ID}"; do + "${REF_PORT_ID}" "${HN_PORT_ID}"; do rm -f /sys/kernel/config/nvmet/ports/"${id}"/subsystems/* rmdir "/sys/kernel/config/nvmet/ports/${id}" 2>/dev/null done for nqn in "${TARGET_NQN}" "${FOREIGN_NQN}" "${CONF_NQN}" \ "${V6_NQN}" "${LL_NQN}" "${REL_NQN}" "${REL2_NQN}" \ - "${MDNS_NQN}" "${NL_NQN}" "${MAP_NQN}" "${REF_NQN}"; do + "${MDNS_NQN}" "${NL_NQN}" "${MAP_NQN}" "${REF_NQN}" \ + "${HN_NQN}" "${HN2_NQN}"; do local subsys_dir="/sys/kernel/config/nvmet/subsystems/${nqn}" if [ -e "${subsys_dir}/namespaces/1/enable" ]; then From b5e4320f629025ef7cda03509828c99663560ecc Mon Sep 17 00:00:00 2001 From: Martin Belanger Date: Fri, 16 Oct 2026 07:01:11 -0400 Subject: [PATCH 101/263] nvme-discoverd: run connect-all for an unowned discovery controller A Discovery Controller connected by hand without --owner, e.g. with "nvme discover --persistent", is not tracked by nvme-discoverd. Its events were ignored. The legacy udev rules handle them, but a default build does not install these rules, so nothing handled them. nvme-discoverd now runs "nvme connect-all" for such a controller, with the same arguments as 70-nvmf-autoconnect.rules. It passes no --owner, so the new connections have no owner, like the controller, and "nvme disconnect-all" removes them. Events from a controller with another owner are still ignored. Signed-off-by: Martin Belanger --- Documentation/nvme-discoverd.txt | 8 +++ NEWS.md | 7 ++ discoverd/README.md | 1 + discoverd/src/events.c | 2 +- discoverd/src/events.h | 5 +- discoverd/src/main.c | 48 ++++++++++++- discoverd/src/units.c | 111 +++++++++++++++++++++++++++++++ discoverd/src/units.h | 18 +++++ discoverd/tests/wringer.sh | 95 +++++++++++++++++++++++++- src/fabrics.c | 7 +- 10 files changed, 292 insertions(+), 10 deletions(-) diff --git a/Documentation/nvme-discoverd.txt b/Documentation/nvme-discoverd.txt index 8d8324a536..d375d08744 100644 --- a/Documentation/nvme-discoverd.txt +++ b/Documentation/nvme-discoverd.txt @@ -45,6 +45,14 @@ controller it connects (--owner discoverd, or --owner nbft for an NBFT-sourced controller) in the NVMe-oF ownership registry. See nvme-registry-list(1) and nvme-exclusion-add(1). +A Discovery Controller that has no owner, for example one connected +with 'nvme discover --persistent', is not tracked. When its discovery +log changes, or when it reconnects, nvme-discoverd runs +'nvme connect-all' for it in a transient unit, as the legacy udev +rules do. The connections it makes have no owner, and +'nvme disconnect-all' removes them. Events from a Discovery +Controller with another owner are ignored. + nvme-discoverd is still experimental. It is meant to replace the older udev-rule/systemd-unit autoconnect mechanism. By default, the legacy mechanism is built only when nvme-discoverd is not. Building diff --git a/NEWS.md b/NEWS.md index 2e5e488933..6f4bf28a22 100644 --- a/NEWS.md +++ b/NEWS.md @@ -272,6 +272,13 @@ Before, the connection was skipped until the next reload. While the first lookups run, nvme-discoverd does not release any controller. +* nvme-discoverd handles the events of a Discovery Controller that has + no owner, for example one connected with + `nvme discover --persistent`. It runs `nvme connect-all` for it, as + `70-nvmf-autoconnect.rules` does. The new connections have no owner + either. Before, nothing handled these events when the legacy udev + rules were not installed. + ### libnvme * A failed read of `/dev/nvme-fabrics` is no longer cached for the diff --git a/discoverd/README.md b/discoverd/README.md index 2f9b701715..ccb568628a 100644 --- a/discoverd/README.md +++ b/discoverd/README.md @@ -16,6 +16,7 @@ The host's alternative today is a udev-rule-triggered swarm of systemd units (`7 - **Connect-only.** nvme-discoverd never disconnects a live controller because of a discovery change. That's TP8010 fabric-zoning territory, out of scope here. The only disconnects it causes are its own transient units' `ExecStop=`, at shutdown or on request. - **Retry with backoff and a give-up horizon.** A failed (re)connect retries with exponential backoff, capped at 5 minutes. NBFT and fabrics-config controllers represent explicit intent, so they always retry forever. A Discovery Controller found only through a referral, FC kickstart, or mDNS is different: it gives up after `dc-giveup-timeout` (default 72hours) of unbroken failure and is dropped from tracking; `infinity` makes it retry forever too. - **Cooperative, not exclusive.** Before connecting or reconnecting anything, nvme-discoverd checks the system-wide exclusion list and registers ownership in the ownership registry (`--owner discoverd`, or `--owner nbft` for an NBFT-sourced controller). See `libnvme/design/REGISTRY.md` and `EXCLUSIONS.md`. +- **Unowned Discovery Controllers.** A DC that has no owner, e.g. from `nvme discover --persistent`, is not tracked. On its log page change AEN or rediscover event, nvme-discoverd runs `nvme connect-all --device=nvmeX` once in a transient unit, as `70-nvmf-autoconnect.rules` does. It passes no `--owner`, so the new connections have no owner either. Events from a DC with another owner are ignored. ## Source layout diff --git a/discoverd/src/events.c b/discoverd/src/events.c index fe09b93ab6..d06e575af0 100644 --- a/discoverd/src/events.c +++ b/discoverd/src/events.c @@ -258,7 +258,7 @@ static int nvme_monitor_handler(sd_device_monitor *monitor __attribute__((unused (shr_streq0(nvme_event, "rediscover") && shr_streq0(cntrltype, "discovery"))) { if (ctx->callbacks.dc_changed) - ctx->callbacks.dc_changed(devname, + ctx->callbacks.dc_changed(devname, dev, ctx->user_data); } break; diff --git a/discoverd/src/events.h b/discoverd/src/events.h index d9957b4c64..fbc7730c12 100644 --- a/discoverd/src/events.h +++ b/discoverd/src/events.h @@ -27,9 +27,10 @@ struct events_callbacks { /* * DC sent NVME_AEN=="0x70f002" (DLP changed) or was rediscovered * after a ctrl-loss (NVME_EVENT=="rediscover"). Caller should - * re-fetch the DLP for the named device. + * re-fetch the DLP for the named device. @dev is the uevent. */ - void (*dc_changed)(const char *devname, void *user_data); + void (*dc_changed)(const char *devname, sd_device *dev, + void *user_data); /* * nvmeX device appeared with cntrltype=="io". diff --git a/discoverd/src/main.c b/discoverd/src/main.c index 74479899d9..ff432182c9 100644 --- a/discoverd/src/main.c +++ b/discoverd/src/main.c @@ -1236,13 +1236,57 @@ static void on_dc_add(const char *devname, dc_connected(e, devname); } -static void on_dc_changed(const char *devname, +/* + * A DC that nobody owns, e.g. from a manual "nvme discover --persistent". + * Without the legacy udev rules, nothing else handles its events. Run + * connect-all for it, as those rules do. The connections it makes stay + * unowned, like the DC. + */ +static void dc_changed_unowned(const char *devname, sd_device *dev) +{ + __cleanup_free char *owner = NULL; + struct dc_addr addr = { }; + int r; + + r = libnvmf_registry_retrieve(ctx.nvme_ctx, devname, "owner", &owner); + if (r < 0 && r != -ENOENT) { + log_warn("%s - failed to check registry owner: %s", + devname, strerror(-r)); + return; + } + if (owner) { + log_dbg("%s - discovery log changed, owned by '%s', skipping", + devname, owner); + return; + } + + sd_device_get_property_value(dev, "NVME_TRTYPE", &addr.transport); + sd_device_get_property_value(dev, "NVME_TRADDR", &addr.traddr); + sd_device_get_property_value(dev, "NVME_TRSVCID", &addr.trsvcid); + sd_device_get_property_value(dev, "NVME_HOST_TRADDR", + &addr.host_traddr); + sd_device_get_property_value(dev, "NVME_HOST_IFACE", + &addr.host_iface); + + log_info("%s - discovery log changed on an unowned DC, running connect-all", + devname); + r = unit_start_connect_all(ctx.umgr, devname, &addr); + if (r < 0) + log_err("%s - failed to start connect-all: %s", + devname, strerror(-r)); +} + +static void on_dc_changed(const char *devname, sd_device *dev, void *user_data __attribute__((unused))) { struct active_ctrl *e; e = ctrl_find_by_devname(devname); - if (!e || !e->tid) { + if (!e) { + dc_changed_unowned(devname, dev); + return; + } + if (!e->tid) { log_warn("%s - dc_changed for untracked device", devname); return; } diff --git a/discoverd/src/units.c b/discoverd/src/units.c index 3c9f99755f..14ffb5afe1 100644 --- a/discoverd/src/units.c +++ b/discoverd/src/units.c @@ -20,6 +20,7 @@ #include #include +#include #include "state.h" #include "units.h" @@ -830,6 +831,116 @@ int unit_start_ioc(struct unit_mgr *mgr, const struct libnvmf_tid *t, return r; } +static int build_connect_all(struct unit_mgr *mgr, const char *unit_name, + const char *devname, const char **argv, + sd_bus_message **out) +{ + sd_bus_message *m = NULL; + char desc[128]; + int r; + + snprintf(desc, sizeof(desc), + "NVMe connect-all for Discovery Controller %s", devname); + + r = sd_bus_message_new_method_call(mgr->bus, &m, + SYSTEMD_BUS_NAME, + SYSTEMD_OBJ_PATH, + SYSTEMD_MGR_IFACE, + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", unit_name, "replace"); + if (r < 0) + goto err; + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + goto err; + r = append_string_prop(m, "Type", "oneshot"); + if (r < 0) + goto err; + r = append_string_prop(m, "Description", desc); + if (r < 0) + goto err; + r = append_string_prop(m, "CollectMode", "inactive-or-failed"); + if (r < 0) + goto err; + r = append_exec_prop(m, "ExecStart", argv, false); + if (r < 0) + goto err; + r = sd_bus_message_close_container(m); // a(sv) + if (r < 0) + goto err; + + /* Empty auxiliary units array. */ + r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); + if (r < 0) + goto err; + r = sd_bus_message_close_container(m); + if (r < 0) + goto err; + + *out = m; + return 0; +err: + sd_bus_message_unref(m); + return r; +} + +int unit_start_connect_all(struct unit_mgr *mgr, const char *devname, + const struct dc_addr *addr) +{ + static const char *const names[] = { + "transport", "traddr", "trsvcid", "host-traddr", "host-iface", + }; + const char *const vals[] = { + addr->transport, addr->traddr, addr->trsvcid, + addr->host_traddr, addr->host_iface, + }; + char opts[ARRAY_SIZE(names) + 1][320]; + const char *argv[ARRAY_SIZE(names) + 5]; + __cleanup_free char *unit_name = NULL; + sd_bus_message *m = NULL; + int argc = 0, n, r; + size_t i; + + if (asprintf(&unit_name, "nvme-discoverd-connect-all-%s.service", + devname) < 0) + return -ENOMEM; + + argv[argc++] = mgr->nvme_path; + argv[argc++] = "connect-all"; + + n = snprintf(opts[0], sizeof(opts[0]), "--device=%s", devname); + if (n < 0 || n >= (int)sizeof(opts[0])) + return -ENAMETOOLONG; + argv[argc++] = opts[0]; + + // The kernel reports a missing value as "none". + for (i = 0; i < ARRAY_SIZE(names); i++) { + if (!vals[i] || !*vals[i] || streq(vals[i], "none")) + continue; + + n = snprintf(opts[i + 1], sizeof(opts[i + 1]), "--%s=%s", + names[i], vals[i]); + if (n < 0 || n >= (int)sizeof(opts[i + 1])) + return -ENAMETOOLONG; + argv[argc++] = opts[i + 1]; + } + + argv[argc++] = "--quiet"; + argv[argc] = NULL; + + r = build_connect_all(mgr, unit_name, devname, argv, &m); + if (r < 0) + return r; + + r = do_start_transient(mgr, m, unit_name); + sd_bus_message_unref(m); + + return r; +} + int unit_restart(struct unit_mgr *mgr, const char *unit_name) { sd_bus_error err = SD_BUS_ERROR_NULL; diff --git a/discoverd/src/units.h b/discoverd/src/units.h index 4f4f4b983b..6dd20a6719 100644 --- a/discoverd/src/units.h +++ b/discoverd/src/units.h @@ -66,6 +66,24 @@ int unit_start_dc(struct unit_mgr *mgr, const struct libnvmf_tid *t, int unit_start_ioc(struct unit_mgr *mgr, const struct libnvmf_tid *t, const struct libnvmf_params *params, bool is_nbft); +/* Address of a connected DC, as its uevent reports it. */ +struct dc_addr { + const char *transport; + const char *traddr; + const char *trsvcid; + const char *host_traddr; + const char *host_iface; +}; + +/* + * Run "nvme connect-all" once over the existing connection to DC + * @devname, as 70-nvmf-autoconnect.rules does. No --owner is given. + * The unit goes away when the command exits. Starting it again while it + * runs replaces the running instance. + */ +int unit_start_connect_all(struct unit_mgr *mgr, const char *devname, + const struct dc_addr *addr); + /* * Restart an existing unit (reconnect with baked-in parameters). Fails if * systemd no longer has the unit. diff --git a/discoverd/tests/wringer.sh b/discoverd/tests/wringer.sh index 6dac34cdcc..b3b1b88d2d 100755 --- a/discoverd/tests/wringer.sh +++ b/discoverd/tests/wringer.sh @@ -186,6 +186,13 @@ HN2_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:host-name-late HN_PORT=8018 HN_PORT_ID=11 +# A DC connected by hand, not tracked by nvme-discoverd. +UO_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:unowned +UO2_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:unowned2 +UO3_NQN=nqn.2026-10.org.nvmexpress.discoverd-wringer:unowned3 +UO_PORT=8019 +UO_PORT_ID=12 + # mDNS phases only. Advertised through mDNS, on ${IFACE}'s address. The # port is opened and closed per phase, so a phase can advertise a DC whose # port is not open yet. @@ -341,14 +348,15 @@ nvmet_teardown() { for id in "${DISC_PORT_ID}" "${FOREIGN_PORT_ID}" "${CONF_PORT_ID}" \ "${V6_PORT_ID}" "${LL_PORT_ID}" "${REL_PORT_ID}" \ "${MDNS_PORT_ID}" "${NL_PORT_ID}" "${MAP_PORT_ID}" \ - "${REF_PORT_ID}" "${HN_PORT_ID}"; do + "${REF_PORT_ID}" "${HN_PORT_ID}" "${UO_PORT_ID}"; do rm -f /sys/kernel/config/nvmet/ports/"${id}"/subsystems/* rmdir "/sys/kernel/config/nvmet/ports/${id}" 2>/dev/null done for nqn in "${TARGET_NQN}" "${FOREIGN_NQN}" "${CONF_NQN}" \ "${V6_NQN}" "${LL_NQN}" "${REL_NQN}" "${REL2_NQN}" \ "${MDNS_NQN}" "${NL_NQN}" "${MAP_NQN}" "${REF_NQN}" \ - "${HN_NQN}" "${HN2_NQN}"; do + "${HN_NQN}" "${HN2_NQN}" "${UO_NQN}" "${UO2_NQN}" \ + "${UO3_NQN}"; do local subsys_dir="/sys/kernel/config/nvmet/subsystems/${nqn}" if [ -e "${subsys_dir}/namespaces/1/enable" ]; then @@ -448,6 +456,35 @@ discoverd_stop() { "${NVME_BIN}" disconnect -n "${REF_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${HN_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${HN2_NQN}" >/dev/null 2>&1 || true + disconnect_unowned +} + +# Discovery controller connected to port $1, or non-zero if there is none. +port_dc_dev() { + local port="$1" d + + for d in /sys/class/nvme/nvme*; do + [ "$(cat "${d}/cntrltype" 2>/dev/null)" = discovery ] || + continue + if grep -q "trsvcid=${port}\(,\|$\)" "${d}/address" \ + 2>/dev/null; then + basename "${d}" + return 0 + fi + done + return 1 +} + +# Disconnect what the unowned DC phases connected by hand. +disconnect_unowned() { + local dev + + while dev=$(port_dc_dev "${UO_PORT}"); do + "${NVME_BIN}" disconnect -d "${dev}" >/dev/null 2>&1 || break + done + "${NVME_BIN}" disconnect -n "${UO_NQN}" >/dev/null 2>&1 || true + "${NVME_BIN}" disconnect -n "${UO2_NQN}" >/dev/null 2>&1 || true + "${NVME_BIN}" disconnect -n "${UO3_NQN}" >/dev/null 2>&1 || true } # Is any nvme-discoverd transient unit loaded? @@ -895,6 +932,7 @@ disconnect_foreign "${NVME_BIN}" disconnect -n "${REF_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${HN_NQN}" >/dev/null 2>&1 || true "${NVME_BIN}" disconnect -n "${HN2_NQN}" >/dev/null 2>&1 || true +disconnect_unowned # ... and left its desired controllers saved. rm -f "${DESIRED_FILE}" @@ -1391,6 +1429,59 @@ countdown 3 "give a release pass time to run" assert_journal_lacks "the IOC was not released" \ "${P_START}" "${P_DEV} - .*released" +phase "an AEN from an unowned DC runs connect-all" +# +# A DC connected by hand without --owner is not tracked. nvme-discoverd +# runs connect-all for it, as the legacy udev rules do. The connections +# stay unowned. nvme discover connects no IOC, so the AEN is what +# connects both subsystems. +nvmet_add_subsystem "${UO_NQN}" +nvmet_add_port "${UO_PORT_ID}" "${UO_PORT}" "${UO_NQN}" +"${NVME_BIN}" discover -t tcp -a "${TRADDR}" -s "${UO_PORT}" \ + --persistent=force >/dev/null 2>&1 +P_DC=$(port_dc_dev "${UO_PORT}") +if [ -z "${P_DC}" ]; then + fail "the DC is connected by hand" +else + pass "the DC is connected by hand" +fi +nvmet_add_subsystem "${UO2_NQN}" +P_START=$(date +%H:%M:%S) +log "nvmet: port ${UO_PORT} also serves ${UO2_NQN}" +ln -s "/sys/kernel/config/nvmet/subsystems/${UO2_NQN}" \ + "/sys/kernel/config/nvmet/ports/${UO_PORT_ID}/subsystems/${UO2_NQN}" +assert_journal_has "connect-all was started" "${P_START}" \ + "${P_DC} - discovery log changed on an unowned DC" 10 +assert_connected "connects the subsystem added to the DLP" "${UO2_NQN}" 30 +assert_connected "connects the subsystem already in the DLP" "${UO_NQN}" 30 +P_DEV=$(connected_dev "${UO2_NQN}") +if [ -e "${REGISTRY_DIR}/${P_DEV}/owner" ]; then + fail "the new IOC has no owner" +else + pass "the new IOC has no owner" +fi +if [ -e "${REGISTRY_DIR}/${P_DC}/owner" ]; then + fail "the DC still has no owner" +else + pass "the DC still has no owner" +fi +disconnect_unowned + +phase "an AEN from a DC owned by another orchestrator is ignored" +"${NVME_BIN}" discover -t tcp -a "${TRADDR}" -s "${UO_PORT}" \ + --persistent=force --owner "${FOREIGN_OWNER}" >/dev/null 2>&1 +P_DC=$(port_dc_dev "${UO_PORT}") +nvmet_add_subsystem "${UO3_NQN}" +P_START=$(date +%H:%M:%S) +log "nvmet: port ${UO_PORT} also serves ${UO3_NQN}" +ln -s "/sys/kernel/config/nvmet/subsystems/${UO3_NQN}" \ + "/sys/kernel/config/nvmet/ports/${UO_PORT_ID}/subsystems/${UO3_NQN}" +assert_journal_has "the AEN was skipped" "${P_START}" \ + "${P_DC} - discovery log changed, owned by '${FOREIGN_OWNER}'" 10 +countdown 5 "give a connect-all time to run" +assert_not_connected "does not connect the subsystem" "${UO3_NQN}" +disconnect_unowned + if [ -z "${IFACE}" ]; then log "No given: mDNS phases not run" results diff --git a/src/fabrics.c b/src/fabrics.c index 0ea43b0ead..3464baf785 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -818,9 +818,10 @@ static int fabrics_discovery_config(struct libnvme_global_ctx *ctx, /* * A controller's ownership extends to everything done through it, so the * caller's operation may not proceed unless the invoking owner matches - * (mirrors disconnect_all_match()'s registry check). No ownerless - * exemption -- a mismatched or missing --owner is skipped the same way; - * the escape hatch is passing the owner's own identity. + * (mirrors disconnect_all_match()'s registry check). A controller with + * no owner proceeds for any caller. A controller with an owner is + * skipped unless --owner names that owner; a missing --owner does not + * match. * * --no-reuse skips the check entirely: it means the caller will never * reuse an existing controller, so there is nothing to check ownership From 44a1b69dcd3e7611cbe577b9ce616b03303f0d14 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:43:33 +0000 Subject: [PATCH 102/263] plugins: use the global verbose level instead of --human-readable fdp configs, lm migration-recv, shannon get-additional-feature and zns id-ns have their own --human-readable (-H), which takes the names of the global spelling of --verbose. Drop them and use the global verbose level, which --human-readable and -H still raise. shannon never used its option. Signed-off-by: Daniel Wagner --- Documentation/nvme-fdp-configs.txt | 6 ------ plugins/fdp/fdp.c | 7 ++----- plugins/lm/lm-nvme.c | 8 ++------ plugins/shannon/shannon-nvme.c | 5 +---- plugins/zns/zns.c | 8 +++----- 5 files changed, 8 insertions(+), 26 deletions(-) diff --git a/Documentation/nvme-fdp-configs.txt b/Documentation/nvme-fdp-configs.txt index 688bf7679e..ada0467896 100644 --- a/Documentation/nvme-fdp-configs.txt +++ b/Documentation/nvme-fdp-configs.txt @@ -9,7 +9,6 @@ SYNOPSIS -------- [verse] 'nvme' [] 'fdp configs' [--endgrp-id= | -e ] - [--human-readable | -H] [--raw-binary | -b] DESCRIPTION @@ -23,11 +22,6 @@ OPTIONS --endgrp-id=:: The endurance group identifier to use when requesting the log page. --H:: ---human-readable:: - Display values in a human-readable format where possible. - (deprecated, use --verbose) - -b:: --raw-binary:: Print the raw buffer to the standard output stream. diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index 97b8850750..c195b2d319 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -26,7 +26,6 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, { const char *desc = "Get Flexible Data Placement Configurations"; const char *egid = "Endurance group identifier"; - const char *human_readable = "show log in readable format"; const char *raw = "use binary output"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -40,7 +39,6 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, struct config { __u16 egid; - bool human_readable; bool raw_binary; }; @@ -51,8 +49,7 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, NVME_ARGS(opts, OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -63,7 +60,7 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, if (cfg.raw_binary) flags = BINARY; - if (cfg.human_readable) + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.egid) { diff --git a/plugins/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index 6b4c363726..0ada0b42fb 100644 --- a/plugins/lm/lm-nvme.c +++ b/plugins/lm/lm-nvme.c @@ -391,7 +391,6 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct const char *offset = "Controller State Offset"; const char *numd = "Number of Dwords (NUMD)"; const char *output = "Controller State Data output file"; - const char *human_readable_info = "show info in readable format"; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -412,7 +411,6 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct __u64 offset; __u32 numd; char *output; - bool human_readable; }; struct config cfg = { @@ -424,7 +422,6 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct .offset = 0, .numd = 0, .output = NULL, - .human_readable = false }; NVME_ARGS(opts, @@ -435,8 +432,7 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct OPT_BYTE("uidx", 'u', &cfg.uidx, uidx), OPT_LONG("offset", 'O', &cfg.offset, offset), OPT_UINT("numd", 'n', &cfg.numd, numd), - OPT_FILE("output-file", 'f', &cfg.output, output), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable_info)); + OPT_FILE("output-file", 'f', &cfg.output, output)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -454,7 +450,7 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct return -EINVAL; } - if (cfg.human_readable) + if (nvme_args.verbose) flags |= VERBOSE; if (cfg.output && strlen(cfg.output)) { diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index e854836f45..1b234e49a8 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -178,7 +178,6 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s const char *sel = "[0-3]: curr./default/saved/supp."; const char *data_len = "buffer len (if) data is returned"; const char *cdw11 = "dword 11 for interrupt vector config"; - const char *human_readable = "show infos in readable format"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_libnvme_free void *buf = NULL; @@ -192,7 +191,6 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s __u32 cdw11; __u32 data_len; bool raw_binary; - bool human_readable; }; struct config cfg = { @@ -210,8 +208,7 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 05c6c0a9d0..19fd7efdad 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -172,7 +172,6 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu "the given device and report information about the specified\n" "namespace in varios formats."; const char *vendor_specific = "dump binary vendor fields"; - const char *human_readable = "show identify in readable format"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -184,7 +183,6 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu struct config { __u32 namespace_id; - bool human_readable; bool vendor_specific; }; @@ -193,8 +191,8 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu NVME_ARGS(opts, OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, + vendor_specific)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -203,7 +201,7 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu flags = nvme_args.output_flags; if (cfg.vendor_specific) flags |= VS; - if (cfg.human_readable) + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.namespace_id) { From 9279afdb64ad373bd5434c774727bbf58382b04d Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:45:13 +0000 Subject: [PATCH 103/263] nvme: warn about the deprecated --human-readable --human-readable and -H are the spelling of --verbose of older versions. They keep working, but say so now and point to --verbose and -v. An option table entry can name the option which replaces it, and argconfig warns once when the deprecated option is used, under the name it was given by. The examples in the man pages use -v. Signed-off-by: Daniel Wagner --- Documentation/nvme-id-iocs.txt | 2 +- .../nvme-log-mi-cmd-support-effects.txt | 2 +- Documentation/nvme-show-regs.txt | 2 +- Documentation/nvme-solidigm-id-ctrl.txt | 2 +- src/argconfig.c | 16 +++++++++++++--- src/argconfig.h | 2 ++ src/global-options.c | 2 +- tests/cli/nvme_usage_test.py | 19 +++++++++++++++++++ 8 files changed, 39 insertions(+), 8 deletions(-) diff --git a/Documentation/nvme-id-iocs.txt b/Documentation/nvme-id-iocs.txt index 68a8d4be79..b546beb01a 100644 --- a/Documentation/nvme-id-iocs.txt +++ b/Documentation/nvme-id-iocs.txt @@ -44,7 +44,7 @@ fields in a human readable format: * show the fields in human readable format + ------------ -# nvme id iocs /dev/nvme0 -H +# nvme id iocs /dev/nvme0 -v ------------ NVME ---- diff --git a/Documentation/nvme-log-mi-cmd-support-effects.txt b/Documentation/nvme-log-mi-cmd-support-effects.txt index 833f0a4671..46c2c5e94c 100644 --- a/Documentation/nvme-log-mi-cmd-support-effects.txt +++ b/Documentation/nvme-log-mi-cmd-support-effects.txt @@ -30,7 +30,7 @@ include::global-options.txt[] EXAMPLES -------- -nvme log mi-cmd-support-effects /dev/nvme0 -H +nvme log mi-cmd-support-effects /dev/nvme0 -v NVME ---- diff --git a/Documentation/nvme-show-regs.txt b/Documentation/nvme-show-regs.txt index 184b0ca379..dba684540f 100644 --- a/Documentation/nvme-show-regs.txt +++ b/Documentation/nvme-show-regs.txt @@ -41,7 +41,7 @@ properties in a binary format: properties in a human readable format: + ------------ -# nvme show-regs /dev/nvme0 -H +# nvme show-regs /dev/nvme0 -v ------------ * Show the NVMe over PCIe controller registers or NVMe-oF controller properties in a json format: diff --git a/Documentation/nvme-solidigm-id-ctrl.txt b/Documentation/nvme-solidigm-id-ctrl.txt index fd4afe063b..d1e379e871 100644 --- a/Documentation/nvme-solidigm-id-ctrl.txt +++ b/Documentation/nvme-solidigm-id-ctrl.txt @@ -62,7 +62,7 @@ EXAMPLES + ------------ # nvme solidigm id-ctrl /dev/nvme0 --human-readable -# nvme solidigm id-ctrl /dev/nvme0 -H +# nvme solidigm id-ctrl /dev/nvme0 -v ------------ SEE ALSO diff --git a/src/argconfig.c b/src/argconfig.c index d78a4bec58..eccaa0cea4 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -507,9 +507,19 @@ argconfig_find_short(struct argconfig_commandline_options *s, char c) return NULL; } -static int argconfig_apply(struct argconfig_commandline_options *s, +/* @s given by its short name @c, or by its long name without, and @arg */ +static int argconfig_apply(struct argconfig_commandline_options *s, char c, char *arg) { + if (s->deprecated && !s->seen) { + if (c) + fprintf(stderr, "WARNING: '-%c'", c); + else + fprintf(stderr, "WARNING: '--%s'", s->option); + fprintf(stderr, + " is deprecated and will be removed in the next major version, use '%s' instead\n", + s->deprecated); + } s->seen = true; if (!s->default_value) @@ -578,7 +588,7 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) break; } - return argconfig_apply(s, value); + return argconfig_apply(s, 0, value); } /* "-a", "-abc", "-ovalue", "-o value" */ @@ -622,7 +632,7 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) break; } - ret = argconfig_apply(s, value); + ret = argconfig_apply(s, *c, value); if (ret || s->argument_type != ARGCONFIG_ARG_NONE) return ret; } diff --git a/src/argconfig.h b/src/argconfig.h index 9e0d2fcd7b..3ae9cef9cb 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -225,6 +225,8 @@ struct argconfig_commandline_options { bool seen; struct argconfig_opt_val *opt_val; bool hidden; + /* the option which replaces a deprecated one, warned about on use */ + const char *deprecated; }; void argconfig_append_usage(const char *str); diff --git a/src/global-options.c b/src/global-options.c index c2323d3257..a75956c74d 100644 --- a/src/global-options.c +++ b/src/global-options.c @@ -50,7 +50,7 @@ static struct argconfig_commandline_options global_options[] = { "output format version: 1|2"), /* the spelling of --verbose of older versions */ OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, NULL, - true), + true, "--verbose/-v"), OPT_STRING("set-options", 0, "KEY=VALUE", &nvme_args.set_options, "set a libnvme library option (key=value[,key=value,...]);"), OPT_END() diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 1d37908074..5ad7029a1e 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -46,6 +46,25 @@ def test_no_escape_sequences_when_not_a_terminal(self): self.assertNotIn('\033[', result.stdout) +class DeprecatedOptionTest(unittest.TestCase): + """--human-readable and -H still raise the verbose level, with a + warning which names the replacement.""" + + def test_human_readable(self): + for opt in ('-H', '--human-readable'): + with self.subTest(opt=opt): + result = run('list', opt) + self.assertIn(f"WARNING: '{opt}' is deprecated", result.stderr) + self.assertIn("use '--verbose/-v' instead", result.stderr) + + def test_warns_once(self): + result = run('list', '-HH') + self.assertEqual(result.stderr.count('WARNING'), 1) + + def test_verbose_does_not_warn(self): + self.assertNotIn('WARNING', run('list', '-v').stderr) + + class SpecialValuesTest(unittest.TestCase): def test_help_lists_the_names(self): From d493e10df6c20e48100793018cca965a218ff16c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:47:01 +0000 Subject: [PATCH 104/263] plugins: use the global --verbose instead of an own one sndk and wdc vs-internal-log, sed discover and zns report-zones have their own --verbose flag with the short name -V, which takes the long name of the global --verbose. Drop them and use the global verbose level, given with --verbose or -v. -V is gone with them. wdc never used its flag, so --verbose did nothing there; only -v reached the verbose level it reads. Signed-off-by: Daniel Wagner --- plugins/sandisk/sandisk-nvme.c | 6 +----- plugins/sed/sed.c | 2 -- plugins/sed/sedopal_cmd.c | 3 +-- plugins/sed/sedopal_cmd.h | 1 - plugins/wdc/wdc-nvme.c | 4 ---- plugins/zns/zns.c | 5 +---- 6 files changed, 3 insertions(+), 18 deletions(-) diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index c22f1b5511..214ae30a3b 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -394,7 +394,6 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, " HOST - Host-initiated telemetry.\n" \ " CONTROLLER - Controller-initiated telemetry.\n" \ " BOTH - Both HOST and CONTROLLER telemetry packaged in tar file."; - const char *verbose = "Display more debug messages."; const char *file_size = "Output file size. Deprecated, see man page for supported devices."; const char *offset = @@ -418,7 +417,6 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, __u64 file_size; __u64 offset; char *type; - bool verbose; }; struct config cfg = { @@ -428,7 +426,6 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, .file_size = 0, .offset = 0, .type = NULL, - .verbose = false, }; NVME_ARGS(opts, @@ -436,7 +433,6 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), OPT_FILE("type", 't', &cfg.type, type), - OPT_FLAG("verbose", 'V', &cfg.verbose, verbose), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), OPT_END()); @@ -569,7 +565,7 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, ret = -1; goto out; } - ret = sndk_do_cap_udui(hdl, f, xfer_size, cfg.verbose); + ret = sndk_do_cap_udui(hdl, f, xfer_size, nvme_args.verbose); goto out; } diff --git a/plugins/sed/sed.c b/plugins/sed/sed.c index 1dfa19d1f8..7a76c7d1df 100644 --- a/plugins/sed/sed.c +++ b/plugins/sed/sed.c @@ -47,8 +47,6 @@ OPT_ARGS(lock_opts) = { }; OPT_ARGS(discovery_opts) = { - OPT_FLAG("verbose", 'V', &sedopal_discovery_verbose, - "Print extended discovery information"), OPT_FLAG("udev", 'u', &sedopal_discovery_udev, "Print locking information in form suitable for udev rules"), OPT_END() diff --git a/plugins/sed/sedopal_cmd.c b/plugins/sed/sedopal_cmd.c index f259777faf..26dcd0b816 100644 --- a/plugins/sed/sedopal_cmd.c +++ b/plugins/sed/sedopal_cmd.c @@ -44,7 +44,6 @@ bool sedopal_lock_ro; /* * Verbose discovery */ -bool sedopal_discovery_verbose; /* * discovery with udev output @@ -1080,7 +1079,7 @@ int sedopal_cmd_discover(struct libnvme_transport_handle *hdl) } else sedopal_print_locking_features(sfp.locking_desc); - if (!sedopal_discovery_verbose) + if (!nvme_args.verbose) return rc; sedopal_print_features(&sfp); diff --git a/plugins/sed/sedopal_cmd.h b/plugins/sed/sedopal_cmd.h index 9c1d7341b8..95c56f2681 100644 --- a/plugins/sed/sedopal_cmd.h +++ b/plugins/sed/sedopal_cmd.h @@ -16,7 +16,6 @@ extern bool sedopal_ask_new_key; extern bool sedopal_destructive_revert; extern bool sedopal_psid_revert; extern bool sedopal_lock_ro; -extern bool sedopal_discovery_verbose; extern bool sedopal_discovery_udev; /* diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index ca7dac6618..da1bfcd805 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -4409,7 +4409,6 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, " NONE - Default, capture without using NVMe telemetry.\n" \ " HOST - Host-initiated telemetry.\n" \ " CONTROLLER - Controller-initiated telemetry."; - const char *verbose = "Display more debug messages."; const char *file_size = "Output file size. Deprecated, see man page for supported devices."; const char *offset = @@ -4435,7 +4434,6 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, __u64 file_size; __u64 offset; char *type; - bool verbose; }; struct config cfg = { @@ -4445,7 +4443,6 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, .file_size = 0, .offset = 0, .type = NULL, - .verbose = false, }; NVME_ARGS(opts, @@ -4453,7 +4450,6 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), OPT_FILE("type", 't', &cfg.type, type), - OPT_FLAG("verbose", 'V', &cfg.verbose, verbose), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), OPT_END()); diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 19fd7efdad..08a09e43b9 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -722,7 +722,6 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug const char *state = "state of zones to list"; const char *ext = "set to use the extended report zones"; const char *part = "set to use the partial report"; - const char *verbose = "show report zones verbosity"; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -750,7 +749,6 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug __u32 namespace_id; __u32 num_descs; __u32 state; - bool verbose; bool extended; bool partial; }; @@ -765,7 +763,6 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), OPT_UINT("state", 'S', &cfg.state, state), - OPT_FLAG("verbose", 'V', &cfg.verbose, verbose), OPT_FLAG("extended", 'e', &cfg.extended, ext), OPT_FLAG("partial", 'p', &cfg.partial, part)); @@ -774,7 +771,7 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug return err; flags = nvme_args.output_flags; - if (cfg.verbose) + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.namespace_id) { From 62c35cb7b492115a730297e3807a34cd8e8a6205 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:48:49 +0000 Subject: [PATCH 105/263] plugins: drop the short names which global options use -o is the short name of the global --output-format and -H the one of the deprecated global --human-readable. Some command options use them as well: config convert --output, rpmb read-data and write-data --address use -o, feat arbitration --hpw, temp-thresh --thsel, host-behavior-support --hdisns, lm set-cdq --hp, nbft show --hfi and samsung vs-internal-log --hide-progress use -H. A global option is the same on every command, so these options are long-only now, as lba-range-type's --hlbars and --save already are. The man pages of feat arbitration and temp-thresh listed short names which those options never had. Signed-off-by: Daniel Wagner --- Documentation/nvme-config-convert.txt | 3 +-- Documentation/nvme-feat-arbitration.txt | 3 +-- Documentation/nvme-feat-host-behavior-support.txt | 3 +-- Documentation/nvme-feat-temp-thresh.txt | 3 +-- Documentation/nvme-rpmb-read-data.txt | 5 ++--- Documentation/nvme-rpmb-write-data.txt | 3 +-- Documentation/nvme-samsung-vs-internal-log.txt | 3 +-- plugins/feat/feat-nvme.c | 6 +++--- plugins/lm/lm-nvme.c | 2 +- plugins/nbft/nbft-plugin.c | 2 +- plugins/rpmb/rpmb.c | 4 ++-- plugins/samsung/samsung-nvme.c | 3 ++- src/config-convert.c | 2 +- tests/cli/samsung/nvme_samsung_mctp_test.py | 2 +- tests/cli/samsung/nvme_samsung_test.py | 12 ++++++------ 15 files changed, 25 insertions(+), 31 deletions(-) diff --git a/Documentation/nvme-config-convert.txt b/Documentation/nvme-config-convert.txt index 8973a65cb6..162247786e 100644 --- a/Documentation/nvme-config-convert.txt +++ b/Documentation/nvme-config-convert.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'nvme' [] 'config convert' [--config= | -J ] - [--output= | -o ] + [--output=] [--force] [--verbose | -v] @@ -50,7 +50,6 @@ OPTIONS Convert this JSON file instead of the default @SYSCONFDIR@/nvme/config.json. --o :: --output=:: Write the converted configuration here instead of the default @SYSCONFDIR@/nvme/nvme-fabrics.conf. diff --git a/Documentation/nvme-feat-arbitration.txt b/Documentation/nvme-feat-arbitration.txt index 34d2f70e83..6f535239e2 100644 --- a/Documentation/nvme-feat-arbitration.txt +++ b/Documentation/nvme-feat-arbitration.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'nvme' [] 'feat arbitration' [--ab= | -a ] [--lpw= | -l ] - [--mpw= | -m ] [--hpw= | -h ] + [--mpw= | -m ] [--hpw=] [--sel=] [--save | -s] DESCRIPTION @@ -41,7 +41,6 @@ OPTIONS --mpw=:: Medium Priority Weight. Specifies the medium priority service weight. --h :: --hpw=:: High Priority Weight. Specifies the high priority service weight. diff --git a/Documentation/nvme-feat-host-behavior-support.txt b/Documentation/nvme-feat-host-behavior-support.txt index 9f6cbbf13e..a7b754f14e 100644 --- a/Documentation/nvme-feat-host-behavior-support.txt +++ b/Documentation/nvme-feat-host-behavior-support.txt @@ -12,7 +12,7 @@ SYNOPSIS [--acre= | -a ] [--etdas= | -e ] [--lbafee= | -l ] - [--hdisns= | -H ] + [--hdisns=] [--cdfe= | -c ] [--sel=] [--save | -s] @@ -44,7 +44,6 @@ OPTIONS --lbafee=:: LBA Format Extension Enable (0 or 1). --H :: --hdisns=:: Host Dispersed Namespace Support (0 or 1). diff --git a/Documentation/nvme-feat-temp-thresh.txt b/Documentation/nvme-feat-temp-thresh.txt index 92a33a6bb6..2c5653ad35 100644 --- a/Documentation/nvme-feat-temp-thresh.txt +++ b/Documentation/nvme-feat-temp-thresh.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'nvme' [] 'feat temp-thresh' - [--tmpth= | -t ] [--thsel= | -T ] + [--tmpth= | -t ] [--thsel=] [--sel=] [--save | -s] DESCRIPTION @@ -31,7 +31,6 @@ OPTIONS --tmpth=:: Temperature Threshold. Specifies the threshold temperature value in Kelvin. --T :: --thsel=:: Threshold Type Select. Specifies the type of threshold (e.g., over-temperature threshold or under-temperature threshold). diff --git a/Documentation/nvme-rpmb-read-data.txt b/Documentation/nvme-rpmb-read-data.txt index e5f5470808..d0c9d4de16 100644 --- a/Documentation/nvme-rpmb-read-data.txt +++ b/Documentation/nvme-rpmb-read-data.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'nvme' [] 'rpmb read-data' [--msgfile= | -f ] - [--address= | -o ] + [--address=] [--blocks=<512 byte sectors> | -b ] [--target= | -t ] @@ -34,7 +34,6 @@ OPTIONS reported by 'nvme rpmb info'. If nothing is given, the default of 0 is used as the RPMB target. --o :: --address=:: The address (in 512 byte sector offset from 0) to read from on the specified RPMB target. @@ -57,7 +56,7 @@ EXAMPLES * the data onto output.bin + ------------ -# nvme rpmb read-data /dev/nvme0 -t 2 -f output.bin -b 200 -o 0x100 +# nvme rpmb read-data /dev/nvme0 -t 2 -f output.bin -b 200 --address=0x100 ------------ NVME diff --git a/Documentation/nvme-rpmb-write-data.txt b/Documentation/nvme-rpmb-write-data.txt index 9b29366516..294ddbc135 100644 --- a/Documentation/nvme-rpmb-write-data.txt +++ b/Documentation/nvme-rpmb-write-data.txt @@ -13,7 +13,7 @@ SYNOPSIS [--msg= | -d ] [--keyfile= | -g ] [--key= | -k ] - [--address= | -o ] + [--address=] [--blocks=<512 byte sectors> | -b ] [--target= | -t ] @@ -37,7 +37,6 @@ OPTIONS reported by 'nvme rpmb info'. If nothing is given, the default of 0 is used as the RPMB target. --o :: --address=:: The address (in 512 byte sector offset from 0) to write to on the specified RPMB target. diff --git a/Documentation/nvme-samsung-vs-internal-log.txt b/Documentation/nvme-samsung-vs-internal-log.txt index 23b802da3c..e6c54edc6f 100644 --- a/Documentation/nvme-samsung-vs-internal-log.txt +++ b/Documentation/nvme-samsung-vs-internal-log.txt @@ -66,7 +66,6 @@ OPTIONS that path: one left behind by an interrupted run has to be removed before retrying. --H:: --hide-progress:: Do not print the incremental progress of an extraction. Useful where the output is captured line by line and a carriage return @@ -104,7 +103,7 @@ Extract data area 2 of the host-initiated telemetry using Log Specific Parameter 1, store the result compressed and keep the output quiet. ------------ -# nvme samsung vs-internal-log /dev/nvme0 -t host1 -a 2 -z -H +# nvme samsung vs-internal-log /dev/nvme0 -t host1 -a 2 -z --hide-progress ------------ NVME diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index aed3d7b973..5ac22a245b 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -529,7 +529,7 @@ static int feat_temp_thresh(int argc, char **argv, struct command *acmd, struct FEAT_ARGS(opts, OPT_SHRT("tmpth", 'T', &cfg.tmpth, tmpth), OPT_BYTE("tmpsel", 'm', &cfg.tmpsel, tmpsel), - OPT_BYTE("thsel", 'H', &cfg.thsel, thsel), + OPT_BYTE("thsel", 0, &cfg.thsel, thsel), OPT_BYTE("tmpthh", 'M', &cfg.tmpthh, tmpthh)); err = parse_and_open(&ctx, &hdl, argc, argv, TEMP_THRESH_DESC, opts); @@ -614,7 +614,7 @@ static int feat_arbitration(int argc, char **argv, struct command *acmd, struct OPT_BYTE("ab", 'a', &cfg.ab, ab), OPT_BYTE("lpw", 'l', &cfg.lpw, lpw), OPT_BYTE("mpw", 'm', &cfg.mpw, mpw), - OPT_BYTE("hpw", 'H', &cfg.hpw, hpw)); + OPT_BYTE("hpw", 0, &cfg.hpw, hpw)); err = parse_and_open(&ctx, &hdl, argc, argv, ARBITRATION_DESC, opts); if (err) @@ -1108,7 +1108,7 @@ static int feat_host_behavior_support(int argc, char **argv, struct command *acm OPT_BYTE("acre", 'a', &cfg.acre, acre_desc), OPT_BYTE("etdas", 'e', &cfg.etdas, etdas_desc), OPT_BYTE("lbafee", 'l', &cfg.lbafee, lbafee_desc), - OPT_BYTE("hdisns", 'H', &cfg.hdisns, hdisns_desc), + OPT_BYTE("hdisns", 0, &cfg.hdisns, hdisns_desc), OPT_SHRT("cdfe", 'c', &cfg.cdfe, cdfe_desc)); err = parse_and_open(&ctx, &hdl, argc, argv, HOST_BEHAVIOR_DESC, opts); diff --git a/plugins/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index 0ada0b42fb..532ef9ce60 100644 --- a/plugins/lm/lm-nvme.c +++ b/plugins/lm/lm-nvme.c @@ -527,7 +527,7 @@ static int lm_set_cdq(int argc, char **argv, struct command *acmd, struct plugin NVME_ARGS(opts, OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), - OPT_UINT("hp", 'H', &cfg.hp, hp), + OPT_UINT("hp", 0, &cfg.hp, hp), OPT_UINT("tpt", 'T', &cfg.tpt, tpt)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 544c100ea8..69edf7b5e8 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -666,7 +666,7 @@ int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin NVME_ARGS(opts, OPT_FLAG("subsystem", 's', &show_subsys, "show NBFT subsystems"), - OPT_FLAG("hfi", 'H', &show_hfi, "show NBFT HFIs"), + OPT_FLAG("hfi", 0, &show_hfi, "show NBFT HFIs"), OPT_FLAG("discovery", 'd', &show_discovery, "show NBFT discovery controllers"), OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables")); diff --git a/plugins/rpmb/rpmb.c b/plugins/rpmb/rpmb.c index 35d60b4bb5..89f2f9b705 100644 --- a/plugins/rpmb/rpmb.c +++ b/plugins/rpmb/rpmb.c @@ -905,7 +905,7 @@ static int rpmb_read_data(int argc, char **argv, struct command *acmd, struct pl NVME_ARGS(opts, OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_UINT("address", 'o', &cfg.address, address), + OPT_UINT("address", 0, &cfg.address, address), OPT_UINT("blocks", 'b', &cfg.blocks, blocks), OPT_BYTE("target", 't', &cfg.target, target)); @@ -974,7 +974,7 @@ static int rpmb_write_data(int argc, char **argv, struct command *acmd, struct p OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), OPT_STRING("key", 'k', "key", &cfg.key, key), OPT_STRING("msg", 'd', "data", &cfg.msg, msg), - OPT_UINT("address", 'o', &cfg.address, address), + OPT_UINT("address", 0, &cfg.address, address), OPT_UINT("blocks", 'b', &cfg.blocks, blocks), OPT_BYTE("target", 't', &cfg.target, target)); diff --git a/plugins/samsung/samsung-nvme.c b/plugins/samsung/samsung-nvme.c index cd54513226..86a1e77969 100644 --- a/plugins/samsung/samsung-nvme.c +++ b/plugins/samsung/samsung-nvme.c @@ -1340,7 +1340,8 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, OPT_INT("telemetry-data-area", 'a', &cfg.area, area), OPT_FILE("output-file", 'O', &cfg.file, file), OPT_FLAG("compress", 'z', &cfg.compress, compress), - OPT_FLAG("hide-progress", 'H', &cfg.hide_progress, hide_progress)); + OPT_FLAG("hide-progress", 0, &cfg.hide_progress, + hide_progress)); samsung_initialize(); diff --git a/src/config-convert.c b/src/config-convert.c index 18d9af7032..0c69342d21 100644 --- a/src/config-convert.c +++ b/src/config-convert.c @@ -554,7 +554,7 @@ int nvme_config_convert(const char *desc, int argc, char **argv) OPT_ARGS(opts) = { OPT_STRING("config", 'J', "FILE", &config_file, "convert this JSON file (default: config.json)"), - OPT_STRING("output", 'o', "FILE", &output_file, + OPT_STRING("output", 0, "FILE", &output_file, "write result here (default: nvme-fabrics.conf)"), OPT_FLAG("force", 0, &force, "overwrite an existing target"), diff --git a/tests/cli/samsung/nvme_samsung_mctp_test.py b/tests/cli/samsung/nvme_samsung_mctp_test.py index 29f720df90..a1e93c6867 100644 --- a/tests/cli/samsung/nvme_samsung_mctp_test.py +++ b/tests/cli/samsung/nvme_samsung_mctp_test.py @@ -147,7 +147,7 @@ def check_dump(self, dump_type, lid, label, device='mctp:1,8:3', area=1): prefix = os.path.join(self.tmp.name, dump_type) + '/' result = run_nvme(NVME_BIN, self.env, self.tmp.name, self.tmp.name, 'samsung', 'vs-internal-log', device, - '-t', dump_type, '-a', str(area), '-O', prefix, '-H') + '-t', dump_type, '-a', str(area), '-O', prefix, '--hide-progress') self.assertEqual(result.returncode, 0, result.stdout + result.stderr) self.assertEqual(self.peer.errors, []) self.assertIn('(Xfer 4K)', result.stdout) diff --git a/tests/cli/samsung/nvme_samsung_test.py b/tests/cli/samsung/nvme_samsung_test.py index ed21c5ab77..32ece5dd71 100644 --- a/tests/cli/samsung/nvme_samsung_test.py +++ b/tests/cli/samsung/nvme_samsung_test.py @@ -319,18 +319,18 @@ def test_merged_file_is_header_plus_areas(self): 'merged size is not header + area 1 + area 2') # ---------------------------------------------------------------- # - # Progress, timing and -H / --verbose # + # Progress, timing and --hide-progress / --verbose # # ---------------------------------------------------------------- # def test_hide_progress_keeps_the_completion_line(self): - result = self.run_cmd('-t', 'ctlr', '-O', './dumps/', '-H') + result = self.run_cmd('-t', 'ctlr', '-O', './dumps/', '--hide-progress') self.assertOk(result) self.assertIn('100%', result.stdout, '--hide-progress swallowed the completion line') def test_hide_progress_drops_incremental_updates(self): with_progress = self.run_cmd('-t', 'ctlr', '-O', './a/') - without = self.run_cmd('-t', 'ctlr', '-O', './b/', '-H') + without = self.run_cmd('-t', 'ctlr', '-O', './b/', '--hide-progress') self.assertOk(with_progress) self.assertOk(without) self.assertLess(without.stdout.count('%'), with_progress.stdout.count('%'), @@ -346,7 +346,7 @@ def test_timing_table_is_verbose_only(self): 'timing table missing under --verbose') def test_timing_table_is_not_tied_to_hide_progress(self): - result = self.run_cmd('-t', 'ctlr', '-O', './a/', '-H', '--verbose') + result = self.run_cmd('-t', 'ctlr', '-O', './a/', '--hide-progress', '--verbose') self.assertOk(result) self.assertIn('Avg time per loop', result.stdout, '--hide-progress suppressed the --verbose timing table') @@ -591,7 +591,7 @@ def test_help_keeps_capital_O_for_output_file(self): def test_direct_telemetry_keeps_127k_transfers(self): self.server.last_blocks = (260, 260, 260, 260) - result = self.run_cmd('-t', 'ctlr', '-a', '1', '-H') + result = self.run_cmd('-t', 'ctlr', '-a', '1', '--hide-progress') self.assertOk(result) self.assertIn('(Xfer 127K)', result.stdout) self.assertEqual(self.server.log_transfers, [ @@ -711,7 +711,7 @@ def test_compress_with_single_data_area(self): @unittest.skipUnless(_HAS_LIBARCHIVE, _NO_LIBARCHIVE_MSG) def test_compress_with_hide_progress(self): - result = self.run_cmd('-t', 'ctlr', '-O', './dumps/', '-z', '-H') + result = self.run_cmd('-t', 'ctlr', '-O', './dumps/', '-z', '--hide-progress') self.assertOk(result) self.assertTrue(any(f.endswith('.tar.gz') for f in self.files('dumps')), f'no archive: {self.files("dumps")}') From 89049fe62ee6cbb36329d908e91385a49d62f762 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:51:18 +0000 Subject: [PATCH 106/263] argconfig: look up the global options first A command's own option took precedence over a global option of the same name, so a global option could mean something else for a single command. No command uses the name of a global option any more. Look the global options up first, so they mean the same everywhere. The locale check for the verbose output looked for an option named verbose, of the command first. Ask whether the global verbose level was given instead, so -H and --human-readable count like -v. Signed-off-by: Daniel Wagner --- src/argconfig.c | 75 ++++++++++++++++++----------- src/args.h | 5 +- tests/unit/c/test-argconfig-parse.c | 13 ++--- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index eccaa0cea4..28146de09d 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -56,8 +56,9 @@ static const char *append_usage_str = ""; /* * Options every command accepts, registered once instead of being added to - * each command's table. The command's own options are looked up first, so - * a command can still use a short or long name of a global option. + * each command's table. They are looked up before the command's own + * options, so a global option means the same for every command; a command + * must not use their names. */ static struct argconfig_commandline_options *global_options; @@ -396,25 +397,6 @@ static int argconfig_parse_val(struct argconfig_commandline_options *s, return argconfig_parse_type(s, arg); } -/* --verbose counts, a command may have a flag of the same name */ -static bool argconfig_check_verbose(struct argconfig_commandline_options *s) -{ - struct argconfig_commandline_options *g = argconfig_globals(s); - - for (; s && s->option; s++) { - if (!strcmp(s->option, "verbose") && - s->config_type == CFG_INCREMENT) - return s->seen; - } - for (; g && g->option; g++) { - if (!strcmp(g->option, "verbose") && - g->config_type == CFG_INCREMENT) - return g->seen; - } - - return false; -} - /* The non-option arguments of the last successful argconfig_parse() */ static struct { char **argv; @@ -508,6 +490,35 @@ argconfig_find_short(struct argconfig_commandline_options *s, char c) } /* @s given by its short name @c, or by its long name without, and @arg */ +/* + * The option @s or the global options name, the global ones first: a + * global option is the same for every command. + */ +static struct argconfig_commandline_options * +argconfig_find_long_any(struct argconfig_commandline_options *s, + const char *name, size_t len) +{ + struct argconfig_commandline_options *g = argconfig_globals(s); + struct argconfig_commandline_options *o = NULL; + + if (g) + o = argconfig_find_long(g, name, len); + + return o ? o : argconfig_find_long(s, name, len); +} + +static struct argconfig_commandline_options * +argconfig_find_short_any(struct argconfig_commandline_options *s, char c) +{ + struct argconfig_commandline_options *g = argconfig_globals(s); + struct argconfig_commandline_options *o = NULL; + + if (g) + o = argconfig_find_short(g, c); + + return o ? o : argconfig_find_short(s, c); +} + static int argconfig_apply(struct argconfig_commandline_options *s, char c, char *arg) { @@ -545,10 +556,7 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) char *eq = strchr(name, '='); size_t len = eq ? (size_t)(eq - name) : strlen(name); - s = argconfig_find_long(p->options, name, len); - if (!s && argconfig_globals(p->options)) - s = argconfig_find_long(argconfig_globals(p->options), name, - len); + s = argconfig_find_long_any(p->options, name, len); if (!s) { bool help = len == 4 && !strncmp(name, "help", 4); bool version = len == 7 && !strncmp(name, "version", 7); @@ -602,10 +610,7 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) if (!p->global && (*c == 'h' || *c == '?')) return ARGCONFIG_HELP; - s = argconfig_find_short(p->options, *c); - if (!s && argconfig_globals(p->options)) - s = argconfig_find_short(argconfig_globals(p->options), - *c); + s = argconfig_find_short_any(p->options, *c); if (!s && p->global && (*c == 'h' || *c == 'V')) { p->help |= *c == 'h'; p->version |= *c == 'V'; @@ -692,6 +697,18 @@ void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) argconfig_parse_hook = hook; } +/* --verbose, or an alias of it such as --human-readable, was given */ +static bool argconfig_check_verbose(struct argconfig_commandline_options *s) +{ + struct argconfig_commandline_options *v; + + v = argconfig_find_long_any(s, "verbose", strlen("verbose")); + if (!v || v->config_type != CFG_INCREMENT) + return false; + + return argconfig_given(s, v->default_value); +} + int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_commandline_options *options) { diff --git a/src/args.h b/src/args.h index 25af17c00a..a5f7addba1 100644 --- a/src/args.h +++ b/src/args.h @@ -65,8 +65,9 @@ enum nvme_print_flags { /* * The command's options. The global options are registered once with - * argconfig (nvme_args_register()) and accepted by every command; a - * command's own option with the same short or long name takes precedence. + * argconfig (nvme_args_register()) and accepted by every command. They + * take precedence over a command's own option of the same short or long + * name, so a command must not use their names. * The output formats a command supports are declared in its struct * command. */ diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 1a7d3b57ab..d808e8d7d6 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -383,7 +383,7 @@ static void operands_test(void) /* * Options registered with argconfig_set_global_options() are accepted by - * every command after the command's own options, which take precedence. + * every command and take precedence over its options of the same name. * Their seen flags are kept across parses: the global options may already * have been given before the command name. */ @@ -403,7 +403,7 @@ static void global_options_test(void) }; OPT_ARGS(opts) = { OPT_FLAG("flag", 'f', &cfg.flag, "flag"), - /* shadows -d of the global options */ + /* -d is the global --dry-run */ OPT_UINT("uint", 'd', &cfg.uint, "uint"), OPT_END() }; @@ -426,21 +426,22 @@ static void global_options_test(void) } { - char **argv = (char *[]){ "list", "-f", "--verbose", "-d", "7", - "-o", "json", "dev", NULL }; + char **argv = (char *[]){ "list", "-f", "--verbose", "-d", + "--uint=7", "-o", "json", "dev", + NULL }; cfg.flag = false; cfg.uint = 0; ret = argconfig_parse(argv_len(argv), argv, desc, opts); if (ret || !cfg.flag || cfg.uint != 7 || verbose != 2 || - dry_run || !format || strcmp(format, "json") || + !dry_run || !format || strcmp(format, "json") || argconfig_nr_operands() != 1) { printf("ERROR: global options: command parse failed\n"); test_rc = 1; } if (!argconfig_given(opts, &format) || !argconfig_given(opts, &verbose) || - argconfig_given(opts, &dry_run)) { + !argconfig_given(opts, &dry_run)) { printf("ERROR: global options: seen mismatch\n"); test_rc = 1; } From 1b8eacd7fc92f09dce6bb3cdb37e54b9a29b7b7a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:53:03 +0000 Subject: [PATCH 107/263] tests: check that option names don't collide A command option with the name of a global option can't be given, as the global options are looked up first, nor can one with -h or -?, and of two options of a command with the same name only the first one is reachable. Check the options of every command in the command metadata for that. A hidden option may share the short name of a visible one, as the old spelling of the option. Signed-off-by: Daniel Wagner --- tests/unit/py/meson.build | 13 ++++ tests/unit/py/test_option_names.py | 97 ++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/unit/py/test_option_names.py diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index 2f10ddac99..1651d71776 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -36,6 +36,19 @@ if python3.found() depends: nvme_exe, timeout: 60, ) + + # The global options are looked up first, -h is the help; a + # command option must use neither, nor a name twice. + test( + 'nvme-cli - option-names', + python3, + args: [ + files('test_option_names.py'), + nvme_exe, + ], + depends: nvme_exe, + timeout: 60, + ) endif # Argument handling of the completion generator; needs no nvme binary or diff --git a/tests/unit/py/test_option_names.py b/tests/unit/py/test_option_names.py new file mode 100644 index 0000000000..a6894cae4b --- /dev/null +++ b/tests/unit/py/test_option_names.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +"""Check that the option names of a command don't collide. + +The global options (src/global-options.c) are looked up before the options +of a command, so a command option with the long or short name of a global +option could never be given. -h and -? always ask for the help. And of two +options of a command with the same name only the first one is reachable. + +This test takes the options of every command from +`nvme utils dump-command-metadata` and checks that a command option uses +neither the name of a global option nor -h or -?, and that no two options +of a command share a name. A hidden option may share the name of a visible +one: it is the old spelling of that option. + +Usage: test_option_names.py +""" +import collections +import json +import subprocess +import sys +import unittest + +if len(sys.argv) < 2: + print("usage: %s " % sys.argv[0]) + sys.exit(77) + +NVME_BIN = sys.argv[1] +HELP_SHORT = {'h', '?'} + + +def commands(): + """(name, options) of every command, the global options left out, and + the global options.""" + out = subprocess.run([NVME_BIN, 'utils', 'dump-command-metadata'], + capture_output=True, text=True, check=True).stdout + model = json.loads(out) + cmds = [(c['name'], c) for c in model['commands']] + for plugin in model['plugins']: + cmds += [(plugin['name'] + ' ' + c['name'], c) + for c in plugin['commands']] + + global_opts = None + result = [] + for name, cmd in cmds: + opts = cmd.get('options', []) + if global_opts is None and any(o.get('global') for o in opts): + global_opts = [o for o in opts if o.get('global')] + result.append((name, [o for o in opts if not o.get('global')])) + if not global_opts: + raise AssertionError('no command with global options') + return result, global_opts + + +class OptionNamesTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.commands, global_opts = commands() + cls.global_long = {o['long'] for o in global_opts} + cls.global_short = {o['short'] for o in global_opts + if o.get('short')} + + def test_no_global_option_name(self): + for name, opts in self.commands: + for o in opts: + with self.subTest(command=name, option=o['long']): + self.assertNotIn(o['long'], self.global_long) + self.assertNotIn(o.get('short'), self.global_short) + + def test_no_help_short_name(self): + for name, opts in self.commands: + for o in opts: + with self.subTest(command=name, option=o['long']): + self.assertNotIn(o.get('short'), HELP_SHORT) + + def test_unique_names(self): + for name, opts in self.commands: + visible = [o for o in opts if not o.get('hidden')] + for key in ('long', 'short'): + counts = collections.Counter(o[key] for o in visible + if o.get(key)) + for value, n in counts.items(): + with self.subTest(command=name, name=value): + self.assertEqual(n, 1) + # an old spelling must not hide a visible option either + names = {o['long'] for o in visible} + for o in opts: + if o.get('hidden'): + with self.subTest(command=name, option=o['long']): + self.assertNotIn(o['long'], names) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]]) From 66b02aa9868595a6bc5bbfbaed5fdb1f4e857c5f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:53:09 +0000 Subject: [PATCH 108/263] completions: update the shell completions Drop the options which use the global verbose level now and the short names which the global options use. Signed-off-by: Daniel Wagner --- completions/_nvme | 27 +--------------- completions/bash-nvme-completion.sh | 50 ++++++++++++++--------------- completions/nvme-completion.ps1 | 37 +++++++++++---------- 3 files changed, 44 insertions(+), 70 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 95b91ba57f..817663774b 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -379,7 +379,7 @@ _nvme () { "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ - "--output -o" && return + "--output" && return local _config_convert _config_convert=( --config=':convert this JSON file (default: config.json)' @@ -389,7 +389,6 @@ _nvme () { --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' --output=':write result here (default: nvme-fabrics.conf)' - -o':alias for --output' --output-format=':Output format: normal|json|binary' -o':alias for --output-format' --output-format-version=':output format version: 1|2' @@ -1017,8 +1016,6 @@ _nvme () { --dry-run':show command instead of executing' --endgrp-id=':Endurance group identifier' -e':alias for --endgrp-id' - --human-readable':show log in readable format' - -H':alias for --human-readable' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' --output-format=':Output format: normal|json|binary' @@ -1301,7 +1298,6 @@ _nvme () { -a':alias for --ab' --dry-run':show command instead of executing' --hpw=':high priority weight' - -H':alias for --hpw' --lpw=':low priority weight' -l':alias for --lpw' --mpw=':medium priority weight' @@ -1446,7 +1442,6 @@ _nvme () { --etdas=':Extended Telemetry Data Area 4 Supported (0 or 1)' -e':alias for --etdas' --hdisns=':Host Dispersed Namespace Support (0 or 1)' - -H':alias for --hdisns' --lbafee=':LBA Format Extension Enable (0 or 1)' -l':alias for --lbafee' --no-ioctl-probing':disable 64-bit IOCTL support probing' @@ -1861,7 +1856,6 @@ _nvme () { -S':alias for --sel' --set-options=':set a libnvme library option (key=value[,key=value,...]);' --thsel=':threshold type select' - -H':alias for --thsel' --timeout=':timeout value, in milliseconds' --tmpsel=':threshold temperature select' -m':alias for --tmpsel' @@ -3680,8 +3674,6 @@ _nvme () { --csvi=':Controller State Version Index (CSVI) (CDW11[7:0])' -V':alias for --csvi' --dry-run':show command instead of executing' - --human-readable':show info in readable format' - -H':alias for --human-readable' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' --numd=':Number of Dwords (NUMD)' @@ -3769,7 +3761,6 @@ _nvme () { -C':alias for --cdqid' --dry-run':show command instead of executing' --hp=':The slot of the head pointer for the specified CDQ' - -H':alias for --hp' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' --output-format=':Output format: normal|json|binary' @@ -6063,7 +6054,6 @@ _nvme () { -d':alias for --discovery' --dry-run':show command instead of executing' --hfi':show NBFT HFIs' - -H':alias for --hfi' --nbft-path=':user-defined path for NBFT tables' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' @@ -7905,7 +7895,6 @@ _nvme () { local _rpmb_read_data _rpmb_read_data=( --address=':Sector offset to read from for an RPMB target, default 0' - -o':alias for --address' --blocks=':Number of 512 byte blocks to read' -b':alias for --blocks' --dry-run':show command instead of executing' @@ -7975,7 +7964,6 @@ _nvme () { local _rpmb_write_data _rpmb_write_data=( --address=':Sector offset to write to for an RPMB target, default 0' - -o':alias for --address' --blocks=':Number of 512 byte blocks to write' -b':alias for --blocks' --dry-run':show command instead of executing' @@ -8037,7 +8025,6 @@ _nvme () { --dump-type=':Use this option if you want to extract a specific kind of dump. Comma-separate to extract several at once (no spaces). Example) -t host1 or -t host1,ctlr,vendor host0---Host_Initiated_Telemetry_Dump(LSP_0) host1---Host_Initiated_Telemetry_Dump(LSP_1) ctlr----Controller_Initiated_Telemetry_Dump vendor--Vendor_Dumps' -t':alias for --dump-type' --hide-progress':Hide the incremental extraction progress. The completion line of each dump is still printed.' - -H':alias for --hide-progress' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' --output-file=':Output file name with path; e.g. '\''-O ./path/name'\'' '\''-O ./path1/path2/'\''; If requested path does not exist, the directory will be newly created.' @@ -8549,8 +8536,6 @@ _nvme () { --timeout=':timeout value, in milliseconds' --udev':Print locking information in form suitable for udev rules' -u':alias for --udev' - --verbose':Print extended discovery information' - -V':alias for --verbose' --verbose':Increase output verbosity' -v':alias for --verbose' --help':show help' @@ -9068,8 +9053,6 @@ _nvme () { --dry-run':show command instead of executing' --feature-id=':hexadecimal feature name' -f':alias for --feature-id' - --human-readable':show infos in readable format' - -H':alias for --human-readable' --namespace-id=':identifier of desired namespace' -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' @@ -9861,8 +9844,6 @@ _nvme () { -s':alias for --transfer-size' --type=':Telemetry type - NONE, HOST, CONTROLLER, or BOTH: NONE - Default, capture without using NVMe telemetry. HOST - Host-initiated telemetry. CONTROLLER - Controller-initiated telemetry. BOTH - Both HOST and CONTROLLER telemetry packaged in tar file.' -t':alias for --type' - --verbose':Display more debug messages.' - -V':alias for --verbose' --verbose':Increase output verbosity' -v':alias for --verbose' --help':show help' @@ -11764,8 +11745,6 @@ _nvme () { -s':alias for --transfer-size' --type=':Telemetry type - NONE, HOST, or CONTROLLER: NONE - Default, capture without using NVMe telemetry. HOST - Host-initiated telemetry. CONTROLLER - Controller-initiated telemetry.' -t':alias for --type' - --verbose':Display more debug messages.' - -V':alias for --verbose' --verbose':Increase output verbosity' -v':alias for --verbose' --help':show help' @@ -12121,8 +12100,6 @@ _nvme () { local _zns_id_ns _zns_id_ns=( --dry-run':show command instead of executing' - --human-readable':show identify in readable format' - -H':alias for --human-readable' --namespace-id=':Namespace identifier to use' -n':alias for --namespace-id' --no-ioctl-probing':disable 64-bit IOCTL support probing' @@ -12263,8 +12240,6 @@ _nvme () { --state=':state of zones to list' -S':alias for --state' --timeout=':timeout value, in milliseconds' - --verbose':show report zones verbosity' - -V':alias for --verbose' --verbose':Increase output verbosity' -v':alias for --verbose' --help':show help' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index f8b146c3ca..728815639a 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -1347,13 +1347,13 @@ plugin_config_opts () { _nvme_detect_value_completion case "$1" in "convert") - opts+=" --config= -J --dry-run --force --no-ioctl-probing --no-retries --output= -o --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" - valopts+=" --config -J --output -o --output-format -o --output-format-version --set-options --timeout" + opts+=" --config= -J --dry-run --force --no-ioctl-probing --no-retries --output= --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" + valopts+=" --config -J --output --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" - _nvme_opt_files "--config -J" "--output -o" + _nvme_opt_files "--config -J" "--output" nodevice=1 ;; @@ -1618,7 +1618,7 @@ plugin_fdp_opts () { _nvme_detect_value_completion case "$1" in "configs") - opts+=" --dry-run --endgrp-id= -e --human-readable -H --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" + opts+=" --dry-run --endgrp-id= -e --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --set-options= --timeout= --verbose -v" valopts+=" --endgrp-id -e --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -1711,8 +1711,8 @@ plugin_feat_opts () { _nvme_detect_value_completion case "$1" in "arbitration") - opts+=" --ab= -a --dry-run --hpw= -H --lpw= -l --mpw= -m --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" - valopts+=" --ab -a --hpw -H --lpw -l --mpw -m --output-format -o --output-format-version --sel -S --set-options --timeout" + opts+=" --ab= -a --dry-run --hpw= --lpw= -l --mpw= -m --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" + valopts+=" --ab -a --hpw --lpw -l --mpw -m --output-format -o --output-format-version --sel -S --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -1752,8 +1752,8 @@ plugin_feat_opts () { ;; "host-behavior-support") - opts+=" --acre= -a --cdfe= -c --dry-run --etdas= -e --hdisns= -H --lbafee= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" - valopts+=" --acre -a --cdfe -c --etdas -e --hdisns -H --lbafee -l --output-format -o --output-format-version --sel -S --set-options --timeout" + opts+=" --acre= -a --cdfe= -c --dry-run --etdas= -e --hdisns= --lbafee= -l --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --timeout= --verbose -v" + valopts+=" --acre -a --cdfe -c --etdas -e --hdisns --lbafee -l --output-format -o --output-format-version --sel -S --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -1865,8 +1865,8 @@ plugin_feat_opts () { ;; "temp-thresh") - opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --thsel= -H --timeout= --tmpsel= -m --tmpth= -T --tmpthh= -M --verbose -v" - valopts+=" --output-format -o --output-format-version --sel -S --set-options --thsel -H --timeout --tmpsel -m --tmpth -T --tmpthh -M" + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --save -s --sel= -S --set-options= --thsel= --timeout= --tmpsel= -m --tmpth= -T --tmpthh= -M --verbose -v" + valopts+=" --output-format -o --output-format-version --sel -S --set-options --thsel --timeout --tmpsel -m --tmpth -T --tmpthh -M" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -2550,7 +2550,7 @@ plugin_lm_opts () { ;; "migration-recv") - opts+=" --cntlid= -c --csuuidi= -U --csvi= -V --dry-run --human-readable -H --no-ioctl-probing --no-retries --numd= -n --offset= -O --output-file= -f --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --uidx= -u --verbose -v" + opts+=" --cntlid= -c --csuuidi= -U --csvi= -V --dry-run --no-ioctl-probing --no-retries --numd= -n --offset= -O --output-file= -f --output-format= -o --output-format-version= --quiet --sel= -s --set-options= --timeout= --uidx= -u --verbose -v" valopts+=" --cntlid -c --csuuidi -U --csvi -V --numd -n --offset -O --output-file -f --output-format -o --output-format-version --sel -s --set-options --timeout --uidx -u" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -2572,8 +2572,8 @@ plugin_lm_opts () { ;; "set-cdq") - opts+=" --cdqid= -C --dry-run --hp= -H --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --tpt= -T --verbose -v" - valopts+=" --cdqid -C --hp -H --output-format -o --output-format-version --set-options --timeout --tpt -T" + opts+=" --cdqid= -C --dry-run --hp= --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --tpt= -T --verbose -v" + valopts+=" --cdqid -C --hp --output-format -o --output-format-version --set-options --timeout --tpt -T" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -3367,7 +3367,7 @@ plugin_nbft_opts () { _nvme_detect_value_completion case "$1" in "show") - opts+=" --discovery -d --dry-run --hfi -H --nbft-path= --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsystem -s --timeout= --verbose -v" + opts+=" --discovery -d --dry-run --hfi --nbft-path= --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --subsystem -s --timeout= --verbose -v" valopts+=" --nbft-path --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -4021,8 +4021,8 @@ plugin_rpmb_opts () { ;; "read-data") - opts+=" --address= -o --blocks= -b --dry-run --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" - valopts+=" --address -o --blocks -b --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" + opts+=" --address= --blocks= -b --dry-run --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" + valopts+=" --address --blocks -b --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -4041,8 +4041,8 @@ plugin_rpmb_opts () { ;; "write-data") - opts+=" --address= -o --blocks= -b --dry-run --key= -k --keyfile= -g --msg= -d --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" - valopts+=" --address -o --blocks -b --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" + opts+=" --address= --blocks= -b --dry-run --key= -k --keyfile= -g --msg= -d --msgfile= -f --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --target= -t --timeout= --verbose -v" + valopts+=" --address --blocks -b --key -k --keyfile -g --msg -d --msgfile -f --output-format -o --output-format-version --set-options --target -t --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ @@ -4069,7 +4069,7 @@ plugin_samsung_opts () { _nvme_detect_value_completion case "$1" in "vs-internal-log") - opts+=" --compress -z --dry-run --dump-type= -t --hide-progress -H --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --telemetry-data-area= -a --timeout= --verbose -v" + opts+=" --compress -z --dry-run --dump-type= -t --hide-progress --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --telemetry-data-area= -a --timeout= --verbose -v" valopts+=" --dump-type -t --output-file -O --output-format -o --output-format-version --set-options --telemetry-data-area -a --timeout" _nvme_opt_vals "--output-format -o" "normal" \ @@ -4268,7 +4268,7 @@ plugin_sed_opts () { _nvme_detect_value_completion case "$1" in "discover"|"1") - opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --udev -u --verbose -V --verbose -v" + opts+=" --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --udev -u --verbose -v" valopts+=" --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -4452,7 +4452,7 @@ plugin_shannon_opts () { _nvme_detect_value_completion case "$1" in "get-additional-feature") - opts+=" --cdw11= -c --data-len= -l --dry-run --feature-id= -f --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --sel= -s --set-options= --timeout= --verbose -v" + opts+=" --cdw11= -c --data-len= -l --dry-run --feature-id= -f --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --raw-binary -b --sel= -s --set-options= --timeout= --verbose -v" valopts+=" --cdw11 -c --data-len -l --feature-id -f --namespace-id -n --output-format -o --output-format-version --sel -s --set-options --timeout" _nvme_opt_vals "--namespace-id -n" "all -1" \ @@ -4715,7 +4715,7 @@ plugin_sndk_opts () { ;; "vs-internal-log") - opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -V --verbose -v" + opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -v" valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -5417,7 +5417,7 @@ plugin_wdc_opts () { ;; "vs-internal-log") - opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -V --verbose -v" + opts+=" --data-area= -d --dry-run --file-size= -f --no-ioctl-probing --no-retries --offset= -e --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --timeout= --transfer-size= -s --type= -t --verbose -v" valopts+=" --data-area -d --file-size -f --offset -e --output-file -O --output-format -o --output-format-version --set-options --timeout --transfer-size -s --type -t" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -5558,7 +5558,7 @@ plugin_zns_opts () { ;; "id-ns") - opts+=" --dry-run --human-readable -H --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --vendor-specific -V --verbose -v" + opts+=" --dry-run --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --vendor-specific -V --verbose -v" valopts+=" --namespace-id -n --output-format -o --output-format-version --set-options --timeout" _nvme_opt_vals "--namespace-id -n" "all -1" \ @@ -5598,7 +5598,7 @@ plugin_zns_opts () { ;; "report-zones") - opts+=" --descs= -d --dry-run --extended -e --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --state= -S --timeout= --verbose -V --verbose -v" + opts+=" --descs= -d --dry-run --extended -e --namespace-id= -n --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --partial -p --quiet --set-options= --start-lba= -s --state= -S --timeout= --verbose -v" valopts+=" --descs -d --namespace-id -n --output-format -o --output-format-version --set-options --start-lba -s --state -S --timeout" _nvme_opt_vals "--namespace-id -n" "all -1" \ diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index f412e0ffe7..d8857e3647 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -249,7 +249,7 @@ $script:NvmeOptions = @{ 'check-tls-key' = @('--compat', '-C', '--dry-run', '--hostnqn', '-n', '--identity', '-I', '--keydata', '-d', '--keyring', '-k', '--keytype', '-t', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--subsysnqn', '-c', '--timeout', '--verbose', '-v', '--help', '-h') 'cmdset-ind-id-ns' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'compare' = @('--app-tag', '-a', '--app-tag-mask', '-m', '--block-count', '-c', '--block-size', '-b', '--data', '-d', '--data-size', '-z', '--dir-spec', '-S', '--dir-type', '-T', '--dry-run', '--dsm', '-D', '--force', '--force-unit-access', '-f', '--latency', '-t', '--limited-retry', '-l', '--metadata', '-M', '--metadata-size', '-y', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--prinfo', '-p', '--quiet', '--ref-tag', '-r', '--set-options', '--show-command', '-V', '--start-block', '-s', '--storage-tag', '-g', '--storage-tag-check', '-C', '--timeout', '--verbose', '-v', '--help', '-h') - 'config convert' = @('--config', '-J', '--dry-run', '--force', '--no-ioctl-probing', '--no-retries', '--output', '-o', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'config convert' = @('--config', '-J', '--dry-run', '--force', '--no-ioctl-probing', '--no-retries', '--output', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'config create' = @('--concat', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--disable-sqflow', '--discovery', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-symname', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--no-ioctl-probing', '--no-retries', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output', '--output-format', '-o', '--output-format-version', '--persistent', '--queue-size', '-Q', '--quiet', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'config show' = @('--config', '-J', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'config status' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -285,7 +285,7 @@ $script:NvmeOptions = @{ 'exclusion edit' = @('--dry-run', '--name', '-N', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'exclusion list' = @('--dry-run', '--name', '-N', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'exclusion remove' = @('--dry-run', '--name', '-N', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'fdp configs' = @('--dry-run', '--endgrp-id', '-e', '--human-readable', '-H', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'fdp configs' = @('--dry-run', '--endgrp-id', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'fdp events' = @('--dry-run', '--endgrp-id', '-e', '--host-events', '-E', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'fdp feature' = @('--disable', '-d', '--dry-run', '--enable-conf-idx', '-c', '--endgrp-id', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'fdp set-events' = @('--dry-run', '--enable', '-e', '--event-types', '-t', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--placement-handle', '-p', '--quiet', '--save', '-s', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -293,11 +293,11 @@ $script:NvmeOptions = @{ 'fdp status' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'fdp update' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--pids', '-p', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'fdp usage' = @('--dry-run', '--endgrp-id', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'feat arbitration' = @('--ab', '-a', '--dry-run', '--hpw', '-H', '--lpw', '-l', '--mpw', '-m', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'feat arbitration' = @('--ab', '-a', '--dry-run', '--hpw', '--lpw', '-l', '--mpw', '-m', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat async-event-conf' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--result', '-r', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat err-recovery' = @('--dry-run', '--dulbe', '-d', '--no-ioctl-probing', '--no-retries', '--nsid', '-n', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--tler', '-t', '--verbose', '-v', '--help', '-h') 'feat hctm' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--tmt1', '-t', '--tmt2', '-T', '--verbose', '-v', '--help', '-h') - 'feat host-behavior-support' = @('--acre', '-a', '--cdfe', '-c', '--dry-run', '--etdas', '-e', '--hdisns', '-H', '--lbafee', '-l', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'feat host-behavior-support' = @('--acre', '-a', '--cdfe', '-c', '--dry-run', '--etdas', '-e', '--hdisns', '--lbafee', '-l', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat int-coalesce' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--thr', '-t', '--time', '-T', '--timeout', '--verbose', '-v', '--help', '-h') 'feat int-vector-config' = @('--cd', '-c', '--dry-run', '--iv', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'feat keep-alive-timer' = @('--dry-run', '--kato', '-k', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -308,7 +308,7 @@ $script:NvmeOptions = @{ 'feat power-meas' = @('--act', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--pmts', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--smt', '--timeout', '--uuid-index', '-u', '--verbose', '-v', '--help', '-h') 'feat power-mgmt' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--ps', '-p', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--wh', '-w', '--help', '-h') 'feat power-thresh' = @('--dry-run', '--ept', '-e', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--pmts', '-m', '--pts', '-t', '--ptv', '-p', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--uuid-index', '-u', '--verbose', '-v', '--help', '-h') - 'feat temp-thresh' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--thsel', '-H', '--timeout', '--tmpsel', '-m', '--tmpth', '-T', '--tmpthh', '-M', '--verbose', '-v', '--help', '-h') + 'feat temp-thresh' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--thsel', '--timeout', '--tmpsel', '-m', '--tmpth', '-T', '--tmpthh', '-M', '--verbose', '-v', '--help', '-h') 'feat timestamp' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--tstmp', '-t', '--verbose', '-v', '--help', '-h') 'feat volatile-wc' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--wce', '-w', '--help', '-h') 'feat write-atom-normal' = @('--dn', '-d', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--sel', '-S', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -397,9 +397,9 @@ $script:NvmeOptions = @{ 'lm create-cdq' = @('--cntlid', '-c', '--consent', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--queue-type', '-q', '--quiet', '--set-options', '--size', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'lm delete-cdq' = @('--cdqid', '-C', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'lm get-cdq' = @('--cdqid', '-C', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'lm migration-recv' = @('--cntlid', '-c', '--csuuidi', '-U', '--csvi', '-V', '--dry-run', '--human-readable', '-H', '--no-ioctl-probing', '--no-retries', '--numd', '-n', '--offset', '-O', '--output-file', '-f', '--output-format', '-o', '--output-format-version', '--quiet', '--sel', '-s', '--set-options', '--timeout', '--uidx', '-u', '--verbose', '-v', '--help', '-h') + 'lm migration-recv' = @('--cntlid', '-c', '--csuuidi', '-U', '--csvi', '-V', '--dry-run', '--no-ioctl-probing', '--no-retries', '--numd', '-n', '--offset', '-O', '--output-file', '-f', '--output-format', '-o', '--output-format-version', '--quiet', '--sel', '-s', '--set-options', '--timeout', '--uidx', '-u', '--verbose', '-v', '--help', '-h') 'lm migration-send' = @('--cntlid', '-c', '--csuuidi', '-U', '--csvi', '-V', '--dry-run', '--dudmq', '-d', '--input-file', '-f', '--no-ioctl-probing', '--no-retries', '--numd', '-n', '--offset', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--sel', '-s', '--seq-ind', '-S', '--set-options', '--stype', '-t', '--timeout', '--uidx', '-u', '--verbose', '-v', '--help', '-h') - 'lm set-cdq' = @('--cdqid', '-C', '--dry-run', '--hp', '-H', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--tpt', '-T', '--verbose', '-v', '--help', '-h') + 'lm set-cdq' = @('--cdqid', '-C', '--dry-run', '--hp', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--tpt', '-T', '--verbose', '-v', '--help', '-h') 'lm track-send' = @('--cdqid', '-C', '--dry-run', '--mos', '-m', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--sel', '-s', '--set-options', '--start', '--stop', '--timeout', '--verbose', '-v', '--help', '-h') 'lockdown' = @('--csel', '-c', '--css', '-C', '--dry-run', '--ifc', '-f', '--no-ioctl-probing', '--no-retries', '--ofi', '-O', '--output-format', '-o', '--output-format-version', '--prhbt', '-p', '--quiet', '--scp', '-s', '--set-options', '--timeout', '--uuid', '-U', '--verbose', '-v', '--help', '-h') 'log ana' = @('--dry-run', '--groups', '-g', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -480,7 +480,7 @@ $script:NvmeOptions = @{ 'micron vs-temperature-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'micron vs-vendor-telemetry-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'micron vs-work-load-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'nbft show' = @('--discovery', '-d', '--dry-run', '--hfi', '-H', '--nbft-path', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--subsystem', '-s', '--timeout', '--verbose', '-v', '--help', '-h') + 'nbft show' = @('--discovery', '-d', '--dry-run', '--hfi', '--nbft-path', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--subsystem', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'netapp ontapdevices' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'netapp smdevices' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'ns attach' = @('--controllers', '-c', '--dry-run', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -557,10 +557,10 @@ $script:NvmeOptions = @{ 'rpmb program-key' = @('--dry-run', '--key', '-k', '--keyfile', '-g', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') 'rpmb read-config' = @('--dry-run', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'rpmb read-counter' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') - 'rpmb read-data' = @('--address', '-o', '--blocks', '-b', '--dry-run', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') + 'rpmb read-data' = @('--address', '--blocks', '-b', '--dry-run', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') 'rpmb write-config' = @('--dry-run', '--key', '-k', '--keyfile', '-g', '--msg', '-d', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'rpmb write-data' = @('--address', '-o', '--blocks', '-b', '--dry-run', '--key', '-k', '--keyfile', '-g', '--msg', '-d', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') - 'samsung vs-internal-log' = @('--compress', '-z', '--dry-run', '--dump-type', '-t', '--hide-progress', '-H', '--no-ioctl-probing', '--no-retries', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--telemetry-data-area', '-a', '--timeout', '--verbose', '-v', '--help', '-h') + 'rpmb write-data' = @('--address', '--blocks', '-b', '--dry-run', '--key', '-k', '--keyfile', '-g', '--msg', '-d', '--msgfile', '-f', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--target', '-t', '--timeout', '--verbose', '-v', '--help', '-h') + 'samsung vs-internal-log' = @('--compress', '-z', '--dry-run', '--dump-type', '-t', '--hide-progress', '--no-ioctl-probing', '--no-retries', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--telemetry-data-area', '-a', '--timeout', '--verbose', '-v', '--help', '-h') 'sanitize' = @('--ause', '-u', '--dry-run', '--emvs', '-e', '--ish', '-I', '--no-dealloc', '-d', '--no-ioctl-probing', '--no-retries', '--oipbp', '-i', '--output-format', '-o', '--output-format-version', '--ovrpat', '-p', '--owpass', '-n', '--preq', '-q', '--quiet', '--repeat', '-r', '--sanact', '-a', '--set-options', '--timeout', '--verbose', '-v', '--wait', '-w', '--help', '-h') 'sanitize-log' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--rae', '-r', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sanitize-ns' = @('--ause', '-u', '--dry-run', '--emvs', '-e', '--ish', '-I', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--preq', '-q', '--quiet', '--sanact', '-a', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -580,8 +580,8 @@ $script:NvmeOptions = @{ 'security send' = @('--dry-run', '--file', '-f', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--secp', '-p', '--set-options', '--spsp', '-s', '--timeout', '--tl', '-t', '--verbose', '-v', '--help', '-h') 'security-recv' = @('--al', '-t', '--dry-run', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--secp', '-p', '--set-options', '--size', '-x', '--spsp', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'security-send' = @('--dry-run', '--file', '-f', '--ish', '-I', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--nssf', '-N', '--output-format', '-o', '--output-format-version', '--quiet', '--secp', '-p', '--set-options', '--spsp', '-s', '--timeout', '--tl', '-t', '--verbose', '-v', '--help', '-h') - 'sed 1' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-V', '--verbose', '-v', '--help', '-h') - 'sed discover' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'sed 1' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-v', '--help', '-h') + 'sed discover' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--udev', '-u', '--verbose', '-v', '--help', '-h') 'sed initialize' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--read-only', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sed lock' = @('--ask-key', '-k', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--read-only', '-r', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sed password' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -601,7 +601,7 @@ $script:NvmeOptions = @{ 'sfx set-feature' = @('--dry-run', '--feature-id', '-f', '--force', '-s', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--value', '-V', '--verbose', '-v', '--help', '-h') 'sfx smart-log-add' = @('--dry-run', '--json', '-j', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sfx status' = @('--dry-run', '--json-print', '-j', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'shannon get-additional-feature' = @('--cdw11', '-c', '--data-len', '-l', '--dry-run', '--feature-id', '-f', '--human-readable', '-H', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--sel', '-s', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') + 'shannon get-additional-feature' = @('--cdw11', '-c', '--data-len', '-l', '--dry-run', '--feature-id', '-f', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--sel', '-s', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'shannon id-ctrl' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--vendor-specific', '-V', '--verbose', '-v', '--help', '-h') 'shannon set-additioal-feature' = @('--data', '-d', '--data-len', '-l', '--dry-run', '--feature-id', '-f', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--save', '-s', '--set-options', '--timeout', '--value', '-V', '--verbose', '-v', '--help', '-h') 'shannon smart-log-add' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -631,7 +631,7 @@ $script:NvmeOptions = @{ 'sndk vs-error-reason-identifier' = @('--dry-run', '--file', '-O', '--log-id', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-fw-activate-history' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-hw-rev-log' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'sndk vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'sndk vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-v', '--help', '-h') 'sndk vs-nand-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-pcie-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'sndk vs-smart-add-log' = @('--dry-run', '--interval', '-i', '--log-page-mask', '-p', '--log-page-version', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -701,7 +701,7 @@ $script:NvmeOptions = @{ 'wdc vs-error-reason-identifier' = @('--dry-run', '--file', '-O', '--log-id', '-i', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-fw-activate-history' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-hw-rev-log' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'wdc vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'wdc vs-internal-log' = @('--data-area', '-d', '--dry-run', '--file-size', '-f', '--no-ioctl-probing', '--no-retries', '--offset', '-e', '--output-file', '-O', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--transfer-size', '-s', '--type', '-t', '--verbose', '-v', '--help', '-h') 'wdc vs-nand-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-pcie-stats' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'wdc vs-smart-add-log' = @('--dry-run', '--interval', '-i', '--log-page-mask', '-p', '--log-page-version', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -715,11 +715,11 @@ $script:NvmeOptions = @{ 'zns close-zone' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select-all', '-a', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'zns finish-zone' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select-all', '-a', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'zns id-ctrl' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') - 'zns id-ns' = @('--dry-run', '--human-readable', '-H', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--vendor-specific', '-V', '--verbose', '-v', '--help', '-h') + 'zns id-ns' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--vendor-specific', '-V', '--verbose', '-v', '--help', '-h') 'zns list' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'zns offline-zone' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select-all', '-a', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'zns open-zone' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select-all', '-a', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--zrwaa', '-r', '--help', '-h') - 'zns report-zones' = @('--descs', '-d', '--dry-run', '--extended', '-e', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--partial', '-p', '--quiet', '--set-options', '--start-lba', '-s', '--state', '-S', '--timeout', '--verbose', '-V', '--verbose', '-v', '--help', '-h') + 'zns report-zones' = @('--descs', '-d', '--dry-run', '--extended', '-e', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--partial', '-p', '--quiet', '--set-options', '--start-lba', '-s', '--state', '-S', '--timeout', '--verbose', '-v', '--help', '-h') 'zns reset-zone' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--select-all', '-a', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'zns set-zone-desc' = @('--data', '-d', '--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--start-lba', '-s', '--timeout', '--verbose', '-v', '--zrwaa', '-r', '--help', '-h') 'zns zone-append' = @('--data', '-d', '--data-size', '-z', '--dry-run', '--force-unit-access', '-f', '--latency', '-t', '--limited-retry', '-l', '--metadata', '-M', '--metadata-size', '-y', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--piremap', '-P', '--prinfo', '-p', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--zslba', '-s', '--help', '-h') @@ -789,7 +789,6 @@ $script:NvmeFileOptions = @{ 'config convert --config' = $true 'config convert --output' = $true 'config convert -J' = $true - 'config convert -o' = $true 'config create --output' = $true 'config show --config' = $true 'config show -J' = $true From bdc9abca57b0ba244a7557d098a8e1dea0bea316 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 11:53:17 +0000 Subject: [PATCH 109/263] NEWS: document the global option name changes Global options are looked up first, the commands which used their names were changed and --human-readable is deprecated. Signed-off-by: Daniel Wagner --- NEWS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NEWS.md b/NEWS.md index 6f4bf28a22..3daf26aa13 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,24 @@ * Number options reject text after the number. Before, the rest was ignored: `--timeout=10s` set a timeout of 10 milliseconds. +* Global options mean the same for every command now: they are looked + up before the options of a command, and no command uses their names + any more. + - The own `--human-readable` (`-H`) of `fdp configs`, + `lm migration-recv`, `shannon get-additional-feature` and + `zns id-ns`, and the own `--verbose` (`-V`) of `sndk` and + `wdc vs-internal-log`, `sed discover` and `zns report-zones` are + gone; the global `--verbose` (`-v`) does their job. `-V` is no + longer accepted by these commands. + - These options lost their short name, which a global option uses: + `config convert --output` and `rpmb read-data`/`write-data + --address` (`-o`), `feat arbitration --hpw`, `feat temp-thresh + --thsel`, `feat host-behavior-support --hdisns`, `lm set-cdq --hp`, + `nbft show --hfi` and `samsung vs-internal-log --hide-progress` + (`-H`). + - `--human-readable` and `-H`, the old spelling of `--verbose`, print + a deprecation warning. + * The deprecated `tls-key` command parses its options like every other command: it has a help, and abbreviated options such as `--keyr` are no longer accepted. It, `gen-dhchap-key` and `check-dhchap-key` From 79dc640c100b1aa4bf126c0a1cbd97e0751fa7cd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:09:39 +0000 Subject: [PATCH 110/263] argconfig: accept the old name of an option with a warning Renamed options kept their old name as a second, hidden option entry with the same variable: --dhchap-secret, --dhchap-ctrl-secret and --fast_io_fail_tmo for the fabrics commands, which also repeated the short names -S, -C and -F, and --force for --no-reuse of discover, connect-all and discovery.conf. They were deprecated with v3.0, but only the man pages said so. Let an option entry name its old long name. argconfig accepts it for the option and warns once that it is deprecated, naming the option which replaces it. The second entries go. Signed-off-by: Daniel Wagner --- src/argconfig.c | 9 +++++++++ src/argconfig.h | 2 ++ src/fabrics.c | 12 ++++-------- src/fabrics.h | 15 +++++++++------ tests/cli/nvme_usage_test.py | 11 +++++++++++ tests/unit/c/test-argconfig-parse.c | 23 +++++++++++++++++++++++ 6 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 28146de09d..85bbfe2b09 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -471,6 +471,9 @@ argconfig_find_long(struct argconfig_commandline_options *s, continue; if (strlen(s->option) == len && !strncmp(s->option, name, len)) return s; + if (s->old_name && strlen(s->old_name) == len && + !strncmp(s->old_name, name, len)) + return s; } return NULL; @@ -596,6 +599,12 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) break; } + if (s->old_name && !s->seen && + (strlen(s->option) != len || strncmp(s->option, name, len))) + fprintf(stderr, + "WARNING: '--%s' is deprecated and will be removed in the next major version, use '--%s' instead\n", + s->old_name, s->option); + return argconfig_apply(s, 0, value); } diff --git a/src/argconfig.h b/src/argconfig.h index 3ae9cef9cb..a02ff3aeff 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -227,6 +227,8 @@ struct argconfig_commandline_options { bool hidden; /* the option which replaces a deprecated one, warned about on use */ const char *deprecated; + /* the long name of earlier versions, accepted with a warning */ + const char *old_name; }; void argconfig_append_usage(const char *str); diff --git a/src/fabrics.c b/src/fabrics.c index 3464baf785..297b98afbc 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -589,10 +589,8 @@ int nvmf_convert_discovery_line(struct libnvmf_config_emitter *emitter, "persistent discovery connection mode " "(default: auto; use =no to disable)"), OPT_FLAG("no-reuse", 0, &no_reuse, - "always create a new connection, never reuse " - "an existing one"), - OPT_FLAG("force", 0, &no_reuse, - "deprecated, see --no-reuse")); + "always create a new connection, never reuse an existing one", + .old_name = "force")); if (line[0] == '#' || line[0] == '\n' || line[0] == '\0') return 0; @@ -883,10 +881,8 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) "(default: auto; use =no to disable)"), OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file), OPT_FLAG("no-reuse", 0, &no_reuse, - "always create a new connection, never reuse " - "an existing one"), - OPT_FLAG("force", 0, &no_reuse, - "deprecated, see --no-reuse"), + "always create a new connection, never reuse an existing one", + .old_name = "force"), OPT_FLAG("nbft", 0, &nbft, "Only look at NBFT tables"), OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"), OPT_STRING("owner", 0, "NAME", &owner, "record this owner in the registry"), diff --git a/src/fabrics.h b/src/fabrics.h index 96cb3ef817..0193895e79 100644 --- a/src/fabrics.h +++ b/src/fabrics.h @@ -85,10 +85,12 @@ struct nvmf_args { OPT_STRING("host-iface", 'f', "STR", &f.host_iface, DESC_NVMF_HIFACE), \ OPT_STRING("hostnqn", 'q', "STR", &f.hostnqn, DESC_NVMF_HOSTNQN), \ OPT_STRING("hostid", 'I', "STR", &f.hostid, DESC_NVMF_HOSTID), \ - OPT_STRING("kxchap-secret", 'S', "STR", &f.hostkey, DESC_NVMF_HOSTKEY), \ - OPT_STRING("kxchap-ctrl-secret", 'C', "STR", &f.ctrlkey, DESC_NVMF_CTRLKEY), \ - OPT_STRING("dhchap-secret", 'S', "STR", &f.hostkey, DESC_NVMF_HOSTKEY, NULL, true), \ - OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &f.ctrlkey, DESC_NVMF_CTRLKEY, NULL, true), \ + OPT_STRING("kxchap-secret", 'S', "STR", &f.hostkey, \ + DESC_NVMF_HOSTKEY, \ + .old_name = "dhchap-secret"), \ + OPT_STRING("kxchap-ctrl-secret", 'C', "STR", &f.ctrlkey, \ + DESC_NVMF_CTRLKEY, \ + .old_name = "dhchap-ctrl-secret"), \ OPT_STRING("keyring", 0, "STR", &f.keyring, DESC_NVMF_KEYRING), \ OPT_STRING("tls-key", 0, "STR", &f.tls_key, DESC_NVMF_TLS_KEY), \ OPT_STRING("tls-key-identity", 0, "STR", &f.tls_key_identity, DESC_NVMF_TLS_KEY_IDENTITY), \ @@ -99,8 +101,9 @@ struct nvmf_args { OPT_INT("keep-alive-tmo", 'k', &f.keep_alive_tmo, DESC_NVMF_KEEP_ALIVE_TMO), \ OPT_INT("reconnect-delay", 'c', &f.reconnect_delay, DESC_NVMF_RECONNECT_DELAY), \ OPT_INT("ctrl-loss-tmo", 'l', &f.ctrl_loss_tmo, DESC_NVMF_CTRL_LOSS_TMO), \ - OPT_INT("fast-io-fail-tmo", 'F', &f.fast_io_fail_tmo, DESC_NVMF_FAST_IO_FAIL_TMO),\ - OPT_INT("fast_io_fail_tmo", 'F', &f.fast_io_fail_tmo, DESC_NVMF_FAST_IO_FAIL_TMO, NULL, true), \ + OPT_INT("fast-io-fail-tmo", 'F', &f.fast_io_fail_tmo, \ + DESC_NVMF_FAST_IO_FAIL_TMO, \ + .old_name = "fast_io_fail_tmo"), \ OPT_INT("tos", 'T', &f.tos, DESC_NVMF_TOS), \ OPT_INT("tls_key", 0, &f.tls_key_id, DESC_NVMF_TLS_KEY_LEGACY), \ OPT_FLAG("duplicate-connect", 'D', &f.duplicate_connect, DESC_NVMF_DUP_CONNECT), \ diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 5ad7029a1e..744e81ab14 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -57,6 +57,17 @@ def test_human_readable(self): self.assertIn(f"WARNING: '{opt}' is deprecated", result.stderr) self.assertIn("use '--verbose/-v' instead", result.stderr) + def test_old_option_name(self): + # discover takes the old spelling of --fast-io-fail-tmo + if run('discover', '--help').returncode: + self.skipTest('no fabrics commands in this build') + result = run('discover', '--fast_io_fail_tmo=1', '--help') + self.assertEqual(result.returncode, 0) + self.assertIn("WARNING: '--fast_io_fail_tmo' is deprecated", + result.stderr) + self.assertIn("use '--fast-io-fail-tmo' instead", result.stderr) + self.assertNotIn('fast_io_fail_tmo', result.stdout) + def test_warns_once(self): result = run('list', '-HH') self.assertEqual(result.stderr.count('WARNING'), 1) diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index d808e8d7d6..0a5b55d972 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -463,6 +463,28 @@ static void global_options_test(void) argconfig_set_global_options(NULL); } +/* An option also takes its old name, the option itself is given then */ +static void old_name_test(void) +{ + const char *desc = "Test old name"; + bool flag = false; + __u32 val = 0; + char **argv = (char *[]){ "prog", "--old-flag", "--old-uint=7", NULL }; + int ret; + + OPT_ARGS(opts) = { + OPT_FLAG("flag", 'f', &flag, "flag", .old_name = "old-flag"), + OPT_UINT("uint", 'u', &val, "uint", .old_name = "old-uint"), + OPT_END() + }; + + ret = argconfig_parse(argv_len(argv), argv, desc, opts); + if (ret || !flag || val != 7 || !argconfig_given(opts, &flag)) { + printf("ERROR: old name: unexpected result\n"); + test_rc = 1; + } +} + /* * argconfig_given() finds an option by the variable it stores to, also an * alias of it or a global option, and NULL is never given. @@ -934,6 +956,7 @@ int main(void) operands_test(); global_options_test(); given_test(); + old_name_test(); special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) From bc41280118cda222f0010884f16d3c86bba0b145 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:14:37 +0000 Subject: [PATCH 111/263] argconfig: follow a usage error with the synopsis, not the help A usage error printed the whole help of the command, a page or more for many commands, so the error scrolled out of sight. A bad value printed no help, but named the internal type: "Expected word argument for 'namespace-id' but got '1x'!", also when -n was given. Print the error, the synopsis and where the help is, for every usage error: fw commit: invalid value 'x' for -s: expected a number from 0 to 255 Usage: nvme fw commit [OPTIONS] Try 'nvme fw commit --help' for more information. The errors start with the name of the command, which plugin.c tells argconfig with the usage, instead of the last word of it. A bad value names the option as it was given, the values it takes and the names it accepts, or points to the help for a long list of names. A device which can't be opened is no usage error. Its error message is enough, without the help which followed it so far, and which also went in between the JSON of -o json. Signed-off-by: Daniel Wagner --- src/argconfig.c | 139 +++++++++++++++++++++++++++-------- src/argconfig.h | 6 +- src/args.c | 23 ++---- src/global-ctx.c | 1 - src/nvme-cmds-io.c | 2 - src/nvme-cmds-sanitize.c | 2 - src/plugin.c | 16 +++- tests/cli/nvme_usage_test.py | 34 ++++++++- 8 files changed, 165 insertions(+), 58 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 85bbfe2b09..c64fd0e09e 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -84,6 +84,47 @@ void argconfig_append_usage(const char *str) append_usage_str = str; } +/* "nvme" and "id ctrl" of the current command, for the usage errors */ +static const char *command_prog; +static const char *command_path; +/* argv[0] of the current parse, if no command is set */ +static const char *parse_argv0 = ""; + +void argconfig_set_command(const char *prog, const char *path) +{ + command_prog = prog; + command_path = path; +} + +static const char *command_name(void) +{ + return command_path ? command_path : parse_argv0; +} + +/* The synopsis and where to find the help, after a usage error */ +void argconfig_print_usage(void) +{ + fprintf(stderr, "Usage: %s\n", append_usage_str); + if (command_prog && command_path) + fprintf(stderr, "Try '%s %s --help' for more information.\n", + command_prog, command_path); +} + +/* A usage error of the current command, followed by its usage */ +int argconfig_usage_error(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", command_name()); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + argconfig_print_usage(); + + return -EINVAL; +} + /* The names an option accepts, below its description */ static void show_value_names(FILE *out, const struct argconfig_opt_val *v) { @@ -189,13 +230,6 @@ static void show_help(FILE *out, const char *program_desc, print_options(out, argconfig_globals(s), "Global options"); } -/* The help after a usage error, on stderr */ -void argconfig_print_help(const char *program_desc, - struct argconfig_commandline_options *s) -{ - show_help(stderr, program_desc, s); -} - static bool help_requested; /* True once argconfig_parse() printed the help -h or --help asked for */ @@ -204,12 +238,6 @@ bool argconfig_help_requested(void) return help_requested; } -static int argconfig_error(char *type, const char *opt, const char *arg) -{ - fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", type, opt, arg); - return -EINVAL; -} - /* * strtoul() accepts a sign and negates the result, so "-1" would become the * largest value. The value tables of an option can name such a value. @@ -238,8 +266,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_LONG: case CFG_LONG_SUFFIX: if (argconfig_negative(arg)) - return argconfig_error("non-negative number", s->option, - arg); + return -EINVAL; break; default: break; @@ -252,13 +279,13 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_INT: *(int *)value = strtol(arg, &endptr, 0); if (errno || arg == endptr || *endptr) - ret = argconfig_error("integer", s->option, arg); + ret = -EINVAL; break; case CFG_BYTE: { unsigned long tmp = strtoul(arg, &endptr, 0); if (errno || tmp >= 1 << 8 || arg == endptr || *endptr) - ret = argconfig_error("byte", s->option, arg); + ret = -EINVAL; else *(uint8_t *)value = tmp; break; @@ -267,7 +294,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, unsigned long tmp = strtoul(arg, &endptr, 0); if (errno || tmp >= 1 << 16 || arg == endptr || *endptr) - ret = argconfig_error("short", s->option, arg); + ret = -EINVAL; else *(uint16_t *)value = tmp; break; @@ -276,7 +303,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, unsigned long long tmp = strtoull(arg, &endptr, 0); if (errno || tmp > UINT32_MAX || arg == endptr || *endptr) - ret = argconfig_error("word", s->option, arg); + ret = -EINVAL; else *(uint32_t *)value = tmp; break; @@ -287,18 +314,15 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_LONG: *(uint64_t *)value = strtoull(arg, &endptr, 0); if (errno || arg == endptr || *endptr) - ret = argconfig_error("long integer", s->option, arg); + ret = -EINVAL; break; case CFG_LONG_SUFFIX: ret = shr_suffix_binary_parse(arg, &endptr, (uint64_t *)value); - if (ret) - argconfig_error("long suffixed integer", s->option, - arg); break; case CFG_DOUBLE: *(double *)value = strtod(arg, &endptr); if (errno || arg == endptr || *endptr) - ret = argconfig_error("float", s->option, arg); + ret = -EINVAL; break; case CFG_FLAG: *(bool *)value = true; @@ -452,7 +476,7 @@ static int argconfig_parse_error(struct argconfig_parser *p, { va_list ap; - fprintf(stderr, "%s: ", p->argv[0]); + fprintf(stderr, "%s: ", command_name()); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); @@ -522,9 +546,57 @@ argconfig_find_short_any(struct argconfig_commandline_options *s, char c) return o ? o : argconfig_find_short(s, c); } +/* What a value of @s has to look like */ +static const char *argconfig_expected(struct argconfig_commandline_options *s) +{ + switch (s->config_type) { + case CFG_BYTE: + return "a number from 0 to 255"; + case CFG_SHORT: + return "a number from 0 to 65535"; + case CFG_POSITIVE: + return "a number from 0 to 4294967295"; + case CFG_LONG: + return "a number from 0 to 18446744073709551615"; + case CFG_LONG_SUFFIX: + return "a size such as 4096 or 4Ki"; + default: + return "a number"; + } +} + +/* @arg is no value of @s, given by its short name @c or its long name */ +static int argconfig_value_error(struct argconfig_commandline_options *s, + char c, const char *arg) +{ + const struct argconfig_opt_val *v; + int n = 0; + + fprintf(stderr, "%s: invalid value '%s' for ", command_name(), arg); + if (c) + fprintf(stderr, "-%c", c); + else + fprintf(stderr, "--%s", s->option); + fprintf(stderr, ": expected %s", argconfig_expected(s)); + + /* A long list of names would drown the error, the help has them */ + for (v = s->opt_val; v && v->str; v++) + n++; + if (n > 8) + fprintf(stderr, " or one of the names in the help"); + for (v = s->opt_val; n <= 8 && v && v->str; v++) + fprintf(stderr, "%s%s", v == s->opt_val ? " or one of: " : ", ", + v->str); + fprintf(stderr, "\n"); + + return ARGCONFIG_USAGE; +} + static int argconfig_apply(struct argconfig_commandline_options *s, char c, char *arg) { + int ret; + if (s->deprecated && !s->seen) { if (c) fprintf(stderr, "WARNING: '-%c'", c); @@ -539,7 +611,11 @@ static int argconfig_apply(struct argconfig_commandline_options *s, char c, if (!s->default_value) return 0; - return argconfig_parse_val(s, arg); + ret = argconfig_parse_val(s, arg); + if (ret == -EINVAL) + return argconfig_value_error(s, c, arg); + + return ret; } /* The value of a required argument which is not attached to the option */ @@ -729,6 +805,7 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, }; int ret; + parse_argv0 = argc > 0 ? argv[0] : ""; free(operand_buf); operand_buf = NULL; operands.argv = NULL; @@ -744,16 +821,15 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, ret = argconfig_parse_args(&p); if (ret) { /* - * The help that was asked for goes to stdout, the one after a - * malformed option to stderr. A bad value prints no help. - * The command stops either way: it gets ARGCONFIG_HELP or an - * error. + * The help that was asked for goes to stdout, a usage error is + * followed by the synopsis on stderr. The command stops + * either way: it gets ARGCONFIG_HELP or an error. */ if (ret == ARGCONFIG_HELP) { show_help(stdout, program_desc, options); help_requested = true; } else if (ret > 0) { - argconfig_print_help(program_desc, options); + argconfig_print_usage(); ret = -EINVAL; } free(p.operands); @@ -795,6 +871,7 @@ int argconfig_parse_global(int argc, char *argv[], }; int ret; + parse_argv0 = argc > 0 ? argv[0] : ""; ret = argconfig_parse_args(&p); *argind = p.ind; if (ret) diff --git a/src/argconfig.h b/src/argconfig.h index a02ff3aeff..749905b27d 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -232,8 +232,10 @@ struct argconfig_commandline_options { }; void argconfig_append_usage(const char *str); -void argconfig_print_help(const char *program_desc, - struct argconfig_commandline_options *options); +void argconfig_set_command(const char *prog, const char *path); +void argconfig_print_usage(void); +int argconfig_usage_error(const char *fmt, ...) + __attribute__((format(printf, 1, 2))); bool argconfig_help_requested(void); int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_commandline_options *options); diff --git a/src/args.c b/src/args.c index 3af1742fe3..bb2f46932e 100644 --- a/src/args.c +++ b/src/args.c @@ -30,28 +30,19 @@ const char *namespace_id_desired = "identifier of desired namespace"; * The operand of the current command: the device, or nothing. A missing * or an extra operand is a usage error. */ -static int check_device(char *argv[], const char *desc, - struct argconfig_commandline_options *opts) +static int check_device(void) { int max = nvme_args.device_operand == COMMAND_NO_DEVICE ? 0 : 1; int nr = argconfig_nr_operands(); - if (nvme_args.device_operand == COMMAND_DEVICE && !nr) { - nvme_show_error("%s: missing ", argv[0]); - goto usage; - } - if (nr > max) { - nvme_show_error("%s: unexpected argument '%s'", argv[0], - argconfig_operand(max)); - goto usage; - } + if (nvme_args.device_operand == COMMAND_DEVICE && !nr) + return argconfig_usage_error("missing "); + if (nr > max) + return argconfig_usage_error("unexpected argument '%s'", + argconfig_operand(max)); nvme_args.device = argconfig_operand(0); return 0; - -usage: - argconfig_print_help(desc, opts); - return -EINVAL; } int parse_cmdline(int argc, char *argv[], const char *desc, @@ -63,7 +54,7 @@ int parse_cmdline(int argc, char *argv[], const char *desc, if (ret) return ret; - return check_device(argv, desc, opts); + return check_device(); } int parse_args(int argc, char *argv[], const char *desc, diff --git a/src/global-ctx.c b/src/global-ctx.c index f45d1b850c..3734dcdded 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -267,7 +267,6 @@ int parse_and_open(struct libnvme_global_ctx **ctx, ret = get_transport_handle(ctx_new, argc, argv, O_RDONLY, &hdl_new); if (ret) { libnvme_free_global_ctx(ctx_new); - argconfig_print_help(desc, opts); return -ENXIO; } diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 018f88c50c..33eae49ac1 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -1065,8 +1065,6 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char if (!cfg.force) nvme_show_error( "Use the force [--force] option to ignore that."); - } else { - argconfig_print_help(desc, opts); } return err; } diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 6d290d7e57..04a7ea7a8c 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -778,8 +778,6 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin nvme_show_error("Namespace is currently busy."); if (!cfg.force) nvme_show_error("Use the force [--force] option to ignore that."); - } else { - argconfig_print_help(desc, opts); } return err; } diff --git a/src/plugin.c b/src/plugin.c index b4f3b5c41e..25a0c37e28 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -437,13 +437,25 @@ void command_prepare(struct command *cmd) static int run_command(int argc, char **argv, struct plugin *plugin, struct command *cmd) { - char use[0x100]; + char use[0x100], path[0x80]; + int err; build_command_usage(use, sizeof(use), plugin->parent, plugin, cmd); + if (plugin->name) + snprintf(path, sizeof(path), "%s %s", plugin->name, cmd->name); + else + snprintf(path, sizeof(path), "%s", cmd->name); argconfig_append_usage(use); + argconfig_set_command(plugin->parent->name, path); command_prepare(cmd); - return cmd->fn(argc, argv, cmd, plugin); + err = cmd->fn(argc, argv, cmd, plugin); + + /* use and path go out of scope */ + argconfig_append_usage(""); + argconfig_set_command(NULL, NULL); + + return err; } /* diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 744e81ab14..3c97c46143 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -37,10 +37,40 @@ def test_help_on_stdout_and_success(self): def test_usage_error_on_stderr_and_failure(self): result = run('id', 'ctrl', '--bogus') self.assertEqual(result.returncode, 1) - self.assertIn("unrecognized option '--bogus'", result.stderr) - self.assertIn('Usage: nvme id ctrl ', result.stderr) + self.assertIn("id ctrl: unrecognized option '--bogus'", result.stderr) self.assertEqual(result.stdout, '') + def test_usage_error_shows_synopsis_and_hint(self): + # the error must not drown in the full help + for args in (('id', 'ctrl', '--bogus'), + ('id', 'ctrl'), + ('id', 'ns', '/dev/nvme0', '-n', '1x')): + with self.subTest(args=args): + result = run(*args) + self.assertEqual(result.returncode, 1) + self.assertIn('Usage: nvme id ', result.stderr) + self.assertRegex(result.stderr, + r"Try 'nvme id c?\w+ --help' for more " + r"information\.") + self.assertNotIn('Options:', result.stderr) + + def test_open_failure_is_no_usage_error(self): + result = run('id', 'ns', '/dev/nvme-does-not-exist') + self.assertEqual(result.returncode, 1) + self.assertIn('/dev/nvme-does-not-exist', result.stderr) + self.assertNotIn('Usage:', result.stderr) + result = run('id', 'ns', '/dev/nvme-does-not-exist', '-o', 'json') + self.assertNotIn('Usage:', result.stdout + result.stderr) + + def test_value_error_names_the_spelling(self): + result = run('id', 'ns', '/dev/nvme0', '-n', '1x') + self.assertIn("id ns: invalid value '1x' for -n: expected a number " + "from 0 to 4294967295 or one of: all, -1", + result.stderr) + result = run('id', 'ns', '/dev/nvme0', '--namespace-id=1x') + self.assertIn("invalid value '1x' for --namespace-id", + result.stderr) + def test_no_escape_sequences_when_not_a_terminal(self): result = run('id', 'ctrl', '--help') self.assertNotIn('\033[', result.stdout) From 1c4e351cdcc1ac8249322b82e8d89b3708a0ccc4 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:14:51 +0000 Subject: [PATCH 112/263] plugin: print the group titles in bold only on a terminal The general help printed its group titles with escape sequences for bold also into a pipe or a file. Print them like the headings of the command help, which do so only on a terminal. Signed-off-by: Daniel Wagner --- src/argconfig.c | 6 +++--- src/argconfig.h | 1 + src/plugin.c | 12 ++++++++++-- tests/cli/nvme_usage_test.py | 6 ++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index c64fd0e09e..e33699121a 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -185,7 +185,7 @@ static void show_option(FILE *out, } /* A heading, in bold on a terminal */ -static void print_heading(FILE *out, const char *fmt, const char *text) +void argconfig_print_heading(FILE *out, const char *fmt, const char *text) { bool tty = isatty(fileno(out)); @@ -208,7 +208,7 @@ static void print_options(FILE *out, struct argconfig_commandline_options *s, } if (!header_printed) { fputc('\n', out); - print_heading(out, "%s:", header); + argconfig_print_heading(out, "%s:", header); fputc('\n', out); header_printed = true; } @@ -220,7 +220,7 @@ static void print_options(FILE *out, struct argconfig_commandline_options *s, static void show_help(FILE *out, const char *program_desc, struct argconfig_commandline_options *s) { - print_heading(out, "Usage: %s", append_usage_str); + argconfig_print_heading(out, "Usage: %s", append_usage_str); fputs("\n\n", out); shr_print_word_wrapped(program_desc, 0, 0, out); diff --git a/src/argconfig.h b/src/argconfig.h index 749905b27d..f30f61dcbc 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -232,6 +232,7 @@ struct argconfig_commandline_options { }; void argconfig_append_usage(const char *str); +void argconfig_print_heading(FILE *out, const char *fmt, const char *text); void argconfig_set_command(const char *prog, const char *path); void argconfig_print_usage(void); int argconfig_usage_error(const char *fmt, ...) diff --git a/src/plugin.c b/src/plugin.c index 25a0c37e28..b7b311e94f 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -233,6 +233,14 @@ static void print_device_desc(void) #endif } +/* The title of a command group, in bold on a terminal */ +static void print_group_title(const char *title) +{ + putchar('\n'); + argconfig_print_heading(stdout, "%s:", title); + putchar('\n'); +} + void general_help(struct plugin *plugin, char *str) { struct program *prog = plugin->parent; @@ -301,7 +309,7 @@ void general_help(struct plugin *plugin, char *str) if (str && !strstr(command->name, str)) continue; if (!header_printed && group->title) { - printf("\n\033[1m%s:\033[0m\n", group->title); + print_group_title(group->title); header_printed = true; } printf(" %-*s %s\n", padding, command->name, command->help); @@ -324,7 +332,7 @@ void general_help(struct plugin *plugin, char *str) if (str && !strstr(extension->name, str)) continue; if (!header_printed) { - printf("\n\033[1m%s:\033[0m\n", group->title); + print_group_title(group->title); header_printed = true; } printf(" %-*s %s\n", padding, extension->name, diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 3c97c46143..176b615225 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -72,8 +72,10 @@ def test_value_error_names_the_spelling(self): result.stderr) def test_no_escape_sequences_when_not_a_terminal(self): - result = run('id', 'ctrl', '--help') - self.assertNotIn('\033[', result.stdout) + for args in (('id', 'ctrl', '--help'), ('help',), ('id', 'help')): + with self.subTest(args=args): + result = run(*args) + self.assertNotIn('\033[', result.stdout + result.stderr) class DeprecatedOptionTest(unittest.TestCase): From 7ae54d31b19727933285dfb3044b07324a6f1f6f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:17:18 +0000 Subject: [PATCH 113/263] argconfig: hide the deprecated options instead of a hidden flag An option entry could be hidden from the help, the metadata and the nvme-cli.conf keys. The renamed options used that for their old names, which are a field of the option now. The only hidden option left is --human-readable, which is deprecated. Leave the deprecated options out instead and drop the flag. The command metadata still marks them hidden. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata.c | 2 +- src/argconfig.c | 2 +- src/argconfig.h | 6 ++++-- src/global-config.c | 2 +- src/global-options.c | 4 ++-- tests/unit/c/test-argconfig-parse.c | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index e05660a06b..eceeaf1cae 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -321,7 +321,7 @@ static int copy_options(const struct argconfig_commandline_options *opts, dst[i].short_option = opts[i].short_option; dst[i].config_type = opts[i].config_type; dst[i].argument_type = opts[i].argument_type; - dst[i].hidden = opts[i].hidden; + dst[i].hidden = opts[i].deprecated; } *out = dst; diff --git a/src/argconfig.c b/src/argconfig.c index e33699121a..51c16f1b27 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -212,7 +212,7 @@ static void print_options(FILE *out, struct argconfig_commandline_options *s, fputc('\n', out); header_printed = true; } - if (!s->hidden) + if (!s->deprecated) show_option(out, s); } } diff --git a/src/argconfig.h b/src/argconfig.h index f30f61dcbc..2cbd360aa3 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -224,8 +224,10 @@ struct argconfig_commandline_options { const char *help; bool seen; struct argconfig_opt_val *opt_val; - bool hidden; - /* the option which replaces a deprecated one, warned about on use */ + /* + * The option which replaces a deprecated one. A deprecated option + * is warned about on use and left out of the help. + */ const char *deprecated; /* the long name of earlier versions, accepted with a warning */ const char *old_name; diff --git a/src/global-config.c b/src/global-config.c index 9d486fe798..c95922e6f4 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -52,7 +52,7 @@ static struct argconfig_commandline_options *find_key(const char *key) struct argconfig_commandline_options *s; for (s = argconfig_get_global_options(); s && s->option; s++) { - if (s->config_type == CFG_GROUP_SEPARATOR || s->hidden) + if (s->config_type == CFG_GROUP_SEPARATOR || s->deprecated) continue; if (!strcmp(s->option, key)) return s; diff --git a/src/global-options.c b/src/global-options.c index a75956c74d..54e2922b3f 100644 --- a/src/global-options.c +++ b/src/global-options.c @@ -49,8 +49,8 @@ static struct argconfig_commandline_options global_options[] = { OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, "output format version: 1|2"), /* the spelling of --verbose of older versions */ - OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, NULL, - true, "--verbose/-v"), + OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, + .deprecated = "--verbose/-v"), OPT_STRING("set-options", 0, "KEY=VALUE", &nvme_args.set_options, "set a libnvme library option (key=value[,key=value,...]);"), OPT_END() diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 0a5b55d972..719b0b3bc1 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -504,7 +504,7 @@ static void given_test(void) }; OPT_ARGS(opts) = { OPT_FLAG("flag", 'f', &alias, "flag"), - OPT_FLAG("old-flag", 0, &alias, NULL, NULL, true), + OPT_FLAG("old-flag", 0, &alias, NULL), OPT_UINT("uint", 'u', &cfg.uint, "uint"), OPT_END() }; From ca9af7f2d99728348feece2a2f3d3699329dac93 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:22:49 +0000 Subject: [PATCH 114/263] argconfig: color usage errors and warnings on a terminal Print usage errors in red and the WARNING of the deprecation warnings in yellow when they go to a terminal, which already gets its headings in bold. A non-empty NO_COLOR turns the colors and the bold off, see https://no-color.org/. Signed-off-by: Daniel Wagner --- Documentation/nvme.txt | 7 +++++ src/argconfig.c | 53 ++++++++++++++++++++++++++---------- src/argconfig.h | 7 +++++ src/plugin.c | 3 +- tests/cli/nvme_usage_test.py | 52 +++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 15 deletions(-) diff --git a/Documentation/nvme.txt b/Documentation/nvme.txt index ed74c1490e..3b88c95ad7 100644 --- a/Documentation/nvme.txt +++ b/Documentation/nvme.txt @@ -52,6 +52,13 @@ RETURNS All commands will behave the same, they will return 0 on success and 1 on failure. +ENVIRONMENT +----------- +NO_COLOR:: + When set to a non-empty value, nvme prints no colors or bold text, + see https://no-color.org/. Without it, errors, warnings and + headings are highlighted when they go to a terminal. + FURTHER DOCUMENTATION --------------------- See the freely available references on the http://nvmexpress.org[Official diff --git a/src/argconfig.c b/src/argconfig.c index 51c16f1b27..2afd4d194d 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -115,11 +115,12 @@ int argconfig_usage_error(const char *fmt, ...) { va_list ap; - fprintf(stderr, "%s: ", command_name()); + fprintf(stderr, "%s%s: ", argconfig_esc(stderr, ARGCONFIG_RED), + command_name()); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); argconfig_print_usage(); return -EINVAL; @@ -184,14 +185,33 @@ static void show_option(FILE *out, fprintf(out, "\n"); } +/* + * Escape sequences for bold and colors go only to a terminal, and not + * when NO_COLOR is set to a non-empty value (https://no-color.org/). + */ +const char *argconfig_esc(FILE *out, const char *seq) +{ + const char *no_color = getenv("NO_COLOR"); + + if (!isatty(fileno(out)) || (no_color && *no_color)) + return ""; + + return seq; +} + +/* "WARNING:" on stderr, in yellow on a terminal */ +void argconfig_print_warning(void) +{ + fprintf(stderr, "%sWARNING:%s", argconfig_esc(stderr, ARGCONFIG_YELLOW), + argconfig_esc(stderr, ARGCONFIG_RESET)); +} + /* A heading, in bold on a terminal */ void argconfig_print_heading(FILE *out, const char *fmt, const char *text) { - bool tty = isatty(fileno(out)); - - fputs(tty ? "\033[1m" : "", out); + fputs(argconfig_esc(out, ARGCONFIG_BOLD), out); fprintf(out, fmt, text); - fputs(tty ? "\033[0m" : "", out); + fputs(argconfig_esc(out, ARGCONFIG_RESET), out); } /* A group header is printed before the first option which follows it */ @@ -476,11 +496,12 @@ static int argconfig_parse_error(struct argconfig_parser *p, { va_list ap; - fprintf(stderr, "%s: ", command_name()); + fprintf(stderr, "%s%s: ", argconfig_esc(stderr, ARGCONFIG_RED), + command_name()); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); return ARGCONFIG_USAGE; } @@ -572,7 +593,8 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, const struct argconfig_opt_val *v; int n = 0; - fprintf(stderr, "%s: invalid value '%s' for ", command_name(), arg); + fprintf(stderr, "%s%s: invalid value '%s' for ", + argconfig_esc(stderr, ARGCONFIG_RED), command_name(), arg); if (c) fprintf(stderr, "-%c", c); else @@ -587,7 +609,7 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, for (v = s->opt_val; n <= 8 && v && v->str; v++) fprintf(stderr, "%s%s", v == s->opt_val ? " or one of: " : ", ", v->str); - fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); return ARGCONFIG_USAGE; } @@ -598,10 +620,11 @@ static int argconfig_apply(struct argconfig_commandline_options *s, char c, int ret; if (s->deprecated && !s->seen) { + argconfig_print_warning(); if (c) - fprintf(stderr, "WARNING: '-%c'", c); + fprintf(stderr, " '-%c'", c); else - fprintf(stderr, "WARNING: '--%s'", s->option); + fprintf(stderr, " '--%s'", s->option); fprintf(stderr, " is deprecated and will be removed in the next major version, use '%s' instead\n", s->deprecated); @@ -676,10 +699,12 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) } if (s->old_name && !s->seen && - (strlen(s->option) != len || strncmp(s->option, name, len))) + (strlen(s->option) != len || strncmp(s->option, name, len))) { + argconfig_print_warning(); fprintf(stderr, - "WARNING: '--%s' is deprecated and will be removed in the next major version, use '--%s' instead\n", + " '--%s' is deprecated and will be removed in the next major version, use '--%s' instead\n", s->old_name, s->option); + } return argconfig_apply(s, 0, value); } diff --git a/src/argconfig.h b/src/argconfig.h index 2cbd360aa3..ed12b3599f 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -234,6 +234,13 @@ struct argconfig_commandline_options { }; void argconfig_append_usage(const char *str); +#define ARGCONFIG_BOLD "\033[1m" +#define ARGCONFIG_RED "\033[31m" +#define ARGCONFIG_YELLOW "\033[33m" +#define ARGCONFIG_RESET "\033[0m" + +const char *argconfig_esc(FILE *out, const char *seq); +void argconfig_print_warning(void); void argconfig_print_heading(FILE *out, const char *fmt, const char *text); void argconfig_set_command(const char *prog, const char *path); void argconfig_print_usage(void); diff --git a/src/plugin.c b/src/plugin.c index b7b311e94f..400b32f443 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -520,8 +520,9 @@ int run_replacement_command(struct plugin *plugin, struct command *cmd, return -ENOTTY; } + argconfig_print_warning(); fprintf(stderr, - "WARNING: '%s' is deprecated and will be removed in the next major version, use 'nvme %s %s' instead\n", + " '%s' is deprecated and will be removed in the next major version, use 'nvme %s %s' instead\n", cmd->name, name, command); /* diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 176b615225..217bd625eb 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -19,6 +19,28 @@ _NVME_BIN = sys.argv[1] if len(sys.argv) > 1 else 'nvme' +def run_tty(*args, env=None): + """Run nvme with stderr on a terminal and return what it wrote + there.""" + import pty + master, slave = pty.openpty() + subprocess.run([_NVME_BIN] + list(args), stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, stderr=slave, env=env, + check=False) + os.close(slave) + out = b'' + try: + while True: + data = os.read(master, 4096) + if not data: + break + out += data + except OSError: + pass + os.close(master) + return out.decode() + + def run(*args): return subprocess.run([_NVME_BIN] + list(args), capture_output=True, text=True, stdin=subprocess.DEVNULL, check=False) @@ -78,6 +100,36 @@ def test_no_escape_sequences_when_not_a_terminal(self): self.assertNotIn('\033[', result.stdout + result.stderr) +class ColorTest(unittest.TestCase): + """Errors are red and warnings yellow on a terminal, unless NO_COLOR + is set to a non-empty value (https://no-color.org/).""" + + def setUp(self): + try: + import pty # noqa: F401 + except ImportError: + self.skipTest('no pty on this platform') + self.env = dict(os.environ) + self.env.pop('NO_COLOR', None) + + def test_error_in_red(self): + out = run_tty('id', 'ctrl', '--bogus', env=self.env) + self.assertIn("\033[31mid ctrl: unrecognized option '--bogus'", + out) + + def test_warning_in_yellow(self): + out = run_tty('list', '-H', env=self.env) + self.assertIn('\033[33mWARNING:\033[0m', out) + + def test_no_color(self): + self.env['NO_COLOR'] = '1' + self.assertNotIn('\033[', run_tty('id', 'ctrl', '--bogus', + env=self.env)) + self.env['NO_COLOR'] = '' + self.assertIn('\033[31m', run_tty('id', 'ctrl', '--bogus', + env=self.env)) + + class DeprecatedOptionTest(unittest.TestCase): """--human-readable and -H still raise the verbose level, with a warning which names the replacement.""" From 32f59dac1da747024f3e87def3b7b47467c6a8d6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 13:50:05 +0000 Subject: [PATCH 115/263] nvme: start the error messages with the name of the command The usage errors start with the name of the command, the other errors of a command did not: "/dev/nvme0: Permission denied". Start them with it as well, "id ns: /dev/nvme0: Permission denied", the errors, the perror messages and the NVMe status messages, unless no command runs. The JSON output stays the same. Color only that label red on a terminal, for the usage errors as well. A status message ends its own line, so a color for the whole message would run into the next one. Signed-off-by: Daniel Wagner --- src/argconfig.c | 29 ++++++++++++++++++++--------- src/argconfig.h | 1 + src/nvme-print-stdout.c | 23 +++++++++++++++++++---- tests/cli/nvme_usage_test.py | 8 ++++++-- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 2afd4d194d..3273128f86 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -101,6 +101,19 @@ static const char *command_name(void) return command_path ? command_path : parse_argv0; } +static void print_label(const char *name) +{ + fprintf(stderr, "%s%s:%s ", argconfig_esc(stderr, ARGCONFIG_RED), name, + argconfig_esc(stderr, ARGCONFIG_RESET)); +} + +/* ": " before an error of the current command, if one runs */ +void argconfig_print_error_label(void) +{ + if (command_path) + print_label(command_path); +} + /* The synopsis and where to find the help, after a usage error */ void argconfig_print_usage(void) { @@ -115,12 +128,11 @@ int argconfig_usage_error(const char *fmt, ...) { va_list ap; - fprintf(stderr, "%s%s: ", argconfig_esc(stderr, ARGCONFIG_RED), - command_name()); + print_label(command_name()); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); + fprintf(stderr, "\n"); argconfig_print_usage(); return -EINVAL; @@ -496,12 +508,11 @@ static int argconfig_parse_error(struct argconfig_parser *p, { va_list ap; - fprintf(stderr, "%s%s: ", argconfig_esc(stderr, ARGCONFIG_RED), - command_name()); + print_label(command_name()); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); + fprintf(stderr, "\n"); return ARGCONFIG_USAGE; } @@ -593,8 +604,8 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, const struct argconfig_opt_val *v; int n = 0; - fprintf(stderr, "%s%s: invalid value '%s' for ", - argconfig_esc(stderr, ARGCONFIG_RED), command_name(), arg); + print_label(command_name()); + fprintf(stderr, "invalid value '%s' for ", arg); if (c) fprintf(stderr, "-%c", c); else @@ -609,7 +620,7 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, for (v = s->opt_val; n <= 8 && v && v->str; v++) fprintf(stderr, "%s%s", v == s->opt_val ? " or one of: " : ", ", v->str); - fprintf(stderr, "%s\n", argconfig_esc(stderr, ARGCONFIG_RESET)); + fprintf(stderr, "\n"); return ARGCONFIG_USAGE; } diff --git a/src/argconfig.h b/src/argconfig.h index ed12b3599f..642d515ff3 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -241,6 +241,7 @@ void argconfig_append_usage(const char *str); const char *argconfig_esc(FILE *out, const char *seq); void argconfig_print_warning(void); +void argconfig_print_error_label(void); void argconfig_print_heading(FILE *out, const char *fmt, const char *text); void argconfig_set_command(const char *prog, const char *path); void argconfig_print_usage(void); diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 3f5f2ca94f..2ef85c3a1e 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -32,6 +32,7 @@ #include #include +#include "argconfig.h" #include "cleanup.h" #include "logging.h" #include "nvme-print.h" @@ -1397,7 +1398,8 @@ static void stdout_single_property(int offset, uint64_t value) stdout_ctrl_register_common(offset, value, true); } -static void stdout_status(int status) +/* The status line, which may follow other text */ +static void stdout_status_line(int status) { int val; int type; @@ -1437,6 +1439,7 @@ static void stdout_opcode_status(int status, bool admin, __u8 opcode) int val = nvme_status_get_value(status); int type = nvme_status_get_type(status); + argconfig_print_error_label(); if (status >= 0 && type == NVME_STATUS_TYPE_NVME) { fprintf(stderr, "NVMe status: %s(0x%x)\n", libnvme_opcode_status_to_string(val, admin, opcode), @@ -1444,14 +1447,21 @@ static void stdout_opcode_status(int status, bool admin, __u8 opcode) return; } - stdout_status(status); + stdout_status_line(status); +} + +static void stdout_status(int status) +{ + argconfig_print_error_label(); + stdout_status_line(status); } static void stdout_error_status(int status, const char *msg, va_list ap) { + argconfig_print_error_label(); vfprintf(stderr, msg, ap); fprintf(stderr, ": "); - stdout_status(status); + stdout_status_line(status); } char *stdout_power_and_scale_str(__u16 power, __u8 scale) @@ -4193,6 +4203,8 @@ static void stdout_topology_multipath(struct libnvme_global_ctx *ctx) static void stdout_message(bool error, const char *msg, va_list ap) { + if (error) + argconfig_print_error_label(); vfprintf(error ? stderr : stdout, msg, ap); fprintf(error ? stderr : stdout, "\n"); @@ -4201,11 +4213,14 @@ static void stdout_message(bool error, const char *msg, va_list ap) static void stdout_perror(const char *msg, va_list ap) { __cleanup_free char *error = NULL; + int err = errno; if (vasprintf(&error, msg, ap) < 0) error = NULL; - perror(error ? error : alloc_error); + argconfig_print_error_label(); + fprintf(stderr, "%s: %s\n", error ? error : alloc_error, + strerror(err)); } static void stdout_key_value(const char *key, const char *val, va_list ap) diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 217bd625eb..2725209582 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -76,6 +76,10 @@ def test_usage_error_shows_synopsis_and_hint(self): r"information\.") self.assertNotIn('Options:', result.stderr) + def test_error_labelled_with_the_command(self): + result = run('id', 'ns', '/dev/nvme-does-not-exist') + self.assertIn('id ns: /dev/nvme-does-not-exist', result.stderr) + def test_open_failure_is_no_usage_error(self): result = run('id', 'ns', '/dev/nvme-does-not-exist') self.assertEqual(result.returncode, 1) @@ -114,8 +118,8 @@ def setUp(self): def test_error_in_red(self): out = run_tty('id', 'ctrl', '--bogus', env=self.env) - self.assertIn("\033[31mid ctrl: unrecognized option '--bogus'", - out) + self.assertIn("\033[31mid ctrl:\033[0m unrecognized option " + "'--bogus'", out) def test_warning_in_yellow(self): out = run_tty('list', '-H', env=self.env) From f4d92a6d49acd978ef4f20773e094dea01880b74 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 13:55:44 +0000 Subject: [PATCH 116/263] doc: a separate value of --persistent is an error discover, connect-all and config create said that a value of --persistent given as a separate argument is dropped without an error. Since the commands check their operands, it is an extra argument and the command fails. Signed-off-by: Daniel Wagner --- Documentation/nvme-config-create.txt | 5 ++--- Documentation/nvme-connect-all.txt | 5 ++--- Documentation/nvme-discover.txt | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Documentation/nvme-config-create.txt b/Documentation/nvme-config-create.txt index c55451f63b..a6cad071b1 100644 --- a/Documentation/nvme-config-create.txt +++ b/Documentation/nvme-config-create.txt @@ -44,9 +44,8 @@ OPTIONS Record this discovery controller's persistence mode. Requires --discovery. Because the value is optional, it must be attached directly to the option (--persistent=force). A value given as a - separate argument (--persistent force) is silently NOT applied - -- it's dropped, and --persistent is treated as if given bare - (mode "auto"), with no error. + separate argument (--persistent force) is no value of the + option but an extra argument, and the command fails. + -- * 'auto' (the default for every --discovery entry, whether or not diff --git a/Documentation/nvme-connect-all.txt b/Documentation/nvme-connect-all.txt index fa588bcfb1..8902b9d6d4 100644 --- a/Documentation/nvme-connect-all.txt +++ b/Documentation/nvme-connect-all.txt @@ -69,9 +69,8 @@ OPTIONS Because the value is optional, it must be attached directly to the option: --persistent=force for the long form, -pforce for the short form. A value given as a separate argument (--persistent - force, -p force) is silently NOT applied -- it's dropped, and - --persistent/-p is treated as if given bare (mode "auto"), with - no error. + force, -p force) is no value of the option but an extra argument, + and the command fails. + -- * 'no': never persist. Use this to opt out of the default. diff --git a/Documentation/nvme-discover.txt b/Documentation/nvme-discover.txt index 59e45e5868..06a7d437fd 100644 --- a/Documentation/nvme-discover.txt +++ b/Documentation/nvme-discover.txt @@ -90,9 +90,8 @@ OPTIONS Because the value is optional, it must be attached directly to the option: --persistent=force for the long form, -pforce for the short form. A value given as a separate argument (--persistent - force, -p force) is silently NOT applied -- it's dropped, and - --persistent/-p is treated as if given bare (mode "auto"), with - no error. + force, -p force) is no value of the option but an extra argument, + and the command fails. + -- * 'no': never persist. Use this to opt out of the default. From 504153b072026c49fb2d5d504b1076c6350cad05 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:02:32 +0000 Subject: [PATCH 117/263] plugin: point to the commands instead of printing the general help main() printed the general help when the dispatch returned -ENOTTY, which handle_plugin() returns for an unknown command. But a command returns it as well, e.g. id ns on a controller device without a namespace id, whose ioctl fails with ENOTTY: the error was followed by 200 lines of help. Print where the commands are listed where the dispatch finds no command, and after a bad global option, which printed the general help as well: ERROR: Invalid sub-command 'bogus' for plugin id See 'nvme id --help' for the commands. Signed-off-by: Daniel Wagner --- src/nvme.c | 2 -- src/plugin.c | 15 ++++++++++++++- tests/cli/nvme_usage_test.py | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/nvme.c b/src/nvme.c index c5a2a09645..10b5901c31 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -188,8 +188,6 @@ int main(int argc, char **argv) return err; err = handle_plugin(argc, argv, nvme.extensions); - if (err == -ENOTTY) - general_help(&builtin, NULL); nvme_show_finish(); diff --git a/src/plugin.c b/src/plugin.c index 400b32f443..a3c295b048 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -432,6 +432,17 @@ void general_help(struct plugin *plugin, char *str) } } +/* Where the commands of @plugin are listed, after an error */ +static void commands_hint(struct plugin *plugin) +{ + if (plugin->name) + fprintf(stderr, "See '%s %s --help' for the commands.\n", + plugin->parent->name, plugin->name); + else + fprintf(stderr, "See '%s --help' for the commands.\n", + plugin->parent->name); +} + /* Apply the output formats and the operand of a command before it runs */ void command_prepare(struct command *cmd) { @@ -482,6 +493,7 @@ static int run_joined_command(int argc, char **argv, struct plugin *plugin) } if (!extension) { fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", argv[0]); + commands_hint(plugin); return -ENOTTY; } @@ -591,7 +603,7 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) */ err = argconfig_parse_global(argc, argv, NULL, &argind); if (err < 0) { - general_help(plugin, NULL); + commands_hint(plugin); return err; } @@ -622,6 +634,7 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) fprintf(stderr, "ERROR: Invalid sub-command '%s' for plugin %s\n", argv[0], plugin->name); + commands_hint(plugin); return -ENOTTY; } diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 2725209582..4f886dbc9f 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -172,6 +172,29 @@ def test_help_lists_the_names(self): self.assertIn('Names: infinite, -1', result.stdout) +class UnknownCommandTest(unittest.TestCase): + """An unknown command or plugin command and a bad global option + print the error and where the commands are listed, not the whole + general help.""" + + def assert_hint(self, result, message, hint): + self.assertEqual(result.returncode, 1) + self.assertIn(message, result.stderr) + self.assertIn(hint, result.stderr) + self.assertNotIn('usage:', result.stdout + result.stderr) + + def test_unknown_command(self): + self.assert_hint(run('bogus-cmd'), "'bogus-cmd'", + "See 'nvme --help'") + + def test_unknown_plugin_command(self): + self.assert_hint(run('id', 'bogus'), "'bogus'", + "See 'nvme id --help'") + + def test_bad_global_option(self): + self.assert_hint(run('--bogus'), "'--bogus'", "See 'nvme --help'") + + class OperandTest(unittest.TestCase): """A command takes a device, an optional one or none. A missing device and extra operands are usage errors.""" From 8f109ef85ff43bd7c73715e79c168c1e01a7ed49 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:17:35 +0000 Subject: [PATCH 118/263] completions: update the shell completions Drop --force of discover and connect-all, which is the old name of --no-reuse now. Signed-off-by: Daniel Wagner --- completions/_nvme | 2 -- completions/bash-nvme-completion.sh | 4 ++-- completions/nvme-completion.ps1 | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 817663774b..0be20ac00b 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -13030,7 +13030,6 @@ _nvme () { -D':alias for --duplicate-connect' --fast-io-fail-tmo=':fast I/O fail timeout (default off)' -F':alias for --fast-io-fail-tmo' - --force':deprecated, see --no-reuse' --hdr-digest':enable transport protocol header digest (TCP transport)' -g':alias for --hdr-digest' --host-iface=':host interface (for tcp transport)' @@ -13572,7 +13571,6 @@ _nvme () { -D':alias for --duplicate-connect' --fast-io-fail-tmo=':fast I/O fail timeout (default off)' -F':alias for --fast-io-fail-tmo' - --force':deprecated, see --no-reuse' --hdr-digest':enable transport protocol header digest (TCP transport)' -g':alias for --hdr-digest' --host-iface=':host interface (for tcp transport)' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index 728815639a..af61fd2a03 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -287,7 +287,7 @@ nvme_list_opts () { ;; "connect-all") - opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --force --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" + opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ @@ -398,7 +398,7 @@ nvme_list_opts () { ;; "discover") - opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --force --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" + opts+=" --concat --config= -J --ctrl-loss-tmo= -l --data-digest -G --device= -d --disable-sqflow --dry-run --duplicate-connect -D --fast-io-fail-tmo= -F --hdr-digest -g --host-iface= -f --host-traddr= -w --hostid= -I --hostnqn= -q --keep-alive-tmo= -k --keyring= --kxchap-ctrl-secret= -C --kxchap-secret= -S --nbft --nbft-path= --no-ioctl-probing --no-nbft --no-retries --no-reuse --nqn= -n --nr-io-queues= -i --nr-poll-queues= -P --nr-write-queues= -W --output-format= -o --output-format-version= --owner= --persistent= -p --queue-size= -Q --quiet --raw= -r --reconnect-delay= -c --set-options= --timeout= --tls --tls-key= --tls-key-identity= --tls_key= --tos= -T --traddr= -a --transport= -t --trsvcid= -s --verbose -v" valopts+=" --config -J --ctrl-loss-tmo -l --device -d --fast-io-fail-tmo -F --host-iface -f --host-traddr -w --hostid -I --hostnqn -q --keep-alive-tmo -k --keyring --kxchap-ctrl-secret -C --kxchap-secret -S --nbft-path --nqn -n --nr-io-queues -i --nr-poll-queues -P --nr-write-queues -W --output-format -o --output-format-version --owner --persistent -p --queue-size -Q --raw -r --reconnect-delay -c --set-options --timeout --tls-key --tls-key-identity --tls_key --tos -T --traddr -a --transport -t --trsvcid -s" _nvme_opt_vals "--output-format -o" "normal json binary" \ diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index d8857e3647..f6cc35ef32 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -255,7 +255,7 @@ $script:NvmeOptions = @{ 'config status' = @('--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'config validate' = @('--config', '-J', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'connect' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--devid-file', '--disable-sqflow', '--discovery', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--idempotent', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--no-ioctl-probing', '--no-retries', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--queue-size', '-Q', '--quiet', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') - 'connect-all' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--force', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--nbft', '--nbft-path', '--no-ioctl-probing', '--no-nbft', '--no-retries', '--no-reuse', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--persistent', '-p', '--queue-size', '-Q', '--quiet', '--raw', '-r', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') + 'connect-all' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--nbft', '--nbft-path', '--no-ioctl-probing', '--no-nbft', '--no-retries', '--no-reuse', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--persistent', '-p', '--queue-size', '-Q', '--quiet', '--raw', '-r', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'copy' = @('--app-tag', '-a', '--app-tag-mask', '-m', '--blocks', '-b', '--dir-spec', '-S', '--dir-type', '-T', '--dry-run', '--expected-app-tag-masks', '-M', '--expected-app-tags', '-A', '--expected-ref-tags', '-R', '--force-unit-access', '-f', '--format', '-F', '--limited-retry', '-l', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--prinfor', '-P', '--prinfow', '-p', '--quiet', '--ref-tag', '-r', '--sdlba', '-d', '--set-options', '--slbs', '-s', '--snsids', '-N', '--sopts', '-O', '--storage-tag', '-t', '--storage-tag-check', '-c', '--timeout', '--verbose', '-v', '--help', '-h') 'create-ns' = @('--anagrp-id', '-a', '--azr', '-z', '--block-size', '-b', '--csi', '-y', '--dps', '-d', '--dry-run', '--endg-id', '-e', '--flbas', '-f', '--ish', '-I', '--lbstm', '-l', '--ncap', '-c', '--ncap-si', '-C', '--nmic', '-m', '--no-ioctl-probing', '--no-retries', '--nphndls', '-n', '--nsze', '-s', '--nsze-si', '-S', '--nvmset-id', '-i', '--output-format', '-o', '--output-format-version', '--phndls', '-p', '--quiet', '--rar', '-r', '--rnumzrwa', '-u', '--ror', '-O', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'dapustor smart-log-add' = @('--dry-run', '--json', '-j', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') @@ -272,7 +272,7 @@ $script:NvmeOptions = @{ 'dir-send' = @('--data-len', '-l', '--dir-oper', '-O', '--dir-spec', '-S', '--dir-type', '-D', '--dry-run', '--endir', '-e', '--input-file', '-i', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--target-dir', '-T', '--timeout', '--verbose', '-v', '--help', '-h') 'disconnect' = @('--concat', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--exclude', '-x', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--no-ioctl-probing', '--no-retries', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--queue-size', '-Q', '--quiet', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'disconnect-all' = @('--dry-run', '--force', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--owner', '--quiet', '--set-options', '--timeout', '--transport', '-t', '--verbose', '-v', '--help', '-h') - 'discover' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--force', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--nbft', '--nbft-path', '--no-ioctl-probing', '--no-nbft', '--no-retries', '--no-reuse', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--persistent', '-p', '--queue-size', '-Q', '--quiet', '--raw', '-r', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') + 'discover' = @('--concat', '--config', '-J', '--ctrl-loss-tmo', '-l', '--data-digest', '-G', '--device', '-d', '--disable-sqflow', '--dry-run', '--duplicate-connect', '-D', '--fast-io-fail-tmo', '-F', '--hdr-digest', '-g', '--host-iface', '-f', '--host-traddr', '-w', '--hostid', '-I', '--hostnqn', '-q', '--keep-alive-tmo', '-k', '--keyring', '--kxchap-ctrl-secret', '-C', '--kxchap-secret', '-S', '--nbft', '--nbft-path', '--no-ioctl-probing', '--no-nbft', '--no-retries', '--no-reuse', '--nqn', '-n', '--nr-io-queues', '-i', '--nr-poll-queues', '-P', '--nr-write-queues', '-W', '--output-format', '-o', '--output-format-version', '--owner', '--persistent', '-p', '--queue-size', '-Q', '--quiet', '--raw', '-r', '--reconnect-delay', '-c', '--set-options', '--timeout', '--tls', '--tls-key', '--tls-key-identity', '--tls_key', '--tos', '-T', '--traddr', '-a', '--transport', '-t', '--trsvcid', '-s', '--verbose', '-v', '--help', '-h') 'dispersed-ns-participating-nss-log' = @('--dry-run', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') 'dsm' = @('--ad', '-d', '--blocks', '-b', '--cdw11', '-c', '--ctx-attrs', '-a', '--dry-run', '--idr', '-r', '--idw', '-w', '--namespace-id', '-n', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--set-options', '--slbs', '-s', '--timeout', '--verbose', '-v', '--help', '-h') 'effects-log' = @('--csi', '-c', '--dry-run', '--no-ioctl-probing', '--no-retries', '--output-format', '-o', '--output-format-version', '--quiet', '--raw-binary', '-b', '--set-options', '--timeout', '--verbose', '-v', '--help', '-h') From fc8870d0295944b6629bd0a1a5521f4fbb25f52f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 12:17:35 +0000 Subject: [PATCH 119/263] NEWS: document the usage error and old option name changes Usage errors print the synopsis instead of the help, errors start with the command name and are colored on a terminal unless NO_COLOR is set, the old option names warn, and a separate value of --persistent fails, which has its own entry now. Signed-off-by: Daniel Wagner --- NEWS.md | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3daf26aa13..f22a9777b0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,8 +24,14 @@ * Every command checks its operand: a command which takes a device fails without one, and an extra operand is an error. Before, extra operands were silently ignored, e.g. a device given to a command which - takes none, or the value of `--persistent force`, which has to be - given as `--persistent=force`. + takes none. + +* `--persistent force` fails now. The value of an option which takes an + optional value has to be attached to it: `--persistent=force` or + `-pforce`. A separate value was never applied: `--persistent force` + connected in mode `auto` and dropped `force` without a word, as the + man pages of `discover`, `connect-all` and `config create` said. + Now it is an extra argument, which is an error. * Options which take an unsigned number reject negative numbers now. Before, `-1` wrapped around to the largest value. The values which @@ -56,6 +62,27 @@ - `--human-readable` and `-H`, the old spelling of `--verbose`, print a deprecation warning. +* A usage error prints the error, the synopsis and where to find the + help instead of the whole help, which buried the error. A bad value + names the option as it was given and the values it takes: + + fw commit: invalid value 'x' for -s: expected a number from 0 to 255 + Usage: nvme fw commit [OPTIONS] + Try 'nvme fw commit --help' for more information. + + The other errors of a command start with its name as well, e.g. + `id ns: /dev/nvme0: Permission denied`, and a device which can't be + opened prints no help any more. An unknown command and a bad global + option point to `nvme --help` instead of printing the general help, + which also followed a command that failed with ENOTTY. On a terminal, + that name is red and the WARNING of a warning yellow. Set `NO_COLOR` + to a non-empty value to turn colors off (https://no-color.org/). + +* The old option names `--dhchap-secret`, `--dhchap-ctrl-secret` and + `--fast_io_fail_tmo` of the fabrics commands and `--force` of + `discover` and `connect-all` print a deprecation warning which names + the option to use instead. + * The deprecated `tls-key` command parses its options like every other command: it has a help, and abbreviated options such as `--keyr` are no longer accepted. It, `gen-dhchap-key` and `check-dhchap-key` From b756540c6e17f3c99c406022a2e0c7a9f5ce9693 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:20:01 +0000 Subject: [PATCH 120/263] utils: report deprecated commands and options in the command metadata The command metadata marked a deprecated option only as hidden and said nothing about deprecated commands, so a consumer could not point to what replaces them. Report a deprecated command and the command which replaces it, e.g. "replacement": "log smart" for smart-log, the option which replaces a deprecated option, and the old name of a renamed option. The fields are optional, so the schema version stays. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata-schema.json | 17 ++++++++++++++++ plugins/utils/command-metadata.c | 20 +++++++++++++++++++ plugins/utils/command-metadata.h | 6 ++++++ tests/unit/py/test_command_metadata_schema.py | 16 +++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/plugins/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index 5ff1b0fc27..a7a619519d 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -73,6 +73,15 @@ "type": "string", "description": "Alternate name accepted for the command. Omitted if none." }, + "deprecated": { + "type": "boolean", + "description": "True for a deprecated command. Absent otherwise.", + "const": true + }, + "replacement": { + "type": "string", + "description": "The command which replaces a deprecated one, as \" \". Omitted if none." + }, "description": { "type": "string", "description": "Command description (the command-list one-liner). Omitted if unset." @@ -122,6 +131,14 @@ "description": "True for options accepted on the command line but suppressed from --help. Absent otherwise.", "const": true }, + "deprecated": { + "type": "string", + "description": "The option which replaces a deprecated one, e.g. \"--verbose/-v\". A deprecated option is hidden as well. Omitted if not deprecated." + }, + "old_name": { + "type": "string", + "description": "The long name of earlier versions, still accepted with a warning. Omitted if none." + }, "values": { "type": "array", "description": "The names the option accepts as values, e.g. the output formats or named special values such as \"all\" for a namespace identifier. A numeric option also takes a number. Absent when the option has no named values.", diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index eceeaf1cae..e101d07d41 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -267,6 +267,8 @@ static void free_option_fields(struct command_metadata_option *o) free((char *)o->option); free((char *)o->meta); free((char *)o->help); + free((char *)o->deprecated); + free((char *)o->old_name); if (o->opt_val) { const struct argconfig_opt_val *v; @@ -310,6 +312,8 @@ static int copy_options(const struct argconfig_commandline_options *opts, if (dup_field(opts[i].option, &dst[i].option) || dup_field(opts[i].meta, &dst[i].meta) || dup_field(opts[i].help, &dst[i].help) || + dup_field(opts[i].deprecated, &dst[i].deprecated) || + dup_field(opts[i].old_name, &dst[i].old_name) || copy_opt_val(opts[i].opt_val, &dst[i].opt_val)) { size_t j; @@ -406,6 +410,9 @@ static int capture_command(struct command_metadata_command *mc, struct command * mc->alias = cmd->alias; mc->help = cmd->help; mc->device = cmd->device; + mc->deprecated = cmd->deprecated; + mc->replacement_plugin = cmd->replacement.plugin; + mc->replacement_command = cmd->replacement.command; mc->captured = false; /* @@ -663,6 +670,10 @@ static void json_option(struct json_object *arr, const struct command_metadata_o json_object_add_value_bool(jo, "global", true); if (o->hidden) json_object_add_value_bool(jo, "hidden", true); + if (o->deprecated) + json_object_add_value_string(jo, "deprecated", o->deprecated); + if (o->old_name) + json_object_add_value_string(jo, "old_name", o->old_name); vals = json_option_values(o); if (vals) @@ -692,6 +703,15 @@ static struct json_object *json_command(const struct command_metadata_command *c if (c->help) json_object_add_value_string(jc, "description", c->help); json_object_add_value_string(jc, "device", device_names[c->device]); + if (c->deprecated) + json_object_add_value_bool(jc, "deprecated", true); + if (c->replacement_plugin && c->replacement_command) { + char replacement[0x80]; + + snprintf(replacement, sizeof(replacement), "%s %s", + c->replacement_plugin, c->replacement_command); + json_object_add_value_string(jc, "replacement", replacement); + } opts = json_create_array(); for (i = 0; i < c->num_options; i++) { diff --git a/plugins/utils/command-metadata.h b/plugins/utils/command-metadata.h index 6f4e80ed30..3c259d2743 100644 --- a/plugins/utils/command-metadata.h +++ b/plugins/utils/command-metadata.h @@ -28,6 +28,8 @@ struct command_metadata_option { const char *help; const struct argconfig_opt_val *opt_val; /* NULL or .str==NULL terminated */ bool hidden; /* not shown in help/completion */ + const char *deprecated; /* the replacement, or NULL */ + const char *old_name; /* the long name it replaced, or NULL */ }; struct command_metadata_command { @@ -38,6 +40,10 @@ struct command_metadata_command { size_t num_options; bool captured; /* hook fired -> options valid */ int device; /* enum command_device */ + bool deprecated; + /* "nvme " which replaces it, or NULL */ + const char *replacement_plugin; + const char *replacement_command; }; struct command_metadata_plugin { diff --git a/tests/unit/py/test_command_metadata_schema.py b/tests/unit/py/test_command_metadata_schema.py index bd02aa59d7..4d7a4ee7d3 100644 --- a/tests/unit/py/test_command_metadata_schema.py +++ b/tests/unit/py/test_command_metadata_schema.py @@ -158,6 +158,22 @@ def test_global_output_format_option(self): self.assertEqual(opt["argument"], "required") self.assertIn("json", opt["values"]) + def test_deprecated(self): + """Deprecated commands name their replacement, deprecated options + theirs, and renamed options their old name.""" + cmds = {c["name"]: c for c in self.data["commands"]} + self.assertTrue(cmds["smart-log"].get("deprecated")) + self.assertEqual(cmds["smart-log"].get("replacement"), "log smart") + self.assertNotIn("deprecated", cmds["list"]) + opt = next(o for o in cmds["list"]["options"] + if o["long"] == "human-readable") + self.assertEqual(opt.get("deprecated"), "--verbose/-v") + self.assertTrue(opt.get("hidden")) + if "discover" in cmds: + opt = next(o for o in cmds["discover"]["options"] + if o["long"] == "no-reuse") + self.assertEqual(opt.get("old_name"), "force") + if __name__ == "__main__": # argv[1:3] are consumed above; hand unittest only its own args. From 6b4df5ec8989f9a5caa25497dc77ec8e227b2361 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:22:15 +0000 Subject: [PATCH 121/263] argconfig: add OPT_ENUM for an option which takes one of some names Commands with an option which takes one of a few words, such as --type=host|controller, take it as a string and compare it themselves, each with its own rules for case and errors, and the help, metadata and completions don't know the words. Add OPT_ENUM: the option takes one of the names of its value table, in any case and as a unique prefix like every value name, and stores the value of the name in an int. A number is no name, the error lists the names. Signed-off-by: Daniel Wagner --- src/argconfig.c | 25 +++++++++++++-- src/argconfig.h | 11 +++++++ tests/unit/c/argconfig-getopt-ref.c | 4 +++ tests/unit/c/test-argconfig-parse.c | 50 +++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 3273128f86..e9d3fe67f4 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -343,6 +343,10 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_INCREMENT: *(int *)value += 1; break; + case CFG_ENUM: + /* only the names, which argconfig_parse_val() looked up */ + ret = -EINVAL; + break; case CFG_LONG: *(uint64_t *)value = strtoull(arg, &endptr, 0); if (errno || arg == endptr || *endptr) @@ -379,6 +383,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *(uint32_t *)val = opt_val->positive; break; case CFG_INT: + case CFG_ENUM: *(int *)val = opt_val->int_val; break; case CFG_LONG: @@ -610,15 +615,18 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, fprintf(stderr, "-%c", c); else fprintf(stderr, "--%s", s->option); - fprintf(stderr, ": expected %s", argconfig_expected(s)); + fprintf(stderr, ": expected "); + if (s->config_type != CFG_ENUM) + fprintf(stderr, "%s%s", argconfig_expected(s), + s->opt_val ? " or " : ""); /* A long list of names would drown the error, the help has them */ for (v = s->opt_val; v && v->str; v++) n++; if (n > 8) - fprintf(stderr, " or one of the names in the help"); + fprintf(stderr, "one of the names in the help"); for (v = s->opt_val; n <= 8 && v && v->str; v++) - fprintf(stderr, "%s%s", v == s->opt_val ? " or one of: " : ", ", + fprintf(stderr, "%s%s", v == s->opt_val ? "one of: " : ", ", v->str); fprintf(stderr, "\n"); @@ -652,6 +660,17 @@ static int argconfig_apply(struct argconfig_commandline_options *s, char c, return ret; } +/* The name of the enum value @val in the value table @v, or NULL */ +const char *argconfig_value_name(const struct argconfig_opt_val *v, int val) +{ + for (; v && v->str; v++) { + if (v->type == CFG_ENUM && v->val.int_val == val) + return v->str; + } + + return NULL; +} + /* The value of a required argument which is not attached to the option */ static char *argconfig_next_arg(struct argconfig_parser *p) { diff --git a/src/argconfig.h b/src/argconfig.h index 642d515ff3..cd695406fa 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -49,6 +49,7 @@ enum argconfig_types { CFG_SHORT, CFG_POSITIVE, CFG_INCREMENT, + CFG_ENUM, CFG_GROUP_SEPARATOR, }; @@ -100,6 +101,11 @@ enum argconfig_types { {l, s, "NUM", CFG_INT, OPT_CHECK(v, int *), \ ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} +/* One of the names of the value table @n, VAL_ENUM() entries */ +#define OPT_ENUM(l, s, v, d, n, ...) \ + {l, s, "NAME", CFG_ENUM, OPT_CHECK(v, int *), \ + ARGCONFIG_ARG_REQUIRED, d, false, n, __VA_ARGS__} + #define OPT_LONG(l, s, v, d, ...) \ {l, s, "NUM", CFG_LONG, OPT_CHECK_64(v), \ ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} @@ -168,6 +174,9 @@ enum argconfig_types { #define VAL_INT(s, v) \ {s, CFG_INT, .val.int_val = v} +#define VAL_ENUM(s, v) \ + {s, CFG_ENUM, .val.int_val = v} + #define VAL_LONG(s, v) \ {s, CFG_LONG, .val.long_val = v} @@ -272,3 +281,5 @@ bool argconfig_given(struct argconfig_commandline_options *options, const void *value); bool argconfig_set_value_name(struct argconfig_commandline_options *option, const char *str); +const char *argconfig_value_name(const struct argconfig_opt_val *values, + int val); diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 43678efd42..125b8e9bd0 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -120,6 +120,9 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, case CFG_INCREMENT: *(int *)value += 1; break; + case CFG_ENUM: + ret = argconfig_error("name", s->option, arg); + break; case CFG_LONG: *(uint64_t *)value = strtoull(arg, &endptr, 0); if (errno || arg == endptr || *endptr) @@ -160,6 +163,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, *(uint32_t *)val = opt_val->positive; break; case CFG_INT: + case CFG_ENUM: *(int *)val = opt_val->int_val; break; case CFG_LONG: diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 719b0b3bc1..71b06aa87c 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -463,6 +463,55 @@ static void global_options_test(void) argconfig_set_global_options(NULL); } +/* An enum takes only its names, in any case and as a unique prefix */ +static void enum_test(void) +{ + const char *desc = "Test enum"; + struct argconfig_opt_val names[] = { + VAL_ENUM("host", 1), + VAL_ENUM("controller", 2), + VAL_ENUM("ctrl-init", 3), + VAL_END() + }; + static const struct { + const char *arg; + int ret; + int val; + } tests[] = { + { "--type=host", 0, 1 }, + { "--type=HOST", 0, 1 }, + { "--type=controller", 0, 2 }, + { "--type=ctrl-", 0, 3 }, + { "--type=c", -EINVAL, 0 }, + { "--type=1", -EINVAL, 0 }, + { "--type=bogus", -EINVAL, 0 }, + }; + unsigned int i; + int type, ret; + + OPT_ARGS(opts) = { + OPT_ENUM("type", 't', &type, "type", names), + OPT_END() + }; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + char *argv[3] = { "prog", (char *)tests[i].arg, NULL }; + + type = 0; + ret = argconfig_parse(2, argv, desc, opts); + if (ret != tests[i].ret || type != tests[i].val) { + printf("ERROR: enum: {%s} gave %d/%d\n", tests[i].arg, + ret, type); + test_rc = 1; + } + } + if (strcmp(argconfig_value_name(names, 2), "controller") || + argconfig_value_name(names, 4)) { + printf("ERROR: enum: value names\n"); + test_rc = 1; + } +} + /* An option also takes its old name, the option itself is given then */ static void old_name_test(void) { @@ -957,6 +1006,7 @@ int main(void) global_options_test(); given_test(); old_name_test(); + enum_test(); special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) From 1049af019830b4e322cf9bf90b2bf0271371dd1b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:22:53 +0000 Subject: [PATCH 122/263] nvme: take the ranking of show-topology as an enum show-topology compared --ranking with its three words itself. Take it with OPT_ENUM, so the help and the completions list the words, and an unknown word is a usage error which lists them. Signed-off-by: Daniel Wagner --- src/nvme-cmds-enumerate.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 3e45856d26..21e5df43b6 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -95,24 +95,29 @@ static int list_subsys(int argc, char **argv, struct command *acmd, static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Show the topology\n"; - const char *ranking = "Ranking order: namespace|ctrl|multipath"; + const char *ranking = "Ranking order"; + struct argconfig_opt_val rankings[] = { + VAL_ENUM("namespace", NVME_CLI_TOPO_NAMESPACE), + VAL_ENUM("ctrl", NVME_CLI_TOPO_CTRL), + VAL_ENUM("multipath", NVME_CLI_TOPO_MULTIPATH), + VAL_END() + }; nvme_print_flags_t flags; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; char *devname = NULL; libnvme_scan_filter_t filter = NULL; - enum nvme_cli_topo_ranking rank; int err; struct config { - char *ranking; + int ranking; }; struct config cfg = { - .ranking = "namespace", + .ranking = NVME_CLI_TOPO_NAMESPACE, }; NVME_ARGS(opts, - OPT_FMT("ranking", 'r', &cfg.ranking, ranking)); + OPT_ENUM("ranking", 'r', &cfg.ranking, ranking, rankings)); err = parse_args(argc, argv, desc, opts); if (err) @@ -123,17 +128,6 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct if (nvme_args.verbose) flags |= VERBOSE; - if (!strcmp(cfg.ranking, "namespace")) { - rank = NVME_CLI_TOPO_NAMESPACE; - } else if (!strcmp(cfg.ranking, "ctrl")) { - rank = NVME_CLI_TOPO_CTRL; - } else if (!strcmp(cfg.ranking, "multipath")) { - rank = NVME_CLI_TOPO_MULTIPATH; - } else { - nvme_show_error("Invalid ranking argument: %s", cfg.ranking); - return -EINVAL; - } - err = nvme_create_global_ctx(&ctx); if (err) return err; @@ -159,7 +153,7 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct if (flags & TABULAR) nvme_show_topology_tabular(ctx, flags); else - nvme_show_topology(ctx, rank, flags); + nvme_show_topology(ctx, cfg.ranking, flags); return err; } From edc53a8d7b10e7e89f0559a2c92745db4a4dfff9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:23:53 +0000 Subject: [PATCH 123/263] plugins/memblaze: take the firmware select of select-download as an enum select-download took --select as a string, upper-cased it in the caller's argv and compared it with its three words. Take it with OPT_ENUM, which compares in any case, lists the words in the help and the completions, and rejects an unknown word as a usage error. Signed-off-by: Daniel Wagner --- plugins/memblaze/memblaze-nvme.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index b1023cd635..956221d5ff 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -761,46 +761,38 @@ static int mb_selective_download(int argc, char **argv, struct command *acmd, st struct stat sb; __cleanup_libnvme_free void *fw_buf = NULL; unsigned char *fw_ptr; - int i; + + struct argconfig_opt_val selects[] = { + VAL_ENUM("OOB", 18), + VAL_ENUM("EEP", 10), + VAL_ENUM("ALL", 26), + VAL_END() + }; struct config { char *fw; - char *select; + int select; }; struct config cfg = { .fw = "", - .select = "\0", + .select = 0, }; NVME_ARGS(opts, OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select)); + OPT_ENUM("select", 's', &cfg.select, select, selects)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (strlen(cfg.select) != 3) { - nvme_show_error("Invalid select flag"); - err = EINVAL; - goto out; - } - - for (i = 0; i < 3; i++) - cfg.select[i] = toupper(cfg.select[i]); - - if (!strncmp(cfg.select, "OOB", 3)) { - selectNo = 18; - } else if (!strncmp(cfg.select, "EEP", 3)) { - selectNo = 10; - } else if (!strncmp(cfg.select, "ALL", 3)) { - selectNo = 26; - } else { + if (!argconfig_given(opts, &cfg.select)) { nvme_show_error("Invalid select flag"); err = EINVAL; goto out; } + selectNo = cfg.select; fw_fd = open(cfg.fw, O_RDONLY); if (fw_fd < 0) { From 8959ee7e27da6df9aa86b4addb88442c9ccb7b90 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:28:14 +0000 Subject: [PATCH 124/263] plugins/micron: take the fixed option values as enums select-download --select, the --option of vs-smbus-option, latency-tracking and vs-telemetry-controller-option, the --command of latency-tracking and latency-stats and the --type of vs-internal-log take one of a few words, which the commands compared themselves, each with its own error. Take them with OPT_ENUM, so the help and the completions list the words, any case and a unique prefix select one, and an unknown word is a usage error which lists them. Signed-off-by: Daniel Wagner --- plugins/micron/micron-nvme.c | 177 ++++++++++-------- tests/cli/micron/micron_latency_mock_test.py | 10 +- tests/cli/micron/micron_options_mock_test.py | 26 ++- .../micron_vs_internal_log_mock_test.py | 2 +- 4 files changed, 122 insertions(+), 93 deletions(-) diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 76d3e9e3d5..23b5a245ee 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -75,6 +75,36 @@ static const char *__version_major = "3"; static const char *__version_minor = "0"; static const char *__version_patch = "0"; +/* --option of the commands which enable, disable or show a feature */ +enum micron_option { + MICRON_OPTION_DISABLE, + MICRON_OPTION_ENABLE, + MICRON_OPTION_STATUS, +}; + +static struct argconfig_opt_val micron_options[] = { + VAL_ENUM("enable", MICRON_OPTION_ENABLE), + VAL_ENUM("disable", MICRON_OPTION_DISABLE), + VAL_ENUM("status", MICRON_OPTION_STATUS), + VAL_END() +}; + +/* --command of the latency commands */ +enum micron_command { + MICRON_COMMAND_ALL, + MICRON_COMMAND_READ, + MICRON_COMMAND_WRITE, + MICRON_COMMAND_TRIM, +}; + +static struct argconfig_opt_val micron_commands[] = { + VAL_ENUM("all", MICRON_COMMAND_ALL), + VAL_ENUM("read", MICRON_COMMAND_READ), + VAL_ENUM("write", MICRON_COMMAND_WRITE), + VAL_ENUM("trim", MICRON_COMMAND_TRIM), + VAL_END() +}; + /* * supported models of micron plugin; new models should be added at the end * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! @@ -590,42 +620,36 @@ static int micron_selective_download(int argc, char **argv, __cleanup_libnvme_free void *fw_buf = NULL; unsigned char *fw_ptr; + struct argconfig_opt_val selects[] = { + VAL_ENUM("OOB", 18), + VAL_ENUM("EEP", 10), + VAL_ENUM("ALL", 26), + VAL_END() + }; + struct config { char *fw; - char *select; + int select; }; struct config cfg = { .fw = "", - .select = "\0", + .select = 0, }; NVME_ARGS(opts, OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select)); + OPT_ENUM("select", 's', &cfg.select, select, selects)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) return err; - if (strlen(cfg.select) != 3) { - nvme_show_error("Invalid select flag"); - return -EINVAL; - } - - for (int i = 0; i < 3; i++) - cfg.select[i] = toupper(cfg.select[i]); - - if (!strncmp(cfg.select, "OOB", 3)) { - selectNo = 18; - } else if (!strncmp(cfg.select, "EEP", 3)) { - selectNo = 10; - } else if (!strncmp(cfg.select, "ALL", 3)) { - selectNo = 26; - } else { + if (!argconfig_given(opts, &cfg.select)) { nvme_show_error("Invalid select flag"); return -EINVAL; } + selectNo = cfg.select; fw_fd = open(cfg.fw, O_RDONLY); if (fw_fd < 0) { @@ -709,19 +733,19 @@ static int micron_smbus_option(int argc, char **argv, int err = 0; struct { - char *option; + int option; __u32 value; __u32 save; int status; } opt = { - .option = "disable", + .option = MICRON_OPTION_DISABLE, .value = 0, .save = 0, .status = 0, }; NVME_ARGS(opts, - OPT_STRING("option", 'O', "option", &opt.option, option), + OPT_ENUM("option", 'O', &opt.option, option, micron_options), OPT_UINT("value", 'V', &opt.value, value), OPT_UINT("save", 's', &opt.save, save)); @@ -734,7 +758,7 @@ static int micron_smbus_option(int argc, char **argv, return -ENOTSUP; } - if (!strcmp(opt.option, "enable")) { + if (opt.option == MICRON_OPTION_ENABLE) { cdw11 = opt.value << 1 | 1; err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11, &result); @@ -742,7 +766,7 @@ static int micron_smbus_option(int argc, char **argv, nvme_show_verbose_result("successfully enabled SMBus on drive"); else nvme_show_error("Failed to enabled SMBus on drive"); - } else if (!strcmp(opt.option, "status")) { + } else if (opt.option == MICRON_OPTION_STATUS) { err = nvme_get_features(hdl, 1, fid, opt.value, 0, 0, NULL, 0, &result); if (!err) printf("SMBus status on the drive: %s (returns %s temperature)\n", @@ -750,7 +774,7 @@ static int micron_smbus_option(int argc, char **argv, (result & 2) ? "hottest component" : "composite"); else nvme_show_error("Failed to retrieve SMBus status on the drive"); - } else if (!strcmp(opt.option, "disable")) { + } else { cdw11 = opt.value << 1 | 0; err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11, &result); @@ -758,10 +782,6 @@ static int micron_smbus_option(int argc, char **argv, nvme_show_verbose_result("Successfully disabled SMBus on drive"); else nvme_show_error("Failed to disable SMBus on drive"); - } else { - nvme_show_error("Invalid option %s, valid values are enable, disable or status", - opt.option); - return -1; } return err; @@ -3028,18 +3048,18 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *acm __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct { - char *option; - char *command; + int option; + int command; uint32_t threshold; } opt = { - .option = "status", - .command = "all", + .option = MICRON_OPTION_STATUS, + .command = MICRON_COMMAND_ALL, .threshold = 0 }; NVME_ARGS(opts, - OPT_STRING("option", 'O', "option", &opt.option, option), - OPT_STRING("command", 'c', "command", &opt.command, cmdstr), + OPT_ENUM("option", 'O', &opt.option, option, micron_options), + OPT_ENUM("command", 'c', &opt.command, cmdstr, micron_commands), OPT_UINT("threshold", 't', &opt.threshold, thrtime)); @@ -3047,14 +3067,10 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *acm if (err) return err; - if (!strcmp(opt.option, "enable")) { + if (opt.option == MICRON_OPTION_ENABLE) enable = 1; - } else if (!strcmp(opt.option, "disable")) { + else if (opt.option == MICRON_OPTION_DISABLE) enable = 0; - } else if (strcmp(opt.option, "status")) { - nvme_show_error("Invalid control option %s specified", opt.option); - return -1; - } err = nvme_get_features(hdl, 0, fid, 0, 0, 0, NULL, 0, &result); if (err) { @@ -3097,29 +3113,33 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *acm } /* read-in command(s) to be monitored */ - if (!strcmp(opt.command, "read")) { + if (opt.command == MICRON_COMMAND_READ) { command_mask = 0x1; timing_mask = (opt.threshold << 24); - } else if (!strcmp(opt.command, "write")) { + } else if (opt.command == MICRON_COMMAND_WRITE) { command_mask = 0x2; timing_mask = (opt.threshold << 16); - } else if (!strcmp(opt.command, "trim")) { + } else if (opt.command == MICRON_COMMAND_TRIM) { command_mask = 0x4; timing_mask = (opt.threshold << 8); - } else if (strcmp(opt.command, "all")) { - nvme_show_error("Invalid command %s specified for option %s", - opt.command, opt.option); - return -1; } err = nvme_set_features(hdl, 0, MICRON_FID_LATENCY_MONITOR, 1, enable, command_mask, timing_mask, 0, 0, NULL, 0, &result); if (!err) { nvme_show_verbose_result("Successfully %sd latency monitoring for %s commands with %dms threshold", - opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10); + argconfig_value_name(micron_options, + opt.option), + argconfig_value_name(micron_commands, + opt.command), + !opt.threshold ? 800 : opt.threshold * 10); } else { nvme_show_error("Failed to %s latency monitoring for %s commands with %dms threshold", - opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10); + argconfig_value_name(micron_options, + opt.option), + argconfig_value_name(micron_commands, + opt.command), + !opt.threshold ? 800 : opt.threshold * 10); } return err; @@ -3228,32 +3248,30 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *acmd }; struct { - char *command; + int command; } opt = { - .command = "all" + .command = MICRON_COMMAND_ALL }; uint64_t *cmd_stats = &log.all_cmds[0]; char *cmd_str = "All"; NVME_ARGS(opts, - OPT_STRING("command", 'c', "command", &opt.command, cmdstr)); + OPT_ENUM("command", 'c', &opt.command, cmdstr, + micron_commands)); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) return err; - if (!strcmp(opt.command, "read")) { + if (opt.command == MICRON_COMMAND_READ) { cmd_stats = &log.read_cmds[0]; - cmd_str = "Read"; - } else if (!strcmp(opt.command, "write")) { + cmd_str = "Read"; + } else if (opt.command == MICRON_COMMAND_WRITE) { cmd_stats = &log.write_cmds[0]; - cmd_str = "Write"; - } else if (!strcmp(opt.command, "trim")) { + cmd_str = "Write"; + } else if (opt.command == MICRON_COMMAND_TRIM) { cmd_stats = &log.trim_cmds[0]; - cmd_str = "Trim"; - } else if (strcmp(opt.command, "all")) { - nvme_show_error("Invalid command option %s to display latency stats", opt.command); - return -1; + cmd_str = "Trim"; } memset(&log, 0, sizeof(log)); @@ -3398,15 +3416,15 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct { - char *option; + int option; __u32 select; } opt = { - .option = "disable", + .option = MICRON_OPTION_DISABLE, .select = 0, }; NVME_ARGS(opts, - OPT_STRING("option", 'O', "option", &opt.option, option), + OPT_ENUM("option", 'O', &opt.option, option, micron_options), OPT_UINT("select", 's', &opt.select, select)); err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); @@ -3420,21 +3438,21 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, return err; } - if (!strcmp(opt.option, "enable")) { + if (opt.option == MICRON_OPTION_ENABLE) { err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 1, 0, 0, 0, 0, NULL, 0, &result); if (!err) nvme_show_verbose_result("successfully set controller telemetry option"); else nvme_show_error("Failed to set controller telemetry option"); - } else if (!strcmp(opt.option, "disable")) { + } else if (opt.option == MICRON_OPTION_DISABLE) { err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 0, 0, 0, 0, 0, NULL, 0, &result); if (!err) nvme_show_verbose_result("successfully disabled controller telemetry option"); else nvme_show_error("Failed to disable controller telemetry option"); - } else if (!strcmp(opt.option, "status")) { + } else { err = nvme_get_features(hdl, 1, fid, opt.select & 0x3, 0, 0, NULL, 0, &result); if (!err) @@ -3442,10 +3460,6 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, (result) ? "enabled" : "disabled"); else nvme_show_error("Failed to retrieve controller telemetry option"); - } else { - nvme_show_error("invalid option %s, valid values are enable,disable or status", - opt.option); - return -1; } return err; @@ -3689,22 +3703,28 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, int maxSize = 0; struct MICRON_WORKLOAD_LOG_HDR stWllHdr = { 0 }; + struct argconfig_opt_val types[] = { + VAL_ENUM("host", 0x07), + VAL_ENUM("controller", 0x08), + VAL_END() + }; + struct config { - char *type; + int type; char *package; __u32 data_area; int log; }; struct config cfg = { - .type = "", + .type = 0x07, .package = "", .data_area = 0, .log = 0x07, }; NVME_ARGS(opts, - OPT_STRING("type", 't', "log type", &cfg.type, type), + OPT_ENUM("type", 't', &cfg.type, type, types), OPT_STRING("package", 'p', "FILE", &cfg.package, package), OPT_UINT("data_area", 'd', &cfg.data_area, data_area)); @@ -3714,13 +3734,8 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, err = -EINVAL; /* if telemetry type is specified, check for data area */ - if (strlen(cfg.type)) { - if (!strcmp(cfg.type, "controller")) { - cfg.log = 0x08; - } else if (strcmp(cfg.type, "host")) { - nvme_show_error("telemetry type (host or controller) should be specified i.e. -t=host"); - goto out; - } + if (argconfig_given(opts, &cfg.type)) { + cfg.log = cfg.type; if (!cfg.data_area || cfg.data_area > 4) { nvme_show_error("data area must be selected using -d option ie --d=1,2,3,4"); diff --git a/tests/cli/micron/micron_latency_mock_test.py b/tests/cli/micron/micron_latency_mock_test.py index 1e97aed9de..0baca9ae20 100644 --- a/tests/cli/micron/micron_latency_mock_test.py +++ b/tests/cli/micron/micron_latency_mock_test.py @@ -360,7 +360,8 @@ def test_unrecognised_option_is_rejected(self): result = self.run_plugin_cmd(_TRACKING, args="-O bogus") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid control option bogus specified", result.stderr) + self.assertIn("invalid value 'bogus' for -O: expected one of: " + "enable, disable, status", result.stderr) def test_unrecognised_option_is_checked_before_the_feature_read(self): """-O is validated first, so a bad value reports only itself.""" @@ -377,7 +378,8 @@ def test_unrecognised_command_class_is_rejected(self): result = self.run_plugin_cmd(_TRACKING, args="-O enable -c bogus") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid command bogus specified", result.stderr) + self.assertIn("invalid value 'bogus' for -c: expected one of: all, " + "read, write, trim", result.stderr) def test_bad_device_returns_error(self): self.check_bad_device_name(_TRACKING) @@ -509,8 +511,8 @@ def test_unrecognised_command_class_is_rejected(self): result = self.run_plugin_cmd(_STATS, args="-c bogus") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid command option bogus to display latency stats", - result.stderr) + self.assertIn("invalid value 'bogus' for -c: expected one of: all, " + "read, write, trim", result.stderr) def test_command_class_is_checked_before_the_log_read(self): """A bad -c reports only itself, on a drive with or without the log.""" diff --git a/tests/cli/micron/micron_options_mock_test.py b/tests/cli/micron/micron_options_mock_test.py index a364c9d15b..db93a02d22 100644 --- a/tests/cli/micron/micron_options_mock_test.py +++ b/tests/cli/micron/micron_options_mock_test.py @@ -214,7 +214,8 @@ def test_unrecognised_option_is_rejected(self): result = self.run_plugin_cmd(_SMBUS, args="-O bogus") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid option bogus", result.stderr) + self.assertIn("invalid value 'bogus' for -O: expected one of: " + "enable, disable, status", result.stderr) self.assertEqual(self.writes(_FID_SMBUS), [], "an unrecognised option must not write the feature") @@ -323,7 +324,8 @@ def test_unrecognised_option_is_rejected(self): result = self.run_plugin_cmd(_TELEMETRY, args="-O bogus") self.assertNotEqual(result.returncode, 0) - self.assertIn("invalid option bogus", result.stderr) + self.assertIn("invalid value 'bogus' for -O: expected one of: " + "enable, disable, status", result.stderr) self.assertEqual(self.writes(_FID_TELEMETRY), []) def test_write_failure_is_reported(self): @@ -398,24 +400,34 @@ def test_unrecognised_selection_is_rejected(self): result = self.run_plugin_cmd(_DOWNLOAD, args=f"-f {path} -s XYZ") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid select flag", result.stderr) + self.assertIn("invalid value 'XYZ' for -s: expected one of: OOB, " + "EEP, ALL", result.stderr) self.assertEqual(self.downloads(), [], "an unrecognised selection must not send the image") - def test_selection_length_is_checked_first(self): - """A selection of the wrong length is refused before it is matched. + def test_selection_prefix(self): + """A selection may be abbreviated to a unique prefix, like every + value name.""" + path = self.firmware() + self.run_plugin_cmd_check(_DOWNLOAD, args=f"-f {path} --select=oo") + + self.assertEqual(self.commits()[0]['cdw12'], _SELECTIONS['OOB']) + + def test_selection_no_name(self): + """An empty or too long selection names no selection. The long form is used so an empty selection survives argument splitting. """ path = self.firmware() - for value in ("", "OO", "OOBB"): + for value in ("", "OOBB"): with self.subTest(select=value): result = self.run_plugin_cmd( _DOWNLOAD, args=f"-f {path} --select={value}") self.assertNotEqual(result.returncode, 0) - self.assertIn("Invalid select flag", result.stderr) + self.assertIn(f"invalid value '{value}' for --select", + result.stderr) def test_missing_selection_is_rejected(self): """The selection has no default, so omitting it is an error.""" diff --git a/tests/cli/micron/micron_vs_internal_log_mock_test.py b/tests/cli/micron/micron_vs_internal_log_mock_test.py index 8267126b9f..dc1125be9c 100644 --- a/tests/cli/micron/micron_vs_internal_log_mock_test.py +++ b/tests/cli/micron/micron_vs_internal_log_mock_test.py @@ -169,7 +169,7 @@ def test_unrecognised_telemetry_type_is_rejected(self): f"--package={self.package_path('t.bin')}") self.assertNotEqual(result.returncode, 0) - self.assertIn("host or controller", result.stderr) + self.assertIn("expected one of: host, controller", result.stderr) def test_data_area_is_required_in_telemetry_mode(self): result = self.run_log( From 293ae0e1031e1c604e33da12774e93688ce3b52e Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:30:45 +0000 Subject: [PATCH 125/263] plugins/ocp: take the telemetry type of internal-log as an enum internal-log compared --telemetry-type with its words itself, and an unknown word printed an error but ended with status 0. Take it with OPT_ENUM, so the help and the completions list the words, and an unknown word is a usage error which lists them and fails. host0 and host1 still warn that they are deprecated. The defect test of the exit status passes now. Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-nvme.c | 53 +++++++++------------ tests/cli/ocp/ocp_internal_log_mock_test.py | 8 ++-- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 4b2138696d..094de894e8 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -971,6 +971,13 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct const char *telemetry_type = "Telemetry Type; 'host' or 'controller'\n" "('host0' and 'host1' are deprecated)"; const char *hgen = "Have the host tell the controller to generate the report (default 1)"; + struct argconfig_opt_val tele_types[] = { + VAL_ENUM("host", TELEMETRY_TYPE_HOST), + VAL_ENUM("controller", TELEMETRY_TYPE_CONTROLLER), + VAL_ENUM("host0", 0), + VAL_ENUM("host1", 1), + VAL_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -980,7 +987,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct struct libnvme_passthru_cmd cmd; bool is_support_telemetry_controller; struct ocp_telemetry_parse_options opt = {0}; - int tele_type = 0; + int tele_type = TELEMETRY_TYPE_HOST; int tele_area = 0; __u32 host_gen = 1; char file_path_telemetry[PATH_MAX], file_path_string[PATH_MAX]; @@ -993,7 +1000,8 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct OPT_STR("string-log", 's', &opt.string_log, string_log), OPT_FILE("output-file", 'f', &opt.output_file, output_file), OPT_INT("data-area", 'a', &opt.data_area, data_area), - OPT_STR("telemetry-type", 't', &opt.telemetry_type, telemetry_type), + OPT_ENUM("telemetry-type", 't', &tele_type, telemetry_type, + tele_types), OPT_UINT("host-generate", 'g', &host_gen, hgen), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); @@ -1001,9 +1009,6 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct if (err) return err; - if (opt.telemetry_type == 0) - opt.telemetry_type = "host"; - if (libnvme_transport_handle_is_ns(hdl)) { err = libnvme_get_nsid(hdl, &nsid); if (err < 0) @@ -1031,31 +1036,19 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct tele_area = opt.data_area; - if (opt.telemetry_type) { - if (!strcmp(opt.telemetry_type, "host0")) { - fprintf(stderr, "WARNING: '--telemetry-type host0' is deprecated and will be removed in the next major version. Use '--telemetry-type host --host-generate=0' instead.\n"); - opt.telemetry_type = "host"; - tele_type = TELEMETRY_TYPE_HOST; - host_gen = 0; - } else if (!strcmp(opt.telemetry_type, "host1")) { - fprintf(stderr, "WARNING: '--telemetry-type host1' is deprecated and will be removed in the next major version. Use '--telemetry-type host' instead.\n"); - opt.telemetry_type = "host"; - tele_type = TELEMETRY_TYPE_HOST; - host_gen = 1; - } else if (!strcmp(opt.telemetry_type, "host")) - tele_type = TELEMETRY_TYPE_HOST; - else if (!strcmp(opt.telemetry_type, "controller")) - tele_type = TELEMETRY_TYPE_CONTROLLER; - else { - nvme_show_error( - "telemetry-type should be host, host0, host1 or controller.\n"); - goto out; - } - } else { - tele_type = TELEMETRY_TYPE_HOST; //Default Type - Host - opt.telemetry_type = "host"; - nvme_show_result("Missing telemetry-type. Using default - host.\n"); - } + if (tele_type == 0) { + fprintf(stderr, + "WARNING: '--telemetry-type host0' is deprecated and will be removed in the next major version. Use '--telemetry-type host --host-generate=0' instead.\n"); + tele_type = TELEMETRY_TYPE_HOST; + host_gen = 0; + } else if (tele_type == 1) { + fprintf(stderr, + "WARNING: '--telemetry-type host1' is deprecated and will be removed in the next major version. Use '--telemetry-type host' instead.\n"); + tele_type = TELEMETRY_TYPE_HOST; + host_gen = 1; + } + opt.telemetry_type = tele_type == TELEMETRY_TYPE_CONTROLLER ? + "controller" : "host"; if (!opt.telemetry_log) { nvme_show_result("\nMissing telemetry-log. Fetching from drive...\n"); diff --git a/tests/cli/ocp/ocp_internal_log_mock_test.py b/tests/cli/ocp/ocp_internal_log_mock_test.py index 578009608b..b9c3672109 100644 --- a/tests/cli/ocp/ocp_internal_log_mock_test.py +++ b/tests/cli/ocp/ocp_internal_log_mock_test.py @@ -56,7 +56,7 @@ Known defects are covered by expectedFailure tests asserting the correct behavior: a controller log's JSON header is decoded with the host -header's layout, and invalid -a and -t values exit 0. +header's layout, and an invalid -a value exits 0. Runs nowhere but Linux: libmock_nvme.so is an LD_PRELOAD shim. @@ -1204,17 +1204,15 @@ def test_invalid_data_area_is_rejected(self): def test_invalid_telemetry_type_is_rejected(self): result = self._invalid_option('-t', 'bogus') - self.assertIn('telemetry-type should be host, host0, host1 or ' - 'controller.', result.stderr) + self.assertIn("invalid value 'bogus' for -t: expected one of: host, " + "controller, host0, host1", result.stderr) @unittest.expectedFailure def test_invalid_data_area_exits_nonzero(self): """Defect: the data area check jumps out with err still 0.""" self.assertNotEqual(self._invalid_option('-a', '5').returncode, 0) - @unittest.expectedFailure def test_invalid_telemetry_type_exits_nonzero(self): - """Defect: the telemetry type check jumps out with err still 0.""" self.assertNotEqual(self._invalid_option('-t', 'bogus').returncode, 0) From 0a99c9a3cd9cead54acf881570dda6e7f2af2726 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:32:23 +0000 Subject: [PATCH 126/263] plugins/sandisk: take the telemetry type of vs-internal-log as an enum vs-internal-log compared --type with its words itself, in upper or lower case. Take it with OPT_ENUM, which compares in any case, lists the words in the help and the completions, and rejects an unknown word as a usage error. Signed-off-by: Daniel Wagner --- plugins/sandisk/sandisk-nvme.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index 214ae30a3b..2f660712e1 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -410,13 +410,21 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; + struct argconfig_opt_val types[] = { + VAL_ENUM("NONE", SNDK_TELEMETRY_TYPE_NONE), + VAL_ENUM("HOST", SNDK_TELEMETRY_TYPE_HOST), + VAL_ENUM("CONTROLLER", SNDK_TELEMETRY_TYPE_CONTROLLER), + VAL_ENUM("BOTH", SNDK_TELEMETRY_TYPE_BOTH), + VAL_END() + }; + struct config { char *file; __u32 xfer_size; __u32 data_area; __u64 file_size; __u64 offset; - char *type; + int type; }; struct config cfg = { @@ -425,14 +433,14 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, .data_area = 0, .file_size = 0, .offset = 0, - .type = NULL, + .type = SNDK_TELEMETRY_TYPE_NONE, }; NVME_ARGS(opts, OPT_FILE("output-file", 'O', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), - OPT_FILE("type", 't', &cfg.type, type), + OPT_ENUM("type", 't', &cfg.type, type, types), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), OPT_END()); @@ -502,24 +510,11 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, } } - if (!cfg.type || !strcmp(cfg.type, "NONE") || !strcmp(cfg.type, "none")) { - telemetry_type = SNDK_TELEMETRY_TYPE_NONE; + telemetry_type = cfg.type; + if (telemetry_type == SNDK_TELEMETRY_TYPE_NONE) telemetry_data_area = 0; - } else if (!strcmp(cfg.type, "HOST") || !strcmp(cfg.type, "host")) { - telemetry_type = SNDK_TELEMETRY_TYPE_HOST; - telemetry_data_area = cfg.data_area; - } else if (!strcmp(cfg.type, "CONTROLLER") || !strcmp(cfg.type, "controller")) { - telemetry_type = SNDK_TELEMETRY_TYPE_CONTROLLER; - telemetry_data_area = cfg.data_area; - } else if (!strcmp(cfg.type, "BOTH") || !strcmp(cfg.type, "both")) { - telemetry_type = SNDK_TELEMETRY_TYPE_BOTH; + else telemetry_data_area = cfg.data_area; - } else { - nvme_show_error( - "ERROR: SNDK: Invalid type - Must be NONE, HOST, CONTROLLER, or BOTH\n"); - ret = -1; - goto out; - } capabilities = sndk_get_drive_capabilities(ctx, hdl); From de02eea8e31847e2480bf6247847a1c41dfb75a7 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:32:23 +0000 Subject: [PATCH 127/263] plugins/wdc: take the telemetry type of vs-internal-log as an enum vs-internal-log compared --type with its words itself, in upper or lower case. Take it with OPT_ENUM, which compares in any case, lists the words in the help and the completions, and rejects an unknown word as a usage error. The NONE case cleared the help text of --data-area instead of the telemetry data area, which was 0 already. Signed-off-by: Daniel Wagner --- plugins/wdc/wdc-nvme.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index da1bfcd805..5bf73532d7 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -4427,13 +4427,20 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, char file_path[PATH_MAX/2] = {0}; int ret = -1; + struct argconfig_opt_val types[] = { + VAL_ENUM("NONE", WDC_TELEMETRY_TYPE_NONE), + VAL_ENUM("HOST", WDC_TELEMETRY_TYPE_HOST), + VAL_ENUM("CONTROLLER", WDC_TELEMETRY_TYPE_CONTROLLER), + VAL_END() + }; + struct config { char *file; __u32 xfer_size; __u32 data_area; __u64 file_size; __u64 offset; - char *type; + int type; }; struct config cfg = { @@ -4442,14 +4449,14 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, .data_area = 0, .file_size = 0, .offset = 0, - .type = NULL, + .type = WDC_TELEMETRY_TYPE_NONE, }; NVME_ARGS(opts, OPT_FILE("output-file", 'O', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), - OPT_FILE("type", 't', &cfg.type, type), + OPT_ENUM("type", 't', &cfg.type, type, types), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), OPT_END()); @@ -4518,21 +4525,11 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, } } - if (!cfg.type || !strcmp(cfg.type, "NONE") || !strcmp(cfg.type, "none")) { - telemetry_type = WDC_TELEMETRY_TYPE_NONE; - data_area = 0; - } else if (!strcmp(cfg.type, "HOST") || !strcmp(cfg.type, "host")) { - telemetry_type = WDC_TELEMETRY_TYPE_HOST; - telemetry_data_area = cfg.data_area; - } else if (!strcmp(cfg.type, "CONTROLLER") || !strcmp(cfg.type, "controller")) { - telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER; + telemetry_type = cfg.type; + if (telemetry_type == WDC_TELEMETRY_TYPE_NONE) + telemetry_data_area = 0; + else telemetry_data_area = cfg.data_area; - } else { - nvme_show_error( - "ERROR: WDC: Invalid type - Must be NONE, HOST or CONTROLLER\n"); - ret = -1; - goto out; - } } else { if (cfg.file) { strncpy(fb, cfg.file, PATH_MAX/2 - 8); From 7ca9a5ff1e6a1ec0372424908c73523f8874a6e9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:33:20 +0000 Subject: [PATCH 128/263] plugins/solidigm: take the log type of internal-log as an enum internal-log upper-cased --type in the caller's argv and compared it with its words itself. Take it with OPT_ENUM, which compares in any case, lists the words in the help and the completions, and rejects an unknown word as a usage error. Signed-off-by: Daniel Wagner --- plugins/solidigm/solidigm-internal-logs.c | 39 ++++++++--------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index 8c52928918..ad0d298465 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -142,7 +142,7 @@ struct nlog_dump_header4_1 { struct config { char *out_dir; - char *type; + int type; bool verbose; }; @@ -857,21 +857,30 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *acmd, struct ilog ilog = {0}; int err; enum log_type log_type = ALL; - char type_ALL[] = "ALL"; time_t current_time; DIR *dir; const char *desc = "Get Debug Firmware Logs and save them."; const char *type = "Log type; Defaults to ALL."; const char *out_dir = "Output directory; defaults to current working directory."; + struct argconfig_opt_val types[] = { + VAL_ENUM("ALL", ALL), + VAL_ENUM("CIT", CIT), + VAL_ENUM("HIT", HIT), + VAL_ENUM("NLOG", NLOG), + VAL_ENUM("ASSERT", ASSERTLOG), + VAL_ENUM("EVENT", EVENTLOG), + VAL_ENUM("EXTENDED", EXTENDED), + VAL_END() + }; struct config cfg = { .out_dir = ".", - .type = type_ALL, + .type = ALL, }; NVME_ARGS(opts, - OPT_STRING("type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED", &cfg.type, type), + OPT_ENUM("type", 't', &cfg.type, type, types), OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -879,27 +888,7 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *acmd, return err; ilog.cfg = &cfg; - for (char *p = cfg.type; *p; ++p) - *p = toupper(*p); - - if (!strcmp(cfg.type, "ALL")) - log_type = ALL; - else if (!strcmp(cfg.type, "HIT")) - log_type = HIT; - else if (!strcmp(cfg.type, "CIT")) - log_type = CIT; - else if (!strcmp(cfg.type, "NLOG")) - log_type = NLOG; - else if (!strcmp(cfg.type, "ASSERT")) - log_type = ASSERTLOG; - else if (!strcmp(cfg.type, "EVENT")) - log_type = EVENTLOG; - else if (!strcmp(cfg.type, "EXTENDED")) - log_type = EXTENDED; - else { - nvme_show_error("Invalid log type: %s", cfg.type); - return -EINVAL; - } + log_type = cfg.type; dir = opendir(cfg.out_dir); if (dir) From 52e686bd813649b1234d116828fcfd4835076232 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:36:45 +0000 Subject: [PATCH 129/263] nvme: define the fabrics and feature option tables directly NVMF_ARGS() and FEAT_ARGS_SAVE() expand to NVME_ARGS(), which only adds an Options group before the options and the end of the table. Define the tables directly, so NVME_ARGS() can go. The help and the command metadata stay the same. Signed-off-by: Daniel Wagner --- plugins/feat/feat-nvme.c | 16 ++++++++++++---- src/fabrics.h | 9 +++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 5ac22a245b..d69f5c1cf5 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -37,10 +37,18 @@ #define ASYNC_EVENT_CONF_DESC "Get and set async event configuration feature" #define KEEP_ALIVE_TIMER_DESC "Get and set keep alive timer feature" -#define FEAT_ARGS_SAVE(n, save_short, ...) \ - NVME_ARGS(n, ##__VA_ARGS__, \ - OPT_FLAG("save", save_short, &cfg.save, save), \ - OPT_BYTE("sel", 'S', &cfg.sel, sel)) +/* + * The options of the command first: an option of the same short name + * comes before --save. The group in front lets an empty argument list + * drop its comma. + */ +#define FEAT_ARGS_SAVE(n, save_short, ...) \ + struct argconfig_commandline_options n[] = { \ + OPT_GROUP("Options"), ##__VA_ARGS__, \ + OPT_FLAG("save", save_short, &cfg.save, save), \ + OPT_BYTE("sel", 'S', &cfg.sel, sel), \ + OPT_END() \ + } #define FEAT_ARGS(n, ...) FEAT_ARGS_SAVE(n, 's', ##__VA_ARGS__) diff --git a/src/fabrics.h b/src/fabrics.h index 0193895e79..972b9df123 100644 --- a/src/fabrics.h +++ b/src/fabrics.h @@ -75,8 +75,8 @@ struct nvmf_args { * config-create.c, which adds an entry to the INI configuration instead of * connecting. */ -#define NVMF_ARGS(n, f, ...) \ - NVME_ARGS(n, \ +#define NVMF_ARGS(n, f, ...) \ + struct argconfig_commandline_options n[] = { \ OPT_STRING("transport", 't', "STR", &f.transport, DESC_NVMF_TPORT), \ OPT_STRING("nqn", 'n', "STR", &f.subsysnqn, DESC_NVMF_NQN), \ OPT_STRING("traddr", 'a', "STR", &f.traddr, DESC_NVMF_TRADDR), \ @@ -112,8 +112,9 @@ struct nvmf_args { OPT_FLAG("data-digest", 'G', &f.data_digest, DESC_NVMF_DATA_DIGEST), \ OPT_FLAG("tls", 0, &f.tls, DESC_NVMF_TLS), \ OPT_FLAG("concat", 0, &f.concat, DESC_NVMF_CONCAT), \ - ##__VA_ARGS__ \ - ) + ##__VA_ARGS__, \ + OPT_END() \ + } /* * Resolve an OPT_STRING_OPTIONAL() "--persistent[=no|auto|force]" argument From d7bf995f6f1e2fa984a6ea7b66466935b3dbaedf Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:43:49 +0000 Subject: [PATCH 130/263] nvme: define the option tables of the built-in commands directly NVME_ARGS() only puts an Options group before the options of a command and ends the table. The help prints the Options heading anyway, so define the tables with OPT_ARGS() and OPT_END() like any argconfig table: the options one tab in, their arguments aligned in columns where the rows fit into 80 columns, and wrapped where not. The help and the command metadata stay the same. Signed-off-by: Daniel Wagner --- src/fabrics.c | 30 +++-- src/nvme-cmds-ctrl.c | 24 ++-- src/nvme-cmds-deprecated.c | 34 ++--- src/nvme-cmds-discovery.c | 34 ++--- src/nvme-cmds-enumerate.c | 20 ++- src/nvme-cmds-fabrics.c | 8 +- src/nvme-cmds-feature.c | 46 ++++--- src/nvme-cmds-io.c | 249 ++++++++++++++++++++----------------- src/nvme-cmds-passthru.c | 50 ++++---- src/nvme-cmds-registers.c | 120 ++++++++++-------- src/nvme-cmds-sanitize.c | 80 ++++++------ src/nvme-cmds-security.c | 18 +-- src/nvme.c | 9 +- 13 files changed, 409 insertions(+), 313 deletions(-) diff --git a/src/fabrics.c b/src/fabrics.c index 297b98afbc..0fc2c9cef1 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -1544,10 +1544,13 @@ int fabrics_disconnect_all(const char *desc, int argc, char **argv) struct config cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("transport", 't', "STR", &cfg.transport, DESC_NVMF_TPORT), - OPT_STRING("owner", 0, "NAME", &cfg.owner, owner_help), - OPT_FLAG("force", 0, &cfg.force, force_help)); + OPT_ARGS(opts) = { + OPT_STRING("transport", 't', "STR", &cfg.transport, + DESC_NVMF_TPORT), + OPT_STRING("owner", 0, "NAME", &cfg.owner, owner_help), + OPT_FLAG("force", 0, &cfg.force, force_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -1659,9 +1662,11 @@ int fabrics_config_show(const char *desc, int argc, char **argv) nvme_print_flags_t flags; int ret; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_STRING("config", 'J', "FILE", &config_file, - nvmf_config_file_ro)); + nvmf_config_file_ro), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -1725,10 +1730,15 @@ int fabrics_dim(const char *desc, int argc, char **argv) char *tas; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, "Comma-separated list of DC nqn"), - OPT_STRING("device", 'd', "DEV", &cfg.device, "Comma-separated list of DC nvme device handle."), - OPT_STRING("task", 't', "TASK", &cfg.tas, "[register|deregister]")); + OPT_ARGS(opts) = { + OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, + "Comma-separated list of DC nqn"), + OPT_STRING("device", 'd', "DEV", &cfg.device, + "Comma-separated list of DC nvme device handle."), + OPT_STRING("task", 't', "TASK", &cfg.tas, + "[register|deregister]"), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/src/nvme-cmds-ctrl.c b/src/nvme-cmds-ctrl.c index 1d8cab9c37..d8afc434af 100644 --- a/src/nvme-cmds-ctrl.c +++ b/src/nvme-cmds-ctrl.c @@ -69,11 +69,13 @@ static int virtual_mgmt(int argc, char **argv, struct command *acmd, struct plug .nr = 0, }; - NVME_ARGS(opts, - OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), - OPT_BYTE("rt", 'r', &cfg.rt, rt), - OPT_BYTE("act", 'a', &cfg.act, act), - OPT_SHRT("nr", 'n', &cfg.nr, nr)); + OPT_ARGS(opts) = { + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_BYTE("rt", 'r', &cfg.rt, rt), + OPT_BYTE("act", 'a', &cfg.act, act), + OPT_SHRT("nr", 'n', &cfg.nr, nr), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -101,7 +103,9 @@ static int subsystem_reset(int argc, char **argv, struct command *acmd, struct p __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -134,7 +138,9 @@ static int reset(int argc, char **argv, struct command *acmd, struct plugin *plu __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -163,7 +169,9 @@ static int ns_rescan(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index a3e09976fc..5e76a60704 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -61,11 +61,13 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl .hmac = 0, }; - NVME_ARGS(opts, - OPT_STR("secret", 's', &cfg.secret, secret), - OPT_UINT("key-length", 'l', &cfg.key_len, key_len), - OPT_STR("nqn", 'n', &cfg.nqn, nqn), - OPT_UINT("hmac", 'm', &cfg.hmac, hmac)); + OPT_ARGS(opts) = { + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_UINT("key-length", 'l', &cfg.key_len, key_len), + OPT_STR("nqn", 'n', &cfg.nqn, nqn), + OPT_UINT("hmac", 'm', &cfg.hmac, hmac), + OPT_END() + }; err = parse_cmdline(argc, argv, desc, opts); if (err) @@ -117,8 +119,10 @@ static int check_dhchap_key(int argc, char **argv, struct command *acmd, struct .key = NULL, }; - NVME_ARGS(opts, - OPT_STR("key", 'k', &cfg.key, key)); + OPT_ARGS(opts) = { + OPT_STR("key", 'k', &cfg.key, key), + OPT_END() + }; err = parse_cmdline(argc, argv, desc, opts); if (err) @@ -167,13 +171,15 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p struct config cfg = { 0 }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), - OPT_FLAG("import", 'i', &cfg.import, import), - OPT_FLAG("export", 'e', &cfg.export, export), - OPT_STR("revoke", 'r', &cfg.revoke, revoke)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FLAG("import", 'i', &cfg.import, import), + OPT_FLAG("export", 'e', &cfg.export, export), + OPT_STR("revoke", 'r', &cfg.revoke, revoke), + OPT_END() + }; err = parse_cmdline(argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-discovery.c b/src/nvme-cmds-discovery.c index 4798e18c04..514dbfa2fd 100644 --- a/src/nvme-cmds-discovery.c +++ b/src/nvme-cmds-discovery.c @@ -135,22 +135,24 @@ static int get_log(int argc, char **argv, struct command *acmd, struct plugin *p VAL_END() }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_BYTE("log-id", 'i', &cfg.log_id, log_id, log_name), - OPT_UINT("log-len", 'l', &cfg.log_len, log_len), - OPT_UINT("aen", 'a', &cfg.aen, aen), - OPT_SUFFIX("lpo", 'L', &cfg.lpo, lpo), - OPT_BYTE("lsp", 's', &cfg.lsp, lsp), - OPT_SHRT("lsi", 'S', &cfg.lsi, lsi), - OPT_FLAG("rae", 'r', &cfg.rae, rae), - OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_BYTE("csi", 'y', &cfg.csi, csi), - OPT_FLAG("ot", 'O', &cfg.ot, offset_type), - OPT_UINT("xfer-len", 'x', &cfg.xfer_len, xfer_len)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_BYTE("log-id", 'i', &cfg.log_id, log_id, log_name), + OPT_UINT("log-len", 'l', &cfg.log_len, log_len), + OPT_UINT("aen", 'a', &cfg.aen, aen), + OPT_SUFFIX("lpo", 'L', &cfg.lpo, lpo), + OPT_BYTE("lsp", 's', &cfg.lsp, lsp), + OPT_SHRT("lsi", 'S', &cfg.lsi, lsi), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_BYTE("csi", 'y', &cfg.csi, csi), + OPT_FLAG("ot", 'O', &cfg.ot, offset_type), + OPT_UINT("xfer-len", 'x', &cfg.xfer_len, xfer_len), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-enumerate.c b/src/nvme-cmds-enumerate.c index 21e5df43b6..1557b46b84 100644 --- a/src/nvme-cmds-enumerate.c +++ b/src/nvme-cmds-enumerate.c @@ -48,7 +48,9 @@ static int list_subsys(int argc, char **argv, struct command *acmd, int err; int nsid = NVME_NSID_ALL; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -116,8 +118,10 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct .ranking = NVME_CLI_TOPO_NAMESPACE, }; - NVME_ARGS(opts, - OPT_ENUM("ranking", 'r', &cfg.ranking, ranking, rankings)); + OPT_ARGS(opts) = { + OPT_ENUM("ranking", 'r', &cfg.ranking, ranking, rankings), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -175,8 +179,10 @@ static int top(int argc, char **argv, struct command *acmd, .delay = 1, }; - NVME_ARGS(opts, - OPT_INT("delay", 'd', &cfg.delay, delay)); + OPT_ARGS(opts) = { + OPT_INT("delay", 'd', &cfg.delay, delay), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -208,7 +214,9 @@ static int list(int argc, char **argv, struct command *acmd, struct plugin *plug __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; int err = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-fabrics.c b/src/nvme-cmds-fabrics.c index c202dbbfc9..1c9832dd73 100644 --- a/src/nvme-cmds-fabrics.c +++ b/src/nvme-cmds-fabrics.c @@ -57,7 +57,9 @@ static int gen_hostnqn_cmd(int argc, char **argv, struct command *acmd, struct p __cleanup_free char *hostnqn = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -97,7 +99,9 @@ static int show_hostnqn_cmd(int argc, char **argv, struct command *acmd, struct __cleanup_free char *hostnqn = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index 67834ca59a..420429cfd0 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -260,16 +260,20 @@ static int get_feature(int argc, char **argv, struct command *acmd, .uuid_index = 0, }; - NVME_ARGS(opts, - OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id, feature_name), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), - OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index_specify), - OPT_FLAG("changed", 'C', &cfg.changed, changed)); + OPT_ARGS(opts) = { + OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id, + feature_name), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, + uuid_index_specify), + OPT_FLAG("changed", 'C', &cfg.changed, changed), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -354,16 +358,18 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi .sv = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_desired, nvme_nsid_values), - OPT_BYTE("feature-id", 'f', &cfg.fid, fid, feature_name), - OPT_SUFFIX("value", 'V', &cfg.value, value), - OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12), - OPT_BYTE("uuid-index", 'U', &cfg.uidx, uuid_index_specify), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_FLAG("save", 's', &cfg.sv, sv)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired, + nvme_nsid_values), + OPT_BYTE("feature-id", 'f', &cfg.fid, fid, feature_name), + OPT_SUFFIX("value", 'V', &cfg.value, value), + OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12), + OPT_BYTE("uuid-index", 'U', &cfg.uidx, uuid_index_specify), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_FLAG("save", 's', &cfg.sv, sv), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-io.c b/src/nvme-cmds-io.c index 33eae49ac1..91af3e5800 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -126,13 +126,15 @@ static int write_uncor(int argc, char **argv, struct command *acmd, struct plugi .dspec = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), - OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), - OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), - OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), + OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), + OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -479,23 +481,27 @@ static int write_zeroes(int argc, char **argv, .nsz = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_desired, nvme_nsid_values), - OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), - OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), - OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), - OPT_FLAG("deac", 'd', &cfg.deac, deac), - OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access), - OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), - OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), - OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), - OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), - OPT_SUFFIX("storage-tag", 'S', &cfg.lbst, storage_tag), - OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check), - OPT_SHRT("dir-spec", 'D', &cfg.dspec, dspec_w_dtype), - OPT_FLAG("namespace-zeroes", 'Z', &cfg.nsz, nsz)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired, + nvme_nsid_values), + OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), + OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), + OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), + OPT_FLAG("deac", 'd', &cfg.deac, deac), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, + limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, + force_unit_access), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), + OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), + OPT_SUFFIX("storage-tag", 'S', &cfg.lbst, storage_tag), + OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check), + OPT_SHRT("dir-spec", 'D', &cfg.dspec, dspec_w_dtype), + OPT_FLAG("namespace-zeroes", 'Z', &cfg.nsz, nsz), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -603,16 +609,18 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi .cdw11 = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_LIST("ctx-attrs", 'a', &cfg.ctx_attrs, context_attrs), - OPT_LIST("blocks", 'b', &cfg.blocks, blocks), - OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks), - OPT_FLAG("ad", 'd', &cfg.ad, ad), - OPT_FLAG("idw", 'w', &cfg.idw, idw), - OPT_FLAG("idr", 'r', &cfg.idr, idr), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_LIST("ctx-attrs", 'a', &cfg.ctx_attrs, context_attrs), + OPT_LIST("blocks", 'b', &cfg.blocks, blocks), + OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks), + OPT_FLAG("ad", 'd', &cfg.ad, ad), + OPT_FLAG("idw", 'w', &cfg.idw, idw), + OPT_FLAG("idr", 'r', &cfg.idr, idr), + OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -763,29 +771,32 @@ static int copy_cmd(int argc, char **argv, struct command *acmd, struct plugin * .stc = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_id_desired, nvme_nsid_values), - OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba), - OPT_LIST("slbs", 's', &cfg.slbas, d_slbas), - OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs), - OPT_LIST("snsids", 'N', &cfg.snsids, d_snsids), - OPT_LIST("sopts", 'O', &cfg.sopts, d_sopts), - OPT_FLAG("limited-retry", 'l', &cfg.lr, d_lr), - OPT_FLAG("force-unit-access", 'f', &cfg.fua, d_fua), - OPT_BYTE("prinfow", 'p', &cfg.prinfow, d_prinfow), - OPT_BYTE("prinfor", 'P', &cfg.prinfor, d_prinfor), - OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, d_ilbrt), - OPT_LIST("expected-ref-tags", 'R', &cfg.eilbrts, d_eilbrts), - OPT_SHRT("app-tag", 'a', &cfg.lbat, d_lbat), - OPT_LIST("expected-app-tags", 'A', &cfg.elbats, d_elbats), - OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, d_lbatm), - OPT_LIST("expected-app-tag-masks", 'M', &cfg.elbatms, d_elbatms), - OPT_BYTE("dir-type", 'T', &cfg.dtype, d_dtype), - OPT_SHRT("dir-spec", 'S', &cfg.dspec, d_dspec), - OPT_BYTE("format", 'F', &cfg.format, d_format), - OPT_SUFFIX("storage-tag", 't', &cfg.lbst, storage_tag), - OPT_FLAG("storage-tag-check", 'c', &cfg.stc, storage_tag_check)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba), + OPT_LIST("slbs", 's', &cfg.slbas, d_slbas), + OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs), + OPT_LIST("snsids", 'N', &cfg.snsids, d_snsids), + OPT_LIST("sopts", 'O', &cfg.sopts, d_sopts), + OPT_FLAG("limited-retry", 'l', &cfg.lr, d_lr), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, d_fua), + OPT_BYTE("prinfow", 'p', &cfg.prinfow, d_prinfow), + OPT_BYTE("prinfor", 'P', &cfg.prinfor, d_prinfor), + OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, d_ilbrt), + OPT_LIST("expected-ref-tags", 'R', &cfg.eilbrts, d_eilbrts), + OPT_SHRT("app-tag", 'a', &cfg.lbat, d_lbat), + OPT_LIST("expected-app-tags", 'A', &cfg.elbats, d_elbats), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, d_lbatm), + OPT_LIST("expected-app-tag-masks", 'M', &cfg.elbatms, + d_elbatms), + OPT_BYTE("dir-type", 'T', &cfg.dtype, d_dtype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, d_dspec), + OPT_BYTE("format", 'F', &cfg.format, d_format), + OPT_SUFFIX("storage-tag", 't', &cfg.lbst, storage_tag), + OPT_FLAG("storage-tag-check", 'c', &cfg.stc, storage_tag_check), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -902,9 +913,11 @@ static int flush_cmd(int argc, char **argv, struct command *acmd, struct plugin .namespace_id = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1023,30 +1036,36 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char .force = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_id_desired, nvme_nsid_values), - OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block_addr), - OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), - OPT_SHRT("block-size", 'b', &cfg.block_size, block_size), - OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), - OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), - OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), - OPT_FILE("data", 'd', &cfg.data, data), - OPT_FILE("metadata", 'M', &cfg.metadata, metadata), - OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), - OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), - OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), - OPT_SUFFIX("storage-tag", 'g', &cfg.lbst, storage_tag), - OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry_num), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access), - OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check), - OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype_for_write), - OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec), - OPT_BYTE("dsm", 'D', &cfg.dsmgmt, dsm), - OPT_FLAG("show-command", 'V', &cfg.show, show), - OPT_FLAG("latency", 't', &cfg.latency, latency), - OPT_FLAG("force", 0, &cfg.force, force)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_SUFFIX("start-block", 's', &cfg.start_block, + start_block_addr), + OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), + OPT_SHRT("block-size", 'b', &cfg.block_size, block_size), + OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), + OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, + metadata_size), + OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), + OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), + OPT_SUFFIX("storage-tag", 'g', &cfg.lbst, storage_tag), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, + limited_retry_num), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, + force_unit_access), + OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check), + OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype_for_write), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec), + OPT_BYTE("dsm", 'D', &cfg.dsmgmt, dsm), + OPT_FLAG("show-command", 'V', &cfg.show, show), + OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_FLAG("force", 0, &cfg.force, force), + OPT_END() + }; if (opcode != nvme_cmd_write) { err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1346,19 +1365,23 @@ static int verify_cmd(int argc, char **argv, struct command *acmd, struct plugin .stc = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_desired, nvme_nsid_values), - OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), - OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), - OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access_verify), - OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), - OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), - OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), - OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), - OPT_SUFFIX("storage-tag", 'S', &cfg.lbst, storage_tag), - OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired, + nvme_nsid_values), + OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), + OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, + limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, + force_unit_access_verify), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, ref_tag), + OPT_SHRT("app-tag", 'a', &cfg.lbat, app_tag), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, app_tag_mask), + OPT_SUFFIX("storage-tag", 'S', &cfg.lbst, storage_tag), + OPT_FLAG("storage-tag-check", 'C', &cfg.stc, storage_tag_check), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1443,14 +1466,16 @@ static int get_lba_status(int argc, char **argv, struct command *acmd, .rl = 0, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.slba, slba), - OPT_UINT("max-dw", 'm', &cfg.mndw, mndw), - OPT_BYTE("action", 'a', &cfg.atype, atype), - OPT_SHRT("range-len", 'l', &cfg.rl, rl)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_SUFFIX("start-lba", 's', &cfg.slba, slba), + OPT_UINT("max-dw", 'm', &cfg.mndw, mndw), + OPT_BYTE("action", 'a', &cfg.atype, atype), + OPT_SHRT("range-len", 'l', &cfg.rl, rl), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1521,12 +1546,14 @@ static int capacity_mgmt(int argc, char **argv, struct command *acmd, struct plu .dw12 = 0, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_BYTE("operation", 'O', &cfg.operation, operation), - OPT_SHRT("element-id", 'i', &cfg.element_id, element_id), - OPT_UINT("cap-lower", 'l', &cfg.dw11, cap_lower), - OPT_UINT("cap-upper", 'u', &cfg.dw12, cap_upper)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_BYTE("operation", 'O', &cfg.operation, operation), + OPT_SHRT("element-id", 'i', &cfg.element_id, element_id), + OPT_UINT("cap-lower", 'l', &cfg.dw11, cap_lower), + OPT_UINT("cap-upper", 'u', &cfg.dw12, cap_upper), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-passthru.c b/src/nvme-cmds-passthru.c index 23e20d6f1f..46c68646b7 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -138,30 +138,32 @@ static int passthru(int argc, char **argv, bool admin, .latency = false, }; - NVME_ARGS(opts, - OPT_BYTE("opcode", 'O', &cfg.opcode, opcode), - OPT_BYTE("flags", 'f', &cfg.flags, cflags), - OPT_BYTE("prefill", 'p', &cfg.prefill, prefill), - OPT_SHRT("rsvd", 'R', &cfg.rsvd, rsvd), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_UINT("metadata-len", 'm', &cfg.metadata_len, metadata_len), - OPT_UINT("cdw2", '2', &cfg.cdw2, cdw2), - OPT_UINT("cdw3", '3', &cfg.cdw3, cdw3), - OPT_UINT("cdw10", '4', &cfg.cdw10, cdw10), - OPT_UINT("cdw11", '5', &cfg.cdw11, cdw11), - OPT_UINT("cdw12", '6', &cfg.cdw12, cdw12), - OPT_UINT("cdw13", '7', &cfg.cdw13, cdw13), - OPT_UINT("cdw14", '8', &cfg.cdw14, cdw14), - OPT_UINT("cdw15", '9', &cfg.cdw15, cdw15), - OPT_FILE("input-file", 'i', &cfg.input_file, input), - OPT_FILE("metadata", 'M', &cfg.metadata, metadata), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump), - OPT_FLAG("show-command", 's', &cfg.show_command, show), - OPT_FLAG("read", 'r', &cfg.read, re), - OPT_FLAG("write", 'w', &cfg.write, wr), - OPT_FLAG("latency", 'T', &cfg.latency, latency)); + OPT_ARGS(opts) = { + OPT_BYTE("opcode", 'O', &cfg.opcode, opcode), + OPT_BYTE("flags", 'f', &cfg.flags, cflags), + OPT_BYTE("prefill", 'p', &cfg.prefill, prefill), + OPT_SHRT("rsvd", 'R', &cfg.rsvd, rsvd), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_UINT("metadata-len", 'm', &cfg.metadata_len, metadata_len), + OPT_UINT("cdw2", '2', &cfg.cdw2, cdw2), + OPT_UINT("cdw3", '3', &cfg.cdw3, cdw3), + OPT_UINT("cdw10", '4', &cfg.cdw10, cdw10), + OPT_UINT("cdw11", '5', &cfg.cdw11, cdw11), + OPT_UINT("cdw12", '6', &cfg.cdw12, cdw12), + OPT_UINT("cdw13", '7', &cfg.cdw13, cdw13), + OPT_UINT("cdw14", '8', &cfg.cdw14, cdw14), + OPT_UINT("cdw15", '9', &cfg.cdw15, cdw15), + OPT_FILE("input-file", 'i', &cfg.input_file, input), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump), + OPT_FLAG("show-command", 's', &cfg.show_command, show), + OPT_FLAG("read", 'r', &cfg.read, re), + OPT_FLAG("write", 'w', &cfg.write, wr), + OPT_FLAG("latency", 'T', &cfg.latency, latency), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-registers.c b/src/nvme-cmds-registers.c index c2f7833635..ac07295e60 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -150,7 +150,9 @@ static int show_registers(int argc, char **argv, struct command *acmd, struct pl .fabrics = false, }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -398,36 +400,38 @@ static int get_register(int argc, char **argv, struct command *acmd, struct plug .offset = 0, }; - NVME_ARGS(opts, - OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_FLAG("cap", 0, &cfg.cap, cap), - OPT_FLAG("vs", 0, &cfg.vs, vs), - OPT_FLAG("cmbloc", 0, &cfg.cmbloc, cmbloc), - OPT_FLAG("cmbsz", 0, &cfg.cmbsz, cmbsz), - OPT_FLAG("bpinfo", 0, &cfg.bpinfo, bpinfo), - OPT_FLAG("cmbsts", 0, &cfg.cmbsts, cmbsts), - OPT_FLAG("cmbebs", 0, &cfg.cmbebs, cmbebs), - OPT_FLAG("cmbswtp", 0, &cfg.cmbswtp, cmbswtp), - OPT_FLAG("crto", 0, &cfg.crto, crto), - OPT_FLAG("pmrcap", 0, &cfg.pmrcap, pmrcap), - OPT_FLAG("pmrsts", 0, &cfg.pmrsts, pmrsts), - OPT_FLAG("pmrebs", 0, &cfg.pmrebs, pmrebs), - OPT_FLAG("pmrswtp", 0, &cfg.pmrswtp, pmrswtp), - OPT_FLAG("intms", 0, &cfg.intms, intms), - OPT_FLAG("intmc", 0, &cfg.intmc, intmc), - OPT_FLAG("cc", 0, &cfg.cc, cc), - OPT_FLAG("csts", 0, &cfg.csts, csts), - OPT_FLAG("nssr", 0, &cfg.nssr, nssr), - OPT_FLAG("aqa", 0, &cfg.aqa, aqa), - OPT_FLAG("asq", 0, &cfg.asq, asq), - OPT_FLAG("acq", 0, &cfg.acq, acq), - OPT_FLAG("bprsel", 0, &cfg.bprsel, bprsel), - OPT_FLAG("bpmbl", 0, &cfg.bpmbl, bpmbl), - OPT_FLAG("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), - OPT_FLAG("nssd", 0, &cfg.nssd, nssd), - OPT_FLAG("pmrctl", 0, &cfg.pmrctl, pmrctl), - OPT_FLAG("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), - OPT_FLAG("pmrmscu", 0, &cfg.pmrmscu, pmrmscu)); + OPT_ARGS(opts) = { + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_FLAG("cap", 0, &cfg.cap, cap), + OPT_FLAG("vs", 0, &cfg.vs, vs), + OPT_FLAG("cmbloc", 0, &cfg.cmbloc, cmbloc), + OPT_FLAG("cmbsz", 0, &cfg.cmbsz, cmbsz), + OPT_FLAG("bpinfo", 0, &cfg.bpinfo, bpinfo), + OPT_FLAG("cmbsts", 0, &cfg.cmbsts, cmbsts), + OPT_FLAG("cmbebs", 0, &cfg.cmbebs, cmbebs), + OPT_FLAG("cmbswtp", 0, &cfg.cmbswtp, cmbswtp), + OPT_FLAG("crto", 0, &cfg.crto, crto), + OPT_FLAG("pmrcap", 0, &cfg.pmrcap, pmrcap), + OPT_FLAG("pmrsts", 0, &cfg.pmrsts, pmrsts), + OPT_FLAG("pmrebs", 0, &cfg.pmrebs, pmrebs), + OPT_FLAG("pmrswtp", 0, &cfg.pmrswtp, pmrswtp), + OPT_FLAG("intms", 0, &cfg.intms, intms), + OPT_FLAG("intmc", 0, &cfg.intmc, intmc), + OPT_FLAG("cc", 0, &cfg.cc, cc), + OPT_FLAG("csts", 0, &cfg.csts, csts), + OPT_FLAG("nssr", 0, &cfg.nssr, nssr), + OPT_FLAG("aqa", 0, &cfg.aqa, aqa), + OPT_FLAG("asq", 0, &cfg.asq, asq), + OPT_FLAG("acq", 0, &cfg.acq, acq), + OPT_FLAG("bprsel", 0, &cfg.bprsel, bprsel), + OPT_FLAG("bpmbl", 0, &cfg.bpmbl, bpmbl), + OPT_FLAG("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), + OPT_FLAG("nssd", 0, &cfg.nssd, nssd), + OPT_FLAG("pmrctl", 0, &cfg.pmrctl, pmrctl), + OPT_FLAG("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), + OPT_FLAG("pmrmscu", 0, &cfg.pmrmscu, pmrmscu), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -721,25 +725,27 @@ static int set_register(int argc, char **argv, struct command *acmd, struct plug .offset = 0, }; - NVME_ARGS(opts, - OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_SUFFIX("value", 'V', &cfg.value, value), - OPT_FLAG("mmio32", 'm', &cfg.mmio32, mmio32), - OPT_UINT("intms", 0, &cfg.intms, intms), - OPT_UINT("intmc", 0, &cfg.intmc, intmc), - OPT_UINT("cc", 0, &cfg.cc, cc), - OPT_UINT("csts", 0, &cfg.csts, csts), - OPT_UINT("nssr", 0, &cfg.nssr, nssr), - OPT_UINT("aqa", 0, &cfg.aqa, aqa), - OPT_SUFFIX("asq", 0, &cfg.asq, asq), - OPT_SUFFIX("acq", 0, &cfg.acq, acq), - OPT_UINT("bprsel", 0, &cfg.bprsel, bprsel), - OPT_SUFFIX("bpmbl", 0, &cfg.bpmbl, bpmbl), - OPT_SUFFIX("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), - OPT_UINT("nssd", 0, &cfg.nssd, nssd), - OPT_UINT("pmrctl", 0, &cfg.pmrctl, pmrctl), - OPT_UINT("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), - OPT_UINT("pmrmscu", 0, &cfg.pmrmscu, pmrmscu)); + OPT_ARGS(opts) = { + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_SUFFIX("value", 'V', &cfg.value, value), + OPT_FLAG("mmio32", 'm', &cfg.mmio32, mmio32), + OPT_UINT("intms", 0, &cfg.intms, intms), + OPT_UINT("intmc", 0, &cfg.intmc, intmc), + OPT_UINT("cc", 0, &cfg.cc, cc), + OPT_UINT("csts", 0, &cfg.csts, csts), + OPT_UINT("nssr", 0, &cfg.nssr, nssr), + OPT_UINT("aqa", 0, &cfg.aqa, aqa), + OPT_SUFFIX("asq", 0, &cfg.asq, asq), + OPT_SUFFIX("acq", 0, &cfg.acq, acq), + OPT_UINT("bprsel", 0, &cfg.bprsel, bprsel), + OPT_SUFFIX("bpmbl", 0, &cfg.bpmbl, bpmbl), + OPT_SUFFIX("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), + OPT_UINT("nssd", 0, &cfg.nssd, nssd), + OPT_UINT("pmrctl", 0, &cfg.pmrctl, pmrctl), + OPT_UINT("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), + OPT_UINT("pmrmscu", 0, &cfg.pmrmscu, pmrmscu), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -777,8 +783,10 @@ static int get_property(int argc, char **argv, struct command *acmd, struct plug .fabrics = true, }; - NVME_ARGS(opts, - OPT_UINT("offset", 'O', &cfg.offset, offset)); + OPT_ARGS(opts) = { + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -817,9 +825,11 @@ static int set_property(int argc, char **argv, struct command *acmd, struct plug .value = 0, }; - NVME_ARGS(opts, - OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_SUFFIX("value", 'V', &cfg.value, value)); + OPT_ARGS(opts) = { + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_SUFFIX("value", 'V', &cfg.value, value), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 04a7ea7a8c..4b4a029c2a 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -197,12 +197,14 @@ static int device_self_test(int argc, char **argv, struct command *acmd, struct .wait = false, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_BYTE("self-test-code", 's', &cfg.stc, self_test_code), - OPT_FLAG("wait", 'w', &cfg.wait, wait)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_BYTE("self-test-code", 's', &cfg.stc, self_test_code), + OPT_FLAG("wait", 'w', &cfg.wait, wait), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -480,18 +482,20 @@ static int sanitize_cmd(int argc, char **argv, struct command *acmd, struct plug VAL_END() }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_FLAG("no-dealloc", 'd', &cfg.no_dealloc, no_dealloc_desc), - OPT_FLAG("oipbp", 'i', &cfg.oipbp, oipbp_desc), - OPT_BYTE("owpass", 'n', &cfg.owpass, owpass_desc), - OPT_FLAG("ause", 'u', &cfg.ause, ause_desc), - OPT_BYTE("sanact", 'a', &cfg.sanact, sanact_desc, sanact), - OPT_UINT("ovrpat", 'p', &cfg.ovrpat, ovrpat_desc), - OPT_FLAG("emvs", 'e', &cfg.emvs, emvs_desc), - OPT_FLAG("preq", 'q', &cfg.preq, preq_desc), - OPT_FLAG("wait", 'w', &cfg.wait, wait), - OPT_UINT("repeat", 'r', &cfg.repeat, repeat)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_FLAG("no-dealloc", 'd', &cfg.no_dealloc, no_dealloc_desc), + OPT_FLAG("oipbp", 'i', &cfg.oipbp, oipbp_desc), + OPT_BYTE("owpass", 'n', &cfg.owpass, owpass_desc), + OPT_FLAG("ause", 'u', &cfg.ause, ause_desc), + OPT_BYTE("sanact", 'a', &cfg.sanact, sanact_desc, sanact), + OPT_UINT("ovrpat", 'p', &cfg.ovrpat, ovrpat_desc), + OPT_FLAG("emvs", 'e', &cfg.emvs, emvs_desc), + OPT_FLAG("preq", 'q', &cfg.preq, preq_desc), + OPT_FLAG("wait", 'w', &cfg.wait, wait), + OPT_UINT("repeat", 'r', &cfg.repeat, repeat), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -632,12 +636,14 @@ static int sanitize_ns_cmd(int argc, char **argv, struct command *acmd, VAL_END() }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_FLAG("ause", 'u', &cfg.ause, ause_desc), - OPT_BYTE("sanact", 'a', &cfg.sanact, sanact_desc, sanact), - OPT_FLAG("emvs", 'e', &cfg.emvs, emvs_desc), - OPT_FLAG("preq", 'q', &cfg.preq, preq_desc)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_FLAG("ause", 'u', &cfg.ause, ause_desc), + OPT_BYTE("sanact", 'a', &cfg.sanact, sanact_desc, sanact), + OPT_FLAG("emvs", 'e', &cfg.emvs, emvs_desc), + OPT_FLAG("preq", 'q', &cfg.preq, preq_desc), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -750,18 +756,20 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin .bs = 0, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_BYTE("lbaf", 'l', &cfg.lbaf, lbaf), - OPT_BYTE("ses", 's', &cfg.ses, ses), - OPT_BYTE("pi", 'i', &cfg.pi, pi), - OPT_BYTE("pil", 'p', &cfg.pil, pil), - OPT_BYTE("ms", 'm', &cfg.mset, mset), - OPT_FLAG("reset", 'r', &cfg.reset, reset), - OPT_FLAG("force", 0, &cfg.force, force), - OPT_SUFFIX("block-size", 'b', &cfg.bs, bs)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_BYTE("lbaf", 'l', &cfg.lbaf, lbaf), + OPT_BYTE("ses", 's', &cfg.ses, ses), + OPT_BYTE("pi", 'i', &cfg.pi, pi), + OPT_BYTE("pil", 'p', &cfg.pil, pil), + OPT_BYTE("ms", 'm', &cfg.mset, mset), + OPT_FLAG("reset", 'r', &cfg.reset, reset), + OPT_FLAG("force", 0, &cfg.force, force), + OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), + OPT_END() + }; /* set default timeout for format to 60 seconds */ nvme_args.timeout = 600000; diff --git a/src/nvme-cmds-security.c b/src/nvme-cmds-security.c index dfb3a746b1..2dd134e737 100644 --- a/src/nvme-cmds-security.c +++ b/src/nvme-cmds-security.c @@ -69,14 +69,16 @@ static int lockdown_cmd(int argc, char **argv, struct command *acmd, struct plug .css = 0, }; - NVME_ARGS(opts, - OPT_BYTE("ofi", 'O', &cfg.ofi, ofi_desc), - OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc), - OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc), - OPT_BYTE("scp", 's', &cfg.scp, scp_desc), - OPT_BYTE("uuid", 'U', &cfg.uuid, uuid_desc), - OPT_BYTE("csel", 'c', &cfg.csel, csel_desc), - OPT_SHRT("css", 'C', &cfg.css, css_desc)); + OPT_ARGS(opts) = { + OPT_BYTE("ofi", 'O', &cfg.ofi, ofi_desc), + OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc), + OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc), + OPT_BYTE("scp", 's', &cfg.scp, scp_desc), + OPT_BYTE("uuid", 'U', &cfg.uuid, uuid_desc), + OPT_BYTE("csel", 'c', &cfg.csel, csel_desc), + OPT_SHRT("css", 'C', &cfg.css, css_desc), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/src/nvme.c b/src/nvme.c index 10b5901c31..c77d4eccc1 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -95,9 +95,12 @@ int __id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify)); + OPT_ARGS(opts) = { + OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, + vendor_specific), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From 0c2160db2b355c35ac1a73bb0c58315794114685 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:43:50 +0000 Subject: [PATCH 131/263] plugins: define the option tables directly NVME_ARGS() only puts an Options group before the options of a command and ends the table. The help prints the Options heading anyway, so define the tables with OPT_ARGS() and OPT_END() like any argconfig table: the options one tab in, their arguments aligned in columns where the rows fit into 80 columns, and wrapped where not. The help and the command metadata stay the same, except that the memblaze lat-log and perf-stats help texts which no longer fit into 80 columns got shorter. Signed-off-by: Daniel Wagner --- plugins/amzn/amzn-nvme.c | 6 +- plugins/dapustor/dapustor-nvme.c | 6 +- plugins/dera/dera-nvme.c | 4 +- plugins/dir/dir-plugin.c | 44 ++-- plugins/exclusion/exclusion-nvme.c | 36 ++- plugins/fdp/fdp.c | 72 +++-- plugins/fw/fw-plugin.c | 30 ++- plugins/huawei/huawei-nvme.c | 4 +- plugins/ibm/ibm-nvme.c | 22 +- plugins/id/id-plugin.c | 139 ++++++---- plugins/innogrit/innogrit-nvme.c | 8 +- plugins/inspur/inspur-nvme.c | 4 +- plugins/intel/intel-nvme.c | 50 ++-- plugins/io-mgmt/io-mgmt-plugin.c | 32 ++- plugins/keys/keys-plugin.c | 114 ++++---- plugins/lm/lm-nvme.c | 94 ++++--- plugins/log/log-plugin.c | 249 +++++++++++------- plugins/memblaze/memblaze-nvme.c | 123 +++++---- plugins/micron/micron-nvme.c | 125 ++++++--- plugins/nbft/nbft-plugin.c | 15 +- plugins/netapp/netapp-nvme.c | 8 +- plugins/ns/ns-plugin.c | 70 ++--- plugins/nvme-mi/nvme-mi-plugin.c | 20 +- plugins/ocp/ocp-clear-features.c | 17 +- plugins/ocp/ocp-fw-activation-history.c | 4 +- plugins/ocp/ocp-hardware-component-log.c | 7 +- plugins/ocp/ocp-nvme.c | 200 ++++++++------ plugins/ocp/ocp-smart-extended-log.c | 6 +- plugins/registry/registry-nvme.c | 22 +- plugins/resv/resv-plugin.c | 66 +++-- plugins/rpmb/rpmb.c | 68 +++-- plugins/samsung/samsung-nvme.c | 13 +- plugins/sandisk/sandisk-nvme.c | 39 ++- plugins/scaleflux/sfx-nvme.c | 97 ++++--- plugins/seagate/seagate-nvme.c | 58 ++-- plugins/security/security-plugin.c | 42 +-- plugins/shannon/shannon-nvme.c | 40 +-- .../solidigm/solidigm-garbage-collection.c | 4 +- plugins/solidigm/solidigm-get-drive-info.c | 4 +- plugins/solidigm/solidigm-internal-logs.c | 8 +- plugins/solidigm/solidigm-latency-tracking.c | 16 +- plugins/solidigm/solidigm-log-page-dir.c | 4 +- plugins/solidigm/solidigm-market-log.c | 6 +- plugins/solidigm/solidigm-ocp-version.c | 4 +- plugins/solidigm/solidigm-smart.c | 9 +- plugins/solidigm/solidigm-telemetry.c | 14 +- plugins/solidigm/solidigm-temp-stats.c | 6 +- plugins/solidigm/solidigm-workload-tracker.c | 27 +- plugins/ssstc/ssstc-nvme.c | 6 +- plugins/toshiba/toshiba-nvme.c | 16 +- plugins/transcend/transcend-nvme.c | 8 +- plugins/virtium/virtium-nvme.c | 16 +- plugins/wdc/wdc-nvme.c | 228 ++++++++++------ plugins/ymtc/ymtc-nvme.c | 8 +- plugins/zns/zns.c | 148 ++++++----- 55 files changed, 1548 insertions(+), 938 deletions(-) diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c index 9b28472ccd..b62e8ca186 100644 --- a/plugins/amzn/amzn-nvme.c +++ b/plugins/amzn/amzn-nvme.c @@ -593,11 +593,13 @@ static int get_stats(int argc, char **argv, struct command *acmd, .output_format = "normal", }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FLAG("details", 'd', &detail, "Detail IO histogram of each block size ranges"), OPT_UINT("interval", 'i', &interval, - "Polling interval in seconds")); + "Polling interval in seconds"), + OPT_END() + }; rc = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (rc) diff --git a/plugins/dapustor/dapustor-nvme.c b/plugins/dapustor/dapustor-nvme.c index 578e587168..d72d1b47d7 100644 --- a/plugins/dapustor/dapustor-nvme.c +++ b/plugins/dapustor/dapustor-nvme.c @@ -526,11 +526,13 @@ static int dapustor_additional_smart_log(int argc, char **argv, struct command * .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + OPT_FLAG_JSON("json", 'j', &cfg.json, json), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index 626e4e0af9..34bab22565 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -124,7 +124,9 @@ static int get_status(int argc, char **argv, struct command *acmd, struct plugin struct nvme_dera_smart_info_log log; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/dir/dir-plugin.c b/plugins/dir/dir-plugin.c index cedc0f9134..24313271a5 100644 --- a/plugins/dir/dir-plugin.c +++ b/plugins/dir/dir-plugin.c @@ -71,17 +71,19 @@ static int dir_send(int argc, char **argv, struct command *acmd, struct plugin * .file = "", }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), - OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), - OPT_BYTE("target-dir", 'T', &cfg.ttype, ttype), - OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype), - OPT_BYTE("dir-oper", 'O', &cfg.doper, doper), - OPT_SHRT("endir", 'e', &cfg.endir, endir), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_directive), - OPT_FILE("input-file", 'i', &cfg.file, input)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), + OPT_BYTE("target-dir", 'T', &cfg.ttype, ttype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype), + OPT_BYTE("dir-oper", 'O', &cfg.doper, doper), + OPT_SHRT("endir", 'e', &cfg.endir, endir), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_directive), + OPT_FILE("input-file", 'i', &cfg.file, input), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -198,15 +200,17 @@ static int dir_receive(int argc, char **argv, struct command *acmd, struct plugi .nsr = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_directive), - OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), - OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype), - OPT_BYTE("dir-oper", 'O', &cfg.doper, doper), - OPT_SHRT("req-resource", 'r', &cfg.nsr, nsr)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_directive), + OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec_w_dtype), + OPT_BYTE("dir-oper", 'O', &cfg.doper, doper), + OPT_SHRT("req-resource", 'r', &cfg.nsr, nsr), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/exclusion/exclusion-nvme.c b/plugins/exclusion/exclusion-nvme.c index a214409c85..b90bffe437 100644 --- a/plugins/exclusion/exclusion-nvme.c +++ b/plugins/exclusion/exclusion-nvme.c @@ -53,8 +53,10 @@ static int excl_create(int argc, char **argv, struct command *acmd, char *name; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("name", 'N', "NAME", &cfg.name, name_help)); + OPT_ARGS(opts) = { + OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -87,8 +89,10 @@ static int excl_delete(int argc, char **argv, struct command *acmd, char *name; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("name", 'N', "NAME", &cfg.name, name_help)); + OPT_ARGS(opts) = { + OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -164,8 +168,10 @@ static int excl_list(int argc, char **argv, struct command *acmd, char *name; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("name", 'N', "NAME", &cfg.name, name_help)); + OPT_ARGS(opts) = { + OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -211,9 +217,11 @@ static int excl_add(int argc, char **argv, struct command *acmd, char *entry; } cfg = { 0 }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), - OPT_STRING("entry", 'e', "ENTRY", &cfg.entry, entry_help)); + OPT_STRING("entry", 'e', "ENTRY", &cfg.entry, entry_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -275,8 +283,10 @@ static int excl_remove(int argc, char **argv, struct command *acmd, char *name; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("name", 'N', "NAME", &cfg.name, name_help)); + OPT_ARGS(opts) = { + OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -491,8 +501,10 @@ static int excl_edit(int argc, char **argv, struct command *acmd, char *name; } cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("name", 'N', "NAME", &cfg.name, name_help)); + OPT_ARGS(opts) = { + OPT_STRING("name", 'N', "NAME", &cfg.name, name_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index c195b2d319..8b0db724ff 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -47,9 +47,11 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -118,9 +120,11 @@ static int fdp_usage(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -180,9 +184,11 @@ static int fdp_stats(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -240,10 +246,12 @@ static int fdp_events(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), - OPT_FLAG("host-events", 'E', &cfg.host_events, host_events), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FLAG("host-events", 'E', &cfg.host_events, host_events), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -298,10 +306,12 @@ static int fdp_status(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -369,10 +379,12 @@ static int fdp_update(int argc, char **argv, struct command *acmd, struct plugin .pids = "", }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, nvme_nsid_values), - OPT_LIST("pids", 'p', &cfg.pids, _pids)); + OPT_LIST("pids", 'p', &cfg.pids, _pids), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -439,13 +451,15 @@ static int fdp_set_events(int argc, char **argv, struct command *acmd, struct pl .sv = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), - OPT_SHRT("placement-handle", 'p', &cfg.ph, ph), - OPT_FLAG("enable", 'e', &cfg.enable, enable), - OPT_FLAG("save", 's', &cfg.sv, sv), - OPT_LIST("event-types", 't', &cfg.event_types, event_types)); + OPT_SHRT("placement-handle", 'p', &cfg.ph, ph), + OPT_FLAG("enable", 'e', &cfg.enable, enable), + OPT_FLAG("save", 's', &cfg.sv, sv), + OPT_LIST("event-types", 't', &cfg.event_types, event_types), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -516,10 +530,12 @@ static int fdp_feature(int argc, char **argv, struct command *acmd, struct plugi .endgid = 0, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'e', &cfg.endgid, endurance_group), + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'e', &cfg.endgid, endurance_group), OPT_BYTE("enable-conf-idx", 'c', &cfg.fdpcidx, enable_conf_idx), - OPT_FLAG("disable", 'd', &cfg.disable, disable)); + OPT_FLAG("disable", 'd', &cfg.disable, disable), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/fw/fw-plugin.c b/plugins/fw/fw-plugin.c index bd39857055..005ef03d89 100644 --- a/plugins/fw/fw-plugin.c +++ b/plugins/fw/fw-plugin.c @@ -191,14 +191,16 @@ static int fw_download(int argc, char **argv, struct command *acmd, struct plugi .stream = false, }; - NVME_ARGS(opts, - OPT_FILE("fw", 'f', &cfg.fw, fw), - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("xfer", 'x', &cfg.xfer, xfer), - OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_FLAG("progress", 'p', &cfg.progress, progress), - OPT_FLAG("ignore-ovr", 'i', &cfg.ignore_ovr, ignore_ovr), - OPT_FLAG("stream", 0, &cfg.stream, stream)); + OPT_ARGS(opts) = { + OPT_FILE("fw", 'f', &cfg.fw, fw), + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("xfer", 'x', &cfg.xfer, xfer), + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_FLAG("progress", 'p', &cfg.progress, progress), + OPT_FLAG("ignore-ovr", 'i', &cfg.ignore_ovr, ignore_ovr), + OPT_FLAG("stream", 0, &cfg.stream, stream), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -422,11 +424,13 @@ static int fw_commit(int argc, char **argv, struct command *acmd, struct plugin VAL_END() }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_BYTE("slot", 's', &cfg.slot, slot), - OPT_BYTE("action", 'a', &cfg.action, action, ca), - OPT_BYTE("bpid", 'b', &cfg.bpid, bpid)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_BYTE("slot", 's', &cfg.slot, slot), + OPT_BYTE("action", 'a', &cfg.action, action, ca), + OPT_BYTE("bpid", 'b', &cfg.bpid, bpid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 4348820b2d..2effa7f159 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -329,7 +329,9 @@ static int huawei_list(int argc, char **argv, struct command *acmd, unsigned int huawei_num = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 7c34414530..55bded610f 100644 --- a/plugins/ibm/ibm-nvme.c +++ b/plugins/ibm/ibm-nvme.c @@ -241,8 +241,10 @@ static int get_ibm_addi_smart_log(int argc, char **argv, struct command *cmd, st .raw_binary = 0, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -371,8 +373,10 @@ static int get_ibm_vpd_log(int argc, char **argv, struct command *cmd, struct pl .raw_binary = 0, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -550,10 +554,12 @@ static int get_ibm_persistent_event_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_BYTE("action", 'a', &cfg.action, action), - OPT_UINT("log_len", 'l', &cfg.log_len, log_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_BYTE("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index 0a207260e1..503d4928c1 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -50,10 +50,12 @@ static int list_ctrl(int argc, char **argv, struct command *acmd, struct plugin .namespace_id = NVME_NSID_NONE, }; - NVME_ARGS(opts, - OPT_SHRT("cntid", 'c', &cfg.cntid, controller), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_optional, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_SHRT("cntid", 'c', &cfg.cntid, controller), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_optional, nvme_nsid_values), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -110,11 +112,13 @@ static int list_ns(int argc, char **argv, struct command *acmd, struct plugin *p .all = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_INT("csi", 'y', &cfg.csi, csi), - OPT_FLAG("all", 'a', &cfg.all, all)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_INT("csi", 'y', &cfg.csi, csi), + OPT_FLAG("all", 'a', &cfg.all, all), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -179,10 +183,12 @@ static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct .uuid_index = NVME_UUID_NONE, }; - NVME_ARGS(opts, - OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, - lba_format_index), - OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); + OPT_ARGS(opts) = { + OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, + lba_format_index), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -233,8 +239,10 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *acmd, .endgrp_id = 0, }; - NVME_ARGS(opts, - OPT_SHRT("endgrp-id", 'i', &cfg.endgrp_id, endurance_grp_id)); + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'i', &cfg.endgrp_id, endurance_grp_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -278,7 +286,9 @@ static int nvm_id_ctrl(int argc, char **argv, struct command *acmd, nvme_print_flags_t flags; int err = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -330,10 +340,12 @@ static int nvm_id_ns(int argc, char **argv, struct command *acmd, .uuid_index = NVME_UUID_NONE, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -404,10 +416,12 @@ static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, str .uuid_index = NVME_UUID_NONE, }; - NVME_ARGS(opts, - OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, - lba_format_index), - OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index)); + OPT_ARGS(opts) = { + OPT_SHRT("lba-format-index", 'i', &cfg.lba_format_index, + lba_format_index), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -472,10 +486,12 @@ static int ns_descs(int argc, char **argv, struct command *acmd, struct plugin * .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -543,12 +559,15 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_FLAG("force", 0, &cfg.force, force), - OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_FLAG("force", 0, &cfg.force, force), + OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, + vendor_specific), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -619,10 +638,12 @@ static int cmd_set_independent_id_ns(int argc, char **argv, struct command *acmd .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_identify), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -675,7 +696,9 @@ static int id_ns_granularity(int argc, char **argv, struct command *acmd, struct nvme_print_flags_t flags; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -722,8 +745,10 @@ static int id_nvmset(int argc, char **argv, struct command *acmd, struct plugin .nvmset_id = 0, }; - NVME_ARGS(opts, - OPT_SHRT("nvmset_id", 'i', &cfg.nvmset_id, nvmset_id)); + OPT_ARGS(opts) = { + OPT_SHRT("nvmset_id", 'i', &cfg.nvmset_id, nvmset_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -770,8 +795,10 @@ static int id_uuid(int argc, char **argv, struct command *acmd, struct plugin *p .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -823,8 +850,10 @@ static int id_iocs(int argc, char **argv, struct command *acmd, struct plugin *p .cntid = 0xffff, }; - NVME_ARGS(opts, - OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id)); + OPT_ARGS(opts) = { + OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -874,8 +903,10 @@ static int id_domain(int argc, char **argv, struct command *acmd, struct plugin .dom_id = 0xffff, }; - NVME_ARGS(opts, - OPT_SHRT("dom-id", 'd', &cfg.dom_id, domain_id)); + OPT_ARGS(opts) = { + OPT_SHRT("dom-id", 'd', &cfg.dom_id, domain_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -923,8 +954,10 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct .cntlid = 0, }; - NVME_ARGS(opts, - OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid)); + OPT_ARGS(opts) = { + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -975,9 +1008,11 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *acmd, stru .num_entries = ARRAY_SIZE(sc_list->sc_entry), }; - NVME_ARGS(opts, - OPT_SHRT("cntid", 'c', &cfg.cntid, controller), - OPT_UINT("num-entries", 'e', &cfg.num_entries, num_entries)); + OPT_ARGS(opts) = { + OPT_SHRT("cntid", 'c', &cfg.cntid, controller), + OPT_UINT("num-entries", 'e', &cfg.num_entries, num_entries), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/innogrit/innogrit-nvme.c b/plugins/innogrit/innogrit-nvme.c index f60697cf16..5839813983 100644 --- a/plugins/innogrit/innogrit-nvme.c +++ b/plugins/innogrit/innogrit-nvme.c @@ -181,7 +181,9 @@ static int innogrit_geteventlog(int argc, char **argv, time_t timep; int ret = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -227,7 +229,9 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, time_t timep; int ret = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/inspur/inspur-nvme.c b/plugins/inspur/inspur-nvme.c index 864536d6f2..4fd77519d6 100644 --- a/plugins/inspur/inspur-nvme.c +++ b/plugins/inspur/inspur-nvme.c @@ -213,7 +213,9 @@ static int nvme_get_vendor_log(int argc, char **argv, struct command *acmd, stru __u8 local_mem[BYTE_OF_4K]; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index c5e738fb48..23b68456bc 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -362,11 +362,13 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + OPT_FLAG_JSON("json", 'j', &cfg.json, json), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -406,8 +408,10 @@ static int get_market_log(int argc, char **argv, struct command *acmd, struct pl struct config cfg = { }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -466,8 +470,10 @@ static int get_temp_stats_log(int argc, char **argv, struct command *acmd, struc struct config cfg = { }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1055,10 +1061,12 @@ static int get_lat_stats_log(int argc, char **argv, struct command *acmd, struct struct config cfg = { }; - NVME_ARGS(opts, - OPT_FLAG("write", 'w', &cfg.write, write), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG_JSON("json", 'j', &cfg.json, json), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1376,14 +1384,16 @@ static int get_internal_log(int argc, char **argv, struct command *acmd, .core = -1 }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("log", 'l', &cfg.log, log), OPT_INT("region", 'r', &cfg.core, core), OPT_INT("nlognum", 'm', &cfg.lnum, nlognum), OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), OPT_FILE("output-file", 'O', &cfg.file, file), - OPT_FLAG("verbose-nlog", 'V', &cfg.verbose, verbose)); + OPT_FLAG("verbose-nlog", 'V', &cfg.verbose, verbose), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1558,9 +1568,11 @@ static int enable_lat_stats_tracking(int argc, char **argv, .disable = false, }; - NVME_ARGS(opts, - OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), - OPT_FLAG("disable", 'd', &cfg.disable, disable_desc)); + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), + OPT_FLAG("disable", 'd', &cfg.disable, disable_desc), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1637,10 +1649,12 @@ static int set_lat_stats_thresholds(int argc, char **argv, .bucket_thresholds = "", }; - NVME_ARGS(opts, - OPT_FLAG("write", 'w', &cfg.write, write), + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), OPT_LIST("bucket-thresholds", 't', &cfg.bucket_thresholds, - bucket_thresholds)); + bucket_thresholds), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/io-mgmt/io-mgmt-plugin.c b/plugins/io-mgmt/io-mgmt-plugin.c index 557fc6a567..e57a795ece 100644 --- a/plugins/io-mgmt/io-mgmt-plugin.c +++ b/plugins/io-mgmt/io-mgmt-plugin.c @@ -56,13 +56,15 @@ static int io_mgmt_send(int argc, char **argv, struct command *acmd, struct plug .data_len = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_id_desired, nvme_nsid_values), - OPT_SHRT("mos", 's', &cfg.mos, mos), - OPT_BYTE("mo", 'm', &cfg.mo, mo), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_SHRT("mos", 's', &cfg.mos, mos), + OPT_BYTE("mo", 'm', &cfg.mo, mo), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -137,13 +139,15 @@ static int io_mgmt_recv(int argc, char **argv, struct command *acmd, struct plug .data_len = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_id_desired, nvme_nsid_values), - OPT_SHRT("mos", 's', &cfg.mos, mos), - OPT_BYTE("mo", 'm', &cfg.mo, mo), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_UINT("data-len", 'l', &cfg.data_len, buf_len)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_SHRT("mos", 's', &cfg.mos, mos), + OPT_BYTE("mo", 'm', &cfg.mo, mo), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/keys/keys-plugin.c b/plugins/keys/keys-plugin.c index 9b58b736d2..2376bab100 100644 --- a/plugins/keys/keys-plugin.c +++ b/plugins/keys/keys-plugin.c @@ -75,10 +75,12 @@ static int gen_kxchap(int argc, char **argv, struct command *acmd, struct plugin .hmac = 0, }; - NVME_ARGS(opts, - OPT_STR("secret", 's', &cfg.secret, secret), - OPT_UINT("secret-length", 'l', &cfg.secret_len, secret_len), - OPT_UINT("hmac", 'm', &cfg.hmac, hmac)); + OPT_ARGS(opts) = { + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_UINT("secret-length", 'l', &cfg.secret_len, secret_len), + OPT_UINT("hmac", 'm', &cfg.hmac, hmac), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -251,11 +253,13 @@ static int check_kxchap(int argc, char **argv, struct command *acmd, struct plug .identity = NULL, }; - NVME_ARGS(opts, - OPT_STR("keydata", 'd', &cfg.keydata, keydata), - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("identity", 'i', &cfg.identity, identity)); + OPT_ARGS(opts) = { + OPT_STR("keydata", 'd', &cfg.keydata, keydata), + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("identity", 'i', &cfg.identity, identity), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -480,17 +484,19 @@ static int gen_tls(int argc, char **argv, struct command *acmd, struct plugin *p .compat = false, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), - OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), - OPT_STR("secret", 's', &cfg.secret, secret), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), - OPT_BYTE("hmac", 'm', &cfg.hmac, hmac), - OPT_BYTE("identity", 'I', &cfg.version, version), - OPT_FLAG("insert", 'i', &cfg.insert, insert), - OPT_FLAG("compat", 'C', &cfg.compat, compat)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), + OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_BYTE("hmac", 'm', &cfg.hmac, hmac), + OPT_BYTE("identity", 'I', &cfg.version, version), + OPT_FLAG("insert", 'i', &cfg.insert, insert), + OPT_FLAG("compat", 'C', &cfg.compat, compat), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -589,14 +595,16 @@ static int check_tls(int argc, char **argv, struct command *acmd, struct plugin .compat = false, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), - OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), - OPT_STR("keydata", 'd', &cfg.keydata, keydata), - OPT_BYTE("identity", 'I', &cfg.identity, identity), - OPT_FLAG("compat", 'C', &cfg.compat, compat)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), + OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), + OPT_STR("keydata", 'd', &cfg.keydata, keydata), + OPT_BYTE("identity", 'I', &cfg.identity, identity), + OPT_FLAG("compat", 'C', &cfg.compat, compat), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -721,15 +729,17 @@ static int insert_tls(int argc, char **argv, struct command *acmd, struct plugin .compat = false, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), - OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), - OPT_STR("keydata", 'd', &cfg.keydata, keydata), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), - OPT_BYTE("identity", 'I', &cfg.identity, identity), - OPT_FLAG("compat", 'C', &cfg.compat, compat)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), + OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), + OPT_STR("keydata", 'd', &cfg.keydata, keydata), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_BYTE("identity", 'I', &cfg.identity, identity), + OPT_FLAG("compat", 'C', &cfg.compat, compat), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -823,9 +833,11 @@ static int key_export(int argc, char **argv, struct command *acmd, struct plugin .keyfile = NULL, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -967,11 +979,13 @@ static int key_import(int argc, char **argv, struct command *acmd, struct plugin .identity = NULL, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), - OPT_STR("keydata", 'd', &cfg.keydata, keydata), - OPT_STR("identity", 'i', &cfg.identity, identity)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_STR("keydata", 'd', &cfg.keydata, keydata), + OPT_STR("identity", 'i', &cfg.identity, identity), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -1060,10 +1074,12 @@ static int key_revoke(int argc, char **argv, struct command *acmd, struct plugin .identity = NULL, }; - NVME_ARGS(opts, - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("identity", 'i', &cfg.identity, identity)); + OPT_ARGS(opts) = { + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_STR("identity", 'i', &cfg.identity, identity), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/plugins/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index 532ef9ce60..aad8212aa0 100644 --- a/plugins/lm/lm-nvme.c +++ b/plugins/lm/lm-nvme.c @@ -72,11 +72,13 @@ static int lm_create_cdq(int argc, char **argv, struct command *acmd, struct plu .file = NULL, }; - NVME_ARGS(opts, - OPT_UINT("size", 's', &cfg.sz, sz), - OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), - OPT_BYTE("queue-type", 'q', &cfg.qt, qt), - OPT_FLAG("consent", 0, &cfg.consent, consent)); + OPT_ARGS(opts) = { + OPT_UINT("size", 's', &cfg.sz, sz), + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_BYTE("queue-type", 'q', &cfg.qt, qt), + OPT_FLAG("consent", 0, &cfg.consent, consent), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -128,8 +130,10 @@ static int lm_delete_cdq(int argc, char **argv, struct command *acmd, struct plu .cdqid = 0 }; - NVME_ARGS(opts, - OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid)); + OPT_ARGS(opts) = { + OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -186,12 +190,14 @@ static int lm_track_send(int argc, char **argv, struct command *acmd, struct plu .stop = false, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_BYTE("mos", 'm', &cfg.mos, mos), - OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), - OPT_FLAG("start", 0, &cfg.start, start), - OPT_FLAG("stop", 0, &cfg.stop, stop)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("mos", 'm', &cfg.mos, mos), + OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), + OPT_FLAG("start", 0, &cfg.start, start), + OPT_FLAG("stop", 0, &cfg.stop, stop), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -299,18 +305,20 @@ static int lm_migration_send(int argc, char **argv, struct command *acmd, struct .dudmq = false }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), - OPT_BYTE("stype", 't', &cfg.stype, stype), - OPT_FLAG("dudmq", 'd', &cfg.dudmq, dudmq), - OPT_BYTE("seq-ind", 'S', &cfg.seqind, seqind), - OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi), - OPT_BYTE("csvi", 'V', &cfg.csvi, csvi), - OPT_BYTE("uidx", 'u', &cfg.uidx, uidx), - OPT_LONG("offset", 'O', &cfg.offset, offset), - OPT_UINT("numd", 'n', &cfg.numd, numd), - OPT_FILE("input-file", 'f', &cfg.input, input)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_BYTE("stype", 't', &cfg.stype, stype), + OPT_FLAG("dudmq", 'd', &cfg.dudmq, dudmq), + OPT_BYTE("seq-ind", 'S', &cfg.seqind, seqind), + OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi), + OPT_BYTE("csvi", 'V', &cfg.csvi, csvi), + OPT_BYTE("uidx", 'u', &cfg.uidx, uidx), + OPT_LONG("offset", 'O', &cfg.offset, offset), + OPT_UINT("numd", 'n', &cfg.numd, numd), + OPT_FILE("input-file", 'f', &cfg.input, input), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -424,15 +432,17 @@ static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct .output = NULL, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), - OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi), - OPT_BYTE("csvi", 'V', &cfg.csvi, csvi), - OPT_BYTE("uidx", 'u', &cfg.uidx, uidx), - OPT_LONG("offset", 'O', &cfg.offset, offset), - OPT_UINT("numd", 'n', &cfg.numd, numd), - OPT_FILE("output-file", 'f', &cfg.output, output)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi), + OPT_BYTE("csvi", 'V', &cfg.csvi, csvi), + OPT_BYTE("uidx", 'u', &cfg.uidx, uidx), + OPT_LONG("offset", 'O', &cfg.offset, offset), + OPT_UINT("numd", 'n', &cfg.numd, numd), + OPT_FILE("output-file", 'f', &cfg.output, output), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -525,10 +535,12 @@ static int lm_set_cdq(int argc, char **argv, struct command *acmd, struct plugin .tpt = 0, }; - NVME_ARGS(opts, - OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), - OPT_UINT("hp", 0, &cfg.hp, hp), - OPT_UINT("tpt", 'T', &cfg.tpt, tpt)); + OPT_ARGS(opts) = { + OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), + OPT_UINT("hp", 0, &cfg.hp, hp), + OPT_UINT("tpt", 'T', &cfg.tpt, tpt), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -567,8 +579,10 @@ static int lm_get_cdq(int argc, char **argv, struct command *acmd, struct plugin .cdqid = 0, }; - NVME_ARGS(opts, - OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid)); + OPT_ARGS(opts) = { + OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 7780310b3c..28f9f44b3f 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -83,7 +83,9 @@ static int get_supported_log_pages(int argc, char **argv, struct command *acmd, nvme_print_flags_t flags; int err = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -135,10 +137,12 @@ static int get_smart_log(int argc, char **argv, struct command *acmd, struct plu .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, - nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + nvme_nsid_values), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -193,8 +197,10 @@ static int get_ana_log(int argc, char **argv, struct command *acmd, .groups = false, }; - NVME_ARGS(opts, - OPT_FLAG("groups", 'g', &cfg.groups, groups)); + OPT_ARGS(opts) = { + OPT_FLAG("groups", 'g', &cfg.groups, groups), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -475,14 +481,16 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, .mcda = 0, }; - NVME_ARGS(opts, - OPT_FILE("output-file", 'O', &cfg.file_name, fname), - OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen), - OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), - OPT_UINT("data-area", 'd', &cfg.data_area, dgen), - OPT_FLAG("rae", 'r', &cfg.rae, rae), - OPT_BYTE("mcda", 'm', &cfg.mcda, mcda), - OPT_FLAG("force", 0, &cfg.force, force)); + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'O', &cfg.file_name, fname), + OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen), + OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), + OPT_UINT("data-area", 'd', &cfg.data_area, dgen), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_BYTE("mcda", 'm', &cfg.mcda, mcda), + OPT_FLAG("force", 0, &cfg.force, force), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -601,8 +609,10 @@ static int get_endurance_log(int argc, char **argv, struct command *acmd, struct .group_id = 0, }; - NVME_ARGS(opts, - OPT_SHRT("group-id", 'g', &cfg.group_id, group_id)); + OPT_ARGS(opts) = { + OPT_SHRT("group-id", 'g', &cfg.group_id, group_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -676,9 +686,11 @@ static int get_effects_log(int argc, char **argv, struct command *acmd, struct p .csi = -1, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_log), - OPT_INT("csi", 'c', &cfg.csi, csi)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_log), + OPT_INT("csi", 'c', &cfg.csi, csi), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -773,18 +785,20 @@ static int get_error_log(int argc, char **argv, struct command *acmd, struct plu .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG("valid-entry", 'V', &cfg.flt.valid, valid_entry), - OPT_SHRT("sqid", 'S', &cfg.flt.sqid, sqid), - OPT_SHRT("status", 's', &cfg.flt.status, status), - OPT_SUFFIX("lba", 'l', &cfg.flt.lba, lba), - OPT_UINT("namespace-id", 'n', &cfg.flt.nsid, nsid, - nvme_nsid_values), - OPT_BYTE("trtype", 't', &cfg.flt.trtype, trtype), - OPT_BYTE("csi", 'c', &cfg.flt.csi, csi), - OPT_BYTE("opcode", 'O', &cfg.flt.opcode, opcode)); + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("valid-entry", 'V', &cfg.flt.valid, valid_entry), + OPT_SHRT("sqid", 'S', &cfg.flt.sqid, sqid), + OPT_SHRT("status", 's', &cfg.flt.status, status), + OPT_SUFFIX("lba", 'l', &cfg.flt.lba, lba), + OPT_UINT("namespace-id", 'n', &cfg.flt.nsid, nsid, + nvme_nsid_values), + OPT_BYTE("trtype", 't', &cfg.flt.trtype, trtype), + OPT_BYTE("csi", 'c', &cfg.flt.csi, csi), + OPT_BYTE("opcode", 'O', &cfg.flt.opcode, opcode), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -849,8 +863,10 @@ static int get_fw_log(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -897,8 +913,10 @@ static int get_changed_ns_list_log(int argc, char **argv, bool alloc) .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output), + OPT_END() + }; if (asprintf(&desc, "Retrieve Changed %s Namespaces log for the given device %s", alloc ? "Allocated" : "Attached", @@ -976,9 +994,11 @@ static int get_pred_lat_per_nvmset_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_SHRT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1035,10 +1055,12 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), - OPT_FLAG("rae", 'r', &cfg.rae, rae), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1116,10 +1138,12 @@ static int get_persistent_event_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_BYTE("action", 'a', &cfg.action, action), - OPT_UINT("log_len", 'l', &cfg.log_len, log_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_BYTE("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1224,10 +1248,12 @@ static int get_endurance_event_agg_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), - OPT_FLAG("rae", 'r', &cfg.rae, rae), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1300,8 +1326,10 @@ static int get_lba_status_log(int argc, char **argv, .rae = false, }; - NVME_ARGS(opts, - OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1349,7 +1377,9 @@ static int get_resv_notif_log(int argc, char **argv, nvme_print_flags_t flags; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1402,9 +1432,11 @@ static int get_boot_part_log(int argc, char **argv, struct command *acmd, struct .file_name = NULL, }; - NVME_ARGS(opts, - OPT_BYTE("lsp", 's', &cfg.lsp, lsp), - OPT_FILE("output-file", 'f', &cfg.file_name, fname)); + OPT_ARGS(opts) = { + OPT_BYTE("lsp", 's', &cfg.lsp, lsp), + OPT_FILE("output-file", 'f', &cfg.file_name, fname), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1491,9 +1523,11 @@ static int get_phy_rx_eom_log(int argc, char **argv, struct command *acmd, .controller = NVME_LOG_LSI_NONE, }; - NVME_ARGS(opts, - OPT_BYTE("lsp", 's', &cfg.lsp, lsp), - OPT_SHRT("controller", 'c', &cfg.controller, controller)); + OPT_ARGS(opts) = { + OPT_BYTE("lsp", 's', &cfg.lsp, lsp), + OPT_SHRT("controller", 'c', &cfg.controller, controller), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1634,9 +1668,11 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1756,9 +1792,11 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_SHRT("domain-id", 'd', &cfg.domainid, domainid), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1803,9 +1841,11 @@ static int sanitize_log(int argc, char **argv, struct command *acmd, struct plug .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("rae", 'r', &cfg.rae, rae), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_log)); + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_log), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1848,7 +1888,9 @@ static int get_fid_support_effects_log(int argc, char **argv, struct command *ac nvme_print_flags_t flags; int err = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1889,7 +1931,9 @@ static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command nvme_print_flags_t flags; int err = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1938,8 +1982,10 @@ static int self_test_log(int argc, char **argv, struct command *acmd, struct plu .dst_entries = NVME_LOG_ST_MAX_RESULTS, }; - NVME_ARGS(opts, - OPT_BYTE("dst-entries", 'e', &cfg.dst_entries, dst_entries)); + OPT_ARGS(opts) = { + OPT_BYTE("dst-entries", 'e', &cfg.dst_entries, dst_entries), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1978,7 +2024,9 @@ static int get_mgmt_addr_list_log(int argc, char **argv, struct command *acmd, s __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2022,8 +2070,10 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * .endgid = 0, }; - NVME_ARGS(opts, - OPT_SHRT("endg-id", 'e', &cfg.endgid, endgid)); + OPT_ARGS(opts) = { + OPT_SHRT("endg-id", 'e', &cfg.endgid, endgid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2107,8 +2157,11 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct .namespace_id = 1, }; - NVME_ARGS(opts, OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2151,8 +2204,10 @@ static int get_power_measurement_log(int argc, char **argv, struct command *acmd .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2330,9 +2385,11 @@ static int get_reachability_groups_log(int argc, char **argv, struct command *ac .rae = false, }; - NVME_ARGS(opts, - OPT_FLAG("groups-only", 'g', &cfg.rgo, rgo), - OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("groups-only", 'g', &cfg.rgo, rgo), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2439,9 +2496,11 @@ static int get_reachability_associations_log(int argc, char **argv, struct comma .rae = false, }; - NVME_ARGS(opts, - OPT_FLAG("associations-only", 'a', &cfg.rao, rao), - OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("associations-only", 'a', &cfg.rao, rao), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2517,9 +2576,11 @@ static int get_host_discovery_log(int argc, char **argv, struct command *acmd, s .rae = false, }; - NVME_ARGS(opts, - OPT_FLAG("all-host-entries", 'a', &cfg.allhoste, allhoste), - OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("all-host-entries", 'a', &cfg.allhoste, allhoste), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -2593,7 +2654,10 @@ static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, st .rae = false, }; - NVME_ARGS(opts, OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2667,7 +2731,10 @@ static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acm .rae = false, }; - NVME_ARGS(opts, OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 956221d5ff..5fa6f00a76 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -438,10 +438,12 @@ static int mb_get_additional_smart_log(int argc, char **argv, struct command *ac .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -488,7 +490,9 @@ static int mb_get_powermanager_status(int argc, char **argv, struct command *acm __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -527,9 +531,11 @@ static int mb_set_powermanager_status(int argc, char **argv, struct command *acm .save = 0, }; - NVME_ARGS(opts, - OPT_UINT("value", 'V', &cfg.value, value), - OPT_FLAG("save", 's', &cfg.save, save)); + OPT_ARGS(opts) = { + OPT_UINT("value", 'V', &cfg.value, value), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -576,8 +582,10 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *acmd, .value = 0, }; - NVME_ARGS(opts, - OPT_LIST("param", 'p', &cfg.param, param)); + OPT_ARGS(opts) = { + OPT_LIST("param", 'p', &cfg.param, param), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -702,7 +710,9 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *acmd FILE *fdi = NULL; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -779,9 +789,11 @@ static int mb_selective_download(int argc, char **argv, struct command *acmd, st .select = 0, }; - NVME_ARGS(opts, - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_ENUM("select", 's', &cfg.select, select, selects)); + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_ENUM("select", 's', &cfg.select, select, selects), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -969,8 +981,10 @@ static int mb_lat_stats_log_print(int argc, char **argv, struct command *acmd, s .write = 0, }; - NVME_ARGS(opts, - OPT_FLAG("write", 'w', &cfg.write, write)); + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1010,7 +1024,9 @@ static int memblaze_clear_error_log(int argc, char **argv, struct command *acmd, .save = 0, }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1057,9 +1073,11 @@ static int mb_set_lat_stats(int argc, char **argv, struct command *acmd, struct .disable = false, }; - NVME_ARGS(opts, - OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), - OPT_FLAG("disable", 'd', &cfg.disable, disable_desc)); + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', &cfg.enable, enable_desc), + OPT_FLAG("disable", 'd', &cfg.disable, disable_desc), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -1225,6 +1243,12 @@ struct __packed performance_stats { static int mb_set_latency_feature(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + const char *read_threshold = + "read high latency log threshold, 0-based, unit is 10ms"; + const char *write_threshold = + "write high latency log threshold, 0-based, unit is 10ms"; + const char *trim_threshold = + "trim high latency log threshold, 0-based, unit is 10ms"; int err = 0; // Get the configuration @@ -1239,23 +1263,25 @@ static int mb_set_latency_feature(int argc, char **argv, struct command *acmd, s struct config cfg = {0}; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("sel-perf-log", 's', &cfg.perf_monitor, "Select features to turn on, default: Disable\n" " bit 0: latency statistics\n" " bit 1: high latency log\n" " bit 2: Performance stat"), OPT_UINT("set-commands-mask", 'm', &cfg.cmd_mask, - "Set Enable, default: Disable\n" - " bit 0: Read commands\n" - " bit 1: high Write commands\n" - " bit 2: De-allocate/TRIM (this bit is not worked for Performance stat.)"), + "Set Enable, default: Disable\n" + " bit 0: Read commands\n" + " bit 1: high Write commands\n" + " bit 2: Deallocate/TRIM (no Performance stat)"), OPT_UINT("set-read-threshold", 'r', &cfg.read_threshold, - "set read high latency log threshold, it's a 0-based value and unit is 10ms"), + read_threshold), OPT_UINT("set-write-threshold", 'w', &cfg.write_threshold, - "set write high latency log threshold, it's a 0-based value and unit is 10ms"), - OPT_UINT("set-trim-threshold", 't', &cfg.de_allocate_trim_threshold, - "set trim high latency log threshold, it's a 0-based value and unit is 10ms")); + write_threshold), + OPT_UINT("set-trim-threshold", 't', + &cfg.de_allocate_trim_threshold, trim_threshold), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1295,7 +1321,9 @@ static int mb_get_latency_feature(int argc, char **argv, struct command *acmd, s // Get the configuration - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1445,11 +1473,11 @@ static int mb_get_latency_stats(int argc, char **argv, struct command *acmd, str struct config cfg = {0}; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", - 'b', - &cfg.raw_binary, - "dump the whole log buffer in binary format")); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1550,11 +1578,11 @@ static int mb_get_high_latency_log(int argc, char **argv, struct command *acmd, struct config cfg = {0}; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", - 'b', - &cfg.raw_binary, - "dump the whole log buffer in binary format")); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1783,6 +1811,8 @@ static void performance_stats_print(struct performance_stats *log, const char *d static int mb_get_performance_stats(int argc, char **argv, struct command *acmd, struct plugin *plugin) { + const char *duration = + "[1-24] hours: duration of the log to print, default 1 hour"; // Get the configuration struct config { @@ -1792,15 +1822,12 @@ static int mb_get_performance_stats(int argc, char **argv, struct command *acmd, struct config cfg = {.duration = 1, .raw_binary = false}; - NVME_ARGS(opts, - OPT_UINT("duration", - 'd', - &cfg.duration, - "[1-24] hours: duration of the log to be printed, default is 1 hour"), - OPT_FLAG("raw-binary", - 'b', - &cfg.raw_binary, - "dump the whole log buffer in binary format")); + OPT_ARGS(opts) = { + OPT_UINT("duration", 'd', &cfg.duration, duration), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 23b5a245ee..ef93fc381f 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -637,9 +637,11 @@ static int micron_selective_download(int argc, char **argv, .select = 0, }; - NVME_ARGS(opts, - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_ENUM("select", 's', &cfg.select, select, selects)); + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_ENUM("select", 's', &cfg.select, select, selects), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -744,10 +746,12 @@ static int micron_smbus_option(int argc, char **argv, .status = 0, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_ENUM("option", 'O', &opt.option, option, micron_options), - OPT_UINT("value", 'V', &opt.value, value), - OPT_UINT("save", 's', &opt.save, save)); + OPT_UINT("value", 'V', &opt.value, value), + OPT_UINT("save", 's', &opt.save, save), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -803,7 +807,9 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -932,7 +938,9 @@ static int micron_pcie_stats(int argc, char **argv, __u32 correctable_errors = 0; __u32 uncorrectable_errors = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1032,7 +1040,9 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, __u64 result = 0; __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -1670,7 +1680,9 @@ static int micron_nand_stats(int argc, char **argv, nsze_from_oacs = false; nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1794,7 +1806,9 @@ static int micron_smart_ext_log(int argc, char **argv, bool is_json = false; nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1835,7 +1849,9 @@ static int micron_work_load_log(int argc, char **argv, struct command *acmd, str bool is_json = false; nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -1874,7 +1890,9 @@ static int micron_vendor_telemetry_log(int argc, char **argv, nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -2610,7 +2628,9 @@ static int micron_drive_info(int argc, char **argv, struct command *acmd, nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -2755,7 +2775,9 @@ static int micron_cloud_ssd_plugin_version(int argc, char **argv, const char *desc = "Prints the Micron cloud SSD plugin version."; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -2772,7 +2794,9 @@ static int micron_plugin_version(int argc, char **argv, struct command *acmd, const char *desc = "Prints the Micron plugin version."; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -2944,7 +2968,9 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *a nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -3057,10 +3083,12 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *acm .threshold = 0 }; - NVME_ARGS(opts, - OPT_ENUM("option", 'O', &opt.option, option, micron_options), + OPT_ARGS(opts) = { + OPT_ENUM("option", 'O', &opt.option, option, micron_options), OPT_ENUM("command", 'c', &opt.command, cmdstr, micron_commands), - OPT_UINT("threshold", 't', &opt.threshold, thrtime)); + OPT_UINT("threshold", 't', &opt.threshold, thrtime), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); @@ -3187,7 +3215,9 @@ static int micron_latency_stats_logs(int argc, char **argv, struct command *acmd int err = -1; const char *desc = "Display Latency tracking log information"; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -3256,9 +3286,10 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *acmd uint64_t *cmd_stats = &log.all_cmds[0]; char *cmd_str = "All"; - NVME_ARGS(opts, - OPT_ENUM("command", 'c', &opt.command, cmdstr, - micron_commands)); + OPT_ARGS(opts) = { + OPT_ENUM("command", 'c', &opt.command, cmdstr, micron_commands), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -3317,7 +3348,9 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *a nvme_print_flags_t format; int err = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -3375,7 +3408,9 @@ static int micron_clr_fw_activation_history(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; int err = 0; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); @@ -3423,9 +3458,11 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, .select = 0, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_ENUM("option", 'O', &opt.option, option, micron_options), - OPT_UINT("select", 's', &opt.select, select)); + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -3723,10 +3760,12 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, .log = 0x07, }; - NVME_ARGS(opts, - OPT_ENUM("type", 't', &cfg.type, type, types), + OPT_ARGS(opts) = { + OPT_ENUM("type", 't', &cfg.type, type, types), OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_UINT("data_area", 'd', &cfg.data_area, data_area)); + OPT_UINT("data_area", 'd', &cfg.data_area, data_area), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4025,7 +4064,9 @@ static int micron_logpage_dir(int argc, char **argv, struct command *acmd, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int i; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -4091,7 +4132,9 @@ static int micron_cloud_boot_SSD_version(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4143,7 +4186,9 @@ static int micron_device_waf(int argc, char **argv, struct command *acmd, long double tlc_units_written, slc_units_written; long double data_units_written, write_amplification_factor; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4198,7 +4243,9 @@ static int micron_cloud_log(int argc, char **argv, struct command *acmd, bool is_json = false; nvme_print_flags_t format; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4429,7 +4476,9 @@ static int micron_health_info(int argc, char **argv, struct command *acmd, nvme_print_flags_t format; int err = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4496,7 +4545,9 @@ static int micron_id_ctrl(int argc, char **argv, struct command *acmd, nvme_print_flags_t flags; int err = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 69edf7b5e8..2e9823b246 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -664,11 +664,16 @@ int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin nvme_print_flags_t flags; int ret; - NVME_ARGS(opts, - OPT_FLAG("subsystem", 's', &show_subsys, "show NBFT subsystems"), - OPT_FLAG("hfi", 0, &show_hfi, "show NBFT HFIs"), - OPT_FLAG("discovery", 'd', &show_discovery, "show NBFT discovery controllers"), - OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables")); + OPT_ARGS(opts) = { + OPT_FLAG("subsystem", 's', &show_subsys, + "show NBFT subsystems"), + OPT_FLAG("hfi", 0, &show_hfi, "show NBFT HFIs"), + OPT_FLAG("discovery", 'd', &show_discovery, + "show NBFT discovery controllers"), + OPT_STRING("nbft-path", 0, "STR", &nbft_path, + "user-defined path for NBFT tables"), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index c4d24f855f..14920ae9ca 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -969,7 +969,9 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, int num_smdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -1066,7 +1068,9 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, int num_ontapdevices = 0; struct libnvme_transport_handle *hdl; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/plugins/ns/ns-plugin.c b/plugins/ns/ns-plugin.c index 92cd10295b..6e89f6f47c 100644 --- a/plugins/ns/ns-plugin.c +++ b/plugins/ns/ns-plugin.c @@ -88,10 +88,12 @@ static int delete_ns(int argc, char **argv, struct command *acmd, struct plugin .namespace_id = 0, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, - nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -143,11 +145,13 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s .cntlist = "", }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, - nvme_nsid_values), - OPT_LIST("controllers", 'c', &cfg.cntlist, cont)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + nvme_nsid_values), + OPT_LIST("controllers", 'c', &cfg.cntlist, cont), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -404,27 +408,29 @@ static int create_ns(int argc, char **argv, struct command *acmd, struct plugin .phndls = "", }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_SUFFIX("nsze", 's', &cfg.nsze, nsze), - OPT_SUFFIX("ncap", 'c', &cfg.ncap, ncap), - OPT_BYTE("flbas", 'f', &cfg.flbas, flbas), - OPT_BYTE("dps", 'd', &cfg.dps, dps), - OPT_BYTE("nmic", 'm', &cfg.nmic, nmic), - OPT_UINT("anagrp-id", 'a', &cfg.anagrpid, anagrpid), - OPT_SHRT("nvmset-id", 'i', &cfg.nvmsetid, nvmsetid), - OPT_SHRT("endg-id", 'e', &cfg.endgid, endgid), - OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), - OPT_BYTE("csi", 'y', &cfg.csi, csi), - OPT_SUFFIX("lbstm", 'l', &cfg.lbstm, lbstm), - OPT_SHRT("nphndls", 'n', &cfg.nphndls, nphndls), - OPT_STR("nsze-si", 'S', &cfg.nsze_si, nsze_si), - OPT_STR("ncap-si", 'C', &cfg.ncap_si, ncap_si), - OPT_FLAG("azr", 'z', &cfg.azr, azr), - OPT_UINT("rar", 'r', &cfg.rar, rar), - OPT_UINT("ror", 'O', &cfg.ror, ror), - OPT_UINT("rnumzrwa", 'u', &cfg.rnumzrwa, rnumzrwa), - OPT_LIST("phndls", 'p', &cfg.phndls, phndls)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_SUFFIX("nsze", 's', &cfg.nsze, nsze), + OPT_SUFFIX("ncap", 'c', &cfg.ncap, ncap), + OPT_BYTE("flbas", 'f', &cfg.flbas, flbas), + OPT_BYTE("dps", 'd', &cfg.dps, dps), + OPT_BYTE("nmic", 'm', &cfg.nmic, nmic), + OPT_UINT("anagrp-id", 'a', &cfg.anagrpid, anagrpid), + OPT_SHRT("nvmset-id", 'i', &cfg.nvmsetid, nvmsetid), + OPT_SHRT("endg-id", 'e', &cfg.endgid, endgid), + OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), + OPT_BYTE("csi", 'y', &cfg.csi, csi), + OPT_SUFFIX("lbstm", 'l', &cfg.lbstm, lbstm), + OPT_SHRT("nphndls", 'n', &cfg.nphndls, nphndls), + OPT_STR("nsze-si", 'S', &cfg.nsze_si, nsze_si), + OPT_STR("ncap-si", 'C', &cfg.ncap_si, ncap_si), + OPT_FLAG("azr", 'z', &cfg.azr, azr), + OPT_UINT("rar", 'r', &cfg.rar, rar), + OPT_UINT("ror", 'O', &cfg.ror, ror), + OPT_UINT("rnumzrwa", 'u', &cfg.rnumzrwa, rnumzrwa), + OPT_LIST("phndls", 'p', &cfg.phndls, phndls), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -588,7 +594,9 @@ static int get_ns_id(int argc, char **argv, struct command *acmd, struct plugin unsigned int nsid; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/nvme-mi/nvme-mi-plugin.c b/plugins/nvme-mi/nvme-mi-plugin.c index 61f625e38d..67b872864a 100644 --- a/plugins/nvme-mi/nvme-mi-plugin.c +++ b/plugins/nvme-mi/nvme-mi-plugin.c @@ -69,15 +69,17 @@ static int libnvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc .input_file = "", }; - NVME_ARGS(opts, - OPT_BYTE("opcode", 'O', &cfg.opcode, opcode), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_UINT("nmimt", 'm', &cfg.nmimt, nmimt), - OPT_UINT("nmd0", '0', &cfg.nmd0, nmd0), - OPT_UINT("nmd1", '1', &cfg.nmd1, nmd1), - OPT_FILE("input-file", 'i', &cfg.input_file, input)); + OPT_ARGS(opts) = { + OPT_BYTE("opcode", 'O', &cfg.opcode, opcode), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_UINT("nmimt", 'm', &cfg.nmimt, nmimt), + OPT_UINT("nmd0", '0', &cfg.nmd0, nmd0), + OPT_UINT("nmd1", '1', &cfg.nmd1, nmd1), + OPT_FILE("input-file", 'i', &cfg.input_file, input), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c index ea024dad77..a9489d3a74 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -20,6 +20,8 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid) { + const char *no_uuid = + "Skip UUID index search (UUID index not required for OCP 1.0)"; bool no_uuid_flag = false; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -29,9 +31,10 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 bool uuid = true; int err; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, - "Skip UUID index search (UUID index not required for OCP 1.0)")); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -88,11 +91,13 @@ int get_ocp_error_counters(int argc, char **argv, struct command *acmd, .nsid = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), - OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index c7a5854c23..9673fa20a1 100644 --- a/plugins/ocp/ocp-fw-activation-history.c +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -31,7 +31,9 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *acmd, { const char *desc = "Retrieves the OCP firmware activation history log."; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; diff --git a/plugins/ocp/ocp-hardware-component-log.c b/plugins/ocp/ocp-hardware-component-log.c index b8e1b054b3..6c2043302a 100644 --- a/plugins/ocp/ocp-hardware-component-log.c +++ b/plugins/ocp/ocp-hardware-component-log.c @@ -310,8 +310,11 @@ int ocp_hwcomp_log(int argc, char **argv, struct command *acmd, struct plugin *p VAL_END() }; - NVME_ARGS(opts, OPT_LONG("comp-id", 'i', &cfg.id, id_desc, id), - OPT_FLAG("list", 'l', &cfg.list, list_desc)); + OPT_ARGS(opts) = { + OPT_LONG("comp-id", 'i', &cfg.id, id_desc, id), + OPT_FLAG("list", 'l', &cfg.list, list_desc), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 094de894e8..b4fba6822c 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -272,8 +272,10 @@ static int ocp_latency_monitor_log(int argc, char **argv, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -338,7 +340,7 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, .latency_monitor_feature_enable = 0x1, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_SHRT("active_bucket_timer_threshold", 't', &cfg.active_bucket_timer_threshold, active_bucket_timer_threshold), @@ -351,8 +353,7 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, OPT_BYTE("active_threshold_d", 'd', &cfg.active_threshold_d, active_threshold_d), OPT_SHRT("active_latency_config", 'f', - &cfg.active_latency_config, - active_latency_config), + &cfg.active_latency_config, active_latency_config), OPT_BYTE("active_latency_minimum_window", 'w', &cfg.active_latency_minimum_window, active_latency_minimum_window), @@ -363,7 +364,9 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, discard_debug_log), OPT_BYTE("latency_monitor_feature_enable", 'e', &cfg.latency_monitor_feature_enable, - latency_monitor_feature_enable)); + latency_monitor_feature_enable), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -442,11 +445,13 @@ static int ocp_get_latency_monitor_feature(int argc, char **argv, struct command .nsid = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), - OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -587,11 +592,13 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *acmd, .sel = 0, }; - NVME_ARGS(opts, - OPT_BYTE("mode", 'm', &cfg.mode, mode), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_BYTE("mode", 'm', &cfg.mode, mode), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -995,15 +1002,18 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct const char *tele_log_suffix = "telemetry.bin"; bool host_behavior_changed = false; - NVME_ARGS(opts, - OPT_STR("telemetry-log", 'l', &opt.telemetry_log, telemetry_log), - OPT_STR("string-log", 's', &opt.string_log, string_log), - OPT_FILE("output-file", 'f', &opt.output_file, output_file), - OPT_INT("data-area", 'a', &opt.data_area, data_area), + OPT_ARGS(opts) = { + OPT_STR("telemetry-log", 'l', &opt.telemetry_log, + telemetry_log), + OPT_STR("string-log", 's', &opt.string_log, string_log), + OPT_FILE("output-file", 'f', &opt.output_file, output_file), + OPT_INT("data-area", 'a', &opt.data_area, data_area), OPT_ENUM("telemetry-type", 't', &tele_type, telemetry_type, tele_types), - OPT_UINT("host-generate", 'g', &host_gen, hgen), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_UINT("host-generate", 'g', &host_gen, hgen), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1228,8 +1238,10 @@ static int ocp_unsupported_requirements_log(int argc, char **argv, struct comman .output_format = "normal", }; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1331,8 +1343,10 @@ static int ocp_error_recovery_log(int argc, char **argv, struct command *acmd, s .output_format = "normal", }; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1425,8 +1439,10 @@ static int ocp_device_capabilities_log(int argc, char **argv, struct command *ac __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1488,8 +1504,10 @@ static int ocp_set_telemetry_profile_feature(int argc, char **argv, struct comma .tps = 0, }; - NVME_ARGS(opts, - OPT_BYTE("telemetry-profile-select", 't', &cfg.tps, tps)); + OPT_ARGS(opts) = { + OPT_BYTE("telemetry-profile-select", 't', &cfg.tps, tps), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1534,11 +1552,13 @@ static int ocp_get_telemetry_profile_feature(int argc, char **argv, struct comma .nsid = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), - OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1630,10 +1650,12 @@ static int set_dssd_power_state_feature(int argc, char **argv, struct command *a .save = false, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_BYTE("power-state", 'p', &cfg.power_state, power_state), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_FLAG("save", 's', &cfg.save, save), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1706,10 +1728,12 @@ static int get_dssd_power_state_feature(int argc, char **argv, struct command *a .all = false, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_FLAG("all", 'a', &all_flag, all), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_FLAG("all", 'a', &all_flag, all), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1762,11 +1786,13 @@ static int set_plp_health_check_interval(int argc, char **argv, struct command * .sv = false, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_SHRT("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), - OPT_FLAG("save", 's', &cfg.sv, sv), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_FLAG("save", 's', &cfg.sv, sv), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1815,8 +1841,10 @@ static int get_plp_health_check_interval(int argc, char **argv, struct command * .sel = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 'S', &cfg.sel, sel)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1867,9 +1895,11 @@ static int set_dssd_async_event_config(int argc, char **argv, struct command *ac .sv = false, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FLAG("enable-panic-notices", 'e', &cfg.epn, epn), - OPT_FLAG("save", 's', &cfg.sv, sv)); + OPT_FLAG("save", 's', &cfg.sv, sv), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1915,8 +1945,10 @@ static int get_dssd_async_event_config(int argc, char **argv, struct command *ac .sel = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 'S', &cfg.sel, sel)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1998,9 +2030,11 @@ static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *a .output_file = NULL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FILE("output-file", 'f', &cfg.output_file, output_file), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -2110,8 +2144,10 @@ static int ocp_tcg_configuration_log(int argc, char **argv, struct command *acmd .output_format = "normal", }; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -2226,10 +2262,12 @@ static int get_error_injection(int argc, char **argv, struct command *acmd, stru struct config cfg = { 0 }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), - OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns)); + OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2321,13 +2359,15 @@ static int set_error_injection(int argc, char **argv, struct command *acmd, stru .number = 1, }; - NVME_ARGS(opts, - OPT_FILE("data", 'd', &cfg.file, data), - OPT_BYTE("number", 'n', &cfg.number, number), - OPT_FLAG("no-uuid", 'N', &no_uuid_flag, no_uuid), - OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns), - OPT_SHRT("type", 't', &cfg.type, type), - OPT_SHRT("nrtdp", 'r', &cfg.nrtdp, nrtdp)); + OPT_ARGS(opts) = { + OPT_FILE("data", 'd', &cfg.file, data), + OPT_BYTE("number", 'n', &cfg.number, number), + OPT_FLAG("no-uuid", 'N', &no_uuid_flag, no_uuid), + OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns), + OPT_SHRT("type", 't', &cfg.type, type), + OPT_SHRT("nrtdp", 'r', &cfg.nrtdp, nrtdp), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2390,9 +2430,11 @@ static int get_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2447,10 +2489,12 @@ static int set_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts, - OPT_FLAG("enable", 'e', &enable_flag, enable), - OPT_FLAG("save", 's', &save_flag, save), - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', &enable_flag, enable), + OPT_FLAG("save", 's', &save_flag, save), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, enable_ieee1667_silo, opts); if (err) @@ -2496,10 +2540,12 @@ static int ocp_get_persistent_event_log(int argc, char **argv, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_BYTE("action", 'a', &cfg.action, action), - OPT_UINT("log_len", 'l', &cfg.log_len, log_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + OPT_ARGS(opts) = { + OPT_BYTE("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2616,11 +2662,13 @@ static int ocp_get_idle_wakeup_time_config_feature(int argc, char **argv, .nsid = 0, }; - NVME_ARGS(opts, - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), - OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid)); + OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index 6c8b2630f1..dcfc7b4e2a 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -121,8 +121,10 @@ int ocp_smart_add_log(int argc, char **argv, struct command *acmd, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret = 0; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid)); + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/registry/registry-nvme.c b/plugins/registry/registry-nvme.c index 8a9c028648..9f82b84b19 100644 --- a/plugins/registry/registry-nvme.c +++ b/plugins/registry/registry-nvme.c @@ -87,7 +87,9 @@ static int registry_list(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; if (parse_args(argc, argv, desc, opts)) return -EINVAL; @@ -115,8 +117,10 @@ static int registry_retrieve(int argc, char **argv, struct command *acmd, struct config cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("attr", 'a', "ATTR", &cfg.attr, attr_help)); + OPT_ARGS(opts) = { + OPT_STRING("attr", 'a', "ATTR", &cfg.attr, attr_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -167,9 +171,11 @@ static int registry_update(int argc, char **argv, struct command *acmd, struct config cfg = { 0 }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_STRING("attr", 'a', "ATTR", &cfg.attr, attr_help), - OPT_STRING("value", 'V', "VAL", &cfg.value, value_help)); + OPT_STRING("value", 'V', "VAL", &cfg.value, value_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -215,8 +221,10 @@ static int registry_delete(int argc, char **argv, struct command *acmd, struct config cfg = { 0 }; - NVME_ARGS(opts, - OPT_STRING("attr", 'a', "ATTR", &cfg.attr, attr_help)); + OPT_ARGS(opts) = { + OPT_STRING("attr", 'a', "ATTR", &cfg.attr, attr_help), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); if (ret) diff --git a/plugins/resv/resv-plugin.c b/plugins/resv/resv-plugin.c index 88af121ce6..7af17efed2 100644 --- a/plugins/resv/resv-plugin.c +++ b/plugins/resv/resv-plugin.c @@ -61,14 +61,16 @@ static int resv_acquire(int argc, char **argv, struct command *acmd, struct plug .iekey = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), - OPT_SUFFIX("prkey", 'p', &cfg.prkey, prkey), - OPT_BYTE("rtype", 't', &cfg.rtype, rtype), - OPT_BYTE("racqa", 'a', &cfg.racqa, racqa), - OPT_FLAG("iekey", 'i', &cfg.iekey, iekey)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), + OPT_SUFFIX("prkey", 'p', &cfg.prkey, prkey), + OPT_BYTE("rtype", 't', &cfg.rtype, rtype), + OPT_BYTE("racqa", 'a', &cfg.racqa, racqa), + OPT_FLAG("iekey", 'i', &cfg.iekey, iekey), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -131,14 +133,16 @@ static int resv_register(int argc, char **argv, struct command *acmd, struct plu .cptpl = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_desired, nvme_nsid_values), - OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), - OPT_SUFFIX("nrkey", 'k', &cfg.nrkey, nrkey), - OPT_BYTE("rrega", 'r', &cfg.rrega, rrega), - OPT_BYTE("cptpl", 'p', &cfg.cptpl, cptpl), - OPT_FLAG("iekey", 'i', &cfg.iekey, iekey)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_desired, nvme_nsid_values), + OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), + OPT_SUFFIX("nrkey", 'k', &cfg.nrkey, nrkey), + OPT_BYTE("rrega", 'r', &cfg.rrega, rrega), + OPT_BYTE("cptpl", 'p', &cfg.cptpl, cptpl), + OPT_FLAG("iekey", 'i', &cfg.iekey, iekey), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -209,13 +213,15 @@ static int resv_release(int argc, char **argv, struct command *acmd, struct plug .iekey = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_desired, nvme_nsid_values), - OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), - OPT_BYTE("rtype", 't', &cfg.rtype, rtype), - OPT_BYTE("rrela", 'a', &cfg.rrela, rrela), - OPT_FLAG("iekey", 'i', &cfg.iekey, iekey)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired, + nvme_nsid_values), + OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey), + OPT_BYTE("rtype", 't', &cfg.rtype, rtype), + OPT_BYTE("rrela", 'a', &cfg.rrela, rrela), + OPT_FLAG("iekey", 'i', &cfg.iekey, iekey), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -301,12 +307,14 @@ static int resv_report(int argc, char **argv, struct command *acmd, struct plugi .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, - namespace_id_desired, nvme_nsid_values), - OPT_UINT("numd", 'd', &cfg.numd, numd), - OPT_FLAG("eds", 'e', &cfg.eds, eds), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_UINT("numd", 'd', &cfg.numd, numd), + OPT_FLAG("eds", 'e', &cfg.eds, eds), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/rpmb/rpmb.c b/plugins/rpmb/rpmb.c index 89f2f9b705..6871d7344f 100644 --- a/plugins/rpmb/rpmb.c +++ b/plugins/rpmb/rpmb.c @@ -778,7 +778,9 @@ static int rpmb_info(int argc, char **argv, struct command *acmd, struct plugin union ctrl_rpmbs_reg regs; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -812,10 +814,12 @@ static int rpmb_program_key(int argc, char **argv, struct command *acmd, struct .target = 0, }; - NVME_ARGS(opts, - OPT_STRING("key", 'k', "key", &cfg.key, key), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + OPT_STRING("key", 'k', "key", &cfg.key, key), + OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_BYTE("target", 't', &cfg.target, target), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -857,8 +861,10 @@ static int rpmb_read_counter(int argc, char **argv, struct command *acmd, .target = 0, }; - NVME_ARGS(opts, - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + OPT_BYTE("target", 't', &cfg.target, target), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -903,11 +909,13 @@ static int rpmb_read_data(int argc, char **argv, struct command *acmd, struct pl .target = 0, }; - NVME_ARGS(opts, - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_UINT("address", 0, &cfg.address, address), - OPT_UINT("blocks", 'b', &cfg.blocks, blocks), - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_UINT("address", 0, &cfg.address, address), + OPT_UINT("blocks", 'b', &cfg.blocks, blocks), + OPT_BYTE("target", 't', &cfg.target, target), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -969,14 +977,16 @@ static int rpmb_write_data(int argc, char **argv, struct command *acmd, struct p .target = 0, }; - NVME_ARGS(opts, - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), - OPT_STRING("key", 'k', "key", &cfg.key, key), - OPT_STRING("msg", 'd', "data", &cfg.msg, msg), - OPT_UINT("address", 0, &cfg.address, address), - OPT_UINT("blocks", 'b', &cfg.blocks, blocks), - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_STRING("key", 'k', "key", &cfg.key, key), + OPT_STRING("msg", 'd', "data", &cfg.msg, msg), + OPT_UINT("address", 0, &cfg.address, address), + OPT_UINT("blocks", 'b', &cfg.blocks, blocks), + OPT_BYTE("target", 't', &cfg.target, target), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1036,8 +1046,10 @@ static int rpmb_read_config(int argc, char **argv, struct command *acmd, .msgfile = NULL, }; - NVME_ARGS(opts, - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile)); + OPT_ARGS(opts) = { + OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -1101,11 +1113,13 @@ static int rpmb_write_config(int argc, char **argv, struct command *acmd, .keyfile = NULL, }; - NVME_ARGS(opts, - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), - OPT_STRING("key", 'k', "key", &cfg.key, key), - OPT_STRING("msg", 'd', "data", &cfg.msg, msg)); + OPT_ARGS(opts) = { + OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_STRING("key", 'k', "key", &cfg.key, key), + OPT_STRING("msg", 'd', "data", &cfg.msg, msg), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; diff --git a/plugins/samsung/samsung-nvme.c b/plugins/samsung/samsung-nvme.c index 86a1e77969..9a94ba1046 100644 --- a/plugins/samsung/samsung-nvme.c +++ b/plugins/samsung/samsung-nvme.c @@ -1335,13 +1335,14 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, .hide_progress = false, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_STRING("dump-type", 't', "TYPE", &cfg.type, type), - OPT_INT("telemetry-data-area", 'a', &cfg.area, area), - OPT_FILE("output-file", 'O', &cfg.file, file), - OPT_FLAG("compress", 'z', &cfg.compress, compress), - OPT_FLAG("hide-progress", 0, &cfg.hide_progress, - hide_progress)); + OPT_INT("telemetry-data-area", 'a', &cfg.area, area), + OPT_FILE("output-file", 'O', &cfg.file, file), + OPT_FLAG("compress", 'z', &cfg.compress, compress), + OPT_FLAG("hide-progress", 0, &cfg.hide_progress, hide_progress), + OPT_END() + }; samsung_initialize(); diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index 2f660712e1..fcf722c97b 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -436,14 +436,15 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, .type = SNDK_TELEMETRY_TYPE_NONE, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FILE("output-file", 'O', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), OPT_ENUM("type", 't', &cfg.type, type, types), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), - OPT_END()); + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -610,12 +611,16 @@ static int sndk_vs_smart_add_log(int argc, char **argv, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_BYTE("interval", 'i', &cfg.interval, interval), - OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), - OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_BYTE("interval", 'i', &cfg.interval, interval), + OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, + log_page_version), + OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, + log_page_mask), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -780,8 +785,10 @@ static int sndk_drive_resize(int argc, char **argv, .size = 0, }; - NVME_ARGS(opts, - OPT_LONG("size", 's', &cfg.size, size)); + OPT_ARGS(opts) = { + OPT_LONG("size", 's', &cfg.size, size), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1127,7 +1134,9 @@ static int sndk_vs_fw_activate_history(int argc, char **argv, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1175,7 +1184,9 @@ static int sndk_clear_fw_activate_history(int argc, char **argv, __u64 capabilities = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -1245,7 +1256,9 @@ static int sndk_capabilities(int argc, char **argv, uint64_t capabilities = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 96b8807b38..e0dd61f877 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -347,11 +347,13 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG_JSON("json", 'j', &cfg.json, json), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -515,9 +517,11 @@ static int get_lat_stats_log(int argc, char **argv, struct command *acmd, struct struct config cfg = { }; - NVME_ARGS(opts, - OPT_FLAG("write", 'w', &cfg.write, write), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -654,7 +658,9 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *acmd, struct char *desc = "Get bad block table of sfx block device."; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -706,8 +712,10 @@ static int query_cap_info(int argc, char **argv, struct command *acmd, struct pl struct config cfg; int err = 0; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -833,10 +841,12 @@ static int change_cap(int argc, char **argv, struct command *acmd, struct plugin .force = 0, }; - NVME_ARGS(opts, - OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), - OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), - OPT_FLAG("force", 'f', &cfg.force, force)); + OPT_ARGS(opts) = { + OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), + OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), + OPT_FLAG("force", 'f', &cfg.force, force), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -908,12 +918,14 @@ static int sfx_set_feature(int argc, char **argv, struct command *acmd, struct p .force = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'V', &cfg.value, value), - OPT_FLAG("force", 's', &cfg.force, force)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_UINT("value", 'V', &cfg.value, value), + OPT_FLAG("force", 's', &cfg.force, force), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -996,10 +1008,12 @@ static int sfx_get_feature(int argc, char **argv, struct command *acmd, struct p .feature_id = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1332,12 +1346,15 @@ static int sfx_dump_evtlog(int argc, char **argv, struct command *acmd, struct p .output = NULL, }; - NVME_ARGS(opts, - OPT_FILE("file", 'f', &cfg.file, file), - OPT_UINT("namespace_id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("storage_medium", 's', &cfg.storage_medium, storage_medium), - OPT_FLAG("parse", 'p', &cfg.parse, parse), - OPT_FILE("output", 'O', &cfg.output, output)); + OPT_ARGS(opts) = { + OPT_FILE("file", 'f', &cfg.file, file), + OPT_UINT("namespace_id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("storage_medium", 's', &cfg.storage_medium, + storage_medium), + OPT_FLAG("parse", 'p', &cfg.parse, parse), + OPT_FILE("output", 'O', &cfg.output, output), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1468,12 +1485,16 @@ static int sfx_expand_cap(int argc, char **argv, struct command *acmd, struct pl .units = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace_id", 'n', &cfg.namespace_id, namespace_id), - OPT_LONG("namespace_size", 's', &cfg.namespace_size, namespace_size), - OPT_LONG("namespace_cap", 'c', &cfg.namespace_cap, namespace_cap), - OPT_UINT("lbaf", 'l', &cfg.lbaf, lbaf), - OPT_UINT("units", 'u', &cfg.units, units)); + OPT_ARGS(opts) = { + OPT_UINT("namespace_id", 'n', &cfg.namespace_id, namespace_id), + OPT_LONG("namespace_size", 's', &cfg.namespace_size, + namespace_size), + OPT_LONG("namespace_cap", 'c', &cfg.namespace_cap, + namespace_cap), + OPT_UINT("lbaf", 'l', &cfg.lbaf, lbaf), + OPT_UINT("units", 'u', &cfg.units, units), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1542,8 +1563,10 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin .json = false }; - NVME_ARGS(opts, - OPT_FLAG("json-print", 'j', &cfg.json, json_desc)); + OPT_ARGS(opts) = { + OPT_FLAG("json-print", 'j', &cfg.json, json_desc), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index ab87714231..d34f92be38 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -158,7 +158,9 @@ static int log_pages_supp(int argc, char **argv, struct command *acmd, nvme_print_flags_t flags; int fmt; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -907,7 +909,9 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug nvme_print_flags_t flags; int err, index = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1081,7 +1085,9 @@ static int temp_stats(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1238,7 +1244,9 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *acmd, struct int err; nvme_print_flags_t flags; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1369,7 +1377,9 @@ static int stx_vs_fw_activate_history(int argc, char **argv, struct command *acm int err; nvme_print_flags_t flags; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1423,8 +1433,10 @@ static int clear_fw_activate_history(int argc, char **argv, struct command *acmd .save = 0, }; - NVME_ARGS(opts, - OPT_FLAG("save", 's', &cfg.save, save)); + OPT_ARGS(opts) = { + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1481,8 +1493,10 @@ static int vs_clr_pcie_correctable_errs(int argc, char **argv, struct command *a .save = 0, }; - NVME_ARGS(opts, - OPT_FLAG("save", 's', &cfg.save, save)); + OPT_ARGS(opts) = { + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1544,11 +1558,13 @@ static int get_host_tele(int argc, char **argv, struct command *acmd, struct plu .log_id = 0, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), OPT_UINT("log_specific", 'i', &cfg.log_id, log_specific), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1650,10 +1666,12 @@ static int get_ctrl_tele(int argc, char **argv, struct command *acmd, struct plu .namespace_id = 0xffffffff, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1764,10 +1782,12 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, struct p .file = "", }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_FILE("dump-file", 'f', &cfg.file, file)); + OPT_FILE("dump-file", 'f', &cfg.file, file), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1847,7 +1867,9 @@ static int seagate_plugin_version(int argc, char **argv, struct command *acmd, s const char *desc = "Prints the Seagate plugin version."; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -1866,7 +1888,9 @@ static int stx_ocp_plugin_version(int argc, char **argv, struct command *acmd, s const char *desc = "Prints the Seagate OCP plugin version."; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/plugins/security/security-plugin.c b/plugins/security/security-plugin.c index 07e1ecfc8d..8b83b01aeb 100644 --- a/plugins/security/security-plugin.c +++ b/plugins/security/security-plugin.c @@ -71,15 +71,17 @@ static int sec_send(int argc, char **argv, struct command *acmd, struct plugin * .tl = 0, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_FILE("file", 'f', &cfg.file, file), - OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), - OPT_BYTE("secp", 'p', &cfg.secp, secp), - OPT_SHRT("spsp", 's', &cfg.spsp, spsp), - OPT_UINT("tl", 't', &cfg.tl, tl)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_FILE("file", 'f', &cfg.file, file), + OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), + OPT_BYTE("secp", 'p', &cfg.secp, secp), + OPT_SHRT("spsp", 's', &cfg.spsp, spsp), + OPT_UINT("tl", 't', &cfg.tl, tl), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -191,16 +193,18 @@ static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin * .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("ish", 'I', &cfg.ish, ish), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_desired, nvme_nsid_values), - OPT_UINT("size", 'x', &cfg.size, size), - OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), - OPT_BYTE("secp", 'p', &cfg.secp, secp), - OPT_SHRT("spsp", 's', &cfg.spsp, spsp), - OPT_UINT("al", 't', &cfg.al, al), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump)); + OPT_ARGS(opts) = { + OPT_FLAG("ish", 'I', &cfg.ish, ish), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_desired, nvme_nsid_values), + OPT_UINT("size", 'x', &cfg.size, size), + OPT_BYTE("nssf", 'N', &cfg.nssf, nssf), + OPT_BYTE("secp", 'p', &cfg.secp, secp), + OPT_SHRT("spsp", 's', &cfg.spsp, spsp), + OPT_UINT("al", 't', &cfg.al, al), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_dump), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index 1b234e49a8..76dcd1a65d 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -134,10 +134,12 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -201,14 +203,16 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s .data_len = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -280,14 +284,16 @@ static int set_additional_feature(int argc, char **argv, struct command *acmd, s .save = 0, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'V', &cfg.value, value), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_FLAG("save", 's', &cfg.save, save)); + OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_UINT("value", 'V', &cfg.value, value), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c index 7dedb1678b..d5e51ef022 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -78,7 +78,9 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *a int err; __u8 uuid_index; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index 81a8deffcc..778f15c834 100644 --- a/plugins/solidigm/solidigm-get-drive-info.c +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -31,7 +31,9 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu __u16 ftl_unit_size; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index ad0d298465..72d319acd1 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -879,9 +879,11 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *acmd, .type = ALL, }; - NVME_ARGS(opts, - OPT_ENUM("type", 't', &cfg.type, type, types), - OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir)); + OPT_ARGS(opts) = { + OPT_ENUM("type", 't', &cfg.type, type, types), + OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 01bd912695..41059de7ac 100644 --- a/plugins/solidigm/solidigm-latency-tracking.c +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -363,12 +363,16 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *acm .has_average_latency_field = false, }; - NVME_ARGS(opts, - OPT_FLAG("enable", 'e', <.cfg.enable, "Enable Latency Tracking"), - OPT_FLAG("disable", 'd', <.cfg.disable, "Disable Latency Tracking"), - OPT_FLAG("read", 'r', <.cfg.read, "Get read statistics"), - OPT_FLAG("write", 'w', <.cfg.write, "Get write statistics"), - OPT_BYTE("type", 't', <.cfg.type, "Log type to get")); + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', <.cfg.enable, + "Enable Latency Tracking"), + OPT_FLAG("disable", 'd', <.cfg.disable, + "Disable Latency Tracking"), + OPT_FLAG("read", 'r', <.cfg.read, "Get read statistics"), + OPT_FLAG("write", 'w', <.cfg.write, "Get write statistics"), + OPT_BYTE("type", 't', <.cfg.type, "Log type to get"), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-log-page-dir.c b/plugins/solidigm/solidigm-log-page-dir.c index 81459545e6..797f654b06 100644 --- a/plugins/solidigm/solidigm-log-page-dir.c +++ b/plugins/solidigm/solidigm-log-page-dir.c @@ -195,7 +195,9 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *a __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - NVME_ARGS(options); + OPT_ARGS(options) = { + OPT_END() + }; int err = parse_and_open(&ctx, &hdl, argc, argv, description, options); if (err) diff --git a/plugins/solidigm/solidigm-market-log.c b/plugins/solidigm/solidigm-market-log.c index ea6508ad63..8b189be058 100644 --- a/plugins/solidigm/solidigm-market-log.c +++ b/plugins/solidigm/solidigm-market-log.c @@ -37,8 +37,10 @@ int sldgm_get_market_log(int argc, char **argv, struct command *acmd, __u8 uuid_idx; bool raw_binary = false; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-ocp-version.c b/plugins/solidigm/solidigm-ocp-version.c index b05b6431e8..cd5e1674ef 100644 --- a/plugins/solidigm/solidigm-ocp-version.c +++ b/plugins/solidigm/solidigm-ocp-version.c @@ -14,7 +14,9 @@ int sldgm_ocp_version(int argc, char **argv, struct command *acmd, struct plugin { const char *desc = "Prints OCP extensions version of Solidigm plugin"; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; int err = parse_args(argc, argv, desc, opts); diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 52f8ef0529..b628960470 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -269,10 +269,11 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *acm .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - "(optional) desired namespace", - nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + "(optional) desired namespace", nvme_nsid_values), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index f610ec0cb4..10a58b7140 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -65,12 +65,14 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *acmd, stru .ctrl_init = false, }; - NVME_ARGS(opts, - OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen), - OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), - OPT_BYTE("data-area", 'd', &cfg.data_area, dgen), - OPT_FILE("config-file", 'j', &cfg.cfg_file, cfile), - OPT_FILE("source-file", 's', &cfg.binary_file, sfile)); + OPT_ARGS(opts) = { + OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen), + OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), + OPT_BYTE("data-area", 'd', &cfg.data_area, dgen), + OPT_FILE("config-file", 'j', &cfg.cfg_file, cfile), + OPT_FILE("source-file", 's', &cfg.binary_file, sfile), + OPT_END() + }; int err = parse_args(argc, argv, desc, opts); diff --git a/plugins/solidigm/solidigm-temp-stats.c b/plugins/solidigm/solidigm-temp-stats.c index d9e39993fa..b9337a07d9 100644 --- a/plugins/solidigm/solidigm-temp-stats.c +++ b/plugins/solidigm/solidigm-temp-stats.c @@ -63,8 +63,10 @@ int sldgm_get_temp_stats_log(int argc, char **argv, struct command *acmd, struct .raw_binary = false, }; - NVME_ARGS(opts, - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c index 364d3a5801..316159eb3e 100644 --- a/plugins/solidigm/solidigm-workload-tracker.c +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -543,23 +543,30 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *acmd, stru join_options(type_options, sizeof(type_options), trk_types, ARRAY_SIZE(trk_types)); join_options(sample_options, sizeof(sample_options), samplet, ARRAY_SIZE(samplet)); - NVME_ARGS(opts, - OPT_BYTE("uuid-index", 'U', &wlt.uuid_index, "specify uuid index"), - OPT_FLAG("enable", 'e', &cfg.enable, "tracker enable"), - OPT_FLAG("disable", 'd', &cfg.disable, "tracker disable"), - OPT_STRING("sample-time", 's', sample_options, &cfg.sample_time, sample_interval), - OPT_STRING("type", 't', type_options, &cfg.tracker_type, "Tracker type"), - OPT_UINT("run-time", 'r', &cfg.run_time_s, run_time), - OPT_INT("flush-freq", 'f', &cfg.flush_frequency, flush_frequency), + OPT_ARGS(opts) = { + OPT_BYTE("uuid-index", 'U', &wlt.uuid_index, + "specify uuid index"), + OPT_FLAG("enable", 'e', &cfg.enable, "tracker enable"), + OPT_FLAG("disable", 'd', &cfg.disable, "tracker disable"), + OPT_STRING("sample-time", 's', sample_options, &cfg.sample_time, + sample_interval), + OPT_STRING("type", 't', type_options, &cfg.tracker_type, + "Tracker type"), + OPT_UINT("run-time", 'r', &cfg.run_time_s, run_time), + OPT_INT("flush-freq", 'f', &cfg.flush_frequency, + flush_frequency), OPT_FLAG("wall-clock", 'w', &wlt.show_wall_timestamp, "Logs current wall timestamp when entry was retrieved"), - OPT_STR("trigger-field", 'T', &cfg.trigger_field, "Field name to stop trigger on"), + OPT_STR("trigger-field", 'T', &cfg.trigger_field, + "Field name to stop trigger on"), OPT_UINT("trigger-threshold", 'V', &cfg.trigger_treshold, "Field value to trigger stop sampling"), OPT_FLAG("trigger-on-delta", 'D', &cfg.trigger_on_delta, "Trigger on delta to stop sampling"), OPT_FLAG("trigger-on-latency", 'L', &cfg.trigger_on_latency, - "Use latency tracker to trigger stop sampling")); + "Use latency tracker to trigger stop sampling"), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/ssstc/ssstc-nvme.c b/plugins/ssstc/ssstc-nvme.c index 32ee0b93ef..eb14aba546 100644 --- a/plugins/ssstc/ssstc-nvme.c +++ b/plugins/ssstc/ssstc-nvme.c @@ -403,11 +403,13 @@ int ssstc_get_add_smart_log(int argc, char **argv, struct command *acmd, struct .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + OPT_FLAG_JSON("json", 'j', &cfg.json, json), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index 8c20ea9a25..836348840f 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -428,11 +428,13 @@ static int vendor_log(int argc, char **argv, struct command *acmd, struct plugin .log = 0xca }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), OPT_FILE("output-file", 'O', &cfg.output_file, output_file), - OPT_UINT("log", 'l', &cfg.log, log)); + OPT_UINT("log", 'l', &cfg.log, log), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -474,9 +476,11 @@ static int internal_log(int argc, char **argv, struct command *acmd, struct plug .prev_log = false }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FILE("output-file", 'O', &cfg.output_file, output_file), - OPT_FLAG("prev-log", 'p', &cfg.prev_log, prev_log)); + OPT_FLAG("prev-log", 'p', &cfg.prev_log, prev_log), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -511,7 +515,9 @@ static int clear_correctable_errors(int argc, char **argv, struct command *acmd, __u64 result; int err; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/transcend/transcend-nvme.c b/plugins/transcend/transcend-nvme.c index a6edb2f6d5..b770ef68a8 100644 --- a/plugins/transcend/transcend-nvme.c +++ b/plugins/transcend/transcend-nvme.c @@ -29,7 +29,9 @@ static int getHealthValue(int argc, char **argv, struct command *acmd, struct pl __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int result; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; result = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (result) @@ -60,7 +62,9 @@ static int getBadblock(int argc, char **argv, struct command *acmd, struct plugi unsigned char data[1] = {0}; int result; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; result = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (result) diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index e97ec0bfdb..54f442899c 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -966,11 +966,13 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, .test_name = NULL, }; - NVME_ARGS(opts, - OPT_DOUBLE("run-time", 'r', &cfg.run_time_hrs, run_time), - OPT_DOUBLE("freq", 'f', &cfg.log_record_frequency_hrs, freq), - OPT_FILE("output-file", 'O', &cfg.output_file, output_file), - OPT_STRING("test-name", 'n', "NAME", &cfg.test_name, test_name)); + OPT_ARGS(opts) = { + OPT_DOUBLE("run-time", 'r', &cfg.run_time_hrs, run_time), + OPT_DOUBLE("freq", 'f', &cfg.log_record_frequency_hrs, freq), + OPT_FILE("output-file", 'O', &cfg.output_file, output_file), + OPT_STRING("test-name", 'n', "NAME", &cfg.test_name, test_name), + OPT_END() + }; vt_generate_vtview_log_file_name(vt_default_log_file_name); @@ -1030,7 +1032,9 @@ static int vt_show_identify(int argc, char **argv, struct command *acmd, struct struct libnvme_passthru_cmd cmd; int ret, err = 0; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 5bf73532d7..3f7ed664bd 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -3888,9 +3888,11 @@ static int wdc_cap_diag(int argc, char **argv, struct command *acmd, .xfer_size = 0x10000 }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FILE("output-file", 'O', &cfg.file, file), - OPT_UINT("transfer-size", 's', &cfg.xfer_size, size)); + OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -4452,14 +4454,15 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, .type = WDC_TELEMETRY_TYPE_NONE, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_FILE("output-file", 'O', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), OPT_ENUM("type", 't', &cfg.type, type, types), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 'e', &cfg.offset, offset), - OPT_END()); + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -4811,8 +4814,10 @@ static int wdc_drive_log(int argc, char **argv, struct command *acmd, .file = NULL }; - NVME_ARGS(opts, - OPT_FILE("output-file", 'O', &cfg.file, file)); + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'O', &cfg.file, file), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -4857,8 +4862,10 @@ static int wdc_get_crash_dump(int argc, char **argv, struct command *acmd, .file = NULL, }; - NVME_ARGS(opts, - OPT_FILE("output-file", 'O', &cfg.file, file)); + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'O', &cfg.file, file), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -4898,8 +4905,10 @@ static int wdc_get_pfail_dump(int argc, char **argv, struct command *acmd, .file = NULL, }; - NVME_ARGS(opts, - OPT_FILE("output-file", 'O', &cfg.file, file)); + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'O', &cfg.file, file), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -4978,7 +4987,9 @@ static int wdc_purge(int argc, char **argv, char *err_str; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -5030,7 +5041,9 @@ static int wdc_purge_monitor(int argc, char **argv, __u64 capabilities; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8447,12 +8460,16 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_BYTE("interval", 'i', &cfg.interval, interval), - OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), - OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_BYTE("interval", 'i', &cfg.interval, interval), + OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, + log_page_version), + OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, + log_page_mask), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8577,8 +8594,10 @@ static int wdc_cu_smart_log(int argc, char **argv, struct command *acmd, .uuid_index = 0, }; - NVME_ARGS(opts, - OPT_UINT("uuid-index", 'u', &cfg.uuid_index, uuid_index)); + OPT_ARGS(opts) = { + OPT_UINT("uuid-index", 'u', &cfg.uuid_index, uuid_index), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8684,9 +8703,11 @@ static int wdc_vs_cloud_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8754,9 +8775,11 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8845,9 +8868,11 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8953,7 +8978,9 @@ static int wdc_get_latency_monitor_log(int argc, char **argv, struct command *ac .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -8997,7 +9024,9 @@ static int wdc_get_error_recovery_log(int argc, char **argv, struct command *acm .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9042,7 +9071,9 @@ static int wdc_get_dev_capabilities_log(int argc, char **argv, struct command *a .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9087,7 +9118,9 @@ static int wdc_get_unsupported_reqs_log(int argc, char **argv, struct command *a .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9165,7 +9198,9 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma __u64 capabilities = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9211,7 +9246,9 @@ static int wdc_drive_status(int argc, char **argv, struct command *acmd, __u64 capabilities = 0; struct nvme_id_uuid_list uuid_list; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9355,7 +9392,9 @@ static int wdc_clear_assert_dump(int argc, char **argv, struct command *acmd, __u64 capabilities = 0; struct libnvme_passthru_cmd admin_cmd; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9578,7 +9617,9 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *acm .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9663,7 +9704,9 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -9717,10 +9760,12 @@ static int wdc_vs_telemetry_controller_option(int argc, char **argv, struct comm .status = false, }; - NVME_ARGS(opts, - OPT_FLAG("disable", 'd', &cfg.disable, disable), - OPT_FLAG("enable", 'e', &cfg.enable, enable), - OPT_FLAG("status", 's', &cfg.status, status)); + OPT_ARGS(opts) = { + OPT_FLAG("disable", 'd', &cfg.disable, disable), + OPT_FLAG("enable", 'e', &cfg.enable, enable), + OPT_FLAG("status", 's', &cfg.status, status), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -10535,8 +10580,10 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *acmd, .dirName = NULL, }; - NVME_ARGS(opts, - OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.dirName, dirName)); + OPT_ARGS(opts) = { + OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.dirName, dirName), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -10632,8 +10679,10 @@ static int wdc_drive_resize(int argc, char **argv, .size = 0, }; - NVME_ARGS(opts, - OPT_LONG("size", 's', &cfg.size, size)); + OPT_ARGS(opts) = { + OPT_LONG("size", 's', &cfg.size, size), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -10680,10 +10729,12 @@ static int wdc_namespace_resize(int argc, char **argv, .op_option = 0xF, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_UINT("op-option", 'O', &cfg.op_option, op_option)); + OPT_UINT("op-option", 'O', &cfg.op_option, op_option), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -10741,9 +10792,11 @@ static int wdc_reason_identifier(int argc, char **argv, .file = NULL, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_UINT("log-id", 'i', &cfg.log_id, log_id), - OPT_FILE("file", 'O', &cfg.file, fname)); + OPT_FILE("file", 'O', &cfg.file, fname), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -11001,7 +11054,9 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *acmd, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -11766,7 +11821,9 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *acmd, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -11845,7 +11902,9 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *acmd, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -11935,7 +11994,9 @@ static int wdc_vs_drive_info(int argc, char **argv, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12189,7 +12250,9 @@ static int wdc_vs_temperature_stats(int argc, char **argv, .output_format = "normal", }; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12295,7 +12358,9 @@ static int wdc_capabilities(int argc, char **argv, struct command *acmd, struct uint64_t capabilities = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12400,7 +12465,9 @@ static int wdc_cloud_ssd_plugin_version(int argc, char **argv, struct command *a uint64_t capabilities = 0; int ret; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12444,9 +12511,11 @@ static int wdc_cloud_boot_SSD_version(int argc, char **argv, struct command *acm .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -12510,10 +12579,12 @@ static int wdc_enc_get_log(int argc, char **argv, struct command *acmd, struct p .log_id = 0xffffffff, }; - NVME_ARGS(opts, - OPT_FILE("output-file", 'O', &cfg.file, file), + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'O', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), - OPT_UINT("log-id", 'l', &cfg.log_id, log)); + OPT_UINT("log-id", 'l', &cfg.log_id, log), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -12807,30 +12878,33 @@ int wdc_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, .latency_monitor_feature_enable = 0x7, }; - NVME_ARGS(opts, + OPT_ARGS(opts) = { OPT_SHRT("active_bucket_timer_threshold", 't', - &cfg.active_bucket_timer_threshold, - active_bucket_timer_threshold), + &cfg.active_bucket_timer_threshold, + active_bucket_timer_threshold), OPT_BYTE("active_threshold_a", 'a', &cfg.active_threshold_a, - active_threshold_a), + active_threshold_a), OPT_BYTE("active_threshold_b", 'b', &cfg.active_threshold_b, - active_threshold_b), + active_threshold_b), OPT_BYTE("active_threshold_c", 'c', &cfg.active_threshold_c, - active_threshold_c), + active_threshold_c), OPT_BYTE("active_threshold_d", 'd', &cfg.active_threshold_d, - active_threshold_d), + active_threshold_d), OPT_SHRT("active_latency_config", 'f', - &cfg.active_latency_config, active_latency_config), + &cfg.active_latency_config, active_latency_config), OPT_BYTE("active_latency_minimum_window", 'w', - &cfg.active_latency_minimum_window, - active_latency_minimum_window), + &cfg.active_latency_minimum_window, + active_latency_minimum_window), OPT_SHRT("debug_log_trigger_enable", 'r', - &cfg.debug_log_trigger_enable, debug_log_trigger_enable), + &cfg.debug_log_trigger_enable, + debug_log_trigger_enable), OPT_BYTE("discard_debug_log", 'l', &cfg.discard_debug_log, - discard_debug_log), + discard_debug_log), OPT_BYTE("latency_monitor_feature_enable", 'e', - &cfg.latency_monitor_feature_enable, - latency_monitor_feature_enable)); + &cfg.latency_monitor_feature_enable, + latency_monitor_feature_enable), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index b92b43752e..d90d155f69 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -412,10 +412,12 @@ static int get_additional_smart_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace, nvme_nsid_values), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; err = ymtc_parse_options(&ctx, &hdl, argc, argv, desc, opts,&model); if (err) diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 08a09e43b9..9918769ac7 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -104,7 +104,9 @@ static int list(int argc, char **argv, struct command *acmd, }; struct shr_table *t; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -148,7 +150,9 @@ static int id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *p nvme_print_flags_t flags; int err = -1; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -188,11 +192,13 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu struct config cfg = { }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, - vendor_specific)); + vendor_specific), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -248,11 +254,13 @@ static int zns_mgmt_send(int argc, char **argv, struct command *acmd, struct plu struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("select-all", 'a', &cfg.select_all, select_all)); + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -343,15 +351,17 @@ static int zone_mgmt_send(int argc, char **argv, struct command *acmd, struct pl struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("zsaso", 'O', &cfg.zsaso, zsaso), - OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), - OPT_BYTE("zsa", 'z', &cfg.zsa, zsa), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_FILE("data", 'd', &cfg.file, data)); + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zsaso", 'O', &cfg.zsaso, zsaso), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_BYTE("zsa", 'z', &cfg.zsa, zsa), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -460,12 +470,14 @@ static int open_zone(int argc, char **argv, struct command *acmd, struct plugin struct config cfg = { }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), - OPT_FLAG("select-all", 'a', &cfg.select_all, select_all)); + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -529,12 +541,14 @@ static int set_zone_desc(int argc, char **argv, struct command *acmd, struct plu struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), - OPT_FILE("data", 'd', &cfg.file, data)); + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -612,10 +626,12 @@ static int zrwa_flush_zone(int argc, char **argv, struct command *acmd, struct p struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("lba", 'l', &cfg.lba, slba)); + OPT_SUFFIX("lba", 'l', &cfg.lba, slba), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -667,14 +683,16 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *acmd, struct pl struct config cfg = { }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_BYTE("zra", 'z', &cfg.zra, zra), - OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), - OPT_FLAG("partial", 'p', &cfg.partial, partial), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_BYTE("zra", 'z', &cfg.zra, zra), + OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), + OPT_FLAG("partial", 'p', &cfg.partial, partial), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -757,14 +775,16 @@ static int report_zones(int argc, char **argv, struct command *acmd, struct plug .num_descs = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id, nvme_nsid_values), - OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), - OPT_UINT("state", 'S', &cfg.state, state), - OPT_FLAG("extended", 'e', &cfg.extended, ext), - OPT_FLAG("partial", 'p', &cfg.partial, part)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), + OPT_UINT("state", 'S', &cfg.state, state), + OPT_FLAG("extended", 'e', &cfg.extended, ext), + OPT_FLAG("partial", 'p', &cfg.partial, part), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -922,19 +942,23 @@ static int zone_append(int argc, char **argv, struct command *acmd, struct plugi struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), - OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), - OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), - OPT_FILE("data", 'd', &cfg.data, data), - OPT_FILE("metadata", 'M', &cfg.metadata, metadata), - OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), - OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), - OPT_FLAG("piremap", 'P', &cfg.piremap, piremap), - OPT_FLAG("latency", 't', &cfg.latency, latency)); + OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), + OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), + OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, + metadata_size), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, + limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_FLAG("piremap", 'P', &cfg.piremap, piremap), + OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1082,10 +1106,12 @@ static int changed_zone_list(int argc, char **argv, struct command *acmd, struct struct config cfg = { }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), - OPT_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From 5ae5016993f08481cb1a4693754f833a00f20fdd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:45:05 +0000 Subject: [PATCH 132/263] nvme: drop NVME_ARGS() No command defines its options with NVME_ARGS() any more. Drop it and the mentions of it. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata.c | 6 +++--- src/args.h | 18 +++++------------- tests/cli/micron/micron_id_ctrl_mock_test.py | 4 ++-- .../e2e/plugins/micron/micron_id_ctrl_test.py | 4 ++-- .../plugins/micron/micron_smart_log_test.py | 2 +- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index e101d07d41..b56b499670 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -10,7 +10,7 @@ * shell-completion generators, documentation, and drift checks. * * The model is captured by walking the live plugin/command tree and, for each - * command, intercepting the options array it builds on its stack via NVME_ARGS. + * command, intercepting the options array it builds on its stack. * Capture installs a hook in argconfig_parse() (see argconfig_set_parse_hook): * when a command calls into the parser, the hook copies the options array into * the model and returns a sentinel so the command unwinds before opening any @@ -716,8 +716,8 @@ static struct json_object *json_command(const struct command_metadata_command *c opts = json_create_array(); for (i = 0; i < c->num_options; i++) { /* - * Options after the "Global options" separator are the shared - * NVME_ARGS globals; flag them so generators can group them. + * Options after the "Global options" separator are the + * global options; flag them so generators can group them. */ if (opt_is_global_separator(&c->options[i])) { global = true; diff --git a/src/args.h b/src/args.h index a5f7addba1..e1c1a07955 100644 --- a/src/args.h +++ b/src/args.h @@ -64,20 +64,12 @@ enum nvme_print_flags { #endif /* CONFIG_JSONC */ /* - * The command's options. The global options are registered once with - * argconfig (nvme_args_register()) and accepted by every command. They - * take precedence over a command's own option of the same short or long - * name, so a command must not use their names. - * The output formats a command supports are declared in its struct - * command. + * The global options are registered once with argconfig + * (nvme_args_register()) and accepted by every command. They take + * precedence over a command's own option of the same short or long name, + * so a command must not use their names. The output formats a command + * supports are declared in its struct command. */ -#define NVME_ARGS(n, ...) \ - struct argconfig_commandline_options n[] = { \ - OPT_GROUP("Options"), \ - ##__VA_ARGS__, \ - OPT_END() \ - } - extern struct argconfig_opt_val nvme_nsid_values[]; extern struct argconfig_opt_val nvme_timeout_values[]; extern const char *uuid_index; diff --git a/tests/cli/micron/micron_id_ctrl_mock_test.py b/tests/cli/micron/micron_id_ctrl_mock_test.py index b20948e679..2316ecd5c2 100644 --- a/tests/cli/micron/micron_id_ctrl_mock_test.py +++ b/tests/cli/micron/micron_id_ctrl_mock_test.py @@ -8,8 +8,8 @@ """Tests for the micron id-ctrl command, hardware-free. micron id-ctrl extends the core id-ctrl rendering with one vendor field: pms, -the Power Measurement Support bit of CTRATT. It is declared with plain -NVME_ARGS, so unlike most micron commands it also honours binary output. +the Power Measurement Support bit of CTRATT. It takes the default output +formats, so unlike most micron commands it also honours binary output. The command warns about an unrecognised drive but proceeds, so it is not gated on the model. With the identify buffer as an input, pms can be checked diff --git a/tests/e2e/plugins/micron/micron_id_ctrl_test.py b/tests/e2e/plugins/micron/micron_id_ctrl_test.py index b85ea958f1..ec67557775 100644 --- a/tests/e2e/plugins/micron/micron_id_ctrl_test.py +++ b/tests/e2e/plugins/micron/micron_id_ctrl_test.py @@ -159,8 +159,8 @@ def test_namespace_path_matches_controller(self): def test_binary_output_is_accepted(self): """--output-format=binary dumps the raw Identify Controller structure. - micron id-ctrl is declared with plain NVME_ARGS, so unlike the other - micron commands it honours binary output. Binary stdout is + micron id-ctrl takes the default output formats, so unlike the + other micron commands it honours binary output. Binary stdout is redirected to a file so the byte count is not perturbed by text decoding. """ diff --git a/tests/e2e/plugins/micron/micron_smart_log_test.py b/tests/e2e/plugins/micron/micron_smart_log_test.py index f8d5389e8a..ac09f7a691 100644 --- a/tests/e2e/plugins/micron/micron_smart_log_test.py +++ b/tests/e2e/plugins/micron/micron_smart_log_test.py @@ -9,7 +9,7 @@ The micron smart-log command reads the SMART/Health log page and prints its own rendering of it, adding the operational lifetime energy consumed (olec) and interval power measurement (ipm) fields. It is -declared NVME_ARGS_OUTPUT_FORMATS(JSON | NORMAL), so binary is rejected. +declared with the output formats JSON | NORMAL, so binary is rejected. The added fields, the temperature conversion and the option surface are covered without hardware in micron_smart_log_mock_test.py. The tests here From 29be039d778aa64d4b23b05384e33a44451f863f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:45:21 +0000 Subject: [PATCH 133/263] completions: update the shell completions Offer the words of the options which take one of a few words. Signed-off-by: Daniel Wagner --- completions/_nvme | 47 ++++++++++++++++++----------- completions/bash-nvme-completion.sh | 33 ++++++++++++++------ completions/nvme-completion.ps1 | 30 +++++++++++++++--- 3 files changed, 78 insertions(+), 32 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 0be20ac00b..05f133c25e 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -5059,14 +5059,14 @@ _nvme () { --quiet':suppress informational output' --sel-perf-log=':Select features to turn on, default: Disable bit 0: latency statistics bit 1: high latency log bit 2: Performance stat' -s':alias for --sel-perf-log' - --set-commands-mask=':Set Enable, default: Disable bit 0: Read commands bit 1: high Write commands bit 2: De-allocate/TRIM (this bit is not worked for Performance stat.)' + --set-commands-mask=':Set Enable, default: Disable bit 0: Read commands bit 1: high Write commands bit 2: Deallocate/TRIM (no Performance stat)' -m':alias for --set-commands-mask' --set-options=':set a libnvme library option (key=value[,key=value,...]);' - --set-read-threshold=':set read high latency log threshold, it'\''s a 0-based value and unit is 10ms' + --set-read-threshold=':read high latency log threshold, 0-based, unit is 10ms' -r':alias for --set-read-threshold' - --set-trim-threshold=':set trim high latency log threshold, it'\''s a 0-based value and unit is 10ms' + --set-trim-threshold=':trim high latency log threshold, 0-based, unit is 10ms' -t':alias for --set-trim-threshold' - --set-write-threshold=':set write high latency log threshold, it'\''s a 0-based value and unit is 10ms' + --set-write-threshold=':write high latency log threshold, 0-based, unit is 10ms' -w':alias for --set-write-threshold' --timeout=':timeout value, in milliseconds' --verbose':Increase output verbosity' @@ -5169,7 +5169,7 @@ _nvme () { local _memblaze_perf_stats_print_x _memblaze_perf_stats_print_x=( --dry-run':show command instead of executing' - --duration=':[1-24] hours: duration of the log to be printed, default is 1 hour' + --duration=':[1-24] hours: duration of the log to print, default 1 hour' -d':alias for --duration' --no-ioctl-probing':disable 64-bit IOCTL support probing' --no-retries':disable retry logic on errors' @@ -5194,6 +5194,7 @@ _nvme () { (select-download) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--select -s" "OOB EEP ALL" \ "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _memblaze_select_download @@ -5499,7 +5500,8 @@ _nvme () { ;; (latency-stats) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--command -c" "all read write trim" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return local _micron_latency_stats @@ -5526,7 +5528,9 @@ _nvme () { ;; (latency-tracking) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--command -c" "all read write trim" \ + "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return local _micron_latency_tracking @@ -5608,6 +5612,7 @@ _nvme () { (select-download) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--select -s" "OOB EEP ALL" \ "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _micron_select_download @@ -5763,7 +5768,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" && return + "--timeout" "infinite -1" \ + "--type -t" "host controller" && return _nvme_opt_files "--package -p" && return local _micron_vs_internal_log _micron_vs_internal_log=( @@ -5893,7 +5899,8 @@ _nvme () { ;; (vs-smbus-option) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return local _micron_vs_smbus_option @@ -5924,7 +5931,8 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return local _micron_vs_telemetry_controller_option @@ -7021,6 +7029,7 @@ _nvme () { (internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--telemetry-type -t" "host controller host0 host1" \ "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _ocp_internal_log @@ -9818,9 +9827,9 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" && return - _nvme_opt_files "--output-file -O" \ - "--type -t" && return + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER BOTH" && return + _nvme_opt_files "--output-file -O" && return local _sndk_vs_internal_log _sndk_vs_internal_log=( --data-area=':Data area to retrieve up to. Supported for telemetry, see man page for other use cases.' @@ -10390,7 +10399,8 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" && return + "--timeout" "infinite -1" \ + "--type -t" "ALL CIT HIT NLOG ASSERT EVENT EXTENDED" && return _nvme_opt_files "--dir-name -d" && return local _solidigm_vs_internal_log _solidigm_vs_internal_log=( @@ -11719,9 +11729,9 @@ _nvme () { (vs-internal-log) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" && return - _nvme_opt_files "--output-file -O" \ - "--type -t" && return + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER" && return + _nvme_opt_files "--output-file -O" && return local _wdc_vs_internal_log _wdc_vs_internal_log=( --data-area=':Data area to retrieve up to. Supported for telemetry, see man page for other use cases.' @@ -16262,6 +16272,7 @@ _nvme () { (show-topology) _nvme_opt_vals "--output-format -o" "normal json tabular" \ "--output-format-version" "1 2" \ + "--ranking -r" "namespace ctrl multipath" \ "--timeout" "infinite -1" && return local _show_topology _show_topology=( @@ -16272,7 +16283,7 @@ _nvme () { -o':alias for --output-format' --output-format-version=':output format version: 1|2' --quiet':suppress informational output' - --ranking=':Ranking order: namespace|ctrl|multipath' + --ranking=':Ranking order' -r':alias for --ranking' --set-options=':set a libnvme library option (key=value[,key=value,...]);' --timeout=':timeout value, in milliseconds' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index af61fd2a03..ae204e8e02 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -1172,6 +1172,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json tabular" \ "--output-format-version" "1 2" \ + "--ranking -r" "namespace ctrl multipath" \ "--timeout" "infinite -1" ;; @@ -3060,6 +3061,7 @@ plugin_memblaze_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--select -s" "OOB EEP ALL" \ "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -3169,7 +3171,8 @@ plugin_micron_opts () { opts+=" --command= -c --dry-run --no-ioctl-probing --no-retries --output-format= -o --output-format-version= --quiet --set-options= --timeout= --verbose -v" valopts+=" --command -c --output-format -o --output-format-version --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--command -c" "all read write trim" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" ;; @@ -3178,7 +3181,9 @@ plugin_micron_opts () { opts+=" --command= -c --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --set-options= --threshold= -t --timeout= --verbose -v" valopts+=" --command -c --option -O --output-format -o --output-format-version --set-options --threshold -t --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--command -c" "all read write trim" \ + "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" ;; @@ -3208,6 +3213,7 @@ plugin_micron_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--select -s" "OOB EEP ALL" \ "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -3263,7 +3269,8 @@ plugin_micron_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" + "--timeout" "infinite -1" \ + "--type -t" "host controller" _nvme_opt_files "--package -p" ;; @@ -3307,7 +3314,8 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --save= -s --set-options= --timeout= --value= -V --verbose -v" valopts+=" --option -O --output-format -o --output-format-version --save -s --set-options --timeout --value -V" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" ;; @@ -3316,7 +3324,8 @@ plugin_micron_opts () { opts+=" --dry-run --no-ioctl-probing --no-retries --option= -O --output-format= -o --output-format-version= --quiet --select= -s --set-options= --timeout= --verbose -v" valopts+=" --option -O --output-format -o --output-format-version --select -s --set-options --timeout" - _nvme_opt_vals "--output-format -o" "normal json binary" \ + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" ;; @@ -3728,6 +3737,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ + "--telemetry-type -t" "host controller host0 host1" \ "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -4720,8 +4730,9 @@ plugin_sndk_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" - _nvme_opt_files "--output-file -O" "--type -t" + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER BOTH" + _nvme_opt_files "--output-file -O" ;; "vs-nand-stats") @@ -4914,7 +4925,8 @@ plugin_solidigm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" + "--timeout" "infinite -1" \ + "--type -t" "ALL CIT HIT NLOG ASSERT EVENT EXTENDED" _nvme_opt_files "--dir-name -d" ;; @@ -5422,8 +5434,9 @@ plugin_wdc_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--timeout" "infinite -1" - _nvme_opt_files "--output-file -O" "--type -t" + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER" + _nvme_opt_files "--output-file -O" ;; "vs-nand-stats") diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index f6cc35ef32..98e78d0ede 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -894,9 +894,7 @@ $script:NvmeFileOptions = @{ 'sndk vs-error-reason-identifier --file' = $true 'sndk vs-error-reason-identifier -O' = $true 'sndk vs-internal-log --output-file' = $true - 'sndk vs-internal-log --type' = $true 'sndk vs-internal-log -O' = $true - 'sndk vs-internal-log -t' = $true 'solidigm parse-telemetry-log --config-file' = $true 'solidigm parse-telemetry-log --source-file' = $true 'solidigm parse-telemetry-log -j' = $true @@ -926,9 +924,7 @@ $script:NvmeFileOptions = @{ 'wdc vs-error-reason-identifier --file' = $true 'wdc vs-error-reason-identifier -O' = $true 'wdc vs-internal-log --output-file' = $true - 'wdc vs-internal-log --type' = $true 'wdc vs-internal-log -O' = $true - 'wdc vs-internal-log -t' = $true 'write --data' = $true 'write --metadata' = $true 'write -M' = $true @@ -1948,8 +1944,10 @@ $script:NvmeOptionValues = @{ 'memblaze perf-stats-print-x -o' = @('normal', 'json', 'binary') 'memblaze select-download --output-format' = @('normal', 'json', 'binary') 'memblaze select-download --output-format-version' = @('1', '2') + 'memblaze select-download --select' = @('OOB', 'EEP', 'ALL') 'memblaze select-download --timeout' = @('infinite', '-1') 'memblaze select-download -o' = @('normal', 'json', 'binary') + 'memblaze select-download -s' = @('OOB', 'EEP', 'ALL') 'memblaze set-pm-status --output-format' = @('normal', 'json', 'binary') 'memblaze set-pm-status --output-format-version' = @('1', '2') 'memblaze set-pm-status --timeout' = @('infinite', '-1') @@ -1996,13 +1994,19 @@ $script:NvmeOptionValues = @{ 'micron latency-logs --output-format-version' = @('1', '2') 'micron latency-logs --timeout' = @('infinite', '-1') 'micron latency-logs -o' = @('normal', 'json', 'binary') + 'micron latency-stats --command' = @('all', 'read', 'write', 'trim') 'micron latency-stats --output-format' = @('normal', 'json', 'binary') 'micron latency-stats --output-format-version' = @('1', '2') 'micron latency-stats --timeout' = @('infinite', '-1') + 'micron latency-stats -c' = @('all', 'read', 'write', 'trim') 'micron latency-stats -o' = @('normal', 'json', 'binary') + 'micron latency-tracking --command' = @('all', 'read', 'write', 'trim') + 'micron latency-tracking --option' = @('enable', 'disable', 'status') 'micron latency-tracking --output-format' = @('normal', 'json', 'binary') 'micron latency-tracking --output-format-version' = @('1', '2') 'micron latency-tracking --timeout' = @('infinite', '-1') + 'micron latency-tracking -O' = @('enable', 'disable', 'status') + 'micron latency-tracking -c' = @('all', 'read', 'write', 'trim') 'micron latency-tracking -o' = @('normal', 'json', 'binary') 'micron log-page-directory --output-format' = @('normal', 'json', 'binary') 'micron log-page-directory --output-format-version' = @('1', '2') @@ -2014,8 +2018,10 @@ $script:NvmeOptionValues = @{ 'micron plugin-version -o' = @('normal', 'json', 'binary') 'micron select-download --output-format' = @('normal', 'json', 'binary') 'micron select-download --output-format-version' = @('1', '2') + 'micron select-download --select' = @('OOB', 'EEP', 'ALL') 'micron select-download --timeout' = @('infinite', '-1') 'micron select-download -o' = @('normal', 'json', 'binary') + 'micron select-download -s' = @('OOB', 'EEP', 'ALL') 'micron smart-log --output-format' = @('normal', 'json') 'micron smart-log --output-format-version' = @('1', '2') 'micron smart-log --timeout' = @('infinite', '-1') @@ -2039,7 +2045,9 @@ $script:NvmeOptionValues = @{ 'micron vs-internal-log --output-format' = @('normal', 'json', 'binary') 'micron vs-internal-log --output-format-version' = @('1', '2') 'micron vs-internal-log --timeout' = @('infinite', '-1') + 'micron vs-internal-log --type' = @('host', 'controller') 'micron vs-internal-log -o' = @('normal', 'json', 'binary') + 'micron vs-internal-log -t' = @('host', 'controller') 'micron vs-nand-stats --output-format' = @('normal', 'json') 'micron vs-nand-stats --output-format-version' = @('1', '2') 'micron vs-nand-stats --timeout' = @('infinite', '-1') @@ -2056,13 +2064,17 @@ $script:NvmeOptionValues = @{ 'micron vs-smart-ext-log --output-format-version' = @('1', '2') 'micron vs-smart-ext-log --timeout' = @('infinite', '-1') 'micron vs-smart-ext-log -o' = @('normal', 'json') + 'micron vs-smbus-option --option' = @('enable', 'disable', 'status') 'micron vs-smbus-option --output-format' = @('normal', 'json', 'binary') 'micron vs-smbus-option --output-format-version' = @('1', '2') 'micron vs-smbus-option --timeout' = @('infinite', '-1') + 'micron vs-smbus-option -O' = @('enable', 'disable', 'status') 'micron vs-smbus-option -o' = @('normal', 'json', 'binary') + 'micron vs-telemetry-controller-option --option' = @('enable', 'disable', 'status') 'micron vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary') 'micron vs-telemetry-controller-option --output-format-version' = @('1', '2') 'micron vs-telemetry-controller-option --timeout' = @('infinite', '-1') + 'micron vs-telemetry-controller-option -O' = @('enable', 'disable', 'status') 'micron vs-telemetry-controller-option -o' = @('normal', 'json', 'binary') 'micron vs-temperature-stats --output-format' = @('normal', 'json') 'micron vs-temperature-stats --output-format-version' = @('1', '2') @@ -2262,8 +2274,10 @@ $script:NvmeOptionValues = @{ 'ocp hardware-component-log -o' = @('normal', 'json', 'binary') 'ocp internal-log --output-format' = @('normal', 'json', 'binary') 'ocp internal-log --output-format-version' = @('1', '2') + 'ocp internal-log --telemetry-type' = @('host', 'controller', 'host0', 'host1') 'ocp internal-log --timeout' = @('infinite', '-1') 'ocp internal-log -o' = @('normal', 'json', 'binary') + 'ocp internal-log -t' = @('host', 'controller', 'host0', 'host1') 'ocp latency-monitor-log --output-format' = @('normal', 'json', 'binary') 'ocp latency-monitor-log --output-format-version' = @('1', '2') 'ocp latency-monitor-log --timeout' = @('infinite', '-1') @@ -2688,8 +2702,10 @@ $script:NvmeOptionValues = @{ 'show-regs -o' = @('normal', 'json', 'binary') 'show-topology --output-format' = @('normal', 'json', 'tabular') 'show-topology --output-format-version' = @('1', '2') + 'show-topology --ranking' = @('namespace', 'ctrl', 'multipath') 'show-topology --timeout' = @('infinite', '-1') 'show-topology -o' = @('normal', 'json', 'tabular') + 'show-topology -r' = @('namespace', 'ctrl', 'multipath') 'smart-log --namespace-id' = @('all', '-1') 'smart-log --output-format' = @('normal', 'json', 'binary') 'smart-log --output-format-version' = @('1', '2') @@ -2797,7 +2813,9 @@ $script:NvmeOptionValues = @{ 'sndk vs-internal-log --output-format' = @('normal', 'json', 'binary') 'sndk vs-internal-log --output-format-version' = @('1', '2') 'sndk vs-internal-log --timeout' = @('infinite', '-1') + 'sndk vs-internal-log --type' = @('NONE', 'HOST', 'CONTROLLER', 'BOTH') 'sndk vs-internal-log -o' = @('normal', 'json', 'binary') + 'sndk vs-internal-log -t' = @('NONE', 'HOST', 'CONTROLLER', 'BOTH') 'sndk vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'sndk vs-nand-stats --output-format-version' = @('1', '2') 'sndk vs-nand-stats --timeout' = @('infinite', '-1') @@ -2877,7 +2895,9 @@ $script:NvmeOptionValues = @{ 'solidigm vs-internal-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-internal-log --output-format-version' = @('1', '2') 'solidigm vs-internal-log --timeout' = @('infinite', '-1') + 'solidigm vs-internal-log --type' = @('ALL', 'CIT', 'HIT', 'NLOG', 'ASSERT', 'EVENT', 'EXTENDED') 'solidigm vs-internal-log -o' = @('normal', 'json', 'binary') + 'solidigm vs-internal-log -t' = @('ALL', 'CIT', 'HIT', 'NLOG', 'ASSERT', 'EVENT', 'EXTENDED') 'solidigm vs-smart-add-log --output-format' = @('normal', 'json', 'binary') 'solidigm vs-smart-add-log --output-format-version' = @('1', '2') 'solidigm vs-smart-add-log --timeout' = @('infinite', '-1') @@ -3093,7 +3113,9 @@ $script:NvmeOptionValues = @{ 'wdc vs-internal-log --output-format' = @('normal', 'json', 'binary') 'wdc vs-internal-log --output-format-version' = @('1', '2') 'wdc vs-internal-log --timeout' = @('infinite', '-1') + 'wdc vs-internal-log --type' = @('NONE', 'HOST', 'CONTROLLER') 'wdc vs-internal-log -o' = @('normal', 'json', 'binary') + 'wdc vs-internal-log -t' = @('NONE', 'HOST', 'CONTROLLER') 'wdc vs-nand-stats --output-format' = @('normal', 'json', 'binary') 'wdc vs-nand-stats --output-format-version' = @('1', '2') 'wdc vs-nand-stats --timeout' = @('infinite', '-1') From 9a3f510f118832f01155e73d29d5fed652075076 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 14:45:21 +0000 Subject: [PATCH 134/263] NEWS: document the option words and the deprecation metadata Options which take one of a few words check it while parsing, and the command metadata reports deprecated commands and options. Signed-off-by: Daniel Wagner --- NEWS.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/NEWS.md b/NEWS.md index f22a9777b0..ebc8f0bfdb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -78,6 +78,25 @@ that name is red and the WARNING of a warning yellow. Set `NO_COLOR` to a non-empty value to turn colors off (https://no-color.org/). +* Options which take one of a few words check the word while parsing + the command line, in any case and as a unique prefix like every + value name, and the help and the completions list the words: + `show-topology --ranking`, `memblaze select-download --select`, + `micron select-download --select`, the `--option` of the micron + `vs-smbus-option`, `latency-tracking` and + `vs-telemetry-controller-option`, the micron `--command` of + `latency-tracking` and `latency-stats`, `micron vs-internal-log + --type`, `ocp internal-log --telemetry-type`, the `--type` of the + `sndk` and `wdc` `vs-internal-log` and `solidigm internal-log + --type`. An unknown word is a usage error which lists the words. + `ocp internal-log` with an unknown telemetry type failed with status + 0 before. + +* The command metadata (`nvme utils dump-command-metadata`) marks + deprecated commands with the command which replaces them, deprecated + options with the option which replaces them, and renamed options + with their old name. + * The old option names `--dhchap-secret`, `--dhchap-ctrl-secret` and `--fast_io_fail_tmo` of the fabrics commands and `--force` of `discover` and `connect-all` print a deprecation warning which names From 20185354992ce89cbbba5c27314aeca0752fbf08 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:19:54 +0000 Subject: [PATCH 135/263] plugins/virtium: fix the length check of the log header strings The device path and the test name are copied into the 256 byte fields of the vtview log header after a check that their length is not larger than the field. A string of 256 characters passes it, and strcpy() then writes its NUL behind the field. Leave room for the NUL. Fixes: d50ea761b892 ("plugins/virtium: fixup possible string overflows") Signed-off-by: Daniel Wagner --- plugins/virtium/virtium-nvme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index 54f442899c..e0a87a8f16 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -326,7 +326,7 @@ vt_update_vtview_log_header(struct libnvme_transport_handle *hdl, const char *pa int ret = 0; vt_initialize_header_buffer(&header); - if (strlen(path) > sizeof(header.path)) { + if (strlen(path) >= sizeof(header.path)) { nvme_show_error("filename too long"); errno = EINVAL; return -1; @@ -336,7 +336,7 @@ vt_update_vtview_log_header(struct libnvme_transport_handle *hdl, const char *pa if (!cfg->test_name) { strcpy(header.test_name, DEFAULT_TEST_NAME); } else { - if (strlen(cfg->test_name) > sizeof(header.test_name)) { + if (strlen(cfg->test_name) >= sizeof(header.test_name)) { nvme_show_error("test name too long"); errno = EINVAL; return -1; From b7085f1e4e008c7f0d20f7dee71b8050851fbd5f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:09:18 +0000 Subject: [PATCH 136/263] argconfig: let an option table include another one Options which several commands share are added to each command's table with a macro, NVMF_ARGS() for the fabrics commands and FEAT_ARGS() for the features, which take variadic arguments and their comma tricks. Let a table include another one with OPT_INCLUDE(): its options are looked up, reset for each parse and found by argconfig_given() like the own ones, and the help lists them under their own heading. The command metadata lists them in place. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata.c | 42 ++++++++++++++++++-- src/argconfig.c | 61 ++++++++++++++++++++++++----- src/argconfig.h | 8 ++++ tests/unit/c/argconfig-getopt-ref.c | 2 + tests/unit/c/test-argconfig-parse.c | 59 ++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 14 deletions(-) diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index b56b499670..1aa15d3b2d 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -338,6 +338,41 @@ static int copy_options(const struct argconfig_commandline_options *opts, * command's options into the model, then returns the sentinel so the parser * unwinds before the command opens a device. */ +/* The entries of @opts with the included tables in place, see flat_copy() */ +static size_t flat_count(struct argconfig_commandline_options *opts) +{ + size_t n = 0; + + for (; opts->option; opts++) { + if (opts->config_type == CFG_INCLUDE) + n += flat_count(opts->default_value); + n++; + } + + return n; +} + +/* Copy @opts to @dst, an included table behind a group of its heading */ +static size_t flat_copy(struct argconfig_commandline_options *dst, + struct argconfig_commandline_options *opts) +{ + size_t n = 0; + + for (; opts->option; opts++) { + if (opts->config_type == CFG_INCLUDE) { + struct argconfig_commandline_options sep = + OPT_GROUP(opts->help); + + memcpy(&dst[n++], &sep, sizeof(sep)); + n += flat_copy(&dst[n], opts->default_value); + continue; + } + memcpy(&dst[n++], opts, sizeof(*opts)); + } + + return n; +} + /* * The command's options followed by the registered global options, behind a * "Global options" separator as json_command() expects. The caller frees @@ -347,11 +382,10 @@ static struct argconfig_commandline_options * with_global_options(struct argconfig_commandline_options *options) { struct argconfig_commandline_options *g, *all; - size_t n = 0, ng = 0, i; + size_t n, ng = 0, i; g = argconfig_get_global_options(); - while (options[n].option) - n++; + n = flat_count(options); while (g && g[ng].option) ng++; @@ -359,7 +393,7 @@ with_global_options(struct argconfig_commandline_options *options) if (!all) return NULL; - memcpy(all, options, n * sizeof(*all)); + flat_copy(all, options); if (ng) { struct argconfig_commandline_options sep = OPT_GROUP("Global options"); diff --git a/src/argconfig.c b/src/argconfig.c index e9d3fe67f4..2f169c409e 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -233,6 +233,12 @@ static void print_options(FILE *out, struct argconfig_commandline_options *s, bool header_printed = false; for (; s && s->option; s++) { + if (s->config_type == CFG_INCLUDE) { + print_options(out, s->default_value, s->help); + /* the own options after it get their heading again */ + header_printed = false; + continue; + } if (s->config_type == CFG_GROUP_SEPARATOR) { header = s->help; header_printed = false; @@ -364,6 +370,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, *(bool *)value = true; break; case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } @@ -405,6 +412,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *(char **)val = opt_val->string; break; case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } } @@ -526,7 +534,15 @@ static struct argconfig_commandline_options * argconfig_find_long(struct argconfig_commandline_options *s, const char *name, size_t len) { + struct argconfig_commandline_options *o; + for (; s->option; s++) { + if (s->config_type == CFG_INCLUDE) { + o = argconfig_find_long(s->default_value, name, len); + if (o) + return o; + continue; + } if (s->config_type == CFG_GROUP_SEPARATOR || is_null_or_empty(s->option)) continue; @@ -543,7 +559,15 @@ argconfig_find_long(struct argconfig_commandline_options *s, static struct argconfig_commandline_options * argconfig_find_short(struct argconfig_commandline_options *s, char c) { + struct argconfig_commandline_options *o; + for (; s->option; s++) { + if (s->config_type == CFG_INCLUDE) { + o = argconfig_find_short(s->default_value, c); + if (o) + return o; + continue; + } if (s->config_type == CFG_GROUP_SEPARATOR) continue; if (s->short_option == c) @@ -790,14 +814,22 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) * Returns 0, ARGCONFIG_HELP, ARGCONFIG_USAGE or a negative errno from the * value conversion. */ +/* Forget the options given before, also those of included tables */ +static void argconfig_reset_seen(struct argconfig_commandline_options *s) +{ + for (; s->option; s++) { + if (s->config_type == CFG_INCLUDE) + argconfig_reset_seen(s->default_value); + s->seen = false; + } +} + static int argconfig_parse_args(struct argconfig_parser *p) { - struct argconfig_commandline_options *s; char *arg; int ret; - for (s = p->options; s->option; s++) - s->seen = false; + argconfig_reset_seen(p->options); for (p->ind = 1; p->ind < p->argc; p->ind++) { arg = p->argv[p->ind]; @@ -936,6 +968,21 @@ int argconfig_parse_global(int argc, char *argv[], (p.version ? ARGCONFIG_VERSION : 0); } +static bool given_in(struct argconfig_commandline_options *s, + const void *value) +{ + bool given = false; + + for (; s && s->option; s++) { + if (s->config_type == CFG_INCLUDE) + given |= given_in(s->default_value, value); + else + given |= s->default_value == value && s->seen; + } + + return given; +} + /* * True if an option which stores to @value was given, in the table or in * the global options. Aliases share the variable, so any of them counts. @@ -944,15 +991,9 @@ bool argconfig_given(struct argconfig_commandline_options *s, const void *value) { struct argconfig_commandline_options *g = argconfig_globals(s); - bool given = false; if (!value) return false; - for (; s && s->option; s++) - given |= s->default_value == value && s->seen; - for (; g && g->option; g++) - given |= g->default_value == value && g->seen; - - return given; + return given_in(s, value) || given_in(g, value); } diff --git a/src/argconfig.h b/src/argconfig.h index cd695406fa..981209a3f3 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -51,6 +51,7 @@ enum argconfig_types { CFG_INCREMENT, CFG_ENUM, CFG_GROUP_SEPARATOR, + CFG_INCLUDE, }; #define OPT_ARGS(n) \ @@ -81,6 +82,13 @@ enum argconfig_types { #define OPT_END() { NULL } +/* + * The options of the table @t at this place, under the heading @d. They + * are parsed, reset and given like the own options of the table. + */ +#define OPT_INCLUDE(t, d) \ + {"", 0, NULL, CFG_INCLUDE, t, ARGCONFIG_ARG_NONE, d, false, NULL} + #define OPT_GROUP(d) \ {"", 0, NULL, CFG_GROUP_SEPARATOR, NULL, ARGCONFIG_ARG_NONE, d, false, \ NULL} diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 125b8e9bd0..c23f2d4b46 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -143,6 +143,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, *(bool *)value = true; break; case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } @@ -185,6 +186,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, *(char **)val = opt_val->string; break; case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } } diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 71b06aa87c..c661419862 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -463,6 +463,64 @@ static void global_options_test(void) argconfig_set_global_options(NULL); } +/* + * A table includes another one: its options are found, reset for each + * parse and given like the own ones. + */ +static void include_test(void) +{ + const char *desc = "Test include"; + static bool shared_flag; + static __u32 shared_uint; + static struct argconfig_commandline_options shared[] = { + OPT_FLAG("shared-flag", 'S', &shared_flag, "shared flag"), + OPT_UINT("shared-uint", 0, &shared_uint, "shared uint"), + OPT_END() + }; + bool own = false; + int ret; + + OPT_ARGS(opts) = { + OPT_INCLUDE(shared, "Shared options"), + OPT_FLAG("own", 'o', &own, "own flag"), + OPT_END() + }; + + { + char *argv[5] = { "prog", "-S", "--shared-uint=3", "-o", NULL }; + + ret = argconfig_parse(4, argv, desc, opts); + if (ret || !shared_flag || shared_uint != 3 || !own || + !argconfig_given(opts, &shared_flag) || + !argconfig_given(opts, &shared_uint)) { + printf("ERROR: include: options not applied\n"); + test_rc = 1; + } + } + + { + /* a later parse starts over with the included options */ + char *argv[2] = { "prog", NULL }; + + ret = argconfig_parse(1, argv, desc, opts); + if (ret || argconfig_given(opts, &shared_flag) || + argconfig_given(opts, &shared_uint)) { + printf("ERROR: include: seen not reset\n"); + test_rc = 1; + } + } + + { + char *argv[3] = { "prog", "--bogus", NULL }; + + ret = argconfig_parse(2, argv, desc, opts); + if (ret != -EINVAL) { + printf("ERROR: include: unknown option accepted\n"); + test_rc = 1; + } + } +} + /* An enum takes only its names, in any case and as a unique prefix */ static void enum_test(void) { @@ -1007,6 +1065,7 @@ int main(void) given_test(); old_name_test(); enum_test(); + include_test(); special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) From 120071cb1a31179f69c81f1594708b95a00abe36 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:17:05 +0000 Subject: [PATCH 137/263] argconfig: bind a template table to a struct A table of options which several commands include stores into one place, but each command wants the values in its own struct, not in a shared global one. Add argconfig_bind(): it copies a template table whose options store into a template struct and points them to another struct of the same type. A command includes the copy and parses into its own struct. Signed-off-by: Daniel Wagner --- src/argconfig.c | 32 +++++++++++++++++++++ src/argconfig.h | 3 ++ tests/unit/c/test-argconfig-parse.c | 44 +++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/argconfig.c b/src/argconfig.c index 2f169c409e..53fd7e447f 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -684,6 +684,38 @@ static int argconfig_apply(struct argconfig_commandline_options *s, char c, return ret; } +/* + * A copy of the template table @tmpl, whose options store into the + * struct at @tmpl_base of @size bytes, bound to the struct of the same + * type at @base. Commands share the template and parse into their own + * struct. The caller frees the copy. + */ +struct argconfig_commandline_options * +argconfig_bind(const struct argconfig_commandline_options *tmpl, + const void *tmpl_base, size_t size, void *base) +{ + struct argconfig_commandline_options *dst; + uintptr_t start = (uintptr_t)tmpl_base; + uintptr_t v; + size_t n = 0, i; + + while (tmpl[n].option) + n++; + + dst = calloc(n + 1, sizeof(*dst)); + if (!dst) + return NULL; + memcpy(dst, tmpl, (n + 1) * sizeof(*dst)); + + for (i = 0; i < n; i++) { + v = (uintptr_t)tmpl[i].default_value; + if (v >= start && v < start + size) + dst[i].default_value = (char *)base + (v - start); + } + + return dst; +} + /* The name of the enum value @val in the value table @v, or NULL */ const char *argconfig_value_name(const struct argconfig_opt_val *v, int val) { diff --git a/src/argconfig.h b/src/argconfig.h index 981209a3f3..79443b5561 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -291,3 +291,6 @@ bool argconfig_set_value_name(struct argconfig_commandline_options *option, const char *str); const char *argconfig_value_name(const struct argconfig_opt_val *values, int val); +struct argconfig_commandline_options * +argconfig_bind(const struct argconfig_commandline_options *tmpl, + const void *tmpl_base, size_t size, void *base); diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index c661419862..6ee106a838 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -521,6 +521,49 @@ static void include_test(void) } } +/* A template table bound to another struct stores into that one */ +static void bind_test(void) +{ + const char *desc = "Test bind"; + struct pair { + bool flag; + __u32 uint; + }; + static struct pair tmpl_pair; + static struct argconfig_commandline_options tmpl[] = { + OPT_FLAG("flag", 'f', &tmpl_pair.flag, "flag"), + OPT_UINT("uint", 'u', &tmpl_pair.uint, "uint"), + OPT_END() + }; + struct pair local = { 0 }; + struct argconfig_commandline_options *bound; + char *argv[4] = { "prog", "-f", "--uint=5", NULL }; + int ret; + + bound = argconfig_bind(tmpl, &tmpl_pair, sizeof(tmpl_pair), &local); + if (!bound) { + printf("ERROR: bind: no table\n"); + test_rc = 1; + return; + } + + { + OPT_ARGS(opts) = { + OPT_INCLUDE(bound, "Bound options"), + OPT_END() + }; + + ret = argconfig_parse(3, argv, desc, opts); + if (ret || !local.flag || local.uint != 5 || + tmpl_pair.flag || tmpl_pair.uint || + !argconfig_given(opts, &local.uint)) { + printf("ERROR: bind: values not in the bound struct\n"); + test_rc = 1; + } + } + free(bound); +} + /* An enum takes only its names, in any case and as a unique prefix */ static void enum_test(void) { @@ -1066,6 +1109,7 @@ int main(void) old_name_test(); enum_test(); include_test(); + bind_test(); special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) From 802e6ae49553c7f932fa44e72613c44c1ec6b2cc Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:18:52 +0000 Subject: [PATCH 138/263] fabrics: include the shared fabrics options as a table The fabrics commands added their shared options to their tables with the NVMF_ARGS() macro. Define them once as a template table and let nvmf_options() bind it to the struct nvmf_args of a command, which nvmf_args_defaults() starts. connect, discover, connect-all, disconnect, config create and the discovery.conf conversion include the bound table with OPT_INCLUDE() and keep parsing into their own struct. The help lists the options under "Fabrics options". The help of --persistent got shorter to fit into a line. Signed-off-by: Daniel Wagner --- src/config-create.c | 19 ++-- src/fabrics.c | 208 +++++++++++++++++++++++++++++++++----------- src/fabrics.h | 56 ++---------- 3 files changed, 178 insertions(+), 105 deletions(-) diff --git a/src/config-create.c b/src/config-create.c index 1e54091eb8..057064b495 100644 --- a/src/config-create.c +++ b/src/config-create.c @@ -182,7 +182,9 @@ int nvme_config_create(const char *desc, int argc, char **argv) __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct libnvmf_config_emitter *emitter = NULL; struct libnvmf_params *params = NULL; - struct nvmf_args fa = { 0 }; + struct nvmf_args fa = nvmf_args_defaults(); + __cleanup_free struct argconfig_commandline_options *nvmf = + nvmf_options(&fa); char *output_file = NULL; char *hostsymname = NULL; bool discovery = false; @@ -193,14 +195,19 @@ int nvme_config_create(const char *desc, int argc, char **argv) const char *target; int err; - NVMF_ARGS(opts, fa, + OPT_ARGS(opts) = { + OPT_INCLUDE(nvmf, "Fabrics options"), OPT_FLAG("discovery", 0, &discovery, desc_discovery), OPT_STRING_OPTIONAL("persistent", 0, "no|auto|force", - &persistent_arg, desc_persistent), - OPT_STRING("host-symname", 0, "STR", &hostsymname, desc_symname), - OPT_STRING("output", 0, "FILE", &output_file, desc_output)); + &persistent_arg, desc_persistent), + OPT_STRING("host-symname", 0, "STR", &hostsymname, + desc_symname), + OPT_STRING("output", 0, "FILE", &output_file, desc_output), + OPT_END() + }; - nvmf_default_args(&fa); + if (!nvmf) + return -ENOMEM; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/src/fabrics.c b/src/fabrics.c index 0fc2c9cef1..1bac52a1f5 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -71,12 +71,90 @@ static char *raw; static const char *nvmf_config_file = "Use specified INI configuration file (auto-converts a legacy .json) or 'none' to disable"; +static const char *nvmf_persistent = + "persistent discovery connection (default: auto; =no disables)"; static const char *nvmf_config_file_ro = "INI configuration file (default: " PATH_NVMF_INI ")"; -void nvmf_default_args(struct nvmf_args *fa) +/* + * The fabrics options, which store into nvmf_tmpl. nvmf_options() binds + * them to the struct nvmf_args of a command. + */ +static struct nvmf_args nvmf_tmpl; + +static struct argconfig_commandline_options nvmf_template[] = { + OPT_STRING("transport", 't', "STR", &nvmf_tmpl.transport, + DESC_NVMF_TPORT), + OPT_STRING("nqn", 'n', "STR", &nvmf_tmpl.subsysnqn, + DESC_NVMF_NQN), + OPT_STRING("traddr", 'a', "STR", &nvmf_tmpl.traddr, + DESC_NVMF_TRADDR), + OPT_STRING("trsvcid", 's', "STR", &nvmf_tmpl.trsvcid, + DESC_NVMF_TRSVCID), + OPT_STRING("host-traddr", 'w', "STR", &nvmf_tmpl.host_traddr, + DESC_NVMF_HTRADDR), + OPT_STRING("host-iface", 'f', "STR", &nvmf_tmpl.host_iface, + DESC_NVMF_HIFACE), + OPT_STRING("hostnqn", 'q', "STR", &nvmf_tmpl.hostnqn, + DESC_NVMF_HOSTNQN), + OPT_STRING("hostid", 'I', "STR", &nvmf_tmpl.hostid, + DESC_NVMF_HOSTID), + OPT_STRING("kxchap-secret", 'S', "STR", &nvmf_tmpl.hostkey, + DESC_NVMF_HOSTKEY, .old_name = "dhchap-secret"), + OPT_STRING("kxchap-ctrl-secret", 'C', "STR", &nvmf_tmpl.ctrlkey, + DESC_NVMF_CTRLKEY, .old_name = "dhchap-ctrl-secret"), + OPT_STRING("keyring", 0, "STR", &nvmf_tmpl.keyring, + DESC_NVMF_KEYRING), + OPT_STRING("tls-key", 0, "STR", &nvmf_tmpl.tls_key, + DESC_NVMF_TLS_KEY), + OPT_STRING("tls-key-identity", 0, "STR", &nvmf_tmpl.tls_key_identity, + DESC_NVMF_TLS_KEY_IDENTITY), + OPT_INT("nr-io-queues", 'i', &nvmf_tmpl.nr_io_queues, + DESC_NVMF_NR_IO_QUEUES), + OPT_INT("nr-write-queues", 'W', &nvmf_tmpl.nr_write_queues, + DESC_NVMF_NR_WRITE_QUEUES), + OPT_INT("nr-poll-queues", 'P', &nvmf_tmpl.nr_poll_queues, + DESC_NVMF_NR_POLL_QUEUES), + OPT_INT("queue-size", 'Q', &nvmf_tmpl.queue_size, + DESC_NVMF_QUEUE_SIZE), + OPT_INT("keep-alive-tmo", 'k', &nvmf_tmpl.keep_alive_tmo, + DESC_NVMF_KEEP_ALIVE_TMO), + OPT_INT("reconnect-delay", 'c', &nvmf_tmpl.reconnect_delay, + DESC_NVMF_RECONNECT_DELAY), + OPT_INT("ctrl-loss-tmo", 'l', &nvmf_tmpl.ctrl_loss_tmo, + DESC_NVMF_CTRL_LOSS_TMO), + OPT_INT("fast-io-fail-tmo", 'F', &nvmf_tmpl.fast_io_fail_tmo, + DESC_NVMF_FAST_IO_FAIL_TMO, .old_name = "fast_io_fail_tmo"), + OPT_INT("tos", 'T', &nvmf_tmpl.tos, + DESC_NVMF_TOS), + OPT_INT("tls_key", 0, &nvmf_tmpl.tls_key_id, + DESC_NVMF_TLS_KEY_LEGACY), + OPT_FLAG("duplicate-connect", 'D', &nvmf_tmpl.duplicate_connect, + DESC_NVMF_DUP_CONNECT), + OPT_FLAG("disable-sqflow", 0, &nvmf_tmpl.disable_sqflow, + DESC_NVMF_DISABLE_SQFLOW), + OPT_FLAG("hdr-digest", 'g', &nvmf_tmpl.hdr_digest, + DESC_NVMF_HDR_DIGEST), + OPT_FLAG("data-digest", 'G', &nvmf_tmpl.data_digest, + DESC_NVMF_DATA_DIGEST), + OPT_FLAG("tls", 0, &nvmf_tmpl.tls, + DESC_NVMF_TLS), + OPT_FLAG("concat", 0, &nvmf_tmpl.concat, + DESC_NVMF_CONCAT), + OPT_END() +}; + +struct nvmf_args nvmf_args_defaults(void) +{ + return (struct nvmf_args) { + .tos = -1, + .ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO, + }; +} + +struct argconfig_commandline_options *nvmf_options(struct nvmf_args *fa) { - fa->tos = -1; - fa->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; + return argconfig_bind(nvmf_template, &nvmf_tmpl, sizeof(nvmf_tmpl), + fa); } void nvmf_args_to_params(struct libnvmf_params *params, @@ -564,10 +642,10 @@ const char *nvmf_resolve_persistent_arg(bool given, const char *arg) /* * Parse one discovery.conf line -- the same argv-style syntax 'nvme - * discover'/'connect-all' accept, reusing NVMF_ARGS so every short and long - * form works identically to real usage -- and hand the parsed arguments to - * nvme_config_convert_discovery_args(). @line is modified in place - * (shr_strsep()). + * discover'/'connect-all' accept, reusing nvmf_options() so every short + * and long form works identically to real usage -- and hand the parsed + * arguments to nvme_config_convert_discovery_args(). @line is modified in + * place (shr_strsep()). * * A blank line, a comment, or one with neither transport nor traddr is * silently skipped (0, nothing added), tolerating a discovery.conf that @@ -576,27 +654,30 @@ const char *nvmf_resolve_persistent_arg(bool given, const char *arg) int nvmf_convert_discovery_line(struct libnvmf_config_emitter *emitter, char *line) { - struct nvmf_args fa = { 0 }; + struct nvmf_args fa = nvmf_args_defaults(); + __cleanup_free struct argconfig_commandline_options *nvmf = + nvmf_options(&fa); char *argv[MAX_DISC_ARGS] = { "discovery.conf" }; char *ptr, *p = line; int argc = 1; bool no_reuse = false; char *persistent_arg = NULL; - NVMF_ARGS(opts, fa, - OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force", - &persistent_arg, - "persistent discovery connection mode " - "(default: auto; use =no to disable)"), - OPT_FLAG("no-reuse", 0, &no_reuse, - "always create a new connection, never reuse an existing one", - .old_name = "force")); + OPT_ARGS(opts) = { + OPT_INCLUDE(nvmf, "Fabrics options"), + OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force", + &persistent_arg, nvmf_persistent), + OPT_FLAG("no-reuse", 0, &no_reuse, + "always create a new connection, never reuse an existing one", + .old_name = "force"), + OPT_END() + }; if (line[0] == '#' || line[0] == '\n' || line[0] == '\0') return 0; - fa.tos = -1; - fa.ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; + if (!nvmf) + return -ENOMEM; /* * Split on whitespace only, not '=' -- a glued "--opt=value" token @@ -863,7 +944,9 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) __cleanup_nvmf_context struct libnvmf_context *fctx = NULL; __cleanup_free char *resolved_traddr = NULL; int ret; - struct nvmf_args fa = { .subsysnqn = NVME_DISC_SUBSYS_NAME }; + struct nvmf_args fa = nvmf_args_defaults(); + __cleanup_free struct argconfig_commandline_options *nvmf = + nvmf_options(&fa); char *device = NULL; bool no_reuse = false; char *persistent_arg = NULL; @@ -872,23 +955,30 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) char *nbft_path = NBFT_SYSFS_PATH; char *owner = NULL; - NVMF_ARGS(opts, fa, - OPT_STRING("device", 'd', "DEV", &device, "use existing discovery controller device"), - OPT_FILE("raw", 'r', &raw, "save raw output to file"), - OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force", - &persistent_arg, - "persistent discovery connection mode " - "(default: auto; use =no to disable)"), - OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file), - OPT_FLAG("no-reuse", 0, &no_reuse, - "always create a new connection, never reuse an existing one", - .old_name = "force"), - OPT_FLAG("nbft", 0, &nbft, "Only look at NBFT tables"), - OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"), - OPT_STRING("owner", 0, "NAME", &owner, "record this owner in the registry"), - OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables")); - - nvmf_default_args(&fa); + OPT_ARGS(opts) = { + OPT_INCLUDE(nvmf, "Fabrics options"), + OPT_STRING("device", 'd', "DEV", &device, + "use existing discovery controller device"), + OPT_FILE("raw", 'r', &raw, "save raw output to file"), + OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force", + &persistent_arg, nvmf_persistent), + OPT_STRING("config", 'J', "FILE", &config_file, + nvmf_config_file), + OPT_FLAG("no-reuse", 0, &no_reuse, + "always create a new connection, never reuse an existing one", + .old_name = "force"), + OPT_FLAG("nbft", 0, &nbft, "Only look at NBFT tables"), + OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"), + OPT_STRING("owner", 0, "NAME", &owner, + "record this owner in the registry"), + OPT_STRING("nbft-path", 0, "STR", &nbft_path, + "user-defined path for NBFT tables"), + OPT_END() + }; + + fa.subsysnqn = NVME_DISC_SUBSYS_NAME; + if (!nvmf) + return -ENOMEM; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -1072,19 +1162,27 @@ int fabrics_connect(const char *desc, int argc, char **argv) __cleanup_free char *resolved_traddr = NULL; int ret; nvme_print_flags_t flags; - struct nvmf_args fa = { 0 }; + struct nvmf_args fa = nvmf_args_defaults(); + __cleanup_free struct argconfig_commandline_options *nvmf = + nvmf_options(&fa); - NVMF_ARGS(opts, fa, - OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file), - OPT_STRING("owner", 0, "NAME", &owner, "record this owner in the registry"), - OPT_STRING("devid-file", 0, "FILE", &devid_file, - "write connected device name to FILE"), - OPT_FLAG("idempotent", 0, &idempotent, - "exit 0 if already connected"), - OPT_FLAG("discovery", 0, &discovery, - "the target is a discovery controller")); + OPT_ARGS(opts) = { + OPT_INCLUDE(nvmf, "Fabrics options"), + OPT_STRING("config", 'J', "FILE", &config_file, + nvmf_config_file), + OPT_STRING("owner", 0, "NAME", &owner, + "record this owner in the registry"), + OPT_STRING("devid-file", 0, "FILE", &devid_file, + "write connected device name to FILE"), + OPT_FLAG("idempotent", 0, &idempotent, + "exit 0 if already connected"), + OPT_FLAG("discovery", 0, &discovery, + "the target is a discovery controller"), + OPT_END() + }; - nvmf_default_args(&fa); + if (!nvmf) + return -ENOMEM; ret = parse_args(argc, argv, desc, opts); if (ret) @@ -1438,7 +1536,9 @@ int fabrics_disconnect(const char *desc, int argc, char **argv) __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_free char *resolved_traddr = NULL; - struct nvmf_args fa = { 0 }; + struct nvmf_args fa = nvmf_args_defaults(); + __cleanup_free struct argconfig_commandline_options *nvmf = + nvmf_options(&fa); bool match_args; int err; @@ -1449,11 +1549,15 @@ int fabrics_disconnect(const char *desc, int argc, char **argv) struct config cfg = { 0 }; - NVMF_ARGS(opts, fa, - OPT_STRING("device", 'd', "DEV", &cfg.device, device), - OPT_FLAG("exclude", 'x', &cfg.exclude, exclude_help)); + OPT_ARGS(opts) = { + OPT_INCLUDE(nvmf, "Fabrics options"), + OPT_STRING("device", 'd', "DEV", &cfg.device, device), + OPT_FLAG("exclude", 'x', &cfg.exclude, exclude_help), + OPT_END() + }; - nvmf_default_args(&fa); + if (!nvmf) + return -ENOMEM; err = parse_args(argc, argv, desc, opts); if (err) diff --git a/src/fabrics.h b/src/fabrics.h index 972b9df123..ada1d570c2 100644 --- a/src/fabrics.h +++ b/src/fabrics.h @@ -38,7 +38,7 @@ struct nvmf_args { bool concat; }; -/* Helper text for NVMF_ARGS */ +/* Helper text for the fabrics options */ #define DESC_NVMF_TPORT "transport type" #define DESC_NVMF_TRADDR "transport address" #define DESC_NVMF_NQN "subsystem nqn" @@ -70,51 +70,15 @@ struct nvmf_args { #define DESC_NVMF_CONCAT "enable secure concatenation" /* - * Common "nvme connect"/"discover"/"connect-all" options, shared by every - * command that builds a struct nvmf_args from the command line -- including - * config-create.c, which adds an entry to the INI configuration instead of - * connecting. + * The options which every command shares that builds a struct nvmf_args + * from the command line -- connect, discover, connect-all, disconnect and + * config create, which adds an entry to the INI configuration instead of + * connecting. nvmf_options() returns them bound to @fa, which the + * command includes with OPT_INCLUDE() and frees. Start @fa with + * nvmf_args_defaults(). */ -#define NVMF_ARGS(n, f, ...) \ - struct argconfig_commandline_options n[] = { \ - OPT_STRING("transport", 't', "STR", &f.transport, DESC_NVMF_TPORT), \ - OPT_STRING("nqn", 'n', "STR", &f.subsysnqn, DESC_NVMF_NQN), \ - OPT_STRING("traddr", 'a', "STR", &f.traddr, DESC_NVMF_TRADDR), \ - OPT_STRING("trsvcid", 's', "STR", &f.trsvcid, DESC_NVMF_TRSVCID), \ - OPT_STRING("host-traddr", 'w', "STR", &f.host_traddr, DESC_NVMF_HTRADDR), \ - OPT_STRING("host-iface", 'f', "STR", &f.host_iface, DESC_NVMF_HIFACE), \ - OPT_STRING("hostnqn", 'q', "STR", &f.hostnqn, DESC_NVMF_HOSTNQN), \ - OPT_STRING("hostid", 'I', "STR", &f.hostid, DESC_NVMF_HOSTID), \ - OPT_STRING("kxchap-secret", 'S', "STR", &f.hostkey, \ - DESC_NVMF_HOSTKEY, \ - .old_name = "dhchap-secret"), \ - OPT_STRING("kxchap-ctrl-secret", 'C', "STR", &f.ctrlkey, \ - DESC_NVMF_CTRLKEY, \ - .old_name = "dhchap-ctrl-secret"), \ - OPT_STRING("keyring", 0, "STR", &f.keyring, DESC_NVMF_KEYRING), \ - OPT_STRING("tls-key", 0, "STR", &f.tls_key, DESC_NVMF_TLS_KEY), \ - OPT_STRING("tls-key-identity", 0, "STR", &f.tls_key_identity, DESC_NVMF_TLS_KEY_IDENTITY), \ - OPT_INT("nr-io-queues", 'i', &f.nr_io_queues, DESC_NVMF_NR_IO_QUEUES), \ - OPT_INT("nr-write-queues", 'W', &f.nr_write_queues, DESC_NVMF_NR_WRITE_QUEUES), \ - OPT_INT("nr-poll-queues", 'P', &f.nr_poll_queues, DESC_NVMF_NR_POLL_QUEUES), \ - OPT_INT("queue-size", 'Q', &f.queue_size, DESC_NVMF_QUEUE_SIZE), \ - OPT_INT("keep-alive-tmo", 'k', &f.keep_alive_tmo, DESC_NVMF_KEEP_ALIVE_TMO), \ - OPT_INT("reconnect-delay", 'c', &f.reconnect_delay, DESC_NVMF_RECONNECT_DELAY), \ - OPT_INT("ctrl-loss-tmo", 'l', &f.ctrl_loss_tmo, DESC_NVMF_CTRL_LOSS_TMO), \ - OPT_INT("fast-io-fail-tmo", 'F', &f.fast_io_fail_tmo, \ - DESC_NVMF_FAST_IO_FAIL_TMO, \ - .old_name = "fast_io_fail_tmo"), \ - OPT_INT("tos", 'T', &f.tos, DESC_NVMF_TOS), \ - OPT_INT("tls_key", 0, &f.tls_key_id, DESC_NVMF_TLS_KEY_LEGACY), \ - OPT_FLAG("duplicate-connect", 'D', &f.duplicate_connect, DESC_NVMF_DUP_CONNECT), \ - OPT_FLAG("disable-sqflow", 0, &f.disable_sqflow, DESC_NVMF_DISABLE_SQFLOW), \ - OPT_FLAG("hdr-digest", 'g', &f.hdr_digest, DESC_NVMF_HDR_DIGEST), \ - OPT_FLAG("data-digest", 'G', &f.data_digest, DESC_NVMF_DATA_DIGEST), \ - OPT_FLAG("tls", 0, &f.tls, DESC_NVMF_TLS), \ - OPT_FLAG("concat", 0, &f.concat, DESC_NVMF_CONCAT), \ - ##__VA_ARGS__, \ - OPT_END() \ - } +struct argconfig_commandline_options *nvmf_options(struct nvmf_args *fa); +struct nvmf_args nvmf_args_defaults(void); /* * Resolve an OPT_STRING_OPTIONAL() "--persistent[=no|auto|force]" argument @@ -135,8 +99,6 @@ int fabrics_config_validate(const char *desc, int argc, char **argv); int fabrics_config_show(const char *desc, int argc, char **argv); int fabrics_dim(const char *desc, int argc, char **argv); -/* Apply NVMF_ARGS's defaults (tos, ctrl_loss_tmo) before parse_args(). */ -void nvmf_default_args(struct nvmf_args *fa); struct libnvmf_params; From e08d50e69eb93c463bbc5bec347f04118af0dc29 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:21:23 +0000 Subject: [PATCH 139/263] plugins/feat: include the feature options as a table The feature commands added --save and --sel to their tables with the FEAT_ARGS() macro, which needed an extra group to drop the comma of an empty argument list. Define them once as a template table and let feat_options() bind it to a struct feat_args, which each command's config embeds instead of its own sel and save. The commands include the bound table after their own options, so that an option of the same short name still comes before --save, as for lba-range-type. The help lists them under "Feature options". Signed-off-by: Daniel Wagner --- plugins/feat/feat-nvme.c | 523 ++++++++++++++++++++++++++------------- 1 file changed, 353 insertions(+), 170 deletions(-) diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index d69f5c1cf5..80c05e4275 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -37,20 +37,12 @@ #define ASYNC_EVENT_CONF_DESC "Get and set async event configuration feature" #define KEEP_ALIVE_TIMER_DESC "Get and set keep alive timer feature" -/* - * The options of the command first: an option of the same short name - * comes before --save. The group in front lets an empty argument list - * drop its comma. - */ -#define FEAT_ARGS_SAVE(n, save_short, ...) \ - struct argconfig_commandline_options n[] = { \ - OPT_GROUP("Options"), ##__VA_ARGS__, \ - OPT_FLAG("save", save_short, &cfg.save, save), \ - OPT_BYTE("sel", 'S', &cfg.sel, sel), \ - OPT_END() \ - } +/* --save and --sel, which every feature command takes */ +struct feat_args { + __u8 sel; + bool save; +}; -#define FEAT_ARGS(n, ...) FEAT_ARGS_SAVE(n, 's', ##__VA_ARGS__) #define STR(x) #x #define TMT(n) "thermal management temperature " STR(n) @@ -63,8 +55,7 @@ struct perfc_config { char *paid; __u16 attrl; char *vs_data; - __u8 sel; - bool save; + struct feat_args feat; }; struct temp_thresh_config { @@ -72,8 +63,7 @@ struct temp_thresh_config { __u8 tmpsel; __u8 thsel; __u8 tmpthh; - __u8 sel; - bool save; + struct feat_args feat; }; struct arbitration_config { @@ -81,15 +71,13 @@ struct arbitration_config { __u8 lpw; __u8 mpw; __u8 hpw; - __u8 sel; - bool save; + struct feat_args feat; }; struct err_recovery_config { __u8 tler; __u8 dulbe; - __u8 sel; - bool save; + struct feat_args feat; }; struct host_behavior_config { @@ -98,8 +86,7 @@ struct host_behavior_config { __u8 lbafee; __u8 hdisns; __u16 cdfe; - __u8 sel; - bool save; + struct feat_args feat; }; struct lba_range_type_config { @@ -111,39 +98,64 @@ struct lba_range_type_config { char *slbas; char *nlbs; char *guids; - __u8 sel; - bool save; + struct feat_args feat; }; struct int_coalesce_config { __u8 thr; __u8 time; - __u8 sel; - bool save; + struct feat_args feat; }; struct int_vec_conf { __u16 iv; bool cd; - __u8 sel; - bool save; + struct feat_args feat; }; struct write_atom_normal_config { bool dn; - __u8 sel; - bool save; + struct feat_args feat; }; struct keep_alive_timer_config { __u32 kato; - __u8 sel; - bool save; + struct feat_args feat; }; static const char *power_mgmt_feat = "power management feature"; -static const char *sel = "[0-3]: current/default/saved/supported"; -static const char *save = "Specifies that the controller shall save the attribute"; +static const char sel[] = "[0-3]: current/default/saved/supported"; +static const char save[] = + "Specifies that the controller shall save the attribute"; + +/* + * The feature options, which store into feat_tmpl. feat_options() binds + * them to the struct feat_args of a command, which includes them after + * its own options, so that an option of the same short name comes before + * --save. + */ +static struct feat_args feat_tmpl; + +static struct argconfig_commandline_options feat_template[] = { + OPT_FLAG("save", 's', &feat_tmpl.save, save), + OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel), + OPT_END() +}; + +/* For a command which uses -s itself */ +static struct argconfig_commandline_options feat_template_save_long[] = { + OPT_FLAG("save", 0, &feat_tmpl.save, save), + OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel), + OPT_END() +}; + +static struct argconfig_commandline_options * +feat_options(struct feat_args *feat, bool save_short) +{ + return argconfig_bind(save_short ? feat_template : + feat_template_save_long, + &feat_tmpl, sizeof(feat_tmpl), feat); +} static const char *perfc_feat = "performance characteristics feature"; static const char *hctm_feat = "host controlled thermal management feature"; static const char *timestamp_feat = "timestamp feature"; @@ -241,15 +253,23 @@ static int feat_power_mgmt(int argc, char **argv, struct command *acmd, struct p struct config { __u8 ps; __u8 wh; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_BYTE("ps", 'p', &cfg.ps, ps), - OPT_BYTE("wh", 'w', &cfg.wh, wh)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("ps", 'p', &cfg.ps, ps), + OPT_BYTE("wh", 'w', &cfg.wh, wh), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, POWER_MGMT_DESC, opts); if (err) @@ -257,9 +277,9 @@ static int feat_power_mgmt(int argc, char **argv, struct command *acmd, struct p if (argconfig_given(opts, &cfg.ps)) err = power_mgmt_set(hdl, fid, cfg.ps, cfg.wh, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, power_mgmt_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, power_mgmt_feat); return err; } @@ -334,15 +354,24 @@ static int feat_perfc(int argc, char **argv, struct command *acmd, struct plugin struct perfc_config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, - namespace_id_optional, nvme_nsid_values), - OPT_BYTE("attri", 'a', &cfg.attri, attri), - OPT_FLAG("rvspa", 'r', &cfg.rvspa, rvspa), - OPT_BYTE("r4karl", 'R', &cfg.r4karl, r4karl), - OPT_STR("paid", 'p', &cfg.paid, paid), - OPT_SHRT("attrl", 'A', &cfg.attrl, attrl), - OPT_FILE("vs-data", 'V', &cfg.vs_data, vs_data)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, + namespace_id_optional, nvme_nsid_values), + OPT_BYTE("attri", 'a', &cfg.attri, attri), + OPT_FLAG("rvspa", 'r', &cfg.rvspa, rvspa), + OPT_BYTE("r4karl", 'R', &cfg.r4karl, r4karl), + OPT_STR("paid", 'p', &cfg.paid, paid), + OPT_SHRT("attrl", 'A', &cfg.attrl, attrl), + OPT_FILE("vs-data", 'V', &cfg.vs_data, vs_data), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, PERFC_DESC, opts); if (err) @@ -354,9 +383,9 @@ static int feat_perfc(int argc, char **argv, struct command *acmd, struct plugin argconfig_given(opts, &cfg.r4karl) || argconfig_given(opts, &cfg.paid)) err = perfc_set(hdl, fid, cdw11, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, cdw11, cfg.sel, 0, perfc_feat); + err = feat_get(hdl, fid, cdw11, cfg.feat.sel, 0, perfc_feat); return err; } @@ -394,15 +423,23 @@ static int feat_hctm(int argc, char **argv, struct command *acmd, struct plugin struct config { __u16 tmt1; __u16 tmt2; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_SHRT("tmt1", 't', &cfg.tmt1, TMT(1)), - OPT_SHRT("tmt2", 'T', &cfg.tmt2, TMT(2))); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_SHRT("tmt1", 't', &cfg.tmt1, TMT(1)), + OPT_SHRT("tmt2", 'T', &cfg.tmt2, TMT(2)), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, HCTM_DESC, opts); if (err) @@ -411,9 +448,9 @@ static int feat_hctm(int argc, char **argv, struct command *acmd, struct plugin if (argconfig_given(opts, &cfg.tmt1) || argconfig_given(opts, &cfg.tmt2)) err = hctm_set(hdl, fid, cfg.tmt1, cfg.tmt2, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, hctm_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, hctm_feat); return err; } @@ -453,13 +490,22 @@ static int feat_timestamp(int argc, char **argv, struct command *acmd, struct pl struct config { __u64 tstmp; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, OPT_LONG("tstmp", 't', &cfg.tstmp, tstmp)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_LONG("tstmp", 't', &cfg.tstmp, tstmp), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, TIMESTAMP_DESC, opts); if (err) @@ -467,9 +513,9 @@ static int feat_timestamp(int argc, char **argv, struct command *acmd, struct pl if (argconfig_given(opts, &cfg.tstmp)) err = timestamp_set(hdl, fid, cfg.tstmp, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, timestamp_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, timestamp_feat); return err; } @@ -487,7 +533,7 @@ static int temp_thresh_set(struct libnvme_transport_handle *hdl, const __u8 fid, int err; bool sv; - sv = cfg->save; + sv = cfg->feat.save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -534,11 +580,20 @@ static int feat_temp_thresh(int argc, char **argv, struct command *acmd, struct struct temp_thresh_config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_SHRT("tmpth", 'T', &cfg.tmpth, tmpth), - OPT_BYTE("tmpsel", 'm', &cfg.tmpsel, tmpsel), - OPT_BYTE("thsel", 0, &cfg.thsel, thsel), - OPT_BYTE("tmpthh", 'M', &cfg.tmpthh, tmpthh)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_SHRT("tmpth", 'T', &cfg.tmpth, tmpth), + OPT_BYTE("tmpsel", 'm', &cfg.tmpsel, tmpsel), + OPT_BYTE("thsel", 0, &cfg.thsel, thsel), + OPT_BYTE("tmpthh", 'M', &cfg.tmpthh, tmpthh), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, TEMP_THRESH_DESC, opts); if (err) @@ -549,8 +604,8 @@ static int feat_temp_thresh(int argc, char **argv, struct command *acmd, struct err = temp_thresh_set(hdl, fid, opts, &cfg); else err = feat_get(hdl, fid, NVME_SET(cfg.tmpsel, FEAT_TT_TMPSEL) | - NVME_SET(cfg.thsel, FEAT_TT_THSEL), cfg.sel, 0, - temp_thresh_feat); + NVME_SET(cfg.thsel, FEAT_TT_THSEL), + cfg.feat.sel, 0, temp_thresh_feat); return err; } @@ -565,7 +620,7 @@ static int arbitration_set(struct libnvme_transport_handle *hdl, const __u8 fid, bool sv; int err; - sv = cfg->save; + sv = cfg->feat.save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -618,18 +673,28 @@ static int feat_arbitration(int argc, char **argv, struct command *acmd, struct struct arbitration_config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_BYTE("ab", 'a', &cfg.ab, ab), - OPT_BYTE("lpw", 'l', &cfg.lpw, lpw), - OPT_BYTE("mpw", 'm', &cfg.mpw, mpw), - OPT_BYTE("hpw", 0, &cfg.hpw, hpw)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("ab", 'a', &cfg.ab, ab), + OPT_BYTE("lpw", 'l', &cfg.lpw, lpw), + OPT_BYTE("mpw", 'm', &cfg.mpw, mpw), + OPT_BYTE("hpw", 0, &cfg.hpw, hpw), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, ARBITRATION_DESC, opts); if (err) return err; - if (argc == 2 || argconfig_given(opts, &cfg.sel)) - return feat_get(hdl, fid, 0, cfg.sel, 0, "arbitration feature"); + if (argc == 2 || argconfig_given(opts, &cfg.feat.sel)) + return feat_get(hdl, fid, 0, cfg.feat.sel, 0, + "arbitration feature"); return arbitration_set(hdl, fid, opts, &cfg); } @@ -666,13 +731,22 @@ static int feat_volatile_wc(int argc, char **argv, struct command *acmd, struct struct config { bool wce; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, OPT_FLAG("wce", 'w', &cfg.wce, wce)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_FLAG("wce", 'w', &cfg.wce, wce), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, VOLATILE_WC_DESC, opts); if (err) @@ -680,9 +754,9 @@ static int feat_volatile_wc(int argc, char **argv, struct command *acmd, struct if (argconfig_given(opts, &cfg.wce)) err = volatile_wc_set(hdl, fid, cfg.wce, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, volatile_wc_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, volatile_wc_feat); return err; } @@ -726,16 +800,24 @@ static int feat_power_limit(int argc, char **argv, struct command *acmd, __u8 plv; __u8 pls; __u8 uidx; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_BYTE("plv", 'p', &cfg.plv, plv), - OPT_BYTE("pls", 'l', &cfg.pls, pls), - OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("plv", 'p', &cfg.plv, plv), + OPT_BYTE("pls", 'l', &cfg.pls, pls), + OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, POWER_LIMIT_DESC, opts); if (err) @@ -743,9 +825,9 @@ static int feat_power_limit(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.plv)) err = power_limit_set(hdl, fid, cfg.plv, cfg.pls, cfg.uidx, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, cfg.uidx, + err = feat_get(hdl, fid, 0, cfg.feat.sel, cfg.uidx, power_limit_feat); return err; @@ -797,18 +879,26 @@ static int feat_power_thresh(int argc, char **argv, struct command *acmd, __u8 pmts; __u8 ept; __u8 uidx; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_SHRT("ptv", 'p', &cfg.ptv, ptv), - OPT_BYTE("pts", 't', &cfg.pts, pts), - OPT_BYTE("pmts", 'm', &cfg.pmts, pmts), - OPT_BYTE("ept", 'e', &cfg.ept, ept), - OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_SHRT("ptv", 'p', &cfg.ptv, ptv), + OPT_BYTE("pts", 't', &cfg.pts, pts), + OPT_BYTE("pmts", 'm', &cfg.pmts, pmts), + OPT_BYTE("ept", 'e', &cfg.ept, ept), + OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, POWER_THRESH_DESC, opts); if (err) @@ -820,9 +910,9 @@ static int feat_power_thresh(int argc, char **argv, struct command *acmd, argconfig_given(opts, &cfg.ept)) err = power_thresh_set(hdl, fid, cfg.ptv, cfg.pts, cfg.pmts, cfg.ept, cfg.uidx, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, cfg.uidx, + err = feat_get(hdl, fid, 0, cfg.feat.sel, cfg.uidx, power_thresh_feat); return err; @@ -870,17 +960,25 @@ static int feat_power_meas(int argc, char **argv, struct command *cmd, __u8 pmts; __u16 smt; __u8 uidx; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_BYTE("act", 0, &cfg.act, action), - OPT_BYTE("pmts", 0, &cfg.pmts, pmts), - OPT_SHRT("smt", 0, &cfg.smt, smt), - OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("act", 0, &cfg.act, action), + OPT_BYTE("pmts", 0, &cfg.pmts, pmts), + OPT_SHRT("smt", 0, &cfg.smt, smt), + OPT_BYTE("uuid-index", 'u', &cfg.uidx, uuid_index), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, POWER_MEAS_DESC, opts); if (err) @@ -891,9 +989,9 @@ static int feat_power_meas(int argc, char **argv, struct command *cmd, argconfig_given(opts, &cfg.smt)) err = power_meas_set(hdl, fid, cfg.act, cfg.pmts, cfg.smt, cfg.uidx, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, power_meas_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, power_meas_feat); return err; } @@ -938,17 +1036,25 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, __u32 nsid; __u16 tler; bool dulbe; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, - nvme_nsid_values), - OPT_SHRT("tler", 't', &cfg.tler, tler), - OPT_FLAG("dulbe", 'd', &cfg.dulbe, dulbe)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_SHRT("tler", 't', &cfg.tler, tler), + OPT_FLAG("dulbe", 'd', &cfg.dulbe, dulbe), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, ERR_RECOVERY_DESC, opts); if (err) @@ -965,9 +1071,9 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.tler) || argconfig_given(opts, &cfg.dulbe)) err = err_recovery_set(hdl, fid, cfg.nsid, cfg.tler, cfg.dulbe, - cfg.save); + cfg.feat.save); else - err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.sel, 0, + err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.feat.sel, 0, err_recovery_feat); return err; @@ -1026,15 +1132,23 @@ static int feat_num_queues(int argc, char **argv, struct command *acmd, struct config { __u16 nsqr; __u16 ncqr; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_SHRT("nsqr", 'n', &cfg.nsqr, nsqr), - OPT_SHRT("ncqr", 'c', &cfg.ncqr, ncqr)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_SHRT("nsqr", 'n', &cfg.nsqr, nsqr), + OPT_SHRT("ncqr", 'c', &cfg.ncqr, ncqr), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, NUM_QUEUES_DESC, opts); if (err) @@ -1043,11 +1157,11 @@ static int feat_num_queues(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.nsqr) || argconfig_given(opts, &cfg.ncqr)) err = num_queues_set(hdl, fid, cfg.nsqr, cfg.ncqr, - cfg.save, + cfg.feat.save, argconfig_given(opts, &cfg.nsqr), argconfig_given(opts, &cfg.ncqr)); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, num_queues_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, num_queues_feat); return err; } @@ -1112,12 +1226,21 @@ static int feat_host_behavior_support(int argc, char **argv, struct command *acm struct host_behavior_config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_BYTE("acre", 'a', &cfg.acre, acre_desc), - OPT_BYTE("etdas", 'e', &cfg.etdas, etdas_desc), - OPT_BYTE("lbafee", 'l', &cfg.lbafee, lbafee_desc), - OPT_BYTE("hdisns", 0, &cfg.hdisns, hdisns_desc), - OPT_SHRT("cdfe", 'c', &cfg.cdfe, cdfe_desc)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("acre", 'a', &cfg.acre, acre_desc), + OPT_BYTE("etdas", 'e', &cfg.etdas, etdas_desc), + OPT_BYTE("lbafee", 'l', &cfg.lbafee, lbafee_desc), + OPT_BYTE("hdisns", 0, &cfg.hdisns, hdisns_desc), + OPT_SHRT("cdfe", 'c', &cfg.cdfe, cdfe_desc), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, HOST_BEHAVIOR_DESC, opts); if (err) @@ -1129,9 +1252,10 @@ static int feat_host_behavior_support(int argc, char **argv, struct command *acm argconfig_given(opts, &cfg.hdisns) || argconfig_given(opts, &cfg.cdfe)) err = host_behavior_set(hdl, fid, opts, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, host_behavior_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, + host_behavior_feat); return err; } @@ -1254,16 +1378,25 @@ static int feat_lba_range_type(int argc, char **argv, struct command *acmd, int err; /* -s is --slbas and -h is help, --save and --hlbars are long only */ - FEAT_ARGS_SAVE(opts, 0, - OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, - nvme_nsid_values), - OPT_BYTE("num", 'N', &cfg.num, num_desc), - OPT_LIST("types", 't', &cfg.types, types_desc), - OPT_LIST("lbaros", 'l', &cfg.lbaros, lbaros_desc), - OPT_LIST("hlbars", 0, &cfg.hlbars, hlbars_desc), - OPT_LIST("slbas", 's', &cfg.slbas, slbas_desc), - OPT_LIST("nlbs", 'b', &cfg.nlbs, nlbs_desc), - OPT_LIST("guids", 'g', &cfg.guids, guids_desc)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, false); + + OPT_ARGS(opts) = { + OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired, + nvme_nsid_values), + OPT_BYTE("num", 'N', &cfg.num, num_desc), + OPT_LIST("types", 't', &cfg.types, types_desc), + OPT_LIST("lbaros", 'l', &cfg.lbaros, lbaros_desc), + OPT_LIST("hlbars", 0, &cfg.hlbars, hlbars_desc), + OPT_LIST("slbas", 's', &cfg.slbas, slbas_desc), + OPT_LIST("nlbs", 'b', &cfg.nlbs, nlbs_desc), + OPT_LIST("guids", 'g', &cfg.guids, guids_desc), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, HOST_BEHAVIOR_DESC, opts); if (err) @@ -1279,9 +1412,9 @@ static int feat_lba_range_type(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.num)) err = lba_range_type_set(hdl, fid, opts, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.sel, 0, + err = feat_get_nsid(hdl, cfg.nsid, fid, 0, cfg.feat.sel, 0, lba_range_feat); return err; @@ -1322,9 +1455,18 @@ static int feat_int_coalesce(int argc, char **argv, struct command *acmd, struct int_coalesce_config cfg = { 0 }; int err; - FEAT_ARGS(opts, - OPT_BYTE("thr", 't', &cfg.thr, thr), - OPT_BYTE("time", 'T', &cfg.time, time)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_BYTE("thr", 't', &cfg.thr, thr), + OPT_BYTE("time", 'T', &cfg.time, time), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, INT_COALESCE_DESC, opts); if (err) @@ -1333,9 +1475,9 @@ static int feat_int_coalesce(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.thr) || argconfig_given(opts, &cfg.time)) err = int_coalesce_set(hdl, fid, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, int_coalesce_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, int_coalesce_feat); return err; } @@ -1374,9 +1516,18 @@ static int feat_int_vec_conf(int argc, char **argv, struct command *acmd, struct int_vec_conf cfg = { 0 }; int err; - FEAT_ARGS(opts, - OPT_SHRT("iv", 'i', &cfg.iv, iv), - OPT_FLAG("cd", 'c', &cfg.cd, cd)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_SHRT("iv", 'i', &cfg.iv, iv), + OPT_FLAG("cd", 'c', &cfg.cd, cd), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, INT_VEC_CONF_DESC, opts); if (err) @@ -1385,9 +1536,9 @@ static int feat_int_vec_conf(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.iv) || argconfig_given(opts, &cfg.cd)) err = int_vec_conf_set(hdl, fid, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, int_vec_conf_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, int_vec_conf_feat); return err; } @@ -1425,7 +1576,17 @@ static int feat_write_atom_normal(int argc, char **argv, struct command *acmd, const char *dn = "disable normal"; int err; - FEAT_ARGS(opts, OPT_FLAG("dn", 'd', &cfg.dn, dn)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_FLAG("dn", 'd', &cfg.dn, dn), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, WRITE_ATOM_NORMAL_DESC, opts); @@ -1434,9 +1595,10 @@ static int feat_write_atom_normal(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.dn)) err = write_atom_normal_set(hdl, fid, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, write_atom_normal_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, + write_atom_normal_feat); return err; } @@ -1473,13 +1635,22 @@ static int feat_async_event_conf(int argc, char **argv, struct command *acmd, struct config { __u32 result; - __u8 sel; - bool save; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, OPT_UINT("result", 'r', &cfg.result, result_desc)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_UINT("result", 'r', &cfg.result, result_desc), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, ASYNC_EVENT_CONF_DESC, opts); @@ -1488,9 +1659,10 @@ static int feat_async_event_conf(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.result)) err = async_event_conf_set(hdl, fid, cfg.result, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, async_event_conf_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, + async_event_conf_feat); return err; } @@ -1527,7 +1699,17 @@ static int feat_keep_alive_timer(int argc, char **argv, struct command *acmd, const __u8 fid = NVME_FEAT_FID_KATO; int err; - FEAT_ARGS(opts, OPT_UINT("kato", 'k', &cfg.kato, kato)); + __cleanup_free struct argconfig_commandline_options *feat = + feat_options(&cfg.feat, true); + + OPT_ARGS(opts) = { + OPT_UINT("kato", 'k', &cfg.kato, kato), + OPT_INCLUDE(feat, "Feature options"), + OPT_END() + }; + + if (!feat) + return -ENOMEM; err = parse_and_open(&ctx, &hdl, argc, argv, KEEP_ALIVE_TIMER_DESC, opts); @@ -1536,9 +1718,10 @@ static int feat_keep_alive_timer(int argc, char **argv, struct command *acmd, if (argconfig_given(opts, &cfg.kato)) err = keep_alive_timer_set(hdl, fid, &cfg, - cfg.save); + cfg.feat.save); else - err = feat_get(hdl, fid, 0, cfg.sel, 0, keep_alive_timer_feat); + err = feat_get(hdl, fid, 0, cfg.feat.sel, 0, + keep_alive_timer_feat); return err; } From ac15ca8f645c796f5b8c31abfc7ab2c51c54960c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:29:21 +0000 Subject: [PATCH 140/263] argconfig: drop the license text and update the copyright The SPDX identifier names the license, so drop the license text which repeats it, and the date and description below it, as the other files do. Add the copyright and the author of the parser rewrite. Signed-off-by: Daniel Wagner --- src/argconfig.c | 31 +++++-------------------------- src/argconfig.h | 33 +++++---------------------------- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 53fd7e447f..00dd000a0c 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -1,32 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright 2014 PMC-Sierra, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * - * Author: Logan Gunthorpe - * - * Date: Oct 23 2014 - * - * Description: - * Functions for parsing command line options. + * This file is part of nvme-cli. + * Copyright (c) 2014 PMC-Sierra, Inc. + * Copyright (c) 2026 SUSE Software Solutions * + * Authors: Logan Gunthorpe + * Daniel Wagner */ #include diff --git a/src/argconfig.h b/src/argconfig.h index 79443b5561..e073668db0 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -1,34 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* + * This file is part of nvme-cli. + * Copyright (c) 2014 PMC-Sierra, Inc. + * Copyright (c) 2026 SUSE Software Solutions * - * Copyright 2014 PMC-Sierra, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * - * Author: Logan Gunthorpe - * Logan Gunthorpe - * - * Date: Oct 23 2014 - * - * Description: - * Header file for argconfig.c - * + * Authors: Logan Gunthorpe + * Daniel Wagner */ #pragma once From 0df00bcf2666df69d92b490fb520c9f802e8f47f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:21:38 +0000 Subject: [PATCH 141/263] completions: update the shell completions Pick up the shorter description of --persistent. Signed-off-by: Daniel Wagner --- completions/_nvme | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 05f133c25e..3aa2694ec5 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -13075,7 +13075,7 @@ _nvme () { -o':alias for --output-format' --output-format-version=':output format version: 1|2' --owner=':record this owner in the registry' - --persistent=':persistent discovery connection mode (default: auto; use =no to disable)' + --persistent=':persistent discovery connection (default: auto; =no disables)' -p':alias for --persistent' --queue-size=':number of io queue elements to use (default 128)' -Q':alias for --queue-size' @@ -13616,7 +13616,7 @@ _nvme () { -o':alias for --output-format' --output-format-version=':output format version: 1|2' --owner=':record this owner in the registry' - --persistent=':persistent discovery connection mode (default: auto; use =no to disable)' + --persistent=':persistent discovery connection (default: auto; =no disables)' -p':alias for --persistent' --queue-size=':number of io queue elements to use (default 128)' -Q':alias for --queue-size' From 14858c5afd462ada9e570a5c69cc05cab1017367 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:21:38 +0000 Subject: [PATCH 142/263] NEWS: document the fabrics and feature option headings The shared fabrics and feature options have their own heading in the help. Signed-off-by: Daniel Wagner --- NEWS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index ebc8f0bfdb..a70b8ce308 100644 --- a/NEWS.md +++ b/NEWS.md @@ -92,6 +92,11 @@ `ocp internal-log` with an unknown telemetry type failed with status 0 before. +* The help of `connect`, `discover`, `connect-all`, `disconnect` and + `config create` lists the fabrics options they share under "Fabrics + options", and the help of the `feat` commands `--save` and `--sel` + under "Feature options". + * The command metadata (`nvme utils dump-command-metadata`) marks deprecated commands with the command which replaces them, deprecated options with the option which replaces them, and renamed options From 4a1ce0ba16c7e8dfe863206b65868ba98ea97c5a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:49:16 +0000 Subject: [PATCH 143/263] shared: add strict parsers for whole numbers strtoul() and friends skip leading whitespace, take a sign and negate an unsigned number, stop at the first character they don't understand and report an overflow only through errno, which the caller has to clear first. Each caller checks a different subset of that. Add shr_parse_u64() and shr_parse_s64(), which take the whole string as a number in the bases strtoull() takes with base 0, reject whitespace, a sign where none belongs and anything after the digits, and check the range the caller gives. Signed-off-by: Daniel Wagner --- shared/parse-util.c | 44 ++++++++++++++++++ shared/parse-util.h | 17 +++++++ shared/tests/test-parse-util.c | 82 ++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/shared/parse-util.c b/shared/parse-util.c index a3e42bb1ca..b45b22c274 100644 --- a/shared/parse-util.c +++ b/shared/parse-util.c @@ -6,6 +6,7 @@ * Authors: Martin Belanger */ +#include #include #include #include @@ -43,6 +44,49 @@ int shr_parse_bool(const char *value, bool *out) return -EINVAL; } +int shr_parse_u64(const char *s, uint64_t max, uint64_t *out) +{ + unsigned long long v; + char *end; + + /* strtoull() skips whitespace and takes a sign, a digit must lead */ + if (!s || !isdigit((unsigned char)*s)) + return -EINVAL; + + errno = 0; + v = strtoull(s, &end, 0); + if (end == s || *end) + return -EINVAL; + if (errno == ERANGE || v > max) + return -ERANGE; + + *out = v; + return 0; +} + +int shr_parse_s64(const char *s, int64_t min, int64_t max, int64_t *out) +{ + const char *digits; + long long v; + char *end; + + if (!s) + return -EINVAL; + digits = *s == '-' ? s + 1 : s; + if (!isdigit((unsigned char)*digits)) + return -EINVAL; + + errno = 0; + v = strtoll(s, &end, 0); + if (end == s || *end) + return -EINVAL; + if (errno == ERANGE || v < min || v > max) + return -ERANGE; + + *out = v; + return 0; +} + #define DEFINE_SHR_PARSE_CSV_FUNC(name, ret_t) \ int shr_parse_csv_ ## name(char *string, ret_t *val, \ unsigned int max_length) \ diff --git a/shared/parse-util.h b/shared/parse-util.h index 08cfc4f892..a2f21b1cc7 100644 --- a/shared/parse-util.h +++ b/shared/parse-util.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include @@ -25,6 +26,22 @@ typedef unsigned __int128 uint128_t; */ int shr_parse_bool(const char *value, bool *out); +/* + * Parse the whole string @s as an unsigned number: decimal, hexadecimal + * with a 0x prefix or octal with a leading 0, like strtoull() with base 0, + * but no whitespace, no sign and nothing after the digits. + * Return: 0 on success (*out set), -EINVAL if @s is no number, -ERANGE if + * it is larger than @max. + */ +int shr_parse_u64(const char *s, uint64_t max, uint64_t *out); + +/* + * The same for a signed number, with an optional '-' in front. + * Return: 0 on success (*out set), -EINVAL if @s is no number, -ERANGE if + * it is outside of @min and @max. + */ +int shr_parse_s64(const char *s, int64_t min, int64_t max, int64_t *out); + /* * Parse a comma-separated list of numbers from string (modified in place by * strtok()) into val, up to max_length entries. Each number is parsed with diff --git a/shared/tests/test-parse-util.c b/shared/tests/test-parse-util.c index a954ac461c..ccfae19e53 100644 --- a/shared/tests/test-parse-util.c +++ b/shared/tests/test-parse-util.c @@ -350,6 +350,86 @@ static bool test_csv_u128(void) #define test_csv_u128() true #endif /* NVME_HAVE_INT128 */ +static bool check_u64(const char *value, uint64_t max, int want_ret, + uint64_t want) +{ + uint64_t out = 0; + int ret; + + ret = shr_parse_u64(value, max, &out); + if (ret != want_ret || (!ret && out != want)) { + printf(" - \"%s\": got ret=%d out=%llu [FAIL]\n", value, ret, + (unsigned long long)out); + return false; + } + printf(" - \"%s\" [PASS]\n", value); + return true; +} + +static bool test_u64(void) +{ + bool pass = true; + + printf("test_u64:\n"); + + pass &= check_u64("0", UINT64_MAX, 0, 0); + pass &= check_u64("42", UINT64_MAX, 0, 42); + pass &= check_u64("0x1F", UINT64_MAX, 0, 31); + pass &= check_u64("010", UINT64_MAX, 0, 8); + pass &= check_u64("18446744073709551615", UINT64_MAX, 0, UINT64_MAX); + pass &= check_u64("18446744073709551616", UINT64_MAX, -ERANGE, 0); + pass &= check_u64("256", 255, -ERANGE, 0); + pass &= check_u64("255", 255, 0, 255); + pass &= check_u64("", UINT64_MAX, -EINVAL, 0); + pass &= check_u64(" 1", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("1 ", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("+1", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("-1", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("1x", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("0x", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("08", UINT64_MAX, -EINVAL, 0); + pass &= check_u64(NULL, UINT64_MAX, -EINVAL, 0); + + return pass; +} + +static bool check_s64(const char *value, int64_t min, int64_t max, + int want_ret, int64_t want) +{ + int64_t out = 0; + int ret; + + ret = shr_parse_s64(value, min, max, &out); + if (ret != want_ret || (!ret && out != want)) { + printf(" - \"%s\": got ret=%d out=%lld [FAIL]\n", value, ret, + (long long)out); + return false; + } + printf(" - \"%s\" [PASS]\n", value); + return true; +} + +static bool test_s64(void) +{ + bool pass = true; + + printf("test_s64:\n"); + + pass &= check_s64("-5", INT32_MIN, INT32_MAX, 0, -5); + pass &= check_s64("2147483647", INT32_MIN, INT32_MAX, 0, INT32_MAX); + pass &= check_s64("2147483648", INT32_MIN, INT32_MAX, -ERANGE, 0); + pass &= check_s64("-2147483649", INT32_MIN, INT32_MAX, -ERANGE, 0); + pass &= check_s64("-0x10", INT32_MIN, INT32_MAX, 0, -16); + pass &= check_s64("9223372036854775808", INT64_MIN, INT64_MAX, + -ERANGE, 0); + pass &= check_s64("--1", INT32_MIN, INT32_MAX, -EINVAL, 0); + pass &= check_s64("- 1", INT32_MIN, INT32_MAX, -EINVAL, 0); + pass &= check_s64("-", INT32_MIN, INT32_MAX, -EINVAL, 0); + pass &= check_s64("1x", INT32_MIN, INT32_MAX, -EINVAL, 0); + + return pass; +} + int main(void) { bool pass = true; @@ -366,6 +446,8 @@ int main(void) pass &= test_csv_u32(); pass &= test_csv_u64(); pass &= test_csv_u128(); + pass &= test_u64(); + pass &= test_s64(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From f55a0b826073fff932d24a0cd97c19c1b4c571ca Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:55:39 +0000 Subject: [PATCH 144/263] shared: add a strict parser for floating point numbers strtod() takes the decimal point of the locale, which nvme sets from the environment, so "1.5" is no number in a German locale. It also takes nan and inf, leading whitespace, and stops at the first character it doesn't understand. Add shr_parse_double(), which parses the whole string with a '.' as decimal point in any locale and takes only finite numbers. Signed-off-by: Daniel Wagner --- shared/parse-util.c | 37 ++++++++++++++++++++++++++++++++++ shared/parse-util.h | 9 +++++++++ shared/tests/test-parse-util.c | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/shared/parse-util.c b/shared/parse-util.c index b45b22c274..6210963bbd 100644 --- a/shared/parse-util.c +++ b/shared/parse-util.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,6 +89,41 @@ int shr_parse_s64(const char *s, int64_t min, int64_t max, int64_t *out) return 0; } +int shr_parse_double(const char *s, double *out) +{ + char *old, *end; + double v; + int err; + + /* a digit, a sign or the point must lead, no whitespace */ + if (!s || !(isdigit((unsigned char)*s) || *s == '-' || *s == '+' || + *s == '.')) + return -EINVAL; + + /* strtod() takes the decimal point of LC_NUMERIC */ + old = setlocale(LC_NUMERIC, NULL); + old = old ? strdup(old) : NULL; + setlocale(LC_NUMERIC, "C"); + + errno = 0; + v = strtod(s, &end); + err = errno; + + if (old) { + setlocale(LC_NUMERIC, old); + free(old); + } + + /* NaN, +inf or -inf, without isfinite() from libm */ + if (end == s || *end || v != v || v > DBL_MAX || v < -DBL_MAX) + return -EINVAL; + if (err == ERANGE) + return -ERANGE; + + *out = v; + return 0; +} + #define DEFINE_SHR_PARSE_CSV_FUNC(name, ret_t) \ int shr_parse_csv_ ## name(char *string, ret_t *val, \ unsigned int max_length) \ diff --git a/shared/parse-util.h b/shared/parse-util.h index a2f21b1cc7..9e6514413f 100644 --- a/shared/parse-util.h +++ b/shared/parse-util.h @@ -42,6 +42,15 @@ int shr_parse_u64(const char *s, uint64_t max, uint64_t *out); */ int shr_parse_s64(const char *s, int64_t min, int64_t max, int64_t *out); +/* + * Parse the whole string @s as a finite floating point number with a '.' + * as decimal point, whatever the locale: no whitespace, no nan or inf and + * nothing after the number. + * Return: 0 on success (*out set), -EINVAL if @s is no such number, + * -ERANGE if it is too large. + */ +int shr_parse_double(const char *s, double *out); + /* * Parse a comma-separated list of numbers from string (modified in place by * strtok()) into val, up to max_length entries. Each number is parsed with diff --git a/shared/tests/test-parse-util.c b/shared/tests/test-parse-util.c index ccfae19e53..d9a3ebcd83 100644 --- a/shared/tests/test-parse-util.c +++ b/shared/tests/test-parse-util.c @@ -430,6 +430,42 @@ static bool test_s64(void) return pass; } +static bool check_double(const char *value, int want_ret, double want) +{ + double out = 0; + int ret; + + ret = shr_parse_double(value, &out); + if (ret != want_ret || (!ret && out != want)) { + printf(" - \"%s\": got ret=%d out=%f [FAIL]\n", value, ret, + out); + return false; + } + printf(" - \"%s\" [PASS]\n", value); + return true; +} + +static bool test_double(void) +{ + bool pass = true; + + printf("test_double:\n"); + + pass &= check_double("1.5", 0, 1.5); + pass &= check_double("-0.25", 0, -0.25); + pass &= check_double("1e3", 0, 1000); + pass &= check_double(".5", 0, 0.5); + pass &= check_double("nan", -EINVAL, 0); + pass &= check_double("inf", -EINVAL, 0); + pass &= check_double("1e999", -EINVAL, 0); + pass &= check_double(" 1", -EINVAL, 0); + pass &= check_double("1 ", -EINVAL, 0); + pass &= check_double("1,5", -EINVAL, 0); + pass &= check_double("", -EINVAL, 0); + + return pass; +} + int main(void) { bool pass = true; @@ -448,6 +484,7 @@ int main(void) pass &= test_csv_u128(); pass &= test_u64(); pass &= test_s64(); + pass &= test_double(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From fd3c7777eaf92e144a32b654302f1647120266bf Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:55:39 +0000 Subject: [PATCH 145/263] shared: add shr_escape() for echoing strings from the user An error message which repeats what the user typed passes control characters and escape sequences on to the terminal. Add shr_escape(), which returns a copy with each control character written as \xNN, and a C file for the string helpers which don't fit into the header. Signed-off-by: Daniel Wagner --- shared/meson.build | 1 + shared/string-util.c | 36 +++++++++++++++++++++++++++++++++ shared/string-util.h | 8 ++++++++ shared/tests/test-string-util.c | 28 +++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 shared/string-util.c diff --git a/shared/meson.build b/shared/meson.build index 66a719ae9f..9ab8ec9a94 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -24,6 +24,7 @@ sources = [ 'progress-util.c', 'sha256-util.c', 'suffix-util.c', + 'string-util.c', 'table-util.c', 'time-util.c', 'uint128-util.c', diff --git a/shared/string-util.c b/shared/string-util.c new file mode 100644 index 0000000000..e237a92b45 --- /dev/null +++ b/shared/string-util.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +#include +#include + +#include "string-util.h" + +char *shr_escape(const char *s) +{ + const unsigned char *p; + size_t n = 1; + char *e, *q; + + for (p = (const unsigned char *)s; *p; p++) + n += (*p < 0x20 || *p == 0x7f) ? 4 : 1; + + e = malloc(n); + if (!e) + return NULL; + + for (p = (const unsigned char *)s, q = e; *p; p++) { + if (*p < 0x20 || *p == 0x7f) + q += snprintf(q, 5, "\\x%02x", *p); + else + *q++ = *p; + } + *q = '\0'; + + return e; +} diff --git a/shared/string-util.h b/shared/string-util.h index 86d9c3a4dc..98d9fcfb6f 100644 --- a/shared/string-util.h +++ b/shared/string-util.h @@ -245,3 +245,11 @@ static inline size_t shr_linelen(char *s) return i; } + +/* + * A copy of @s with each control character written as \xNN, so that a + * string from the user can't drive the terminal when it is echoed, e.g. + * in an error message. Other bytes, also those of UTF-8 characters, stay. + * The caller frees it. NULL if out of memory. + */ +char *shr_escape(const char *s); diff --git a/shared/tests/test-string-util.c b/shared/tests/test-string-util.c index 7c33a5dacc..7e4db7b405 100644 --- a/shared/tests/test-string-util.c +++ b/shared/tests/test-string-util.c @@ -256,6 +256,33 @@ static bool test_kv_keymatch(void) return pass; } +static bool check_escape(const char *in, const char *want) +{ + char *out = shr_escape(in); + bool ok = out && !strcmp(out, want); + + printf(" - \"%s\" -> \"%s\" [%s]\n", want, out ? out : "(null)", + ok ? "PASS" : "FAIL"); + free(out); + return ok; +} + +static bool test_escape(void) +{ + bool pass = true; + + printf("test_escape:\n"); + + pass &= check_escape("plain", "plain"); + pass &= check_escape("", ""); + pass &= check_escape("a\033[31mb", "a\\x1b[31mb"); + pass &= check_escape("tab\there", "tab\\x09here"); + pass &= check_escape("del\x7f", "del\\x7f"); + pass &= check_escape("\xc3\xa4", "\xc3\xa4"); + + return pass; +} + int main(void) { bool pass = true; @@ -271,6 +298,7 @@ int main(void) pass &= test_sanitize_name(); pass &= test_kv_strip(); pass &= test_kv_keymatch(); + pass &= test_escape(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From 611eef77e1e3888a6f9683f49a6841676999a909 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:56:00 +0000 Subject: [PATCH 146/263] argconfig: parse values strictly The values of number options went through strtoul() and friends with checks of their own: OPT_INT stored a long in an int without a range check, so --int=4294967296 wrapped; all of them took leading whitespace and a '+'; a double took nan and inf and the decimal point of the locale; and OPT_INT and OPT_LONG stored the value even when it was no number. Parse them with the strict parsers of shared/parse-util, which take the whole value in range or nothing. The getopt reference of the differential test converts the same way. Also harden the parser against what callers or users may pass: - an empty value is no prefix of a name, so --opt= doesn't select the only name of a value table; - an option whose value is optional may be given without one, which reaches the value conversion as NULL; - argv ends at a NULL entry even if argc says more; - the option line of the help is built with a bounded append. Signed-off-by: Daniel Wagner --- src/argconfig.c | 174 ++++++++++++++-------------- tests/unit/c/argconfig-getopt-ref.c | 111 +++++++----------- tests/unit/c/test-argconfig-parse.c | 59 ++++++++++ 3 files changed, 188 insertions(+), 156 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index 00dd000a0c..e24ac48ba4 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include +#include #include #include @@ -135,41 +137,64 @@ static void show_value_names(FILE *out, const struct argconfig_opt_val *v) if (!names) return; - p = names + sprintf(names, "\nNames:"); + p = names + snprintf(names, len, "\nNames:"); for (n = v; n->str; n++) - p += sprintf(p, " %s%s", n->str, n[1].str ? "," : ""); + p += snprintf(p, len - (p - names), " %s%s", n->str, + n[1].str ? "," : ""); shr_print_word_wrapped(names, 44, 44, out); } +/* Append to @buf of @size, whose @len stays below @size when it is full */ +static void buf_add(char *buf, size_t size, size_t *len, const char *fmt, ...) +{ + va_list ap; + int n; + + if (*len >= size - 1) + return; + + va_start(ap, fmt); + n = vsnprintf(buf + *len, size - *len, fmt, ap); + va_end(ap); + + if (n < 0) + return; + *len = *len + n < size ? *len + n : size - 1; +} + static void show_option(FILE *out, const struct argconfig_commandline_options *option) { + const char *meta = option->meta ? option->meta : "arg"; + enum argconfig_argument arg = option->argument_type; char buffer[0x1000]; - char *b = buffer; + size_t len = 0; - b += sprintf(b, " [ "); + buffer[0] = '\0'; + buf_add(buffer, sizeof(buffer), &len, " [ "); if (option->option) { - b += sprintf(b, " --%s", option->option); - if (option->argument_type == ARGCONFIG_ARG_OPTIONAL) - b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == ARGCONFIG_ARG_REQUIRED) - b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg"); + buf_add(buffer, sizeof(buffer), &len, " --%s", option->option); + if (arg == ARGCONFIG_ARG_OPTIONAL) + buf_add(buffer, sizeof(buffer), &len, "[=<%s>]", meta); + if (arg == ARGCONFIG_ARG_REQUIRED) + buf_add(buffer, sizeof(buffer), &len, "=<%s>", meta); if (option->short_option) - b += sprintf(b, ","); + buf_add(buffer, sizeof(buffer), &len, ","); } if (option->short_option) { - b += sprintf(b, " -%c", option->short_option); - if (option->argument_type == ARGCONFIG_ARG_OPTIONAL) - b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == ARGCONFIG_ARG_REQUIRED) - b += sprintf(b, " <%s>", option->meta ? option->meta : "arg"); + buf_add(buffer, sizeof(buffer), &len, " -%c", + option->short_option); + if (arg == ARGCONFIG_ARG_OPTIONAL) + buf_add(buffer, sizeof(buffer), &len, " [<%s>]", meta); + if (arg == ARGCONFIG_ARG_REQUIRED) + buf_add(buffer, sizeof(buffer), &len, " <%s>", meta); } - b += sprintf(b, " ] "); + buf_add(buffer, sizeof(buffer), &len, " ] "); fprintf(out, "%s", buffer); if (option->help) { - shr_print_word_wrapped("--- ", 40, b - buffer, out); + shr_print_word_wrapped("--- ", 40, len, out); shr_print_word_wrapped(option->help, 44, 44, out); show_value_names(out, option->opt_val); } @@ -255,76 +280,44 @@ bool argconfig_help_requested(void) return help_requested; } -/* - * strtoul() accepts a sign and negates the result, so "-1" would become the - * largest value. The value tables of an option can name such a value. - */ -static bool argconfig_negative(const char *arg) -{ - while (isspace((unsigned char)*arg)) - arg++; - - return *arg == '-'; -} - static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { void *value = s->default_value; - char *endptr; + uint64_t u; + int64_t i; + double d; int ret = 0; - errno = 0; /* To distinguish success/failure after strtol/stroul call */ - - switch (s->config_type) { - case CFG_BYTE: - case CFG_SHORT: - case CFG_POSITIVE: - case CFG_LONG: - case CFG_LONG_SUFFIX: - if (argconfig_negative(arg)) - return -EINVAL; - break; - default: - break; - } + /* only a string option can do without a value */ + if (!arg && s->config_type != CFG_STRING && + s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT) + return -EINVAL; switch (s->config_type) { case CFG_STRING: *(char **)value = arg; break; case CFG_INT: - *(int *)value = strtol(arg, &endptr, 0); - if (errno || arg == endptr || *endptr) - ret = -EINVAL; + ret = shr_parse_s64(arg, INT_MIN, INT_MAX, &i); + if (!ret) + *(int *)value = i; break; - case CFG_BYTE: { - unsigned long tmp = strtoul(arg, &endptr, 0); - - if (errno || tmp >= 1 << 8 || arg == endptr || *endptr) - ret = -EINVAL; - else - *(uint8_t *)value = tmp; + case CFG_BYTE: + ret = shr_parse_u64(arg, UINT8_MAX, &u); + if (!ret) + *(uint8_t *)value = u; break; - } - case CFG_SHORT: { - unsigned long tmp = strtoul(arg, &endptr, 0); - - if (errno || tmp >= 1 << 16 || arg == endptr || *endptr) - ret = -EINVAL; - else - *(uint16_t *)value = tmp; + case CFG_SHORT: + ret = shr_parse_u64(arg, UINT16_MAX, &u); + if (!ret) + *(uint16_t *)value = u; break; - } - case CFG_POSITIVE: { - unsigned long long tmp = strtoull(arg, &endptr, 0); - - if (errno || tmp > UINT32_MAX || arg == endptr || *endptr) - ret = -EINVAL; - else - *(uint32_t *)value = tmp; + case CFG_POSITIVE: + ret = shr_parse_u64(arg, UINT32_MAX, &u); + if (!ret) + *(uint32_t *)value = u; break; - } case CFG_INCREMENT: *(int *)value += 1; break; @@ -333,17 +326,25 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, ret = -EINVAL; break; case CFG_LONG: - *(uint64_t *)value = strtoull(arg, &endptr, 0); - if (errno || arg == endptr || *endptr) - ret = -EINVAL; + ret = shr_parse_u64(arg, UINT64_MAX, &u); + if (!ret) + *(uint64_t *)value = u; break; - case CFG_LONG_SUFFIX: - ret = shr_suffix_binary_parse(arg, &endptr, (uint64_t *)value); + case CFG_LONG_SUFFIX: { + char *endptr; + + /* the suffix parser takes whitespace and a sign */ + if (!isdigit((unsigned char)*arg)) + return -EINVAL; + ret = shr_suffix_binary_parse(arg, &endptr, &u); + if (!ret) + *(uint64_t *)value = u; break; + } case CFG_DOUBLE: - *(double *)value = strtod(arg, &endptr); - if (errno || arg == endptr || *endptr) - ret = -EINVAL; + ret = shr_parse_double(arg, &d); + if (!ret) + *(double *)value = d; break; case CFG_FLAG: *(bool *)value = true; @@ -402,6 +403,10 @@ argconfig_match_val(struct argconfig_opt_val *v, const char *str) size_t len = strlen(str); struct argconfig_opt_val *match = NULL; + /* an empty value is a prefix of every name */ + if (!len) + return NULL; + for (; v->str; v++) { if (strncasecmp(str, v->str, len)) continue; @@ -439,7 +444,7 @@ bool argconfig_set_value_name(struct argconfig_commandline_options *s, static int argconfig_parse_val(struct argconfig_commandline_options *s, char *arg) { - if (argconfig_set_value_name(s, arg)) + if (arg && argconfig_set_value_name(s, arg)) return 0; return argconfig_parse_type(s, arg); @@ -613,7 +618,7 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, int n = 0; print_label(command_name()); - fprintf(stderr, "invalid value '%s' for ", arg); + fprintf(stderr, "invalid value '%s' for ", arg ? arg : ""); if (c) fprintf(stderr, "-%c", c); else @@ -657,7 +662,7 @@ static int argconfig_apply(struct argconfig_commandline_options *s, char c, return 0; ret = argconfig_parse_val(s, arg); - if (ret == -EINVAL) + if (ret == -EINVAL || ret == -ERANGE) return argconfig_value_error(s, c, arg); return ret; @@ -842,14 +847,15 @@ static int argconfig_parse_args(struct argconfig_parser *p) argconfig_reset_seen(p->options); - for (p->ind = 1; p->ind < p->argc; p->ind++) { + /* argv ends at argc or at a NULL, whichever comes first */ + for (p->ind = 1; p->ind < p->argc && p->argv[p->ind]; p->ind++) { arg = p->argv[p->ind]; if (!strcmp(arg, "--")) { p->ind++; if (p->global) return 0; - while (p->ind < p->argc) + while (p->ind < p->argc && p->argv[p->ind]) p->operands[p->nr_operands++] = p->argv[p->ind++]; return 0; diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index c23f2d4b46..2f23c922c7 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -4,12 +4,13 @@ * argconfig got its own parser. test-argconfig-diff compares both, so any * change in behaviour shows up. Help output and the locale side effect * are left out. The test compares the parsing of the command line, so the - * value conversion follows the rules of argconfig: unsigned options reject - * negative numbers, and a word is at most UINT32_MAX. + * value conversion is the one of argconfig, which uses the strict number + * parsers of shared/parse-util. */ #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include +#include #include #include "../../../src/argconfig.h" @@ -42,102 +44,67 @@ static bool is_null_or_empty(const char *s) return !s || !*s; } -static int argconfig_error(char *type, const char *opt, const char *arg) -{ - fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", - type, opt, arg); - return -EINVAL; -} - -static bool argconfig_negative(const char *arg) -{ - while (isspace((unsigned char)*arg)) - arg++; - - return *arg == '-'; -} - +/* The value conversion of argconfig, see argconfig_parse_type() */ static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { void *value = s->default_value; char *endptr; + uint64_t u; + int64_t i; + double d; int ret = 0; - /* To distinguish success/failure after strtol/stroul call */ - errno = 0; - - switch (s->config_type) { - case CFG_BYTE: - case CFG_SHORT: - case CFG_POSITIVE: - case CFG_LONG: - case CFG_LONG_SUFFIX: - if (argconfig_negative(arg)) - return argconfig_error("non-negative number", s->option, - arg); - break; - default: - break; - } + if (!arg && s->config_type != CFG_STRING && + s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT) + return -EINVAL; switch (s->config_type) { case CFG_STRING: *(char **)value = arg; break; case CFG_INT: - *(int *)value = strtol(arg, &endptr, 0); - if (errno || arg == endptr || *endptr) - ret = argconfig_error("integer", s->option, arg); + ret = shr_parse_s64(arg, INT_MIN, INT_MAX, &i); + if (!ret) + *(int *)value = i; break; - case CFG_BYTE: { - unsigned long tmp = strtoul(arg, &endptr, 0); - - if (errno || tmp >= 1 << 8 || arg == endptr || *endptr) - ret = argconfig_error("byte", s->option, arg); - else - *(uint8_t *)value = tmp; + case CFG_BYTE: + ret = shr_parse_u64(arg, UINT8_MAX, &u); + if (!ret) + *(uint8_t *)value = u; break; - } - case CFG_SHORT: { - unsigned long tmp = strtoul(arg, &endptr, 0); - - if (errno || tmp >= 1 << 16 || arg == endptr || *endptr) - ret = argconfig_error("short", s->option, arg); - else - *(uint16_t *)value = tmp; + case CFG_SHORT: + ret = shr_parse_u64(arg, UINT16_MAX, &u); + if (!ret) + *(uint16_t *)value = u; break; - } - case CFG_POSITIVE: { - unsigned long long tmp = strtoull(arg, &endptr, 0); - - if (errno || tmp > UINT32_MAX || arg == endptr || *endptr) - ret = argconfig_error("word", s->option, arg); - else - *(uint32_t *)value = tmp; + case CFG_POSITIVE: + ret = shr_parse_u64(arg, UINT32_MAX, &u); + if (!ret) + *(uint32_t *)value = u; break; - } case CFG_INCREMENT: *(int *)value += 1; break; case CFG_ENUM: - ret = argconfig_error("name", s->option, arg); + ret = -EINVAL; break; case CFG_LONG: - *(uint64_t *)value = strtoull(arg, &endptr, 0); - if (errno || arg == endptr || *endptr) - ret = argconfig_error("long integer", s->option, arg); + ret = shr_parse_u64(arg, UINT64_MAX, &u); + if (!ret) + *(uint64_t *)value = u; break; case CFG_LONG_SUFFIX: - ret = shr_suffix_binary_parse(arg, &endptr, (uint64_t *)value); - if (ret) - argconfig_error("long suffixed integer", s->option, - arg); + if (!isdigit((unsigned char)*arg)) + return -EINVAL; + ret = shr_suffix_binary_parse(arg, &endptr, &u); + if (!ret) + *(uint64_t *)value = u; break; case CFG_DOUBLE: - *(double *)value = strtod(arg, &endptr); - if (errno || arg == endptr || *endptr) - ret = argconfig_error("float", s->option, arg); + ret = shr_parse_double(arg, &d); + if (!ret) + *(double *)value = d; break; case CFG_FLAG: *(bool *)value = true; @@ -147,7 +114,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, break; } - return ret; + return ret ? -EINVAL : 0; } static void argconfig_set_opt_val(enum argconfig_types type, diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 6ee106a838..73a683986c 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -122,6 +122,16 @@ static struct toval_test toval_tests[] = { VAL_TEST("--int=1x", int_val, 0, true, -EINVAL), VAL_TEST("--double=1.5x", double_val, 0, true, -EINVAL), VAL_TEST("--double=1.5", double_val, 1.5, true, 0), + VAL_TEST("--int=4294967296", int_val, 0, true, -EINVAL), + VAL_TEST("--int=-2147483649", int_val, 0, true, -EINVAL), + VAL_TEST("--int=-2147483648", int_val, INT32_MIN, true, 0), + VAL_TEST("--int= 1", int_val, 0, true, -EINVAL), + VAL_TEST("--uint= 1", uint, 0, true, -EINVAL), + VAL_TEST("--uint=+1", uint, 0, true, -EINVAL), + VAL_TEST("--double=nan", double_val, 0, true, -EINVAL), + VAL_TEST("--double=inf", double_val, 0, true, -EINVAL), + VAL_TEST("--double=1e999", double_val, 0, true, -EINVAL), + VAL_TEST("--double= 1", double_val, 0, true, -EINVAL), VAL_TEST("--val=12x", val, 0, true, -EINVAL), VAL_TEST("--int=-1", int_val, -1, true, 0), VAL_TEST("--int=1", int_val, 1, true, 0), @@ -521,6 +531,53 @@ static void include_test(void) } } +/* A double takes a decimal point in any locale */ +static void double_locale_test(void) +{ + const char *desc = "Test double"; + char *argv[3] = { "prog", "--d=1.5", NULL }; + double d = 0; + int ret; + + OPT_ARGS(opts) = { + OPT_DOUBLE("d", 0, &d, "double"), + OPT_END() + }; + + if (!setlocale(LC_NUMERIC, "de_DE.UTF-8")) + return; /* not installed */ + + ret = argconfig_parse(2, argv, desc, opts); + if (ret || d != 1.5) { + printf("ERROR: double: '1.5' in de_DE gave %d/%f\n", ret, d); + test_rc = 1; + } + setlocale(LC_NUMERIC, "C"); +} + +/* An empty value names nothing, even with a single name */ +static void empty_name_test(void) +{ + const char *desc = "Test empty name"; + struct argconfig_opt_val only[] = { + VAL_ENUM("only", 7), + VAL_END() + }; + char *argv[3] = { "prog", "--e=", NULL }; + int e = 0, ret; + + OPT_ARGS(opts) = { + OPT_ENUM("e", 0, &e, "enum", only), + OPT_END() + }; + + ret = argconfig_parse(2, argv, desc, opts); + if (ret != -EINVAL || e) { + printf("ERROR: empty name: gave %d/%d\n", ret, e); + test_rc = 1; + } +} + /* A template table bound to another struct stores into that one */ static void bind_test(void) { @@ -1110,6 +1167,8 @@ int main(void) enum_test(); include_test(); bind_test(); + double_locale_test(); + empty_name_test(); special_values_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) From f0af13c6784e46355f5dd854c17abbf3be54a4ee Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 15:56:24 +0000 Subject: [PATCH 147/263] nvme: escape the user input which an error repeats An unknown option, a bad value, an extra operand or an unknown command is repeated in the error message as given, so a terminal interprets the escape sequences in it. Write their control characters as \xNN with shr_escape(). Signed-off-by: Daniel Wagner --- src/argconfig.c | 19 ++++++++++++++----- src/args.c | 10 ++++++++-- src/plugin.c | 10 ++++++++-- tests/cli/nvme_usage_test.py | 22 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index e24ac48ba4..00deb2a6ac 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -614,11 +615,12 @@ static const char *argconfig_expected(struct argconfig_commandline_options *s) static int argconfig_value_error(struct argconfig_commandline_options *s, char c, const char *arg) { + __cleanup_free char *e = arg ? shr_escape(arg) : NULL; const struct argconfig_opt_val *v; int n = 0; print_label(command_name()); - fprintf(stderr, "invalid value '%s' for ", arg ? arg : ""); + fprintf(stderr, "invalid value '%s' for ", e ? e : ""); if (c) fprintf(stderr, "-%c", c); else @@ -735,9 +737,12 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) if (help && !p->global) return ARGCONFIG_HELP; - if (!p->global || (!help && !version)) + if (!p->global || (!help && !version)) { + __cleanup_free char *e = shr_escape(arg); + return argconfig_parse_error(p, - "unrecognized option '%s'", arg); + "unrecognized option '%s'", e ? e : ""); + } if (eq) return argconfig_parse_error(p, "option '--%.*s' doesn't allow an argument", @@ -796,9 +801,13 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) p->version |= *c == 'V'; continue; } - if (!s) + if (!s) { + char bad[2] = { *c, '\0' }; + __cleanup_free char *e = shr_escape(bad); + return argconfig_parse_error(p, - "invalid option -- '%c'", *c); + "invalid option -- '%s'", e ? e : ""); + } value = NULL; switch (s->argument_type) { diff --git a/src/args.c b/src/args.c index bb2f46932e..3ca2626095 100644 --- a/src/args.c +++ b/src/args.c @@ -11,8 +11,11 @@ #include +#include + #include "argconfig.h" #include "args.h" +#include "cleanup.h" #include "nvme-print.h" #include "plugin.h" @@ -34,12 +37,15 @@ static int check_device(void) { int max = nvme_args.device_operand == COMMAND_NO_DEVICE ? 0 : 1; int nr = argconfig_nr_operands(); + __cleanup_free char *e = NULL; if (nvme_args.device_operand == COMMAND_DEVICE && !nr) return argconfig_usage_error("missing "); - if (nr > max) + if (nr > max) { + e = shr_escape(argconfig_operand(max)); return argconfig_usage_error("unexpected argument '%s'", - argconfig_operand(max)); + e ? e : ""); + } nvme_args.device = argconfig_operand(0); return 0; diff --git a/src/plugin.c b/src/plugin.c index a3c295b048..57b42d999c 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -7,6 +7,7 @@ #include +#include #include #include "argconfig.h" @@ -492,7 +493,10 @@ static int run_joined_command(int argc, char **argv, struct plugin *plugin) break; } if (!extension) { - fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", argv[0]); + __cleanup_free char *e = shr_escape(argv[0]); + + fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", + e ? e : ""); commands_hint(plugin); return -ENOTTY; } @@ -631,9 +635,11 @@ int handle_plugin(int argc, char **argv, struct plugin *plugin) /* Only the built-in plugin has extensions */ if (plugin->name) { + __cleanup_free char *e = shr_escape(argv[0]); + fprintf(stderr, "ERROR: Invalid sub-command '%s' for plugin %s\n", - argv[0], plugin->name); + e ? e : "", plugin->name); commands_hint(plugin); return -ENOTTY; } diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py index 4f886dbc9f..3fd9dc91df 100644 --- a/tests/cli/nvme_usage_test.py +++ b/tests/cli/nvme_usage_test.py @@ -172,6 +172,28 @@ def test_help_lists_the_names(self): self.assertIn('Names: infinite, -1', result.stdout) +class EchoTest(unittest.TestCase): + """Input which is echoed in an error can't drive the terminal: its + control characters are written as \\xNN.""" + + def assert_escaped(self, result): + self.assertNotIn('\033', result.stderr) + self.assertIn('\\x1b', result.stderr) + + def test_unknown_option(self): + self.assert_escaped(run('id', 'ctrl', '--bo\033[31mgus')) + + def test_bad_value(self): + self.assert_escaped(run('id', 'ns', '/dev/nvme0', '-n', '1\033[2J')) + + def test_extra_argument(self): + self.assert_escaped(run('list', '\033]0;title\007')) + + def test_unknown_command(self): + self.assert_escaped(run('bo\033[31mgus')) + self.assert_escaped(run('id', 'bo\033[31mgus')) + + class UnknownCommandTest(unittest.TestCase): """An unknown command or plugin command and a bad global option print the error and where the commands are listed, not the whole From 358be1fadaae68628dc0d383a72d7055f2858207 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:00:43 +0000 Subject: [PATCH 148/263] shared: harden the suffix parsers shr_suffix_si_parse() and shr_suffix_binary_parse() took leading whitespace and a sign, so "-1Ki" wrapped around, didn't clear errno before strtoull(), so a stale ERANGE rejected UINT64_MAX, and didn't check the multiplication by the suffix, so "16777216Ti" wrapped to 0. shr_suffix_si_parse() also read the digits after the decimal point in base 0, which made "1.09k" invalid octal, dropped them when there were as many as the suffix has digits, so "1.500k" was 1000, and took the decimal point of the locale. Require a digit first, clear errno, check every multiplication and addition, read the fraction in decimal after a '.' in any locale, and pass only unsigned char to tolower(). Signed-off-by: Daniel Wagner --- shared/suffix-util.c | 177 ++++++++++++++++---------------- shared/tests/test-suffix-util.c | 19 ++++ 2 files changed, 106 insertions(+), 90 deletions(-) diff --git a/shared/suffix-util.c b/shared/suffix-util.c index 211735c179..1cf0782e4f 100644 --- a/shared/suffix-util.c +++ b/shared/suffix-util.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -50,101 +51,89 @@ const char *shr_suffix_si_get(double *value) return suffix; } -static bool suffix_si_check(const char val) +/* @v * 10, false if that is larger than UINT64_MAX */ +static bool mul10(uint64_t *v) { - int i; - struct si_suffix *s; - - for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) { - s = &si_suffixes[i]; - - if (val == *s->suffix) - return true; - } - - return false; + if (*v > UINT64_MAX / 10) + return false; + *v *= 10; + return true; } int shr_suffix_si_parse(const char *str, char **endptr, uint64_t *val) { - unsigned long long num, frac = 0; - char *sep, *tmp; - int frac_len = 0, len, i; + unsigned long long num; + uint64_t frac = 0; + const char *p; + int frac_len = 0, i; - num = strtoull(str, endptr, 0); - if (str == *endptr || - ((num == ULLONG_MAX) && errno == ERANGE)) + /* strtoull() takes whitespace and a sign, a digit must lead */ + if (!isdigit((unsigned char)*str)) return -EINVAL; - /* simple number, no decimal point not suffix */ - if ((*endptr)[0] == '\0') { - *val = num; - return 0; - } + errno = 0; + num = strtoull(str, endptr, 0); + if (str == *endptr || errno == ERANGE) + return -EINVAL; + p = *endptr; - /* get rid of the decimal point */ - sep = localeconv()->decimal_point; - if (sep) - len = strlen(sep); - else - len = 0; - - for (i = 0; i < len; i++) { - if (suffix_si_check((*endptr)[i])) - break; - if (((*endptr)[i] == '\0') || (*endptr)[i] != sep[i]) + /* the digits after the point, which the suffix may keep */ + if (*p == '.') { + p++; + if (!isdigit((unsigned char)*p)) return -EINVAL; + for (; isdigit((unsigned char)*p); p++, frac_len++) { + /* more digits don't fit into 64 bits, cut them off */ + if (frac_len >= 19) + continue; + if (!mul10(&frac)) + return -EINVAL; + frac += *p - '0'; + } } + *endptr = (char *)p; - if (suffix_si_check((*endptr)[i])) { - if ((*endptr)[i + 1] != '\0') - return -EINVAL; - } else { - *endptr += len; - tmp = *endptr; - - /* extract the digits after decimal point */ - frac = strtoull(tmp, endptr, 0); - if (tmp == *endptr || - ((frac == ULLONG_MAX) && errno == ERANGE)) - return -EINVAL; - - /* test that we have max one character as suffix */ - if ((*endptr)[0] != '\0' && (*endptr)[1] != '\0') - return -EINVAL; - - frac_len = *endptr - tmp; + if (*p == '\0') { + /* no suffix keeps no fraction */ + *val = num; + return 0; } + if (p[1] != '\0') + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) { struct si_suffix *s = &si_suffixes[i]; + uint64_t v = num; + int j; - if ((*endptr)[0] != s->suffix[0]) + if (*p != s->suffix[0]) continue; - /* we should check for overflow */ - for (int j = 0; j < s->exponent; j++) - num *= 10; - - if (s->exponent > frac_len) { - for (int j = 0; j < s->exponent - frac_len; j++) - frac *= 10; - } else if (s->exponent < frac_len) { - for (int j = 0; j < frac_len - s->exponent; j++) - frac /= 10; - } else { - frac = 0; + for (j = 0; j < s->exponent; j++) { + if (!mul10(&v)) + return -EINVAL; } - *val = num + frac; + /* scale the fraction to the exponent, cutting off the rest */ + if (frac_len > 19) + frac_len = 19; + for (j = frac_len; j < (int)s->exponent; j++) { + if (!mul10(&frac)) + return -EINVAL; + } + for (j = s->exponent; j < frac_len; j++) + frac /= 10; + + if (frac > UINT64_MAX - v) + return -EINVAL; + + *endptr = (char *)p + 1; + *val = v + frac; return 0; } - if ((*endptr)[0] != '\0') - return -EINVAL; - - *val = num; - return 0; + return -EINVAL; } const char *shr_suffix_si_get_ld(long double *value) @@ -209,38 +198,46 @@ const char *shr_suffix_dbinary_get(double *value) int shr_suffix_binary_parse(const char *str, char **endptr, uint64_t *val) { - uint64_t ret; + unsigned long long ret; + const char *p; int i; - ret = strtoull(str, endptr, 0); - if (str == *endptr || - ((ret == ULLONG_MAX) && errno == ERANGE)) + /* strtoull() takes whitespace and a sign, a digit must lead */ + if (!isdigit((unsigned char)*str)) return -EINVAL; - if (str == *endptr) { - *val = ret; - return 0; - } + errno = 0; + ret = strtoull(str, endptr, 0); + if (str == *endptr || errno == ERANGE) + return -EINVAL; + p = *endptr; /* simple number, no decimal point, no suffix */ - if ((*endptr)[0] == '\0') { + if (*p == '\0') { *val = ret; return 0; } + /* a suffix is two characters, e.g. Ki */ + if (!p[1] || p[2]) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(binary_suffixes); i++) { struct binary_suffix *s = &binary_suffixes[i]; - if (tolower((*endptr)[0]) == tolower(s->suffix[0]) && - (s->suffix[0] != '\0' && - (((*endptr)[0] != '\0' && - (*endptr)[1] != '\0' && - (*endptr)[2] == '\0') && - (tolower((*endptr)[1]) == tolower(s->suffix[1]))))) { - ret <<= s->shift; - *val = ret; - return 0; - } + if (!s->suffix[0] || + tolower((unsigned char)p[0]) != + tolower((unsigned char)s->suffix[0]) || + tolower((unsigned char)p[1]) != + tolower((unsigned char)s->suffix[1])) + continue; + + if (ret > UINT64_MAX >> s->shift) + return -EINVAL; + + *endptr = (char *)p + 2; + *val = ret << s->shift; + return 0; } return -EINVAL; diff --git a/shared/tests/test-suffix-util.c b/shared/tests/test-suffix-util.c index 90d187e086..412067662c 100644 --- a/shared/tests/test-suffix-util.c +++ b/shared/tests/test-suffix-util.c @@ -79,6 +79,18 @@ static bool test_suffix_si_parse(void) { "800.0G", 800000000000ULL, 0 }, { "800.G", 0, -EINVAL }, { "800.", 0, -EINVAL }, + { "1.500k", 1500ULL, 0 }, + { "1.09k", 1090ULL, 0 }, + { "1.05k", 1050ULL, 0 }, + { "18E", 18000000000000000000ULL, 0 }, + { "19E", 0, -EINVAL }, + { "18446744073709551616", 0, -EINVAL }, + { "18446744073709551615", 18446744073709551615ULL, 0 }, + { "18446744073709551615.9k", 0, -EINVAL }, + { "-1k", 0, -EINVAL }, + { " 1k", 0, -EINVAL }, + { "+1k", 0, -EINVAL }, + { "", 0, -EINVAL }, }; bool pass = true; unsigned int i; @@ -110,6 +122,13 @@ static bool test_suffix_binary_parse(void) { "34Gi", 36507222016ULL, 0 }, { "34.9Ki", 0, -EINVAL }, { "32Gii", 0, -EINVAL }, + { "16777215Ti", 16777215ULL << 40, 0 }, + { "16777216Ti", 0, -EINVAL }, + { "18446744073709551616", 0, -EINVAL }, + { "-1Ki", 0, -EINVAL }, + { " 1Ki", 0, -EINVAL }, + { "1ki", 1024ULL, 0 }, + { "", 0, -EINVAL }, }; bool pass = true; unsigned int i; From c0740800680735ad315991a1b7d4231542f73b33 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:00:43 +0000 Subject: [PATCH 149/263] shared: parse the comma separated numbers strictly The shr_parse_csv_*() helpers split with strtok(), which keeps hidden state across calls, and took each number with strtoumax(), which accepts a '+' and leading whitespace. Split with shr_strsep() and parse each trimmed entry with shr_parse_u64() in the range of the type. Empty entries are skipped as before, blank ones are no number. The entry which is out of range is escaped when it is reported. Signed-off-by: Daniel Wagner --- shared/parse-util.c | 75 +++++++++++++++++++--------------- shared/tests/test-parse-util.c | 36 ++++++++++++++++ 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/shared/parse-util.c b/shared/parse-util.c index 6210963bbd..f4fc1b4747 100644 --- a/shared/parse-util.c +++ b/shared/parse-util.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,9 @@ #include +#include "cleanup-util.h" +#include "string-util.h" + #include "parse-util.h" int shr_parse_bool(const char *value, bool *out) @@ -124,50 +128,51 @@ int shr_parse_double(const char *s, double *out) return 0; } -#define DEFINE_SHR_PARSE_CSV_FUNC(name, ret_t) \ +/* + * Split @string at the commas, trim each entry and parse it with + * shr_parse_u64(), up to @max. Empty entries are skipped, like strtok() + * did before, but blank ones are no number. + */ +#define DEFINE_SHR_PARSE_CSV_FUNC(name, ret_t, max) \ int shr_parse_csv_ ## name(char *string, ret_t *val, \ unsigned int max_length) \ { \ - int ret = 0; \ - uintmax_t v; \ - char *tmp; \ - char *p; \ + char *rest = string, *tok; \ + unsigned int n = 0; \ + uint64_t v; \ + int err; \ \ if (!string || !*string) \ return 0; \ \ - tmp = strtok(string, ","); \ - \ - while (tmp) { \ - if (ret >= max_length) \ + while ((tok = shr_strsep(&rest, ",")) != NULL) { \ + if (!*tok) \ + continue; \ + if (n >= max_length) \ return -1; \ \ - if (tmp[strspn(tmp, " \t\n\r\f\v")] == '-') \ - return -1; \ + tok = shr_trim(tok); \ \ - errno = 0; \ - v = strtoumax(tmp, &p, 0); \ - if (*p != 0) \ - return -1; \ - if (errno == ERANGE || \ - (ret_t)(v) != v) { \ - fprintf(stderr, "%s out of range\n", tmp); \ - return -1; \ - } \ - val[ret] = v; \ - ret++; \ + err = shr_parse_u64(tok, max, &v); \ + if (err == -ERANGE) { \ + __cleanup_free char *e = shr_escape(tok); \ \ - tmp = strtok(NULL, ","); \ + fprintf(stderr, "%s out of range\n", \ + e ? e : ""); \ + } \ + if (err) \ + return -1; \ + val[n++] = v; \ } \ \ - return ret; \ + return n; \ } -DEFINE_SHR_PARSE_CSV_FUNC(int, int) -DEFINE_SHR_PARSE_CSV_FUNC(ushort, unsigned short) -DEFINE_SHR_PARSE_CSV_FUNC(uint, unsigned int) -DEFINE_SHR_PARSE_CSV_FUNC(ulonglong, unsigned long long) -DEFINE_SHR_PARSE_CSV_FUNC(uchar, unsigned char) +DEFINE_SHR_PARSE_CSV_FUNC(int, int, INT_MAX) +DEFINE_SHR_PARSE_CSV_FUNC(ushort, unsigned short, USHRT_MAX) +DEFINE_SHR_PARSE_CSV_FUNC(uint, unsigned int, UINT_MAX) +DEFINE_SHR_PARSE_CSV_FUNC(ulonglong, unsigned long long, ULLONG_MAX) +DEFINE_SHR_PARSE_CSV_FUNC(uchar, unsigned char, UCHAR_MAX) #ifdef NVME_HAVE_INT128 int shr_parse_csv_uint128(char *string, uint128_t *val, unsigned int max_length) @@ -179,15 +184,19 @@ int shr_parse_csv_uint128(char *string, uint128_t *val, unsigned int max_length) char *src; int base; + char *rest = string; + if (!string || !*string) return 0; - temp = strtok(string, ","); - - while (temp) { + while ((temp = shr_strsep(&rest, ",")) != NULL) { + if (!*temp) + continue; if (ret >= max_length) return -EINVAL; + temp = shr_trim(temp); + errno = 0; v = 0; base = 10; @@ -220,8 +229,6 @@ int shr_parse_csv_uint128(char *string, uint128_t *val, unsigned int max_length) val[ret] = v; ret++; - - temp = strtok(NULL, ","); } return ret; diff --git a/shared/tests/test-parse-util.c b/shared/tests/test-parse-util.c index d9a3ebcd83..ffd6c2eff5 100644 --- a/shared/tests/test-parse-util.c +++ b/shared/tests/test-parse-util.c @@ -13,6 +13,7 @@ #include #include +#include #include static bool check_bool(const char *value, int want_ret, bool want_out) @@ -466,6 +467,40 @@ static bool test_double(void) return pass; } +static bool test_csv_strict(void) +{ + static const struct { + const char *in; + int want; + } tests[] = { + { "1, 2", 2 }, + { " 1 ,2 ", 2 }, + { "1,,2", 2 }, + { "1,2,", 2 }, + { ",1", 1 }, + { "1,-2", -1 }, + { "+1", -1 }, + { "4294967296", -1 }, + { "0x10,010", 2 }, + }; + unsigned int val[8]; + bool pass = true; + unsigned int i; + + printf("test_csv_strict:\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + char buf[64]; + + snprintf(buf, sizeof(buf), "%s", tests[i].in); + pass &= check_int_ret(tests[i].in, + shr_parse_csv_uint(buf, val, 8), + tests[i].want); + } + + return pass; +} + int main(void) { bool pass = true; @@ -485,6 +520,7 @@ int main(void) pass &= test_u64(); pass &= test_s64(); pass &= test_double(); + pass &= test_csv_strict(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From c9efce438b8e4cdadcd63ec84628cc5573bc5e89 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:02:05 +0000 Subject: [PATCH 150/263] nvme: parse nvme-cli.conf numbers strictly and escape what is echoed nvme-cli.conf read its numbers with an own strtoul() wrapper, which took a '+' and whitespace, and its warnings repeated keys and values of the file as they were. Parse the numbers with shr_parse_u64() and escape the keys, values and lines which a warning repeats. Add nvme_load_global_config_buf(), which reads the config from a string, for the tests and the fuzzer. Signed-off-by: Daniel Wagner --- src/global-config.c | 50 ++++++++++++++++--------------- src/global-config.h | 3 ++ tests/unit/c/test-global-config.c | 27 +++++++++++++++++ 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/global-config.c b/src/global-config.c index c95922e6f4..804f94701b 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -21,29 +21,20 @@ #include #include +#include #include "argconfig.h" +#include "cleanup.h" #include "global-config.h" struct global_config_ctx { const char *path; }; -static int parse_uint(const char *value, unsigned int *out) +/* A number of the config file up to @max, -EINVAL if it is none */ +static int parse_uint(const char *value, uint64_t max, uint64_t *out) { - unsigned long v; - char *end; - - if (!value || !*value || *value == '-') - return -EINVAL; - - errno = 0; - v = strtoul(value, &end, 0); - if (errno || *end || v > (unsigned long)~0U) - return -EINVAL; - - *out = (unsigned int)v; - return 0; + return shr_parse_u64(value, max, out) ? -EINVAL : 0; } /* The keys are the global options which the help shows */ @@ -65,14 +56,15 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, const char *value, unsigned int line) { struct argconfig_commandline_options *s = find_key(key); - unsigned int uval; + __cleanup_free char *k = shr_escape(key); + uint64_t uval; bool bval; char *dup; int err; if (!s || !s->default_value) { fprintf(stderr, "%s:%u: unknown key \"%s\", ignoring\n", - gc->path, line, key); + gc->path, line, k ? k : ""); return 0; } @@ -89,15 +81,13 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, break; case CFG_INCREMENT: /* the number of times the option is given */ - err = parse_uint(value, &uval); - if (!err && uval > INT_MAX) - err = -EINVAL; + err = parse_uint(value, INT_MAX, &uval); if (err) goto bad_value; *(int *)s->default_value = uval; break; case CFG_POSITIVE: - err = parse_uint(value, &uval); + err = parse_uint(value, UINT32_MAX, &uval); if (err) goto bad_value; *(uint32_t *)s->default_value = uval; @@ -110,7 +100,7 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, break; default: fprintf(stderr, "%s:%u: key \"%s\" can't be set, ignoring\n", - gc->path, line, key); + gc->path, line, k ? k : ""); break; } @@ -118,9 +108,11 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, bad_value: if (err == -EINVAL) { + __cleanup_free char *v = shr_escape(value); + fprintf(stderr, "%s:%u: invalid value \"%s\" for \"%s\", ignoring\n", - gc->path, line, value, key); + gc->path, line, v ? v : "", k ? k : ""); err = 0; } return err; @@ -131,6 +123,7 @@ static int global_config_event(enum shr_ini_event event, const char *section, unsigned int line, void *user_data) { struct global_config_ctx *gc = user_data; + __cleanup_free char *k = NULL; switch (event) { case SHR_INI_SECTION: @@ -138,13 +131,15 @@ static int global_config_event(enum shr_ini_event event, const char *section, case SHR_INI_KV: if (section && !strcmp(section, "Global")) return apply_kv(gc, key, value, line); + k = shr_escape(key); fprintf(stderr, "%s:%u: \"%s\" outside the [Global] section, ignoring\n", - gc->path, line, key); + gc->path, line, k ? k : ""); return 0; case SHR_INI_JUNK: + k = shr_escape(key); fprintf(stderr, "%s:%u: malformed line \"%s\", ignoring\n", - gc->path, line, key); + gc->path, line, k ? k : ""); return 0; } @@ -162,6 +157,13 @@ int nvme_load_global_config_from(const char *path) return err; } +int nvme_load_global_config_buf(const char *text, const char *name) +{ + struct global_config_ctx gc = { .path = name }; + + return shr_ini_parse_buf(text, global_config_event, &gc); +} + int nvme_load_global_config(void) { return nvme_load_global_config_from(PATH_NVME_CLI_INI); diff --git a/src/global-config.h b/src/global-config.h index ef704a3d29..1d67df930d 100644 --- a/src/global-config.h +++ b/src/global-config.h @@ -19,3 +19,6 @@ int nvme_load_global_config(void); /* Exposed for unit testing; nvme_load_global_config() is the real entry point. */ int nvme_load_global_config_from(const char *path); + +/* The same for the text of a config file, @name names it in warnings */ +int nvme_load_global_config_buf(const char *text, const char *name); diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index c03d498fe1..701a1f8330 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -138,6 +138,32 @@ static bool test_value_names(void) return pass; } +static bool test_strict_numbers_and_buf(void) +{ + bool pass = true; + + printf("test_strict_numbers_and_buf:\n"); + + nvme_args.timeout = 42; + nvme_load_global_config_buf("[Global]\ntimeout = +5\n", "buf"); + if (nvme_args.timeout != 42) { + printf(" - '+5' rejected [FAIL]\n"); + pass = false; + } else { + printf(" - '+5' rejected [PASS]\n"); + } + + nvme_load_global_config_buf("[Global]\ntimeout = 0x10\n", "buf"); + if (nvme_args.timeout != 16) { + printf(" - '0x10' taken from a buffer [FAIL]\n"); + pass = false; + } else { + printf(" - '0x10' taken from a buffer [PASS]\n"); + } + + return pass; +} + static bool test_malformed_value_skipped(void) { char *path; @@ -252,6 +278,7 @@ int main(void) pass &= test_valid_keys(); pass &= test_unknown_key_ignored(); pass &= test_value_names(); + pass &= test_strict_numbers_and_buf(); pass &= test_malformed_value_skipped(); pass &= test_hidden_and_out_of_range(); pass &= test_keys_outside_global_ignored(); From 320704b854372adca1cf0f1d2a9c985c204a1428 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:07:51 +0000 Subject: [PATCH 151/263] nvme: free the nvme-cli.conf strings which a later key replaces A string key given twice in nvme-cli.conf leaked the copy of the first value. Keep the strings the config set and free the one a later key of the same name replaces. nvme_global_config_free() frees them all, for the tests and the fuzzer. Signed-off-by: Daniel Wagner --- src/global-config.c | 47 +++++++++++++++++++++++++++---- src/global-config.h | 6 ++++ tests/unit/c/test-global-config.c | 21 ++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/global-config.c b/src/global-config.c index 804f94701b..a261938d30 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,44 @@ struct global_config_ctx { const char *path; }; +/* The strings the config set, for a later key of the same name to free */ +static char *owned[16]; +static unsigned int nr_owned; + +/* Set the string option @s to a copy of @value */ +static int set_string(struct argconfig_commandline_options *s, + const char *value) +{ + char **var = s->default_value; + unsigned int i; + char *dup; + + if (nr_owned == ARRAY_SIZE(owned)) + return -ENOMEM; + + dup = strdup(value); + if (!dup) + return -ENOMEM; + + for (i = 0; i < nr_owned; i++) { + if (owned[i] == *var) { + free(owned[i]); + owned[i] = owned[--nr_owned]; + break; + } + } + owned[nr_owned++] = dup; + *var = dup; + + return 0; +} + +void nvme_global_config_free(void) +{ + while (nr_owned) + free(owned[--nr_owned]); +} + /* A number of the config file up to @max, -EINVAL if it is none */ static int parse_uint(const char *value, uint64_t max, uint64_t *out) { @@ -59,7 +98,6 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, __cleanup_free char *k = shr_escape(key); uint64_t uval; bool bval; - char *dup; int err; if (!s || !s->default_value) { @@ -93,10 +131,9 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, *(uint32_t *)s->default_value = uval; break; case CFG_STRING: - dup = strdup(value); - if (!dup) - return -ENOMEM; - *(char **)s->default_value = dup; + err = set_string(s, value); + if (err) + return err; break; default: fprintf(stderr, "%s:%u: key \"%s\" can't be set, ignoring\n", diff --git a/src/global-config.h b/src/global-config.h index 1d67df930d..a06cf21e64 100644 --- a/src/global-config.h +++ b/src/global-config.h @@ -22,3 +22,9 @@ int nvme_load_global_config_from(const char *path); /* The same for the text of a config file, @name names it in warnings */ int nvme_load_global_config_buf(const char *text, const char *name); + +/* + * Free the strings the config set. The options which point to them must + * be set anew before they are used. + */ +void nvme_global_config_free(void); diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index 701a1f8330..8174b6386a 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -164,6 +164,26 @@ static bool test_strict_numbers_and_buf(void) return pass; } +static bool test_repeated_string(void) +{ + bool pass = true; + + printf("test_repeated_string:\n"); + + nvme_load_global_config_buf("[Global]\noutput-format = json\n" + "output-format = binary\n", "buf"); + if (strcmp(nvme_args.output_format, "binary")) { + printf(" - the last value counts [FAIL]\n"); + pass = false; + } else { + printf(" - the last value counts [PASS]\n"); + } + nvme_global_config_free(); + nvme_args.output_format = "normal"; + + return pass; +} + static bool test_malformed_value_skipped(void) { char *path; @@ -279,6 +299,7 @@ int main(void) pass &= test_unknown_key_ignored(); pass &= test_value_names(); pass &= test_strict_numbers_and_buf(); + pass &= test_repeated_string(); pass &= test_malformed_value_skipped(); pass &= test_hidden_and_out_of_range(); pass &= test_keys_outside_global_ignored(); From 23a5a9d9d491af73c954eaca7a2000e3ecc1edfc Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:18:20 +0000 Subject: [PATCH 152/263] tests: fuzz the parsers of the command line and the config files The command line and the config files are input nvme-cli doesn't control, so add fuzz targets for their parsers: argconfig, the INI parser, nvme-cli.conf and nvme-fabrics.conf. Every build runs them as tests of the fuzz suite: a small driver feeds each target its seeds and then mutations of them for a few seconds, from a fixed seed so a failure can be repeated, and keeps the input which crashes the target. With -Dfuzzing=true and clang the same targets are built for libFuzzer, for long coverage guided runs; AFL++ takes them as well. Documentation/TESTING.md describes both. The seeds are binary to git, they contain NUL bytes and the line endings of the files the parsers have to cope with. Signed-off-by: Daniel Wagner --- .gitattributes | 1 + Documentation/TESTING.md | 35 ++++ meson.build | 8 + meson_options.txt | 6 + tests/fuzz/argconfig.dict | 38 ++++ tests/fuzz/config.dict | 56 ++++++ tests/fuzz/corpus/argconfig/seed-00 | Bin 0 -> 20 bytes tests/fuzz/corpus/argconfig/seed-01 | Bin 0 -> 20 bytes tests/fuzz/corpus/argconfig/seed-02 | Bin 0 -> 26 bytes tests/fuzz/corpus/argconfig/seed-03 | Bin 0 -> 34 bytes tests/fuzz/corpus/argconfig/seed-04 | Bin 0 -> 39 bytes tests/fuzz/corpus/argconfig/seed-05 | Bin 0 -> 21 bytes tests/fuzz/corpus/argconfig/seed-06 | Bin 0 -> 20 bytes tests/fuzz/corpus/argconfig/seed-07 | Bin 0 -> 28 bytes tests/fuzz/corpus/argconfig/seed-08 | Bin 0 -> 43 bytes tests/fuzz/corpus/argconfig/seed-09 | Bin 0 -> 18 bytes tests/fuzz/corpus/fabrics-conf/seed-00 | 46 +++++ tests/fuzz/corpus/fabrics-conf/seed-01 | 10 + tests/fuzz/corpus/fabrics-conf/seed-02 | 4 + tests/fuzz/corpus/fabrics-conf/seed-03 | 5 + tests/fuzz/corpus/global-config/seed-00 | 10 + tests/fuzz/corpus/global-config/seed-01 | 4 + tests/fuzz/corpus/global-config/seed-02 | 6 + tests/fuzz/corpus/ini/seed-00 | 46 +++++ tests/fuzz/corpus/ini/seed-01 | 3 + tests/fuzz/corpus/ini/seed-02 | 9 + tests/fuzz/corpus/ini/seed-03 | 3 + tests/fuzz/fuzz-argconfig.c | 159 +++++++++++++++ tests/fuzz/fuzz-driver.c | 245 ++++++++++++++++++++++++ tests/fuzz/fuzz-fabrics-conf.c | 65 +++++++ tests/fuzz/fuzz-global-config.c | 54 ++++++ tests/fuzz/fuzz-ini.c | 50 +++++ tests/fuzz/fuzz.h | 15 ++ tests/fuzz/meson.build | 71 +++++++ tests/meson.build | 4 + 35 files changed, 953 insertions(+) create mode 100644 .gitattributes create mode 100644 tests/fuzz/argconfig.dict create mode 100644 tests/fuzz/config.dict create mode 100644 tests/fuzz/corpus/argconfig/seed-00 create mode 100644 tests/fuzz/corpus/argconfig/seed-01 create mode 100644 tests/fuzz/corpus/argconfig/seed-02 create mode 100644 tests/fuzz/corpus/argconfig/seed-03 create mode 100644 tests/fuzz/corpus/argconfig/seed-04 create mode 100644 tests/fuzz/corpus/argconfig/seed-05 create mode 100644 tests/fuzz/corpus/argconfig/seed-06 create mode 100644 tests/fuzz/corpus/argconfig/seed-07 create mode 100644 tests/fuzz/corpus/argconfig/seed-08 create mode 100644 tests/fuzz/corpus/argconfig/seed-09 create mode 100644 tests/fuzz/corpus/fabrics-conf/seed-00 create mode 100644 tests/fuzz/corpus/fabrics-conf/seed-01 create mode 100644 tests/fuzz/corpus/fabrics-conf/seed-02 create mode 100644 tests/fuzz/corpus/fabrics-conf/seed-03 create mode 100644 tests/fuzz/corpus/global-config/seed-00 create mode 100644 tests/fuzz/corpus/global-config/seed-01 create mode 100644 tests/fuzz/corpus/global-config/seed-02 create mode 100644 tests/fuzz/corpus/ini/seed-00 create mode 100644 tests/fuzz/corpus/ini/seed-01 create mode 100644 tests/fuzz/corpus/ini/seed-02 create mode 100644 tests/fuzz/corpus/ini/seed-03 create mode 100644 tests/fuzz/fuzz-argconfig.c create mode 100644 tests/fuzz/fuzz-driver.c create mode 100644 tests/fuzz/fuzz-fabrics-conf.c create mode 100644 tests/fuzz/fuzz-global-config.c create mode 100644 tests/fuzz/fuzz-ini.c create mode 100644 tests/fuzz/fuzz.h create mode 100644 tests/fuzz/meson.build diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..3dd72d6e10 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +tests/fuzz/corpus/** binary diff --git a/Documentation/TESTING.md b/Documentation/TESTING.md index b295e44d02..900ba7b530 100644 --- a/Documentation/TESTING.md +++ b/Documentation/TESTING.md @@ -195,3 +195,38 @@ when a violation is detected. The same known limitations apply as for Valgrind: Python tests and shell script tests may produce false positives. Focus triage on the `nvme-cli - *` unit tests. + +## Fuzzing + +The parsers of untrusted input have fuzz targets in `tests/fuzz`: the +command line parser (`argconfig`), the INI parser (`ini`), `nvme-cli.conf` +(`global-config`) and `nvme-fabrics.conf` (`fabrics-conf`). + +Every build runs them as tests of the `fuzz` suite: each target replays its +seeds in `tests/fuzz/corpus/` and then a few seconds of random +mutations of them. The mutations come from a fixed seed, so a failure can be +repeated, and the input which crashed a target is written to +`fuzz-crash-` in the build directory. + +```bash +meson test -C .build --suite fuzz +FUZZ_SECONDS=60 FUZZ_SEED=7 meson test -C .build --suite fuzz +``` + +Run in a sanitizer build (see above) they also catch memory errors which +don't crash. + +For longer, coverage guided runs, build the targets for libFuzzer with clang: + +```bash +CC=clang meson setup .build-fuzz -Dfuzzing=true -Db_sanitize=address,undefined +meson compile -C .build-fuzz +mkdir corpus && cp tests/fuzz/corpus/argconfig/* corpus/ +.build-fuzz/tests/fuzz/fuzz-argconfig -close_fd_mask=3 \ + -dict=tests/fuzz/argconfig.dict corpus +``` + +The config targets use `tests/fuzz/config.dict`. The targets use the +libFuzzer interface, so AFL++ can run them too (`afl-clang-fast` with +`-fsanitize=fuzzer`). Add an input which found a bug to the seeds of its +target. diff --git a/meson.build b/meson.build index fdc0ece671..611a346472 100644 --- a/meson.build +++ b/meson.build @@ -656,6 +656,14 @@ if host_system == 'windows' ) endif +# Coverage for the libFuzzer builds of the fuzz targets in tests/fuzz +if get_option('fuzzing') + if cc.get_id() != 'clang' + error('-Dfuzzing=true needs clang') + endif + add_project_arguments('-fsanitize=fuzzer-no-link', language : 'c') +endif + substs = configuration_data() substs.set('VERSION', meson.project_version()) substs.set('NVMECLI_LICENSE', meson.project_license()[0]) diff --git a/meson_options.txt b/meson_options.txt index 79f3d6c005..b985141d34 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -131,6 +131,12 @@ option( value : false, description: 'Run tests against real hardware' ) +option( + 'fuzzing', + type : 'boolean', + value : false, + description: 'Build the fuzz targets for libFuzzer instead of the fuzz tests (needs clang)' +) option( 'e2e-controller', type : 'string', diff --git a/tests/fuzz/argconfig.dict b/tests/fuzz/argconfig.dict new file mode 100644 index 0000000000..a1f34524bc --- /dev/null +++ b/tests/fuzz/argconfig.dict @@ -0,0 +1,38 @@ +"--flag" +"--string" +"--optional" +"--int" +"--byte" +"--named" +"--short" +"--uint" +"--long" +"--suffix" +"--double" +"--enum" +"--shared-flag" +"--shared-uint" +"--old-flag" +"--verbose" +"--output-format" +"--timeout" +"--dry-run" +"--human-readable" +"--help" +"--version" +"-h" +"-V" +"-?" +"--" +"-" +"=" +"infinite" +"-1" +"host" +"controller" +"one" +"two" +"Ki" +"Gi" +"0x" +"\x00" diff --git a/tests/fuzz/config.dict b/tests/fuzz/config.dict new file mode 100644 index 0000000000..3bb6916d88 --- /dev/null +++ b/tests/fuzz/config.dict @@ -0,0 +1,56 @@ +"nr-io-queues" +"nr-write-queues" +"nr-poll-queues" +"queue-size" +"keep-alive-tmo" +"reconnect-delay" +"ctrl-loss-tmo" +"fast-io-fail-tmo" +"tos" +"duplicate-connect" +"disable-sqflow" +"hdr-digest" +"data-digest" +"persistent" +"tls" +"concat" +"tls-key" +"tls-key-source" +"tls-key-identity" +"keyring" +"kxchap-secret" +"kxchap-ctrl-secret" +"hostnqn" +"hostid" +"hostsymname" +"nqn" +"controller" +"transport" +"traddr" +"trsvcid" +"host-traddr" +"host-iface" +"[Host]" +"[Subsystem]" +"[Discovery Controller]" +"[Discovery Controller Defaults]" +"[I/O Controller Defaults]" +"[Global]" +"=" +";" +"#" +"\x0a" +"yes" +"no" +"force" +"auto" +"tcp" +"rdma" +"fc" +"loop" +"infinite" +"verbose" +"timeout" +"output-format" +"quiet" +"dry-run" diff --git a/tests/fuzz/corpus/argconfig/seed-00 b/tests/fuzz/corpus/argconfig/seed-00 new file mode 100644 index 0000000000000000000000000000000000000000..a942f4514fd3fe363a0f659d76b202e1d69bebd3 GIT binary patch literal 20 bcmdPZP0LA4XV5KXs9@05EzQg;u{8w%IPL|R literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-01 b/tests/fuzz/corpus/argconfig/seed-01 new file mode 100644 index 0000000000000000000000000000000000000000..a78496688259e25ad5462509d91598ee80b59aff GIT binary patch literal 20 bcmdN*OUh#?DbHun)lJPS&9!w73Gx8|K;;HM literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-02 b/tests/fuzz/corpus/argconfig/seed-02 new file mode 100644 index 0000000000000000000000000000000000000000..a6029e62b40d1cd8ec23256c05f0497bf0c055ba GIT binary patch literal 26 fcmdPZ%`Yg)%+E{AVbJwYOiE_Z)dix`%)Al+Z1@M) literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-03 b/tests/fuzz/corpus/argconfig/seed-03 new file mode 100644 index 0000000000000000000000000000000000000000..13910d89050650523f7f5f6d58e8045f40b08b15 GIT binary patch literal 34 pcmdPZEy>JH%`Yvn&CE;7%*!lEWza1v^I*``&Cf~EP0LA42LRDv3$_3N literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-04 b/tests/fuzz/corpus/argconfig/seed-04 new file mode 100644 index 0000000000000000000000000000000000000000..b03f7587c382c5aeee9547c08a0a49fd661b11d6 GIT binary patch literal 39 scmdPZEiO$<%dD_9@y=w>)lJDSP0C5NHPkarH3o8X^7GPd4Jr%`0PuzjqW}N^ literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-05 b/tests/fuzz/corpus/argconfig/seed-05 new file mode 100644 index 0000000000000000000000000000000000000000..6373cf1625df30e967ff7929acee2c2c15fbc1c0 GIT binary patch literal 21 ccmdN5X3*6w&PXguP0=mQ%qy`qWYEn307Gd8LI3~& literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-06 b/tests/fuzz/corpus/argconfig/seed-06 new file mode 100644 index 0000000000000000000000000000000000000000..18b297ae0717d8805045332d9166007009d6be00 GIT binary patch literal 20 bcmdPZ%}dNpO|dOu&`o8?V9?cN(5(OfI%Wk5 literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-07 b/tests/fuzz/corpus/argconfig/seed-07 new file mode 100644 index 0000000000000000000000000000000000000000..cfe462bb17fb8010c23eb87a4339becb605e7127 GIT binary patch literal 28 hcmdPZ%`Yt}C@s-V%P-1JEV0ch&d+1WFE9jRBLI|q3EBVv literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-08 b/tests/fuzz/corpus/argconfig/seed-08 new file mode 100644 index 0000000000000000000000000000000000000000..5dc6358d9b87b44a6d87c1f6ddf1872fcbf2d079 GIT binary patch literal 43 wcmdPZ&CDyY)ip9SF*mU=HZ!qc(A6!@$S*3fH8V9eHU;vMDoavr4Jypj0P*t+egFUf literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/argconfig/seed-09 b/tests/fuzz/corpus/argconfig/seed-09 new file mode 100644 index 0000000000000000000000000000000000000000..dba65af4fee399c24303e672a5c48e3aae3c426e GIT binary patch literal 18 ZcmdNbW{{3HHq2$v)lJGzFD + */ + +/* + * Fuzz the command line parser: the input is split at NUL bytes into the + * arguments of a command, which is parsed against a table with every kind + * of option, and against the global options alone. + */ +#include +#include +#include +#include +#include + +#include "fuzz.h" + +#include "argconfig.h" + +#define MAX_ARGS 64 + +static struct { + bool flag, gflag; + char *str, *optstr; + int incr, num, enm; + uint8_t byte, named; + uint16_t shrt; + uint32_t uint, timeout; + uint64_t lng, suffix; + double dbl; +} v; + +static struct { + bool flag; + uint32_t uint; +} shared_tmpl, bound; + +static struct argconfig_opt_val names[] = { + VAL_BYTE("one", 1), + VAL_BYTE("two", 2), + VAL_BYTE("three", 3), + VAL_END() +}; + +static struct argconfig_opt_val enums[] = { + VAL_ENUM("host", 1), + VAL_ENUM("controller", 2), + VAL_ENUM("ctrl-init", 3), + VAL_END() +}; + +static struct argconfig_opt_val timeouts[] = { + VAL_UINT("infinite", UINT32_MAX), + VAL_UINT("-1", UINT32_MAX), + VAL_END() +}; + +static struct argconfig_commandline_options shared[] = { + OPT_FLAG("shared-flag", 'S', &shared_tmpl.flag, "shared flag"), + OPT_UINT("shared-uint", 0, &shared_tmpl.uint, "shared uint"), + OPT_END() +}; + +static struct argconfig_commandline_options globals[] = { + OPT_INCR("verbose", 'v', &v.incr, "verbose"), + OPT_FMT("output-format", 'o', &v.str, "format"), + OPT_UINT("timeout", 0, &v.timeout, "timeout", timeouts), + OPT_FLAG("dry-run", 0, &v.gflag, "dry run"), + OPT_INCR("human-readable", 'H', &v.incr, NULL, + .deprecated = "--verbose/-v"), + OPT_END() +}; + +static int parse(int argc, char **argv) +{ + struct argconfig_commandline_options *b; + int ret, i; + + b = argconfig_bind(shared, &shared_tmpl, sizeof(shared_tmpl), &bound); + if (!b) + return 0; + + { + struct argconfig_commandline_options opts[] = { + OPT_FLAG("flag", 'f', &v.flag, "flag", + .old_name = "old-flag"), + OPT_STRING("string", 's', "STR", &v.str, "string"), + OPT_STRING_OPTIONAL("optional", 'O', "STR", &v.optstr, + "optional"), + OPT_INT("int", 'i', &v.num, "int"), + OPT_BYTE("byte", 'b', &v.byte, "byte"), + OPT_BYTE("named", 'n', &v.named, "named", names), + OPT_SHRT("short", 'w', &v.shrt, "short"), + OPT_UINT("uint", 'u', &v.uint, "uint"), + OPT_LONG("long", 'l', &v.lng, "long"), + OPT_SUFFIX("suffix", 'z', &v.suffix, "suffix"), + OPT_DOUBLE("double", 'd', &v.dbl, "double"), + OPT_ENUM("enum", 'e', &v.enm, "enum", enums), + OPT_GROUP("Group"), + OPT_INCLUDE(b, "Included options"), + OPT_END() + }; + + ret = argconfig_parse(argc, argv, "fuzz", opts); + if (!ret) { + for (i = 0; i < argconfig_nr_operands(); i++) + (void)strlen(argconfig_operand(i)); + (void)argconfig_given(opts, &v.uint); + (void)argconfig_given(opts, &bound.uint); + } + } + free(b); + + argconfig_parse_global(argc, argv, NULL, &i); + + return ret; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + (void)argc; + (void)argv; + argconfig_set_global_options(globals); + argconfig_append_usage("fuzz [OPTIONS]"); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *argv[MAX_ARGS + 1]; + int argc = 1; + size_t i, start = 0; + + argv[0] = strdup("prog"); + for (i = 0; i <= size && argc < MAX_ARGS; i++) { + if (i == size || !data[i]) { + /* each argument in its own allocation, for ASan */ + argv[argc] = malloc(i - start + 1); + memcpy(argv[argc], data + start, i - start); + argv[argc][i - start] = '\0'; + argc++; + start = i + 1; + } + } + argv[argc] = NULL; + + memset(&v, 0, sizeof(v)); + memset(&bound, 0, sizeof(bound)); + parse(argc, argv); + + for (i = 0; i < (size_t)argc; i++) + free(argv[i]); + + return 0; +} diff --git a/tests/fuzz/fuzz-driver.c b/tests/fuzz/fuzz-driver.c new file mode 100644 index 0000000000..064dfc9505 --- /dev/null +++ b/tests/fuzz/fuzz-driver.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Run a fuzz target without libFuzzer, for the regular test suite: feed + * it the seed corpus, then mutations of it for a while. The mutations + * come from a fixed seed, so a run can be repeated. An input which + * crashes the target is written to fuzz-crash- first. + * + * Usage: fuzz- + * FUZZ_SECONDS sets the time for the mutations (default 2), FUZZ_SEED the + * seed (default 1). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUZZ_MAX_INPUT 4096 +#define FUZZ_MAX_SEEDS 256 + +#include "fuzz.h" + +/* a target need not have one */ +int LLVMFuzzerInitialize(int *argc, char ***argv) __attribute__((weak)); + +static struct { + uint8_t *data; + size_t size; +} seeds[FUZZ_MAX_SEEDS]; +static int nr_seeds; + +static const char *target; +static uint8_t input[FUZZ_MAX_INPUT]; +static size_t input_size; + +static uint64_t rng_state; + +static uint64_t rng(void) +{ + /* xorshift64 */ + rng_state ^= rng_state << 13; + rng_state ^= rng_state >> 7; + rng_state ^= rng_state << 17; + return rng_state; +} + +/* Keep the input which crashed the target */ +static void crash(int sig) +{ + char path[256]; + FILE *f; + + snprintf(path, sizeof(path), "fuzz-crash-%s", target); + f = fopen(path, "wb"); + if (f) { + fwrite(input, 1, input_size, f); + fclose(f); + } + signal(sig, SIG_DFL); + raise(sig); +} + +static void run(const uint8_t *data, size_t size) +{ + if (size > FUZZ_MAX_INPUT) + size = FUZZ_MAX_INPUT; + memcpy(input, data, size); + input_size = size; + LLVMFuzzerTestOneInput(input, input_size); +} + +static void load_seeds(const char *dir) +{ + struct dirent *e; + char path[4096]; + DIR *d = opendir(dir); + + if (!d) + return; + + while ((e = readdir(d)) && nr_seeds < FUZZ_MAX_SEEDS) { + FILE *f; + size_t n; + + if (e->d_name[0] == '.') + continue; + snprintf(path, sizeof(path), "%s/%s", dir, e->d_name); + f = fopen(path, "rb"); + if (!f) + continue; + seeds[nr_seeds].data = malloc(FUZZ_MAX_INPUT); + if (!seeds[nr_seeds].data) { + fclose(f); + break; + } + n = fread(seeds[nr_seeds].data, 1, FUZZ_MAX_INPUT, f); + fclose(f); + seeds[nr_seeds].size = n; + nr_seeds++; + } + closedir(d); +} + +/* The bytes which separate or mark things for the parsers */ +static const uint8_t special[] = { + '\0', '-', '=', ',', '.', '[', ']', '\n', '#', ' ', '0', 'x', 'K', 'i', + 0x1b, 0x7f, 0xff, +}; + +static void mutate(uint8_t *buf, size_t *size) +{ + int n = 1 + rng() % 8; + + while (n--) { + size_t pos = *size ? rng() % *size : 0; + + switch (rng() % 7) { + case 0: /* flip a bit */ + if (*size) + buf[pos] ^= 1 << (rng() % 8); + break; + case 1: /* set a random byte */ + if (*size) + buf[pos] = rng(); + break; + case 2: /* insert a special byte */ + if (*size < FUZZ_MAX_INPUT) { + memmove(buf + pos + 1, buf + pos, *size - pos); + buf[pos] = special[rng() % sizeof(special)]; + (*size)++; + } + break; + case 3: /* delete a byte */ + if (*size) { + memmove(buf + pos, buf + pos + 1, + *size - pos - 1); + (*size)--; + } + break; + case 4: { /* duplicate a chunk */ + size_t len = *size ? 1 + rng() % (*size - pos) : 0; + + if (*size + len <= FUZZ_MAX_INPUT) { + memmove(buf + pos + len, buf + pos, + *size - pos); + *size += len; + } + break; + } + case 5: { /* splice in a part of another seed */ + int s = rng() % nr_seeds; + size_t len = seeds[s].size ? rng() % seeds[s].size : 0; + + if (pos + len <= FUZZ_MAX_INPUT) { + memcpy(buf + pos, seeds[s].data, len); + if (pos + len > *size) + *size = pos + len; + } + break; + } + case 6: /* cut off the end */ + *size = pos; + break; + } + } +} + +int main(int argc, char **argv) +{ + const char *secs = getenv("FUZZ_SECONDS"); + const char *seed = getenv("FUZZ_SEED"); + unsigned long runs = 0; + uint8_t buf[FUZZ_MAX_INPUT]; + time_t end; + int i, out; + + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + target = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; + if (!strncmp(target, "fuzz-", 5)) + target += 5; + + rng_state = seed ? strtoull(seed, NULL, 0) : 1; + if (!rng_state) + rng_state = 1; + load_seeds(argv[1]); + if (!nr_seeds) { + fprintf(stderr, "no seeds in %s\n", argv[1]); + return 2; + } + + signal(SIGSEGV, crash); + signal(SIGABRT, crash); + signal(SIGBUS, crash); + signal(SIGFPE, crash); + signal(SIGILL, crash); + + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + + /* the target may print, keep stdout for the summary */ + fflush(stdout); + out = dup(STDOUT_FILENO); + if (!freopen("/dev/null", "w", stdout) || + !freopen("/dev/null", "w", stderr)) + return 2; + + for (i = 0; i < nr_seeds; i++) + run(seeds[i].data, seeds[i].size); + + end = time(NULL) + (secs ? strtol(secs, NULL, 0) : 2); + while (time(NULL) < end) { + int s = rng() % nr_seeds; + size_t size = seeds[s].size; + + memcpy(buf, seeds[s].data, size); + mutate(buf, &size); + run(buf, size); + runs++; + } + + if (out >= 0) { + dprintf(out, "%s: %d seeds, %lu mutations, seed %s\n", target, + nr_seeds, runs, seed ? seed : "1"); + close(out); + } + + for (i = 0; i < nr_seeds; i++) + free(seeds[i].data); + + return 0; +} diff --git a/tests/fuzz/fuzz-fabrics-conf.c b/tests/fuzz/fuzz-fabrics-conf.c new file mode 100644 index 0000000000..041c0871ba --- /dev/null +++ b/tests/fuzz/fuzz-fabrics-conf.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Fuzz nvme-fabrics.conf: the input is the text of the file, which + * libnvme parses into endpoints. The parser reads a path, so each input + * goes to a temporary file first. + */ +#include +#include +#include +#include +#include + +#include + +#include "fuzz.h" + +#include "nvme/config-ini.h" + +static struct libnvme_global_ctx *ctx; +static char path[32]; + +static void cleanup(void) +{ + unlink(path); + libnvme_free_global_ctx(ctx); +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + int fd; + + (void)argc; + (void)argv; + ctx = libnvme_create_global_ctx(); + snprintf(path, sizeof(path), "fuzz-fabrics-conf-XXXXXX"); + fd = mkstemp(path); + if (!ctx || fd < 0) + abort(); + close(fd); + atexit(cleanup); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct libnvmf_conf_file *file = NULL; + FILE *f = fopen(path, "wb"); + + if (!f) + return 0; + fwrite(data, 1, size, f); + fclose(f); + + if (!libnvmf_conf_file_parse(ctx, path, &file)) + libnvmf_conf_file_free(file); + + return 0; +} diff --git a/tests/fuzz/fuzz-global-config.c b/tests/fuzz/fuzz-global-config.c new file mode 100644 index 0000000000..bd520d4a37 --- /dev/null +++ b/tests/fuzz/fuzz-global-config.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Fuzz nvme-cli.conf: the input is the text of the file, which sets the + * global options. + */ +#include +#include +#include + +#include "fuzz.h" + +#include "args.h" +#include "global-config.h" + +struct nvme_args nvme_args; + +static const struct nvme_args defaults = { + .output_format = "normal", + .output_format_ver = 2, + .timeout = 5000, + .supported_output_formats = NORMAL, +}; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + (void)argc; + (void)argv; + nvme_args_register(); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *text = malloc(size + 1); + + if (!text) + return 0; + memcpy(text, data, size); + text[size] = '\0'; + + nvme_args = defaults; + nvme_load_global_config_buf(text, "fuzz"); + nvme_global_config_free(); + + free(text); + return 0; +} diff --git a/tests/fuzz/fuzz-ini.c b/tests/fuzz/fuzz-ini.c new file mode 100644 index 0000000000..f6dadd8fa4 --- /dev/null +++ b/tests/fuzz/fuzz-ini.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Fuzz the INI parser, which reads nvme-cli.conf, nvme-fabrics.conf and + * the configs of the daemons: the input is the text of a file. + */ +#include +#include +#include + +#include + +#include "fuzz.h" + +static int event(enum shr_ini_event event, const char *section, + const char *key, const char *value, unsigned int line, + void *user_data) +{ + size_t *n = user_data; + + /* touch every string the parser hands out */ + *n += section ? strlen(section) : 0; + *n += key ? strlen(key) : 0; + *n += value ? strlen(value) : 0; + *n += line + event; + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *text = malloc(size + 1); + size_t n = 0; + + if (!text) + return 0; + memcpy(text, data, size); + text[size] = '\0'; + + shr_ini_parse_buf(text, event, &n); + + free(text); + return 0; +} diff --git a/tests/fuzz/fuzz.h b/tests/fuzz/fuzz.h new file mode 100644 index 0000000000..7ac2c71b9c --- /dev/null +++ b/tests/fuzz/fuzz.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ +#pragma once + +#include +#include + +/* The libFuzzer interface of a fuzz target */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerInitialize(int *argc, char ***argv); diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build new file mode 100644 index 0000000000..f03362c569 --- /dev/null +++ b/tests/fuzz/meson.build @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Fuzz targets for the parsers of untrusted input: the command line and +# the config files. Without -Dfuzzing=true each target runs as a test, +# with the seeds in corpus/ and a few seconds of mutations of +# them (FUZZ_SECONDS, FUZZ_SEED). With -Dfuzzing=true and clang, the +# targets are built for libFuzzer: +# +# CC=clang meson setup fuzz -Dfuzzing=true -Db_sanitize=address,undefined +# ninja -C fuzz +# fuzz/tests/fuzz/fuzz-argconfig -dict=tests/fuzz/argconfig.dict \ +# tests/fuzz/corpus/argconfig +# +# The targets use the libFuzzer interface, which AFL++ (afl-clang-fast +# -fsanitize=fuzzer) runs as well. + +fuzz_targets = { + 'argconfig': { + 'sources': ['fuzz-argconfig.c', '../../src/argconfig.c'], + 'deps': [config_dep, ccan_dep, libnvme_dep, shared_dep], + }, + 'ini': { + 'sources': ['fuzz-ini.c'], + 'deps': [config_dep, shared_dep], + }, + 'global-config': { + 'sources': [ + 'fuzz-global-config.c', + '../../src/argconfig.c', + '../../src/global-config.c', + '../../src/global-options.c', + ], + 'deps': [config_dep, ccan_dep, libnvme_dep, shared_dep], + }, +} + +if want_fabrics + fuzz_targets += { + 'fabrics-conf': { + 'sources': ['fuzz-fabrics-conf.c'], + 'deps': [config_dep, ccan_dep, shared_dep, libnvme_test_dep], + }, + } +endif + +foreach name, t : fuzz_targets + if get_option('fuzzing') + executable( + 'fuzz-' + name, + t['sources'], + include_directories: include_directories('../../src'), + dependencies: t['deps'], + link_args: ['-fsanitize=fuzzer'], + ) + else + exe = executable( + 'fuzz-' + name, + t['sources'] + ['fuzz-driver.c'], + include_directories: include_directories('../../src'), + dependencies: t['deps'], + ) + test( + 'nvme-cli - fuzz-' + name, + exe, + args: [meson.current_source_dir() / 'corpus' / name], + workdir: meson.current_build_dir(), + suite: 'fuzz', + timeout: 120, + ) + endif +endforeach diff --git a/tests/meson.build b/tests/meson.build index b8b541b5e3..26c9b8f610 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -24,6 +24,10 @@ if get_option('tests') subdir('cli') endif +if (get_option('tests') or get_option('fuzzing')) and host_system != 'windows' + subdir('fuzz') +endif + if get_option('e2e-tests') subdir('e2e') endif From 4d06ff7ac6a1f45987e0e8d7651b3b0c950334ef Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 16:18:34 +0000 Subject: [PATCH 153/263] NEWS: document the stricter number parsing and the escaped echoes Numbers are parsed strictly on the command line and in nvme-cli.conf, and echoed input is escaped. Signed-off-by: Daniel Wagner --- NEWS.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/NEWS.md b/NEWS.md index a70b8ce308..a1233f7627 100644 --- a/NEWS.md +++ b/NEWS.md @@ -44,6 +44,19 @@ * Number options reject text after the number. Before, the rest was ignored: `--timeout=10s` set a timeout of 10 milliseconds. +* Numbers on the command line and in `nvme-cli.conf` are parsed + strictly: leading whitespace and a `+` are rejected, a signed option + rejects a value out of its range instead of wrapping around, and a + floating point option takes a `.` as decimal point in any locale and + rejects `nan` and `inf`. Sizes with a suffix (`4Ki`, `1.5G`) reject a + value which overflows, take the digits after a `.` in decimal + (`1.09k` was rejected as octal, `1.500k` was 1000), and use a `.` in + any locale as well. An empty value no longer selects a value name. + +* Errors and warnings which repeat a command line argument or a line of + a config file write its control characters as `\xNN`, so it can't + send escape sequences to the terminal. + * Global options mean the same for every command now: they are looked up before the options of a command, and no command uses their names any more. From 7f24af13fa50432d0ccdb6e669c8a64a04ac6024 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:31:59 +0000 Subject: [PATCH 154/263] nvme-print-stdout: decode the reserved RPMB boot partition write protection value RPMBBPWPS is a two bit field, but its table has names for only three values. A controller reporting the reserved value 11b made id-ctrl read past the end of the table and print whatever the pointer behind it points to. Found by fuzzing the device data. Fixes: d0350945ace4 ("print: id-ctrl print updated based on NVMe v2.1") Signed-off-by: Daniel Wagner --- src/nvme-print-stdout-id.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-stdout-id.c b/src/nvme-print-stdout-id.c index 555e5547f1..f69b25f867 100644 --- a/src/nvme-print-stdout-id.c +++ b/src/nvme-print-stdout-id.c @@ -231,7 +231,8 @@ static struct shr_table *stdout_id_ctrl_bpcap_table(__u8 ctrl_bpcap) static const char * const rpmbbpwps_def[] = { "Support Not Specified", "Not Supported", - "Supported" + "Supported", + "Reserved" }; t = stdout_bits_table_create(); From f9c512a8f207d6cd63e6a43963afff6f203721b6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:32:59 +0000 Subject: [PATCH 155/263] nvme-print: add nvme_log_nr_entries() to bound a log page entry count A log page reports how many entries it holds, but that count comes from the device. Commands read the header first, size the buffer by its count and then read the whole page, which can report more entries the second time. And get-log prints a page with the length the user asked for. Add a helper which bounds an entry count by the length of the buffer, for the printers to use. Signed-off-by: Daniel Wagner --- src/nvme-print.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/nvme-print.h b/src/nvme-print.h index 72ae2c1ad7..9b51bb9a60 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -89,6 +89,20 @@ static inline int nvme_id_ns_nr_lbaf(struct nvme_id_ns *ns) return n < max ? n : max; } +/* + * A log page counts its entries, but the count comes from the device and + * the page may be shorter: a later read can report more entries than the + * buffer was sized for. Return: @nr, but at most the number of @size byte + * entries @len bytes hold after a @hdr byte header. + */ +static inline size_t nvme_log_nr_entries(size_t nr, size_t len, size_t hdr, + size_t size) +{ + size_t max = len > hdr ? (len - hdr) / size : 0; + + return nr < max ? nr : max; +} + /* Return: the LBA data size in bytes, or 0 if it is not representable. */ static inline unsigned int nvme_lbaf_data_size(__u8 ds) { From cbe984499092967bceeca609cf4cc26aa89a05da Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:33:33 +0000 Subject: [PATCH 156/263] nvme-print: bound the FDP reclaim unit handle entries by the log length fdp usage and fdp status read the header, size the buffer by its entry count and read the whole page again. The printers then trust the count of the second read, which the device may have raised, and walk past the end of the buffer. get-log passes a page of any length to the usage printer as well. Print no more entries than the buffer holds. Found by fuzzing the device data. Fixes: 83e508404085 ("nvme: add flexible data placement management commands") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 4 ++++ src/nvme-print-stdout-log.c | 4 +++- src/nvme-print-stdout.c | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index dba1fb255f..c841c811d4 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2800,6 +2800,8 @@ static void json_nvme_fdp_usage(struct nvme_fdp_ruhu_log *log, size_t len) obj_add_uint(r, "nruh", nruh); + nruh = nvme_log_nr_entries(nruh, len, sizeof(*log), + sizeof(log->ruhus[0])); for (int i = 0; i < nruh; i++) { struct nvme_fdp_ruhu_desc *ruhu = &log->ruhus[i]; @@ -2875,6 +2877,8 @@ static void json_nvme_fdp_ruh_status(struct nvme_fdp_ruh_status *status, size_t obj_add_uint(r, "nruhsd", nruhsd); + nruhsd = nvme_log_nr_entries(nruhsd, len, sizeof(*status), + sizeof(status->ruhss[0])); for (unsigned int i = 0; i < nruhsd; i++) { struct nvme_fdp_ruh_status_desc *ruhs = &status->ruhss[i]; diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 127186664f..bc93d17866 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -1420,7 +1420,9 @@ void stdout_fdp_configs(struct nvme_fdp_config_log *log, size_t len) void stdout_fdp_usage(struct nvme_fdp_ruhu_log *log, size_t len) { - uint16_t nruh = le16_to_cpu(log->nruh); + uint16_t nruh = nvme_log_nr_entries(le16_to_cpu(log->nruh), len, + sizeof(*log), + sizeof(log->ruhus[0])); struct shr_table *t; t = stdout_kv_table_create(); diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 2ef85c3a1e..516509c472 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -288,7 +288,9 @@ static void stdout_kv_render(FILE *stream, struct shr_table *t); static void stdout_fdp_ruh_status(struct nvme_fdp_ruh_status *status, size_t len) { - uint16_t nruhsd = le16_to_cpu(status->nruhsd); + uint16_t nruhsd = nvme_log_nr_entries(le16_to_cpu(status->nruhsd), + len, sizeof(*status), + sizeof(status->ruhss[0])); struct shr_table *t; for (unsigned int i = 0; i < nruhsd; i++) { From 1aa2dd7067d8030a0eda04d3bc6669df8fe98040 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:39:35 +0000 Subject: [PATCH 157/263] log: read the records of the host and AVE discovery and pull model logs After the header, get_host_discovery(), get_ave_discovery() and get_pull_model_ddc_req() mean to read the rest of the page. But they set the offset to the length of the whole page and ask for its length minus that offset: zero bytes past the end. The buffer grows to the length the header reports, and its zeroed tail is printed as the records. The zero length read also goes to the device as a request for the largest transfer, as the length of a Get Log Page is zero based. Read the part of the page which follows the header, if there is one. get_host_discovery() also passed rae as ALLHOSTE and never set RAE for the header, so with --all-host-entries the header came from the other page. Add a mock test which serves the pages and checks the printed records. Fixes: 0a8067d8e919 ("nvme: add host-discovery-log command") Fixes: 1571543dcd9b ("nvme: add ave-discovery-log command") Fixes: d86ab5d847e3 ("nvme: add pull-model-ddc-req-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 46 ++++--- tests/cli/meson.build | 10 ++ tests/cli/nvme_discovery_logs_test.py | 179 ++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 17 deletions(-) create mode 100644 tests/cli/nvme_discovery_logs_test.py diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 28f9f44b3f..cd358005d4 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2526,6 +2526,7 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost struct nvme_host_discovery_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u32 thdlpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_HOST_DISCOVERY, .nsid = NVME_NSID_ALL, @@ -2537,16 +2538,19 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost if (!log) return -ENOMEM; - nvme_init_get_log_host_discovery(&cmd, rae, log, log_len); - err = libnvme_get_log(hdl, &cmd, false, log_len); + nvme_init_get_log_host_discovery(&cmd, allhoste, log, log_len); + err = libnvme_get_log(hdl, &cmd, rae, log_len); if (err) goto err_free; - log_len = le32_to_cpu(log->thdlpl); - err = get_log_offset(hdl, &args, &log_len, le32_to_cpu(log->thdlpl) - log_len, - (void **)&log); - if (err) - goto err_free; + /* the records follow the header */ + thdlpl = le32_to_cpu(log->thdlpl); + if (thdlpl > log_len) { + err = get_log_offset(hdl, &args, &log_len, thdlpl - log_len, + (void **)&log); + if (err) + goto err_free; + } *logp = log; return 0; @@ -2607,6 +2611,7 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, struct nvme_ave_discovery_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u32 tadlpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_AVE_DISCOVERY, .nsid = NVME_NSID_ALL, @@ -2622,11 +2627,14 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, if (err) goto err_free; - log_len = le32_to_cpu(log->tadlpl); - err = get_log_offset(hdl, &args, &log_len, le32_to_cpu(log->tadlpl) - log_len, - (void **)&log); - if (err) - goto err_free; + /* the records follow the header */ + tadlpl = le32_to_cpu(log->tadlpl); + if (tadlpl > log_len) { + err = get_log_offset(hdl, &args, &log_len, tadlpl - log_len, + (void **)&log); + if (err) + goto err_free; + } *logp = log; return 0; @@ -2683,6 +2691,7 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, struct nvme_pull_model_ddc_req_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u32 tpdrpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_PULL_MODEL_DDC_REQ, .nsid = NVME_NSID_ALL, @@ -2698,11 +2707,14 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, if (err) goto err_free; - log_len = le32_to_cpu(log->tpdrpl); - err = get_log_offset(hdl, &args, &log_len, le32_to_cpu(log->tpdrpl) - log_len, - (void **)&log); - if (err) - goto err_free; + /* the operating system specific parameters follow the header */ + tpdrpl = le32_to_cpu(log->tpdrpl); + if (tpdrpl > log_len) { + err = get_log_offset(hdl, &args, &log_len, tpdrpl - log_len, + (void **)&log); + if (err) + goto err_free; + } *logp = log; return 0; diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 62f2f6fa3e..a867639880 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -119,6 +119,16 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) + # The host and AVE discovery printers need the fabrics code. + if want_fabrics and json_c_dep.found() and 'log' in selected_plugins + test('nvme-cli - discovery-logs-cli', + python3, + args: [files('nvme_discovery_logs_test.py'), nvme_exe, + mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib]) + endif + if 'id' in selected_plugins and 'log' in selected_plugins and \ 'feat' in selected_plugins print_test_env = environment() diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py new file mode 100644 index 0000000000..d12ce9b4e2 --- /dev/null +++ b/tests/cli/nvme_discovery_logs_test.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE Software Solutions +# +# Authors: Daniel Wagner +"""Tests for the host-discovery, ave-discovery and pull-model-ddc-req log +commands against a mocked controller. + +Each of these log pages has a header which says how long the whole page +is, and records or data behind it. The tests check that the commands read +the part behind the header and print it, and that the header asks for +the page the options select. + +Usage: python3 nvme_discovery_logs_test.py +""" +import json +import os +import shutil +import struct +import sys +import tempfile +import unittest + +from nvme_mock_ipc import MockIPCServer, make_mock_env, resolve_mock_lib_path, run_nvme + +_NVME_BIN = sys.argv[1] if len(sys.argv) > 1 and not sys.argv[1].startswith('-') else 'nvme' +_MOCK_LIB = resolve_mock_lib_path("./libmock_nvme.so") + +_OPC_GET_LOG_PAGE = 0x02 +_LID_HOST_DISCOVERY = 0x71 +_LID_AVE_DISCOVERY = 0x72 +_LID_PULL_MODEL_DDC_REQ = 0x73 + +_HEADER_LEN = 1024 # host and AVE discovery log headers +_HEDLPE_LEN = 1032 # struct nvme_host_ext_discovery_log +_ADLPE_LEN = 232 # struct nvme_ave_discovery_log_entry +_ATR_LEN = 20 # struct nvme_ave_tr_record +_PULL_HEADER_LEN = 8 # struct nvme_pull_model_ddc_req_log + +_NVMF_TRTYPE_TCP = 3 +_NVMF_ADDR_FAMILY_IP4 = 1 + + +def _host_discovery_log(hostnqns): + """A host discovery log page with one record for each of @hostnqns.""" + records = b'' + for nqn in hostnqns: + rec = bytearray(_HEDLPE_LEN) + rec[0] = _NVMF_TRTYPE_TCP + rec[1] = _NVMF_ADDR_FAMILY_IP4 + rec[256:256 + len(nqn)] = nqn.encode() + traddr = b'192.0.2.1' + rec[512:512 + len(traddr)] = traddr + struct.pack_into('> 8) & 0x7f + self.requests.append((lid, lsp, lpo, req_len)) + page = self.logs.get(lid, b'') + data = page[lpo:lpo + req_len] + self.send_response(conn, 0, payload=data + b'\x00' * (req_len - len(data))) + + +class DiscoveryLogsTest(unittest.TestCase): + + DEVICE = '/dev/nvme0' + + def setUp(self): + self.tmp = tempfile.mkdtemp(prefix='nvme-discovery-logs-', dir='/tmp') + self.sysfs_dir = os.path.join(self.tmp, 'sysfs') + self.base_dir = os.path.join(self.tmp, 'base') + os.mkdir(self.sysfs_dir) + os.mkdir(self.base_dir) + self.server = LogMockServer(os.path.join(self.tmp, 'ipc.sock')) + self.server.start() + self.env = make_mock_env(_MOCK_LIB, self.server.sock_path) + + def tearDown(self): + self.server.shutdown() + self.server.join() + shutil.rmtree(self.tmp, ignore_errors=True) + + def _run_json(self, *args): + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'log', *args, self.DEVICE, '-o', 'json') + self.assertEqual(result.returncode, 0, + f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}') + self.assertEqual(self.server.errors, []) + return json.loads(result.stdout) + + def test_host_discovery_records_are_read(self): + self.server.logs[_LID_HOST_DISCOVERY] = \ + _host_discovery_log(['nqn.2014-08.org.example:host1', + 'nqn.2014-08.org.example:host2']) + out = self._run_json('host-discovery') + self.assertEqual(out['hedlpe: 0']['hostnqn'], + 'nqn.2014-08.org.example:host1') + self.assertEqual(out['hedlpe: 1']['hostnqn'], + 'nqn.2014-08.org.example:host2') + self.assertEqual(out['hedlpe: 1']['traddr'], '192.0.2.1') + + def test_host_discovery_header_asks_for_all_host_entries(self): + self.server.logs[_LID_HOST_DISCOVERY] = \ + _host_discovery_log(['nqn.2014-08.org.example:host1']) + self._run_json('host-discovery', '--all-host-entries') + reqs = [r for r in self.server.requests if r[0] == _LID_HOST_DISCOVERY] + self.assertTrue(reqs) + for lid, lsp, lpo, length in reqs: + self.assertEqual(lsp, 1, f'request at offset {lpo}') + + def test_host_discovery_header_only(self): + self.server.logs[_LID_HOST_DISCOVERY] = _host_discovery_log([]) + out = self._run_json('host-discovery') + self.assertEqual(out['thdlpl'], _HEADER_LEN) + self.assertFalse([k for k in out if k.startswith('hedlpe')]) + + def test_ave_discovery_records_are_read(self): + self.server.logs[_LID_AVE_DISCOVERY] = \ + _ave_discovery_log(['nqn.2014-08.org.example:ave1']) + out = self._run_json('ave-discovery') + self.assertEqual(out['adlpe: 0']['avenqn'], + 'nqn.2014-08.org.example:ave1') + self.assertEqual(out['adlpe: 0']['atr: 0']['avetraddr'], '192.0.2.7') + self.assertEqual(out['adlpe: 0']['atr: 0']['avetrsvcid'], 4420) + + def test_pull_model_ddc_req_osp_is_read(self): + self.server.logs[_LID_PULL_MODEL_DDC_REQ] = \ + _pull_model_ddc_req_log(b'OSPDATA!') + out = self._run_json('pull-model-ddc-req') + self.assertEqual(out['osp'], ['OSPDATA!']) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]]) From 4d4af263f83deb862c7406342e9435fc5bf563f4 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:40:33 +0000 Subject: [PATCH 158/263] nvme-print: pass the log length to the discovery and pull model printers The binary printers of the host discovery, AVE discovery and pull model DDC request logs dump as many bytes as the total length in the header says, which the device sets. A buffer read before the length changed, or a short page from get-log, is smaller than that. Pass the length of the buffer to the printers. The binary printers dump the buffer; the text and JSON printers get bounded next. Fixes: 0a8067d8e919 ("nvme: add host-discovery-log command") Fixes: 1571543dcd9b ("nvme: add ave-discovery-log command") Fixes: d86ab5d847e3 ("nvme: add pull-model-ddc-req-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 27 ++++++++++++++++++--------- src/nvme-print-binary.c | 15 +++++++++------ src/nvme-print-json.c | 20 +++++++++++++++----- src/nvme-print-stdout-log.c | 7 ++++--- src/nvme-print-stdout.h | 7 ++++--- src/nvme-print.c | 18 +++++++++--------- src/nvme-print.h | 15 +++++++++------ 7 files changed, 68 insertions(+), 41 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index cd358005d4..724d4b22e8 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2520,7 +2520,8 @@ static int get_reachability_associations_log(int argc, char **argv, struct comma } static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhoste, bool rae, - struct nvme_host_discovery_log **logp) + struct nvme_host_discovery_log **logp, + __u64 *lenp) { int err; struct nvme_host_discovery_log *log; @@ -2553,6 +2554,7 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2567,6 +2569,7 @@ static int get_host_discovery_log(int argc, char **argv, struct command *acmd, s nvme_print_flags_t flags; int err; __cleanup_libnvme_free struct nvme_host_discovery_log *log = NULL; + __u64 log_len; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2593,19 +2596,19 @@ static int get_host_discovery_log(int argc, char **argv, struct command *acmd, s flags = nvme_args.output_flags; - err = get_host_discovery(hdl, cfg.allhoste, cfg.rae, &log); + err = get_host_discovery(hdl, cfg.allhoste, cfg.rae, &log, &log_len); if (err) { nvme_show_err(err, "host discovery log"); return err; } - nvme_show_host_discovery_log(log, flags); + nvme_show_host_discovery_log(log, log_len, flags); return err; } static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, - struct nvme_ave_discovery_log **logp) + struct nvme_ave_discovery_log **logp, __u64 *lenp) { int err; struct nvme_ave_discovery_log *log; @@ -2637,6 +2640,7 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2651,6 +2655,7 @@ static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, st int err; __cleanup_libnvme_free struct nvme_ave_discovery_log *log = NULL; + __u64 log_len; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2673,19 +2678,21 @@ static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, st flags = nvme_args.output_flags; - err = get_ave_discovery(hdl, cfg.rae, &log); + err = get_ave_discovery(hdl, cfg.rae, &log, &log_len); if (err) { nvme_show_err(err, "ave discovery log"); return err; } - nvme_show_ave_discovery_log(log, flags); + nvme_show_ave_discovery_log(log, log_len, flags); return err; } static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, - bool rae, struct nvme_pull_model_ddc_req_log **logp) + bool rae, + struct nvme_pull_model_ddc_req_log **logp, + __u64 *lenp) { int err; struct nvme_pull_model_ddc_req_log *log; @@ -2717,6 +2724,7 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2732,6 +2740,7 @@ static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acm int err; __cleanup_libnvme_free struct nvme_pull_model_ddc_req_log *log = NULL; + __u64 log_len; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -2754,13 +2763,13 @@ static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acm flags = nvme_args.output_flags; - err = get_pull_model_ddc_req(hdl, cfg.rae, &log); + err = get_pull_model_ddc_req(hdl, cfg.rae, &log, &log_len); if (err) { nvme_show_err(err, "pull model ddc req log"); return err; } - nvme_show_pull_model_ddc_req_log(log, flags); + nvme_show_pull_model_ddc_req_log(log, log_len, flags); return err; } diff --git a/src/nvme-print-binary.c b/src/nvme-print-binary.c index 506028561a..d13ba6405d 100644 --- a/src/nvme-print-binary.c +++ b/src/nvme-print-binary.c @@ -331,19 +331,22 @@ static void binary_reachability_associations_log(struct nvme_reachability_associ d_raw((unsigned char *)log, len); } -static void binary_host_discovery_log(struct nvme_host_discovery_log *log) +static void binary_host_discovery_log(struct nvme_host_discovery_log *log, + __u64 len) { - d_raw((unsigned char *)log, le32_to_cpu(log->thdlpl)); + d_raw((unsigned char *)log, len); } -static void binary_ave_discovery_log(struct nvme_ave_discovery_log *log) +static void binary_ave_discovery_log(struct nvme_ave_discovery_log *log, + __u64 len) { - d_raw((unsigned char *)log, le32_to_cpu(log->tadlpl)); + d_raw((unsigned char *)log, len); } -static void binary_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) +static void binary_pull_model_ddc_req_log( + struct nvme_pull_model_ddc_req_log *log, __u64 len) { - d_raw((unsigned char *)log, le32_to_cpu(log->tpdrpl)); + d_raw((unsigned char *)log, len); } static void binary_power_meas_log(struct nvme_power_meas_log *log, __u32 size) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index c841c811d4..85579c6fb6 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5998,7 +5998,8 @@ static void json_reachability_associations_log(struct nvme_reachability_associat } #ifdef CONFIG_FABRICS -static void json_host_discovery_log(struct nvme_host_discovery_log *log) +static void json_host_discovery_log(struct nvme_host_discovery_log *log, + __u64 len) { struct json_object *r = json_r; __u32 i; @@ -6088,7 +6089,8 @@ static void obj_add_traddr(struct json_object *o, const char *k, __u8 adrfam, __ obj_add_str(o, k, dst); } -static void json_ave_discovery_log(struct nvme_ave_discovery_log *log) +static void json_ave_discovery_log(struct nvme_ave_discovery_log *log, + __u64 len) { struct json_object *r = json_r; __u32 i; @@ -6132,11 +6134,19 @@ static void json_ave_discovery_log(struct nvme_ave_discovery_log *log) } } #else -static void json_host_discovery_log(struct nvme_host_discovery_log *log) {} -static void json_ave_discovery_log(struct nvme_ave_discovery_log *log) {} +static void json_host_discovery_log(struct nvme_host_discovery_log *log, + __u64 len) +{ +} + +static void json_ave_discovery_log(struct nvme_ave_discovery_log *log, + __u64 len) +{ +} #endif -static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) +static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, + __u64 len) { struct json_object *r = json_r; __u32 tpdrpl = le32_to_cpu(log->tpdrpl); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index bc93d17866..12cc506044 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -2714,7 +2714,7 @@ void stdout_discovery_log(const struct nvmf_discovery_log *log, int numrec) } } -void stdout_host_discovery_log(struct nvme_host_discovery_log *log) +void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) { __u32 i; __u16 j; @@ -2834,7 +2834,7 @@ static void stdout_kv_add_traddr(struct shr_table *t, const char *field, stdout_kv_add(t, field, "%s", dst); } -void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log) +void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len) { __u32 i; __u8 j; @@ -3052,7 +3052,8 @@ void stdout_reachability_associations_log( stdout_kv_table_finish(t, "reachability-associations"); } -void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) +void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, + __u64 len) { __u32 tpdrpl = le32_to_cpu(log->tpdrpl); __u32 osp_len = diff --git a/src/nvme-print-stdout.h b/src/nvme-print-stdout.h index 410dca02c6..6e854bbe6f 100644 --- a/src/nvme-print-stdout.h +++ b/src/nvme-print-stdout.h @@ -97,12 +97,13 @@ void stdout_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len); void stdout_reachability_associations_log( struct nvme_reachability_associations_log *log, __u64 len); -void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log); +void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, + __u64 len); void stdout_power_meas_log(struct nvme_power_meas_log *log, __u32 size); #ifdef CONFIG_FABRICS void stdout_discovery_log(const struct nvmf_discovery_log *log, int numrec); -void stdout_host_discovery_log(struct nvme_host_discovery_log *log); -void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log); +void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len); +void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len); #else /* CONFIG_FABRICS */ #define stdout_discovery_log NULL #define stdout_host_discovery_log NULL diff --git a/src/nvme-print.c b/src/nvme-print.c index 84112eaf4b..27decf0b6b 100644 --- a/src/nvme-print.c +++ b/src/nvme-print.c @@ -1987,21 +1987,21 @@ void nvme_show_reachability_associations_log(struct nvme_reachability_associatio } void nvme_show_host_discovery_log(struct nvme_host_discovery_log *log, - nvme_print_flags_t flags) + __u64 len, nvme_print_flags_t flags) { - nvme_print(host_discovery_log, flags, log); + nvme_print(host_discovery_log, flags, log, len); } void nvme_show_ave_discovery_log(struct nvme_ave_discovery_log *log, - nvme_print_flags_t flags) + __u64 len, nvme_print_flags_t flags) { - nvme_print(ave_discovery_log, flags, log); + nvme_print(ave_discovery_log, flags, log, len); } void nvme_show_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, - nvme_print_flags_t flags) + __u64 len, nvme_print_flags_t flags) { - nvme_print(pull_model_ddc_req_log, flags, log); + nvme_print(pull_model_ddc_req_log, flags, log, len); } void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u16 lsi, @@ -2117,13 +2117,13 @@ void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsi break; } case NVME_LOG_LID_HOST_DISCOVERY: - nvme_show_host_discovery_log(log, flags); + nvme_show_host_discovery_log(log, len, flags); break; case NVME_LOG_LID_AVE_DISCOVERY: - nvme_show_ave_discovery_log(log, flags); + nvme_show_ave_discovery_log(log, len, flags); break; case NVME_LOG_LID_PULL_MODEL_DDC_REQ: - nvme_show_pull_model_ddc_req_log(log, flags); + nvme_show_pull_model_ddc_req_log(log, len, flags); break; case NVME_LOG_LID_POWER_MEASUREMENT: nvme_show_power_meas_log(log, len, flags); diff --git a/src/nvme-print.h b/src/nvme-print.h index 9b51bb9a60..37f2a170a9 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -243,9 +243,12 @@ struct print_ops { void (*reachability_groups_log)(struct nvme_reachability_groups_log *log, __u64 len); void (*reachability_associations_log)(struct nvme_reachability_associations_log *log, __u64 len); - void (*host_discovery_log)(struct nvme_host_discovery_log *log); - void (*ave_discovery_log)(struct nvme_ave_discovery_log *log); - void (*pull_model_ddc_req_log)(struct nvme_pull_model_ddc_req_log *log); + void (*host_discovery_log)(struct nvme_host_discovery_log *log, + __u64 len); + void (*ave_discovery_log)(struct nvme_ave_discovery_log *log, + __u64 len); + void (*pull_model_ddc_req_log)(struct nvme_pull_model_ddc_req_log *log, + __u64 len); /* libnvme tree print functions */ void (*list_item)(struct libnvme_ns *n, struct shr_table *t); @@ -670,11 +673,11 @@ void nvme_show_reachability_groups_log(struct nvme_reachability_groups_log *log, void nvme_show_reachability_associations_log(struct nvme_reachability_associations_log *log, __u64 len, nvme_print_flags_t flags); void nvme_show_host_discovery_log(struct nvme_host_discovery_log *log, - nvme_print_flags_t flags); + __u64 len, nvme_print_flags_t flags); void nvme_show_ave_discovery_log(struct nvme_ave_discovery_log *log, - nvme_print_flags_t flags); + __u64 len, nvme_print_flags_t flags); void nvme_show_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, - nvme_print_flags_t flags); + __u64 len, nvme_print_flags_t flags); void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u16 lsi, __u8 lsp, void *log, __u32 len, nvme_print_flags_t flags); void nvme_show_pel_header(struct nvme_persistent_event_log *pevent_log_head, int human); From d4bf8e33aa33318a59e5010fc0af3b4f38227e96 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:43:32 +0000 Subject: [PATCH 159/263] nvme-print: walk the host discovery log records within the page The host discovery log printers step from record to record by the record length, until the total length from the header. Neither comes from anything but the device: - a record length of 0 never gets anywhere, the printers loop until memory runs out - a record or a total length past the end of the buffer has them read past it - the extended attributes of a record are walked by their count and lengths, past the record and the buffer - the host NQN and transport address need not end in a NUL Add nvme_host_discovery_record() and nvme_host_discovery_exat(), which return a record or an attribute only if it fits, and print no further than that. Print the strings no longer than their fields. Found by fuzzing the device data. Fixes: 0a8067d8e919 ("nvme: add host-discovery-log command") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 28 ++++++++++------- src/nvme-print-stdout-log.c | 22 ++++++++------ src/nvme-print.h | 43 +++++++++++++++++++++++++++ tests/cli/nvme_discovery_logs_test.py | 41 ++++++++++++++++++++----- 4 files changed, 107 insertions(+), 27 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 85579c6fb6..a0695705ff 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -6002,10 +6002,13 @@ static void json_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) { struct json_object *r = json_r; - __u32 i; + __u64 i; __u16 j; struct nvme_host_ext_discovery_log *hedlpe; struct nvmf_ext_attr *exat; + void *p; + char hostnqn[sizeof(hedlpe->hostnqn) + 1]; + char traddr[sizeof(hedlpe->traddr) + 1]; __u32 thdlpl = le32_to_cpu(log->thdlpl); __u32 tel; __u16 numexat; @@ -6021,17 +6024,21 @@ static void json_host_discovery_log(struct nvme_host_discovery_log *log, obj_add_uint_02x(r, "hdlpf", log->hdlpf); obj_add_uint(r, "thdlpl", thdlpl); - for (i = sizeof(*log); i < le32_to_cpu(log->thdlpl); i += tel) { + for (i = sizeof(*log); + (hedlpe = nvme_host_discovery_record(log, len, i)); i += tel) { hedlpe_o = json_create_object(); - hedlpe = (void *)log + i; tel = le32_to_cpu(hedlpe->tel); numexat = le16_to_cpu(hedlpe->numexat); + snprintf(hostnqn, sizeof(hostnqn), "%-.*s", + (int)sizeof(hedlpe->hostnqn), hedlpe->hostnqn); + snprintf(traddr, sizeof(traddr), "%-.*s", + (int)sizeof(hedlpe->traddr), hedlpe->traddr); obj_add_str(hedlpe_o, "trtype", libnvmf_trtype_str(hedlpe->trtype)); - obj_add_str(hedlpe_o, "adrfam", - strlen(hedlpe->traddr) ? libnvmf_adrfam_str(hedlpe->adrfam) : ""); + obj_add_str(hedlpe_o, "adrfam", traddr[0] ? + libnvmf_adrfam_str(hedlpe->adrfam) : ""); obj_add_str(hedlpe_o, "eflags", libnvmf_eflags_str(le16_to_cpu(hedlpe->eflags))); - obj_add_str(hedlpe_o, "hostnqn", hedlpe->hostnqn); - obj_add_str(hedlpe_o, "traddr", hedlpe->traddr); + obj_add_str(hedlpe_o, "hostnqn", hostnqn); + obj_add_str(hedlpe_o, "traddr", traddr); tsas_o = json_create_object(); switch (hedlpe->trtype) { case NVMF_TRTYPE_RDMA: @@ -6052,8 +6059,9 @@ static void json_host_discovery_log(struct nvme_host_discovery_log *log, obj_add_uint(hedlpe_o, "tel", tel); obj_add_uint(hedlpe_o, "numexat", numexat); - exat = hedlpe->exat; - for (j = 0; j < numexat; j++) { + p = hedlpe->exat; + for (j = 0; j < numexat && + (exat = nvme_host_discovery_exat(hedlpe, p)); j++) { exat_o = json_create_object(); snprintf(json_str, sizeof(json_str), "exat: %d", j); obj_add_uint(exat_o, "exattype", le16_to_cpu(exat->exattype)); @@ -6061,7 +6069,7 @@ static void json_host_discovery_log(struct nvme_host_discovery_log *log, obj_d(exat_o, "exatval", (unsigned char *)exat->exatval, le16_to_cpu(exat->exatlen), 16, 1); obj_add_obj(hedlpe_o, json_str, exat_o); - exat = libnvmf_exat_ptr_next(exat); + p = libnvmf_exat_ptr_next(exat); } snprintf(json_str, sizeof(json_str), "hedlpe: %d", n++); obj_add_obj(r, json_str, hedlpe_o); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 12cc506044..9ee6a6fd8d 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -2716,10 +2716,11 @@ void stdout_discovery_log(const struct nvmf_discovery_log *log, int numrec) void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) { - __u32 i; + __u64 i; __u16 j; struct nvme_host_ext_discovery_log *hedlpe; struct nvmf_ext_attr *exat; + void *p; __u32 thdlpl = le32_to_cpu(log->thdlpl); __u32 tel; __u16 numexat; @@ -2738,9 +2739,9 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) stdout_kv_table_finish(t, "host-discovery-log"); - for (i = sizeof(*log); i < le32_to_cpu(log->thdlpl); i += tel) { + for (i = sizeof(*log); + (hedlpe = nvme_host_discovery_record(log, len, i)); i += tel) { printf("hedlpe: %d\n", n++); - hedlpe = (void *)log + i; tel = le32_to_cpu(hedlpe->tel); numexat = le16_to_cpu(hedlpe->numexat); @@ -2751,12 +2752,14 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) stdout_kv_add(t, "trtype", "%s", libnvmf_trtype_str(hedlpe->trtype)); stdout_kv_add(t, "adrfam", "%s", - strlen(hedlpe->traddr) ? + hedlpe->traddr[0] ? libnvmf_adrfam_str(hedlpe->adrfam) : ""); stdout_kv_add(t, "eflags", "%s", libnvmf_eflags_str(le16_to_cpu(hedlpe->eflags))); - stdout_kv_add(t, "hostnqn", "%s", hedlpe->hostnqn); - stdout_kv_add(t, "traddr", "%s", hedlpe->traddr); + stdout_kv_add(t, "hostnqn", "%-.*s", + (int)sizeof(hedlpe->hostnqn), hedlpe->hostnqn); + stdout_kv_add(t, "traddr", "%-.*s", + (int)sizeof(hedlpe->traddr), hedlpe->traddr); switch (hedlpe->trtype) { case NVMF_TRTYPE_RDMA: stdout_kv_add(t, "tsas.prtype", "%s", @@ -2789,8 +2792,9 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) d((unsigned char *)hedlpe->tsas.common, sizeof(hedlpe->tsas.common), 16, 1); - exat = hedlpe->exat; - for (j = 0; j < numexat; j++) { + p = hedlpe->exat; + for (j = 0; j < numexat && + (exat = nvme_host_discovery_exat(hedlpe, p)); j++) { printf("exat: %d\n", j); t = stdout_kv_table_create(); @@ -2807,7 +2811,7 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log, __u64 len) printf("exatval:\n"); d((unsigned char *)exat->exatval, le16_to_cpu(exat->exatlen), 16, 1); - exat = libnvmf_exat_ptr_next(exat); + p = libnvmf_exat_ptr_next(exat); } } } diff --git a/src/nvme-print.h b/src/nvme-print.h index 37f2a170a9..ebf25af7c8 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -103,6 +103,49 @@ static inline size_t nvme_log_nr_entries(size_t nr, size_t len, size_t hdr, return nr < max ? nr : max; } +/* + * The host discovery log record at offset @off of a page of @len bytes, or + * NULL if no whole record starts there. The page ends where its header + * says or where the buffer ends, whichever comes first. A record must hold + * at least its fixed part, and its length must not reach past the page. + */ +static inline struct nvme_host_ext_discovery_log * +nvme_host_discovery_record(struct nvme_host_discovery_log *log, __u64 len, + __u64 off) +{ + __u64 thdlpl = le32_to_cpu(log->thdlpl); + __u64 end = thdlpl < len ? thdlpl : len; + struct nvme_host_ext_discovery_log *rec; + __u32 tel; + + if (off > end || end - off < sizeof(*rec)) + return NULL; + + rec = (void *)log + off; + tel = le32_to_cpu(rec->tel); + if (tel < sizeof(*rec) || tel > end - off) + return NULL; + + return rec; +} + +/* + * The extended attribute at @p of the host discovery log record @rec, or + * NULL if it, with its value, doesn't fit the record. + */ +static inline struct nvmf_ext_attr * +nvme_host_discovery_exat(struct nvme_host_ext_discovery_log *rec, void *p) +{ + void *end = (void *)rec + le32_to_cpu(rec->tel); + struct nvmf_ext_attr *exat = p; + + if (!shr_buf_has_room(p, end, sizeof(*exat)) || + !shr_buf_has_room(exat->exatval, end, le16_to_cpu(exat->exatlen))) + return NULL; + + return exat; +} + /* Return: the LBA data size in bytes, or 0 if it is not representable. */ static inline unsigned int nvme_lbaf_data_size(__u8 ds) { diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index d12ce9b4e2..1cfe2619c4 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -10,8 +10,9 @@ Each of these log pages has a header which says how long the whole page is, and records or data behind it. The tests check that the commands read -the part behind the header and print it, and that the header asks for -the page the options select. +the part behind the header and print it, that the header asks for the +page the options select, and that a page whose lengths don't add up is +printed only as far as it goes. Usage: python3 nvme_discovery_logs_test.py """ @@ -43,8 +44,9 @@ _NVMF_ADDR_FAMILY_IP4 = 1 -def _host_discovery_log(hostnqns): - """A host discovery log page with one record for each of @hostnqns.""" +def _host_discovery_log(hostnqns, tel=_HEDLPE_LEN): + """A host discovery log page with one record for each of @hostnqns, + whose record lengths say @tel.""" records = b'' for nqn in hostnqns: rec = bytearray(_HEDLPE_LEN) @@ -53,7 +55,7 @@ def _host_discovery_log(hostnqns): rec[256:256 + len(nqn)] = nqn.encode() traddr = b'192.0.2.1' rec[512:512 + len(traddr)] = traddr - struct.pack_into(' Date: Fri, 16 Oct 2026 17:45:41 +0000 Subject: [PATCH 160/263] nvme-print: walk the AVE discovery log records within the page The AVE discovery log printers have the same problems as the host discovery ones: a record length of 0 loops forever, record and total lengths past the buffer are followed, the transport records are walked by their count past the record, and the AVE NQN need not end in a NUL. Add nvme_ave_discovery_record(), print no more transport records than the record holds, and the NQN no longer than its field. Found by fuzzing the device data. Fixes: 1571543dcd9b ("nvme: add ave-discovery-log command") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 14 ++++++++---- src/nvme-print-stdout-log.c | 12 ++++++---- src/nvme-print.h | 24 ++++++++++++++++++++ tests/cli/nvme_discovery_logs_test.py | 32 +++++++++++++++++++++++---- 4 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index a0695705ff..659ab10825 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -6101,9 +6101,10 @@ static void json_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len) { struct json_object *r = json_r; - __u32 i; + __u64 i; __u8 j; struct nvme_ave_discovery_log_entry *adlpe; + char avenqn[sizeof(adlpe->avenqn) + 1]; struct nvme_ave_tr_record *atr; __u32 tadlpl = le32_to_cpu(log->tadlpl); __u32 tel; @@ -6118,15 +6119,20 @@ static void json_ave_discovery_log(struct nvme_ave_discovery_log *log, obj_add_uint(r, "recfmt", le16_to_cpu(log->recfmt)); obj_add_uint(r, "thdlpl", tadlpl); - for (i = sizeof(*log); i < le32_to_cpu(log->tadlpl); i += tel) { + for (i = sizeof(*log); + (adlpe = nvme_ave_discovery_record(log, len, i)); i += tel) { adlpe_o = json_create_object(); - adlpe = (void *)log + i; tel = le32_to_cpu(adlpe->tel); numatr = adlpe->numatr; + snprintf(avenqn, sizeof(avenqn), "%-.*s", + (int)sizeof(adlpe->avenqn), adlpe->avenqn); obj_add_uint(adlpe_o, "tel", tel); - obj_add_str(adlpe_o, "avenqn", adlpe->avenqn); + obj_add_str(adlpe_o, "avenqn", avenqn); obj_add_uint(adlpe_o, "numatr", numatr); + /* the transport records must fit the record */ + numatr = nvme_log_nr_entries(numatr, tel, sizeof(*adlpe), + sizeof(*atr)); atr = adlpe->atr; for (j = 0; j < numatr; j++) { atr_o = json_create_object(); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 9ee6a6fd8d..ea4fab0183 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -2840,7 +2840,7 @@ static void stdout_kv_add_traddr(struct shr_table *t, const char *field, void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len) { - __u32 i; + __u64 i; __u8 j; struct nvme_ave_discovery_log_entry *adlpe; struct nvme_ave_tr_record *atr; @@ -2861,9 +2861,9 @@ void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len) stdout_kv_table_finish(t, "ave-discovery-log"); - for (i = sizeof(*log); i < le32_to_cpu(log->tadlpl); i += tel) { + for (i = sizeof(*log); + (adlpe = nvme_ave_discovery_record(log, len, i)); i += tel) { printf("adlpe: %d\n", n++); - adlpe = (void *)log + i; tel = le32_to_cpu(adlpe->tel); numatr = adlpe->numatr; @@ -2872,11 +2872,15 @@ void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log, __u64 len) return; stdout_kv_add(t, "tel", "%u", tel); - stdout_kv_add(t, "avenqn", "%s", adlpe->avenqn); + stdout_kv_add(t, "avenqn", "%-.*s", + (int)sizeof(adlpe->avenqn), adlpe->avenqn); stdout_kv_add(t, "numatr", "%u", numatr); stdout_kv_table_finish(t, "ave-discovery-log"); + /* the transport records must fit the record */ + numatr = nvme_log_nr_entries(numatr, tel, sizeof(*adlpe), + sizeof(*atr)); atr = adlpe->atr; for (j = 0; j < numatr; j++) { printf("atr: %d\n", j); diff --git a/src/nvme-print.h b/src/nvme-print.h index ebf25af7c8..fd5d20974a 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -146,6 +146,30 @@ nvme_host_discovery_exat(struct nvme_host_ext_discovery_log *rec, void *p) return exat; } +/* + * The AVE discovery log record at offset @off of a page of @len bytes, or + * NULL if no whole record starts there, as nvme_host_discovery_record(). + */ +static inline struct nvme_ave_discovery_log_entry * +nvme_ave_discovery_record(struct nvme_ave_discovery_log *log, __u64 len, + __u64 off) +{ + __u64 tadlpl = le32_to_cpu(log->tadlpl); + __u64 end = tadlpl < len ? tadlpl : len; + struct nvme_ave_discovery_log_entry *rec; + __u32 tel; + + if (off > end || end - off < sizeof(*rec)) + return NULL; + + rec = (void *)log + off; + tel = le32_to_cpu(rec->tel); + if (tel < sizeof(*rec) || tel > end - off) + return NULL; + + return rec; +} + /* Return: the LBA data size in bytes, or 0 if it is not representable. */ static inline unsigned int nvme_lbaf_data_size(__u8 ds) { diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 1cfe2619c4..1f2c96d07b 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -63,15 +63,16 @@ def _host_discovery_log(hostnqns, tel=_HEDLPE_LEN): return bytes(hdr) + records -def _ave_discovery_log(avenqns): +def _ave_discovery_log(avenqns, tel=None, numatr=1): """An AVE discovery log page with one record of one IPv4 transport - record for each of @avenqns.""" + record for each of @avenqns. @tel and @numatr default to the record + length and the number of transport records which match.""" records = b'' for nqn in avenqns: rec = bytearray(_ADLPE_LEN + _ATR_LEN) - struct.pack_into(' Date: Fri, 16 Oct 2026 17:49:32 +0000 Subject: [PATCH 161/263] nvme-print: bound the pull model DDC request OSP by the buffer The pull model DDC request log printers take the length of the operating system specific parameters from the total length in the header. A total length past the buffer, as get-log --verbose passes it with a short --log-len, has them dump memory past it. A total length shorter than the header wraps around, which only the conversion to int in the dumpers happens to turn into nothing. Add nvme_pull_model_ddc_req_osp_len(), which bounds the length by the buffer and the header. Found by fuzzing the device data. Fixes: d86ab5d847e3 ("nvme: add pull-model-ddc-req-log command") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 2 +- src/nvme-print-stdout-log.c | 3 +-- src/nvme-print.h | 18 ++++++++++++++++++ tests/cli/nvme_discovery_logs_test.py | 27 +++++++++++++++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 659ab10825..71f7ae24fe 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -6164,7 +6164,7 @@ static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, { struct json_object *r = json_r; __u32 tpdrpl = le32_to_cpu(log->tpdrpl); - __u32 osp_len = tpdrpl - offsetof(struct nvme_pull_model_ddc_req_log, osp); + __u32 osp_len = nvme_pull_model_ddc_req_osp_len(log, len); obj_add_uint(r, "ori", log->ori); obj_add_uint(r, "tpdrpl", tpdrpl); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index ea4fab0183..902eb0f36d 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -3064,8 +3064,7 @@ void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, __u64 len) { __u32 tpdrpl = le32_to_cpu(log->tpdrpl); - __u32 osp_len = - tpdrpl - offsetof(struct nvme_pull_model_ddc_req_log, osp); + __u32 osp_len = nvme_pull_model_ddc_req_osp_len(log, len); struct shr_table *t; t = stdout_kv_table_create(); diff --git a/src/nvme-print.h b/src/nvme-print.h index fd5d20974a..96ad87aae2 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -170,6 +170,24 @@ nvme_ave_discovery_record(struct nvme_ave_discovery_log *log, __u64 len, return rec; } +/* + * Return: the length of the operating system specific parameters of a pull + * model DDC request log of @len bytes: what the header says, but no more + * than the buffer holds. + */ +static inline __u32 +nvme_pull_model_ddc_req_osp_len(struct nvme_pull_model_ddc_req_log *log, + __u64 len) +{ + __u64 tpdrpl = le32_to_cpu(log->tpdrpl); + __u64 end = tpdrpl < len ? tpdrpl : len; + + if (end <= offsetof(struct nvme_pull_model_ddc_req_log, osp)) + return 0; + + return end - offsetof(struct nvme_pull_model_ddc_req_log, osp); +} + /* Return: the LBA data size in bytes, or 0 if it is not representable. */ static inline unsigned int nvme_lbaf_data_size(__u8 ds) { diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 1f2c96d07b..a534bddef8 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -83,8 +83,10 @@ def _ave_discovery_log(avenqns, tel=None, numatr=1): return bytes(hdr) + records -def _pull_model_ddc_req_log(osp): - return struct.pack(' Date: Fri, 16 Oct 2026 17:50:22 +0000 Subject: [PATCH 162/263] nvme-print: bound the power histogram descriptors by the log size The power measurement log printers print as many histogram descriptors as NPHD says, but the page is only as long as SZE, and get-log passes a page as long as the user asked for. Both printers got the size and ignored it. Print no more descriptors than the page holds. Found by fuzzing the device data. Fixes: e06fe27ae1f2 ("nvme: Add power-measurement-log command (LID 0x25)") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 5 ++++- src/nvme-print-stdout-log.c | 3 +++ tests/cli/nvme_discovery_logs_test.py | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 71f7ae24fe..439df69298 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -6171,7 +6171,7 @@ static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, obj_d(r, "osp", (unsigned char *)log->osp, osp_len, 16, 1); } -static void json_power_meas_log(struct nvme_power_meas_log *log, __u32 size UNUSED) +static void json_power_meas_log(struct nvme_power_meas_log *log, __u32 size) { struct json_object *r = json_r; struct json_object *descs; @@ -6215,6 +6215,9 @@ static void json_power_meas_log(struct nvme_power_meas_log *log, __u32 size UNUS obj_add_uint(r, "ipwrpe", log->ipwrpe); + /* the descriptors the buffer holds */ + nphd = nvme_log_nr_entries(nphd, size, sizeof(*log), + sizeof(log->descs[0])); descs = json_create_array(); for (i = 0; i < nphd; i++) { struct json_object *desc = json_create_object(); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 902eb0f36d..c8321ef009 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -3188,6 +3188,9 @@ void stdout_power_meas_log(struct nvme_power_meas_log *log, __u32 size) stdout_kv_table_finish(t, "power-meas-log"); + /* the descriptors the buffer holds */ + nphd = nvme_log_nr_entries(nphd, size, sizeof(*log), + sizeof(log->descs[0])); if (verbose) { for (i = 0; i < nphd; i++) { __u32 phblt = le32_to_cpu(log->descs[i].phblt); diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index a534bddef8..8a71ec4be9 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -33,12 +33,14 @@ _LID_HOST_DISCOVERY = 0x71 _LID_AVE_DISCOVERY = 0x72 _LID_PULL_MODEL_DDC_REQ = 0x73 +_LID_POWER_MEASUREMENT = 0x25 _HEADER_LEN = 1024 # host and AVE discovery log headers _HEDLPE_LEN = 1032 # struct nvme_host_ext_discovery_log _ADLPE_LEN = 232 # struct nvme_ave_discovery_log_entry _ATR_LEN = 20 # struct nvme_ave_tr_record _PULL_HEADER_LEN = 8 # struct nvme_pull_model_ddc_req_log +_POWER_MEAS_HEADER_LEN = 64 # struct nvme_power_meas_log _NVMF_TRTYPE_TCP = 3 _NVMF_ADDR_FAMILY_IP4 = 1 @@ -89,6 +91,16 @@ def _pull_model_ddc_req_log(osp, tpdrpl=None): return struct.pack(' Date: Fri, 16 Oct 2026 17:50:56 +0000 Subject: [PATCH 163/263] log: keep the log buffer when growing it fails get_log_offset() and get_power_measurement_log() assign the result of libnvme_realloc() to the only pointer to the buffer. When the larger allocation fails, which a device can arrange with a large log length, the buffer is leaked. Assign the new buffer only once there is one. Fixes: 4053efff0ea6 ("nvme: add reachability-groups-log command") Fixes: e06fe27ae1f2 ("nvme: Add power-measurement-log command (LID 0x25)") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 724d4b22e8..eab3c9cc79 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2241,9 +2241,11 @@ static int get_power_measurement_log(int argc, char **argv, struct command *acmd /* If the log is larger, re-read with full size */ if (log_size > min_log_size) { - log = libnvme_realloc(log, log_size); - if (!log) + void *p = libnvme_realloc(log, log_size); + + if (!p) return -ENOMEM; + log = p; nvme_init_get_log_power_measurement(&cmd, log, log_size); err = libnvme_get_log(hdl, &cmd, false, NVME_LOG_PAGE_PDU_SIZE); @@ -2263,15 +2265,17 @@ static int get_log_offset(struct libnvme_transport_handle *hdl, __u32 len, void **log) { struct libnvme_passthru_cmd cmd; + void *p; int err; args->lpo = *offset; args->len = len; *offset += args->len; - *log = libnvme_realloc(*log, *offset); - if (!*log) + p = libnvme_realloc(*log, *offset); + if (!p) return -ENOMEM; + *log = p; args->log = (char *)*log + args->lpo; From 122841b9d6c5d6fd48b5a7314bae3e4f3208d4bf Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:53:33 +0000 Subject: [PATCH 164/263] log: walk the reachability descriptors at their offsets A reachability group descriptor is followed by its list of namespace IDs, a reachability association descriptor by its list of group IDs, so the descriptors differ in length. The readers and the printers index them as an array of the fixed part though: from the second descriptor on, they read the counts and IDs from the wrong place. And the printers take the counts from the device without looking at the length of the log. - the readers take each descriptor from the offset it was read at, reject an ID count no log can hold and skip the zero length reads of descriptors without IDs, which asked the device for the largest transfer - add nvme_reachability_group() and nvme_reachability_association(), which return a descriptor only if it fits the log with its IDs, and walk the descriptors with them - as the descriptors are only 4 byte aligned, the readers and printers copy their fixed part, which has a 64 bit field, before using it - the groups header read honours --rae, as the associations one does Found by fuzzing the device data. Fixes: 4053efff0ea6 ("nvme: add reachability-groups-log command") Fixes: 86495e85ef73 ("nvme: add reachability-associations-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 48 ++++++++++++++++----- src/nvme-print-json.c | 45 +++++++++++++------- src/nvme-print-stdout-log.c | 42 ++++++++++-------- src/nvme-print.h | 61 +++++++++++++++++++++++++++ tests/cli/nvme_discovery_logs_test.py | 61 +++++++++++++++++++++++++++ 5 files changed, 214 insertions(+), 43 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index eab3c9cc79..2c052e1719 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2307,16 +2307,29 @@ static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, str { int err; struct nvme_reachability_groups_log *log = *logp; + struct nvme_reachability_group_desc rgd; + __u64 desc; + __u32 nnid; __u16 i; - __u32 len; + /* each descriptor is followed by its list of namespace IDs */ for (i = 0; i < le16_to_cpu(log->nrgd); i++) { - len = sizeof(*log->rgd); - err = get_log_offset(hdl, args, offset, len, (void **)&log); + desc = *offset; + err = get_log_offset(hdl, args, offset, sizeof(rgd), + (void **)&log); if (err) goto err_free; - len = le32_to_cpu(log->rgd[i].nnid) * sizeof(*log->rgd[i].nsid); - err = get_log_offset(hdl, args, offset, len, (void **)&log); + /* the descriptors are only 4 byte aligned */ + memcpy(&rgd, (void *)log + desc, sizeof(rgd)); + nnid = le32_to_cpu(rgd.nnid); + if (!nnid) + continue; + if (nnid > UINT32_MAX / sizeof(*rgd.nsid)) { + err = -EINVAL; + goto err_free; + } + err = get_log_offset(hdl, args, offset, + nnid * sizeof(*rgd.nsid), (void **)&log); if (err) goto err_free; } @@ -2350,7 +2363,7 @@ static int get_reachability_groups(struct libnvme_transport_handle *hdl, bool rg return -ENOMEM; nvme_init_get_log_reachability_groups(&cmd, rgo, log, log_len); - err = libnvme_get_log(hdl, &cmd, false, log_len); + err = libnvme_get_log(hdl, &cmd, rae, log_len); if (err) goto err_free; @@ -2418,16 +2431,29 @@ static int get_reachability_association_desc(struct libnvme_transport_handle *hd { int err; struct nvme_reachability_associations_log *log = *logp; + struct nvme_reachability_association_desc rad; + __u64 desc; + __u32 nrid; __u16 i; - __u32 len; + /* each descriptor is followed by its list of reachability group IDs */ for (i = 0; i < le16_to_cpu(log->nrad); i++) { - len = sizeof(*log->rad); - err = get_log_offset(hdl, args, offset, len, (void **)&log); + desc = *offset; + err = get_log_offset(hdl, args, offset, sizeof(rad), + (void **)&log); if (err) goto err_free; - len = le32_to_cpu(log->rad[i].nrid) * sizeof(*log->rad[i].rgid); - err = get_log_offset(hdl, args, offset, len, (void **)&log); + /* the descriptors are only 4 byte aligned */ + memcpy(&rad, (void *)log + desc, sizeof(rad)); + nrid = le32_to_cpu(rad.nrid); + if (!nrid) + continue; + if (nrid > UINT32_MAX / sizeof(*rad.rgid)) { + err = -EINVAL; + goto err_free; + } + err = get_log_offset(hdl, args, offset, + nrid * sizeof(*rad.rgid), (void **)&log); if (err) goto err_free; } diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 439df69298..5da3c7c093 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5951,9 +5951,13 @@ static void json_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_ns } } -static void json_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len UNUSED) +static void json_reachability_groups_log( + struct nvme_reachability_groups_log *log, __u64 len) { struct json_object *r = json_r; + struct nvme_reachability_group_desc desc; + __u64 off = sizeof(*log); + __le32 *nsid; __u16 i; __u32 j; char json_str[STR_LEN]; @@ -5962,21 +5966,28 @@ static void json_reachability_groups_log(struct nvme_reachability_groups_log *lo obj_add_uint64(r, "chngc", le64_to_cpu(log->chngc)); obj_add_uint(r, "nrgd", le16_to_cpu(log->nrgd)); - for (i = 0; i < le16_to_cpu(log->nrgd); i++) { - snprintf(json_str, sizeof(json_str), "rgid: %u", le32_to_cpu(log->rgd[i].rgid)); + for (i = 0; i < le16_to_cpu(log->nrgd) && + nvme_reachability_group(log, len, off, &desc); i++) { + snprintf(json_str, sizeof(json_str), "rgid: %u", + le32_to_cpu(desc.rgid)); rgd = json_create_object(); - obj_add_uint(rgd, "nnid", le32_to_cpu(log->rgd[i].nnid)); - obj_add_uint64(rgd, "chngc", le64_to_cpu(log->rgd[i].chngc)); - for (j = 0; j < le32_to_cpu(log->rgd[i].nnid); j++) - obj_add_uint(rgd, "nnid", le32_to_cpu(log->rgd[i].nsid[j])); + obj_add_uint(rgd, "nnid", le32_to_cpu(desc.nnid)); + obj_add_uint64(rgd, "chngc", le64_to_cpu(desc.chngc)); + nsid = nvme_reachability_ids(log, off, sizeof(desc)); + for (j = 0; j < le32_to_cpu(desc.nnid); j++) + obj_add_uint(rgd, "nnid", le32_to_cpu(nsid[j])); obj_add_obj(r, json_str, rgd); + off = nvme_reachability_group_next(&desc, off); } } static void json_reachability_associations_log(struct nvme_reachability_associations_log *log, - __u64 len UNUSED) + __u64 len) { struct json_object *r = json_r; + struct nvme_reachability_association_desc desc; + __u64 off = sizeof(*log); + __le32 *rgid; __u16 i; __u32 j; char json_str[STR_LEN]; @@ -5985,15 +5996,19 @@ static void json_reachability_associations_log(struct nvme_reachability_associat obj_add_uint64(r, "chngc", le64_to_cpu(log->chngc)); obj_add_uint(r, "nrad", le16_to_cpu(log->nrad)); - for (i = 0; i < le16_to_cpu(log->nrad); i++) { - snprintf(json_str, sizeof(json_str), "rasid: %u", le32_to_cpu(log->rad[i].rasid)); + for (i = 0; i < le16_to_cpu(log->nrad) && + nvme_reachability_association(log, len, off, &desc); i++) { + snprintf(json_str, sizeof(json_str), "rasid: %u", + le32_to_cpu(desc.rasid)); rad = json_create_object(); - obj_add_uint(rad, "nrid", le32_to_cpu(log->rad[i].nrid)); - obj_add_uint64(rad, "chngc", le64_to_cpu(log->rad[i].chngc)); - obj_add_uint(rad, "rac", log->rad[i].rac); - for (j = 0; j < le32_to_cpu(log->rad[i].nrid); j++) - obj_add_uint(rad, "rgid", le32_to_cpu(log->rad[i].rgid[j])); + obj_add_uint(rad, "nrid", le32_to_cpu(desc.nrid)); + obj_add_uint64(rad, "chngc", le64_to_cpu(desc.chngc)); + obj_add_uint(rad, "rac", desc.rac); + rgid = nvme_reachability_ids(log, off, sizeof(desc)); + for (j = 0; j < le32_to_cpu(desc.nrid); j++) + obj_add_uint(rad, "rgid", le32_to_cpu(rgid[j])); obj_add_obj(r, json_str, rad); + off = nvme_reachability_association_next(&desc, off); } } diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index c8321ef009..3ddc14cb35 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -2996,7 +2996,10 @@ void stdout_dispersed_ns_psub_log( void stdout_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len) { + struct nvme_reachability_group_desc rgd; + __u64 off = sizeof(*log); struct shr_table *t; + __le32 *nsid; __u16 i; __u32 j; @@ -3009,18 +3012,19 @@ void stdout_reachability_groups_log(struct nvme_reachability_groups_log *log, stdout_kv_add(t, "chngc", "%"PRIu64, le64_to_cpu(log->chngc)); stdout_kv_add(t, "nrgd", "%u", le16_to_cpu(log->nrgd)); - for (i = 0; i < le16_to_cpu(log->nrgd); i++) { - stdout_kv_add(t, "rgid", "%u", le32_to_cpu(log->rgd[i].rgid)); - stdout_kv_add(t, "nnid", "%u", le32_to_cpu(log->rgd[i].nnid)); - stdout_kv_add(t, "chngc", "%"PRIu64, - le64_to_cpu(log->rgd[i].chngc)); - for (j = 0; j < le32_to_cpu(log->rgd[i].nnid); j++) { + for (i = 0; i < le16_to_cpu(log->nrgd) && + nvme_reachability_group(log, len, off, &rgd); i++) { + stdout_kv_add(t, "rgid", "%u", le32_to_cpu(rgd.rgid)); + stdout_kv_add(t, "nnid", "%u", le32_to_cpu(rgd.nnid)); + stdout_kv_add(t, "chngc", "%"PRIu64, le64_to_cpu(rgd.chngc)); + nsid = nvme_reachability_ids(log, off, sizeof(rgd)); + for (j = 0; j < le32_to_cpu(rgd.nnid); j++) { char name[16]; snprintf(name, sizeof(name), "nsid%u", j); - stdout_kv_add(t, name, "%u", - le32_to_cpu(log->rgd[i].nsid[j])); + stdout_kv_add(t, name, "%u", le32_to_cpu(nsid[j])); } + off = nvme_reachability_group_next(&rgd, off); } stdout_kv_table_finish(t, "reachability-groups"); @@ -3029,7 +3033,10 @@ void stdout_reachability_groups_log(struct nvme_reachability_groups_log *log, void stdout_reachability_associations_log( struct nvme_reachability_associations_log *log, __u64 len) { + struct nvme_reachability_association_desc rad; + __u64 off = sizeof(*log); struct shr_table *t; + __le32 *rgid; __u16 i; __u32 j; @@ -3042,19 +3049,20 @@ void stdout_reachability_associations_log( stdout_kv_add(t, "chngc", "%"PRIu64, le64_to_cpu(log->chngc)); stdout_kv_add(t, "nrad", "%u", le16_to_cpu(log->nrad)); - for (i = 0; i < le16_to_cpu(log->nrad); i++) { - stdout_kv_add(t, "rasid", "%u", le32_to_cpu(log->rad[i].rasid)); - stdout_kv_add(t, "nrid", "%u", le32_to_cpu(log->rad[i].nrid)); - stdout_kv_add(t, "chngc", "%"PRIu64, - le64_to_cpu(log->rad[i].chngc)); - stdout_kv_add(t, "rac", "%u", log->rad[i].rac); - for (j = 0; j < le32_to_cpu(log->rad[i].nrid); j++) { + for (i = 0; i < le16_to_cpu(log->nrad) && + nvme_reachability_association(log, len, off, &rad); i++) { + stdout_kv_add(t, "rasid", "%u", le32_to_cpu(rad.rasid)); + stdout_kv_add(t, "nrid", "%u", le32_to_cpu(rad.nrid)); + stdout_kv_add(t, "chngc", "%"PRIu64, le64_to_cpu(rad.chngc)); + stdout_kv_add(t, "rac", "%u", rad.rac); + rgid = nvme_reachability_ids(log, off, sizeof(rad)); + for (j = 0; j < le32_to_cpu(rad.nrid); j++) { char name[16]; snprintf(name, sizeof(name), "rgid%u", j); - stdout_kv_add(t, name, "%u", - le32_to_cpu(log->rad[i].rgid[j])); + stdout_kv_add(t, name, "%u", le32_to_cpu(rgid[j])); } + off = nvme_reachability_association_next(&rad, off); } stdout_kv_table_finish(t, "reachability-associations"); diff --git a/src/nvme-print.h b/src/nvme-print.h index 96ad87aae2..9c01f8b7d9 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -188,6 +188,67 @@ nvme_pull_model_ddc_req_osp_len(struct nvme_pull_model_ddc_req_log *log, return end - offsetof(struct nvme_pull_model_ddc_req_log, osp); } +/* + * Copy the fixed part of the reachability group descriptor at offset @off + * of a log of @len bytes to @rgd. The descriptors differ in length, so + * they are only 4 byte aligned. Return: false if the descriptor doesn't + * fit with its namespace ID list, which nvme_reachability_ids() returns. + * The next descriptor is at nvme_reachability_group_next(). + */ +static inline bool +nvme_reachability_group(struct nvme_reachability_groups_log *log, __u64 len, + __u64 off, struct nvme_reachability_group_desc *rgd) +{ + if (off > len || len - off < sizeof(*rgd)) + return false; + + memcpy(rgd, (void *)log + off, sizeof(*rgd)); + return le32_to_cpu(rgd->nnid) <= + (len - off - sizeof(*rgd)) / sizeof(rgd->nsid[0]); +} + +static inline __u64 +nvme_reachability_group_next(struct nvme_reachability_group_desc *rgd, + __u64 off) +{ + return off + sizeof(*rgd) + + (__u64)le32_to_cpu(rgd->nnid) * sizeof(rgd->nsid[0]); +} + +/* + * The reachability association descriptor at offset @off of a log of @len + * bytes, as nvme_reachability_group(). + */ +static inline bool +nvme_reachability_association(struct nvme_reachability_associations_log *log, + __u64 len, __u64 off, + struct nvme_reachability_association_desc *rad) +{ + if (off > len || len - off < sizeof(*rad)) + return false; + + memcpy(rad, (void *)log + off, sizeof(*rad)); + return le32_to_cpu(rad->nrid) <= + (len - off - sizeof(*rad)) / sizeof(rad->rgid[0]); +} + +static inline __u64 +nvme_reachability_association_next( + struct nvme_reachability_association_desc *rad, __u64 off) +{ + return off + sizeof(*rad) + + (__u64)le32_to_cpu(rad->nrid) * sizeof(rad->rgid[0]); +} + +/* + * The IDs which follow the reachability descriptor at offset @off, whose + * fixed part is @size bytes long. They are 4 byte aligned, as they need. + */ +static inline __le32 *nvme_reachability_ids(void *log, __u64 off, size_t size) +{ + return log + off + size; +} + /* Return: the LBA data size in bytes, or 0 if it is not representable. */ static inline unsigned int nvme_lbaf_data_size(__u8 ds) { diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 8a71ec4be9..7a4a36d23e 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -34,6 +34,8 @@ _LID_AVE_DISCOVERY = 0x72 _LID_PULL_MODEL_DDC_REQ = 0x73 _LID_POWER_MEASUREMENT = 0x25 +_LID_REACHABILITY_GROUPS = 0x1a +_LID_REACHABILITY_ASSOCIATIONS = 0x1b _HEADER_LEN = 1024 # host and AVE discovery log headers _HEDLPE_LEN = 1032 # struct nvme_host_ext_discovery_log @@ -101,6 +103,28 @@ def _power_meas_log(nphd, ndescs): return bytes(hdr) + descs +def _reachability_log(descs, fixed_len=32): + """A reachability groups (or associations) log page with a descriptor + for each (id, [ids]) in @descs: the ID, the number of IDs, and the + IDs after the @fixed_len byte fixed part.""" + out = struct.pack(' Date: Fri, 16 Oct 2026 17:54:09 +0000 Subject: [PATCH 165/263] nvme-print: list all IDs of a reachability descriptor in JSON The JSON printers add each namespace ID of a reachability group under the key "nnid", and each group ID of a reachability association under "rgid". As a key can be there only once, all but the last ID are lost, and the namespace IDs replace the namespace ID count. Print the IDs as arrays, "nsid" for the namespace IDs of a group and "rgid" for the group IDs of an association. Fixes: 4053efff0ea6 ("nvme: add reachability-groups-log command") Fixes: 86495e85ef73 ("nvme: add reachability-associations-log command") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 14 ++++++++++---- tests/cli/nvme_discovery_logs_test.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 5da3c7c093..7ea6205414 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5961,7 +5961,7 @@ static void json_reachability_groups_log( __u16 i; __u32 j; char json_str[STR_LEN]; - struct json_object *rgd; + struct json_object *rgd, *nsids; obj_add_uint64(r, "chngc", le64_to_cpu(log->chngc)); obj_add_uint(r, "nrgd", le16_to_cpu(log->nrgd)); @@ -5973,9 +5973,12 @@ static void json_reachability_groups_log( rgd = json_create_object(); obj_add_uint(rgd, "nnid", le32_to_cpu(desc.nnid)); obj_add_uint64(rgd, "chngc", le64_to_cpu(desc.chngc)); + nsids = json_create_array(); nsid = nvme_reachability_ids(log, off, sizeof(desc)); for (j = 0; j < le32_to_cpu(desc.nnid); j++) - obj_add_uint(rgd, "nnid", le32_to_cpu(nsid[j])); + json_object_array_add(nsids, + json_object_new_uint64(le32_to_cpu(nsid[j]))); + obj_add_array(rgd, "nsid", nsids); obj_add_obj(r, json_str, rgd); off = nvme_reachability_group_next(&desc, off); } @@ -5991,7 +5994,7 @@ static void json_reachability_associations_log(struct nvme_reachability_associat __u16 i; __u32 j; char json_str[STR_LEN]; - struct json_object *rad; + struct json_object *rad, *rgids; obj_add_uint64(r, "chngc", le64_to_cpu(log->chngc)); obj_add_uint(r, "nrad", le16_to_cpu(log->nrad)); @@ -6004,9 +6007,12 @@ static void json_reachability_associations_log(struct nvme_reachability_associat obj_add_uint(rad, "nrid", le32_to_cpu(desc.nrid)); obj_add_uint64(rad, "chngc", le64_to_cpu(desc.chngc)); obj_add_uint(rad, "rac", desc.rac); + rgids = json_create_array(); rgid = nvme_reachability_ids(log, off, sizeof(desc)); for (j = 0; j < le32_to_cpu(desc.nrid); j++) - obj_add_uint(rad, "rgid", le32_to_cpu(rgid[j])); + json_object_array_add(rgids, + json_object_new_uint64(le32_to_cpu(rgid[j]))); + obj_add_array(rad, "rgid", rgids); obj_add_obj(r, json_str, rad); off = nvme_reachability_association_next(&desc, off); } diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 7a4a36d23e..80d85c604a 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -328,6 +328,18 @@ def test_reachability_associations_descriptors_at_their_offsets(self): self.assertEqual([v for k, v in kv if k.startswith('rgid')], ['10', '11', '20']) + def test_reachability_json_lists_all_ids(self): + self.server.logs[_LID_REACHABILITY_GROUPS] = \ + _reachability_log([(1, [10, 11]), (2, [20])]) + out = self._run_json('reachability-groups') + self.assertEqual(out['rgid: 1']['nsid'], [10, 11]) + self.assertEqual(out['rgid: 2']['nsid'], [20]) + self.assertEqual(out['rgid: 2']['nnid'], 1) + self.server.logs[_LID_REACHABILITY_ASSOCIATIONS] = \ + _reachability_log([(1, [10, 11])]) + out = self._run_json('reachability-associations') + self.assertEqual(out['rasid: 1']['rgid'], [10, 11]) + if __name__ == '__main__': unittest.main(argv=[sys.argv[0]]) From 76d9df35a5979e5a4a03879d555fc2a6750847f1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:56:09 +0000 Subject: [PATCH 166/263] log: read the dispersed namespace participating NVM subsystems safely get_dispersed_ns_psub() multiplies the subsystem count from the device by the NQN length without a check. A large count wraps to a short buffer, which the printers then walk by the count. A count of zero sends a zero length Get Log Page, which asks the device for the largest transfer. And when growing the buffer fails, it is leaked. Reject a count whose list the command can't read, skip the read when there is nothing to read, and keep the buffer when it can't grow. Found by fuzzing the device data. Fixes: 6dba5272189b ("nvme: add dispersed-ns-participating-nss-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 19 +++++++++++---- tests/cli/nvme_discovery_logs_test.py | 34 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 2c052e1719..3587a7bebf 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2102,9 +2102,10 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi { int err; __u64 header_len = sizeof(**logp); - __u64 psub_list_len; + __u64 numpsub, psub_list_len; struct nvme_dispersed_ns_participating_nss_log *log = libnvme_alloc(header_len); struct libnvme_passthru_cmd cmd; + void *p; if (!log) return -ENOMEM; @@ -2114,13 +2115,22 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi if (err) goto err_free; - psub_list_len = le64_to_cpu(log->numpsub) * NVME_NQN_LENGTH; + numpsub = le64_to_cpu(log->numpsub); + if (!numpsub) + goto out; + /* the command takes a 32 bit length */ + if (numpsub > UINT32_MAX / NVME_NQN_LENGTH) { + err = -EINVAL; + goto err_free; + } + psub_list_len = numpsub * NVME_NQN_LENGTH; - log = libnvme_realloc(log, header_len + psub_list_len); - if (!log) { + p = libnvme_realloc(log, header_len + psub_list_len); + if (!p) { err = -ENOMEM; goto err_free; } + log = p; nvme_init_get_log_dispersed_ns_participating_nss(&cmd, nsid, (void *)log->participating_nss, psub_list_len); @@ -2130,6 +2140,7 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi if (err) goto err_free; +out: *logp = log; return 0; diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 80d85c604a..35d5486da8 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -36,6 +36,7 @@ _LID_POWER_MEASUREMENT = 0x25 _LID_REACHABILITY_GROUPS = 0x1a _LID_REACHABILITY_ASSOCIATIONS = 0x1b +_LID_DISPERSED_NS_PSUB = 0x17 _HEADER_LEN = 1024 # host and AVE discovery log headers _HEDLPE_LEN = 1032 # struct nvme_host_ext_discovery_log @@ -115,6 +116,16 @@ def _reachability_log(descs, fixed_len=32): return out +def _dispersed_ns_psub_log(nqns, numpsub=None): + """A dispersed namespace participating NVM subsystems log page with + @nqns, which says it has @numpsub of them (default: as many).""" + if numpsub is None: + numpsub = len(nqns) + hdr = bytearray(256) + struct.pack_into(' Date: Fri, 16 Oct 2026 17:57:23 +0000 Subject: [PATCH 167/263] nvme-print: bound the dispersed namespace participating NVM subsystems The printers of the dispersed namespace participating NVM subsystems log print as many NQNs as the header counts, and the binary printer dumps that many. get-log passes a page of the length the user asked for, which can hold fewer. Pass the length of the page to the printers and print no more NQNs than it holds. Found by fuzzing the device data. Fixes: 6dba5272189b ("nvme: add dispersed-ns-participating-nss-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 11 ++++++++--- src/nvme-print-binary.c | 5 +++-- src/nvme-print-json.c | 8 +++++--- src/nvme-print-stdout-log.c | 7 ++++--- src/nvme-print-stdout.h | 2 +- src/nvme-print.c | 6 +++--- src/nvme-print.h | 5 +++-- tests/cli/nvme_discovery_logs_test.py | 13 +++++++++++++ 8 files changed, 40 insertions(+), 17 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 3587a7bebf..ea355254f3 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2098,7 +2098,9 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * } static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsid, - struct nvme_dispersed_ns_participating_nss_log **logp) + struct nvme_dispersed_ns_participating_nss_log + **logp, + __u64 *lenp) { int err; __u64 header_len = sizeof(**logp); @@ -2115,6 +2117,7 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi if (err) goto err_free; + *lenp = header_len; numpsub = le64_to_cpu(log->numpsub); if (!numpsub) goto out; @@ -2131,6 +2134,7 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi goto err_free; } log = p; + *lenp = header_len + psub_list_len; nvme_init_get_log_dispersed_ns_participating_nss(&cmd, nsid, (void *)log->participating_nss, psub_list_len); @@ -2159,6 +2163,7 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_libnvme_free struct nvme_dispersed_ns_participating_nss_log *log = NULL; + __u64 log_len; struct config { __u32 namespace_id; @@ -2180,13 +2185,13 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct flags = nvme_args.output_flags; - err = get_dispersed_ns_psub(hdl, cfg.namespace_id, &log); + err = get_dispersed_ns_psub(hdl, cfg.namespace_id, &log, &log_len); if (err) { nvme_show_err(err, "dispersed ns participating nss log"); return err; } - nvme_show_dispersed_ns_psub_log(log, flags); + nvme_show_dispersed_ns_psub_log(log, log_len, flags); return err; } diff --git a/src/nvme-print-binary.c b/src/nvme-print-binary.c index d13ba6405d..18298ef176 100644 --- a/src/nvme-print-binary.c +++ b/src/nvme-print-binary.c @@ -315,9 +315,10 @@ static void binary_rotational_media_info_log(struct nvme_rotational_media_info_l d_raw((unsigned char *)info, sizeof(*info)); } -static void binary_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_nss_log *log) +static void binary_dispersed_ns_psub_log( + struct nvme_dispersed_ns_participating_nss_log *log, __u64 len) { - d_raw((unsigned char *)log, sizeof(*log) + le64_to_cpu(log->numpsub) * NVME_NQN_LENGTH); + d_raw((unsigned char *)log, len); } static void binary_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 7ea6205414..8e8cd902ae 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5933,17 +5933,19 @@ static void json_rotational_media_info_log(struct nvme_rotational_media_info_log obj_add_uint(r, "fldc", le32_to_cpu(info->fldc)); } -static void json_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_nss_log *log) +static void json_dispersed_ns_psub_log( + struct nvme_dispersed_ns_participating_nss_log *log, __u64 len) { struct json_object *r = json_r; __u64 numpsub = le64_to_cpu(log->numpsub); - __u64 i; + __u64 i, n; char json_str[STR_LEN]; char psub[NVME_NQN_LENGTH + 1]; obj_add_uint64(r, "genctr", le64_to_cpu(log->genctr)); obj_add_uint64(r, "numpsub", numpsub); - for (i = 0; i < numpsub; i++) { + n = nvme_log_nr_entries(numpsub, len, sizeof(*log), NVME_NQN_LENGTH); + for (i = 0; i < n; i++) { snprintf(json_str, sizeof(json_str), "participating_nss %"PRIu64"", (uint64_t)i); snprintf(psub, sizeof(psub), "%-.*s", NVME_NQN_LENGTH, &log->participating_nss[i * NVME_NQN_LENGTH]); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 3ddc14cb35..ab7aa1b8e1 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -2968,11 +2968,11 @@ void stdout_rotational_media_info_log( } void stdout_dispersed_ns_psub_log( - struct nvme_dispersed_ns_participating_nss_log *log) + struct nvme_dispersed_ns_participating_nss_log *log, __u64 len) { __u64 numpsub = le64_to_cpu(log->numpsub); struct shr_table *t; - __u64 i; + __u64 i, n; t = stdout_kv_table_create(); if (!t) @@ -2981,7 +2981,8 @@ void stdout_dispersed_ns_psub_log( stdout_kv_add(t, "genctr", "%"PRIu64, le64_to_cpu(log->genctr)); stdout_kv_add(t, "numpsub", "%"PRIu64, (uint64_t)numpsub); - for (i = 0; i < numpsub; i++) { + n = nvme_log_nr_entries(numpsub, len, sizeof(*log), NVME_NQN_LENGTH); + for (i = 0; i < n; i++) { char name[40]; snprintf(name, sizeof(name), "participating_nss %"PRIu64, diff --git a/src/nvme-print-stdout.h b/src/nvme-print-stdout.h index 6e854bbe6f..05a42d02bf 100644 --- a/src/nvme-print-stdout.h +++ b/src/nvme-print-stdout.h @@ -92,7 +92,7 @@ void stdout_mgmt_addr_list_log(struct nvme_mgmt_addr_list_log *ma_list); void stdout_rotational_media_info_log( struct nvme_rotational_media_info_log *info); void stdout_dispersed_ns_psub_log( - struct nvme_dispersed_ns_participating_nss_log *log); + struct nvme_dispersed_ns_participating_nss_log *log, __u64 len); void stdout_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len); void stdout_reachability_associations_log( diff --git a/src/nvme-print.c b/src/nvme-print.c index 27decf0b6b..2e43dbe606 100644 --- a/src/nvme-print.c +++ b/src/nvme-print.c @@ -1969,9 +1969,9 @@ void nvme_show_rotational_media_info_log(struct nvme_rotational_media_info_log * } void nvme_show_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_nss_log *log, - nvme_print_flags_t flags) + __u64 len, nvme_print_flags_t flags) { - nvme_print(dispersed_ns_psub_log, flags, log); + nvme_print(dispersed_ns_psub_log, flags, log, len); } void nvme_show_reachability_groups_log(struct nvme_reachability_groups_log *log, @@ -2081,7 +2081,7 @@ void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsi nvme_show_rotational_media_info_log(log, flags); break; case NVME_LOG_LID_DISPERSED_NS_PARTICIPATING_NSS: - nvme_show_dispersed_ns_psub_log(log, flags); + nvme_show_dispersed_ns_psub_log(log, len, flags); break; case NVME_LOG_LID_MGMT_ADDR_LIST: nvme_show_mgmt_addr_list_log(log, flags); diff --git a/src/nvme-print.h b/src/nvme-print.h index 9c01f8b7d9..a97d0196d9 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -385,7 +385,8 @@ struct print_ops { void (*show_finish)(void); void (*mgmt_addr_list_log)(struct nvme_mgmt_addr_list_log *ma_log); void (*rotational_media_info_log)(struct nvme_rotational_media_info_log *info); - void (*dispersed_ns_psub_log)(struct nvme_dispersed_ns_participating_nss_log *log); + void (*dispersed_ns_psub_log)( + struct nvme_dispersed_ns_participating_nss_log *log, __u64 len); void (*reachability_groups_log)(struct nvme_reachability_groups_log *log, __u64 len); void (*reachability_associations_log)(struct nvme_reachability_associations_log *log, __u64 len); @@ -813,7 +814,7 @@ void nvme_show_power_meas_log(struct nvme_power_meas_log *log, __u32 size, void nvme_show_rotational_media_info_log(struct nvme_rotational_media_info_log *info, nvme_print_flags_t flags); void nvme_show_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_nss_log *log, - nvme_print_flags_t flags); + __u64 len, nvme_print_flags_t flags); void nvme_show_reachability_groups_log(struct nvme_reachability_groups_log *log, __u64 len, nvme_print_flags_t flags); void nvme_show_reachability_associations_log(struct nvme_reachability_associations_log *log, diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index 35d5486da8..e9deb5891b 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -374,6 +374,19 @@ def test_dispersed_ns_psub_too_many_subsystems(self): self.assertGreater(result.returncode, 0, 'not an error exit') self.assertNotIn('participating_nss 0', result.stdout) + def test_dispersed_ns_psub_get_log_short_buffer(self): + """get-log --verbose prints no more NQNs than its buffer holds.""" + self.server.logs[_LID_DISPERSED_NS_PSUB] = _dispersed_ns_psub_log( + ['nqn.2014-08.org.example:sub%d' % i for i in range(4)], + numpsub=100) + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'get-log', self.DEVICE, '--log-id=0x17', + '--log-len=512', '--verbose') + self.assertEqual(result.returncode, 0, + f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}') + self.assertIn('participating_nss 0', result.stdout) + self.assertNotIn('participating_nss 1', result.stdout) + if __name__ == '__main__': unittest.main(argv=[sys.argv[0]]) From d254bbc82cd15f740e1cb639edd6d95052c35ff2 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:58:13 +0000 Subject: [PATCH 168/263] nvme-print-json: don't leak the entry after the last UUID The UUID list printer creates the JSON object for an entry before it looks whether the entry ends the list, and leaks it when it does: on every list with less than 128 UUIDs. Create the object only for a UUID which gets printed. Found by fuzzing the device data. Fixes: 4ef5a032d50c ("nvme: UUIDs for Vendor-Specific Information") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 8e8cd902ae..77b5a89b24 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -4805,11 +4805,12 @@ static void json_nvme_id_uuid_list(const struct nvme_id_uuid_list *uuid_list) for (i = 0; i < NVME_ID_UUID_LIST_MAX; i++) { __u8 uuid[NVME_UUID_LEN]; - struct json_object *entry = json_create_object(); + struct json_object *entry; /* The list is terminated by a zero UUID value */ if (memcmp(uuid_list->entry[i].uuid, zero_uuid, sizeof(zero_uuid)) == 0) break; + entry = json_create_object(); memcpy(&uuid, uuid_list->entry[i].uuid, sizeof(uuid)); obj_add_int(entry, "association", uuid_list->entry[i].header & 0x3); From 7116af088f0c6bdf622d2b7037312c04a781d08e Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 17:59:11 +0000 Subject: [PATCH 169/263] nvme-print-json: don't leak the LBA status objects of a short log The LBA status log printer leaks its element list when the log is shorter than the header, and the element with its descriptor list when the descriptors don't fit the log. Create the element list after the length check, and free the element which isn't added. Found by fuzzing the device data. Fixes: 5a8c7f75e4e1 ("nvme-print: fix out-of-bounds reads in LBA status log parsers") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 77b5a89b24..38bbcdf211 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2221,7 +2221,7 @@ static void json_lba_status_log(void *lba_status, __u32 size, const char *devnam struct json_object *desc; struct json_object *element; struct json_object *desc_list; - struct json_object *elements_list = json_create_array(); + struct json_object *elements_list; struct nvme_lba_status_log *hdr = lba_status; struct nvme_lbas_ns_element *ns_element; struct nvme_lba_rd *range_desc; @@ -2234,6 +2234,7 @@ static void json_lba_status_log(void *lba_status, __u32 size, const char *devnam if (size < sizeof(*hdr)) return; + elements_list = json_create_array(); num_elements = le32_to_cpu(hdr->nlslne); obj_add_uint(r, "lslplen", le32_to_cpu(hdr->lslplen)); @@ -2256,8 +2257,11 @@ static void json_lba_status_log(void *lba_status, __u32 size, const char *devnam if (num_lba_desc != 0xffffffff) { if (num_lba_desc > - (size - offset) / sizeof(*range_desc)) + (size - offset) / sizeof(*range_desc)) { + json_free_object(desc_list); + json_free_object(element); break; + } for (i = 0; i < num_lba_desc; i++) { range_desc = lba_status + offset; desc = json_create_object(); From 35001154c67f54d11326d9f09b1b626345515ee6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:00:16 +0000 Subject: [PATCH 170/263] mem: free a zero length huge allocation libnvme_alloc_huge() of zero bytes returns memory, but records a length of zero, and libnvme_free_huge() frees nothing of zero length. Each such buffer leaks, e.g. for a log page the device says is empty. Allocate at least a page, so the buffer has a length to free it by. Found by fuzzing the device data. Fixes: 415f90d066b3 ("mem: move alloc_huge and free_huge to library") Signed-off-by: Daniel Wagner --- libnvme/src/nvme/mem-freebsd.c | 6 ++++++ libnvme/src/nvme/mem-linux.c | 6 ++++++ libnvme/src/nvme/mem-win.c | 6 ++++++ libnvme/tests/mem.c | 7 +++++++ 4 files changed, 25 insertions(+) diff --git a/libnvme/src/nvme/mem-freebsd.c b/libnvme/src/nvme/mem-freebsd.c index 517e1ce9b2..d2b5dfc2a2 100644 --- a/libnvme/src/nvme/mem-freebsd.c +++ b/libnvme/src/nvme/mem-freebsd.c @@ -63,6 +63,12 @@ __shr_public void *libnvme_alloc_huge(size_t len, { memset(mh, 0, sizeof(*mh)); + /* + * Even a zero length gets memory, which libnvme_free_huge() only + * frees with a length: allocate at least a page. + */ + if (!len) + len = 1; len = round_up(len, 0x1000); mh->p = libnvme_alloc(len); diff --git a/libnvme/src/nvme/mem-linux.c b/libnvme/src/nvme/mem-linux.c index da8a74fda6..60aa010c3e 100644 --- a/libnvme/src/nvme/mem-linux.c +++ b/libnvme/src/nvme/mem-linux.c @@ -92,6 +92,12 @@ __shr_public void *libnvme_alloc_huge(size_t len, { memset(mh, 0, sizeof(*mh)); + /* + * Even a zero length gets memory, which libnvme_free_huge() only + * frees with a length: allocate at least a page. + */ + if (!len) + len = 1; if (!round_up_checked(len, 0x1000, &len)) return NULL; diff --git a/libnvme/src/nvme/mem-win.c b/libnvme/src/nvme/mem-win.c index e28828fb94..755e156c9b 100644 --- a/libnvme/src/nvme/mem-win.c +++ b/libnvme/src/nvme/mem-win.c @@ -76,6 +76,12 @@ __shr_public void *libnvme_alloc_huge(size_t len, memset(mh, 0, sizeof(*mh)); + /* + * Even a zero length gets memory, which libnvme_free_huge() only + * frees with a length: allocate at least a page. + */ + if (!len) + len = 1; len = round_up(len, page_size); /* diff --git a/libnvme/tests/mem.c b/libnvme/tests/mem.c index 71abef3c04..2bf0adf59b 100644 --- a/libnvme/tests/mem.c +++ b/libnvme/tests/mem.c @@ -62,5 +62,12 @@ int main(void) shr_assert(!mh.p); shr_assert(!mh.len); + /* a zero length allocation must be freed as well */ + buf = libnvme_alloc_huge(0, &mh); + shr_assert(buf); + shr_assert(mh.len); + libnvme_free_huge(&mh); + shr_assert(!mh.p); + return 0; } From 0d278549fcf9ffa2b6a2f101dbc18d05f317ec64 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:01:26 +0000 Subject: [PATCH 171/263] log: reject a persistent event log too large to read The total log length of the persistent event log is a 64 bit value, but the log is read with a 32 bit length. A larger log is silently cut to the lower 32 bits of its length, and what is read is printed as the whole log. Fail with an error for a log which can't be read whole. Found by fuzzing the device data. Fixes: 18de3a6d61a7 ("Convert to libnvme") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 10 +++++++++- tests/cli/nvme_discovery_logs_test.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index ea355254f3..8df99977a4 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -1171,7 +1171,15 @@ static int get_persistent_event_log(int argc, char **argv, } if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { - cfg.log_len = le64_to_cpu(pevent->tll); + __u64 tll = le64_to_cpu(pevent->tll); + + /* the log is read with a 32 bit length */ + if (tll > UINT32_MAX) { + nvme_show_error("persistent event log of %"PRIu64 + " bytes is too large", (uint64_t)tll); + return -EINVAL; + } + cfg.log_len = tll; } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { nvme_show_error("Establishing Persistent Event Log Context"); return 0; diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py index e9deb5891b..7a8400bb8e 100644 --- a/tests/cli/nvme_discovery_logs_test.py +++ b/tests/cli/nvme_discovery_logs_test.py @@ -37,6 +37,7 @@ _LID_REACHABILITY_GROUPS = 0x1a _LID_REACHABILITY_ASSOCIATIONS = 0x1b _LID_DISPERSED_NS_PSUB = 0x17 +_LID_PERSISTENT_EVENT = 0x0d _HEADER_LEN = 1024 # host and AVE discovery log headers _HEDLPE_LEN = 1032 # struct nvme_host_ext_discovery_log @@ -387,6 +388,20 @@ def test_dispersed_ns_psub_get_log_short_buffer(self): self.assertIn('participating_nss 0', result.stdout) self.assertNotIn('participating_nss 1', result.stdout) + def test_persistent_event_log_too_large(self): + """A total log length which doesn't fit the 32 bit length the log + is read with fails, instead of reading a truncated log.""" + hdr = bytearray(512) + struct.pack_into(' Date: Fri, 16 Oct 2026 18:02:41 +0000 Subject: [PATCH 172/263] plugins/dera: don't index the status names with unknown values smart-log-add looks up the names of the device status in a table of ten names, with the result of a vendor command and with a byte of the log page as the index. Any other value reads past the table and prints what the pointer behind it points to, or crashes. Print "Unknown" for a status without a name. Found by fuzzing the device data. Fixes: cf13566caa4e ("Add Dera plugin extension. Add stat sub-command to retrieve Dera device status and additional SMART log.") Signed-off-by: Daniel Wagner --- plugins/dera/dera-nvme.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index 34bab22565..7814398bc1 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -158,13 +158,17 @@ static int get_status(int argc, char **argv, struct command *acmd, struct plugin if (!err) { if (state > 0 && state < 4) printf("device_status : %s %d%% completed\n", dev_status[state], log.rebuild_percent); - else + else if (state < ARRAY_SIZE(dev_status)) printf("device_status : %s\n", dev_status[state]); + else + puts("device_status : Unknown"); } else { goto exit; } - printf("dev_status_up : %s\n", dev_status[log.dev_status_up]); + printf("dev_status_up : %s\n", + log.dev_status_up < ARRAY_SIZE(dev_status) ? + dev_status[log.dev_status_up] : "Unknown"); printf("cap_aged : %s\n", log.cap_aged == 1 ? "True" : "False"); printf("cap_aged_ratio : %d%%\n", log.cap_aged_ratio < 100 ? log.cap_aged_ratio : 100); printf("cap_status : %s\n", log.cap_status == 0 ? "Normal" : (log.cap_status == 1 ? "Warning" : "Critical")); From 10cf31309c13056e91f14e3a20398dc77781a113 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:03:29 +0000 Subject: [PATCH 173/263] plugins/dera: read the four byte counters as unsigned char4_to_int() shifts the bytes into an int, which overflows for a value of 2^31 and more: undefined behaviour, in practice a negative count. Read the counters into a __u32 and print them unsigned. Found by fuzzing the device data. Fixes: cf13566caa4e ("Add Dera plugin extension. Add stat sub-command to retrieve Dera device status and additional SMART log.") Signed-off-by: Daniel Wagner --- plugins/dera/dera-nvme.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index 7814398bc1..8aa98aadb5 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -20,10 +20,10 @@ #include "nvme-print.h" #include "plugin.h" -static int char4_to_int(__u8 *data) +static __u32 char4_to_int(__u8 *data) { int i; - int result = 0; + __u32 result = 0; for (i = 0; i < 4; i++) { result = result << 8; @@ -172,9 +172,12 @@ static int get_status(int argc, char **argv, struct command *acmd, struct plugin printf("cap_aged : %s\n", log.cap_aged == 1 ? "True" : "False"); printf("cap_aged_ratio : %d%%\n", log.cap_aged_ratio < 100 ? log.cap_aged_ratio : 100); printf("cap_status : %s\n", log.cap_status == 0 ? "Normal" : (log.cap_status == 1 ? "Warning" : "Critical")); - printf("cap_voltage : %d mV\n", char4_to_int(log.cap_voltage)); - printf("nand_erase_err_cnt : %d\n", char4_to_int(log.nand_erase_err_cnt)); - printf("nand_program_err_cnt : %d\n", char4_to_int(log.nand_program_err_cnt)); + printf("cap_voltage : %u mV\n", + char4_to_int(log.cap_voltage)); + printf("nand_erase_err_cnt : %u\n", + char4_to_int(log.nand_erase_err_cnt)); + printf("nand_program_err_cnt : %u\n", + char4_to_int(log.nand_program_err_cnt)); printf("power_level : %d W\n", log.power_level); printf("current_power : %d mW\n", log.current_power[1] << 8 | log.current_power[0]); printf("nand_init_fail : %d\n", log.nand_init_fail[1] << 8 | log.nand_init_fail[0]); @@ -188,7 +191,8 @@ static int get_status(int argc, char **argv, struct command *acmd, struct plugin printf("init_pcie_volt_low_cnt : %d\n", log.init_pcie_volt_low[1] << 8 | log.init_pcie_volt_low[0]); printf("rt_pcie_volt_low_cnt : %d\n", log.rt_pcie_volt_low[1] << 8 | log.rt_pcie_volt_low[0]); printf("temp_sensor_abnormal_cnt : %d\n", log.temp_sensor_abnormal[1] << 8 | log.temp_sensor_abnormal[0]); - printf("nand_read_retry_fail_cnt : %d\n", char4_to_int(log.nand_read_retry_fail_cnt)); + printf("nand_read_retry_fail_cnt : %u\n", + char4_to_int(log.nand_read_retry_fail_cnt)); printf("fw_slot_version : %.*s\n", 8, log.fw_slot_version); exit: From a6ce9d075fa50a58908f6fa8cc6ef567cede4301 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:05:20 +0000 Subject: [PATCH 174/263] plugins/memblaze: have room for the new SMART log format smart-log-add reads the log into a struct nvme_memblaze_smart_log of 512 bytes, and prints the new format through a struct nvme_p4_smart_log of 4096 bytes at the same address. The items it reads lie within the first 512 bytes, but accessing them through a pointer to an object larger than the buffer is undefined. Read the log into a union of both structs. Found by fuzzing the device data. Fixes: e19a83034299 ("[Memblaze] modify SMART info") Signed-off-by: Daniel Wagner --- plugins/memblaze/memblaze-nvme.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 5fa6f00a76..9919ed764e 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -420,7 +420,11 @@ int parse_params(char *str, int number, ...) static int mb_get_additional_smart_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) { - struct nvme_memblaze_smart_log smart_log; + /* the new format is printed with the larger struct */ + union { + struct nvme_memblaze_smart_log old; + struct nvme_p4_smart_log p4; + } smart_log = {}; char *desc = "Get Memblaze vendor specific additional smart log, and show it."; const char *namespace = "(optional) desired namespace"; @@ -450,15 +454,16 @@ static int mb_get_additional_smart_log(int argc, char **argv, struct command *ac return err; nvme_init_get_log(&cmd, cfg.namespace_id, 0xca, NVME_CSI_NVM, - &smart_log, sizeof(smart_log)); + &smart_log.old, sizeof(smart_log.old)); err = libnvme_get_log(hdl, &cmd, false, NVME_LOG_PAGE_PDU_SIZE); if (!err) { if (!cfg.raw_binary) err = show_memblaze_smart_log(hdl, cfg.namespace_id, libnvme_transport_handle_get_name(hdl), - &smart_log); + &smart_log.old); else - d_raw((unsigned char *)&smart_log, sizeof(smart_log)); + d_raw((unsigned char *)&smart_log.old, + sizeof(smart_log.old)); } if (err > 0) nvme_show_status(err); From 59d88e7e37182dee32d3a19d87c4fdabbee226f0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:09:40 +0000 Subject: [PATCH 175/263] plugins/seagate: read and print the SMART logs of vs-smart-add-log once When the drive family check was added to vs-smart-add-log, the code it replaced stayed behind it. So after the logs of its family, every drive gets the extended SMART log 0xC4 and the DRAM SMART log 0xCF read and printed again: legacy drives print them twice, the others print logs which aren't theirs. In JSON, the family branches print and free the JSON object which the code behind them then adds to again, a use after free, and the objects leak in text mode. Drop the leftover code, create the JSON objects only for JSON output and print and free them once. Add a mock test which checks which logs are read and that they are printed once. Found by fuzzing the device data. Fixes: c4690eb24e47 ("plugins/seagate: Add support for OCP") Signed-off-by: Daniel Wagner --- plugins/seagate/seagate-nvme.c | 75 +++--------- tests/cli/meson.build | 4 + tests/cli/seagate/meson.build | 13 +++ .../seagate_vs_smart_add_log_mock_test.py | 109 ++++++++++++++++++ 4 files changed, 142 insertions(+), 59 deletions(-) create mode 100644 tests/cli/seagate/meson.build create mode 100644 tests/cli/seagate/seagate_vs_smart_add_log_mock_test.py diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index d34f92be38..895b257e70 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -899,14 +899,15 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug STX_EXT_SMART_LOG_PAGE_C0 ehExtSmart; EXTENDED_SMART_INFO_T ExtdSMARTInfo; vendor_log_page_CF logPageCF; - struct json_object *root = json_create_object(); - struct json_object *lbafs = json_create_array(); + __cleanup_json_object struct json_object *root = NULL; + struct json_object *lbafs = NULL; struct json_object *lbafs_ExtSmart, *lbafs_DramSmart; const char *desc = "Retrieve the Firmware Activation History for Seagate NVMe drives"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; + bool found = false; int err, index = 0; OPT_ARGS(opts) = { @@ -919,9 +920,12 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug flags = nvme_args.output_flags; - if (flags & JSON) + if (flags & JSON) { printf("Seagate Extended SMART Information :\n"); - + root = json_create_object(); + lbafs = json_create_array(); + json_object_add_value_array(root, "SMART-Attributes", lbafs); + } /** * Here we should identify if the drive is a Panthor or Jaguar. @@ -941,6 +945,7 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug if (!stx_is_jag_pan(modelNo)) { err = nvme_get_log_simple(hdl, 0xC4, &ExtdSMARTInfo, sizeof(ExtdSMARTInfo)); if (!err) { + found = true; if (!(flags & JSON)) { printf("%-39s %-15s %-19s\n", "Description", "Ext-Smart-Id", "Ext-Smart-Value"); for (index = 0; index < 80; index++) @@ -949,11 +954,9 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug for (index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++) print_smart_log(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index], index == (NUMBER_EXTENDED_SMART_ATTRIBUTES - 1)); - } else if (flags & JSON){ + } else { lbafs_ExtSmart = json_create_object(); json_print_smart_log(lbafs_ExtSmart, &ExtdSMARTInfo); - - json_object_add_value_array(root, "SMART-Attributes", lbafs); json_array_add_value_object(lbafs, lbafs_ExtSmart); } @@ -965,16 +968,11 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug if (!err) { if (!(flags & JSON)) { print_smart_log_CF(&logPageCF); - } else if (flags & JSON) { + } else { lbafs_DramSmart = json_create_object(); json_print_smart_log_CF(lbafs_DramSmart, &logPageCF); json_array_add_value_object(lbafs, lbafs_DramSmart); - json_print_object(root, NULL); } - } else if (flags & JSON) { - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); } } else if (err > 0) { nvme_show_status(err); @@ -983,18 +981,13 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug err = nvme_get_log_simple(hdl, 0xC0, &ehExtSmart, sizeof(ehExtSmart)); if (!err) { + found = true; if (!(flags & JSON)) { print_stx_smart_log_C0(&ehExtSmart); - } else if (flags & JSON){ + } else { lbafs_ExtSmart = json_create_object(); json_print_stx_smart_log_C0(lbafs_ExtSmart, &ehExtSmart); - - json_object_add_value_array(root, "SMART-Attributes", lbafs); json_array_add_value_object(lbafs, lbafs_ExtSmart); - - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); } } @@ -1002,45 +995,9 @@ static int vs_smart_log(int argc, char **argv, struct command *acmd, struct plug nvme_show_status(err); } - err = nvme_get_log_simple(hdl, 0xC4, - &ExtdSMARTInfo, sizeof(ExtdSMARTInfo)); - if (!err) { - if (!(flags & JSON)) { - printf("%-39s %-15s %-19s\n", "Description", "Ext-Smart-Id", "Ext-Smart-Value"); - for (index = 0; index < 80; index++) - printf("-"); - printf("\n"); - for (index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++) - print_smart_log(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index], index == (NUMBER_EXTENDED_SMART_ATTRIBUTES - 1)); - - } else if (flags & JSON) { - lbafs_ExtSmart = json_create_object(); - json_print_smart_log(lbafs_ExtSmart, &ExtdSMARTInfo); - - json_object_add_value_array(root, "SMART-Attributes", lbafs); - json_array_add_value_object(lbafs, lbafs_ExtSmart); - } - - /** - * Next get Log Page 0xCF - */ - - err = nvme_get_log_simple(hdl, 0xCF, - &logPageCF, sizeof(logPageCF)); - if (!err) { - if (!(flags & JSON)) { - print_smart_log_CF(&logPageCF); - } else if (flags & JSON) { - lbafs_DramSmart = json_create_object(); - json_print_smart_log_CF(lbafs_DramSmart, &logPageCF); - json_array_add_value_object(lbafs, lbafs_DramSmart); - json_print_object(root, NULL); - } - } else if (flags & JSON) { - json_print_object(root, NULL); - } - } else if (err > 0) { - nvme_show_status(err); + if (found && (flags & JSON)) { + json_print_object(root, NULL); + printf("\n"); } return err; diff --git a/tests/cli/meson.build b/tests/cli/meson.build index a867639880..33cad27828 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -171,4 +171,8 @@ if python3.found() if 'samsung' in selected_plugins subdir('samsung') endif + + if 'seagate' in selected_plugins + subdir('seagate') + endif endif diff --git a/tests/cli/seagate/meson.build b/tests/cli/seagate/meson.build new file mode 100644 index 0000000000..54f71a6531 --- /dev/null +++ b/tests/cli/seagate/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Hardware-free Seagate plugin tests. + +if have_mock_nvme and json_c_dep.found() + test('nvme-cli - seagate-vs-smart-add-log-cli', + python3, + args: [files('seagate_vs_smart_add_log_mock_test.py'), nvme_exe, + mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib], + suite: ['seagate', 'seagate-cli']) +endif diff --git a/tests/cli/seagate/seagate_vs_smart_add_log_mock_test.py b/tests/cli/seagate/seagate_vs_smart_add_log_mock_test.py new file mode 100644 index 0000000000..752483868d --- /dev/null +++ b/tests/cli/seagate/seagate_vs_smart_add_log_mock_test.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE Software Solutions +# +# Authors: Daniel Wagner +"""Tests for "nvme seagate vs-smart-add-log" against a mocked controller. + +The command reads the extended SMART log 0xC4 and the DRAM SMART log 0xCF +of a legacy (Jaguar or Panther) drive, and the log 0xC0 of any other. +Each must be printed once, in text and in JSON. + +Usage: python3 seagate_vs_smart_add_log_mock_test.py +""" +import json +import os +import shutil +import sys +import tempfile +import unittest + +from tests.cli.nvme_mock_ipc import (MockIPCServer, make_mock_env, + resolve_mock_lib_path, run_nvme) + +_NVME_BIN = sys.argv[1] if len(sys.argv) > 1 and not sys.argv[1].startswith('-') else 'nvme' +_MOCK_LIB = resolve_mock_lib_path("./libmock_nvme.so") + +_OPC_GET_LOG_PAGE = 0x02 +_OPC_IDENTIFY = 0x06 +_MN_OFFSET = 24 +_JAGUAR_MN = b'ST1000KN0002' + + +class SeagateMockServer(MockIPCServer): + """A controller whose model number is @mn, with zeroed log pages. + Records the log page IDs read.""" + + def __init__(self, sock_path): + super().__init__(sock_path) + self.mn = b'ST800FM0173' + self.lids = [] + + def handle_ioctl(self, conn, fd, request, opcode, nsid, + cdw10, cdw11, cdw12, cdw13, cdw14, cdw15, lpo, req_len): + data = bytearray(req_len) + if opcode == _OPC_IDENTIFY and (cdw10 & 0xff) == 1: + data[_MN_OFFSET:_MN_OFFSET + 40] = self.mn.ljust(40, b' ') + elif opcode == _OPC_GET_LOG_PAGE: + self.lids.append(cdw10 & 0xff) + self.send_response(conn, 0, payload=bytes(data)) + + +class SeagateVsSmartAddLogTest(unittest.TestCase): + + DEVICE = '/dev/nvme0' + + def setUp(self): + self.tmp = tempfile.mkdtemp(prefix='nvme-seagate-', dir='/tmp') + self.sysfs_dir = os.path.join(self.tmp, 'sysfs') + self.base_dir = os.path.join(self.tmp, 'base') + os.mkdir(self.sysfs_dir) + os.mkdir(self.base_dir) + self.server = SeagateMockServer(os.path.join(self.tmp, 'ipc.sock')) + self.server.start() + self.env = make_mock_env(_MOCK_LIB, self.server.sock_path) + + def tearDown(self): + self.server.shutdown() + self.server.join() + shutil.rmtree(self.tmp, ignore_errors=True) + + def _run(self, *args): + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'seagate', 'vs-smart-add-log', self.DEVICE, *args) + self.assertEqual(result.returncode, 0, + f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}') + self.assertEqual(self.server.errors, []) + return result.stdout + + @staticmethod + def _json(out): + # the JSON follows a heading line + return json.loads(out.split('\n', 1)[1]) + + def test_jaguar_logs_read_and_printed_once(self): + self.server.mn = _JAGUAR_MN + out = self._run() + self.assertEqual(self.server.lids, [0xc4, 0xcf]) + self.assertEqual(out.count('Ext-Smart-Id'), 1) + + def test_jaguar_json_printed_once(self): + self.server.mn = _JAGUAR_MN + out = self._json(self._run('-o', 'json')) + self.assertEqual(self.server.lids, [0xc4, 0xcf]) + self.assertEqual(len(out['SMART-Attributes']), 2) + + def test_log_c0_read_and_printed_once(self): + self._run() + self.assertEqual(self.server.lids, [0xc0]) + + def test_log_c0_json(self): + out = self._json(self._run('-o', 'json')) + self.assertEqual(self.server.lids, [0xc0]) + self.assertEqual(len(out['SMART-Attributes']), 1) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]]) From aafcb221de6eb1a6b3bfa90bbc5ad1f218912826 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:11:43 +0000 Subject: [PATCH 176/263] plugins/seagate: free the JSON output of the temperature and PCIe stats vs-temperature-stats and vs-pcie-stats print their JSON object but never free it. Free it when it goes out of scope. Found by fuzzing the device data. Fixes: 36351e2e47c4 ("Initial checkin for Seagate Plugin") Signed-off-by: Daniel Wagner --- plugins/seagate/seagate-nvme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 895b257e70..64a5b6bc8c 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -1012,7 +1012,7 @@ static void json_temp_stats(__u32 temperature, __u32 PcbTemp, __u32 SocTemp, __u32 maxTemperature, __u32 MaxSocTemp, __u32 cf_err, __u32 scCurrentTemp, __u32 scMaxTem) { - struct json_object *root = json_create_object(); + __cleanup_json_object struct json_object *root = json_create_object(); json_object_add_value_int(root, "Current temperature", temperature); json_object_add_value_int(root, "Current PCB temperature", PcbTemp); @@ -1156,7 +1156,7 @@ static void print_vs_pcie_error_log(pcie_error_log_page pcieErrorLog) static void json_vs_pcie_error_log(pcie_error_log_page pcieErrorLog) { - struct json_object *root = json_create_object(); + __cleanup_json_object struct json_object *root = json_create_object(); __u32 correctPcieEc = pcieErrorLog.BadDllpErrCnt + pcieErrorLog.BadTlpErrCnt + pcieErrorLog.RcvrErrCnt + pcieErrorLog.ReplayTOErrCnt + pcieErrorLog.ReplayNumRolloverErrCnt; From 34b3dd30945cb846c7be1b907cd696a071ad3ffb Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:27:11 +0000 Subject: [PATCH 177/263] log: grow the buffer of a log read in parts geometrically get_log_offset() reallocates the log buffer to the exact length for every part it reads, and libnvme_realloc() always allocates, clears and copies. The reachability logs are read in two parts per descriptor, so a log of 65535 descriptors takes some 130000 reallocations of a buffer of up to megabytes: minutes of copying. Track the size of the buffer and grow it to at least twice its size. Found by fuzzing the device data. Fixes: 4053efff0ea6 ("nvme: add reachability-groups-log command") Signed-off-by: Daniel Wagner --- plugins/log/log-plugin.c | 64 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/plugins/log/log-plugin.c b/plugins/log/log-plugin.c index 8df99977a4..9dd7e1446f 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -2284,22 +2284,34 @@ static int get_power_measurement_log(int argc, char **argv, struct command *acmd return err; } +/* + * Read @len bytes of a log at @offset into the buffer @log of @size bytes, + * and advance @offset past them. The buffer grows as needed, at least to + * twice its size, so reading a log in many small parts doesn't copy it + * over and over. + */ static int get_log_offset(struct libnvme_transport_handle *hdl, struct nvme_get_log_args *args, __u64 *offset, - __u32 len, void **log) + __u32 len, void **log, __u64 *size) { struct libnvme_passthru_cmd cmd; + __u64 need = *offset + len; void *p; int err; + if (need > *size) { + __u64 grow = *size * 2 > need ? *size * 2 : need; + + p = libnvme_realloc(*log, grow); + if (!p) + return -ENOMEM; + *log = p; + *size = grow; + } + args->lpo = *offset; args->len = len; - *offset += args->len; - - p = libnvme_realloc(*log, *offset); - if (!p) - return -ENOMEM; - *log = p; + *offset = need; args->log = (char *)*log + args->lpo; @@ -2326,8 +2338,9 @@ static int get_log_offset(struct libnvme_transport_handle *hdl, return err; } -static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, struct nvme_get_log_args *args, - __u64 *offset, struct nvme_reachability_groups_log **logp) +static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, + struct nvme_get_log_args *args, __u64 *offset, + struct nvme_reachability_groups_log **logp, __u64 *size) { int err; struct nvme_reachability_groups_log *log = *logp; @@ -2340,7 +2353,7 @@ static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, str for (i = 0; i < le16_to_cpu(log->nrgd); i++) { desc = *offset; err = get_log_offset(hdl, args, offset, sizeof(rgd), - (void **)&log); + (void **)&log, size); if (err) goto err_free; /* the descriptors are only 4 byte aligned */ @@ -2353,7 +2366,8 @@ static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, str goto err_free; } err = get_log_offset(hdl, args, offset, - nnid * sizeof(*rgd.nsid), (void **)&log); + nnid * sizeof(*rgd.nsid), (void **)&log, + size); if (err) goto err_free; } @@ -2375,6 +2389,7 @@ static int get_reachability_groups(struct libnvme_transport_handle *hdl, bool rg struct nvme_reachability_groups_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u64 size = log_len; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_REACHABILITY_GROUPS, .nsid = NVME_NSID_ALL, @@ -2391,7 +2406,7 @@ static int get_reachability_groups(struct libnvme_transport_handle *hdl, bool rg if (err) goto err_free; - err = get_reachability_group_desc(hdl, &args, &log_len, &log); + err = get_reachability_group_desc(hdl, &args, &log_len, &log, &size); if (err) goto err_free; @@ -2449,9 +2464,10 @@ static int get_reachability_groups_log(int argc, char **argv, struct command *ac return err; } -static int get_reachability_association_desc(struct libnvme_transport_handle *hdl, struct nvme_get_log_args *args, - __u64 *offset, - struct nvme_reachability_associations_log **logp) +static int get_reachability_association_desc( + struct libnvme_transport_handle *hdl, + struct nvme_get_log_args *args, __u64 *offset, + struct nvme_reachability_associations_log **logp, __u64 *size) { int err; struct nvme_reachability_associations_log *log = *logp; @@ -2464,7 +2480,7 @@ static int get_reachability_association_desc(struct libnvme_transport_handle *hd for (i = 0; i < le16_to_cpu(log->nrad); i++) { desc = *offset; err = get_log_offset(hdl, args, offset, sizeof(rad), - (void **)&log); + (void **)&log, size); if (err) goto err_free; /* the descriptors are only 4 byte aligned */ @@ -2477,7 +2493,8 @@ static int get_reachability_association_desc(struct libnvme_transport_handle *hd goto err_free; } err = get_log_offset(hdl, args, offset, - nrid * sizeof(*rad.rgid), (void **)&log); + nrid * sizeof(*rad.rgid), (void **)&log, + size); if (err) goto err_free; } @@ -2499,6 +2516,7 @@ static int get_reachability_associations(struct libnvme_transport_handle *hdl, b struct nvme_reachability_associations_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u64 size = log_len; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_REACHABILITY_ASSOCIATIONS, .nsid = NVME_NSID_ALL, @@ -2515,7 +2533,8 @@ static int get_reachability_associations(struct libnvme_transport_handle *hdl, b if (err) goto err_free; - err = get_reachability_association_desc(hdl, &args, &log_len, &log); + err = get_reachability_association_desc(hdl, &args, &log_len, &log, + &size); if (err) goto err_free; @@ -2581,6 +2600,7 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost struct nvme_host_discovery_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u64 size = log_len; __u32 thdlpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_HOST_DISCOVERY, @@ -2602,7 +2622,7 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost thdlpl = le32_to_cpu(log->thdlpl); if (thdlpl > log_len) { err = get_log_offset(hdl, &args, &log_len, thdlpl - log_len, - (void **)&log); + (void **)&log, &size); if (err) goto err_free; } @@ -2668,6 +2688,7 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, struct nvme_ave_discovery_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u64 size = log_len; __u32 tadlpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_AVE_DISCOVERY, @@ -2688,7 +2709,7 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, tadlpl = le32_to_cpu(log->tadlpl); if (tadlpl > log_len) { err = get_log_offset(hdl, &args, &log_len, tadlpl - log_len, - (void **)&log); + (void **)&log, &size); if (err) goto err_free; } @@ -2752,6 +2773,7 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, struct nvme_pull_model_ddc_req_log *log; struct libnvme_passthru_cmd cmd; __u64 log_len = sizeof(*log); + __u64 size = log_len; __u32 tpdrpl; struct nvme_get_log_args args = { .lid = NVME_LOG_LID_PULL_MODEL_DDC_REQ, @@ -2772,7 +2794,7 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, tpdrpl = le32_to_cpu(log->tpdrpl); if (tpdrpl > log_len) { err = get_log_offset(hdl, &args, &log_len, tpdrpl - log_len, - (void **)&log); + (void **)&log, &size); if (err) goto err_free; } From c564e4070e6c38f90244741d81d8c19cad4eb636 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:31:34 +0000 Subject: [PATCH 178/263] shared: grow the rows of a table by doubling shr_table_get_row_id() grows the row array by one row for each row. A table of a log page with many entries, such as the up to two million IDs of a reachability log of 8 MiB, takes as many reallocations, and each one copies the array where realloc() can't grow it in place, as under ASan: a quadratic runtime of many minutes. Keep track of the allocated rows and double them when they are used up. Found by fuzzing the device data. Signed-off-by: Daniel Wagner --- shared/table-util.c | 22 +++++++++++++++++----- shared/table-util.h | 1 + shared/tests/test-table-util.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/shared/table-util.c b/shared/table-util.c index 7fcb5529f4..fe9d93e8b7 100644 --- a/shared/table-util.c +++ b/shared/table-util.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "table-util.h" @@ -374,14 +375,25 @@ int shr_table_get_row_id(struct shr_table *t) { struct shr_table_row *new_rows; int row = t->num_rows; + int alloc; - new_rows = reallocarray(t->rows, (row + 1), sizeof(struct shr_table_row)); - if (!new_rows) { - t->error = true; - return -ENOMEM; + /* grow by doubling, a table can have very many rows */ + if (row == t->rows_alloc) { + if (row > INT_MAX / 2) { + t->error = true; + return -ENOMEM; + } + alloc = row ? row * 2 : 16; + new_rows = reallocarray(t->rows, alloc, + sizeof(struct shr_table_row)); + if (!new_rows) { + t->error = true; + return -ENOMEM; + } + t->rows = new_rows; + t->rows_alloc = alloc; } - t->rows = new_rows; t->rows[row] = (struct shr_table_row){ 0 }; t->rows[row].val = calloc(t->num_columns, sizeof(struct shr_table_value)); if (!t->rows[row].val) { diff --git a/shared/table-util.h b/shared/table-util.h index 1bee1fca33..e00bb1e4bb 100644 --- a/shared/table-util.h +++ b/shared/table-util.h @@ -84,6 +84,7 @@ struct shr_table { int num_columns; struct shr_table_row *rows; int num_rows; + int rows_alloc; char *col_sep; bool no_header; int indent; diff --git a/shared/tests/test-table-util.c b/shared/tests/test-table-util.c index 8a45e74d6b..a5c4d9b2db 100644 --- a/shared/tests/test-table-util.c +++ b/shared/tests/test-table-util.c @@ -939,6 +939,39 @@ static bool test_align_column_subtables_only(void) return pass; } +static bool test_many_rows(void) +{ + struct shr_table_column columns[] = { + { "Row", RIGHT, AUTO_WIDTH }, + }; + struct shr_table *t; + bool pass = true; + int i, row = 0; + + printf("test_many_rows:\n"); + + t = shr_table_init_with_columns(columns, 1); + pass &= check_bool("table allocated", t != NULL); + if (!t) + return pass; + + for (i = 0; i < 100000 && row >= 0; i++) { + row = shr_table_get_row_id(t); + if (row >= 0) + shr_table_set_value_int(t, 0, row, i, RIGHT); + } + pass &= check_bool("every row gets the next id", row == 100000 - 1); + pass &= check_bool("the table has all rows", t->num_rows == 100000); + pass &= check_bool("the first row kept its value", + t->rows[0].val[0].i == 0); + pass &= check_bool("the last row has its value", + t->rows[99999].val[0].i == 99999); + + shr_table_free(t); + + return pass; +} + int main(void) { bool pass = true; @@ -957,6 +990,7 @@ int main(void) pass &= test_align_column_including_outer(); pass &= test_align_column_subtables_only(); pass &= test_shr_table_print(); + pass &= test_many_rows(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From cd2c9905f9e8cb5e885467c7a13a176b7ed01c82 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:32:48 +0000 Subject: [PATCH 179/263] nvme-print-json: don't leak the event list of a short persistent event log The persistent event log printer creates the list of event entries before it checks the length of the log, and leaks it when the log is too short to have any. Create the list only for a log with entries. Found by fuzzing the device data. Fixes: 6879ac41fcc6 ("nvme: add support for persistent event log page") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 38bbcdf211..3f8effa2c7 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2136,10 +2136,11 @@ static void json_persistent_event_log(void *pevent_log_info, __u8 action, __u32 size, const char *devname) { struct json_object *r = json_r; - struct json_object *valid = json_create_array(); + struct json_object *valid; __u32 offset = sizeof(struct nvme_persistent_event_log); if (size >= offset) { + valid = json_create_array(); nvme_json_pevent_log_head(pevent_log_info, r); json_pevent_entry(pevent_log_info, action, size, devname, offset, valid); obj_add_array(r, "list_of_event_entries", valid); From 068120c2f5e279682d5c92e4f9c870464365df38 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:33:22 +0000 Subject: [PATCH 180/263] plugins/ibm: free the persistent event log and check its length persist-event-log never frees the buffer it reads the log into. It allocates the 64 bit total log length the device reports, but reads and prints the log with a 32 bit length, so a larger log is silently cut. And when the allocation fails, it returns success. Free the buffer, fail for a log which can't be read whole, and return -ENOMEM when the buffer can't be allocated. Found by fuzzing the device data. Fixes: 1c9817ac8a4f ("plugins/ibm: show persist events log") Signed-off-by: Daniel Wagner --- plugins/ibm/ibm-nvme.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 55bded610f..26e4b1279c 100644 --- a/plugins/ibm/ibm-nvme.c +++ b/plugins/ibm/ibm-nvme.c @@ -535,9 +535,10 @@ static int get_ibm_persistent_event_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct nvme_persistent_event_log pevent_log; - void *pevent_log_info = NULL; + __cleanup_libnvme_free void *pevent_log_info = NULL; enum nvme_print_flags flags; __u32 log_length = 0; + __u64 tll; int err = 0; struct config { @@ -588,11 +589,18 @@ static int get_ibm_persistent_event_log(int argc, char **argv, return err; } - log_length = le64_to_cpu(pevent_log.tll); + /* the log is read with a 32 bit length */ + tll = le64_to_cpu(pevent_log.tll); + if (tll > UINT32_MAX) { + nvme_show_error("persistent event log of %"PRIu64 + " bytes is too large", (uint64_t)tll); + return -EINVAL; + } + log_length = tll; pevent_log_info = libnvme_alloc(log_length); if (!pevent_log_info) { nvme_show_perror("could not alloc buffer for persistent event log page (ignored)!\n"); - return err; + return -ENOMEM; } err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_READ, From 947cfa3739c575aa9fbb657626cecd2c5bc1887e Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:33:52 +0000 Subject: [PATCH 181/263] plugins/solidigm: don't leak the JSON object of an empty SMART item smart_log_item_add_json() creates the JSON object for an item before it looks whether the item is used, and leaks it for each unused one. Create the object only for a used item. Found by fuzzing the device data. Fixes: fadb20e17e71 ("solidigm: Add Solidigm pluging for vendor specific SMART fields.") Signed-off-by: Daniel Wagner --- plugins/solidigm/solidigm-smart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index b628960470..64fee6ec4c 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -182,11 +182,12 @@ static void smart_log_item_add_json(struct nvme_additional_smart_log_item *item, bool has_smart_id_e7_or_e8) { struct smart_ref_clk *pll_item = (struct smart_ref_clk *)item; - struct json_object *entry_stats = json_create_object(); + struct json_object *entry_stats; if (!item->id) return; + entry_stats = json_create_object(); json_object_add_value_int(entry_stats, "normalized", item->normalized); switch (item->id) { From 2389c92644651047ac0f0befbfb96a472ebded71 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:34:32 +0000 Subject: [PATCH 182/263] plugins/wdc: release the handle and the log of vs-hw-rev-log vs-hw-rev-log never closes the device handle and never frees the global context, and leaks the log buffer on the error paths. Release all three when they go out of scope. Found by fuzzing the device data. Fixes: 66ae0df182cc ("wdc: Plugin changes to support new SN810-2 drive") Signed-off-by: Daniel Wagner --- plugins/wdc/wdc-nvme.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 3f7ed664bd..59d72cbe67 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -8760,10 +8760,11 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, const char *namespace_id = "desired namespace id"; nvme_print_flags_t fmt; __u64 capabilities = 0; - struct libnvme_transport_handle *hdl; + __cleanup_nvme_transport_handle + struct libnvme_transport_handle *hdl = NULL; + __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; + __cleanup_free __u8 *data = NULL; int ret; - __u8 *data = NULL; - struct libnvme_global_ctx *ctx; struct config { char *output_format; @@ -8808,7 +8809,7 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, ret = validate_output_format(cfg.output_format, &fmt); if (ret < 0) { nvme_show_error("ERROR: WDC %s: invalid output format", __func__); - goto free_buf; + goto out; } if (!data) { @@ -8831,9 +8832,6 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, ret = -1; } -free_buf: - free(data); - out: return ret; } From c0b9257463ff0e37b00e57d5e9ed0b78f2975efd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 19:19:02 +0000 Subject: [PATCH 183/263] nvme-print-json: don't leak the list of a changed namespace list log The printer of the changed namespace list logs creates the JSON object for the list before it returns early for a log which lists more than 1024 namespaces, and leaks it then. Create the object after the check. Found by fuzzing the device data. Fixes: 4551442b7a73 ("nvme-print-json: Combine variable definition and setting value") Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 3f8effa2c7..5670555ae7 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -724,7 +724,7 @@ void json_fw_log(struct nvme_firmware_slot *fw_log, const char *devname) void json_changed_ns_list_log(struct nvme_ns_list *log, const char *devname, bool alloc) { struct json_object *r = json_r; - struct json_object *nsi = json_create_object(); + struct json_object *nsi; char fmt[32]; char str[32]; __u32 nsid; @@ -735,6 +735,8 @@ void json_changed_ns_list_log(struct nvme_ns_list *log, const char *devname, boo if (log->ns[0] == cpu_to_le32(0xffffffff)) return; + nsi = json_create_object(); + if (asprintf(&k, "Changed %s Namespace List Log", alloc ? "Allocated" : "Attached") > 0) obj_add_str(r, k, devname); From d21f3725f66ad669386927bb8e9b9a215ef17eaa Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 19:19:23 +0000 Subject: [PATCH 184/263] plugins/ocp: don't leak the event list of a short persistent event log Like the one of the core, the persistent event log printer of the ocp plugin creates the list of event entries before it checks the length of the log, and leaks it when the log is too short to have any. Create the list only for a log with entries. Found by fuzzing the device data. Fixes: 406f50aad466 ("ocp: add Persistent Event log support") Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-print-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ocp/ocp-print-json.c b/plugins/ocp/ocp-print-json.c index a81c0412bc..6b0300ddcb 100644 --- a/plugins/ocp/ocp-print-json.c +++ b/plugins/ocp/ocp-print-json.c @@ -1389,10 +1389,11 @@ static void json_persistent_event_log(void *pevent_log_info, __u8 action, __u32 size, const char *devname) { struct json_object *r = json_create_object(); - struct json_object *valid = json_create_array(); + struct json_object *valid; __u32 offset = sizeof(struct nvme_persistent_event_log); if (size >= offset) { + valid = json_create_array(); nvme_json_pevent_log_head(pevent_log_info, r); json_pevent_entry(pevent_log_info, action, size, devname, offset, valid); obj_add_array(r, "list_of_event_entries", valid); From 1f0d2cf0dfc1e5aed6145a8983334dfa3a8b02b1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 19:20:44 +0000 Subject: [PATCH 185/263] plugins/solidigm: free the namespace of vs-drive-info vs-drive-info falls back to scanning the namespace on its own when the controller can't be scanned. Such a namespace isn't part of the tree, so freeing the context doesn't free it: it leaks. And a controller without namespaces has the command identify a NULL namespace. Free the namespace it scanned, and fail when there is none. Found by fuzzing the device data. Fixes: 10b742e343a3 ("plugins/solidigm: Added OCP 2.0 vs-drive-info command.") Signed-off-by: Daniel Wagner --- plugins/solidigm/solidigm-get-drive-info.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index 778f15c834..eb132bddc0 100644 --- a/plugins/solidigm/solidigm-get-drive-info.c +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -16,6 +16,11 @@ #include "nvme-print.h" #include "plugin.h" +static void free_nsp(struct libnvme_ns **n) +{ + libnvme_free_ns(*n); +} + int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Get drive HW information"; @@ -24,6 +29,8 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; struct libnvme_ctrl *c; + /* a namespace scanned on its own isn't part of the tree */ + __cleanup(free_nsp) struct libnvme_ns *scanned = NULL; struct libnvme_ns *n; struct nvme_id_ns ns = { 0 }; __u8 flbaf_inUse; @@ -50,11 +57,16 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu n = libnvme_ctrl_first_ns(c); else { err = libnvme_scan_namespace(ctx, - libnvme_transport_handle_get_name(hdl), &n); + libnvme_transport_handle_get_name(hdl), &scanned); if (err) { nvme_show_error("solidigm-vs-drive-info: drive missing namespace"); return err; } + n = scanned; + } + if (!n) { + nvme_show_error("solidigm-vs-drive-info: no namespace"); + return -ENODEV; } err = libnvme_ns_identify(n, &ns); From f71f5f3df0d1edb87a6b38634ae60b8b4082102b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 19:21:23 +0000 Subject: [PATCH 186/263] plugins/solidigm: compute the FTL unit size of vs-drive-info safely vs-drive-info shifts 1 by the LBA data size the device reports, which is undefined from 31 on, keeps the result in 16 bits, which is wrong from 16 on, and multiplies it with the preferred write granularity in int, which can overflow too. Get the LBA size from nvme_lbaf_data_size() and fail for one which isn't representable, and compute and print the FTL unit size in 64 bits. Fixes: 10b742e343a3 ("plugins/solidigm: Added OCP 2.0 vs-drive-info command.") Signed-off-by: Daniel Wagner --- plugins/solidigm/solidigm-get-drive-info.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index eb132bddc0..bbab9ad182 100644 --- a/plugins/solidigm/solidigm-get-drive-info.c +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -34,8 +34,8 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu struct libnvme_ns *n; struct nvme_id_ns ns = { 0 }; __u8 flbaf_inUse; - __u16 lba_size; - __u16 ftl_unit_size; + unsigned int lba_size; + uint64_t ftl_unit_size; int err; OPT_ARGS(opts) = { @@ -81,18 +81,23 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *acmd, struct plu } nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &flbaf_inUse); - lba_size = 1 << ns.lbaf[flbaf_inUse].ds; - ftl_unit_size = (le16_to_cpu(ns.npwg) + 1) * lba_size / 1024; + lba_size = nvme_lbaf_data_size(ns.lbaf[flbaf_inUse].ds); + if (!lba_size) { + nvme_show_error("solidigm-vs-drive-info: bad LBA data size"); + return -EINVAL; + } + ftl_unit_size = (le16_to_cpu(ns.npwg) + 1ULL) * lba_size / 1024; if (flags == JSON) { struct json_object *root = json_create_object(); - json_object_add_value_int(root, FTL_unit_size_str, ftl_unit_size); + json_object_add_value_uint64(root, FTL_unit_size_str, + ftl_unit_size); json_print_object(root, NULL); printf("\n"); json_free_object(root); } else { - printf("%s: %d\n", FTL_unit_size_str, ftl_unit_size); + printf("%s: %"PRIu64"\n", FTL_unit_size_str, ftl_unit_size); } return err; From 3f4604557e41464cf78ec1ee657dac888d27efb9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:41:12 +0000 Subject: [PATCH 187/263] NEWS: document the fixes for unexpected device data Note the fixes for device data whose lengths and counts do not add up, the log page records which were never read, and the plugin fixes. Signed-off-by: Daniel Wagner --- NEWS.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NEWS.md b/NEWS.md index a1233f7627..b76827893f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -288,6 +288,27 @@ * The wdc plugin no longer creates its dump directories with a broken mode that only root could write to. +* A device, or a fabrics target, which returns log pages whose lengths + and counts don't add up no longer makes nvme-cli read past its + buffers, loop for minutes or leak memory. This covers id-ctrl and the + FDP, host and AVE discovery, pull model DDC request, power + measurement, reachability, dispersed namespace, LBA status, changed + namespace list and persistent event logs. Found by fuzzing the device + data. + +* `nvme log host-discovery`, `ave-discovery` and `pull-model-ddc-req` + read the records which follow the header now. Before, they printed + zeros instead. `host-discovery --all-host-entries` reads the header + of the right page as well. + +* `nvme log reachability-groups` and `reachability-associations` decode + every descriptor now. Before, only the first one was read from the + right place. Their JSON output lists the IDs of a descriptor in an + array, `nsid` and `rgid`. Before, all but the last ID were lost. + +* `nvme log persistent-event` fails for a log larger than 4 GiB, which + it can't read, instead of printing a part of it as the whole log. + ### Plugins * `nvme feat num-queues --nsqr=N` without `--ncqr` keeps the current @@ -299,6 +320,14 @@ a caller-controlled string reached a shell (`system()`), so a crafted input could run arbitrary commands as root. +* `nvme seagate vs-smart-add-log` prints the SMART logs of a drive + once. Before, it printed those of legacy drives twice and those of + other drives followed by logs which aren't theirs, and its JSON + output could crash. + +* The dera, ibm, memblaze, ocp, seagate, solidigm and wdc plugins handle + unexpected values from the device and free the memory they used. + * WDC, SanDisk, Micron, Solidigm and Samsung plugins no longer spawn external `tar`, `zip`, `cat`, `lsmod`, `uname` or `dmesg` processes; they use libarchive or direct syscalls instead. From 166acffd07c83a5fdef33b29695aea7cab63e0bf Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:12 +0000 Subject: [PATCH 188/263] shared: add __shr_destructor The counterpart of __shr_constructor, for a function which releases at exit what a constructor set up. Signed-off-by: Daniel Wagner --- shared/compiler-attributes-util.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shared/compiler-attributes-util.h b/shared/compiler-attributes-util.h index 2ac70588f6..5ecdf1f86f 100644 --- a/shared/compiler-attributes-util.h +++ b/shared/compiler-attributes-util.h @@ -52,6 +52,14 @@ */ #define __shr_constructor __attribute__((constructor)) +/** + * __shr_destructor - run a function automatically after main() returns. + * + * The counterpart of __shr_constructor, e.g. to release what a + * constructor allocated. + */ +#define __shr_destructor __attribute__((destructor)) + /* * __packed - mark a struct/union as having no padding between members. * From 83eae0acd3e439618722b0baf3423601ec6ab9cc Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:13 +0000 Subject: [PATCH 189/263] nvme: release the plugins in a destructor main() registers free_plugins() with atexit() on every call. Running main() many times in one process, as a fuzz test does, piles up one exit handler per run. Release the plugins in a destructor, the counterpart of the constructors which register them. Signed-off-by: Daniel Wagner --- src/nvme.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/nvme.c b/src/nvme.c index c77d4eccc1..a91678dae8 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -142,9 +143,11 @@ void register_extension(struct plugin *plugin) /* * plugin_add_group() runs from constructors, before main(), and its - * allocations live for the whole process. Release them at exit. + * allocations live for the whole process. Release them at exit. A + * destructor, not atexit(), so main() can run more than once in a + * process, as the fuzz tests do. */ -static void free_plugins(void) +static void __shr_destructor free_plugins(void) { struct plugin *plugin; @@ -174,7 +177,6 @@ int main(int argc, char **argv) #endif nvme.extensions->parent = &nvme; - atexit(free_plugins); if (argc < 2) { general_help(&builtin, NULL); return 0; From 3106ca92471a26ee8ff7ee5f6eba1e3453355c90 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:20 +0000 Subject: [PATCH 190/263] nvme: move main() into a file of its own A test which runs nvme-cli in its own process, like a fuzz target, needs everything of nvme-cli but main(), which it has itself. Rename main() to nvme_main() and call it from main() in src/main.c, so such a test can link all other objects of the nvme executable. Signed-off-by: Daniel Wagner --- meson.build | 2 +- src/main.c | 18 ++++++++++++++++++ src/nvme.c | 2 +- src/plugin.h | 3 +++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/main.c diff --git a/meson.build b/meson.build index 611a346472..fc7c1b509e 100644 --- a/meson.build +++ b/meson.build @@ -738,7 +738,7 @@ if want_nvme nvme_exe = executable( 'nvme', - sources, + sources + ['src/main.c'], include_directories: 'src', dependencies: link_deps, link_args: link_args_list, diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000000..aa27837a35 --- /dev/null +++ b/src/main.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Only main() lives here, so the fuzz tests can link everything else of + * the program and run it with their own main(). + */ +#include "plugin.h" + +int main(int argc, char **argv) +{ + return nvme_main(argc, argv); +} diff --git a/src/nvme.c b/src/nvme.c index a91678dae8..60b4f0ef96 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -163,7 +163,7 @@ static void __shr_destructor free_plugins(void) } } -int main(int argc, char **argv) +int nvme_main(int argc, char **argv) { int err; diff --git a/src/plugin.h b/src/plugin.h index bb2e7a9f80..145a234715 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -88,6 +88,9 @@ int run_replacement_command(struct plugin *plugin, struct command *cmd, void register_extension(struct plugin *plugin); +/* The program: main() in main.c, and the fuzz tests, call it */ +int nvme_main(int argc, char **argv); + /* * Attach one file's worth of commands to a plugin (built-in or named). * commands must be a NULL-terminated array with static storage duration From 37f9b1ffed26da07c5524d1f6c6318723202b315 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:28 +0000 Subject: [PATCH 191/263] tests/cli: transfer no data for a command without a buffer The kernel maps the data buffer of a passthru command only when it has an address and a length. A command with a length but no buffer, which solidigm latency-tracking-log sends to get a feature, transfers no data. The mock copies the answer to address zero instead. Copy the data only to a buffer. Signed-off-by: Daniel Wagner --- tests/cli/libmock_nvme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cli/libmock_nvme.c b/tests/cli/libmock_nvme.c index e402c76085..2f43a60f2e 100644 --- a/tests/cli/libmock_nvme.c +++ b/tests/cli/libmock_nvme.c @@ -749,7 +749,8 @@ static int handle_passthru_ioctl(int instance, unsigned long request, cmd->result = resp.result; } - if (resp_data) { + /* like the kernel, which transfers no data without a buffer */ + if (resp_data && cmd->addr) { if (resp.data_len > cmd->data_len) { mock_dbg("ioctl response data_len %u exceeds caller buffer %u, truncating\n", resp.data_len, cmd->data_len); From 14529ccf350c2dab5c56a0738437acf46f8747f0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:49:40 +0000 Subject: [PATCH 192/263] tests/fuzz: write the crashing input without stdio The driver writes the input which crashed the target with fopen() from the signal handler. When the crash comes from a corrupted heap, as an overflow of a heap buffer can cause, fopen() allocates from that heap and fails, and the crash file stays empty. Write the input with open() and write(), and set up the file name before the run. Signed-off-by: Daniel Wagner --- tests/fuzz/fuzz-driver.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/fuzz/fuzz-driver.c b/tests/fuzz/fuzz-driver.c index 064dfc9505..b45906bde5 100644 --- a/tests/fuzz/fuzz-driver.c +++ b/tests/fuzz/fuzz-driver.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -55,18 +56,27 @@ static uint64_t rng(void) return rng_state; } -/* Keep the input which crashed the target */ +static char crash_path[256]; + +/* + * Keep the input which crashed the target. Without stdio: the crash may + * come from a corrupted heap, which malloc() would trip over again. + */ static void crash(int sig) { - char path[256]; - FILE *f; - - snprintf(path, sizeof(path), "fuzz-crash-%s", target); - f = fopen(path, "wb"); - if (f) { - fwrite(input, 1, input_size, f); - fclose(f); + size_t done = 0; + ssize_t n; + int fd; + + fd = open(crash_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + while (fd >= 0 && done < input_size) { + n = write(fd, input + done, input_size - done); + if (n <= 0) + break; + done += n; } + if (fd >= 0) + close(fd); signal(sig, SIG_DFL); raise(sig); } @@ -192,6 +202,7 @@ int main(int argc, char **argv) target = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; if (!strncmp(target, "fuzz-", 5)) target += 5; + snprintf(crash_path, sizeof(crash_path), "fuzz-crash-%s", target); rng_state = seed ? strtoull(seed, NULL, 0) : 1; if (!rng_state) From dd3130d92548f5b88cdd527d8fbda7a38383cb78 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:34 +0000 Subject: [PATCH 193/263] tests/fuzz: count an input which hangs the target as a crash An input which makes the target loop forever stops the test only when meson's timeout kills it, without the input. Give each input FUZZ_TIMEOUT seconds, 10 by default, and handle one which takes longer like a crash: write it to fuzz-crash- and abort. Signed-off-by: Daniel Wagner --- tests/fuzz/fuzz-driver.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/fuzz-driver.c b/tests/fuzz/fuzz-driver.c index b45906bde5..1d5f38ac3c 100644 --- a/tests/fuzz/fuzz-driver.c +++ b/tests/fuzz/fuzz-driver.c @@ -14,7 +14,8 @@ * * Usage: fuzz- * FUZZ_SECONDS sets the time for the mutations (default 2), FUZZ_SEED the - * seed (default 1). + * seed (default 1). An input which takes longer than FUZZ_TIMEOUT seconds + * (default 10) counts as a crash. */ #include @@ -81,13 +82,24 @@ static void crash(int sig) raise(sig); } +static unsigned int timeout = 10; + +/* An input which hangs the target is a crash too */ +static void hang(int sig) +{ + (void)sig; + crash(SIGABRT); +} + static void run(const uint8_t *data, size_t size) { if (size > FUZZ_MAX_INPUT) size = FUZZ_MAX_INPUT; memcpy(input, data, size); input_size = size; + alarm(timeout); LLVMFuzzerTestOneInput(input, input_size); + alarm(0); } static void load_seeds(const char *dir) @@ -190,6 +202,7 @@ int main(int argc, char **argv) { const char *secs = getenv("FUZZ_SECONDS"); const char *seed = getenv("FUZZ_SEED"); + const char *tmo = getenv("FUZZ_TIMEOUT"); unsigned long runs = 0; uint8_t buf[FUZZ_MAX_INPUT]; time_t end; @@ -218,6 +231,9 @@ int main(int argc, char **argv) signal(SIGBUS, crash); signal(SIGFPE, crash); signal(SIGILL, crash); + signal(SIGALRM, hang); + if (tmo) + timeout = strtoul(tmo, NULL, 0); if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); From c60974b6b5fc73b13aa02b7ff3ed09e019dc4ba7 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:42:46 +0000 Subject: [PATCH 194/263] tests/cli: answer the commands from a fuzz input To fuzz what nvme-cli does with the data a device returns, the mock can be built into the fuzz program with MOCK_FUZZ. There is no IPC server then: each command gets its answer from the bytes mock_fuzz_input() set. The first byte selects success, an NVMe status or a failed ioctl(), four more are the result, and then come the data the command reads. Like the kernel, a transfer larger than a controller takes fails, with EINVAL. And a run of commands gets at most 8 MiB of data, so a fuzzed log length doesn't have nvme-cli read and print gigabytes. Linked into the program, the mock interposes the libc functions as it does with LD_PRELOAD. Requests other than device commands, such as a password prompt or a route lookup, fail. Signed-off-by: Daniel Wagner --- tests/cli/libmock_nvme.c | 123 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/tests/cli/libmock_nvme.c b/tests/cli/libmock_nvme.c index 2f43a60f2e..6c8733dce0 100644 --- a/tests/cli/libmock_nvme.c +++ b/tests/cli/libmock_nvme.c @@ -171,6 +171,7 @@ struct __attribute__((packed)) ipc_response { uint32_t data_len; /* payload length (follows immediately) */ }; +#ifndef MOCK_FUZZ /* * This process may be running as real target-arch code (a cross build * under qemu-user), while nvme_mock_ipc.py on the other end of the socket @@ -205,6 +206,7 @@ static void ipc_response_decode(struct ipc_response *r) r->result = le32toh(r->result); r->data_len = le32toh(r->data_len); } +#endif typedef int (*orig_open_t)(const char *pathname, int flags, ...); typedef int (*orig_open64_t)(const char *pathname, int flags, ...); @@ -229,7 +231,9 @@ static orig_open64_t orig_open64; static orig_openat_t orig_openat; static orig_openat64_t orig_openat64; static orig_write_t orig_write; +#ifndef MOCK_FUZZ static orig_read_t orig_read; +#endif static orig_ioctl_t orig_ioctl; static orig_ioctl_t orig_ioctl_time64; static orig_close_t orig_close; @@ -295,7 +299,9 @@ static void init_orig_functions(void) orig_openat = (orig_openat_t)dlsym(RTLD_NEXT, "openat"); orig_openat64 = (orig_openat64_t)dlsym(RTLD_NEXT, "openat64"); orig_write = (orig_write_t)dlsym(RTLD_NEXT, "write"); +#ifndef MOCK_FUZZ orig_read = (orig_read_t)dlsym(RTLD_NEXT, "read"); +#endif orig_ioctl = (orig_ioctl_t)dlsym(RTLD_NEXT, "ioctl"); orig_ioctl_time64 = (orig_ioctl_t)dlsym(RTLD_NEXT, "__ioctl_time64"); orig_close = (orig_close_t)dlsym(RTLD_NEXT, "close"); @@ -313,6 +319,17 @@ static void init_orig_functions(void) orig_get_nprocs = (orig_get_nprocs_t)dlsym(RTLD_NEXT, "get_nprocs"); } +#ifdef MOCK_FUZZ +/* + * Built into a fuzz target (tests/fuzz/fuzz-device.c), the shim has no + * IPC server: the fuzz input answers every command instead. A "socket" + * is a /dev/null fd, so the callers can close it as usual. + */ +static int connect_ipc_socket(void) +{ + return orig_open("/dev/null", O_RDWR); +} +#else static int connect_ipc_socket(void) { const char *sock_path = getenv("MOCK_IPC_SOCK"); @@ -342,6 +359,7 @@ static int connect_ipc_socket(void) return fd; } +#endif const char *default_opts = "instance,cntlid,concat,ctrl_loss_tmo,data_digest," @@ -544,6 +562,109 @@ int __openat64_2(int dirfd, const char *pathname, int flags) return orig_openat64(dirfd, pathname, flags, 0); } +#ifdef MOCK_FUZZ +/* + * The kernel rejects a transfer larger than the controller takes. And a + * fuzzed log length must not have the mock serve, and nvme-cli print, + * gigabytes: the data of all commands of a run is limited too. + */ +#define FUZZ_MAX_TRANSFER (2 * 1024 * 1024) +#define FUZZ_MAX_TOTAL (8 * 1024 * 1024) + +static const uint8_t *fuzz_data; +static size_t fuzz_size; +static size_t fuzz_total; + +/* The bytes the following commands get their answers from */ +void mock_fuzz_input(const uint8_t *data, size_t size) +{ + fuzz_data = data; + fuzz_size = size; + fuzz_total = 0; +} + +/* The next bytes of the input, zeros once it is used up */ +static void fuzz_read(void *buf, size_t len) +{ + size_t n = len < fuzz_size ? len : fuzz_size; + + memcpy(buf, fuzz_data, n); + memset((uint8_t *)buf + n, 0, len - n); + fuzz_data += n; + fuzz_size -= n; +} + +/* + * Answer a command from the fuzz input: a control byte, the result + * dword, and then the data the command reads. Most control bytes mean + * success, the rest an NVMe status (from the next two bytes) or a + * failed ioctl(). Everything that is no device command fails, so the + * real function takes over where there is one. + */ +static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data, + struct ipc_response *resp) +{ + uint8_t ctl; + uint16_t sc; + void *buf; + + (void)ipc_fd; + (void)data; + memset(resp, 0, sizeof(*resp)); + + switch (req->type) { + case IPC_TYPE_IOCTL: + resp->data_len = req->req_len; + break; + case IPC_TYPE_RAW_IOCTL: + if (req->ioc_dir & IPC_IOC_READ) + resp->data_len = req->ioc_size; + break; + default: + resp->status = -1; + resp->errno_val = EOPNOTSUPP; + return NULL; + } + + if (resp->data_len > FUZZ_MAX_TRANSFER) { + resp->status = -1; + resp->errno_val = EINVAL; + return NULL; + } + if (resp->data_len > FUZZ_MAX_TOTAL - fuzz_total) { + resp->status = -1; + resp->errno_val = EIO; + return NULL; + } + fuzz_total += resp->data_len; + + fuzz_read(&ctl, sizeof(ctl)); + fuzz_read(&resp->result, sizeof(resp->result)); + resp->result = le32toh(resp->result); + if (ctl >= 0xf8) { + resp->status = -1; + resp->errno_val = EIO; + return NULL; + } + if (ctl >= 0xe0) { + fuzz_read(&sc, sizeof(sc)); + resp->sc_status = le16toh(sc) & 0x7fff; + return NULL; + } + + if (!resp->data_len) + return NULL; + buf = malloc(resp->data_len); + if (!buf) { + resp->status = -1; + resp->errno_val = ENOMEM; + return NULL; + } + fuzz_read(buf, resp->data_len); + + return buf; +} +#else static bool read_full(int fd, void *buf, size_t len) { size_t total_read = 0; @@ -630,6 +751,7 @@ static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data, return read_ipc_response(ipc_fd, resp); } +#endif ssize_t write(int fd, const void *buf, size_t count) { @@ -706,6 +828,7 @@ static int handle_passthru_ioctl(int instance, unsigned long request, mock_dbg("ioctl intercepted on instance %d (request 0x%lx)\n", instance, request); + ipc_fd = connect_ipc_socket(); if (ipc_fd < 0) { mock_dbg("ioctl could not connect to IPC socket\n"); From 3068da9302a2dec4914bbd7c8e4cfb7f150cf3ef Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Oct 2026 18:47:27 +0000 Subject: [PATCH 195/263] tests/fuzz: fuzz the data the device returns nvme-cli trusts what the device returns: the lengths and counts of log pages, the values it looks names up for, the strings it prints. A buggy device, or a fabrics target, can make it read past its buffers. Add a fuzz target which runs a command against the device mock, built with MOCK_FUZZ into the target, with the device answers taken from the input. The input selects one of about 130 commands which read and print something, from the core, the id, log, zns and fdp plugins and the vendor plugins, the output format and verbosity, and the controller or namespace device. The rest are the answers to the commands. The target links the objects of the nvme executable but main() and runs nvme_main() per input. Without ASan, it limits its address space, so a gigabyte allocation of a log length from the device fails instead of spending the run time on clearing it. The seeds include the inputs which found the bugs fixed before, so the fuzz suite replays them in every build. Signed-off-by: Daniel Wagner --- Documentation/TESTING.md | 17 +- .../fuzz/corpus/device/regress-ave-discovery | 1 + .../device/regress-ave-discovery-stdout | 1 + .../device/regress-changed-ns-list-leak | Bin 0 -> 130 bytes tests/fuzz/corpus/device/regress-dera-counter | Bin 0 -> 60 bytes tests/fuzz/corpus/device/regress-dera-status | Bin 0 -> 47 bytes tests/fuzz/corpus/device/regress-dispersed-ns | Bin 0 -> 129 bytes .../corpus/device/regress-dispersed-ns-leak | Bin 0 -> 64 bytes tests/fuzz/corpus/device/regress-fdp-status | Bin 0 -> 57 bytes tests/fuzz/corpus/device/regress-fdp-usage | Bin 0 -> 67 bytes .../corpus/device/regress-fdp-usage-stdout | Bin 0 -> 77 bytes .../fuzz/corpus/device/regress-host-discovery | 1 + .../device/regress-host-discovery-stdout | 1 + .../device/regress-ibm-persistent-event | Bin 0 -> 102 bytes .../corpus/device/regress-id-ctrl-rpmbbpwps | Bin 0 -> 491 bytes tests/fuzz/corpus/device/regress-id-uuid-leak | 1 + .../corpus/device/regress-lba-status-leak | Bin 0 -> 37 bytes .../fuzz/corpus/device/regress-memblaze-smart | 1 + .../device/regress-ocp-persistent-event-leak | Bin 0 -> 17 bytes .../device/regress-persistent-event-empty | Bin 0 -> 8 bytes .../device/regress-persistent-event-json-leak | 1 + .../corpus/device/regress-power-measurement | Bin 0 -> 29 bytes .../corpus/device/regress-pull-model-ddc-req | 1 + .../device/regress-reachability-misaligned | Bin 0 -> 52 bytes .../corpus/device/regress-reachability-slow | Bin 0 -> 42 bytes .../device/regress-reachability-table-slow | Bin 0 -> 17 bytes .../corpus/device/regress-seagate-pcie-leak | Bin 0 -> 9 bytes .../corpus/device/regress-seagate-temp-leak | 1 + .../corpus/device/regress-seagate-vs-smart | Bin 0 -> 20 bytes .../device/regress-solidigm-drive-info-leak | Bin 0 -> 5 bytes .../corpus/device/regress-solidigm-latency | 1 + .../corpus/device/regress-solidigm-smart-leak | 1 + .../corpus/device/regress-wdc-hw-rev-leak | 1 + tests/fuzz/corpus/device/seed-0 | Bin 0 -> 1031 bytes tests/fuzz/corpus/device/seed-1 | Bin 0 -> 1031 bytes tests/fuzz/corpus/device/seed-2 | Bin 0 -> 1031 bytes tests/fuzz/corpus/device/seed-3 | Bin 0 -> 1031 bytes tests/fuzz/corpus/device/seed-4 | Bin 0 -> 1031 bytes tests/fuzz/corpus/device/seed-5 | Bin 0 -> 1031 bytes tests/fuzz/fuzz-device.c | 232 ++++++++++++++++++ tests/fuzz/meson.build | 31 ++- 41 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 tests/fuzz/corpus/device/regress-ave-discovery create mode 100644 tests/fuzz/corpus/device/regress-ave-discovery-stdout create mode 100644 tests/fuzz/corpus/device/regress-changed-ns-list-leak create mode 100644 tests/fuzz/corpus/device/regress-dera-counter create mode 100644 tests/fuzz/corpus/device/regress-dera-status create mode 100644 tests/fuzz/corpus/device/regress-dispersed-ns create mode 100644 tests/fuzz/corpus/device/regress-dispersed-ns-leak create mode 100644 tests/fuzz/corpus/device/regress-fdp-status create mode 100644 tests/fuzz/corpus/device/regress-fdp-usage create mode 100644 tests/fuzz/corpus/device/regress-fdp-usage-stdout create mode 100644 tests/fuzz/corpus/device/regress-host-discovery create mode 100644 tests/fuzz/corpus/device/regress-host-discovery-stdout create mode 100644 tests/fuzz/corpus/device/regress-ibm-persistent-event create mode 100644 tests/fuzz/corpus/device/regress-id-ctrl-rpmbbpwps create mode 100644 tests/fuzz/corpus/device/regress-id-uuid-leak create mode 100644 tests/fuzz/corpus/device/regress-lba-status-leak create mode 100644 tests/fuzz/corpus/device/regress-memblaze-smart create mode 100644 tests/fuzz/corpus/device/regress-ocp-persistent-event-leak create mode 100644 tests/fuzz/corpus/device/regress-persistent-event-empty create mode 100644 tests/fuzz/corpus/device/regress-persistent-event-json-leak create mode 100644 tests/fuzz/corpus/device/regress-power-measurement create mode 100644 tests/fuzz/corpus/device/regress-pull-model-ddc-req create mode 100644 tests/fuzz/corpus/device/regress-reachability-misaligned create mode 100644 tests/fuzz/corpus/device/regress-reachability-slow create mode 100644 tests/fuzz/corpus/device/regress-reachability-table-slow create mode 100644 tests/fuzz/corpus/device/regress-seagate-pcie-leak create mode 100644 tests/fuzz/corpus/device/regress-seagate-temp-leak create mode 100644 tests/fuzz/corpus/device/regress-seagate-vs-smart create mode 100644 tests/fuzz/corpus/device/regress-solidigm-drive-info-leak create mode 100644 tests/fuzz/corpus/device/regress-solidigm-latency create mode 100644 tests/fuzz/corpus/device/regress-solidigm-smart-leak create mode 100644 tests/fuzz/corpus/device/regress-wdc-hw-rev-leak create mode 100644 tests/fuzz/corpus/device/seed-0 create mode 100644 tests/fuzz/corpus/device/seed-1 create mode 100644 tests/fuzz/corpus/device/seed-2 create mode 100644 tests/fuzz/corpus/device/seed-3 create mode 100644 tests/fuzz/corpus/device/seed-4 create mode 100644 tests/fuzz/corpus/device/seed-5 create mode 100644 tests/fuzz/fuzz-device.c diff --git a/Documentation/TESTING.md b/Documentation/TESTING.md index 900ba7b530..58ae125069 100644 --- a/Documentation/TESTING.md +++ b/Documentation/TESTING.md @@ -202,6 +202,13 @@ The parsers of untrusted input have fuzz targets in `tests/fuzz`: the command line parser (`argconfig`), the INI parser (`ini`), `nvme-cli.conf` (`global-config`) and `nvme-fabrics.conf` (`fabrics-conf`). +The `device` target fuzzes what nvme-cli does with the data a device +returns. It runs one of the commands which read and print something, from +the table in `tests/fuzz/fuzz-device.c`, against the device mock of +`tests/cli/libmock_nvme.c`, which answers every command from the input. +It needs a dynamically linked Linux build. Its seeds named `regress-*` are +inputs which found a bug. + Every build runs them as tests of the `fuzz` suite: each target replays its seeds in `tests/fuzz/corpus/` and then a few seconds of random mutations of them. The mutations come from a fixed seed, so a failure can be @@ -226,7 +233,15 @@ mkdir corpus && cp tests/fuzz/corpus/argconfig/* corpus/ -dict=tests/fuzz/argconfig.dict corpus ``` -The config targets use `tests/fuzz/config.dict`. The targets use the +The config targets use `tests/fuzz/config.dict`. The `device` target +reads log pages, so give it longer inputs, and let allocations of the sizes +a device reports fail instead of aborting: + +```bash +ASAN_OPTIONS=allocator_may_return_null=1 .build-fuzz/tests/fuzz/fuzz-device \ + -close_fd_mask=3 -max_len=16384 -timeout=10 corpus +``` + The targets use the libFuzzer interface, so AFL++ can run them too (`afl-clang-fast` with `-fsanitize=fuzzer`). Add an input which found a bug to the seeds of its target. diff --git a/tests/fuzz/corpus/device/regress-ave-discovery b/tests/fuzz/corpus/device/regress-ave-discovery new file mode 100644 index 0000000000..d0c1d97e22 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-ave-discovery @@ -0,0 +1 @@ ++ôûõ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-ave-discovery-stdout b/tests/fuzz/corpus/device/regress-ave-discovery-stdout new file mode 100644 index 0000000000..1c01b6f270 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-ave-discovery-stdout @@ -0,0 +1 @@ ++ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-changed-ns-list-leak b/tests/fuzz/corpus/device/regress-changed-ns-list-leak new file mode 100644 index 0000000000000000000000000000000000000000..99eb17718f99d8c2a79440c08984205d230f3355 GIT binary patch literal 130 qcmb0zRa5^@O<)QF7GM%Y=zxe)AZCC7i&G~+BBrL+AmW0EsR#h3lUKw5 literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-dera-counter b/tests/fuzz/corpus/device/regress-dera-counter new file mode 100644 index 0000000000000000000000000000000000000000..68ba2a23b90211de2ca980b77e116761df577008 GIT binary patch literal 60 jcmb=Z00TEH04z~jnweq+QB%Rr00A<4zcB1&eEtOhczYDT literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-dera-status b/tests/fuzz/corpus/device/regress-dera-status new file mode 100644 index 0000000000000000000000000000000000000000..7700d08f5d636086677e75848b45d513f52715cf GIT binary patch literal 47 ncmb=ZkgGrg%Ipk4(7?bTlUJ6Ts#{!|R9soS_Y1>b#^+xEpTH6; literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-dispersed-ns b/tests/fuzz/corpus/device/regress-dispersed-ns new file mode 100644 index 0000000000000000000000000000000000000000..e6baed08653a35b7c471ce52595db3288bb1c1d7 GIT binary patch literal 129 hcmY#qU}R-rIIt=-G&Jq!e|iCioP6WkGu|-(0RS8bZ~OoN literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-dispersed-ns-leak b/tests/fuzz/corpus/device/regress-dispersed-ns-leak new file mode 100644 index 0000000000000000000000000000000000000000..bd574ef94be86839b8b9806440c897092bcdb4cd GIT binary patch literal 64 zcmY#aXJBB>XJE`RzCGg|14A?egBpVo!~g#>|1kjfR^zgunhhzJI$xM;*>)N;Ft{=> F0078_AFKcX literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-fdp-status b/tests/fuzz/corpus/device/regress-fdp-status new file mode 100644 index 0000000000000000000000000000000000000000..338759fa225e15bd675fe926951a8e2881330ed5 GIT binary patch literal 57 gcmXqKW?yt4TS)E5XAsyr==7?#6rz8Q*{6RH^czuFQ5wGdVqS7K`BBW06NJWIsgCw literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-id-ctrl-rpmbbpwps b/tests/fuzz/corpus/device/regress-id-ctrl-rpmbbpwps new file mode 100644 index 0000000000000000000000000000000000000000..ee71dcc915c6ef9805771aecd10a53f1a53fdb00 GIT binary patch literal 491 zcmZQzVqjo2WngJzYvH{SsA>_I%Jnc~*VXBjR~jCk5ID1eL$U8>W_NP8>vV2&L*Ffl zUwvM*u)dpE=VH*=y0_!8rj@?hZQhV?QQs37Sf{os<|*9_oqc-u=5>KHlv$&XGq5s% zz?9^Gn+LoC`=_5N3#!?Wa;fu$xt49G@$DJ!?0Am{J)f7&A9Cm;dqhmk%oz_?-p?>S z`RtaCrOn3r>9rh08ulLx0GWJg z(Ovy!nV3>X#e{z}h1rMD5^=n$5ct!WD1__1En}Q3cpZ>W% ZM}qy*EgQ#I*B8ikm{fh*VjaC^3jq12`rQBk literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-id-uuid-leak b/tests/fuzz/corpus/device/regress-id-uuid-leak new file mode 100644 index 0000000000..cd6ff2a11a --- /dev/null +++ b/tests/fuzz/corpus/device/regress-id-uuid-leak @@ -0,0 +1 @@ +�ÿ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-lba-status-leak b/tests/fuzz/corpus/device/regress-lba-status-leak new file mode 100644 index 0000000000000000000000000000000000000000..5cb821899cb0b0f29fb5c53681ae5871ff3bd58f GIT binary patch literal 37 ocmb0&)Me1MX4GY5U|?X9|If^j31Km0S~JYHmX~K>n9UFk09xw>ZvX%Q literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-memblaze-smart b/tests/fuzz/corpus/device/regress-memblaze-smart new file mode 100644 index 0000000000..3d47050a09 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-memblaze-smart @@ -0,0 +1 @@ +ï:+ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-ocp-persistent-event-leak b/tests/fuzz/corpus/device/regress-ocp-persistent-event-leak new file mode 100644 index 0000000000000000000000000000000000000000..229d5525bcbe463ed89c2867cf8484bc98535116 GIT binary patch literal 17 YcmZ=z%qz=H)h#Yf%E?TzV$f#*05i%3F#rGn literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-persistent-event-empty b/tests/fuzz/corpus/device/regress-persistent-event-empty new file mode 100644 index 0000000000000000000000000000000000000000..913d29bd367c7db56f2bb31334cf83642a54481c GIT binary patch literal 8 PcmbQkuy^kl#`j+U6RZU9 literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-persistent-event-json-leak b/tests/fuzz/corpus/device/regress-persistent-event-json-leak new file mode 100644 index 0000000000..7cbe90772b --- /dev/null +++ b/tests/fuzz/corpus/device/regress-persistent-event-json-leak @@ -0,0 +1 @@ +ŽQ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-power-measurement b/tests/fuzz/corpus/device/regress-power-measurement new file mode 100644 index 0000000000000000000000000000000000000000..271dbbfd57f37fee35e4d692cfd9402ce125cfeb GIT binary patch literal 29 acmdO8RJLGX$TSpWfCEEjpa>8!F#rG|h5@4h literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-pull-model-ddc-req b/tests/fuzz/corpus/device/regress-pull-model-ddc-req new file mode 100644 index 0000000000..d2664b1385 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-pull-model-ddc-req @@ -0,0 +1 @@ +, :u :u£o \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-reachability-misaligned b/tests/fuzz/corpus/device/regress-reachability-misaligned new file mode 100644 index 0000000000000000000000000000000000000000..525eb06c8c3c9e36b7432569abcd1923de0d291e GIT binary patch literal 52 pcmdN;oCF2t|6XMNPhrqC|N5TcD-ynzL0VQz3NGXq0LYED5V05f(4bpQYW literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-seagate-pcie-leak b/tests/fuzz/corpus/device/regress-seagate-pcie-leak new file mode 100644 index 0000000000000000000000000000000000000000..eb681b65a39771c285e470f591857cbf39fc970c GIT binary patch literal 9 Qcmd1FPfrzKU|`S#01Wy8<^TWy literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-seagate-temp-leak b/tests/fuzz/corpus/device/regress-seagate-temp-leak new file mode 100644 index 0000000000..27ae57ddd3 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-seagate-temp-leak @@ -0,0 +1 @@ +ê' \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-seagate-vs-smart b/tests/fuzz/corpus/device/regress-seagate-vs-smart new file mode 100644 index 0000000000000000000000000000000000000000..f091bc906f1fdd683d620740bd1b9998f7c4d2e3 GIT binary patch literal 20 YcmaD;U;jVTngN6v8L|?~Z5f!N0bLdc4gdfE literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-solidigm-drive-info-leak b/tests/fuzz/corpus/device/regress-solidigm-drive-info-leak new file mode 100644 index 0000000000000000000000000000000000000000..440782a16fa4c628364f19b73a1d35d8c412cadd GIT binary patch literal 5 McmX^0kAZ<100;5`%m4rY literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/regress-solidigm-latency b/tests/fuzz/corpus/device/regress-solidigm-latency new file mode 100644 index 0000000000..019f559fad --- /dev/null +++ b/tests/fuzz/corpus/device/regress-solidigm-latency @@ -0,0 +1 @@ +ÆŒ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-solidigm-smart-leak b/tests/fuzz/corpus/device/regress-solidigm-smart-leak new file mode 100644 index 0000000000..d56a799494 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-solidigm-smart-leak @@ -0,0 +1 @@ +ÂÊþ:o \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-wdc-hw-rev-leak b/tests/fuzz/corpus/device/regress-wdc-hw-rev-leak new file mode 100644 index 0000000000..e6c9eb6d3d --- /dev/null +++ b/tests/fuzz/corpus/device/regress-wdc-hw-rev-leak @@ -0,0 +1 @@ +W㸠\ No newline at end of file diff --git a/tests/fuzz/corpus/device/seed-0 b/tests/fuzz/corpus/device/seed-0 new file mode 100644 index 0000000000000000000000000000000000000000..a3ec9544225a204671723f8826dfe9d82706b2da GIT binary patch literal 1031 zcmV+i1o-;^00000001J9*v-QbK^}g>b9km<$u$^!2jtabZkxlz0J3sKxXn73@fzk) z2L%SEiURQAVXrs%Y`F)AIKy!8evLa=JFPgvb@NK?1!~snj_jlvFQNXq+)Ez5Rpq$( zwt(;VY=PA2t1wMSmigg-*zkd5mf8q!^WL|x{ z2Y3rkwAuTemy~3sD=UCH`vb)@itF5rJ7jGOhk0b&7I$qUm<1D&e_7ik^S;>rpzJ<0r*YbhObDy}^!zStUX>GojG_=@P!ON>CadQcfP zrcnw80?GUAOSza)b7W9v5D@86_?h^096&e7p2zy+ir%nzsarxWiZxC$KU@^iM;80Q zau=qPq*m}cWA{!9Q!i1<*p%e!O+Hl_i=33sm=``c1ZeBdr8o@h62pR};9e+# z#LD6{PfW2)*^OT+v$MQ*m=)U}<(u_`l3*<-Ky5eik+{Lg4Sud^=C|Verl8Ze=RyQ*a=k=zR4y9&VY4Xy_iP zOGiIozmEa+Fo$rI1qYz{n?BV-G%QFZiZew|mBB#Ou5!xU&)WGbi(7qb+aJU=lHp=B zNY5MR&ITTlzXOX)_pHLX`KBThfM1f%Pj7&&Tf&D@03UI;b6HM`W>(0%u9AKnrRpK+ B1h@bI literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/seed-1 b/tests/fuzz/corpus/device/seed-1 new file mode 100644 index 0000000000000000000000000000000000000000..2ea8258d480fc1a1313f2f554563ce34384aad5b GIT binary patch literal 1031 zcmV+i1o-!oNN1~ay;l0$lf-?NCyR+r@zY%`?zCWY)rb?j&Xt<##qQ~!L$VJ#K#1hcPngUtrL(H{l z->-{JCU(_Ad@FZ?3q^t%zL#tfS`nym1uB8H@+q(vW}>i0noTu_H9X=~L=h9R)aiw& zUU!0yybLQ$r@XfR(2hl0oV`0_k7g`;%0k_oRJS-n_MEgn*r^BF=H8!YQSEN-!avDG zFcP4GoN-sF)#sV%d~Q>oR62;nTvUCd=su17juNs)`PfVL>4jcBa=&=K~3e4~Ewv3hqY7 z9&Q&f2Y;g>zDKXo*fFVcV^pC;_(G(jK0XhY@5z@eS#6rKkE4b759eBpX^Ato;fZYP zsS&nBzMQ!Dz!E?$`4}b->onSJ+6xV%7wOQ0cz|9R{7?!YhzN12AmwGlwdUb+2E2wv z7eLHY6-@}-Vh+1PP`n{R%VEwP+pSF)Y}Y=3jx<#4R)WZ6^yQU%8X>39a)$hmxY*T{ zvxSKV<@(h9O1>yFUSow*7-(1^lMo9`(9EWYP-;w3T18X8zlDGTh94$T>bg_NQ<4#L z`bT0Rx;_+ilF>=f*j%?=_AhJBOT{y@k+vy#77T>bIPq+VxEcA5HDh=D2xB|xt@ zkRs?DFT`o^x}FTr8TyN^M7A3=LJ^^ohNKkQ63f{)q}D8g+-?PzUgp?*wa!R5K(AWR@L1;4qzlgCxbpbH-OtP?~ z0g4{dO@rh9zfkzbi=~qOjYx)Rity3x^n_@dp_EQ@O(B7CmLrVC`uQn9q5}SGys3~0 zUTbDZ@2KU#s|4m3>=)PpVnlXD%gA4%zuJ4mR%3L{9eY|OY9$2zEYLz;+aQ+7N%Q|{ zLhyr0y=$;WZdJzVd^fk$ee?Kcw`>;>AvE_UI=TiLKqq>}^BHEQxi8fK7HypL{tb*b zh-_F4@}~Ih8oZ6IYt^g09{xkGM=g5J%e31J$u_N|7TseXt8z=Of`8{^9h=&E8zy3% z=d&{^g+Xe*S;ds95dd_q9Y0ilA^z!spi+I)X+; zv_EMjAwe-{kD;08^$v*Dn}R0tX+=kRvQ9*OHh*53cs*7vo5C)=;gqp+h$aq!Q-{MF B2N(bV literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/seed-2 b/tests/fuzz/corpus/device/seed-2 new file mode 100644 index 0000000000000000000000000000000000000000..332a425c71a246e5ba2dcc0067800ff3c5ae6685 GIT binary patch literal 1031 zcmV+i1o-UlFJJi(HG2*v+|tuapO#PI9gxCll$EB(34o zH+$F3*~C>{<4Pze*~?+oaAzL+nI%O_s;|tWn)3pQ^8)M8q#+Ta87?`;qAPr(4Z=(Xu}6&YwLSkMmpKC>_QM z>az4p*!llX=Rr&Z-s6NQ8cD{ICex}8#x$iMt%)*vPWU}_u82B#D7 zE_K9Us({SUN$Mvj_=eLP@<9Z4WzV9tyvpf1iLzw<0gKH)`)ueasV!HjJrl->?~e5; zEMS!dgEwtF%L?y=xiGVVu$-s=i4#9%$9Eo)qzq#gj~JxTdJFo5J;wtD^xIE&N4aV$ zm?Hd-wbN0>h@x`+fX!-+Dzas=W6m>v(MMeECPI-%*eu7mpB1}YR_!E0K|onzN04^| zCiEfoKshoJ&6J6rGK+1u-#wEfjdf(UG8Lfj6DP^44hO$yVQQ`;mzW`}iHj2LJ=tL+ zNi(Uq{AF9e)-5?qwIo@6iAolO(@ZtAb_Ghz&!3kXonOImL7ol`)W}dM&mr{eq0$;0 z+HazkKE5@9fn^`*=lr!c(eh%c?1UoFx0FG-0Jk2`GyKDlV5xhHojXJcqbjSb>y3ar z+G+Pj#Hnj$MSdBp)YTy`j|6nU3w<_Z(z=P#>{jSL7!$3(3fOGbaWM2On1C?Yf?|bS zGCN$T;FkNr#{3Xh=M8lW)toKlCEDolNq7sDfDrxN)sSTu{$|UA*OH}8WJKp$c=qoM zjq>Q@dJMdSP08QS`83jt6O2$+?MvK*A_;NOUCh4; z2mMfLzbWI63bdB9siEJ7Y%lreGCLiXA^DbpAGkzyGRO{G_H%hJA!dFvw7@Qvi+bXV%%>Hq#Rq{$m#+? zz>2*MPGAF5R!^4T$lcF*1067Vu;@H?9do;{&CKDK3HHJ>K9{Y<{N;amW=w69Kae=Dpd2mM}ejaT2z zgMCr(njDp-O3#d`Mr{JuPrw$VeI0-}-kZOqzxn;`LvLTmI}I9`gM)%8Axi8F`r;8c z0IUyeySLf}5Do$eiB?s}1e^koHh42cOO%X-K;t_uHDu!tJ@}5Za|u*aXdVQRFM*>M z!Y?;DEluan84t@);JGCb)o~^}3%^PBSPqsJaWl(0sxKZ6GY!AG9TxM+&%QYDNwz?S zY~Me)2)RJRF;iJvbi?25sNS7n-K}C4ZSFqQ)P3Mr=`EV39X%4o%x~gGh)wJG?p49m zUTAb(T2N*6c!C68AWbVxks#lUwz?)Nb)}#tA}JKmoIpHVrBEwI+j&nDZ6}Oc;&bX7 z*e9=05w9d_slg9Ki1hz zQ;=sqT*VAK@k+9h0Wui*A~{}x<3uDWIulQlf`aDbikau#ipp->-*A+Jc`k$9TQY9V z5=JyS!z3cbH3cbrTrUjBTos%=t+L)W7I37eGMZN^ld#s+tF-*>1UFFQdW{L@4cc9Q zk69vo5rR>wz3!2#|4*7xlv;mR}9mxn9y*a6GW3sf$h77IDJgTQMmjaqiU% z^j(ow_|7bmeR=|uJL2G+4{@g{f;IeR{&ycxLnHkwR3R?T>AjwZPCJdXZFhBojZZ6u zoq_aEm)FTONUSHW0nqzb9&BN@r-QsLp5t(H(1>we)iezixEmOWV3XEZ3PuF5t!ojt|VM5c%zlI&#@jaaH5K8e_#>P#ImyraH{*1s;3pcg8f*=8CjDQJQt(G*17m!*a!tLqO{7`b B@b&-z literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/seed-4 b/tests/fuzz/corpus/device/seed-4 new file mode 100644 index 0000000000000000000000000000000000000000..351e015cb31f6f872446755f4ba74ad4208f0649 GIT binary patch literal 1031 zcmV+i1o-;^1poj5005L@P}#srEE#<+awl~gi64nkQ2x?>^R18XqF1?;QID6*c2az> zWYUS~H!3}eGuxQ%J`KlG=bpk3RBQ1ESX^A0nBl44Xf?^^+WtxCIZ$Q(vSO>oECOSu z=J)kkn#jkU*5o)l5Yn7cVl=hiOBinf&RG}EX!9o=h|0Us{4Ky&Bw%k-ir=I0vxWIa z@$fYEIB-}f0um-?4}kXhVTljO;Wr5G6c41wE@|bjY1>kr)ju(9+R{0Sj*0$wz7-+&r-9EWWgGbW4?&E>eQ|sAayoLfF<%%KM7pO(PqC|0Dl&*$HxZx{>KqieDhM`2&eF#YCUa z5Xg2&1EoZ{_JFYkkY*eiuTq+xqq4A)?c;J6oPUQ`mfx)lFe?t79nK0Kj)zXb$TKO3 zCOX|W7Jypiu-A}pM4ci@lg~d9+n7TR?)?RBocBoAd2Daj&k-*+#H|SKqx;gqZS>h{ zTJTze=_UOw$~ijE@ef@Q-f~jVH#k8ju;lu*g<%_due;{%$eaLpPeaDbvr7K@gjE?oK7ErNEq`0Ir16-d@vP>p29K$`=@`XcbVI-xNF&V^ zXtelY2YQvbA;!dy_+)*}*Tp-62Vl()X_*y2tP19I6-%57Si}ku64MKnPFhYEiFlq{ z*;CuZE90PA;D$d_nmhbIqQ$c}PxSsy)QM8IO_l+udqCf)$T}tXJ=`f3LS{1~D$tGh z>dV^JpAt}F=(IK{334}P${e%hPsKR6qe_DN%cgQx6%i8BJ06C1w$qMv0-5tFb#96r zF#|qMHiVl?=uSiHSwu+;2j2pq$J+XE3N#Z?bE!=oKdjyyF$XcC=OUk&ui~ru25|bi z2ah_`c`Vk5^XdXQBM=1NBU0&;6@-dHF=YaaM_NMsieleqhlhxFM;0!^dXQuy=AP`; B_?G|x literal 0 HcmV?d00001 diff --git a/tests/fuzz/corpus/device/seed-5 b/tests/fuzz/corpus/device/seed-5 new file mode 100644 index 0000000000000000000000000000000000000000..3e1b3cb3b8cbec7099e339cec89121aa2145d703 GIT binary patch literal 1031 zcmV+i1o-;_1ONa4001?H280dRQ6)H0WeVYFy49C?)PUj05X`U$A&zN_V~a(X3pX)M zv|;s3=!6CCkwh?wg}sR5DLNK4dMXX5*JtBM118NG*76FqmTr0UEb-Adkay3i?;H5t zsb}9g5Fl28d47YbUT)Fbl|R_?aYFHDTVnprkUC)Ip4}TRuilxV&R7!$Yv7iC5BkG( z<{Y4U+ z*z2|q1#p8hWGDVDI}~LKEKvb;iO`;gDGAnWIMDAwtc3XWa4^;in%B2rX=Z|LMR8V; z>;@Eg)w{oIDdlb{)r*41fx3X7EJXcjzk5mbSnsuT-erwoNj+PNi_hY>w1+uE1=^?* zL$%;zDMSCo%$7iX1Sryb95^N%Vh$`37e-II zc=9b50uTl+M=`i{X10ogMY8$a>#jqNV%{3HWp+Ihy1P&!ubKv-v|ytSN?66H-R|cE zv7T^Il>)VY-hY5+mQa@1On>s#*Ub1!}wV&XS@m| z?eNY?gtMG!(xocNk$AaFmCyQ^LA|XA$Khm(@Rw=nB~mMNWRv{u>W=33sUU#mq!gN# z^qpivWPQ2_&7i$XDA%IzL}NxDLH7dq9@VSt%hwxs@+NmZ$UF);6dTa)7`h3Rs2vMH zY9-KE9SqmZ=wtcIouAS%r_}FMP1gkQ&_)1k-;VhI6>@t=Es!JS6MTAdT^&Ft83Ih=UmSj9-U z2OF4&^9?JX!8yg@iE!CVY-XPU5oIU4mNnPud8_VZei>-$$ffHl=C-JQH>Xecj>pld B1*HH0 literal 0 HcmV?d00001 diff --git a/tests/fuzz/fuzz-device.c b/tests/fuzz/fuzz-device.c new file mode 100644 index 0000000000..e6d50b70bf --- /dev/null +++ b/tests/fuzz/fuzz-device.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of nvme-cli. + * Copyright (c) 2026 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +/* + * Fuzz what nvme-cli does with the data a device returns: run a command + * which reads and prints something against a mocked controller whose + * every answer comes from the fuzz input. The device mock is + * tests/cli/libmock_nvme.c built with MOCK_FUZZ, linked into this + * program, so its open()/ioctl() replace the libc ones. + * + * The input is the command (one byte, an index into the table below), + * the variant (one byte: output format, verbose, controller or namespace + * device) and then the answers of the device, see ipc_call() in the + * mock. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "args.h" +#include "fuzz.h" +#include "plugin.h" + +/* the fuzz backend of libmock_nvme.c */ +void mock_fuzz_input(const uint8_t *data, size_t size); + +#define MAX_ARGS 8 + +/* + * Commands which read from the device and print what they got. Nothing + * which writes a file, waits or loops on the device, or needs a value + * which can't have a default. The seeds select a command by its index: + * add new commands at the end. + */ +static const char *const commands[][3] = { + { "id", "ctrl" }, + { "id", "ns" }, + { "id", "ns-granularity" }, + { "id", "ns-list" }, + { "id", "ctrl-list" }, + { "id", "nvm-ctrl" }, + { "id", "nvm-ns" }, + { "id", "primary-ctrl-caps" }, + { "id", "secondary-ctrl-list" }, + { "id", "ns-ind" }, + { "id", "ns-descs" }, + { "id", "nvmset" }, + { "id", "uuid" }, + { "id", "iocs" }, + { "id", "domain" }, + { "id", "endgrp-list" }, + { "log", "supported-pages" }, + { "log", "smart" }, + { "log", "ana" }, + { "log", "fw" }, + { "log", "endurance" }, + { "log", "effects" }, + { "log", "error" }, + { "log", "changed-ns-list" }, + { "log", "changed-alloc-ns-list" }, + { "log", "predictable-lat" }, + { "log", "pred-lat-event-agg" }, + { "log", "persistent-event" }, + { "log", "endurance-event-agg" }, + { "log", "lba-status" }, + { "log", "resv-notif" }, + { "log", "phy-rx-eom" }, + { "log", "self-test" }, + { "log", "fid-support-effects" }, + { "log", "mi-cmd-support-effects" }, + { "log", "media-unit-stat" }, + { "log", "supported-cap-config" }, + { "log", "mgmt-addr-list" }, + { "log", "rotational-media-info" }, + { "log", "dispersed-ns-participating-nss" }, + { "log", "reachability-groups" }, + { "log", "reachability-associations" }, + { "log", "host-discovery" }, + { "log", "ave-discovery" }, + { "log", "pull-model-ddc-req" }, + { "log", "power-measurement" }, + { "log", "sanitize" }, + { "get-feature" }, + { "zns", "id-ctrl" }, + { "zns", "id-ns" }, + { "zns", "changed-zone-list" }, + { "fdp", "configs" }, + { "fdp", "usage" }, + { "fdp", "stats" }, + { "fdp", "events" }, + { "fdp", "status" }, + { "ocp", "smart-add-log" }, + { "ocp", "latency-monitor-log" }, + { "ocp", "fw-activate-history" }, + { "ocp", "unsupported-reqs-log" }, + { "ocp", "error-recovery-log" }, + { "ocp", "device-capability-log" }, + { "ocp", "tcg-configuration-log" }, + { "ocp", "hardware-component-log" }, + { "ocp", "persistent-event-log" }, + { "solidigm", "smart-log-add" }, + { "solidigm", "vs-smart-add-log" }, + { "solidigm", "garbage-collect-log" }, + { "solidigm", "market-log" }, + { "solidigm", "latency-tracking-log" }, + { "solidigm", "vs-fw-activate-history" }, + { "solidigm", "log-page-directory" }, + { "solidigm", "temp-stats" }, + { "solidigm", "vs-drive-info" }, + { "wdc", "vs-smart-add-log" }, + { "wdc", "vs-nand-stats" }, + { "wdc", "vs-fw-activate-history" }, + { "wdc", "log-page-directory" }, + { "wdc", "vs-drive-info" }, + { "wdc", "vs-temperature-stats" }, + { "wdc", "capabilities" }, + { "wdc", "vs-pcie-stats" }, + { "wdc", "get-latency-monitor-log" }, + { "wdc", "get-error-recovery-log" }, + { "wdc", "get-dev-capabilities-log" }, + { "wdc", "get-unsupported-reqs-log" }, + { "wdc", "vs-cloud-log" }, + { "wdc", "vs-hw-rev-log" }, + { "wdc", "vs-device-waf" }, + { "wdc", "cu-smart-log" }, + { "micron", "vs-temperature-stats" }, + { "micron", "vs-pcie-stats" }, + { "micron", "vs-nand-stats" }, + { "micron", "vs-smart-ext-log" }, + { "micron", "vs-drive-info" }, + { "micron", "log-page-directory" }, + { "micron", "vs-fw-activate-history" }, + { "micron", "vs-smart-add-log" }, + { "micron", "vs-device-waf" }, + { "micron", "smart-log" }, + { "intel", "id-ctrl" }, + { "intel", "lat-stats" }, + { "intel", "market-name" }, + { "intel", "smart-log-add" }, + { "intel", "temp-stats" }, + { "seagate", "vs-temperature-stats" }, + { "seagate", "vs-log-page-sup" }, + { "seagate", "vs-smart-add-log" }, + { "seagate", "vs-pcie-stats" }, + { "seagate", "vs-fw-activate-history" }, + { "memblaze", "smart-log-add" }, + { "memblaze", "get-pm-status" }, + { "sfx", "smart-log-add" }, + { "sfx", "lat-stats" }, + { "sfx", "query-cap" }, + { "ibm", "crit-log" }, + { "ibm", "vpd" }, + { "ibm", "persist-event-log" }, + { "amzn", "stats" }, + { "dapustor", "smart-log-add" }, + { "dera", "smart-log-add" }, + { "huawei", "id-ctrl" }, + { "shannon", "smart-log-add" }, + { "ssstc", "smart-log-add" }, + { "toshiba", "vs-smart-add-log" }, + { "transcend", "healthvalue" }, + { "virtium", "show-identify" }, + { "ymtc", "smart-log-add" }, + { "resv", "report" }, +}; + +#define NR_COMMANDS ARRAY_SIZE(commands) + +static struct nvme_args defaults; + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define __SANITIZE_ADDRESS__ 1 +#endif +#endif + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ +#ifndef __SANITIZE_ADDRESS__ + /* + * nvme-cli allocates what a log length from the device says. Let a + * gigabyte allocation fail instead of spending the time on clearing + * it. ASan reserves terabytes of address space, so not with ASan. + */ + struct rlimit rl = { 1024UL << 20, 1024UL << 20 }; + + setrlimit(RLIMIT_AS, &rl); +#endif + (void)argc; + (void)argv; + defaults = nvme_args; + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *argv[MAX_ARGS + 1]; + uint8_t variant; + int argc = 0, i; + + if (size < 2) + return 0; + + argv[argc++] = "nvme"; + for (i = 0; i < 3 && commands[data[0] % NR_COMMANDS][i]; i++) + argv[argc++] = (char *)commands[data[0] % NR_COMMANDS][i]; + + variant = data[1]; + argv[argc++] = variant & 4 ? "/dev/nvme0n1" : "/dev/nvme0"; + if (variant & 1) + argv[argc++] = "--output-format=json"; + if (variant & 2) + argv[argc++] = "--verbose"; + argv[argc] = NULL; + + nvme_args = defaults; + mock_fuzz_input(data + 2, size - 2); + nvme_main(argc, argv); + mock_fuzz_input(NULL, 0); + + return 0; +} diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index f03362c569..22f79e13c0 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later # -# Fuzz targets for the parsers of untrusted input: the command line and -# the config files. Without -Dfuzzing=true each target runs as a test, -# with the seeds in corpus/ and a few seconds of mutations of -# them (FUZZ_SECONDS, FUZZ_SEED). With -Dfuzzing=true and clang, the -# targets are built for libFuzzer: +# Fuzz targets for the parsers of untrusted input: the command line, the +# config files and the data from the device. Without -Dfuzzing=true each +# target runs as a test, with the seeds in corpus/ and a few +# seconds of mutations of them (FUZZ_SECONDS, FUZZ_SEED). With +# -Dfuzzing=true and clang, the targets are built for libFuzzer: # # CC=clang meson setup fuzz -Dfuzzing=true -Db_sanitize=address,undefined # ninja -C fuzz @@ -43,21 +43,40 @@ if want_fabrics } endif +# The device mock answers every command with the fuzz input. It needs +# dlsym(RTLD_NEXT), so a dynamic Linux build. +if want_nvme and have_mock_nvme + fuzz_targets += { + 'device': { + 'sources': ['fuzz-device.c', '../cli/libmock_nvme.c'], + 'objects': nvme_exe.extract_objects(sources), + 'c_args': ['-DMOCK_FUZZ'], + 'deps': link_deps, + 'link_args': link_args_list, + }, + } +endif + foreach name, t : fuzz_targets if get_option('fuzzing') executable( 'fuzz-' + name, t['sources'], + objects: t.get('objects', []), + c_args: t.get('c_args', []), include_directories: include_directories('../../src'), dependencies: t['deps'], - link_args: ['-fsanitize=fuzzer'], + link_args: t.get('link_args', []) + ['-fsanitize=fuzzer'], ) else exe = executable( 'fuzz-' + name, t['sources'] + ['fuzz-driver.c'], + objects: t.get('objects', []), + c_args: t.get('c_args', []), include_directories: include_directories('../../src'), dependencies: t['deps'], + link_args: t.get('link_args', []), ) test( 'nvme-cli - fuzz-' + name, From 7845f6158f1e45bc8d6f6dc0cfbf751e5fcef4ac Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:29:37 +0000 Subject: [PATCH 196/263] tests/fuzz: build fabrics-conf only with the in-tree libnvme The fabrics-conf target uses libnvme internals (config-ini.h and libnvme_test_dep), which a build against an installed libnvme (-Dlibnvme=disabled) doesn't have, so meson setup fails. Fixes: 23a5a9d9d491 ("tests: fuzz the parsers of the command line and the config files") Signed-off-by: Daniel Wagner --- tests/fuzz/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 22f79e13c0..541f10c14d 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -34,7 +34,9 @@ fuzz_targets = { }, } -if want_fabrics +# The target parses with the libnvme internals, so it needs the in-tree +# libnvme. +if want_fabrics and want_libnvme fuzz_targets += { 'fabrics-conf': { 'sources': ['fuzz-fabrics-conf.c'], From acb99d936132bea6f814180ed7a968bad61e18d2 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:29:37 +0000 Subject: [PATCH 197/263] mem: allocate a page for a zero length libnvme_alloc() of zero bytes passes the zero on to posix_memalign(), where its result is implementation-defined, e.g. for a log page the device says is empty. valgrind reports it as an error. Allocate at least a page, as libnvme_alloc_huge() does. Found by fuzzing the device data. Fixes: 691b1f43a114 ("mem: move memory allocator helpers") Signed-off-by: Daniel Wagner --- libnvme/src/nvme/mem-freebsd.c | 7 ++++++- libnvme/src/nvme/mem-linux.c | 3 +++ libnvme/src/nvme/mem-win.c | 7 ++++++- libnvme/tests/mem.c | 6 ++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/libnvme/src/nvme/mem-freebsd.c b/libnvme/src/nvme/mem-freebsd.c index d2b5dfc2a2..d11bceebc2 100644 --- a/libnvme/src/nvme/mem-freebsd.c +++ b/libnvme/src/nvme/mem-freebsd.c @@ -30,9 +30,14 @@ __shr_public void *libnvme_alloc(size_t len) { - size_t _len = round_up(len, 0x1000); + size_t _len; void *p; + /* a zero size is implementation-defined, get a page */ + if (!len) + len = 1; + _len = round_up(len, 0x1000); + if (posix_memalign((void *)&p, getpagesize(), _len)) return NULL; diff --git a/libnvme/src/nvme/mem-linux.c b/libnvme/src/nvme/mem-linux.c index 60aa010c3e..34408c0656 100644 --- a/libnvme/src/nvme/mem-linux.c +++ b/libnvme/src/nvme/mem-linux.c @@ -53,6 +53,9 @@ __shr_public void *libnvme_alloc(size_t len) size_t _len; void *p; + /* a zero size is implementation-defined, get a page */ + if (!len) + len = 1; if (!round_up_checked(len, 0x1000, &_len)) return NULL; diff --git a/libnvme/src/nvme/mem-win.c b/libnvme/src/nvme/mem-win.c index 755e156c9b..783036c5cc 100644 --- a/libnvme/src/nvme/mem-win.c +++ b/libnvme/src/nvme/mem-win.c @@ -31,9 +31,14 @@ static int getpagesize(void) __shr_public void *libnvme_alloc(size_t len) { - size_t _len = round_up(len, 0x1000); + size_t _len; void *p; + /* a zero size is implementation-defined, get a page */ + if (!len) + len = 1; + _len = round_up(len, 0x1000); + p = _aligned_malloc(_len, getpagesize()); if (!p) return NULL; diff --git a/libnvme/tests/mem.c b/libnvme/tests/mem.c index 2bf0adf59b..3618b6a042 100644 --- a/libnvme/tests/mem.c +++ b/libnvme/tests/mem.c @@ -69,5 +69,11 @@ int main(void) libnvme_free_huge(&mh); shr_assert(!mh.p); + /* a zero length allocation gets a page */ + buf = libnvme_alloc(0); + shr_assert(buf); + shr_assert(buf[0] == 0); + libnvme_free(buf); + return 0; } From c1c0c1c67d5c178e60e960a7606ea50a00e76f25 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:45:06 +0000 Subject: [PATCH 198/263] argconfig: include the global options like a table The global options were special-cased wherever a command's options are walked: the help printed them after the command's table, given() and the lookups searched them on their own, and the metadata dump appended them behind its own "Global options" group. Build the options a command accepts as a root table instead, which includes the command's table and the global options like OPT_INCLUDE() does. The help, given() and the metadata walk the root, and the parse hook gets it. Only the lookup still searches the global options first, as a global option is the same for every command, and only the command's own options are reset before a parse, as the global options may have been given before the command name. The help and the metadata of all commands are unchanged. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata.c | 48 +++++------- src/argconfig.c | 122 ++++++++++++++++++------------- src/argconfig.h | 8 +- 3 files changed, 94 insertions(+), 84 deletions(-) diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 1aa15d3b2d..5542e19736 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -333,11 +333,6 @@ static int copy_options(const struct argconfig_commandline_options *opts, return 0; } -/* - * argconfig_parse() hook installed by build_model(): copies the current - * command's options into the model, then returns the sentinel so the parser - * unwinds before the command opens a device. - */ /* The entries of @opts with the included tables in place, see flat_copy() */ static size_t flat_count(struct argconfig_commandline_options *opts) { @@ -374,40 +369,31 @@ static size_t flat_copy(struct argconfig_commandline_options *dst, } /* - * The command's options followed by the registered global options, behind a - * "Global options" separator as json_command() expects. The caller frees - * the returned array; the entries alias the original tables. + * The options of @root, the included tables behind a group of their + * heading: json_command() takes the options after "Global options" for the + * global ones. The caller frees the returned array; the entries alias the + * original tables. */ static struct argconfig_commandline_options * -with_global_options(struct argconfig_commandline_options *options) +flatten(struct argconfig_commandline_options *root) { - struct argconfig_commandline_options *g, *all; - size_t n, ng = 0, i; - - g = argconfig_get_global_options(); - n = flat_count(options); - while (g && g[ng].option) - ng++; - - all = calloc(n + ng + 2, sizeof(*all)); - if (!all) - return NULL; + struct argconfig_commandline_options *all; - flat_copy(all, options); - if (ng) { - struct argconfig_commandline_options sep = - OPT_GROUP("Global options"); - - memcpy(&all[n], &sep, sizeof(sep)); - for (i = 0; i < ng; i++) - memcpy(&all[n + 1 + i], &g[i], sizeof(*all)); - } + all = calloc(flat_count(root) + 1, sizeof(*all)); + if (all) + flat_copy(all, root); return all; } +/* + * argconfig_parse() hook installed by build_model(): copies the current + * command's options, the global ones included, into the model, then + * returns the sentinel so the parser unwinds before the command opens a + * device. + */ static int metadata_capture_hook(int argc, char **argv, const char *program_desc, - struct argconfig_commandline_options *options) + struct argconfig_commandline_options *root) { (void)argc; (void)argv; @@ -415,7 +401,7 @@ static int metadata_capture_hook(int argc, char **argv, const char *program_desc if (command_metadata_cur_command && !command_metadata_cur_command->captured) { __cleanup_free struct argconfig_commandline_options *all = - with_global_options(options); + flatten(root); int err = -ENOMEM; if (all) diff --git a/src/argconfig.c b/src/argconfig.c index 00deb2a6ac..319cfe5cb6 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -54,11 +54,28 @@ struct argconfig_commandline_options *argconfig_get_global_options(void) return global_options; } -/* The global options, unless they are the table itself */ -static struct argconfig_commandline_options * -argconfig_globals(struct argconfig_commandline_options *options) +/* The entries of a root, see argconfig_root() */ +#define ARGCONFIG_ROOT_LEN 3 + +/* + * The options a command accepts: its table @options and the global + * options, each as an include with the heading of the help. The global + * options are included last, as root[1]. The table of the global options + * themselves includes nothing else. + */ +static void argconfig_root(struct argconfig_commandline_options *root, + struct argconfig_commandline_options *options) { - return options == global_options ? NULL : global_options; + struct argconfig_commandline_options r[ARGCONFIG_ROOT_LEN] = { + OPT_INCLUDE(options, "Options"), + OPT_INCLUDE(global_options, "Global options"), + OPT_END() + }; + struct argconfig_commandline_options end[] = { OPT_END() }; + + memcpy(root, r, sizeof(r)); + if (!global_options || options == global_options) + memcpy(&root[1], end, sizeof(end)); } void argconfig_append_usage(const char *str) @@ -261,7 +278,7 @@ static void print_options(FILE *out, struct argconfig_commandline_options *s, } static void show_help(FILE *out, const char *program_desc, - struct argconfig_commandline_options *s) + struct argconfig_commandline_options *root) { argconfig_print_heading(out, "Usage: %s", append_usage_str); fputs("\n\n", out); @@ -269,8 +286,7 @@ static void show_help(FILE *out, const char *program_desc, shr_print_word_wrapped(program_desc, 0, 0, out); fputc('\n', out); - print_options(out, s, "Options"); - print_options(out, argconfig_globals(s), "Global options"); + print_options(out, root, "Options"); } static bool help_requested; @@ -478,7 +494,8 @@ struct argconfig_parser { int argc; char **argv; int ind; - struct argconfig_commandline_options *options; + /* the table of the command, the global options included */ + struct argconfig_commandline_options root[ARGCONFIG_ROOT_LEN]; /* * Global options stop at the first operand, the subcommand. Their * implicit -h, --help, -V and --version are recorded and the parse @@ -562,34 +579,30 @@ argconfig_find_short(struct argconfig_commandline_options *s, char c) return NULL; } -/* @s given by its short name @c, or by its long name without, and @arg */ /* - * The option @s or the global options name, the global ones first: a - * global option is the same for every command. + * The option of @root with the long name @name, a global option first: a + * global option is the same for every command. &root[1] is the include + * of the global options alone. */ static struct argconfig_commandline_options * -argconfig_find_long_any(struct argconfig_commandline_options *s, +argconfig_find_long_any(struct argconfig_commandline_options *root, const char *name, size_t len) { - struct argconfig_commandline_options *g = argconfig_globals(s); - struct argconfig_commandline_options *o = NULL; + struct argconfig_commandline_options *o; - if (g) - o = argconfig_find_long(g, name, len); + o = argconfig_find_long(&root[1], name, len); - return o ? o : argconfig_find_long(s, name, len); + return o ? o : argconfig_find_long(root, name, len); } static struct argconfig_commandline_options * -argconfig_find_short_any(struct argconfig_commandline_options *s, char c) +argconfig_find_short_any(struct argconfig_commandline_options *root, char c) { - struct argconfig_commandline_options *g = argconfig_globals(s); - struct argconfig_commandline_options *o = NULL; + struct argconfig_commandline_options *o; - if (g) - o = argconfig_find_short(g, c); + o = argconfig_find_short(&root[1], c); - return o ? o : argconfig_find_short(s, c); + return o ? o : argconfig_find_short(root, c); } /* What a value of @s has to look like */ @@ -643,6 +656,7 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, return ARGCONFIG_USAGE; } +/* @s given by its short name @c, or by its long name without, and @arg */ static int argconfig_apply(struct argconfig_commandline_options *s, char c, char *arg) { @@ -730,7 +744,7 @@ static int argconfig_parse_long(struct argconfig_parser *p, char *arg) char *eq = strchr(name, '='); size_t len = eq ? (size_t)(eq - name) : strlen(name); - s = argconfig_find_long_any(p->options, name, len); + s = argconfig_find_long_any(p->root, name, len); if (!s) { bool help = len == 4 && !strncmp(name, "help", 4); bool version = len == 7 && !strncmp(name, "version", 7); @@ -795,7 +809,7 @@ static int argconfig_parse_short(struct argconfig_parser *p, char *arg) if (!p->global && (*c == 'h' || *c == '?')) return ARGCONFIG_HELP; - s = argconfig_find_short_any(p->options, *c); + s = argconfig_find_short_any(p->root, *c); if (!s && p->global && (*c == 'h' || *c == 'V')) { p->help |= *c == 'h'; p->version |= *c == 'V'; @@ -854,7 +868,11 @@ static int argconfig_parse_args(struct argconfig_parser *p) char *arg; int ret; - argconfig_reset_seen(p->options); + /* + * The global options keep theirs: they may have been given before + * the command name. + */ + argconfig_reset_seen(p->root[0].default_value); /* argv ends at argc or at a NULL, whichever comes first */ for (p->ind = 1; p->ind < p->argc && p->argv[p->ind]; p->ind++) { @@ -895,16 +913,31 @@ void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) argconfig_parse_hook = hook; } +static bool given_in(struct argconfig_commandline_options *s, + const void *value) +{ + bool given = false; + + for (; s && s->option; s++) { + if (s->config_type == CFG_INCLUDE) + given |= given_in(s->default_value, value); + else + given |= s->default_value == value && s->seen; + } + + return given; +} + /* --verbose, or an alias of it such as --human-readable, was given */ -static bool argconfig_check_verbose(struct argconfig_commandline_options *s) +static bool argconfig_check_verbose(struct argconfig_commandline_options *root) { struct argconfig_commandline_options *v; - v = argconfig_find_long_any(s, "verbose", strlen("verbose")); + v = argconfig_find_long_any(root, "verbose", strlen("verbose")); if (!v || v->config_type != CFG_INCREMENT) return false; - return argconfig_given(s, v->default_value); + return given_in(root, v->default_value); } int argconfig_parse(int argc, char *argv[], const char *program_desc, @@ -914,10 +947,10 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, struct argconfig_parser p = { .argc = argc, .argv = argv, - .options = options, }; int ret; + argconfig_root(p.root, options); parse_argv0 = argc > 0 ? argv[0] : ""; free(operand_buf); operand_buf = NULL; @@ -925,7 +958,7 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, operands.argc = 0; if (argconfig_parse_hook) - return argconfig_parse_hook(argc, argv, program_desc, options); + return argconfig_parse_hook(argc, argv, program_desc, p.root); p.operands = calloc(argc > 0 ? argc : 1, sizeof(*p.operands)); if (!p.operands) @@ -939,7 +972,7 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, * either way: it gets ARGCONFIG_HELP or an error. */ if (ret == ARGCONFIG_HELP) { - show_help(stdout, program_desc, options); + show_help(stdout, program_desc, p.root); help_requested = true; } else if (ret > 0) { argconfig_print_usage(); @@ -952,7 +985,7 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, operands.argc = p.nr_operands; } - if (!argconfig_check_verbose(options)) + if (!argconfig_check_verbose(p.root)) setlocale(LC_ALL, "C"); return ret; @@ -979,11 +1012,12 @@ int argconfig_parse_global(int argc, char *argv[], struct argconfig_parser p = { .argc = argc, .argv = argv, - .options = options ? options : no_options, .global = true, }; int ret; + argconfig_root(p.root, options ? options : no_options); + parse_argv0 = argc > 0 ? argv[0] : ""; ret = argconfig_parse_args(&p); *argind = p.ind; @@ -994,21 +1028,6 @@ int argconfig_parse_global(int argc, char *argv[], (p.version ? ARGCONFIG_VERSION : 0); } -static bool given_in(struct argconfig_commandline_options *s, - const void *value) -{ - bool given = false; - - for (; s && s->option; s++) { - if (s->config_type == CFG_INCLUDE) - given |= given_in(s->default_value, value); - else - given |= s->default_value == value && s->seen; - } - - return given; -} - /* * True if an option which stores to @value was given, in the table or in * the global options. Aliases share the variable, so any of them counts. @@ -1016,10 +1035,11 @@ static bool given_in(struct argconfig_commandline_options *s, bool argconfig_given(struct argconfig_commandline_options *s, const void *value) { - struct argconfig_commandline_options *g = argconfig_globals(s); + struct argconfig_commandline_options root[ARGCONFIG_ROOT_LEN]; if (!value) return false; + argconfig_root(root, s); - return given_in(s, value) || given_in(g, value); + return given_in(root, value); } diff --git a/src/argconfig.h b/src/argconfig.h index e073668db0..8e2e95525f 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -257,9 +257,13 @@ struct argconfig_commandline_options *argconfig_get_global_options(void); int argconfig_nr_operands(void); char *argconfig_operand(int i); +/* + * Called by argconfig_parse() instead of parsing. @root includes the table + * of the command and the global options, under the headings of the help. + */ typedef int (*argconfig_parse_hook_fn)(int argc, char *argv[], - const char *program_desc, - struct argconfig_commandline_options *options); + const char *program_desc, + struct argconfig_commandline_options *root); void argconfig_set_parse_hook(argconfig_parse_hook_fn hook); bool argconfig_given(struct argconfig_commandline_options *options, From 6a4774e2b92be866aff231779be2809094e032e8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:33:15 +0000 Subject: [PATCH 199/263] sed: don't read an unfilled Level 0 Discovery buffer With --dry-run, libnvme returns success for the Level 0 Discovery without sending it, so the discovery buffer on the stack kept whatever was there. sed initialize --dry-run then saw a random locking state and failed with "cannot initialize an initialized drive" now and then, as in the sed-cli test of a clang release build in CI. Clear the buffer before the discovery: without data, the drive reports no locking feature. Signed-off-by: Daniel Wagner --- plugins/sed/sedopal_cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/sed/sedopal_cmd.c b/plugins/sed/sedopal_cmd.c index 26dcd0b816..e94547f353 100644 --- a/plugins/sed/sedopal_cmd.c +++ b/plugins/sed/sedopal_cmd.c @@ -1041,6 +1041,8 @@ static int sedopal_discover_device(struct libnvme_transport_handle *hdl, { int rc; + /* --dry-run returns success without filling the buffer */ + memset(buf, 0, len); rc = libnvme_sed_discover(hdl, buf, len); if (rc) { nvme_show_err(rc, "level 0 discovery"); From 3549f7a3d515ace546a74155c42f520ce2a8ed23 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:51:45 +0000 Subject: [PATCH 200/263] argconfig: give files and directories types of their own An option which names a file or a directory was a string with the metavar FILE or DIRECTORY, and the completions guessed from the metavar whether to complete paths. A few path options used other metavars and got no path completion at all. Add the types CFG_FILE and CFG_DIR, which parse like a string, with OPT_FILE() and OPT_DIR(). Use them for all options with the metavar FILE or DIRECTORY, and for the keyfile options of the keys commands, the telemetry and string logs of ocp internal-log and --nbft-path, which name a file or a directory too. Their metavar becomes FILE or DIRECTORY. Signed-off-by: Daniel Wagner --- plugins/keys/keys-plugin.c | 8 ++++---- plugins/memblaze/memblaze-nvme.c | 4 ++-- plugins/micron/micron-nvme.c | 6 +++--- plugins/nbft/nbft-plugin.c | 4 ++-- plugins/ocp/ocp-nvme.c | 6 +++--- plugins/rpmb/rpmb.c | 14 +++++++------- plugins/solidigm/solidigm-internal-logs.c | 4 ++-- plugins/wdc/wdc-nvme.c | 2 +- src/argconfig.c | 11 ++++++++++- src/argconfig.h | 12 +++++++++++- src/config-convert.c | 8 ++++---- src/config-create.c | 2 +- src/fabrics.c | 19 ++++++++----------- src/global-config.c | 2 ++ src/nvme-cmds-deprecated.c | 12 ++++++------ tests/unit/c/argconfig-getopt-ref.c | 4 ++++ tests/unit/c/test-argconfig-parse.c | 5 +++++ 17 files changed, 75 insertions(+), 48 deletions(-) diff --git a/plugins/keys/keys-plugin.c b/plugins/keys/keys-plugin.c index 2376bab100..e2172ccacf 100644 --- a/plugins/keys/keys-plugin.c +++ b/plugins/keys/keys-plugin.c @@ -490,7 +490,7 @@ static int gen_tls(int argc, char **argv, struct command *acmd, struct plugin *p OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), OPT_STR("secret", 's', &cfg.secret, secret), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile), OPT_BYTE("hmac", 'm', &cfg.hmac, hmac), OPT_BYTE("identity", 'I', &cfg.version, version), OPT_FLAG("insert", 'i', &cfg.insert, insert), @@ -735,7 +735,7 @@ static int insert_tls(int argc, char **argv, struct command *acmd, struct plugin OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn), OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn), OPT_STR("keydata", 'd', &cfg.keydata, keydata), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile), OPT_BYTE("identity", 'I', &cfg.identity, identity), OPT_FLAG("compat", 'C', &cfg.compat, compat), OPT_END() @@ -835,7 +835,7 @@ static int key_export(int argc, char **argv, struct command *acmd, struct plugin OPT_ARGS(opts) = { OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile), OPT_END() }; @@ -981,7 +981,7 @@ static int key_import(int argc, char **argv, struct command *acmd, struct plugin OPT_ARGS(opts) = { OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile), OPT_STR("keydata", 'd', &cfg.keydata, keydata), OPT_STR("identity", 'i', &cfg.identity, identity), OPT_END() diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 9919ed764e..391c1a3ef7 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -795,8 +795,8 @@ static int mb_selective_download(int argc, char **argv, struct command *acmd, st }; OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_ENUM("select", 's', &cfg.select, select, selects), + OPT_FILE("fw", 'f', &cfg.fw, fw), + OPT_ENUM("select", 's', &cfg.select, select, selects), OPT_END() }; diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index ef93fc381f..74049300cb 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -638,8 +638,8 @@ static int micron_selective_download(int argc, char **argv, }; OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_ENUM("select", 's', &cfg.select, select, selects), + OPT_FILE("fw", 'f', &cfg.fw, fw), + OPT_ENUM("select", 's', &cfg.select, select, selects), OPT_END() }; @@ -3762,7 +3762,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, OPT_ARGS(opts) = { OPT_ENUM("type", 't', &cfg.type, type, types), - OPT_STRING("package", 'p', "FILE", &cfg.package, package), + OPT_FILE("package", 'p', &cfg.package, package), OPT_UINT("data_area", 'd', &cfg.data_area, data_area), OPT_END() }; diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 2e9823b246..7e2a788e4a 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -670,8 +670,8 @@ int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin OPT_FLAG("hfi", 0, &show_hfi, "show NBFT HFIs"), OPT_FLAG("discovery", 'd', &show_discovery, "show NBFT discovery controllers"), - OPT_STRING("nbft-path", 0, "STR", &nbft_path, - "user-defined path for NBFT tables"), + OPT_DIR("nbft-path", 0, &nbft_path, + "user-defined path for NBFT tables"), OPT_END() }; diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index b4fba6822c..945e632ed1 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -1003,9 +1003,9 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct bool host_behavior_changed = false; OPT_ARGS(opts) = { - OPT_STR("telemetry-log", 'l', &opt.telemetry_log, - telemetry_log), - OPT_STR("string-log", 's', &opt.string_log, string_log), + OPT_FILE("telemetry-log", 'l', &opt.telemetry_log, + telemetry_log), + OPT_FILE("string-log", 's', &opt.string_log, string_log), OPT_FILE("output-file", 'f', &opt.output_file, output_file), OPT_INT("data-area", 'a', &opt.data_area, data_area), OPT_ENUM("telemetry-type", 't', &tele_type, telemetry_type, diff --git a/plugins/rpmb/rpmb.c b/plugins/rpmb/rpmb.c index 6871d7344f..084dd7f4fa 100644 --- a/plugins/rpmb/rpmb.c +++ b/plugins/rpmb/rpmb.c @@ -816,7 +816,7 @@ static int rpmb_program_key(int argc, char **argv, struct command *acmd, struct OPT_ARGS(opts) = { OPT_STRING("key", 'k', "key", &cfg.key, key), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile), OPT_BYTE("target", 't', &cfg.target, target), OPT_END() }; @@ -910,7 +910,7 @@ static int rpmb_read_data(int argc, char **argv, struct command *acmd, struct pl }; OPT_ARGS(opts) = { - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile), OPT_UINT("address", 0, &cfg.address, address), OPT_UINT("blocks", 'b', &cfg.blocks, blocks), OPT_BYTE("target", 't', &cfg.target, target), @@ -978,8 +978,8 @@ static int rpmb_write_data(int argc, char **argv, struct command *acmd, struct p }; OPT_ARGS(opts) = { - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile), + OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile), OPT_STRING("key", 'k', "key", &cfg.key, key), OPT_STRING("msg", 'd', "data", &cfg.msg, msg), OPT_UINT("address", 0, &cfg.address, address), @@ -1047,7 +1047,7 @@ static int rpmb_read_config(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile), OPT_END() }; @@ -1114,8 +1114,8 @@ static int rpmb_write_config(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), - OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile), + OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile), OPT_STRING("key", 'k', "key", &cfg.key, key), OPT_STRING("msg", 'd', "data", &cfg.msg, msg), OPT_END() diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index 72d319acd1..335f1c608c 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -880,8 +880,8 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_ENUM("type", 't', &cfg.type, type, types), - OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir), + OPT_ENUM("type", 't', &cfg.type, type, types), + OPT_DIR("dir-name", 'd', &cfg.out_dir, out_dir), OPT_END() }; diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 59d72cbe67..7844a49aaa 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -10579,7 +10579,7 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.dirName, dirName), + OPT_DIR("dir-name", 'd', &cfg.dirName, dirName), OPT_END() }; diff --git a/src/argconfig.c b/src/argconfig.c index 319cfe5cb6..189185085b 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -297,6 +297,11 @@ bool argconfig_help_requested(void) return help_requested; } +static bool argconfig_is_string(enum argconfig_types type) +{ + return type == CFG_STRING || type == CFG_FILE || type == CFG_DIR; +} + static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { @@ -307,12 +312,14 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, int ret = 0; /* only a string option can do without a value */ - if (!arg && s->config_type != CFG_STRING && + if (!arg && !argconfig_is_string(s->config_type) && s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT) return -EINVAL; switch (s->config_type) { case CFG_STRING: + case CFG_FILE: + case CFG_DIR: *(char **)value = arg; break; case CFG_INT: @@ -406,6 +413,8 @@ static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *(int *)val = opt_val->increment; break; case CFG_STRING: + case CFG_FILE: + case CFG_DIR: *(char **)val = opt_val->string; break; case CFG_GROUP_SEPARATOR: diff --git a/src/argconfig.h b/src/argconfig.h index 8e2e95525f..9515316dec 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -27,6 +27,9 @@ enum argconfig_types { CFG_POSITIVE, CFG_INCREMENT, CFG_ENUM, + /* strings which name a file or a directory */ + CFG_FILE, + CFG_DIR, CFG_GROUP_SEPARATOR, CFG_INCLUDE, }; @@ -126,8 +129,15 @@ enum argconfig_types { {l, s, m, CFG_STRING, OPT_CHECK_STR(v), \ ARGCONFIG_ARG_OPTIONAL, d, false, __VA_ARGS__} +#define OPT_FILE(l, s, v, d, ...) \ + {l, s, "FILE", CFG_FILE, OPT_CHECK_STR(v), \ + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} + +#define OPT_DIR(l, s, v, d, ...) \ + {l, s, "DIRECTORY", CFG_DIR, OPT_CHECK_STR(v), \ + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} + #define OPT_FMT(l, s, v, d, ...) OPT_STRING(l, s, "FMT", v, d, __VA_ARGS__) -#define OPT_FILE(l, s, v, d, ...) OPT_STRING(l, s, "FILE", v, d, __VA_ARGS__) #define OPT_LIST(l, s, v, d, ...) OPT_STRING(l, s, "LIST", v, d, __VA_ARGS__) #define OPT_STR(l, s, v, d, ...) OPT_STRING(l, s, "STRING", v, d, __VA_ARGS__) diff --git a/src/config-convert.c b/src/config-convert.c index 0c69342d21..4c249aed0c 100644 --- a/src/config-convert.c +++ b/src/config-convert.c @@ -552,10 +552,10 @@ int nvme_config_convert(const char *desc, int argc, char **argv) int ret; OPT_ARGS(opts) = { - OPT_STRING("config", 'J', "FILE", &config_file, - "convert this JSON file (default: config.json)"), - OPT_STRING("output", 0, "FILE", &output_file, - "write result here (default: nvme-fabrics.conf)"), + OPT_FILE("config", 'J', &config_file, + "convert this JSON file (default: config.json)"), + OPT_FILE("output", 0, &output_file, + "write result here (default: nvme-fabrics.conf)"), OPT_FLAG("force", 0, &force, "overwrite an existing target"), OPT_END() diff --git a/src/config-create.c b/src/config-create.c index 057064b495..891540dc59 100644 --- a/src/config-create.c +++ b/src/config-create.c @@ -202,7 +202,7 @@ int nvme_config_create(const char *desc, int argc, char **argv) &persistent_arg, desc_persistent), OPT_STRING("host-symname", 0, "STR", &hostsymname, desc_symname), - OPT_STRING("output", 0, "FILE", &output_file, desc_output), + OPT_FILE("output", 0, &output_file, desc_output), OPT_END() }; diff --git a/src/fabrics.c b/src/fabrics.c index 1bac52a1f5..bedca44e9e 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -962,8 +962,7 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) OPT_FILE("raw", 'r', &raw, "save raw output to file"), OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force", &persistent_arg, nvmf_persistent), - OPT_STRING("config", 'J', "FILE", &config_file, - nvmf_config_file), + OPT_FILE("config", 'J', &config_file, nvmf_config_file), OPT_FLAG("no-reuse", 0, &no_reuse, "always create a new connection, never reuse an existing one", .old_name = "force"), @@ -971,8 +970,8 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"), OPT_STRING("owner", 0, "NAME", &owner, "record this owner in the registry"), - OPT_STRING("nbft-path", 0, "STR", &nbft_path, - "user-defined path for NBFT tables"), + OPT_DIR("nbft-path", 0, &nbft_path, + "user-defined path for NBFT tables"), OPT_END() }; @@ -1168,12 +1167,11 @@ int fabrics_connect(const char *desc, int argc, char **argv) OPT_ARGS(opts) = { OPT_INCLUDE(nvmf, "Fabrics options"), - OPT_STRING("config", 'J', "FILE", &config_file, - nvmf_config_file), + OPT_FILE("config", 'J', &config_file, nvmf_config_file), OPT_STRING("owner", 0, "NAME", &owner, "record this owner in the registry"), - OPT_STRING("devid-file", 0, "FILE", &devid_file, - "write connected device name to FILE"), + OPT_FILE("devid-file", 0, &devid_file, + "write connected device name to FILE"), OPT_FLAG("idempotent", 0, &idempotent, "exit 0 if already connected"), OPT_FLAG("discovery", 0, &discovery, @@ -1732,7 +1730,7 @@ int fabrics_config_validate(const char *desc, int argc, char **argv) int ret; OPT_ARGS(opts) = { - OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file_ro), + OPT_FILE("config", 'J', &config_file, nvmf_config_file_ro), OPT_END() }; @@ -1767,8 +1765,7 @@ int fabrics_config_show(const char *desc, int argc, char **argv) int ret; OPT_ARGS(opts) = { - OPT_STRING("config", 'J', "FILE", &config_file, - nvmf_config_file_ro), + OPT_FILE("config", 'J', &config_file, nvmf_config_file_ro), OPT_END() }; diff --git a/src/global-config.c b/src/global-config.c index a261938d30..ee7f6635d3 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -131,6 +131,8 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, *(uint32_t *)s->default_value = uval; break; case CFG_STRING: + case CFG_FILE: + case CFG_DIR: err = set_string(s, value); if (err) return err; diff --git a/src/nvme-cmds-deprecated.c b/src/nvme-cmds-deprecated.c index 5e76a60704..182c297494 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -172,12 +172,12 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p struct config cfg = { 0 }; OPT_ARGS(opts) = { - OPT_STR("keyring", 'k', &cfg.keyring, keyring), - OPT_STR("keytype", 't', &cfg.keytype, keytype), - OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile), - OPT_FLAG("import", 'i', &cfg.import, import), - OPT_FLAG("export", 'e', &cfg.export, export), - OPT_STR("revoke", 'r', &cfg.revoke, revoke), + OPT_STR("keyring", 'k', &cfg.keyring, keyring), + OPT_STR("keytype", 't', &cfg.keytype, keytype), + OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile), + OPT_FLAG("import", 'i', &cfg.import, import), + OPT_FLAG("export", 'e', &cfg.export, export), + OPT_STR("revoke", 'r', &cfg.revoke, revoke), OPT_END() }; diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index 2f23c922c7..e0634a0168 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -61,6 +61,8 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, switch (s->config_type) { case CFG_STRING: + case CFG_FILE: + case CFG_DIR: *(char **)value = arg; break; case CFG_INT: @@ -150,6 +152,8 @@ static void argconfig_set_opt_val(enum argconfig_types type, *(int *)val = opt_val->increment; break; case CFG_STRING: + case CFG_FILE: + case CFG_DIR: *(char **)val = opt_val->string; break; case CFG_GROUP_SEPARATOR: diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 73a683986c..8b8779d872 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -31,6 +31,7 @@ union val { char *string; char *fmt; char *file; + char *dir; char *list; char *str; __u8 val; @@ -82,6 +83,7 @@ struct cfg { char *string; char *fmt; char *file; + char *dir; char *list; char *str; __u8 val; @@ -147,6 +149,8 @@ static struct toval_test toval_tests[] = { VAL_TEST("--string=string", string, "string", false, 0), VAL_TEST("--fmt=fmt", fmt, "fmt", false, 0), VAL_TEST("--file=file", file, "file", false, 0), + VAL_TEST("--dir=dir", dir, "dir", false, 0), + VAL_TEST("--file", file, NULL, false, -EINVAL), VAL_TEST("--list=list", list, "list", false, 0), VAL_TEST("--str=str", str, "str", false, 0), VAL_TEST("--val=", val, 0, true, -EINVAL), @@ -224,6 +228,7 @@ void toval_test(struct toval_test *test) OPT_STRING("string", 't', "STRING", &cfg.string, "string"), OPT_FMT("fmt", 'F', &cfg.fmt, "fmt"), OPT_FILE("file", 'L', &cfg.file, "file"), + OPT_DIR("dir", 'D', &cfg.dir, "dir"), OPT_LIST("list", 'T', &cfg.list, "list"), OPT_STR("str", 'r', &cfg.str, "str"), OPT_BYTE("val", 'v', &cfg.val, "val", opt_vals), From b44d806b762b61ce6b7604b8afb710b07d73878d Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:53:18 +0000 Subject: [PATCH 201/263] argconfig: let unsigned options declare a range Some unsigned options take only a few numbers. --output-format-version knows the versions 1 and 2, but took every number and printed version 1 for anything but 2. Add the fields min and max to an option: if max is set, a number outside of min to max is rejected on the command line and in nvme-cli.conf, and the error names the range. Named values are taken as they are. Limit --output-format-version to 1 and 2. Signed-off-by: Daniel Wagner --- Documentation/global-options.txt | 2 +- src/argconfig.c | 38 ++++++++++++++++++--- src/argconfig.h | 8 +++++ src/global-config.c | 2 ++ src/global-options.c | 2 +- tests/unit/c/test-argconfig-parse.c | 51 +++++++++++++++++++++++++++++ tests/unit/c/test-global-config.c | 10 ++++++ 7 files changed, 107 insertions(+), 6 deletions(-) diff --git a/Documentation/global-options.txt b/Documentation/global-options.txt index 0fdfd79de6..256975a497 100644 --- a/Documentation/global-options.txt +++ b/Documentation/global-options.txt @@ -22,7 +22,7 @@ and are available to this subcommand: --output-format-version=:: Select the output format version. Version '1' uses the original field naming, while version '2' (default) provides more consistent - and script-friendly field names. + and script-friendly field names. Other versions are rejected. --quiet:: Suppress informational messages. Errors are still reported, and diff --git a/src/argconfig.c b/src/argconfig.c index 189185085b..6da752eb4b 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -302,6 +303,25 @@ static bool argconfig_is_string(enum argconfig_types type) return type == CFG_STRING || type == CFG_FILE || type == CFG_DIR; } +/* @u is in the range of @s, if it has one */ +bool argconfig_in_range(const struct argconfig_commandline_options *s, + uint64_t u) +{ + return !s->max || (u >= s->min && u <= s->max); +} + +/* An unsigned number up to @max, in the range of @s */ +static int argconfig_parse_u64(struct argconfig_commandline_options *s, + const char *arg, uint64_t max, uint64_t *u) +{ + int ret = shr_parse_u64(arg, max, u); + + if (!ret && !argconfig_in_range(s, *u)) + return -ERANGE; + + return ret; +} + static int argconfig_parse_type(struct argconfig_commandline_options *s, char *arg) { @@ -328,17 +348,17 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, *(int *)value = i; break; case CFG_BYTE: - ret = shr_parse_u64(arg, UINT8_MAX, &u); + ret = argconfig_parse_u64(s, arg, UINT8_MAX, &u); if (!ret) *(uint8_t *)value = u; break; case CFG_SHORT: - ret = shr_parse_u64(arg, UINT16_MAX, &u); + ret = argconfig_parse_u64(s, arg, UINT16_MAX, &u); if (!ret) *(uint16_t *)value = u; break; case CFG_POSITIVE: - ret = shr_parse_u64(arg, UINT32_MAX, &u); + ret = argconfig_parse_u64(s, arg, UINT32_MAX, &u); if (!ret) *(uint32_t *)value = u; break; @@ -350,7 +370,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, ret = -EINVAL; break; case CFG_LONG: - ret = shr_parse_u64(arg, UINT64_MAX, &u); + ret = argconfig_parse_u64(s, arg, UINT64_MAX, &u); if (!ret) *(uint64_t *)value = u; break; @@ -361,6 +381,8 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, if (!isdigit((unsigned char)*arg)) return -EINVAL; ret = shr_suffix_binary_parse(arg, &endptr, &u); + if (!ret && !argconfig_in_range(s, u)) + ret = -ERANGE; if (!ret) *(uint64_t *)value = u; break; @@ -617,6 +639,14 @@ argconfig_find_short_any(struct argconfig_commandline_options *root, char c) /* What a value of @s has to look like */ static const char *argconfig_expected(struct argconfig_commandline_options *s) { + static char range[64]; + + if (s->max) { + snprintf(range, sizeof(range), "a number from %" PRIu64 + " to %" PRIu64, s->min, s->max); + return range; + } + switch (s->config_type) { case CFG_BYTE: return "a number from 0 to 255"; diff --git a/src/argconfig.h b/src/argconfig.h index 9515316dec..8871d16453 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -235,6 +235,12 @@ struct argconfig_commandline_options { const char *deprecated; /* the long name of earlier versions, accepted with a warning */ const char *old_name; + /* + * The range of an unsigned number, unless max is 0. The named values + * are taken as they are. + */ + uint64_t min; + uint64_t max; }; void argconfig_append_usage(const char *str); @@ -278,6 +284,8 @@ void argconfig_set_parse_hook(argconfig_parse_hook_fn hook); bool argconfig_given(struct argconfig_commandline_options *options, const void *value); +bool argconfig_in_range(const struct argconfig_commandline_options *s, + uint64_t u); bool argconfig_set_value_name(struct argconfig_commandline_options *option, const char *str); const char *argconfig_value_name(const struct argconfig_opt_val *values, diff --git a/src/global-config.c b/src/global-config.c index ee7f6635d3..5ade7707cf 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -126,6 +126,8 @@ static int apply_kv(struct global_config_ctx *gc, const char *key, break; case CFG_POSITIVE: err = parse_uint(value, UINT32_MAX, &uval); + if (!err && !argconfig_in_range(s, uval)) + err = -EINVAL; if (err) goto bad_value; *(uint32_t *)s->default_value = uval; diff --git a/src/global-options.c b/src/global-options.c index 54e2922b3f..c82a2aa46c 100644 --- a/src/global-options.c +++ b/src/global-options.c @@ -47,7 +47,7 @@ static struct argconfig_commandline_options global_options[] = { OPT_FLAG("no-ioctl-probing", 0, &nvme_args.no_ioctl_probing, "disable 64-bit IOCTL support probing"), OPT_UINT("output-format-version", 0, &nvme_args.output_format_ver, - "output format version: 1|2"), + "output format version: 1|2", .min = 1, .max = 2), /* the spelling of --verbose of older versions */ OPT_INCR("human-readable", 'H', &nvme_args.verbose, NULL, .deprecated = "--verbose/-v"), diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 8b8779d872..f95612dca2 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -776,6 +776,56 @@ static void special_values_test(void) } } +/* + * A number outside the range of an option is rejected, a named value is + * taken as it is. + */ +static void range_test(void) +{ + static const struct { + const char *arg; + int ret; + __u32 value; + } tests[] = { + { "--ranged=1", 0, 1 }, + { "--ranged=3", 0, 3 }, + { "--ranged=0", -EINVAL, 0 }, + { "--ranged=4", -EINVAL, 0 }, + { "--ranged=0x100000000", -EINVAL, 0 }, + { "--ranged=big", 0, 100 }, + { "--size=4Ki", 0, 4096 }, + { "--size=8Ki", -EINVAL, 0 }, + }; + OPT_VALS(values) = { + VAL_UINT("big", 100), + VAL_END() + }; + __u64 size; + __u32 ranged; + unsigned int i; + int ret; + + OPT_ARGS(opts) = { + OPT_UINT("ranged", 'r', &ranged, "ranged", values, + .min = 1, .max = 3), + OPT_SUFFIX("size", 's', &size, "size", .max = 4096), + OPT_END() + }; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + char **argv = (char *[]){ "prog", (char *)tests[i].arg, NULL }; + + ranged = 0; + size = 0; + ret = argconfig_parse(argv_len(argv), argv, "test", opts); + if (ret != tests[i].ret || + (!ret && ranged + size != tests[i].value)) { + printf("ERROR: range: %s\n", tests[i].arg); + test_rc = 1; + } + } +} + #define COMMA_SEP_ARRAY_MAX_VALUES 4 struct comma_sep_array_test { @@ -1175,6 +1225,7 @@ int main(void) double_locale_test(); empty_name_test(); special_values_test(); + range_test(); for (i = 0; i < ARRAY_SIZE(comma_sep_array_tests); i++) comma_sep_array_test(&comma_sep_array_tests[i]); diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index 8174b6386a..5f63e36e55 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -161,6 +161,16 @@ static bool test_strict_numbers_and_buf(void) printf(" - '0x10' taken from a buffer [PASS]\n"); } + nvme_args.output_format_ver = 2; + nvme_load_global_config_buf("[Global]\noutput-format-version = 3\n", + "buf"); + if (nvme_args.output_format_ver != 2) { + printf(" - '3' out of range rejected [FAIL]\n"); + pass = false; + } else { + printf(" - '3' out of range rejected [PASS]\n"); + } + return pass; } From 61fdbd7339ddbb4d63e9a46e3f093cbc0314b386 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:55:49 +0000 Subject: [PATCH 202/263] nvme: accept the names of the Select values The Select field of Get Features takes 0 to 3, but --sel took any byte and sent the reserved values 4 to 7, or truncated larger values into the field. Name the values current, default, saved and supported like the help text does, and reject numbers above 3, for get-feature, the feat commands and the ocp and shannon commands which get a feature. The lm commands have a --sel option of their own meaning and are left alone. Document the names in a table of the Select values, which the feat pages and ocp get-idle-wakeup-time announced but did not have. Signed-off-by: Daniel Wagner --- Documentation/nvme-feat-arbitration.txt | 9 ++ Documentation/nvme-feat-async-event-conf.txt | 9 ++ Documentation/nvme-feat-err-recovery.txt | 9 ++ Documentation/nvme-feat-hctm.txt | 9 ++ .../nvme-feat-host-behavior-support.txt | 9 ++ Documentation/nvme-feat-int-coalesce.txt | 9 ++ Documentation/nvme-feat-int-vector-config.txt | 9 ++ Documentation/nvme-feat-keep-alive-timer.txt | 9 ++ Documentation/nvme-feat-lba-range-type.txt | 9 ++ Documentation/nvme-feat-num-queues.txt | 9 ++ Documentation/nvme-feat-perfc.txt | 9 ++ Documentation/nvme-feat-power-limit.txt | 9 ++ Documentation/nvme-feat-power-meas.txt | 9 ++ Documentation/nvme-feat-power-mgmt.txt | 9 ++ Documentation/nvme-feat-power-thresh.txt | 9 ++ Documentation/nvme-feat-temp-thresh.txt | 9 ++ Documentation/nvme-feat-timestamp.txt | 9 ++ Documentation/nvme-feat-volatile-wc.txt | 9 ++ Documentation/nvme-feat-write-atom-normal.txt | 9 ++ Documentation/nvme-get-feature.txt | 11 +-- .../nvme-ocp-eol-plp-failure-mode.txt | 11 +-- ...-ocp-get-clear-pcie-correctable-errors.txt | 11 +-- .../nvme-ocp-get-dssd-async-event-config.txt | 11 +-- .../nvme-ocp-get-dssd-power-state-feature.txt | 11 +-- .../nvme-ocp-get-error-injection.txt | 11 +-- .../nvme-ocp-get-idle-wakeup-time.txt | 9 ++ .../nvme-ocp-get-latency-monitor.txt | 11 +-- ...nvme-ocp-get-plp-health-check-interval.txt | 11 +-- .../nvme-ocp-get-telemetry-profile.txt | 11 +-- plugins/feat/feat-nvme.c | 6 +- plugins/ocp/ocp-clear-features.c | 3 +- plugins/ocp/ocp-nvme.c | 27 ++++-- plugins/shannon/shannon-nvme.c | 3 +- src/args.h | 3 + src/global-options.c | 9 ++ src/nvme-cmds-feature.c | 3 +- tests/cli/meson.build | 6 ++ tests/cli/nvme_sel_test.py | 97 +++++++++++++++++++ 38 files changed, 368 insertions(+), 68 deletions(-) create mode 100644 tests/cli/nvme_sel_test.py diff --git a/Documentation/nvme-feat-arbitration.txt b/Documentation/nvme-feat-arbitration.txt index 6f535239e2..2ca3283cec 100644 --- a/Documentation/nvme-feat-arbitration.txt +++ b/Documentation/nvme-feat-arbitration.txt @@ -48,6 +48,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-err-recovery.txt b/Documentation/nvme-feat-err-recovery.txt index 100023f34a..1e8ae1a940 100644 --- a/Documentation/nvme-feat-err-recovery.txt +++ b/Documentation/nvme-feat-err-recovery.txt @@ -52,6 +52,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-host-behavior-support.txt b/Documentation/nvme-feat-host-behavior-support.txt index a7b754f14e..cde43bc00b 100644 --- a/Documentation/nvme-feat-host-behavior-support.txt +++ b/Documentation/nvme-feat-host-behavior-support.txt @@ -55,6 +55,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-int-vector-config.txt b/Documentation/nvme-feat-int-vector-config.txt index d3ca913de6..907bd1e145 100644 --- a/Documentation/nvme-feat-int-vector-config.txt +++ b/Documentation/nvme-feat-int-vector-config.txt @@ -41,6 +41,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-lba-range-type.txt b/Documentation/nvme-feat-lba-range-type.txt index 96cb57fdd0..8811bda5d6 100644 --- a/Documentation/nvme-feat-lba-range-type.txt +++ b/Documentation/nvme-feat-lba-range-type.txt @@ -69,6 +69,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-perfc.txt b/Documentation/nvme-feat-perfc.txt index cfaf13999b..0b50bfa02d 100644 --- a/Documentation/nvme-feat-perfc.txt +++ b/Documentation/nvme-feat-perfc.txt @@ -63,6 +63,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-power-meas.txt b/Documentation/nvme-feat-power-meas.txt index 5cfea4415b..da6d4b41ed 100644 --- a/Documentation/nvme-feat-power-meas.txt +++ b/Documentation/nvme-feat-power-meas.txt @@ -39,6 +39,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-power-thresh.txt b/Documentation/nvme-feat-power-thresh.txt index 0b89951d61..5e58d3850e 100644 --- a/Documentation/nvme-feat-power-thresh.txt +++ b/Documentation/nvme-feat-power-thresh.txt @@ -39,6 +39,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-timestamp.txt b/Documentation/nvme-feat-timestamp.txt index be341de172..3b31c1a694 100644 --- a/Documentation/nvme-feat-timestamp.txt +++ b/Documentation/nvme-feat-timestamp.txt @@ -36,6 +36,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -s:: --save:: diff --git a/Documentation/nvme-feat-write-atom-normal.txt b/Documentation/nvme-feat-write-atom-normal.txt index 4fe11eb895..95854ed0d1 100644 --- a/Documentation/nvme-feat-write-atom-normal.txt +++ b/Documentation/nvme-feat-write-atom-normal.txt @@ -36,6 +36,15 @@ OPTIONS --sel=:: Select (SEL): This field specifies which value of the attributes to return in the provided data: ++ +[] +|================== +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities +|================== -n :: --namespace-id=:: diff --git a/Documentation/nvme-ocp-get-latency-monitor.txt b/Documentation/nvme-ocp-get-latency-monitor.txt index 82f8ec7fdc..6dbefde583 100644 --- a/Documentation/nvme-ocp-get-latency-monitor.txt +++ b/Documentation/nvme-ocp-get-latency-monitor.txt @@ -39,12 +39,11 @@ OPTIONS + [] |================== -|Select|Description -|0|Current -|1|Default -|2|Saved -|3|Supported capabilities -|4-7|Reserved +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities |================== -u:: diff --git a/Documentation/nvme-ocp-get-plp-health-check-interval.txt b/Documentation/nvme-ocp-get-plp-health-check-interval.txt index bb08f6dec6..ac5e606b84 100644 --- a/Documentation/nvme-ocp-get-plp-health-check-interval.txt +++ b/Documentation/nvme-ocp-get-plp-health-check-interval.txt @@ -34,12 +34,11 @@ OPTIONS + [] |================== -|Select|Description -|0|Current -|1|Default -|2|Saved -|3|Supported capabilities -|4-7|Reserved +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities |================== include::global-options.txt[] diff --git a/Documentation/nvme-ocp-get-telemetry-profile.txt b/Documentation/nvme-ocp-get-telemetry-profile.txt index 7b0ceea43d..b33e7b6f38 100644 --- a/Documentation/nvme-ocp-get-telemetry-profile.txt +++ b/Documentation/nvme-ocp-get-telemetry-profile.txt @@ -39,12 +39,11 @@ OPTIONS + [] |================== -|Select|Description -|0|Current -|1|Default -|2|Saved -|3|Supported capabilities -|4-7|Reserved +|Select|Name|Description +|0|current|Current +|1|default|Default +|2|saved|Saved +|3|supported|Supported capabilities |================== -u:: diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 80c05e4275..fbf5d22127 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -138,14 +138,16 @@ static struct feat_args feat_tmpl; static struct argconfig_commandline_options feat_template[] = { OPT_FLAG("save", 's', &feat_tmpl.save, save), - OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel), + OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_END() }; /* For a command which uses -s itself */ static struct argconfig_commandline_options feat_template_save_long[] = { OPT_FLAG("save", 0, &feat_tmpl.save, save), - OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel), + OPT_BYTE("sel", 'S', &feat_tmpl.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_END() }; diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c index a9489d3a74..2581655a3f 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -92,7 +92,8 @@ int get_ocp_error_counters(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 945e632ed1..28559385df 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -446,7 +446,8 @@ static int ocp_get_latency_monitor_feature(int argc, char **argv, struct command }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), @@ -595,7 +596,8 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *acmd, OPT_ARGS(opts) = { OPT_BYTE("mode", 'm', &cfg.mode, mode), OPT_FLAG("save", 's', &cfg.save, save), - OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_BYTE("sel", 'S', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), OPT_END() }; @@ -1553,7 +1555,8 @@ static int ocp_get_telemetry_profile_feature(int argc, char **argv, struct comma }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), @@ -1729,7 +1732,8 @@ static int get_dssd_power_state_feature(int argc, char **argv, struct command *a }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_BYTE("sel", 'S', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_FLAG("all", 'a', &all_flag, all), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), OPT_END() @@ -1842,7 +1846,8 @@ static int get_plp_health_check_interval(int argc, char **argv, struct command * }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_BYTE("sel", 'S', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_END() }; @@ -1946,7 +1951,8 @@ static int get_dssd_async_event_config(int argc, char **argv, struct command *ac }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_BYTE("sel", 'S', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_END() }; @@ -2263,7 +2269,8 @@ static int get_error_injection(int argc, char **argv, struct command *acmd, stru OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), OPT_FLAG("all-ns", 'a', &all_ns_flag, all_ns), OPT_END() @@ -2431,7 +2438,8 @@ static int get_enable_ieee1667_silo(int argc, char **argv, struct command *acmd, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), OPT_END() }; @@ -2663,7 +2671,8 @@ static int ocp_get_idle_wakeup_time_config_feature(int argc, char **argv, }; OPT_ARGS(opts) = { - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("namespace-id", 'n', &cfg.nsid, nsid, nvme_nsid_values), OPT_FLAG("no-uuid", 'u', &no_uuid_flag, no_uuid), diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index 76dcd1a65d..579af38a30 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -207,7 +207,8 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, nvme_nsid_values), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), diff --git a/src/args.h b/src/args.h index e1c1a07955..febf242431 100644 --- a/src/args.h +++ b/src/args.h @@ -72,6 +72,9 @@ enum nvme_print_flags { */ extern struct argconfig_opt_val nvme_nsid_values[]; extern struct argconfig_opt_val nvme_timeout_values[]; +extern struct argconfig_opt_val nvme_sel_values[]; +/* The largest value of the Select field, nvme_sel_values names them */ +#define NVME_SEL_MAX NVME_GET_FEATURES_SEL_SUPPORTED extern const char *uuid_index; extern const char *namespace_id_desired; diff --git a/src/global-options.c b/src/global-options.c index c82a2aa46c..11bcfb53db 100644 --- a/src/global-options.c +++ b/src/global-options.c @@ -22,6 +22,15 @@ struct argconfig_opt_val nvme_nsid_values[] = { VAL_END() }; +/* The Select field of Get Features, see the NVM Express specification */ +struct argconfig_opt_val nvme_sel_values[] = { + VAL_BYTE("current", NVME_GET_FEATURES_SEL_CURRENT), + VAL_BYTE("default", NVME_GET_FEATURES_SEL_DEFAULT), + VAL_BYTE("saved", NVME_GET_FEATURES_SEL_SAVED), + VAL_BYTE("supported", NVME_GET_FEATURES_SEL_SUPPORTED), + VAL_END() +}; + /* No timeout: the largest timeout the kernel takes */ struct argconfig_opt_val nvme_timeout_values[] = { VAL_UINT("infinite", UINT32_MAX), diff --git a/src/nvme-cmds-feature.c b/src/nvme-cmds-feature.c index 420429cfd0..590ad716a5 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -265,7 +265,8 @@ static int get_feature(int argc, char **argv, struct command *acmd, feature_name), OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_desired, nvme_nsid_values), - OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_BYTE("sel", 's', &cfg.sel, sel, + nvme_sel_values, .max = NVME_SEL_MAX), OPT_UINT("data-len", 'l', &cfg.data_len, buf_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 33cad27828..261a6a253b 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -119,6 +119,12 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) + test('nvme-cli - sel-cli', + python3, + args: [files('nvme_sel_test.py'), nvme_exe, mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib]) + # The host and AVE discovery printers need the fabrics code. if want_fabrics and json_c_dep.found() and 'log' in selected_plugins test('nvme-cli - discovery-logs-cli', diff --git a/tests/cli/nvme_sel_test.py b/tests/cli/nvme_sel_test.py new file mode 100644 index 0000000000..fed9c49025 --- /dev/null +++ b/tests/cli/nvme_sel_test.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE LLC +# +# Authors: Daniel Wagner +"""Tests the Select option of get-feature: the numbers 0 to 3 and their +names current, default, saved and supported end up in the SEL field of +the Get Features command, other numbers are rejected before a command is +sent. + +Usage: python3 nvme_sel_test.py +""" +import os +import shutil +import sys +import tempfile +import unittest + +from nvme_mock_ipc import (MockIPCServer, make_mock_env, + resolve_mock_lib_path, run_nvme) + +_NVME_BIN = (sys.argv[1] + if len(sys.argv) > 1 and not sys.argv[1].startswith('-') + else 'nvme') +_MOCK_LIB = resolve_mock_lib_path("./libmock_nvme.so") + +_NVME_ADMIN_GET_FEATURES = 0x0a + + +class SelMockIPCServer(MockIPCServer): + + def __init__(self, sock_path): + super().__init__(sock_path) + self.sel = [] + + def handle_ioctl(self, conn, fd, request, opcode, nsid, + cdw10, cdw11, cdw12, cdw13, cdw14, cdw15, lpo, req_len): + if opcode == _NVME_ADMIN_GET_FEATURES: + self.sel.append((cdw10 >> 8) & 0x7) + self.send_response(conn, 0, payload=b"\x00" * req_len) + + +class SelCLITest(unittest.TestCase): + + DEVICE = '/dev/nvme0' + + def setUp(self): + self.sysfs_dir = tempfile.mkdtemp(prefix='nvme-sel-sysfs-', + dir='/tmp') + self.base_dir = tempfile.mkdtemp(prefix='nvme-sel-base-', dir='/tmp') + self.ipc_dir = tempfile.mkdtemp(prefix='nvme-sel-ipc-', dir='/tmp') + self.ipc_sock_path = os.path.join(self.ipc_dir, "ipc.sock") + + self.server = SelMockIPCServer(self.ipc_sock_path) + self.server.start() + + self.env = make_mock_env(_MOCK_LIB, self.ipc_sock_path) + + def tearDown(self): + self.server.shutdown() + self.server.join() + + shutil.rmtree(self.sysfs_dir, ignore_errors=True) + shutil.rmtree(self.base_dir, ignore_errors=True) + shutil.rmtree(self.ipc_dir, ignore_errors=True) + + def _get_feature(self, *args): + return run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'get-feature', self.DEVICE, '--feature-id=2', *args) + + def test_sel_values(self): + for arg, sel in (('--sel=0', 0), ('--sel=3', 3), + ('--sel=current', 0), ('--sel=default', 1), + ('--sel=saved', 2), ('--sel=supported', 3), + ('-ssaved', 2)): + with self.subTest(arg=arg): + self.server.sel.clear() + result = self._get_feature(arg) + self.assertEqual(result.returncode, 0, + f'{arg} failed:\nstdout:\n{result.stdout}' + f'\nstderr:\n{result.stderr}') + self.assertEqual(self.server.sel, [sel]) + + def test_sel_rejected(self): + for arg in ('--sel=4', '--sel=7', '--sel=bogus'): + with self.subTest(arg=arg): + self.server.sel.clear() + result = self._get_feature(arg) + self.assertNotEqual(result.returncode, 0) + self.assertIn('--sel', result.stderr) + self.assertEqual(self.server.sel, []) + + +if __name__ == '__main__': + unittest.main(argv=[sys.argv[0]] + sys.argv[3:]) From f427b546c713e85638bec9bd8e416a33842a7d1b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:57:39 +0000 Subject: [PATCH 203/263] utils: report the value type and range of an option The metadata told only whether an option takes a value and its metavar, so the completions guessed paths from the metavars FILE and DIRECTORY and kept a list of values for --sel and --output-format-version, which offered 0 to 3 for the --sel of the lm commands as well. Add "type", the kind of value an option takes, and "range", the numbers an unsigned option is limited to, to the options in the metadata. Both are optional in the schema, so it stays at version 2. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata-schema.json | 15 ++++++ plugins/utils/command-metadata.c | 49 +++++++++++++++++++ plugins/utils/command-metadata.h | 1 + tests/unit/py/test_command_metadata_schema.py | 19 +++++++ 4 files changed, 84 insertions(+) diff --git a/plugins/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index a7a619519d..e5d164f0de 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -113,6 +113,21 @@ "description": "Whether the option consumes an argument.", "enum": ["none", "required", "optional"] }, + "type": { + "type": "string", + "description": "The kind of value: flag (no value), count (no value, may be repeated), int, u8, u16, u32 and u64 (numbers of that size; the unsigned ones take no negative number), size (a number with an optional binary suffix such as Ki), double, string, enum (one of \"values\"), file or directory (a path).", + "enum": ["flag", "count", "int", "u8", "u16", "u32", "u64", "size", "double", "string", "enum", "file", "directory"] + }, + "range": { + "type": "object", + "description": "The numbers an unsigned option accepts, if fewer than its type holds. The names in \"values\" are accepted besides. Absent otherwise.", + "properties": { + "min": { "type": "integer", "minimum": 0 }, + "max": { "type": "integer", "minimum": 0 } + }, + "required": ["min", "max"], + "additionalProperties": false + }, "metavar": { "type": "string", "description": "Placeholder name for the option's value (e.g. \"NUM\", \"FMT\"). Present only for options that take a value." diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index 5542e19736..b00ebdd212 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -326,6 +326,8 @@ static int copy_options(const struct argconfig_commandline_options *opts, dst[i].config_type = opts[i].config_type; dst[i].argument_type = opts[i].argument_type; dst[i].hidden = opts[i].deprecated; + dst[i].min = opts[i].min; + dst[i].max = opts[i].max; } *out = dst; @@ -667,6 +669,44 @@ static struct json_object *json_option_values(const struct command_metadata_opti return vals; } +/* The kind of value an option takes, see argconfig_parse_type() */ +static const char *opt_type(const struct command_metadata_option *o) +{ + switch (o->config_type) { + case CFG_FLAG: + return "flag"; + case CFG_INCREMENT: + return "count"; + case CFG_INT: + return "int"; + case CFG_BYTE: + return "u8"; + case CFG_SHORT: + return "u16"; + case CFG_POSITIVE: + return "u32"; + case CFG_LONG: + return "u64"; + case CFG_LONG_SUFFIX: + return "size"; + case CFG_DOUBLE: + return "double"; + case CFG_STRING: + return "string"; + case CFG_ENUM: + return "enum"; + case CFG_FILE: + return "file"; + case CFG_DIR: + return "directory"; + case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: + break; + } + + return NULL; +} + /* Build one option as a json object and add it to the given array. */ static void json_option(struct json_object *arr, const struct command_metadata_option *o, bool global) @@ -682,6 +722,15 @@ static void json_option(struct json_object *arr, const struct command_metadata_o if (o->short_option) json_object_add_value_string(jo, "short", shortbuf); json_object_add_value_string(jo, "argument", opt_argument(o)); + if (opt_type(o)) + json_object_add_value_string(jo, "type", opt_type(o)); + if (o->max) { + struct json_object *range = json_create_object(); + + json_object_add_value_uint64(range, "min", o->min); + json_object_add_value_uint64(range, "max", o->max); + json_object_add_value_object(jo, "range", range); + } if (o->meta && opt_takes_value(o)) json_object_add_value_string(jo, "metavar", o->meta); if (o->help) diff --git a/plugins/utils/command-metadata.h b/plugins/utils/command-metadata.h index 3c259d2743..280b97b939 100644 --- a/plugins/utils/command-metadata.h +++ b/plugins/utils/command-metadata.h @@ -30,6 +30,7 @@ struct command_metadata_option { bool hidden; /* not shown in help/completion */ const char *deprecated; /* the replacement, or NULL */ const char *old_name; /* the long name it replaced, or NULL */ + uint64_t min, max; /* the range of a number, if max */ }; struct command_metadata_command { diff --git a/tests/unit/py/test_command_metadata_schema.py b/tests/unit/py/test_command_metadata_schema.py index 4d7a4ee7d3..7505ae661f 100644 --- a/tests/unit/py/test_command_metadata_schema.py +++ b/tests/unit/py/test_command_metadata_schema.py @@ -97,6 +97,9 @@ def test_schema_rejects_malformed(self): lambda d: d["commands"][0]["options"][0].update(surprise=1), lambda d: d["commands"][0].pop("device"), lambda d: d["commands"][0].update(device="bogus"), + lambda d: d["commands"][0]["options"][0].update(type="bogus"), + lambda d: d["commands"][0]["options"][0].update( + range={"min": 0}), ] for mutate in cases: bad = copy.deepcopy(self.data) @@ -174,6 +177,22 @@ def test_deprecated(self): if o["long"] == "no-reuse") self.assertEqual(opt.get("old_name"), "force") + def test_value_types(self): + """Options carry the kind of value they take and a range if + they take fewer numbers than their type holds.""" + cmds = {c["name"]: c for c in self.data["commands"]} + opts = {o["long"]: o for o in cmds["get-feature"]["options"]} + self.assertEqual(opts["sel"]["type"], "u8") + self.assertEqual(opts["sel"]["range"], {"min": 0, "max": 3}) + self.assertIn("saved", opts["sel"]["values"]) + self.assertEqual(opts["namespace-id"]["type"], "u32") + self.assertNotIn("range", opts["namespace-id"]) + self.assertEqual(opts["output-format-version"]["range"], + {"min": 1, "max": 2}) + self.assertEqual(opts["verbose"]["type"], "count") + opts = {o["long"]: o for o in cmds["fw-download"]["options"]} + self.assertEqual(opts["fw"]["type"], "file") + if __name__ == "__main__": # argv[1:3] are consumed above; hand unittest only its own args. From 0031e97fbbe12bcfdd25f7277a3097cd61eac086 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:59:17 +0000 Subject: [PATCH 204/263] completions: complete by the type and range of an option The generator completed paths for options with the metavar FILE or DIRECTORY and offered values from a list of option names, which applied 0 to 3 to the --sel of the lm commands too, where it means something else. Complete paths for the options of the type file or directory and offer the numbers of a range of up to 16 numbers, followed by the names, and drop the list. The fixture gets types and ranges, and a test checks what the generator makes of them. Signed-off-by: Daniel Wagner --- completions/TESTING.md | 4 +- completions/generate-completions.py | 23 +++----- completions/test-command-metadata.json | 26 ++++++-- tests/unit/py/meson.build | 12 ++++ .../py/test_generate_completions_values.py | 59 +++++++++++++++++++ 5 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 tests/unit/py/test_generate_completions_values.py diff --git a/completions/TESTING.md b/completions/TESTING.md index 09797e56a1..1eabbcbefa 100644 --- a/completions/TESTING.md +++ b/completions/TESTING.md @@ -69,8 +69,8 @@ These behaviors are identical in bash, zsh, and PowerShell. - [ ] **Enumerated value** — `nvme id ctrl --output-format `, `nvme id ctrl -o `, and `nvme id ctrl --output-format=` all list `normal json binary tabular`. -- [ ] **Value from a hint** — `nvme feat power-meas --sel ` lists - `0 1 2 3`. +- [ ] **Value from a range** — `nvme feat power-meas --sel ` lists + `0 1 2 3 current default saved supported`. - [ ] **Command-local value** — `nvme fw commit --action ` lists `replace … set-active …`. - [ ] **File-valued option** — `nvme fw download --fw ` completes diff --git a/completions/generate-completions.py b/completions/generate-completions.py index 1ea645b426..a9adad54ae 100755 --- a/completions/generate-completions.py +++ b/completions/generate-completions.py @@ -53,15 +53,8 @@ def cmd_names(cmd): return [n for n in (s.strip() for s in names) if n] -# Completion values for options the metadata carries none for. Keyed by long -# option name, applied to every command with it, so only list values that are -# uniform across all commands. -# NOTE: This is a stopgap. These should probably be defined in the C option -# definitions (OPT_VALS) so they flow through the metadata for all tooling. -VALUE_HINTS = { - "output-format-version": ["1", "2"], - "sel": ["0", "1", "2", "3"], -} +# An option whose range holds at most this many numbers gets them offered. +MAX_RANGE_VALUES = 16 # help and version are matched as literal strings in the C dispatcher, not # registered in any command table, so dump-command-metadata never reports them. @@ -75,14 +68,16 @@ def command_options(cmd): Hidden options are always skipped: they are accepted on the command line but suppressed from --help, so they should not be offered as completions. - Options that have no enforced value set but do have a completion hint (see - VALUE_HINTS) are yielded with that hint filled into "values". + An option with a small "range" is yielded with its numbers in "values", + ahead of its names. """ for opt in cmd.get("options", []): if opt.get("hidden"): continue - if "values" not in opt and opt["long"] in VALUE_HINTS: - opt = dict(opt, values=VALUE_HINTS[opt["long"]]) + rng = opt.get("range") + if rng and rng["max"] - rng["min"] < MAX_RANGE_VALUES: + numbers = [str(n) for n in range(rng["min"], rng["max"] + 1)] + opt = dict(opt, values=numbers + opt.get("values", [])) yield opt @@ -349,7 +344,7 @@ def bash_valopt_words(opts): def opt_is_file(opt): """True if the option's value is a filename/path we can file-complete.""" - return opt.get("metavar") in ("FILE", "DIRECTORY") + return opt.get("type") in ("file", "directory") def opt_spellings(opt): diff --git a/completions/test-command-metadata.json b/completions/test-command-metadata.json index 82cca1df9c..267238adb5 100644 --- a/completions/test-command-metadata.json +++ b/completions/test-command-metadata.json @@ -14,6 +14,7 @@ "long": "mode", "short": "m", "argument": "required", + "type": "enum", "metavar": "MODE", "description": "operating mode", "values": ["fast", "slow"] @@ -22,12 +23,14 @@ "long": "force", "short": "x", "argument": "none", + "type": "flag", "description": "don't prompt for 'confirmation'" }, { "long": "format", "short": "o", "argument": "required", + "type": "string", "metavar": "FMT", "description": "output format", "global": true, @@ -36,12 +39,14 @@ { "long": "secret", "argument": "none", + "type": "flag", "hidden": true, "description": "undocumented, hidden from help" }, { "long": "verbose-level", "argument": "optional", + "type": "string", "description": "verbosity level" } ] @@ -61,6 +66,7 @@ "long": "wait-time", "short": "w", "argument": "required", + "type": "u32", "metavar": "NUM", "description": "milliseconds to wait" }, @@ -68,6 +74,7 @@ "long": "format", "short": "o", "argument": "required", + "type": "string", "metavar": "FMT", "description": "output format", "global": true, @@ -77,20 +84,23 @@ "long": "in-file", "short": "f", "argument": "required", + "type": "file", "metavar": "FILE", "description": "input file path" }, { "long": "format-version", "argument": "required", + "type": "u32", + "range": {"min": 1, "max": 2}, "metavar": "VER", "description": "metadata format version", - "global": true, - "values": ["1", "2"] + "global": true }, { "long": "dry-run", "argument": "none", + "type": "flag", "description": "print actions without running them" } ] @@ -110,13 +120,15 @@ { "long": "out-dir", "argument": "required", - "metavar": "DIRECTORY", + "type": "directory", + "metavar": "PATH", "description": "output directory" }, { "long": "format", "short": "o", "argument": "required", + "type": "string", "metavar": "FMT", "description": "output format", "global": true, @@ -126,13 +138,17 @@ "long": "sel", "short": "S", "argument": "required", + "type": "u8", + "range": {"min": 0, "max": 3}, "metavar": "SEL", - "description": "selection source" + "description": "selection source", + "values": ["current", "saved"] }, { "long": "in-file", "short": "f", "argument": "required", + "type": "file", "metavar": "FILE", "description": "input file path" } @@ -159,6 +175,7 @@ "long": "mode", "short": "m", "argument": "required", + "type": "enum", "metavar": "MODE", "description": "operating mode", "values": ["fast", "slow"] @@ -166,6 +183,7 @@ { "long": "secret", "argument": "none", + "type": "flag", "hidden": true, "description": "undocumented, hidden from help" } diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index 1651d71776..e0eb685754 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -91,6 +91,18 @@ if python3.found() timeout: 60, ) + # The options complete what their type and range in the metadata say. + test( + 'nvme-cli - generate-completions-values', + python3, + args: [ + files('test_generate_completions_values.py'), + meson.project_source_root() / 'completions' / 'generate-completions.py', + meson.project_source_root() / 'completions' / 'test-command-metadata.json', + ], + timeout: 60, + ) + # The completion test fixture is a hand-written, synthetic metadata model; # validate it against the schema so a schema change the fixture no longer # satisfies is caught. Both inputs are committed, so no binary needed. diff --git a/tests/unit/py/test_generate_completions_values.py b/tests/unit/py/test_generate_completions_values.py new file mode 100644 index 0000000000..68a5e4dc2c --- /dev/null +++ b/tests/unit/py/test_generate_completions_values.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE LLC +# +# Authors: Daniel Wagner +"""Test that generate-completions.py completes what the metadata types say. + +An option of the type file or directory completes paths, whatever its +metavar. An option with a small range offers its numbers, followed by its +names. Checked on the bash and PowerShell output for the fixture. + +Usage: test_generate_completions_values.py +""" +import subprocess +import sys +import unittest + +if len(sys.argv) < 3: + print("usage: %s " % sys.argv[0]) + sys.exit(77) + +GENERATOR, FIXTURE = sys.argv[1], sys.argv[2] + + +def generate(shell): + return subprocess.run([sys.executable, GENERATOR, FIXTURE, + "--" + shell, "-"], + capture_output=True, text=True, check=True).stdout + + +class CompletionValuesTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.bash = generate("bash") + cls.powershell = generate("powershell") + + def test_paths(self): + self.assertIn('_nvme_opt_files "--in-file -f" "--out-dir"', + self.bash) + self.assertIn("'zeta-plug report --out-dir' = $true", + self.powershell) + + def test_range(self): + self.assertIn('"--sel -S" "0 1 2 3 current saved"', self.bash) + self.assertIn('"--format-version" "1 2"', self.bash) + self.assertIn("'zeta-plug report --sel' = " + "@('0', '1', '2', '3', 'current', 'saved')", + self.powershell) + + def test_no_guess_from_name(self): + # a number without a range offers nothing + self.assertNotIn('"--wait-time -w"', self.bash) + + +if __name__ == "__main__": + unittest.main(argv=[sys.argv[0]] + sys.argv[3:]) From 6e33d9bf9fddfe79acf78792a5cf5e5a6c598b9b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:59:35 +0000 Subject: [PATCH 205/263] completions: update the shell completions Regenerate the completions for the value types and ranges of the options. Signed-off-by: Daniel Wagner --- completions/_nvme | 78 ++++++++------- completions/bash-nvme-completion.sh | 78 ++++++++------- completions/nvme-completion.ps1 | 149 +++++++++++++++------------- 3 files changed, 165 insertions(+), 140 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 3aa2694ec5..6a6626a5a3 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -1290,7 +1290,7 @@ _nvme () { (arbitration) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_arbitration _feat_arbitration=( @@ -1327,7 +1327,7 @@ _nvme () { (async-event-conf) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_async_event_conf _feat_async_event_conf=( @@ -1360,7 +1360,7 @@ _nvme () { _nvme_opt_vals "--nsid -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_err_recovery _feat_err_recovery=( @@ -1396,7 +1396,7 @@ _nvme () { (hctm) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_hctm _feat_hctm=( @@ -1430,7 +1430,7 @@ _nvme () { (host-behavior-support) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_host_behavior_support _feat_host_behavior_support=( @@ -1469,7 +1469,7 @@ _nvme () { (int-coalesce) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_int_coalesce _feat_int_coalesce=( @@ -1503,7 +1503,7 @@ _nvme () { (int-vector-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_int_vector_config _feat_int_vector_config=( @@ -1537,7 +1537,7 @@ _nvme () { (keep-alive-timer) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_keep_alive_timer _feat_keep_alive_timer=( @@ -1570,7 +1570,7 @@ _nvme () { _nvme_opt_vals "--nsid -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_lba_range_type _feat_lba_range_type=( @@ -1614,7 +1614,7 @@ _nvme () { (num-queues) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_num_queues _feat_num_queues=( @@ -1649,7 +1649,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return _nvme_opt_files "--vs-data -V" && return local _feat_perf_characteristics @@ -1694,7 +1694,7 @@ _nvme () { (power-limit) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_power_limit _feat_power_limit=( @@ -1730,7 +1730,7 @@ _nvme () { (power-meas) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_power_meas _feat_power_meas=( @@ -1765,7 +1765,7 @@ _nvme () { (power-mgmt) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_power_mgmt _feat_power_mgmt=( @@ -1799,7 +1799,7 @@ _nvme () { (power-thresh) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_power_thresh _feat_power_thresh=( @@ -1839,7 +1839,7 @@ _nvme () { (temp-thresh) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_temp_thresh _feat_temp_thresh=( @@ -1876,7 +1876,7 @@ _nvme () { (timestamp) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_timestamp _feat_timestamp=( @@ -1908,7 +1908,7 @@ _nvme () { (volatile-wc) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_volatile_wc _feat_volatile_wc=( @@ -1940,7 +1940,7 @@ _nvme () { (write-atom-normal) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _feat_write_atom_normal _feat_write_atom_normal=( @@ -3350,6 +3350,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _keys_export _keys_export=( --dry-run':show command instead of executing' @@ -3408,6 +3409,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _keys_gen_tls_psk _keys_gen_tls_psk=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -3452,6 +3454,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _keys_import _keys_import=( --dry-run':show command instead of executing' @@ -3484,6 +3487,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _keys_insert_tls_psk _keys_insert_tls_psk=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -3662,7 +3666,6 @@ _nvme () { (migration-recv) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _lm_migration_recv @@ -3705,7 +3708,6 @@ _nvme () { (migration-send) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -f" && return local _lm_migration_send @@ -3784,7 +3786,6 @@ _nvme () { (track-send) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" && return local _lm_track_send _lm_track_send=( @@ -6056,6 +6057,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--nbft-path" && return local _nbft_show _nbft_show=( --discovery':show NBFT discovery controllers' @@ -6631,7 +6633,7 @@ _nvme () { (eol-plp-failure-mode) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_eol_plp_failure_mode _ocp_eol_plp_failure_mode=( @@ -6718,7 +6720,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_clear_pcie_correctable_errors _ocp_get_clear_pcie_correctable_errors=( @@ -6750,7 +6752,7 @@ _nvme () { (get-dssd-async-event-config) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_dssd_async_event_config _ocp_get_dssd_async_event_config=( @@ -6778,7 +6780,7 @@ _nvme () { (get-dssd-power-state-feature) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_dssd_power_state_feature _ocp_get_dssd_power_state_feature=( @@ -6810,7 +6812,7 @@ _nvme () { (get-enable-ieee1667-silo) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_enable_ieee1667_silo _ocp_get_enable_ieee1667_silo=( @@ -6840,7 +6842,7 @@ _nvme () { (get-error-injection) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_error_injection _ocp_get_error_injection=( @@ -6873,7 +6875,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_idle_wakeup_time _ocp_get_idle_wakeup_time=( @@ -6906,7 +6908,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_latency_monitor _ocp_get_latency_monitor=( @@ -6938,7 +6940,7 @@ _nvme () { (get-plp-health-check-interval) _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_plp_health_check_interval _ocp_get_plp_health_check_interval=( @@ -6967,7 +6969,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _ocp_get_telemetry_profile _ocp_get_telemetry_profile=( @@ -7031,7 +7033,9 @@ _nvme () { "--output-format-version" "1 2" \ "--telemetry-type -t" "host controller host0 host1" \ "--timeout" "infinite -1" && return - _nvme_opt_files "--output-file -f" && return + _nvme_opt_files "--output-file -f" \ + "--string-log -s" \ + "--telemetry-log -l" && return local _ocp_internal_log _ocp_internal_log=( --data-area=':Telemetry Data Area; 1, 2, 3, or 4; e.g. '\''-a 1 for Data Area 1.'\'' e.g. '\''-a 2 for Data Areas 1 and 2.'\'' e.g. '\''-a 3 for Data Areas 1, 2, and 3.'\'' e.g. '\''-a 4 for Data Areas 1, 2, 3, and 4.'\'';' @@ -9051,7 +9055,7 @@ _nvme () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _shannon_get_additional_feature _shannon_get_additional_feature=( @@ -13022,6 +13026,7 @@ _nvme () { "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ + "--nbft-path" \ "--raw -r" && return local _connect_all _connect_all=( @@ -13563,6 +13568,7 @@ _nvme () { "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ + "--nbft-path" \ "--raw -r" && return local _discover _discover=( @@ -14109,6 +14115,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _gen_tls_key _gen_tls_key=( --compat':Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.' @@ -14154,7 +14161,7 @@ _nvme () { "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" && return local _get_feature _get_feature=( @@ -16449,6 +16456,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_files "--keyfile -f" && return local _tls_key _tls_key=( --dry-run':show command instead of executing' diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index ae204e8e02..06a5be7a6c 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -293,7 +293,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" - _nvme_opt_files "--config -J" "--raw -r" + _nvme_opt_files "--config -J" "--nbft-path" "--raw -r" nodevice=1 ;; @@ -404,7 +404,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" - _nvme_opt_files "--config -J" "--raw -r" + _nvme_opt_files "--config -J" "--nbft-path" "--raw -r" nodevice=1 ;; @@ -550,6 +550,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -561,7 +562,7 @@ nvme_list_opts () { "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1230,6 +1231,7 @@ nvme_list_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -1717,7 +1719,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1727,7 +1729,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1738,7 +1740,7 @@ plugin_feat_opts () { _nvme_opt_vals "--nsid -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1748,7 +1750,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1758,7 +1760,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1768,7 +1770,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1778,7 +1780,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1788,7 +1790,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1799,7 +1801,7 @@ plugin_feat_opts () { _nvme_opt_vals "--nsid -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1809,7 +1811,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1820,7 +1822,7 @@ plugin_feat_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" _nvme_opt_files "--vs-data -V" ;; @@ -1831,7 +1833,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1841,7 +1843,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1851,7 +1853,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1861,7 +1863,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1871,7 +1873,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1881,7 +1883,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1891,7 +1893,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -1901,7 +1903,7 @@ plugin_feat_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; esac @@ -2452,6 +2454,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -2472,6 +2475,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -2482,6 +2486,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -2492,6 +2497,7 @@ plugin_keys_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" nodevice=1 ;; @@ -2556,7 +2562,6 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -2567,7 +2572,6 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" _nvme_opt_files "--input-file -f" ;; @@ -2587,7 +2591,6 @@ plugin_lm_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ "--timeout" "infinite -1" ;; esac @@ -3382,6 +3385,7 @@ plugin_nbft_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" + _nvme_opt_files "--nbft-path" nodevice=1 ;; esac @@ -3605,7 +3609,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3634,7 +3638,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3644,7 +3648,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3654,7 +3658,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3664,7 +3668,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3674,7 +3678,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3685,7 +3689,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3696,7 +3700,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3706,7 +3710,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -S" "0 1 2 3" \ + "--sel -S" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3717,7 +3721,7 @@ plugin_ocp_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; @@ -3739,7 +3743,7 @@ plugin_ocp_opts () { "--output-format-version" "1 2" \ "--telemetry-type -t" "host controller host0 host1" \ "--timeout" "infinite -1" - _nvme_opt_files "--output-file -f" + _nvme_opt_files "--output-file -f" "--string-log -s" "--telemetry-log -l" ;; "latency-monitor-log") @@ -4468,7 +4472,7 @@ plugin_shannon_opts () { _nvme_opt_vals "--namespace-id -n" "all -1" \ "--output-format -o" "normal json binary" \ "--output-format-version" "1 2" \ - "--sel -s" "0 1 2 3" \ + "--sel -s" "0 1 2 3 current default saved supported" \ "--timeout" "infinite -1" ;; diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 98e78d0ede..0ed2817ad2 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -798,6 +798,7 @@ $script:NvmeFileOptions = @{ 'connect --devid-file' = $true 'connect -J' = $true 'connect-all --config' = $true + 'connect-all --nbft-path' = $true 'connect-all --raw' = $true 'connect-all -J' = $true 'connect-all -r' = $true @@ -806,6 +807,7 @@ $script:NvmeFileOptions = @{ 'dir-send --input-file' = $true 'dir-send -i' = $true 'discover --config' = $true + 'discover --nbft-path' = $true 'discover --raw' = $true 'discover -J' = $true 'discover -r' = $true @@ -815,6 +817,8 @@ $script:NvmeFileOptions = @{ 'fw download -f' = $true 'fw-download --fw' = $true 'fw-download -f' = $true + 'gen-tls-key --keyfile' = $true + 'gen-tls-key -f' = $true 'intel internal-log --output-file' = $true 'intel internal-log -O' = $true 'io-mgmt recv --data' = $true @@ -829,6 +833,14 @@ $script:NvmeFileOptions = @{ 'io-passthru --metadata' = $true 'io-passthru -M' = $true 'io-passthru -i' = $true + 'keys export --keyfile' = $true + 'keys export -f' = $true + 'keys gen-tls-psk --keyfile' = $true + 'keys gen-tls-psk -f' = $true + 'keys import --keyfile' = $true + 'keys import -f' = $true + 'keys insert-tls-psk --keyfile' = $true + 'keys insert-tls-psk -f' = $true 'lm migration-recv --output-file' = $true 'lm migration-recv -f' = $true 'lm migration-send --input-file' = $true @@ -843,6 +855,7 @@ $script:NvmeFileOptions = @{ 'micron select-download -f' = $true 'micron vs-internal-log --package' = $true 'micron vs-internal-log -p' = $true + 'nbft show --nbft-path' = $true 'nvme-mi recv --input-file' = $true 'nvme-mi recv -i' = $true 'nvme-mi send --input-file' = $true @@ -852,7 +865,11 @@ $script:NvmeFileOptions = @{ 'nvme-mi-send --input-file' = $true 'nvme-mi-send -i' = $true 'ocp internal-log --output-file' = $true + 'ocp internal-log --string-log' = $true + 'ocp internal-log --telemetry-log' = $true 'ocp internal-log -f' = $true + 'ocp internal-log -l' = $true + 'ocp internal-log -s' = $true 'ocp set-error-injection --data' = $true 'ocp set-error-injection -d' = $true 'ocp telemetry-string-log --output-file' = $true @@ -903,6 +920,8 @@ $script:NvmeFileOptions = @{ 'solidigm vs-internal-log -d' = $true 'telemetry-log --output-file' = $true 'telemetry-log -O' = $true + 'tls-key --keyfile' = $true + 'tls-key -f' = $true 'toshiba vs-internal-log --output-file' = $true 'toshiba vs-internal-log -O' = $true 'toshiba vs-smart-add-log --output-file' = $true @@ -1212,123 +1231,123 @@ $script:NvmeOptionValues = @{ 'fdp usage -o' = @('normal', 'json', 'binary') 'feat arbitration --output-format' = @('normal', 'json', 'binary') 'feat arbitration --output-format-version' = @('1', '2') - 'feat arbitration --sel' = @('0', '1', '2', '3') + 'feat arbitration --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat arbitration --timeout' = @('infinite', '-1') - 'feat arbitration -S' = @('0', '1', '2', '3') + 'feat arbitration -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat arbitration -o' = @('normal', 'json', 'binary') 'feat async-event-conf --output-format' = @('normal', 'json', 'binary') 'feat async-event-conf --output-format-version' = @('1', '2') - 'feat async-event-conf --sel' = @('0', '1', '2', '3') + 'feat async-event-conf --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat async-event-conf --timeout' = @('infinite', '-1') - 'feat async-event-conf -S' = @('0', '1', '2', '3') + 'feat async-event-conf -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat async-event-conf -o' = @('normal', 'json', 'binary') 'feat err-recovery --nsid' = @('all', '-1') 'feat err-recovery --output-format' = @('normal', 'json', 'binary') 'feat err-recovery --output-format-version' = @('1', '2') - 'feat err-recovery --sel' = @('0', '1', '2', '3') + 'feat err-recovery --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat err-recovery --timeout' = @('infinite', '-1') - 'feat err-recovery -S' = @('0', '1', '2', '3') + 'feat err-recovery -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat err-recovery -n' = @('all', '-1') 'feat err-recovery -o' = @('normal', 'json', 'binary') 'feat hctm --output-format' = @('normal', 'json', 'binary') 'feat hctm --output-format-version' = @('1', '2') - 'feat hctm --sel' = @('0', '1', '2', '3') + 'feat hctm --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat hctm --timeout' = @('infinite', '-1') - 'feat hctm -S' = @('0', '1', '2', '3') + 'feat hctm -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat hctm -o' = @('normal', 'json', 'binary') 'feat host-behavior-support --output-format' = @('normal', 'json', 'binary') 'feat host-behavior-support --output-format-version' = @('1', '2') - 'feat host-behavior-support --sel' = @('0', '1', '2', '3') + 'feat host-behavior-support --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat host-behavior-support --timeout' = @('infinite', '-1') - 'feat host-behavior-support -S' = @('0', '1', '2', '3') + 'feat host-behavior-support -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat host-behavior-support -o' = @('normal', 'json', 'binary') 'feat int-coalesce --output-format' = @('normal', 'json', 'binary') 'feat int-coalesce --output-format-version' = @('1', '2') - 'feat int-coalesce --sel' = @('0', '1', '2', '3') + 'feat int-coalesce --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat int-coalesce --timeout' = @('infinite', '-1') - 'feat int-coalesce -S' = @('0', '1', '2', '3') + 'feat int-coalesce -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat int-coalesce -o' = @('normal', 'json', 'binary') 'feat int-vector-config --output-format' = @('normal', 'json', 'binary') 'feat int-vector-config --output-format-version' = @('1', '2') - 'feat int-vector-config --sel' = @('0', '1', '2', '3') + 'feat int-vector-config --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat int-vector-config --timeout' = @('infinite', '-1') - 'feat int-vector-config -S' = @('0', '1', '2', '3') + 'feat int-vector-config -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat int-vector-config -o' = @('normal', 'json', 'binary') 'feat keep-alive-timer --output-format' = @('normal', 'json', 'binary') 'feat keep-alive-timer --output-format-version' = @('1', '2') - 'feat keep-alive-timer --sel' = @('0', '1', '2', '3') + 'feat keep-alive-timer --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat keep-alive-timer --timeout' = @('infinite', '-1') - 'feat keep-alive-timer -S' = @('0', '1', '2', '3') + 'feat keep-alive-timer -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat keep-alive-timer -o' = @('normal', 'json', 'binary') 'feat lba-range-type --nsid' = @('all', '-1') 'feat lba-range-type --output-format' = @('normal', 'json', 'binary') 'feat lba-range-type --output-format-version' = @('1', '2') - 'feat lba-range-type --sel' = @('0', '1', '2', '3') + 'feat lba-range-type --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat lba-range-type --timeout' = @('infinite', '-1') - 'feat lba-range-type -S' = @('0', '1', '2', '3') + 'feat lba-range-type -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat lba-range-type -n' = @('all', '-1') 'feat lba-range-type -o' = @('normal', 'json', 'binary') 'feat num-queues --output-format' = @('normal', 'json', 'binary') 'feat num-queues --output-format-version' = @('1', '2') - 'feat num-queues --sel' = @('0', '1', '2', '3') + 'feat num-queues --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat num-queues --timeout' = @('infinite', '-1') - 'feat num-queues -S' = @('0', '1', '2', '3') + 'feat num-queues -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat num-queues -o' = @('normal', 'json', 'binary') 'feat perf-characteristics --namespace-id' = @('all', '-1') 'feat perf-characteristics --output-format' = @('normal', 'json', 'binary') 'feat perf-characteristics --output-format-version' = @('1', '2') - 'feat perf-characteristics --sel' = @('0', '1', '2', '3') + 'feat perf-characteristics --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat perf-characteristics --timeout' = @('infinite', '-1') - 'feat perf-characteristics -S' = @('0', '1', '2', '3') + 'feat perf-characteristics -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat perf-characteristics -n' = @('all', '-1') 'feat perf-characteristics -o' = @('normal', 'json', 'binary') 'feat power-limit --output-format' = @('normal', 'json', 'binary') 'feat power-limit --output-format-version' = @('1', '2') - 'feat power-limit --sel' = @('0', '1', '2', '3') + 'feat power-limit --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-limit --timeout' = @('infinite', '-1') - 'feat power-limit -S' = @('0', '1', '2', '3') + 'feat power-limit -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-limit -o' = @('normal', 'json', 'binary') 'feat power-meas --output-format' = @('normal', 'json', 'binary') 'feat power-meas --output-format-version' = @('1', '2') - 'feat power-meas --sel' = @('0', '1', '2', '3') + 'feat power-meas --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-meas --timeout' = @('infinite', '-1') - 'feat power-meas -S' = @('0', '1', '2', '3') + 'feat power-meas -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-meas -o' = @('normal', 'json', 'binary') 'feat power-mgmt --output-format' = @('normal', 'json', 'binary') 'feat power-mgmt --output-format-version' = @('1', '2') - 'feat power-mgmt --sel' = @('0', '1', '2', '3') + 'feat power-mgmt --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-mgmt --timeout' = @('infinite', '-1') - 'feat power-mgmt -S' = @('0', '1', '2', '3') + 'feat power-mgmt -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-mgmt -o' = @('normal', 'json', 'binary') 'feat power-thresh --output-format' = @('normal', 'json', 'binary') 'feat power-thresh --output-format-version' = @('1', '2') - 'feat power-thresh --sel' = @('0', '1', '2', '3') + 'feat power-thresh --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-thresh --timeout' = @('infinite', '-1') - 'feat power-thresh -S' = @('0', '1', '2', '3') + 'feat power-thresh -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat power-thresh -o' = @('normal', 'json', 'binary') 'feat temp-thresh --output-format' = @('normal', 'json', 'binary') 'feat temp-thresh --output-format-version' = @('1', '2') - 'feat temp-thresh --sel' = @('0', '1', '2', '3') + 'feat temp-thresh --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat temp-thresh --timeout' = @('infinite', '-1') - 'feat temp-thresh -S' = @('0', '1', '2', '3') + 'feat temp-thresh -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat temp-thresh -o' = @('normal', 'json', 'binary') 'feat timestamp --output-format' = @('normal', 'json', 'binary') 'feat timestamp --output-format-version' = @('1', '2') - 'feat timestamp --sel' = @('0', '1', '2', '3') + 'feat timestamp --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat timestamp --timeout' = @('infinite', '-1') - 'feat timestamp -S' = @('0', '1', '2', '3') + 'feat timestamp -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat timestamp -o' = @('normal', 'json', 'binary') 'feat volatile-wc --output-format' = @('normal', 'json', 'binary') 'feat volatile-wc --output-format-version' = @('1', '2') - 'feat volatile-wc --sel' = @('0', '1', '2', '3') + 'feat volatile-wc --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat volatile-wc --timeout' = @('infinite', '-1') - 'feat volatile-wc -S' = @('0', '1', '2', '3') + 'feat volatile-wc -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat volatile-wc -o' = @('normal', 'json', 'binary') 'feat write-atom-normal --output-format' = @('normal', 'json', 'binary') 'feat write-atom-normal --output-format-version' = @('1', '2') - 'feat write-atom-normal --sel' = @('0', '1', '2', '3') + 'feat write-atom-normal --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat write-atom-normal --timeout' = @('infinite', '-1') - 'feat write-atom-normal -S' = @('0', '1', '2', '3') + 'feat write-atom-normal -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'feat write-atom-normal -o' = @('normal', 'json', 'binary') 'fid-support-effects-log --output-format' = @('normal', 'json', 'binary') 'fid-support-effects-log --output-format-version' = @('1', '2') @@ -1398,12 +1417,12 @@ $script:NvmeOptionValues = @{ 'get-feature --namespace-id' = @('all', '-1') 'get-feature --output-format' = @('normal', 'json', 'binary') 'get-feature --output-format-version' = @('1', '2') - 'get-feature --sel' = @('0', '1', '2', '3') + 'get-feature --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'get-feature --timeout' = @('infinite', '-1') 'get-feature -f' = @('arbitration', 'power-mgmt', 'lba-range', 'temp-thresh', 'err-recovery', 'volatile-wc', 'num-queues', 'irq-coalesce', 'irq-config', 'write-atomic', 'async-event', 'auto-pst', 'host-mem-buf', 'timestamp', 'kato', 'hctm', 'nopsc', 'rrl', 'plm-config', 'plm-window', 'lba-sts-interval', 'host-behavior', 'sanitize', 'endurance-evt-cfg', 'iocs-profile', 'spinup-control', 'power-loss-signal', 'perf-characteristics', 'fdp', 'fdp-events', 'ns-admin-label', 'key-value', 'ctrl-data-queue', 'emb-mgmt-ctrl-addr', 'host-mgmt-agent-addr', 'enh-ctrl-metadata', 'ctrl-metadata', 'ns-metadata', 'sw-progress', 'host-id', 'resv-nf-mask', 'resv-persist', 'write-protect', 'bp-write-protect') 'get-feature -n' = @('all', '-1') 'get-feature -o' = @('normal', 'json', 'binary') - 'get-feature -s' = @('0', '1', '2', '3') + 'get-feature -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'get-lba-status --namespace-id' = @('all', '-1') 'get-lba-status --output-format' = @('normal', 'json', 'binary') 'get-lba-status --output-format-version' = @('1', '2') @@ -1728,26 +1747,20 @@ $script:NvmeOptionValues = @{ 'lm get-cdq -o' = @('normal', 'json', 'binary') 'lm migration-recv --output-format' = @('normal', 'json', 'binary') 'lm migration-recv --output-format-version' = @('1', '2') - 'lm migration-recv --sel' = @('0', '1', '2', '3') 'lm migration-recv --timeout' = @('infinite', '-1') 'lm migration-recv -o' = @('normal', 'json', 'binary') - 'lm migration-recv -s' = @('0', '1', '2', '3') 'lm migration-send --output-format' = @('normal', 'json', 'binary') 'lm migration-send --output-format-version' = @('1', '2') - 'lm migration-send --sel' = @('0', '1', '2', '3') 'lm migration-send --timeout' = @('infinite', '-1') 'lm migration-send -o' = @('normal', 'json', 'binary') - 'lm migration-send -s' = @('0', '1', '2', '3') 'lm set-cdq --output-format' = @('normal', 'json', 'binary') 'lm set-cdq --output-format-version' = @('1', '2') 'lm set-cdq --timeout' = @('infinite', '-1') 'lm set-cdq -o' = @('normal', 'json', 'binary') 'lm track-send --output-format' = @('normal', 'json', 'binary') 'lm track-send --output-format-version' = @('1', '2') - 'lm track-send --sel' = @('0', '1', '2', '3') 'lm track-send --timeout' = @('infinite', '-1') 'lm track-send -o' = @('normal', 'json', 'binary') - 'lm track-send -s' = @('0', '1', '2', '3') 'lockdown --output-format' = @('normal', 'json', 'binary') 'lockdown --output-format-version' = @('1', '2') 'lockdown --timeout' = @('infinite', '-1') @@ -2192,9 +2205,9 @@ $script:NvmeOptionValues = @{ 'ocp device-capability-log -o' = @('normal', 'json', 'binary') 'ocp eol-plp-failure-mode --output-format' = @('normal', 'json', 'binary') 'ocp eol-plp-failure-mode --output-format-version' = @('1', '2') - 'ocp eol-plp-failure-mode --sel' = @('0', '1', '2', '3') + 'ocp eol-plp-failure-mode --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp eol-plp-failure-mode --timeout' = @('infinite', '-1') - 'ocp eol-plp-failure-mode -S' = @('0', '1', '2', '3') + 'ocp eol-plp-failure-mode -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp eol-plp-failure-mode -o' = @('normal', 'json', 'binary') 'ocp error-recovery-log --output-format' = @('normal', 'json', 'binary') 'ocp error-recovery-log --output-format-version' = @('1', '2') @@ -2207,65 +2220,65 @@ $script:NvmeOptionValues = @{ 'ocp get-clear-pcie-correctable-errors --namespace-id' = @('all', '-1') 'ocp get-clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary') 'ocp get-clear-pcie-correctable-errors --output-format-version' = @('1', '2') - 'ocp get-clear-pcie-correctable-errors --sel' = @('0', '1', '2', '3') + 'ocp get-clear-pcie-correctable-errors --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-clear-pcie-correctable-errors --timeout' = @('infinite', '-1') 'ocp get-clear-pcie-correctable-errors -n' = @('all', '-1') 'ocp get-clear-pcie-correctable-errors -o' = @('normal', 'json', 'binary') - 'ocp get-clear-pcie-correctable-errors -s' = @('0', '1', '2', '3') + 'ocp get-clear-pcie-correctable-errors -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-dssd-async-event-config --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-async-event-config --output-format-version' = @('1', '2') - 'ocp get-dssd-async-event-config --sel' = @('0', '1', '2', '3') + 'ocp get-dssd-async-event-config --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-dssd-async-event-config --timeout' = @('infinite', '-1') - 'ocp get-dssd-async-event-config -S' = @('0', '1', '2', '3') + 'ocp get-dssd-async-event-config -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-dssd-async-event-config -o' = @('normal', 'json', 'binary') 'ocp get-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary') 'ocp get-dssd-power-state-feature --output-format-version' = @('1', '2') - 'ocp get-dssd-power-state-feature --sel' = @('0', '1', '2', '3') + 'ocp get-dssd-power-state-feature --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-dssd-power-state-feature --timeout' = @('infinite', '-1') - 'ocp get-dssd-power-state-feature -S' = @('0', '1', '2', '3') + 'ocp get-dssd-power-state-feature -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-dssd-power-state-feature -o' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary') 'ocp get-enable-ieee1667-silo --output-format-version' = @('1', '2') - 'ocp get-enable-ieee1667-silo --sel' = @('0', '1', '2', '3') + 'ocp get-enable-ieee1667-silo --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-enable-ieee1667-silo --timeout' = @('infinite', '-1') 'ocp get-enable-ieee1667-silo -o' = @('normal', 'json', 'binary') - 'ocp get-enable-ieee1667-silo -s' = @('0', '1', '2', '3') + 'ocp get-enable-ieee1667-silo -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-error-injection --output-format' = @('normal', 'json', 'binary') 'ocp get-error-injection --output-format-version' = @('1', '2') - 'ocp get-error-injection --sel' = @('0', '1', '2', '3') + 'ocp get-error-injection --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-error-injection --timeout' = @('infinite', '-1') 'ocp get-error-injection -o' = @('normal', 'json', 'binary') - 'ocp get-error-injection -s' = @('0', '1', '2', '3') + 'ocp get-error-injection -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-idle-wakeup-time --namespace-id' = @('all', '-1') 'ocp get-idle-wakeup-time --output-format' = @('normal', 'json', 'binary') 'ocp get-idle-wakeup-time --output-format-version' = @('1', '2') - 'ocp get-idle-wakeup-time --sel' = @('0', '1', '2', '3') + 'ocp get-idle-wakeup-time --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-idle-wakeup-time --timeout' = @('infinite', '-1') 'ocp get-idle-wakeup-time -n' = @('all', '-1') 'ocp get-idle-wakeup-time -o' = @('normal', 'json', 'binary') - 'ocp get-idle-wakeup-time -s' = @('0', '1', '2', '3') + 'ocp get-idle-wakeup-time -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-latency-monitor --namespace-id' = @('all', '-1') 'ocp get-latency-monitor --output-format' = @('normal', 'json', 'binary') 'ocp get-latency-monitor --output-format-version' = @('1', '2') - 'ocp get-latency-monitor --sel' = @('0', '1', '2', '3') + 'ocp get-latency-monitor --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-latency-monitor --timeout' = @('infinite', '-1') 'ocp get-latency-monitor -n' = @('all', '-1') 'ocp get-latency-monitor -o' = @('normal', 'json', 'binary') - 'ocp get-latency-monitor -s' = @('0', '1', '2', '3') + 'ocp get-latency-monitor -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-plp-health-check-interval --output-format' = @('normal', 'json', 'binary') 'ocp get-plp-health-check-interval --output-format-version' = @('1', '2') - 'ocp get-plp-health-check-interval --sel' = @('0', '1', '2', '3') + 'ocp get-plp-health-check-interval --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-plp-health-check-interval --timeout' = @('infinite', '-1') - 'ocp get-plp-health-check-interval -S' = @('0', '1', '2', '3') + 'ocp get-plp-health-check-interval -S' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-plp-health-check-interval -o' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile --namespace-id' = @('all', '-1') 'ocp get-telemetry-profile --output-format' = @('normal', 'json', 'binary') 'ocp get-telemetry-profile --output-format-version' = @('1', '2') - 'ocp get-telemetry-profile --sel' = @('0', '1', '2', '3') + 'ocp get-telemetry-profile --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp get-telemetry-profile --timeout' = @('infinite', '-1') 'ocp get-telemetry-profile -n' = @('all', '-1') 'ocp get-telemetry-profile -o' = @('normal', 'json', 'binary') - 'ocp get-telemetry-profile -s' = @('0', '1', '2', '3') + 'ocp get-telemetry-profile -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'ocp hardware-component-log --comp-id' = @('asic', 'nand', 'dram', 'pmic', 'pcb', 'cap', 'reg', 'case', 'sn', 'country', 'hw-rev', 'born-on-date', 'vendor') 'ocp hardware-component-log --output-format' = @('normal', 'json', 'binary') 'ocp hardware-component-log --output-format-version' = @('1', '2') @@ -2671,11 +2684,11 @@ $script:NvmeOptionValues = @{ 'shannon get-additional-feature --namespace-id' = @('all', '-1') 'shannon get-additional-feature --output-format' = @('normal', 'json', 'binary') 'shannon get-additional-feature --output-format-version' = @('1', '2') - 'shannon get-additional-feature --sel' = @('0', '1', '2', '3') + 'shannon get-additional-feature --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'shannon get-additional-feature --timeout' = @('infinite', '-1') 'shannon get-additional-feature -n' = @('all', '-1') 'shannon get-additional-feature -o' = @('normal', 'json', 'binary') - 'shannon get-additional-feature -s' = @('0', '1', '2', '3') + 'shannon get-additional-feature -s' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') 'shannon id-ctrl --output-format' = @('normal', 'json', 'binary') 'shannon id-ctrl --output-format-version' = @('1', '2') 'shannon id-ctrl --timeout' = @('infinite', '-1') From c6fef102cf943852a950e15970b4527342fcffeb Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 08:59:55 +0000 Subject: [PATCH 206/263] NEWS: document the value types and ranges of the options Announce the names and the range of --sel, the range of --output-format-version and what the metadata and the completions make of the value types. Signed-off-by: Daniel Wagner --- NEWS.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/NEWS.md b/NEWS.md index b76827893f..3f00c4fb09 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,6 +41,14 @@ help lists the names of an option. `lm migration-recv` requires `--sel` now, like the other lm commands. +* `--sel` of `get-feature`, the `feat` commands and the `ocp` and + `shannon` commands which get a feature rejects numbers above 3 and + accepts the names `current`, `default`, `saved` and `supported`. + Before, it sent the reserved values 4 to 7 and truncated larger + values. `--output-format-version` (also in `nvme-cli.conf`) rejects + versions other than 1 and 2. Before, every version but 2 printed + version 1. + * Number options reject text after the number. Before, the rest was ignored: `--timeout=10s` set a timeout of 10 milliseconds. @@ -234,6 +242,14 @@ `memblaze smart-log-add-x`, `sndk vs-internal-log`, `wdc vs-internal-log` and the `sed` commands did not accept them. +* The command metadata reports the type of the value of each option and + the range of numbers an option takes. The shell completions use them: + they complete paths for every option which names a file or directory, + now also the `--keyfile` of the keys commands, `--nbft-path` and the + logs of `ocp internal-log`, and offer the values of `--sel`. They no + longer offer 0 to 3 for the `--sel` of the `lm` commands, which means + something else there. + * The help and the shell completions of `--output-format` list the formats the command supports. Before, the completions offered `tabular` everywhere, and some commands listed formats they rejected. From 2202cfb7c7922e8bf72167ee8d1fda33ad46865c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:08:52 +0000 Subject: [PATCH 207/263] argconfig: add options which take a list of names An option which takes several of a few words had to take a string and split and compare it itself, so its names were neither checked by the parser nor shown in the help and the completions. Add OPT_ENUM_LIST(), which takes names of its value table separated by commas. The names are bits, ORed into a uint32_t, also when the option is given more than once. Each name is matched like the other value names, an unknown or empty one is a usage error which lists the names. The metadata reports the type as enum-list. Signed-off-by: Daniel Wagner --- plugins/utils/command-metadata-schema.json | 4 +- plugins/utils/command-metadata.c | 2 + src/argconfig.c | 50 +++++++++++++++++-- src/argconfig.h | 10 ++++ tests/unit/c/argconfig-getopt-ref.c | 2 + tests/unit/c/test-argconfig-parse.c | 56 ++++++++++++++++++++++ 6 files changed, 117 insertions(+), 7 deletions(-) diff --git a/plugins/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index e5d164f0de..a84e6b5808 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -115,8 +115,8 @@ }, "type": { "type": "string", - "description": "The kind of value: flag (no value), count (no value, may be repeated), int, u8, u16, u32 and u64 (numbers of that size; the unsigned ones take no negative number), size (a number with an optional binary suffix such as Ki), double, string, enum (one of \"values\"), file or directory (a path).", - "enum": ["flag", "count", "int", "u8", "u16", "u32", "u64", "size", "double", "string", "enum", "file", "directory"] + "description": "The kind of value: flag (no value), count (no value, may be repeated), int, u8, u16, u32 and u64 (numbers of that size; the unsigned ones take no negative number), size (a number with an optional binary suffix such as Ki), double, string, enum (one of \"values\"), enum-list (\"values\" separated by commas), file or directory (a path).", + "enum": ["flag", "count", "int", "u8", "u16", "u32", "u64", "size", "double", "string", "enum", "enum-list", "file", "directory"] }, "range": { "type": "object", diff --git a/plugins/utils/command-metadata.c b/plugins/utils/command-metadata.c index b00ebdd212..123726a7ed 100644 --- a/plugins/utils/command-metadata.c +++ b/plugins/utils/command-metadata.c @@ -695,6 +695,8 @@ static const char *opt_type(const struct command_metadata_option *o) return "string"; case CFG_ENUM: return "enum"; + case CFG_ENUM_LIST: + return "enum-list"; case CFG_FILE: return "file"; case CFG_DIR: diff --git a/src/argconfig.c b/src/argconfig.c index 6da752eb4b..522f064195 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -310,6 +310,31 @@ bool argconfig_in_range(const struct argconfig_commandline_options *s, return !s->max || (u >= s->min && u <= s->max); } +static struct argconfig_opt_val * +argconfig_match_val(struct argconfig_opt_val *v, const char *str); + +/* The bits the names in the comma-separated list @arg stand for */ +static int argconfig_parse_enum_list(struct argconfig_commandline_options *s, + const char *arg, uint32_t *bits) +{ + __cleanup_free char *list = strdup(arg); + struct argconfig_opt_val *v; + char *p = list, *name; + + if (!list) + return -ENOMEM; + + *bits = 0; + while ((name = shr_strsep(&p, ",")) != NULL) { + v = s->opt_val ? argconfig_match_val(s->opt_val, name) : NULL; + if (!v) + return -EINVAL; + *bits |= (uint32_t)v->val.int_val; + } + + return 0; +} + /* An unsigned number up to @max, in the range of @s */ static int argconfig_parse_u64(struct argconfig_commandline_options *s, const char *arg, uint64_t max, uint64_t *u) @@ -369,6 +394,14 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, /* only the names, which argconfig_parse_val() looked up */ ret = -EINVAL; break; + case CFG_ENUM_LIST: { + uint32_t bits; + + ret = argconfig_parse_enum_list(s, arg, &bits); + if (!ret) + *(uint32_t *)value |= bits; + break; + } case CFG_LONG: ret = argconfig_parse_u64(s, arg, UINT64_MAX, &u); if (!ret) @@ -439,6 +472,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val case CFG_DIR: *(char **)val = opt_val->string; break; + case CFG_ENUM_LIST: case CFG_GROUP_SEPARATOR: case CFG_INCLUDE: break; @@ -492,7 +526,9 @@ bool argconfig_set_value_name(struct argconfig_commandline_options *s, static int argconfig_parse_val(struct argconfig_commandline_options *s, char *arg) { - if (arg && argconfig_set_value_name(s, arg)) + /* the names of a list are looked up one by one */ + if (arg && s->config_type != CFG_ENUM_LIST && + argconfig_set_value_name(s, arg)) return 0; return argconfig_parse_type(s, arg); @@ -668,6 +704,7 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, char c, const char *arg) { __cleanup_free char *e = arg ? shr_escape(arg) : NULL; + bool list = s->config_type == CFG_ENUM_LIST; const struct argconfig_opt_val *v; int n = 0; @@ -678,7 +715,9 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, else fprintf(stderr, "--%s", s->option); fprintf(stderr, ": expected "); - if (s->config_type != CFG_ENUM) + if (list) + fprintf(stderr, "a comma-separated list of "); + else if (s->config_type != CFG_ENUM) fprintf(stderr, "%s%s", argconfig_expected(s), s->opt_val ? " or " : ""); @@ -686,10 +725,11 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, for (v = s->opt_val; v && v->str; v++) n++; if (n > 8) - fprintf(stderr, "one of the names in the help"); + fprintf(stderr, "%sthe names in the help", + list ? "" : "one of "); for (v = s->opt_val; n <= 8 && v && v->str; v++) - fprintf(stderr, "%s%s", v == s->opt_val ? "one of: " : ", ", - v->str); + fprintf(stderr, "%s%s", v != s->opt_val ? ", " : + list ? "" : "one of: ", v->str); fprintf(stderr, "\n"); return ARGCONFIG_USAGE; diff --git a/src/argconfig.h b/src/argconfig.h index 8871d16453..e33da1cb99 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -27,6 +27,7 @@ enum argconfig_types { CFG_POSITIVE, CFG_INCREMENT, CFG_ENUM, + CFG_ENUM_LIST, /* strings which name a file or a directory */ CFG_FILE, CFG_DIR, @@ -94,6 +95,15 @@ enum argconfig_types { {l, s, "NAME", CFG_ENUM, OPT_CHECK(v, int *), \ ARGCONFIG_ARG_REQUIRED, d, false, n, __VA_ARGS__} +/* + * Names of the value table @n separated by commas. The VAL_ENUM() entries + * are bits, which are ORed into the variable, also when the option is + * given more than once. + */ +#define OPT_ENUM_LIST(l, s, v, d, n, ...) \ + {l, s, "NAME,...", CFG_ENUM_LIST, OPT_CHECK(v, uint32_t *), \ + ARGCONFIG_ARG_REQUIRED, d, false, n, __VA_ARGS__} + #define OPT_LONG(l, s, v, d, ...) \ {l, s, "NUM", CFG_LONG, OPT_CHECK_64(v), \ ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} diff --git a/tests/unit/c/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c index e0634a0168..072afcb9ae 100644 --- a/tests/unit/c/argconfig-getopt-ref.c +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -89,6 +89,7 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, *(int *)value += 1; break; case CFG_ENUM: + case CFG_ENUM_LIST: ret = -EINVAL; break; case CFG_LONG: @@ -156,6 +157,7 @@ static void argconfig_set_opt_val(enum argconfig_types type, case CFG_DIR: *(char **)val = opt_val->string; break; + case CFG_ENUM_LIST: case CFG_GROUP_SEPARATOR: case CFG_INCLUDE: break; diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index f95612dca2..1545df6b0a 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -776,6 +776,61 @@ static void special_values_test(void) } } +/* + * A list of names is ORed into a bitmask, also over several uses of the + * option. Every name of the list has to be known, an empty one too. + */ +static void enum_list_test(void) +{ + struct argconfig_opt_val names[] = { + VAL_ENUM("host0", 1 << 0), + VAL_ENUM("host1", 1 << 1), + VAL_ENUM("ctlr", 1 << 2), + VAL_ENUM("controller", 1 << 2), + VAL_END() + }; + static const struct { + const char *arg1, *arg2; + int ret; + __u32 val; + } tests[] = { + { "--types=host0", NULL, 0, 0x1 }, + { "--types=host1,ctlr", NULL, 0, 0x6 }, + { "--types=HOST0,co", NULL, 0, 0x5 }, + { "-thost0", "-tcontroller", 0, 0x5 }, + { "--types=ctlr,controller", NULL, 0, 0x4 }, + { "--types=host", NULL, -EINVAL, 0 }, + { "--types=host0,", NULL, -EINVAL, 0 }, + { "--types=,host0", NULL, -EINVAL, 0 }, + { "--types=host0,,ctlr", NULL, -EINVAL, 0 }, + { "--types=", NULL, -EINVAL, 0 }, + { "--types=1", NULL, -EINVAL, 0 }, + { "--types=host0,bogus", NULL, -EINVAL, 0 }, + }; + unsigned int i; + __u32 types; + int ret; + + OPT_ARGS(opts) = { + OPT_ENUM_LIST("types", 't', &types, "types", names), + OPT_END() + }; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + char *argv[4] = { "prog", (char *)tests[i].arg1, + (char *)tests[i].arg2, NULL }; + + types = 0; + ret = argconfig_parse(tests[i].arg2 ? 3 : 2, argv, "test", + opts); + if (ret != tests[i].ret || (!ret && types != tests[i].val)) { + printf("ERROR: enum list: {%s} gave %d/%u\n", + tests[i].arg1, ret, types); + test_rc = 1; + } + } +} + /* * A number outside the range of an option is rejected, a named value is * taken as it is. @@ -1220,6 +1275,7 @@ int main(void) given_test(); old_name_test(); enum_test(); + enum_list_test(); include_test(); bind_test(); double_locale_test(); From 450bc5d1c9c244dc186deecf9b816ebf82beb59f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:08:52 +0000 Subject: [PATCH 208/263] samsung: check the dump types while parsing the command line vs-internal-log split --dump-type and compared the types itself, after it opened the device and identified the controller, and printed an unknown type to stdout. Take the dump types as an OPT_ENUM_LIST(): an unknown type is a usage error which lists the types, the help lists them too, and the option may be given more than once. Signed-off-by: Daniel Wagner --- .../nvme-samsung-vs-internal-log.txt | 14 ++-- plugins/samsung/samsung-nvme.c | 79 +++++-------------- tests/cli/samsung/nvme_samsung_test.py | 17 +++- 3 files changed, 42 insertions(+), 68 deletions(-) diff --git a/Documentation/nvme-samsung-vs-internal-log.txt b/Documentation/nvme-samsung-vs-internal-log.txt index e6c54edc6f..bfd87f95f2 100644 --- a/Documentation/nvme-samsung-vs-internal-log.txt +++ b/Documentation/nvme-samsung-vs-internal-log.txt @@ -33,15 +33,17 @@ device (ex: /dev/nvme0) or an nvme block device (ex: /dev/nvme0n1). OPTIONS ------- --t :: ---dump-type=:: +-t :: +--dump-type=:: Comma-separated list of dump types to extract, given without spaces. The supported types are 'host0' and 'host1' for the host-initiated telemetry dump using Log Specific Parameter 0 and - 1 respectively, 'ctlr' for the controller-initiated telemetry - dump, and 'vendor' for the vendor dumps. When this option is - omitted, every dump type except 'host0' is extracted; 'host0' - is only collected when it is requested explicitly. + 1 respectively, 'ctlr' (or 'controller') for the + controller-initiated telemetry dump, and 'vendor' for the vendor + dumps. The option may be given more than once to add more types. + When this option is omitted, every dump type except 'host0' is + extracted; 'host0' is only collected when it is requested + explicitly. -a :: --telemetry-data-area=:: diff --git a/plugins/samsung/samsung-nvme.c b/plugins/samsung/samsung-nvme.c index 9a94ba1046..1b175c1084 100644 --- a/plugins/samsung/samsung-nvme.c +++ b/plugins/samsung/samsung-nvme.c @@ -1255,35 +1255,14 @@ static void run_vendor_dump(int (*get_dump)(struct libnvme_transport_handle *, samsung_print_error(err); } -/* - * Translate one dump-type token (e.g. "host0") into a selection in @sel. - * Called once per comma-separated token so -t accepts a list such as - * "host0,ctlr,vendor". Returns 0 on success, or a SAMSUNG_GENERAL_* error - * code if the token is not a recognized dump type. - */ -static int vs_internal_log_select_type(const char *tok, bool *sel) -{ - if (!strcmp(tok, "host0")) - sel[DUMP_TYPE_HOST_0] = true; - else if (!strcmp(tok, "host1")) - sel[DUMP_TYPE_HOST_1] = true; - else if (!strcmp(tok, "ctlr") || !strcmp(tok, "controller")) - sel[DUMP_TYPE_CTLR] = true; - else if (!strcmp(tok, "vendor")) - sel[DUMP_TYPE_VENDOR] = true; - else { - printf("Invalid dump type: '%s'.\n", tok); - return SAMSUNG_GENERAL_INVALID_PARAMETER_ERROR; - } - - return 0; -} +/* The bit of a dump type in the --dump-type selection */ +#define DUMP_BIT(t) (1U << (t)) /* - * A dump type is extracted when either no -t was given (select_all, i.e. - * extract everything) or it was explicitly named in sel[]. + * A dump type is extracted when either no -t was given (extract + * everything) or it was named. */ -#define WANT_DUMP(t) (select_all || sel[(t)]) +#define WANT_DUMP(t) (!cfg.types || (cfg.types & DUMP_BIT(t))) static int vs_internal_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) @@ -1315,12 +1294,19 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - bool sel[DUMP_TYPE_MAX] = { false }; - bool select_all = false; int tele_data_area = 0; + OPT_VALS(types) = { + VAL_ENUM("host0", DUMP_BIT(DUMP_TYPE_HOST_0)), + VAL_ENUM("host1", DUMP_BIT(DUMP_TYPE_HOST_1)), + VAL_ENUM("ctlr", DUMP_BIT(DUMP_TYPE_CTLR)), + VAL_ENUM("controller", DUMP_BIT(DUMP_TYPE_CTLR)), + VAL_ENUM("vendor", DUMP_BIT(DUMP_TYPE_VENDOR)), + VAL_END() + }; + struct config { - char *type; + __u32 types; int area; char *file; bool compress; @@ -1328,7 +1314,7 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, }; struct config cfg = { - .type = NULL, + .types = 0, .area = 0, .file = NULL, .compress = false, @@ -1336,7 +1322,7 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, }; OPT_ARGS(opts) = { - OPT_STRING("dump-type", 't', "TYPE", &cfg.type, type), + OPT_ENUM_LIST("dump-type", 't', &cfg.types, type, types), OPT_INT("telemetry-data-area", 'a', &cfg.area, area), OPT_FILE("output-file", 'O', &cfg.file, file), OPT_FLAG("compress", 'z', &cfg.compress, compress), @@ -1377,31 +1363,6 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, return SAMSUNG_GENERAL_INVALID_PARAMETER_ERROR; } - if (!cfg.type) - select_all = true; - else { - __cleanup_free char *type_buf = NULL; - char *tok, *saveptr = NULL; - - /* - * -t accepts a comma-separated list, e.g. "host0,ctlr,vendor". - * strtok_r() mutates its input, so parse a copy of cfg.type - * rather than the option string itself. - */ - type_buf = strdup(cfg.type); - if (!type_buf) - return SAMSUNG_GENERAL_MEM_ALLOC_ERROR; - - for (tok = strtok_r(type_buf, ",", &saveptr); tok != NULL; - tok = strtok_r(NULL, ",", &saveptr)) { - err = vs_internal_log_select_type(tok, sel); - if (err != 0) { - samsung_print_error(err); - return err; - } - } - } - /* * -O is a file name prefix: make_file_path() concatenates it onto the * generated name without inserting a separator, so "./dumps/" yields @@ -1442,10 +1403,10 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, /* * Host(0) is intentionally excluded from "all" dumps (only run when - * explicitly selected), so it is gated on sel[] directly rather than - * WANT_DUMP()/select_all. + * explicitly selected), so it is gated on the selection directly + * rather than WANT_DUMP(). */ - if (sel[DUMP_TYPE_HOST_0]) { + if (cfg.types & DUMP_BIT(DUMP_TYPE_HOST_0)) { if (tele_data_area == 0) try_get_telemetry_all(hdl, &ctrl, dump_save_dir, sn, DUMP_TYPE_HOST_0); diff --git a/tests/cli/samsung/nvme_samsung_test.py b/tests/cli/samsung/nvme_samsung_test.py index 32ece5dd71..981b5073d3 100644 --- a/tests/cli/samsung/nvme_samsung_test.py +++ b/tests/cli/samsung/nvme_samsung_test.py @@ -527,10 +527,21 @@ def test_host0_is_excluded_unless_named(self): self.assertTrue(any('Host(0)' in f for f in self.files('h0')), f'host0 was not collected when named: {self.files("h0")}') + def test_repeated_dump_types(self): + result = self.run_cmd('-t', 'CTLR', '-t', 'vendor', '-O', './dumps/') + self.assertOk(result) + names = self.files('dumps') + self.assertTrue(any('Telemetry_Controller' in f for f in names), names) + self.assertTrue(any('VendorCrashDump' in f for f in names), names) + def test_unknown_dump_type_is_rejected(self): - result = self.run_cmd('-t', 'nosuchtype', '-O', './dumps/') - self.assertNotEqual(result.returncode, 0, - 'an unknown -t token must fail') + for types in ('nosuchtype', 'ctlr,nosuchtype', 'ctlr,', ''): + with self.subTest(types=types): + result = self.run_cmd('-t', types, '-O', './dumps/') + self.assertNotEqual(result.returncode, 0, + 'an unknown -t token must fail') + self.assertIn('host0, host1, ctlr, controller, vendor', + result.stderr) # ---------------------------------------------------------------- # # -a validation, which used to be skipped without -t # From ec9e49ceb46d2a500928a846ed77e735f5b0de08 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:10:08 +0000 Subject: [PATCH 209/263] completions: complete the names of a list after the last comma An option which takes a list of names was completed like one which takes a single name, so only its first name could be completed. Complete the name after the last comma for the options of the type enum-list: bash keeps the names before it as prefix, zsh uses _values with ',' as separator and PowerShell gets a table of these options. Signed-off-by: Daniel Wagner --- completions/TESTING.md | 3 + completions/generate-completions.py | 101 +++++++++++++++--- completions/test-command-metadata.json | 9 ++ .../py/test_generate_completions_values.py | 14 ++- 4 files changed, 109 insertions(+), 18 deletions(-) diff --git a/completions/TESTING.md b/completions/TESTING.md index 1eabbcbefa..1c2412b363 100644 --- a/completions/TESTING.md +++ b/completions/TESTING.md @@ -71,6 +71,9 @@ These behaviors are identical in bash, zsh, and PowerShell. list `normal json binary tabular`. - [ ] **Value from a range** — `nvme feat power-meas --sel ` lists `0 1 2 3 current default saved supported`. +- [ ] **List of names** — `nvme samsung vs-internal-log -t host0,` + lists `host0,host1 host0,ctlr …`, completing the name after the + last comma. - [ ] **Command-local value** — `nvme fw commit --action ` lists `replace … set-active …`. - [ ] **File-valued option** — `nvme fw download --fw ` completes diff --git a/completions/generate-completions.py b/completions/generate-completions.py index a9adad54ae..820726eed1 100755 --- a/completions/generate-completions.py +++ b/completions/generate-completions.py @@ -212,6 +212,7 @@ def sorted_options(opts): {func} () {{ \tlocal opts="" \tlocal valopts="" +\tlocal listopts="" \tlocal vals="" \tlocal opt="" \tlocal val="" @@ -273,7 +274,12 @@ def sorted_options(opts): \tif [[ $completing_value -eq 1 ]]; then \t\tif [[ $vals != " " ]]; then -\t\t\tCOMPREPLY+=( $( compgen -W "$vals" -- "$val" ) ) +\t\t\t# A list of names completes the name after its last comma. +\t\t\tlocal pre="" +\t\t\tif [[ " $listopts " == *" $opt "* ]] && [[ $val == *,* ]]; then +\t\t\t\tpre="${val%,*}," +\t\t\tfi +\t\t\tCOMPREPLY+=( $( compgen -P "$pre" -W "$vals" -- "${val#"$pre"}" ) ) \t\telif [[ $wantfiles -eq 1 ]]; then \t\t\t: # value is a filename; let 'complete -o default' offer files \t\telse @@ -342,6 +348,11 @@ def bash_valopt_words(opts): return out +def opt_is_list(opt): + """True if the option takes a comma-separated list of its values.""" + return opt.get("type") == "enum-list" + + def opt_is_file(opt): """True if the option's value is a filename/path we can file-complete.""" return opt.get("type") in ("file", "directory") @@ -368,6 +379,9 @@ def bash_option_body(opts): valwords = bash_valopt_words(opts) if valwords: out += f'\t\t\tvalopts+="{valwords}"\n' + listwords = " ".join(opt_spellings(o) for o in opts if opt_is_list(o)) + if listwords: + out += f'\t\t\tlistopts+=" {listwords}"\n' if val_opts or file_opts: out += "\n" if val_opts: @@ -551,6 +565,25 @@ def generate_bash(model, out): \t\treturn 1 \t} +\t# Like _nvme_opt_vals, for an option which takes a comma-separated list +\t# of its values. +\t_nvme_opt_list() { +\t\tlocal -a names +\t\tlocal name +\t\twhile (( $# >= 2 )); do +\t\t\tnames=(${=1}) +\t\t\tfor name in "${names[@]}"; do +\t\t\t\tif [[ ${words[CURRENT]} == ${name}=* ]] || \\ +\t\t\t\t [[ ${words[CURRENT-1]} == "${name}" ]]; then +\t\t\t\t\t_values -s , '' ${=2} +\t\t\t\t\treturn 0 +\t\t\t\tfi +\t\t\tdone +\t\t\tshift 2 +\t\tdone +\t\treturn 1 +\t} + \t# Complete an option's value as a filename, in both the attached \t# ('--opt=path', '-o=path') and separate-word ('--opt path', '-o path') forms. \t# Each argument is a space-separated spelling list ('--fw -f'); if the word @@ -604,18 +637,22 @@ def zsh_var(plugin, cmd): def emit_zsh_value_completions(out, tab, cmd): """Emit a single _nvme_opt_vals call listing the command's value-set options - as alternating spelling/value-list pairs.""" - pairs = [f'"{opt_spellings(o)}" "{" ".join(o["values"])}"' - for o in sorted_options(command_options(cmd)) if o.get("values")] - if not pairs: - return - if len(pairs) == 1: - out.write(f'{tab}_nvme_opt_vals {pairs[0]} && return\n') - else: - # Continuation lines align under the first argument. - cont = tab + " " * len("_nvme_opt_vals ") - joined = f" \\\n{cont}".join(pairs) - out.write(f'{tab}_nvme_opt_vals {joined} && return\n') + as alternating spelling/value-list pairs, and a _nvme_opt_list call for + the options which take a list of values.""" + opts = [o for o in sorted_options(command_options(cmd)) if o.get("values")] + for func, want_list in (("_nvme_opt_vals", False), + ("_nvme_opt_list", True)): + pairs = [f'"{opt_spellings(o)}" "{" ".join(o["values"])}"' + for o in opts if opt_is_list(o) == want_list] + if not pairs: + continue + if len(pairs) == 1: + out.write(f'{tab}{func} {pairs[0]} && return\n') + else: + # Continuation lines align under the first argument. + cont = tab + " " * len(func + " ") + joined = f" \\\n{cont}".join(pairs) + out.write(f'{tab}{func} {joined} && return\n') def emit_zsh_file_completions(out, tab, cmd): @@ -823,9 +860,16 @@ def generate_zsh(model, out): if ($prevToken -like '-*') { $valKey = "$key $prevToken" if ($script:NvmeOptionValues.ContainsKey($valKey)) { + # A list of names completes the name after its last comma. + $pre = '' + if ($script:NvmeListOptions.ContainsKey($valKey) -and + $wordToComplete -match '^(.*,)') { + $pre = $Matches[1] + } + $last = $wordToComplete.Substring($pre.Length) return $script:NvmeOptionValues[$valKey] | - Where-Object { $_ -like "$wordToComplete*" } | - ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } + Where-Object { $_ -like "$last*" } | + ForEach-Object { [System.Management.Automation.CompletionResult]::new("$pre$_", $_, 'ParameterValue', $_) } } if ($script:NvmeFileOptions.ContainsKey($valKey)) { return Get-ChildItem -Path "$wordToComplete*" -ErrorAction SilentlyContinue | @@ -845,9 +889,15 @@ def generate_zsh(model, out): $valPrefix = $Matches[2] $valKey = "$key $optPart" if ($script:NvmeOptionValues.ContainsKey($valKey)) { + $pre = '' + if ($script:NvmeListOptions.ContainsKey($valKey) -and + $valPrefix -match '^(.*,)') { + $pre = $Matches[1] + } + $last = $valPrefix.Substring($pre.Length) return $script:NvmeOptionValues[$valKey] | - Where-Object { $_ -like "$valPrefix*" } | - ForEach-Object { [System.Management.Automation.CompletionResult]::new("$optPart=$_", $_, 'ParameterValue', $_) } + Where-Object { $_ -like "$last*" } | + ForEach-Object { [System.Management.Automation.CompletionResult]::new("$optPart=$pre$_", $_, 'ParameterValue', $_) } } if ($script:NvmeFileOptions.ContainsKey($valKey)) { return Get-ChildItem -Path "$valPrefix*" -ErrorAction SilentlyContinue | @@ -961,6 +1011,23 @@ def generate_powershell(model, out): out.write(f" '{entry}' = $true\n") out.write("}\n") + out.write("\n$script:NvmeListOptions = @{\n") + list_entries = [] + for prefix, c in ([("", c) for c in builtin_commands(model)] + + [(p["name"] + " ", c) for p in plugins(model) + for c in p["commands"]]): + for o in command_options(c): + if not opt_is_list(o): + continue + for n in cmd_names(c): + key = ps_escape(prefix + n) + list_entries.append(f"{key} --{o['long']}") + if o.get("short"): + list_entries.append(f"{key} -{o['short']}") + for entry in sorted(list_entries): + out.write(f" '{entry}' = $true\n") + out.write("}\n") + out.write("\n$script:NvmeOptionValues = @{\n") val_entries = [] for c in builtin_commands(model): diff --git a/completions/test-command-metadata.json b/completions/test-command-metadata.json index 267238adb5..65adde1d27 100644 --- a/completions/test-command-metadata.json +++ b/completions/test-command-metadata.json @@ -151,6 +151,15 @@ "type": "file", "metavar": "FILE", "description": "input file path" + }, + { + "long": "kinds", + "short": "k", + "argument": "required", + "type": "enum-list", + "metavar": "NAME,...", + "description": "kinds to report", + "values": ["red", "green", "blue"] } ] }, diff --git a/tests/unit/py/test_generate_completions_values.py b/tests/unit/py/test_generate_completions_values.py index 68a5e4dc2c..605ad2074d 100644 --- a/tests/unit/py/test_generate_completions_values.py +++ b/tests/unit/py/test_generate_completions_values.py @@ -9,7 +9,9 @@ An option of the type file or directory completes paths, whatever its metavar. An option with a small range offers its numbers, followed by its -names. Checked on the bash and PowerShell output for the fixture. +names. An option which takes a list of names completes the name after the +last comma. Checked on the bash, zsh and PowerShell output for the +fixture. Usage: test_generate_completions_values.py """ @@ -35,6 +37,7 @@ class CompletionValuesTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.bash = generate("bash") + cls.zsh = generate("zsh") cls.powershell = generate("powershell") def test_paths(self): @@ -50,6 +53,15 @@ def test_range(self): "@('0', '1', '2', '3', 'current', 'saved')", self.powershell) + def test_list(self): + self.assertIn('listopts+=" --kinds -k"', self.bash) + self.assertIn('"--kinds -k" "red green blue"', self.bash) + self.assertIn('_nvme_opt_list "--kinds -k" "red green blue"', + self.zsh) + self.assertNotIn('_nvme_opt_vals "--kinds -k"', self.zsh) + self.assertIn("'zeta-plug report --kinds' = $true", self.powershell) + self.assertIn("'zeta-plug report -k' = $true", self.powershell) + def test_no_guess_from_name(self): # a number without a range offers nothing self.assertNotIn('"--wait-time -w"', self.bash) From 81f2629e68cacb86740f8e9766f1ba81e9ab9e9c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:10:08 +0000 Subject: [PATCH 210/263] completions: update the shell completions Regenerate the completions for the list of dump types of samsung vs-internal-log. Signed-off-by: Daniel Wagner --- completions/_nvme | 20 ++++++++++ completions/bash-nvme-completion.sh | 60 ++++++++++++++++++++++++++++- completions/nvme-completion.ps1 | 28 ++++++++++++-- 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/completions/_nvme b/completions/_nvme index 6a6626a5a3..3eebd29ac9 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -240,6 +240,25 @@ _nvme () { return 1 } + # Like _nvme_opt_vals, for an option which takes a comma-separated list + # of its values. + _nvme_opt_list() { + local -a names + local name + while (( $# >= 2 )); do + names=(${=1}) + for name in "${names[@]}"; do + if [[ ${words[CURRENT]} == ${name}=* ]] || \ + [[ ${words[CURRENT-1]} == "${name}" ]]; then + _values -s , '' ${=2} + return 0 + fi + done + shift 2 + done + return 1 + } + # Complete an option's value as a filename, in both the attached # ('--opt=path', '-o=path') and separate-word ('--opt path', '-o path') forms. # Each argument is a space-separated spelling list ('--fw -f'); if the word @@ -8029,6 +8048,7 @@ _nvme () { _nvme_opt_vals "--output-format -o" "normal" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" && return + _nvme_opt_list "--dump-type -t" "host0 host1 ctlr controller vendor" && return _nvme_opt_files "--output-file -O" && return local _samsung_vs_internal_log _samsung_vs_internal_log=( diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index 06a5be7a6c..e11133018b 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -124,7 +124,12 @@ _nvme_finish_completion () { if [[ $completing_value -eq 1 ]]; then if [[ $vals != " " ]]; then - COMPREPLY+=( $( compgen -W "$vals" -- "$val" ) ) + # A list of names completes the name after its last comma. + local pre="" + if [[ " $listopts " == *" $opt "* ]] && [[ $val == *,* ]]; then + pre="${val%,*}," + fi + COMPREPLY+=( $( compgen -P "$pre" -W "$vals" -- "${val#"$pre"}" ) ) elif [[ $wantfiles -eq 1 ]]; then : # value is a filename; let 'complete -o default' offer files else @@ -146,6 +151,7 @@ _nvme_finish_completion () { nvme_list_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1301,6 +1307,7 @@ nvme_list_opts () { plugin_amzn_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1337,6 +1344,7 @@ plugin_amzn_opts () { plugin_config_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1409,6 +1417,7 @@ plugin_config_opts () { plugin_dapustor_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1437,6 +1446,7 @@ plugin_dapustor_opts () { plugin_dell_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1464,6 +1474,7 @@ plugin_dell_opts () { plugin_dera_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1491,6 +1502,7 @@ plugin_dera_opts () { plugin_dir_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1530,6 +1542,7 @@ plugin_dir_opts () { plugin_exclusion_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1608,6 +1621,7 @@ plugin_exclusion_opts () { plugin_fdp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1701,6 +1715,7 @@ plugin_fdp_opts () { plugin_feat_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1913,6 +1928,7 @@ plugin_feat_opts () { plugin_fw_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1951,6 +1967,7 @@ plugin_fw_opts () { plugin_huawei_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1988,6 +2005,7 @@ plugin_huawei_opts () { plugin_ibm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2033,6 +2051,7 @@ plugin_ibm_opts () { plugin_id_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2219,6 +2238,7 @@ plugin_id_opts () { plugin_innogrit_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2255,6 +2275,7 @@ plugin_innogrit_opts () { plugin_inspur_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2282,6 +2303,7 @@ plugin_inspur_opts () { plugin_intel_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2375,6 +2397,7 @@ plugin_intel_opts () { plugin_io_mgmt_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2415,6 +2438,7 @@ plugin_io_mgmt_opts () { plugin_keys_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2517,6 +2541,7 @@ plugin_keys_opts () { plugin_lm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2600,6 +2625,7 @@ plugin_lm_opts () { plugin_log_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2920,6 +2946,7 @@ plugin_log_opts () { plugin_mangoboost_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2947,6 +2974,7 @@ plugin_mangoboost_opts () { plugin_memblaze_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3103,6 +3131,7 @@ plugin_memblaze_opts () { plugin_micron_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3366,6 +3395,7 @@ plugin_micron_opts () { plugin_nbft_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3395,6 +3425,7 @@ plugin_nbft_opts () { plugin_netapp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3431,6 +3462,7 @@ plugin_netapp_opts () { plugin_ns_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3497,6 +3529,7 @@ plugin_ns_opts () { plugin_nvidia_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3524,6 +3557,7 @@ plugin_nvidia_opts () { plugin_nvme_mi_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3564,6 +3598,7 @@ plugin_nvme_mi_opts () { plugin_ocp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3871,6 +3906,7 @@ plugin_ocp_opts () { plugin_registry_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3926,6 +3962,7 @@ plugin_registry_opts () { plugin_resv_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3984,6 +4021,7 @@ plugin_resv_opts () { plugin_rpmb_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4070,6 +4108,7 @@ plugin_rpmb_opts () { plugin_samsung_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4085,8 +4124,10 @@ plugin_samsung_opts () { "vs-internal-log") opts+=" --compress -z --dry-run --dump-type= -t --hide-progress --no-ioctl-probing --no-retries --output-file= -O --output-format= -o --output-format-version= --quiet --set-options= --telemetry-data-area= -a --timeout= --verbose -v" valopts+=" --dump-type -t --output-file -O --output-format -o --output-format-version --set-options --telemetry-data-area -a --timeout" + listopts+=" --dump-type -t" - _nvme_opt_vals "--output-format -o" "normal" \ + _nvme_opt_vals "--dump-type -t" "host0 host1 ctlr controller vendor" \ + "--output-format -o" "normal" \ "--output-format-version" "1 2" \ "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" @@ -4098,6 +4139,7 @@ plugin_samsung_opts () { plugin_seagate_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4230,6 +4272,7 @@ plugin_seagate_opts () { plugin_security_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4269,6 +4312,7 @@ plugin_security_opts () { plugin_sed_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4341,6 +4385,7 @@ plugin_sed_opts () { plugin_sfx_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4453,6 +4498,7 @@ plugin_sfx_opts () { plugin_shannon_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4512,6 +4558,7 @@ plugin_shannon_opts () { plugin_sndk_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4791,6 +4838,7 @@ plugin_sndk_opts () { plugin_solidigm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4958,6 +5006,7 @@ plugin_solidigm_opts () { plugin_ssstc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4986,6 +5035,7 @@ plugin_ssstc_opts () { plugin_toshiba_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5034,6 +5084,7 @@ plugin_toshiba_opts () { plugin_transcend_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5070,6 +5121,7 @@ plugin_transcend_opts () { plugin_utils_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5092,6 +5144,7 @@ plugin_utils_opts () { plugin_virtium_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5129,6 +5182,7 @@ plugin_virtium_opts () { plugin_wdc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5495,6 +5549,7 @@ plugin_wdc_opts () { plugin_ymtc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -5523,6 +5578,7 @@ plugin_ymtc_opts () { plugin_zns_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" diff --git a/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 0ed2817ad2..5ea914e609 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -958,6 +958,11 @@ $script:NvmeFileOptions = @{ 'zns zone-mgmt-send -d' = $true } +$script:NvmeListOptions = @{ + 'samsung vs-internal-log --dump-type' = $true + 'samsung vs-internal-log -t' = $true +} + $script:NvmeOptionValues = @{ 'admin-passthru --namespace-id' = @('all', '-1') 'admin-passthru --output-format' = @('normal', 'json', 'binary') @@ -2489,10 +2494,12 @@ $script:NvmeOptionValues = @{ 'rpmb write-data --output-format-version' = @('1', '2') 'rpmb write-data --timeout' = @('infinite', '-1') 'rpmb write-data -o' = @('normal', 'json', 'binary') + 'samsung vs-internal-log --dump-type' = @('host0', 'host1', 'ctlr', 'controller', 'vendor') 'samsung vs-internal-log --output-format' = @('normal') 'samsung vs-internal-log --output-format-version' = @('1', '2') 'samsung vs-internal-log --timeout' = @('infinite', '-1') 'samsung vs-internal-log -o' = @('normal') + 'samsung vs-internal-log -t' = @('host0', 'host1', 'ctlr', 'controller', 'vendor') 'sanitize --output-format' = @('normal', 'json', 'binary') 'sanitize --output-format-version' = @('1', '2') 'sanitize --sanact' = @('exit-failure', 'start-block-erase', 'start-overwrite', 'start-crypto-erase', 'exit-media-verification') @@ -3319,9 +3326,16 @@ Register-ArgumentCompleter -Native -CommandName nvme -ScriptBlock { if ($prevToken -like '-*') { $valKey = "$key $prevToken" if ($script:NvmeOptionValues.ContainsKey($valKey)) { + # A list of names completes the name after its last comma. + $pre = '' + if ($script:NvmeListOptions.ContainsKey($valKey) -and + $wordToComplete -match '^(.*,)') { + $pre = $Matches[1] + } + $last = $wordToComplete.Substring($pre.Length) return $script:NvmeOptionValues[$valKey] | - Where-Object { $_ -like "$wordToComplete*" } | - ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } + Where-Object { $_ -like "$last*" } | + ForEach-Object { [System.Management.Automation.CompletionResult]::new("$pre$_", $_, 'ParameterValue', $_) } } if ($script:NvmeFileOptions.ContainsKey($valKey)) { return Get-ChildItem -Path "$wordToComplete*" -ErrorAction SilentlyContinue | @@ -3341,9 +3355,15 @@ Register-ArgumentCompleter -Native -CommandName nvme -ScriptBlock { $valPrefix = $Matches[2] $valKey = "$key $optPart" if ($script:NvmeOptionValues.ContainsKey($valKey)) { + $pre = '' + if ($script:NvmeListOptions.ContainsKey($valKey) -and + $valPrefix -match '^(.*,)') { + $pre = $Matches[1] + } + $last = $valPrefix.Substring($pre.Length) return $script:NvmeOptionValues[$valKey] | - Where-Object { $_ -like "$valPrefix*" } | - ForEach-Object { [System.Management.Automation.CompletionResult]::new("$optPart=$_", $_, 'ParameterValue', $_) } + Where-Object { $_ -like "$last*" } | + ForEach-Object { [System.Management.Automation.CompletionResult]::new("$optPart=$pre$_", $_, 'ParameterValue', $_) } } if ($script:NvmeFileOptions.ContainsKey($valKey)) { return Get-ChildItem -Path "$valPrefix*" -ErrorAction SilentlyContinue | From db7a0caf9f132ee0f2c084ab7f514ecb6b72925b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:10:16 +0000 Subject: [PATCH 211/263] NEWS: document the list of dump types of samsung vs-internal-log Note that the dump types are checked while parsing the command line and completed after the last comma. Signed-off-by: Daniel Wagner --- NEWS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 3f00c4fb09..3681c3d5f4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -111,7 +111,10 @@ `sndk` and `wdc` `vs-internal-log` and `solidigm internal-log --type`. An unknown word is a usage error which lists the words. `ocp internal-log` with an unknown telemetry type failed with status - 0 before. + 0 before. `samsung vs-internal-log --dump-type` checks its list of + dump types the same way, before it opens the device, and may be given + more than once; the completions complete the type after the last + comma. * The help of `connect`, `discover`, `connect-all`, `disconnect` and `config create` lists the fabrics options they share under "Fabrics From bea4b59e8e5a7f4fbd18dcbeff7969066c52ddff Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:19:47 +0000 Subject: [PATCH 212/263] shared: reject numbers with a leading zero The number parsers used strtoull() with base 0, so a leading 0 made a number octal: --namespace-id=010 was namespace 8 and 08 was no number at all. Hardly anyone means octal on the nvme command line, and a zero padded number such as 0010 was silently misread. The 128-bit list parser read the same number as decimal instead. Reject a number which starts with 0 and another digit, on the command line, in nvme-cli.conf, in the lists and in the sizes with a suffix. 0 itself and hexadecimal numbers with 0x stay. The error of an option says what to write instead: invalid value '010' for -f: expected a number from 0 to 255 a number must not start with 0: write 10, or 0x10 for hexadecimal The admin-passthru examples gave the opcode as 06, it is 0x06 now. Signed-off-by: Daniel Wagner --- Documentation/nvme-admin-passthru.txt | 4 ++-- shared/parse-util.c | 11 +++++++++-- shared/parse-util.h | 14 +++++++++++--- shared/suffix-util.c | 5 +++-- shared/tests/test-parse-util.c | 19 +++++++++++++++++-- shared/tests/test-suffix-util.c | 4 ++++ src/argconfig.c | 12 ++++++++++++ tests/unit/c/test-argconfig-parse.c | 6 ++++++ tests/unit/c/test-global-config.c | 9 +++++++++ 9 files changed, 73 insertions(+), 11 deletions(-) diff --git a/Documentation/nvme-admin-passthru.txt b/Documentation/nvme-admin-passthru.txt index 78d6884ee7..c32c55fc16 100644 --- a/Documentation/nvme-admin-passthru.txt +++ b/Documentation/nvme-admin-passthru.txt @@ -130,14 +130,14 @@ EXAMPLES option is used because it is a data-in command + ------------ -# nvme admin-passthru /dev/nvme0 --opcode=06 --data-len=4096 --cdw10=1 -r +# nvme admin-passthru /dev/nvme0 --opcode=0x06 --data-len=4096 --cdw10=1 -r ------------ + * Or if you want to save that structure to a file: + ------------ -# nvme admin-passthru /dev/nvme0 --opcode=06 --data-len=4096 --cdw10=1 -r -b > id_ns.raw +# nvme admin-passthru /dev/nvme0 --opcode=0x06 --data-len=4096 --cdw10=1 -r -b > id_ns.raw ------------ NVME diff --git a/shared/parse-util.c b/shared/parse-util.c index f4fc1b4747..83b3c2b9e0 100644 --- a/shared/parse-util.c +++ b/shared/parse-util.c @@ -50,13 +50,18 @@ int shr_parse_bool(const char *value, bool *out) return -EINVAL; } +bool shr_has_leading_zero(const char *s) +{ + return s[0] == '0' && isdigit((unsigned char)s[1]); +} + int shr_parse_u64(const char *s, uint64_t max, uint64_t *out) { unsigned long long v; char *end; /* strtoull() skips whitespace and takes a sign, a digit must lead */ - if (!s || !isdigit((unsigned char)*s)) + if (!s || !isdigit((unsigned char)*s) || shr_has_leading_zero(s)) return -EINVAL; errno = 0; @@ -79,7 +84,7 @@ int shr_parse_s64(const char *s, int64_t min, int64_t max, int64_t *out) if (!s) return -EINVAL; digits = *s == '-' ? s + 1 : s; - if (!isdigit((unsigned char)*digits)) + if (!isdigit((unsigned char)*digits) || shr_has_leading_zero(digits)) return -EINVAL; errno = 0; @@ -205,6 +210,8 @@ int shr_parse_csv_uint128(char *string, uint128_t *val, unsigned int max_length) if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { base = 16; src += 2; + } else if (shr_has_leading_zero(src)) { + return -EINVAL; } if (*src == '\0') diff --git a/shared/parse-util.h b/shared/parse-util.h index 9e6514413f..c94b811618 100644 --- a/shared/parse-util.h +++ b/shared/parse-util.h @@ -27,9 +27,17 @@ typedef unsigned __int128 uint128_t; int shr_parse_bool(const char *value, bool *out); /* - * Parse the whole string @s as an unsigned number: decimal, hexadecimal - * with a 0x prefix or octal with a leading 0, like strtoull() with base 0, - * but no whitespace, no sign and nothing after the digits. + * True if the number @s starts with a 0 followed by another digit, such + * as "010". strtoull() with base 0 reads it as octal, which hardly anyone + * means, so the number parsers reject it. + */ +bool shr_has_leading_zero(const char *s); + +/* + * Parse the whole string @s as an unsigned number: decimal, or hexadecimal + * with a 0x prefix, like strtoull() with base 0, but no octal (see + * shr_has_leading_zero()), no whitespace, no sign and nothing after the + * digits. * Return: 0 on success (*out set), -EINVAL if @s is no number, -ERANGE if * it is larger than @max. */ diff --git a/shared/suffix-util.c b/shared/suffix-util.c index 1cf0782e4f..ed85103cbc 100644 --- a/shared/suffix-util.c +++ b/shared/suffix-util.c @@ -22,6 +22,7 @@ #include +#include "parse-util.h" #include "suffix-util.h" static struct si_suffix { @@ -68,7 +69,7 @@ int shr_suffix_si_parse(const char *str, char **endptr, uint64_t *val) int frac_len = 0, i; /* strtoull() takes whitespace and a sign, a digit must lead */ - if (!isdigit((unsigned char)*str)) + if (!isdigit((unsigned char)*str) || shr_has_leading_zero(str)) return -EINVAL; errno = 0; @@ -203,7 +204,7 @@ int shr_suffix_binary_parse(const char *str, char **endptr, uint64_t *val) int i; /* strtoull() takes whitespace and a sign, a digit must lead */ - if (!isdigit((unsigned char)*str)) + if (!isdigit((unsigned char)*str) || shr_has_leading_zero(str)) return -EINVAL; errno = 0; diff --git a/shared/tests/test-parse-util.c b/shared/tests/test-parse-util.c index ffd6c2eff5..b3f159f2ca 100644 --- a/shared/tests/test-parse-util.c +++ b/shared/tests/test-parse-util.c @@ -345,6 +345,14 @@ static bool test_csv_u128(void) pass &= check_int_ret("val[1]", (int)val[1], 2147483647); pass &= check_uint128_ret("val[2]", val[2], want); + { + /* no octal, a leading 0 was read as decimal */ + char octal[] = "1,010"; + + ret = shr_parse_csv_u128(octal, val, 8); + pass &= check_int_ret("leading 0 rejected", ret, -EINVAL); + } + return pass; } #else /* NVME_HAVE_INT128 */ @@ -376,7 +384,10 @@ static bool test_u64(void) pass &= check_u64("0", UINT64_MAX, 0, 0); pass &= check_u64("42", UINT64_MAX, 0, 42); pass &= check_u64("0x1F", UINT64_MAX, 0, 31); - pass &= check_u64("010", UINT64_MAX, 0, 8); + /* no octal: a leading 0 is rejected */ + pass &= check_u64("010", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("00", UINT64_MAX, -EINVAL, 0); + pass &= check_u64("0x010", UINT64_MAX, 0, 16); pass &= check_u64("18446744073709551615", UINT64_MAX, 0, UINT64_MAX); pass &= check_u64("18446744073709551616", UINT64_MAX, -ERANGE, 0); pass &= check_u64("256", 255, -ERANGE, 0); @@ -421,6 +432,9 @@ static bool test_s64(void) pass &= check_s64("2147483648", INT32_MIN, INT32_MAX, -ERANGE, 0); pass &= check_s64("-2147483649", INT32_MIN, INT32_MAX, -ERANGE, 0); pass &= check_s64("-0x10", INT32_MIN, INT32_MAX, 0, -16); + pass &= check_s64("-0", INT32_MIN, INT32_MAX, 0, 0); + pass &= check_s64("010", INT32_MIN, INT32_MAX, -EINVAL, 0); + pass &= check_s64("-010", INT32_MIN, INT32_MAX, -EINVAL, 0); pass &= check_s64("9223372036854775808", INT64_MIN, INT64_MAX, -ERANGE, 0); pass &= check_s64("--1", INT32_MIN, INT32_MAX, -EINVAL, 0); @@ -481,7 +495,8 @@ static bool test_csv_strict(void) { "1,-2", -1 }, { "+1", -1 }, { "4294967296", -1 }, - { "0x10,010", 2 }, + { "0x10,10", 2 }, + { "0x10,010", -1 }, }; unsigned int val[8]; bool pass = true; diff --git a/shared/tests/test-suffix-util.c b/shared/tests/test-suffix-util.c index 412067662c..ef3a3887bc 100644 --- a/shared/tests/test-suffix-util.c +++ b/shared/tests/test-suffix-util.c @@ -90,6 +90,8 @@ static bool test_suffix_si_parse(void) { "-1k", 0, -EINVAL }, { " 1k", 0, -EINVAL }, { "+1k", 0, -EINVAL }, + { "010k", 0, -EINVAL }, + { "0.5k", 500ULL, 0 }, { "", 0, -EINVAL }, }; bool pass = true; @@ -127,6 +129,8 @@ static bool test_suffix_binary_parse(void) { "18446744073709551616", 0, -EINVAL }, { "-1Ki", 0, -EINVAL }, { " 1Ki", 0, -EINVAL }, + { "010Ki", 0, -EINVAL }, + { "0", 0, 0 }, { "1ki", 1024ULL, 0 }, { "", 0, -EINVAL }, }; diff --git a/src/argconfig.c b/src/argconfig.c index 522f064195..eff3e3de24 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -732,6 +732,18 @@ static int argconfig_value_error(struct argconfig_commandline_options *s, list ? "" : "one of: ", v->str); fprintf(stderr, "\n"); + /* "010" was octal once, say what to write instead */ + if (arg && !list && s->config_type != CFG_ENUM && + shr_has_leading_zero(arg)) { + const char *d = arg; + + while (d[0] == '0' && d[1]) + d++; + fprintf(stderr, + "a number must not start with 0: write %s, or 0x%s for hexadecimal\n", + d, d); + } + return ARGCONFIG_USAGE; } diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index 1545df6b0a..667bceaea7 100644 --- a/tests/unit/c/test-argconfig-parse.c +++ b/tests/unit/c/test-argconfig-parse.c @@ -108,6 +108,12 @@ static struct toval_test toval_tests[] = { VAL_TEST("--suffix=32Gii", suffix, 0, true, -EINVAL), VAL_TEST("--uint=1", uint, 1, true, 0), VAL_TEST("--uint=4294967295", uint, 4294967295, true, 0), + /* no octal: a leading 0 is rejected, 0 and 0x are fine */ + VAL_TEST("--uint=010", uint, 0, true, -EINVAL), + VAL_TEST("--uint=0", uint, 0, true, 0), + VAL_TEST("--uint=0x10", uint, 16, true, 0), + VAL_TEST("--int=-010", int_val, 0, true, -EINVAL), + VAL_TEST("--suffix=010Ki", suffix, 0, true, -EINVAL), VAL_TEST("--uint=4294967296", uint, 0, true, -EINVAL), /* unsigned options reject negative numbers, signed ones don't */ VAL_TEST("--uint=-1", uint, 0, true, -EINVAL), diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index 5f63e36e55..04612fdb0d 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -161,6 +161,15 @@ static bool test_strict_numbers_and_buf(void) printf(" - '0x10' taken from a buffer [PASS]\n"); } + nvme_args.timeout = 42; + nvme_load_global_config_buf("[Global]\ntimeout = 010\n", "buf"); + if (nvme_args.timeout != 42) { + printf(" - '010' rejected [FAIL]\n"); + pass = false; + } else { + printf(" - '010' rejected [PASS]\n"); + } + nvme_args.output_format_ver = 2; nvme_load_global_config_buf("[Global]\noutput-format-version = 3\n", "buf"); From 0b7e746113114077098f143e0318641cfcdb3e04 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 09:19:47 +0000 Subject: [PATCH 213/263] NEWS: document that numbers take no leading zero Note that a leading zero is rejected instead of making a number octal. Signed-off-by: Daniel Wagner --- NEWS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.md b/NEWS.md index 3681c3d5f4..94e30bb4bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -61,6 +61,12 @@ (`1.09k` was rejected as octal, `1.500k` was 1000), and use a `.` in any locale as well. An empty value no longer selects a value name. +* Numbers no longer take a leading zero, on the command line, in + `nvme-cli.conf` and in the lists of numbers. Before, a leading zero + made a number octal: `--namespace-id=010` was namespace 8 and `08` + was rejected. Write `10`, or `0x10` for hexadecimal; `0` itself is + fine. + * Errors and warnings which repeat a command line argument or a line of a config file write its control characters as `\xNN`, so it can't send escape sequences to the terminal. From cd494118d41c791fc33596894bc657058e14af60 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:13:08 +0000 Subject: [PATCH 214/263] shared: add bounded string copy and append helpers The plugins build strings with strcpy(), strcat() and sprintf() into fixed buffers, which overflow if the input is longer than expected. Add shr_strscpy() and shr_strscat(), which copy or append a string and cut it off at the size of the buffer, always terminated, and report the cut with -E2BIG, and shr_snprintf_append(), which appends formatted text the same way and keeps track of the length, like the buf_add() of argconfig. Signed-off-by: Daniel Wagner --- shared/string-util.c | 58 ++++++++++++++++++++++ shared/string-util.h | 25 ++++++++++ shared/tests/test-string-util.c | 87 +++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) diff --git a/shared/string-util.c b/shared/string-util.c index e237a92b45..1afd4aa7ca 100644 --- a/shared/string-util.c +++ b/shared/string-util.c @@ -6,6 +6,8 @@ * Authors: Daniel Wagner */ +#include +#include #include #include @@ -34,3 +36,59 @@ char *shr_escape(const char *s) return e; } + +ssize_t shr_strscpy(char *dst, const char *src, size_t size) +{ + size_t len; + + if (!size) + return -E2BIG; + + len = strnlen(src, size); + if (len == size) { + memcpy(dst, src, size - 1); + dst[size - 1] = '\0'; + return -E2BIG; + } + memcpy(dst, src, len + 1); + + return len; +} + +ssize_t shr_strscat(char *dst, const char *src, size_t size) +{ + size_t len = strnlen(dst, size); + ssize_t n; + + /* no terminated string in @dst to append to */ + if (len == size) + return -E2BIG; + + n = shr_strscpy(dst + len, src, size - len); + + return n < 0 ? n : (ssize_t)(len + n); +} + +int shr_snprintf_append(char *buf, size_t size, size_t *len, + const char *fmt, ...) +{ + va_list ap; + int n; + + if (!size || *len >= size - 1) + return -E2BIG; + + va_start(ap, fmt); + n = vsnprintf(buf + *len, size - *len, fmt, ap); + va_end(ap); + + if (n < 0) + return -EINVAL; + if ((size_t)n >= size - *len) { + *len = size - 1; + return -E2BIG; + } + *len += n; + + return 0; +} diff --git a/shared/string-util.h b/shared/string-util.h index 98d9fcfb6f..059a81aa16 100644 --- a/shared/string-util.h +++ b/shared/string-util.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * strsep() of the C library, with a fallback for those which don't have @@ -253,3 +254,27 @@ static inline size_t shr_linelen(char *s) * The caller frees it. NULL if out of memory. */ char *shr_escape(const char *s); + +/* + * Copy the string @src to @dst of @size bytes, cut off if it doesn't fit. + * @dst is always terminated, unless @size is 0. + * Return: the length of the copy, or -E2BIG if @src was cut off. + */ +ssize_t shr_strscpy(char *dst, const char *src, size_t size); + +/* + * Append the string @src to the string in @dst of @size bytes, cut off if + * it doesn't fit. @dst is always terminated, unless @size is 0. + * Return: the length of the result, or -E2BIG if @src was cut off. + */ +ssize_t shr_strscat(char *dst, const char *src, size_t size); + +/* + * Append the formatted text to @buf of @size bytes, which holds a string + * of *@len characters, and advance *@len. Text which doesn't fit is cut + * off, @buf stays terminated. + * Return: 0, or -E2BIG if the text was cut off. + */ +int shr_snprintf_append(char *buf, size_t size, size_t *len, + const char *fmt, ...) + __attribute__((format(printf, 4, 5))); diff --git a/shared/tests/test-string-util.c b/shared/tests/test-string-util.c index 7e4db7b405..b066b29329 100644 --- a/shared/tests/test-string-util.c +++ b/shared/tests/test-string-util.c @@ -6,6 +6,7 @@ * Authors: Martin Belanger */ +#include #include #include #include @@ -283,6 +284,89 @@ static bool test_escape(void) return pass; } +static bool check_ret(const char *name, long got, long want) +{ + if (got == want) { + printf(" - %s [PASS]\n", name); + return true; + } + + printf(" - %s: got %ld, want %ld [FAIL]\n", name, got, want); + return false; +} + +static bool test_strscpy(void) +{ + char buf[4] = "xyz"; + bool pass = true; + + printf("test_strscpy:\n"); + + pass &= check_ret("fits", shr_strscpy(buf, "ab", sizeof(buf)), 2); + pass &= check_str("fits, copied", buf, "ab"); + pass &= check_ret("fits exactly", + shr_strscpy(buf, "abc", sizeof(buf)), 3); + pass &= check_str("fits exactly, copied", buf, "abc"); + pass &= check_ret("too long", shr_strscpy(buf, "abcd", sizeof(buf)), + -E2BIG); + pass &= check_str("too long, cut off", buf, "abc"); + pass &= check_ret("empty", shr_strscpy(buf, "", sizeof(buf)), 0); + pass &= check_str("empty, copied", buf, ""); + pass &= check_ret("no room", shr_strscpy(buf, "a", 0), -E2BIG); + + return pass; +} + +static bool test_strscat(void) +{ + char buf[6] = "ab"; + bool pass = true; + + printf("test_strscat:\n"); + + pass &= check_ret("fits", shr_strscat(buf, "cd", sizeof(buf)), 4); + pass &= check_str("fits, appended", buf, "abcd"); + pass &= check_ret("too long", shr_strscat(buf, "efg", sizeof(buf)), + -E2BIG); + pass &= check_str("too long, cut off", buf, "abcde"); + pass &= check_ret("full", shr_strscat(buf, "x", sizeof(buf)), -E2BIG); + pass &= check_str("full, unchanged", buf, "abcde"); + + return pass; +} + +static bool test_snprintf_append(void) +{ + char buf[8]; + size_t len = 0; + bool pass = true; + + printf("test_snprintf_append:\n"); + + buf[0] = '\0'; + pass &= check_ret("first", + shr_snprintf_append(buf, sizeof(buf), &len, "%d", 12), + 0); + pass &= check_ret("second", + shr_snprintf_append(buf, sizeof(buf), &len, "-%s", + "ab"), + 0); + pass &= check_str("appended", buf, "12-ab"); + pass &= check_ret("length", len, 5); + pass &= check_ret("cut off", + shr_snprintf_append(buf, sizeof(buf), &len, "%s", + "xyz"), + -E2BIG); + pass &= check_str("cut off, terminated", buf, "12-abxy"); + pass &= check_ret("length at the end", len, 7); + pass &= check_ret("full", + shr_snprintf_append(buf, sizeof(buf), &len, "z"), + -E2BIG); + pass &= check_str("full, unchanged", buf, "12-abxy"); + + return pass; +} + int main(void) { bool pass = true; @@ -299,6 +383,9 @@ int main(void) pass &= test_kv_strip(); pass &= test_kv_keymatch(); pass &= test_escape(); + pass &= test_strscpy(); + pass &= test_strscat(); + pass &= test_snprintf_append(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From 447b9ba9c38cda6a5b824b8b2f697d7eef7e31d1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:13:08 +0000 Subject: [PATCH 215/263] argconfig: use shr_snprintf_append() buf_add() moved to shared as shr_snprintf_append(). The help output is unchanged. Signed-off-by: Daniel Wagner --- src/argconfig.c | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/argconfig.c b/src/argconfig.c index eff3e3de24..65d643f681 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -164,24 +164,6 @@ static void show_value_names(FILE *out, const struct argconfig_opt_val *v) shr_print_word_wrapped(names, 44, 44, out); } -/* Append to @buf of @size, whose @len stays below @size when it is full */ -static void buf_add(char *buf, size_t size, size_t *len, const char *fmt, ...) -{ - va_list ap; - int n; - - if (*len >= size - 1) - return; - - va_start(ap, fmt); - n = vsnprintf(buf + *len, size - *len, fmt, ap); - va_end(ap); - - if (n < 0) - return; - *len = *len + n < size ? *len + n : size - 1; -} - static void show_option(FILE *out, const struct argconfig_commandline_options *option) { @@ -191,25 +173,30 @@ static void show_option(FILE *out, size_t len = 0; buffer[0] = '\0'; - buf_add(buffer, sizeof(buffer), &len, " [ "); + shr_snprintf_append(buffer, sizeof(buffer), &len, " [ "); if (option->option) { - buf_add(buffer, sizeof(buffer), &len, " --%s", option->option); + shr_snprintf_append(buffer, sizeof(buffer), &len, " --%s", + option->option); if (arg == ARGCONFIG_ARG_OPTIONAL) - buf_add(buffer, sizeof(buffer), &len, "[=<%s>]", meta); + shr_snprintf_append(buffer, sizeof(buffer), &len, + "[=<%s>]", meta); if (arg == ARGCONFIG_ARG_REQUIRED) - buf_add(buffer, sizeof(buffer), &len, "=<%s>", meta); + shr_snprintf_append(buffer, sizeof(buffer), &len, + "=<%s>", meta); if (option->short_option) - buf_add(buffer, sizeof(buffer), &len, ","); + shr_snprintf_append(buffer, sizeof(buffer), &len, ","); } if (option->short_option) { - buf_add(buffer, sizeof(buffer), &len, " -%c", - option->short_option); + shr_snprintf_append(buffer, sizeof(buffer), &len, " -%c", + option->short_option); if (arg == ARGCONFIG_ARG_OPTIONAL) - buf_add(buffer, sizeof(buffer), &len, " [<%s>]", meta); + shr_snprintf_append(buffer, sizeof(buffer), &len, + " [<%s>]", meta); if (arg == ARGCONFIG_ARG_REQUIRED) - buf_add(buffer, sizeof(buffer), &len, " <%s>", meta); + shr_snprintf_append(buffer, sizeof(buffer), &len, + " <%s>", meta); } - buf_add(buffer, sizeof(buffer), &len, " ] "); + shr_snprintf_append(buffer, sizeof(buffer), &len, " ] "); fprintf(out, "%s", buffer); if (option->help) { From 64813bf62b12d6299eeceded68a0c921a453fc72 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:13:08 +0000 Subject: [PATCH 216/263] shared: add shr_bin_to_hex() Several plugins print a GUID or another byte array as hex with a loop of sprintf(p, "%02x") into a buffer whose size the loop never checks. Add shr_bin_to_hex(), which writes the bytes as hex digits in lower or upper case and fails with -E2BIG if the buffer is too small. Signed-off-by: Daniel Wagner --- shared/hex-util.c | 26 +++++++++++++++++ shared/hex-util.h | 18 ++++++++++++ shared/tests/test-hex-util.c | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/shared/hex-util.c b/shared/hex-util.c index 6521da9a54..dd727be6d0 100644 --- a/shared/hex-util.c +++ b/shared/hex-util.c @@ -6,6 +6,7 @@ * Authors: Chaithanya Shoba */ +#include #include #include @@ -74,3 +75,28 @@ char *shr_hex_to_ascii(const char *hex) return text; } + +int shr_bin_to_hex(char *dst, size_t size, const void *src, size_t len, + unsigned int flags) +{ + const char *digits = flags & SHR_HEX_UPPER ? + "0123456789ABCDEF" : "0123456789abcdef"; + const unsigned char *p = src; + unsigned char b; + size_t i; + + if (size < 2 * len + 1) { + if (size) + dst[0] = '\0'; + return -E2BIG; + } + + for (i = 0; i < len; i++) { + b = flags & SHR_HEX_REVERSE ? p[len - 1 - i] : p[i]; + dst[2 * i] = digits[b >> 4]; + dst[2 * i + 1] = digits[b & 0xf]; + } + dst[2 * len] = '\0'; + + return 2 * len; +} diff --git a/shared/hex-util.h b/shared/hex-util.h index fd0b153d2f..49507dc3f1 100644 --- a/shared/hex-util.h +++ b/shared/hex-util.h @@ -7,6 +7,9 @@ */ #pragma once +#include +#include + /* * Convert a single hexadecimal digit ('0'-'9', 'a'-'f', 'A'-'F') to its * integer value. Return: 0-15, or -1 if c is not a hex digit. @@ -19,3 +22,18 @@ int shr_hex_to_int(char c); * Return: a newly allocated buffer (caller must free), or NULL on error. */ char *shr_hex_to_ascii(const char *hex); + +/* shr_bin_to_hex(): upper case digits, the last byte first */ +#define SHR_HEX_UPPER (1 << 0) +#define SHR_HEX_REVERSE (1 << 1) + +/* + * Write the @len bytes at @src to @dst of @size bytes as a string of two + * hexadecimal digits per byte. @flags are SHR_HEX_* values: upper case + * digits, and the bytes from the last to the first, e.g. for a little + * endian GUID. + * Return: the length of the string, or -E2BIG if @size is less than + * 2 * @len + 1, in which case @dst is the empty string (if @size isn't 0). + */ +int shr_bin_to_hex(char *dst, size_t size, const void *src, size_t len, + unsigned int flags); diff --git a/shared/tests/test-hex-util.c b/shared/tests/test-hex-util.c index fc46ab060d..1d4ff0146d 100644 --- a/shared/tests/test-hex-util.c +++ b/shared/tests/test-hex-util.c @@ -6,6 +6,7 @@ * Authors: Daniel Wagner */ +#include #include #include #include @@ -85,12 +86,67 @@ static bool test_hex_to_ascii(void) return pass; } +static bool test_bin_to_hex(void) +{ + const unsigned char bin[] = { 0x00, 0x1f, 0xa0, 0xff }; + char buf[9]; + bool pass = true; + int ret; + + printf("test_bin_to_hex:\n"); + + ret = shr_bin_to_hex(buf, sizeof(buf), bin, sizeof(bin), 0); + if (ret != 8 || strcmp(buf, "001fa0ff")) { + printf(" - lower case [FAIL] got %d '%s'\n", ret, buf); + pass = false; + } else { + printf(" - lower case [PASS]\n"); + } + + ret = shr_bin_to_hex(buf, sizeof(buf), bin, sizeof(bin), + SHR_HEX_UPPER); + if (ret != 8 || strcmp(buf, "001FA0FF")) { + printf(" - upper case [FAIL] got %d '%s'\n", ret, buf); + pass = false; + } else { + printf(" - upper case [PASS]\n"); + } + + ret = shr_bin_to_hex(buf, sizeof(buf), bin, sizeof(bin), + SHR_HEX_REVERSE); + if (ret != 8 || strcmp(buf, "ffa01f00")) { + printf(" - reverse [FAIL] got %d '%s'\n", ret, buf); + pass = false; + } else { + printf(" - reverse [PASS]\n"); + } + + ret = shr_bin_to_hex(buf, sizeof(buf) - 1, bin, sizeof(bin), 0); + if (ret != -E2BIG || buf[0]) { + printf(" - too small [FAIL] got %d\n", ret); + pass = false; + } else { + printf(" - too small [PASS]\n"); + } + + ret = shr_bin_to_hex(buf, 1, bin, 0, 0); + if (ret != 0 || buf[0]) { + printf(" - empty [FAIL] got %d\n", ret); + pass = false; + } else { + printf(" - empty [PASS]\n"); + } + + return pass; +} + int main(void) { bool pass = true; pass &= test_hex_to_int(); pass &= test_hex_to_ascii(); + pass &= test_bin_to_hex(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); From 77f0ed21f7d161f7912247adcca716457d981b4f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:13:45 +0000 Subject: [PATCH 217/263] huawei: bound the string buffers Use snprintf(), shr_strscpy() and shr_bin_to_hex() instead of sprintf() and strncpy(), which don't check the size of the buffer or leave it unterminated. Signed-off-by: Daniel Wagner --- plugins/huawei/huawei-nvme.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 2effa7f159..4a96a806c6 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -29,6 +29,8 @@ #include #include +#include +#include #include #include "cleanup.h" @@ -93,8 +95,7 @@ static int huawei_get_nvme_info(struct libnvme_transport_handle *hdl, if (err) return err; - strncpy(item->node, node, sizeof(item->node)); - item->node[sizeof(item->node) - 1] = '\0'; + shr_strscpy(item->node, node, sizeof(item->node)); item->block = libnvme_transport_handle_is_ns(hdl); if (item->ns.vs[0] == 0) { @@ -231,14 +232,11 @@ static void huawei_print_list_item(struct huawei_list_item *list_item, char usage[128]; char nguid_buf[2 * sizeof(list_item->ns.nguid) + 1]; - char *nguid = nguid_buf; - int i; - - sprintf(usage, "%6.2f %2sB / %6.2f %2sB", nuse, u_suffix, nsze, s_suffix); - memset(nguid, 0, sizeof(nguid_buf)); - for (i = 0; i < sizeof(list_item->ns.nguid); i++) - nguid += sprintf(nguid, "%02x", list_item->ns.nguid[i]); + snprintf(usage, sizeof(usage), "%6.2f %2sB / %6.2f %2sB", nuse, + u_suffix, nsze, s_suffix); + shr_bin_to_hex(nguid_buf, sizeof(nguid_buf), list_item->ns.nguid, + sizeof(list_item->ns.nguid), 0); printf("%-*.*s %-*.*s %-*.*s %-*d %-*.*s %-*.*s\n", element_len.node, element_len.node, list_item->node, From 272216e3a6fcf75a269f964c324313dc57adf74a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:14:39 +0000 Subject: [PATCH 218/263] innogrit: bound the string buffers Use snprintf() and shr_strscpy() instead of sprintf() and strcpy(), and pass getcwd() the size of the buffer instead of a number. Signed-off-by: Daniel Wagner --- plugins/innogrit/innogrit-nvme.c | 47 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/plugins/innogrit/innogrit-nvme.c b/plugins/innogrit/innogrit-nvme.c index 5839813983..8c4b95fe61 100644 --- a/plugins/innogrit/innogrit-nvme.c +++ b/plugins/innogrit/innogrit-nvme.c @@ -10,6 +10,7 @@ #include #include +#include #include "global-ctx.h" #include "nvme-print.h" @@ -189,14 +190,15 @@ static int innogrit_geteventlog(int argc, char **argv, if (ret) return ret; - if (getcwd(currentdir, 128) == NULL) + if (getcwd(currentdir, sizeof(currentdir)) == NULL) return -1; time(&timep); logtime = localtime(&timep); - sprintf(filename, "%s/eventlog_%02d%02d-%02d%02d%02d.eraw", currentdir, - logtime->tm_mon + 1, logtime->tm_mday, logtime->tm_hour, - logtime->tm_min, logtime->tm_sec); + snprintf(filename, sizeof(filename), + "%s/eventlog_%02d%02d-%02d%02d%02d.eraw", currentdir, + logtime->tm_mon + 1, logtime->tm_mday, logtime->tm_hour, + logtime->tm_min, logtime->tm_sec); printf("output eventlog file : %s\n", filename); fp = fopen(filename, "a+"); @@ -239,7 +241,7 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, ivsctype = getvsctype(hdl); - if (getcwd(currentdir, 128) == NULL) + if (getcwd(currentdir, sizeof(currentdir)) == NULL) return -1; time(&timep); @@ -268,10 +270,13 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, itotal = cdumpinfo.cdumppack[ipackindex].ilenth; memset(fwvera, 0, sizeof(fwvera)); memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8); - sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", - logtime->tm_mon+1, logtime->tm_mday, logtime->tm_hour, - logtime->tm_min, logtime->tm_sec, ipackindex, fwvera); - sprintf(filename, "%s/%s", currentdir, fname); + snprintf(fname, sizeof(fname), + "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", + logtime->tm_mon+1, logtime->tm_mday, + logtime->tm_hour, logtime->tm_min, + logtime->tm_sec, ipackindex, fwvera); + snprintf(filename, sizeof(filename), "%s/%s", + currentdir, fname); fp = fopen(filename, "a+"); } } @@ -286,9 +291,12 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, ipackcount = 1; memcpy(&itotal, &data[4092], 4); - sprintf(fname, "cdump_%02d%02d-%02d%02d%02d.cdp", logtime->tm_mon+1, - logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec); - sprintf(filename, "%s/%s", currentdir, fname); + snprintf(fname, sizeof(fname), + "cdump_%02d%02d-%02d%02d%02d.cdp", logtime->tm_mon+1, + logtime->tm_mday, logtime->tm_hour, logtime->tm_min, + logtime->tm_sec); + snprintf(filename, sizeof(filename), "%s/%s", currentdir, + fname); fp = fopen(filename, "a+"); } @@ -301,7 +309,7 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, while (ipackindex < ipackcount) { memset(data, 0, 4096); - strcpy((char *)data, "cdumpstart"); + shr_strscpy(data, "cdumpstart", sizeof(data)); fwrite(data, 1, strlen((char *)data), fp); for (icur = 0; icur < itotal; icur += 4096) { memset(data, 0, 4096); @@ -331,7 +339,7 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, ((icur + 4096) * 100 / itotal)); } memset(data, 0, 4096); - strcpy((char *)data, "cdumpend"); + shr_strscpy(data, "cdumpend", sizeof(data)); fwrite(data, 1, strlen((char *)data), fp); printf("\r%s\n", fname); ipackindex++; @@ -361,10 +369,13 @@ static int innogrit_vsc_getcdump(int argc, char **argv, struct command *acmd, itotal = cdumpinfo.cdumppack[ipackindex].ilenth; memset(fwvera, 0, sizeof(fwvera)); memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8); - sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", logtime->tm_mon+1, - logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec, - ipackindex, fwvera); - sprintf(filename, "%s/%s", currentdir, fname); + snprintf(fname, sizeof(fname), + "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", + logtime->tm_mon+1, logtime->tm_mday, + logtime->tm_hour, logtime->tm_min, + logtime->tm_sec, ipackindex, fwvera); + snprintf(filename, sizeof(filename), "%s/%s", + currentdir, fname); if (fp != NULL) fclose(fp); fp = fopen(filename, "a+"); From cd8e41fa3e63bd4bf75e365f11e63b183d91ef4b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:15:11 +0000 Subject: [PATCH 219/263] intel: pass the size of the log file name buffer setup_file() wrote the file name with sprintf() into a buffer whose size it didn't know. Pass the size and use snprintf(). Signed-off-by: Daniel Wagner --- plugins/intel/intel-nvme.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 23b68456bc..42bbf7c5ff 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -1290,7 +1290,8 @@ static int read_header(struct libnvme_passthru_cmd *cmd, __u8 *buf, return read_entire_cmd(cmd, 0x400, 0x400, -1, hdl, buf); } -static int setup_file(char *f, char *file, struct libnvme_transport_handle *hdl, int type) +static int setup_file(char *f, size_t size, char *file, + struct libnvme_transport_handle *hdl, int type) { struct nvme_id_ctrl ctrl; struct libnvme_passthru_cmd cmd; @@ -1307,9 +1308,9 @@ static int setup_file(char *f, char *file, struct libnvme_transport_handle *hdl, i--; } - sprintf(f, "%s_%-.*s.bin", type == 0 ? "Nlog" : - type == 1 ? "EventLog" : "AssertLog", - (int)sizeof(ctrl.sn), ctrl.sn); + snprintf(f, size, "%s_%-.*s.bin", type == 0 ? "Nlog" : + type == 1 ? "EventLog" : "AssertLog", + (int)sizeof(ctrl.sn), ctrl.sn); return err; } @@ -1412,7 +1413,7 @@ static int get_internal_log(int argc, char **argv, struct command *acmd, } if (!cfg.file) { - err = setup_file(f, cfg.file, hdl, cfg.log); + err = setup_file(f, sizeof(f), cfg.file, hdl, cfg.log); if (err) goto out_free; cfg.file = f; From 28b98cb344771e94664ca3f16d283b86ac9a36e8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:16:50 +0000 Subject: [PATCH 220/263] nbft: write the namespace identifier with shr_bin_to_hex() The EUI-64 and NGUID were written byte by byte with sprintf() into a buffer whose size the loop didn't check. Signed-off-by: Daniel Wagner --- plugins/nbft/nbft-plugin.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 7e2a788e4a..e303985615 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -8,6 +8,7 @@ #include #include +#include #include "cleanup.h" #include "global-ctx.h" @@ -211,7 +212,6 @@ static json_object *ssns_to_json(struct libnbft_subsystem_ns *ss) struct json_object *ss_json; struct json_object *hfi_array_json; char json_str[40]; - char *json_str_p; int i; ss_json = json_create_object(); @@ -238,21 +238,18 @@ static json_object *ssns_to_json(struct libnbft_subsystem_ns *ss) goto fail; memset(json_str, 0, sizeof(json_str)); - json_str_p = json_str; switch (ss->nid_type) { case LIBNBFT_NID_TYPE_EUI64: if (json_object_add_value_string(ss_json, "nid_type", "eui64")) goto fail; - for (i = 0; i < 8; i++) - json_str_p += sprintf(json_str_p, "%02x", ss->nid[i]); + shr_bin_to_hex(json_str, sizeof(json_str), ss->nid, 8, 0); break; case LIBNBFT_NID_TYPE_NGUID: if (json_object_add_value_string(ss_json, "nid_type", "nguid")) goto fail; - for (i = 0; i < 16; i++) - json_str_p += sprintf(json_str_p, "%02x", ss->nid[i]); + shr_bin_to_hex(json_str, sizeof(json_str), ss->nid, 16, 0); break; case LIBNBFT_NID_TYPE_NS_UUID: From fcabecab866fae4e9c9684491ac4630e84b33837 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:16:50 +0000 Subject: [PATCH 221/263] netapp: bound the string buffers The helpers wrote the NGUID, the sizes and the device name with sprintf() and strncpy() into buffers whose size they didn't know, strncpy() possibly unterminated. Give the buffers a defined length and use shr_bin_to_hex(), snprintf() and shr_strscpy(). Signed-off-by: Daniel Wagner --- plugins/netapp/netapp-nvme.c | 59 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 14920ae9ca..5dcf73abf4 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include "cleanup.h" @@ -99,16 +101,17 @@ static void netapp_convert_string(char *dst, char *src, unsigned int count) dst[0] = ' '; } -static void netapp_nguid_to_str(char *str, __u8 *nguid) -{ - int i; +/* The NGUID as hex string, and the size, use or block size of a namespace */ +#define NETAPP_NGUID_LEN (2 * 16 + 1) +#define NETAPP_ATTR_LEN 128 - memset(str, 0, 33); - for (i = 0; i < 16; i++) - str += sprintf(str, "%02x", nguid[i]); +static void netapp_nguid_to_str(char str[NETAPP_NGUID_LEN], __u8 *nguid) +{ + shr_bin_to_hex(str, NETAPP_NGUID_LEN, nguid, 16, 0); } -static void netapp_get_ns_size(char *size, unsigned long long *lba, +static void netapp_get_ns_size(char size[NETAPP_ATTR_LEN], + unsigned long long *lba, struct nvme_id_ns *ns) { __u8 lba_index; @@ -118,10 +121,11 @@ static void netapp_get_ns_size(char *size, unsigned long long *lba, double nsze = le64_to_cpu(ns->nsze) * (*lba); const char *s_suffix = shr_suffix_si_get(&nsze); - sprintf(size, "%.2f%sB", nsze, s_suffix); + snprintf(size, NETAPP_ATTR_LEN, "%.2f%sB", nsze, s_suffix); } -static void netapp_get_ns_attrs(char *size, char *used, char *blk_size, +static void netapp_get_ns_attrs(char size[NETAPP_ATTR_LEN], + char used[NETAPP_ATTR_LEN], char blk_size[NETAPP_ATTR_LEN], char *version, unsigned long long *lba, struct nvme_id_ctrl *ctrl, struct nvme_id_ns *ns) { @@ -134,19 +138,20 @@ static void netapp_get_ns_attrs(char *size, char *used, char *blk_size, double nsze = le64_to_cpu(ns->nsze) * (*lba); const char *s_suffix = shr_suffix_si_get(&nsze); - sprintf(size, "%.2f%sB", nsze, s_suffix); + snprintf(size, NETAPP_ATTR_LEN, "%.2f%sB", nsze, s_suffix); /* get the namespace utilization */ double nuse = le64_to_cpu(ns->nuse) * (*lba); const char *u_suffix = shr_suffix_si_get(&nuse); - sprintf(used, "%.2f%sB", nuse, u_suffix); + snprintf(used, NETAPP_ATTR_LEN, "%.2f%sB", nuse, u_suffix); /* get the namespace block size */ long long addr = 1LL << ns->lbaf[lba_index].ds; const char *l_suffix = shr_suffix_binary_get(&addr); - sprintf(blk_size, "%u%sB", (unsigned int)addr, l_suffix); + snprintf(blk_size, NETAPP_ATTR_LEN, "%u%sB", (unsigned int)addr, + l_suffix); /* get the firmware version */ int i, max = sizeof(ctrl->fr); @@ -364,10 +369,10 @@ static void netapp_smdevices_print_verbose(struct smdevice_info *devices, int i, slta; char array_label[ARRAY_LABEL_LEN / 2 + 1]; char volume_label[VOLUME_LABEL_LEN / 2 + 1]; - char nguid_str[33]; + char nguid_str[NETAPP_NGUID_LEN]; unsigned long long lba; - char size[128], used[128]; - char blk_size[128], version[9]; + char size[NETAPP_ATTR_LEN], used[NETAPP_ATTR_LEN]; + char blk_size[NETAPP_ATTR_LEN], version[9]; char *formatstr = NULL; char basestr[] = @@ -445,9 +450,9 @@ static void netapp_smdevices_print_regular(struct smdevice_info *devices, int i, slta; char array_label[ARRAY_LABEL_LEN / 2 + 1]; char volume_label[VOLUME_LABEL_LEN / 2 + 1]; - char nguid_str[33]; + char nguid_str[NETAPP_NGUID_LEN]; unsigned long long lba; - char size[128]; + char size[NETAPP_ATTR_LEN]; char *formatstr = NULL; char basestr[] = @@ -514,10 +519,10 @@ static void netapp_smdevices_print_json(struct smdevice_info *devices, int i, slta; char array_label[ARRAY_LABEL_LEN / 2 + 1]; char volume_label[VOLUME_LABEL_LEN / 2 + 1]; - char nguid_str[33]; + char nguid_str[NETAPP_NGUID_LEN]; unsigned long long lba; - char size[128], used[128]; - char blk_size[128], version[9]; + char size[NETAPP_ATTR_LEN], used[NETAPP_ATTR_LEN]; + char blk_size[NETAPP_ATTR_LEN], version[9]; /* prepare for the json output */ root = json_create_object(); @@ -581,8 +586,8 @@ static void netapp_ontapdevices_print_verbose(struct ontapdevice_info *devices, char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; unsigned long long lba; - char size[128], used[128]; - char blk_size[128], version[9]; + char size[NETAPP_ATTR_LEN], used[NETAPP_ATTR_LEN]; + char blk_size[NETAPP_ATTR_LEN], version[9]; char uuid_str[37] = " "; char subnqn[257], subsysname[65]; int i; @@ -652,7 +657,7 @@ static void netapp_ontapdevices_print_regular(struct ontapdevice_info *devices, char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; unsigned long long lba; - char size[128]; + char size[NETAPP_ATTR_LEN]; char uuid_str[37] = " "; char subnqn[257], subsysname[65]; int i; @@ -718,8 +723,8 @@ static void netapp_ontapdevices_print_json(struct ontapdevice_info *devices, char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; unsigned long long lba; - char size[128], used[128]; - char blk_size[128], version[9]; + char size[NETAPP_ATTR_LEN], used[NETAPP_ATTR_LEN]; + char blk_size[NETAPP_ATTR_LEN], version[9]; char uuid_str[37] = " "; char subnqn[257], subsysname[65]; int i; @@ -834,7 +839,7 @@ static int netapp_smdevices_get_info(struct libnvme_transport_handle *hdl, libnvme_status_to_string(err, false)); return 0; } - strncpy(item->dev, dev, sizeof(item->dev) - 1); + shr_strscpy(item->dev, dev, sizeof(item->dev)); return 1; } @@ -907,7 +912,7 @@ static int netapp_ontapdevices_get_info(struct libnvme_transport_handle *hdl, return 0; } - strncpy(item->dev, dev, sizeof(item->dev) - 1); + shr_strscpy(item->dev, dev, sizeof(item->dev)); return 1; } From 608a59fbc3c2a486c83eb516e04aec3a181c7d2f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:33:53 +0000 Subject: [PATCH 222/263] ocp: bound the string buffers Use snprintf(), shr_strscpy() and shr_bin_to_hex() instead of sprintf(), strcpy() and strcat(): the GUIDs and the debug log size are written with shr_bin_to_hex(), the FIFO strings are copied with memcpy() and the reserved fields are appended with shr_snprintf_append(), each into a buffer whose size is checked. The output is unchanged. Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-nvme.c | 12 +- plugins/ocp/ocp-print-json.c | 246 ++++++++++++--------------- plugins/ocp/ocp-print-stdout.c | 6 +- plugins/ocp/ocp-smart-extended-log.c | 7 +- plugins/ocp/ocp-telemetry-decode.c | 30 ++-- 5 files changed, 142 insertions(+), 159 deletions(-) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 28559385df..1c8cb321d4 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -1079,7 +1079,8 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct } /* Pull the Telemetry log */ - sprintf(file_path_telemetry, "%s-%s", opt.output_file, tele_log_suffix); + snprintf(file_path_telemetry, sizeof(file_path_telemetry), + "%s-%s", opt.output_file, tele_log_suffix); err = get_telemetry_log_page_data(hdl, tele_type, !!host_gen, @@ -1105,7 +1106,8 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct nvme_show_result("Missing string-log. Fetching from drive...\n"); /* Pull String log */ - sprintf(file_path_string, "%s-%s", opt.output_file, string_suffix); + snprintf(file_path_string, sizeof(file_path_string), "%s-%s", + opt.output_file, string_suffix); err = get_c9_log_page_data(hdl, 0, 1, (const char *)file_path_string, NULL, NULL, NULL, !no_uuid_flag); @@ -2047,9 +2049,11 @@ static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *a return ret; if (cfg.output_file != NULL) - sprintf(file_path, "%s-%s", cfg.output_file, string_suffix); + snprintf(file_path, sizeof(file_path), "%s-%s", cfg.output_file, + string_suffix); else - sprintf(file_path, "%s", DEFAULT_STRING_BIN); + snprintf(file_path, sizeof(file_path), "%s", + DEFAULT_STRING_BIN); ret = get_c9_log_page(hdl, cfg.output_format, file_path, !no_uuid_flag); diff --git a/plugins/ocp/ocp-print-json.c b/plugins/ocp/ocp-print-json.c index 6b0300ddcb..572ea3c9f8 100644 --- a/plugins/ocp/ocp-print-json.c +++ b/plugins/ocp/ocp-print-json.c @@ -3,9 +3,12 @@ #include #include +#include #include +#include #include #include +#include #include #include "cleanup.h" @@ -198,9 +201,9 @@ static void json_fw_activation_history(const struct fw_activation_history *fw_hi char guid[2 * sizeof(fw_history->log_page_guid) + 3] = { 0 }; - sprintf(guid, "0x%016"PRIx64"%016"PRIx64"", - le64_to_cpu(fw_history->log_page_guid[1]), - le64_to_cpu(fw_history->log_page_guid[0])); + snprintf(guid, sizeof(guid), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(fw_history->log_page_guid[1]), + le64_to_cpu(fw_history->log_page_guid[0])); json_object_add_value_string(root, "log page guid", guid); json_print_object(root, NULL); @@ -286,9 +289,9 @@ static void json_smart_extended_log_v1(struct ocp_smart_extended_log *log) json_object_add_value_uint(root, "Log page version", smart_log_ver); memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%016"PRIx64"%016"PRIx64"", - le64_to_cpu(*(uint64_t *)&log->log_page_guid[8]), - le64_to_cpu(*(uint64_t *)&log->log_page_guid)); + snprintf((char *)guid, sizeof(guid), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(*(uint64_t *)&log->log_page_guid[8]), + le64_to_cpu(*(uint64_t *)&log->log_page_guid)); json_object_add_value_string(root, "Log page GUID", guid); switch (smart_log_ver) { @@ -455,9 +458,9 @@ static void json_smart_extended_log_v2(struct ocp_smart_extended_log *log) json_object_add_value_uint(root, "log_page_version", smart_log_ver); memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%016"PRIx64"%016"PRIx64"", - le64_to_cpu(*(uint64_t *)&log->log_page_guid[8]), - le64_to_cpu(*(uint64_t *)&log->log_page_guid)); + snprintf((char *)guid, sizeof(guid), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(*(uint64_t *)&log->log_page_guid[8]), + le64_to_cpu(*(uint64_t *)&log->log_page_guid)); json_object_add_value_string(root, "log_page_guid", guid); switch (smart_log_ver) { @@ -604,7 +607,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Active Bucket Counter: Bucket %d", i); + snprintf(buf, sizeof(buf), "Active Bucket Counter: Bucket %d", + i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le32_to_cpu(log_data->active_bucket_counter[i][j+1])); @@ -616,7 +620,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Active Latency Time Stamp: Bucket %d", i); + snprintf(buf, sizeof(buf), + "Active Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); @@ -633,7 +638,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Active Measured Latency: Bucket %d", i); + snprintf(buf, sizeof(buf), "Active Measured Latency: Bucket %d", + i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le16_to_cpu(log_data->active_measured_latency[3-i][j])); @@ -648,7 +654,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Static Bucket Counter: Bucket %d", i); + snprintf(buf, sizeof(buf), "Static Bucket Counter: Bucket %d", + i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le32_to_cpu(log_data->static_bucket_counter[i][j+1])); @@ -660,7 +667,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Static Latency Time Stamp: Bucket %d", i); + snprintf(buf, sizeof(buf), + "Static Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); @@ -677,7 +685,8 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency struct json_object *bucket; bucket = json_create_object(); - sprintf(buf, "Static Measured Latency: Bucket %d", i); + snprintf(buf, sizeof(buf), "Static Measured Latency: Bucket %d", + i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le16_to_cpu(log_data->static_measured_latency[3-i][j])); @@ -689,16 +698,13 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency le16_to_cpu(log_data->static_latency_stamp_units)); if (log_page_version >= 0x4) { - strcpy(buf, "0x"); for (i = ARRAY_SIZE(log_data->latency_monitor_debug_log_size) - 1; i > 0 && (log_data->latency_monitor_debug_log_size[i] == 0); i--) ; - while (i >= 0) { - char hex_string[3]; - - sprintf(hex_string, "%02x", log_data->latency_monitor_debug_log_size[i--]); - strcat(buf, hex_string); - } + shr_strscpy(buf, "0x", sizeof(buf)); + shr_bin_to_hex(buf + 2, sizeof(buf) - 2, + log_data->latency_monitor_debug_log_size, i + 1, + SHR_HEX_REVERSE); json_object_add_value_string(root, "Debug Telemetry Log Size", buf); } @@ -721,10 +727,9 @@ static void json_c3_log(struct libnvme_transport_handle *hdl, struct ssd_latency json_object_add_value_uint(root, "Log Page Version", log_page_version); char guid[(GUID_LEN * 2) + 1]; - char *ptr = &guid[0]; - for (i = GUID_LEN - 1; i >= 0; i--) - ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]); + shr_bin_to_hex(guid, sizeof(guid), log_data->log_page_guid, GUID_LEN, + SHR_HEX_UPPER | SHR_HEX_REVERSE); json_object_add_value_string(root, "Log Page GUID", guid); @@ -740,7 +745,6 @@ static void json_c5_log(struct libnvme_transport_handle *hdl, struct unsupported struct json_object *root; char unsup_req_list_str[40]; char guid_buf[(GUID_LEN * 2) + 1]; - char *guid = guid_buf; root = json_create_object(); @@ -762,9 +766,8 @@ static void json_c5_log(struct libnvme_transport_handle *hdl, struct unsupported json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); - memset((void *)guid, 0, sizeof(guid_buf)); - for (j = GUID_LEN - 1; j >= 0; j--) - guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + shr_bin_to_hex(guid_buf, sizeof(guid_buf), log_data->log_page_guid, + GUID_LEN, SHR_HEX_REVERSE); json_object_add_value_string(root, "Log page GUID", guid_buf); json_print_object(root, NULL); @@ -804,9 +807,9 @@ static void json_c1_log(struct ocp_error_recovery_log_page *log_data) le16_to_cpu(log_data->log_page_version)); memset((void *)guid, 0, 64); - sprintf((char *)guid, "0x%016"PRIx64"%016"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%016"PRIx64"%016"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); json_print_object(root, NULL); @@ -849,9 +852,9 @@ static void json_c4_log(struct ocp_device_capabilities_log_page *log_data) json_object_add_value_int(root, "Log Page Version", log_page_version); memset((void *)guid, 0, 64); - sprintf((char *)guid, "0x%016"PRIx64"%016"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%016"PRIx64"%016"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); json_print_object(root, NULL); @@ -859,6 +862,21 @@ static void json_c4_log(struct ocp_device_capabilities_log_page *log_data) json_free_object(root); } +/* + * Append the @n bytes at @data to the string in @buf, each as a hex or + * decimal number without padding, as the reserved fields were printed. + */ +static void concat_bytes(char *buf, size_t size, const __u8 *data, int n, + bool hex) +{ + size_t len = strnlen(buf, size); + int i; + + for (i = 0; i < n; i++) + shr_snprintf_append(buf, size, &len, hex ? "%x" : "%d", + data[i]); +} + /* Array lengths need to be doubled + 2 to handle * conversion to null terminated strings */ @@ -872,11 +890,8 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat { struct json_object *root = json_create_object(); char res_arr[RESERVED_ARRAY_LEN]; - char *res = res_arr; char guid_buf[GUID_BUFFER_LEN]; - char *guid = guid_buf; char fifo_arr[FIFO_ARRAY_LEN]; - char *fifo = fifo_arr; char buf[128]; __u64 sits = le64_to_cpu(log_data->sits); __u64 sitsz = le64_to_cpu(log_data->sitsz); @@ -893,27 +908,22 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat struct json_object *errors = json_create_array(); __u64 entries = 0, i; __u8 *table; - int j; json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); - memset((__u8 *)res, 0, RESERVED_ARRAY_LEN); - for (j = 0; j < 15; j++) - res += sprintf(res, "%x", log_data->reserved1[j]); + res_arr[0] = '\0'; + concat_bytes(res_arr, sizeof(res_arr), log_data->reserved1, 15, true); json_object_add_value_string(root, "Reserved 1", res_arr); - memset((void *)guid, 0, GUID_BUFFER_LEN); - for (j = GUID_LEN - 1; j >= 0; j--) - guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + shr_bin_to_hex(guid_buf, sizeof(guid_buf), log_data->log_page_guid, + GUID_LEN, SHR_HEX_REVERSE); json_object_add_value_string(root, "Log page GUID", guid_buf); json_object_add_value_int(root, "Telemetry String Log Size", le64_to_cpu(log_data->sls)); - res = res_arr; - memset((__u8 *)res, 0, RESERVED_ARRAY_LEN); - for (j = 0; j < 24; j++) - res += sprintf(res, "%x", log_data->reserved2[j]); + res_arr[0] = '\0'; + concat_bytes(res_arr, sizeof(res_arr), log_data->reserved2, 24, true); json_object_add_value_string(root, "Reserved 2", res_arr); json_object_add_value_int(root, "Statistics Identifier String Table Start", @@ -931,105 +941,72 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat json_object_add_value_int(root, "ASCII Table Size", le64_to_cpu(log_data->asctsz)); - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo1[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo1, sizeof(log_data->fifo1)); json_object_add_value_string(root, "FIFO 1 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo2[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo2, sizeof(log_data->fifo2)); json_object_add_value_string(root, "FIFO 2 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo3[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo3, sizeof(log_data->fifo3)); json_object_add_value_string(root, "FIFO 3 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo4[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo4, sizeof(log_data->fifo4)); json_object_add_value_string(root, "FIFO 4 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo5[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo5, sizeof(log_data->fifo5)); json_object_add_value_string(root, "FIFO 5 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo6[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo6, sizeof(log_data->fifo6)); json_object_add_value_string(root, "FIFO 6 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo7[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo7, sizeof(log_data->fifo7)); json_object_add_value_string(root, "FIFO 7 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo8[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo8, sizeof(log_data->fifo8)); json_object_add_value_string(root, "FIFO 8 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo9[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo9, sizeof(log_data->fifo9)); json_object_add_value_string(root, "FIFO 9 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo10[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo10, sizeof(log_data->fifo10)); json_object_add_value_string(root, "FIFO 10 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo11[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo11, sizeof(log_data->fifo11)); json_object_add_value_string(root, "FIFO 11 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo12[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo12, sizeof(log_data->fifo12)); json_object_add_value_string(root, "FIFO 12 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo13[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo13, sizeof(log_data->fifo13)); json_object_add_value_string(root, "FIFO 13 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo14[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo14, sizeof(log_data->fifo14)); json_object_add_value_string(root, "FIFO 14 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo15[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo15, sizeof(log_data->fifo15)); json_object_add_value_string(root, "FIFO 15 ASCII String", fifo_arr); - fifo = fifo_arr; - memset((void *)fifo, 0, FIFO_ARRAY_LEN); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo16[j]); + memset(fifo_arr, 0, sizeof(fifo_arr)); + memcpy(fifo_arr, log_data->fifo16, sizeof(log_data->fifo16)); json_object_add_value_string(root, "FIFO 16 ASCII String", fifo_arr); - res = res_arr; - memset((__u8 *)res, 0, RESERVED_ARRAY_LEN); - for (j = 0; j < 48; j++) - res += sprintf(res, "%x", log_data->reserved3[j]); + res_arr[0] = '\0'; + concat_bytes(res_arr, sizeof(res_arr), log_data->reserved3, 48, true); json_object_add_value_string(root, "Reserved 3", res_arr); table = ocp_string_table(log_data_buf, total_log_page_size, sits, sitsz, @@ -1052,7 +1029,9 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat le64_to_cpu(stat_entry.ascii_id_ofst)); json_object_add_value_uint(entry, "Reserved2", le32_to_cpu(stat_entry.reserved2)); - sprintf(buf, "Statistics Identifier String Table %"PRIu64, (uint64_t)i); + snprintf(buf, sizeof(buf), + "Statistics Identifier String Table %"PRIu64, + (uint64_t)i); json_object_add_value_object(stat_table, buf, entry); } @@ -1081,7 +1060,9 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat le64_to_cpu(eve_entry.ascii_id_ofst)); json_object_add_value_int(entry, "Reserved", le32_to_cpu(eve_entry.reserved2)); - sprintf(buf, "Event Identifier String Table Entry %"PRIu64, (uint64_t)i); + snprintf(buf, sizeof(buf), + "Event Identifier String Table Entry %"PRIu64, + (uint64_t)i); json_object_add_value_object(eve_table, buf, entry); } json_object_add_value_object(root, "Event Identifier String Table Entry", @@ -1110,8 +1091,9 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat le64_to_cpu(vu_eve_entry.ascii_id_ofst)); json_object_add_value_int(entry, "Reserved", le32_to_cpu(vu_eve_entry.reserved)); - sprintf(buf, "VU Event Identifier String Table Entry %"PRIu64, - (uint64_t)i); + snprintf(buf, sizeof(buf), + "VU Event Identifier String Table Entry %llu", + (unsigned long long)i); json_object_add_value_object(vu_eve_table, buf, entry); } json_object_add_value_object(root, "VU Event Identifier String Table Entry", @@ -1139,25 +1121,21 @@ static void json_c9_log(struct telemetry_str_log_format *log_data, __u8 *log_dat static void json_c7_log(struct libnvme_transport_handle *hdl, struct tcg_configuration_log *log_data) { - int j; struct json_object *root; char guid_buf[(GUID_LEN * 2) + 1]; - char *guid = guid_buf; /* * rsvd38 holds 456 __u8 values printed as decimal (up to 3 digits * each), preceded on log_page_version == 1 by two more __u8 values * (up to 3 digits each) from no_of_ns_prov_locking_obj_ext, plus NUL. */ char res_arr[456 * 3 + 2 * 3 + 1]; - char *res = res_arr; __u16 log_page_version = le16_to_cpu(log_data->log_page_version); root = json_create_object(); json_object_add_value_int(root, "State", log_data->state); - memset((__u8 *)res, 0, sizeof(res_arr)); - for (j = 0; j < 3; j++) - res += sprintf(res, "%d", log_data->rsvd1[j]); + res_arr[0] = '\0'; + concat_bytes(res_arr, sizeof(res_arr), log_data->rsvd1, 3, false); json_object_add_value_string(root, "Reserved1", res_arr); json_object_add_value_int(root, "Locking SP Activation Count", log_data->locking_sp_act_count); @@ -1191,26 +1169,24 @@ static void json_c7_log(struct libnvme_transport_handle *hdl, struct tcg_configu le32_to_cpu(log_data->pro_rlc)); json_object_add_value_int(root, "TCG Error Count", le32_to_cpu(log_data->tcg_ec)); - res = res_arr; - memset((__u8 *)res, 0, sizeof(res_arr)); + res_arr[0] = '\0'; if (log_page_version == 1) { - res += sprintf(res, "%d%d", *(__u8 *)&log_data->no_of_ns_prov_locking_obj_ext, - *((__u8 *)&log_data->no_of_ns_prov_locking_obj_ext + 1)); + concat_bytes(res_arr, sizeof(res_arr), + (__u8 *)&log_data->no_of_ns_prov_locking_obj_ext, + 2, false); } else { json_object_add_value_int(root, "Number of Namespace Provisioned Locking Objects Extended", log_data->no_of_ns_prov_locking_obj_ext); } - for (j = 0; j < 456; j++) - res += sprintf(res, "%d", log_data->rsvd38[j]); + concat_bytes(res_arr, sizeof(res_arr), log_data->rsvd38, 456, false); json_object_add_value_string(root, "Reserved3", res_arr); json_object_add_value_int(root, "Log Page Version", log_page_version); - memset((void *)guid, 0, sizeof(guid_buf)); - for (j = GUID_LEN - 1; j >= 0; j--) - guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + shr_bin_to_hex(guid_buf, sizeof(guid_buf), log_data->log_page_guid, + GUID_LEN, SHR_HEX_REVERSE); json_object_add_value_string(root, "Log page GUID", guid_buf); json_print_object(root, NULL); diff --git a/plugins/ocp/ocp-print-stdout.c b/plugins/ocp/ocp-print-stdout.c index 2550a09abd..f6f33b4578 100644 --- a/plugins/ocp/ocp-print-stdout.c +++ b/plugins/ocp/ocp-print-stdout.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -325,10 +326,9 @@ static void stdout_c3_log(struct libnvme_transport_handle *hdl, struct ssd_laten printf(" Log Page Version %d\n", log_page_version); char guid[(GUID_LEN * 2) + 1]; - char *ptr = &guid[0]; - for (i = GUID_LEN - 1; i >= 0; i--) - ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]); + shr_bin_to_hex(guid, sizeof(guid), log_data->log_page_guid, GUID_LEN, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" Log Page GUID %s\n", guid); printf("\n"); diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index dcfc7b4e2a..7eb0c5df1c 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -11,6 +11,8 @@ #include +#include + #include "cleanup.h" #include "global-ctx.h" #include "nvme-print.h" @@ -32,10 +34,7 @@ static __u8 scao_guid[GUID_LEN] = { /* Render a log page GUID as GUID_LEN * 2 lower-case hex digits. */ static void format_guid(char str[GUID_LEN * 2 + 1], const __u8 guid[GUID_LEN]) { - int i; - - for (i = 0; i < GUID_LEN; i++) - sprintf(&str[i * 2], "%02x", guid[i]); + shr_bin_to_hex(str, GUID_LEN * 2 + 1, guid, GUID_LEN, 0); } static int get_c0_log_page(struct libnvme_transport_handle *hdl, char *format, diff --git a/plugins/ocp/ocp-telemetry-decode.c b/plugins/ocp/ocp-telemetry-decode.c index 532f59ea6f..f56604dd7e 100644 --- a/plugins/ocp/ocp-telemetry-decode.c +++ b/plugins/ocp/ocp-telemetry-decode.c @@ -7,6 +7,8 @@ #include #include +#include +#include #include "nvme-print.h" #include "ocp-telemetry-decode.h" @@ -436,7 +438,7 @@ void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, un { char data_str[70] = { 0 }; - sprintf(data_str, "0x%x", pdata); + snprintf(data_str, sizeof(data_str), "0x%x", pdata); json_object_add_value_string(pobject, msg, data_str); } @@ -444,19 +446,16 @@ void json_add_formatted_var_size_str(struct json_object *pobject, const char *ms unsigned int data_size) { char *description_str = NULL; - char temp_buffer[3] = { 0 }; + size_t size = data_size * 2 + 1; - /* Allocate 2 chars for each value in the data + 2 bytes for the null terminator */ - description_str = (char *) calloc(1, data_size*2 + 2); + /* 2 chars for each value in the data and the null terminator */ + description_str = (char *) calloc(1, size); if (!description_str) { nvme_show_error("Failed to allocate description buffer"); return; } - for (size_t i = 0; i < data_size; ++i) { - sprintf(temp_buffer, "%02X", pdata[i]); - strcat(description_str, temp_buffer); - } + shr_bin_to_hex(description_str, size, pdata, data_size, SHR_HEX_UPPER); json_object_add_value_string(pobject, msg, description_str); free(description_str); @@ -587,7 +586,9 @@ int get_statistic_id_ascii_string(int identifier, char *description) // If ASCII string isn't found, see in our internal Map // for 2.5 Spec defined strings if (identifier <= 0x6F) { - strcpy(description, statistic_identifiers_map[identifier].description); + shr_strscpy(description, + statistic_identifiers_map[identifier].description, + OCP_TELEMETRY_DESCRIPTION_MAX); return 0; } @@ -633,7 +634,7 @@ static int ocp_spec_event_id_string(int identifier, int debug_event_class, if (!str) return -1; - strcpy(description, str); + shr_strscpy(description, str, OCP_TELEMETRY_DESCRIPTION_MAX); return 0; } @@ -1337,7 +1338,8 @@ int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, else { char buffer[1024] = {0}; - sprintf(buffer, "%s%s\n%s", STR_LINE, event_fifo_name, STR_LINE); + snprintf(buffer, sizeof(buffer), "%s%s\n%s", STR_LINE, + event_fifo_name, STR_LINE); if (fp) fprintf(fp, "%s", buffer); else @@ -2095,7 +2097,8 @@ int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) } if (options->output_file != NULL) { - sprintf(file_path, "%s.%s", options->output_file, "txt"); + snprintf(file_path, sizeof(file_path), "%s.%s", + options->output_file, "txt"); fp = fopen(file_path, "w"); if (!fp) { nvme_show_error("Failed to open %s file.", file_path); @@ -2337,7 +2340,8 @@ int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) if (options->output_file != NULL) { const char *json_string = json_object_to_json_string(root); - sprintf(file_path, "%s.%s", options->output_file, "json"); + snprintf(file_path, sizeof(file_path), "%s.%s", + options->output_file, "json"); FILE *fp = fopen(file_path, "w"); if (fp) { From dd925144eb7d49993c5c5b1e7e1eb1d7eb3c6cc4 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:35:07 +0000 Subject: [PATCH 223/263] seagate: bound the string buffers Use snprintf() instead of sprintf(), and print the names of the 64 bit attributes directly instead of copying them into a small buffer first. The output is unchanged. Signed-off-by: Daniel Wagner --- plugins/seagate/seagate-nvme.c | 103 ++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 39 deletions(-) diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 64a5b6bc8c..ea21943ee0 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -311,6 +311,20 @@ static char *print_ext_smart_id(__u8 attrId) } } +/* A 128 bit counter as "0x" and 32 hex digits */ +static void u128_hex_str(char *buf, size_t size, const U128 *v) +{ + snprintf(buf, size, "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)le64_to_cpu(v->MS__u64), + (uint64_t)le64_to_cpu(v->LS__u64)); +} + +/* The name of a 64 bit attribute by its LSB, without the "LSB of " */ +static void print_64bit_name(__u8 lsb_id) +{ + printf("%-40s", print_ext_smart_id(lsb_id) + strlen("LSB of ")); +} + static __u64 smart_attribute_vs(__u16 verNo, SmartVendorSpecific attr) { __u64 val = 0; @@ -332,7 +346,6 @@ static void print_smart_log(__u16 verNo, SmartVendorSpecific attr, int lastAttr) static __u64 lsbGbErased = 0, msbGbErased = 0, lsbLifWrtToFlash = 0, msbLifWrtToFlash = 0, lsbLifWrtFrmHost = 0, msbLifWrtFrmHost = 0, lsbLifRdToHost = 0, msbLifRdToHost = 0, lsbTrimCnt = 0, msbTrimCnt = 0; char buf[40] = {0}; - char strBuf[35] = {0}; int hideAttr = 0; if (attr.AttributeNumber == VS_ATTR_ID_GB_ERASED_LSB) { @@ -392,44 +405,46 @@ static void print_smart_log(__u16 verNo, SmartVendorSpecific attr, int lastAttr) } if (lastAttr == 1) { - sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_GB_ERASED_LSB) + 7)); - printf("%-40s", strBuf); + print_64bit_name(VS_ATTR_ID_GB_ERASED_LSB); printf("%-15d", VS_ATTR_ID_GB_ERASED_MSB << 8 | VS_ATTR_ID_GB_ERASED_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbGbErased, (uint64_t)lsbGbErased); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbGbErased, (uint64_t)lsbGbErased); printf(" %s\n", buf); - sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB) + 7)); - printf("%-40s", strBuf); + print_64bit_name(VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB); printf("%-15d", VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtToFlash, (uint64_t)lsbLifWrtToFlash); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifWrtToFlash, + (uint64_t)lsbLifWrtToFlash); printf(" %s\n", buf); - sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB) + 7)); - printf("%-40s", strBuf); + print_64bit_name(VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB); printf("%-15d", VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtFrmHost, (uint64_t)lsbLifWrtFrmHost); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifWrtFrmHost, + (uint64_t)lsbLifWrtFrmHost); printf(" %s\n", buf); - sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB) + 7)); - printf("%-40s", strBuf); + print_64bit_name(VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB); printf("%-15d", VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost); printf(" %s\n", buf); - sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_TRIM_COUNT_LSB) + 7)); - printf("%-40s", strBuf); + print_64bit_name(VS_ATTR_ID_TRIM_COUNT_LSB); printf("%-15d", VS_ATTR_ID_TRIM_COUNT_MSB << 8 | VS_ATTR_ID_TRIM_COUNT_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt); printf(" %s\n", buf); } @@ -494,7 +509,8 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_GB_ERASED_MSB << 8 | VS_ATTR_ID_GB_ERASED_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbGbErased, (uint64_t)lsbGbErased); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbGbErased, (uint64_t)lsbGbErased); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(lbafs, lbaf); @@ -505,7 +521,8 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtToFlash, (uint64_t)lsbLifWrtToFlash); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifWrtToFlash, (uint64_t)lsbLifWrtToFlash); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(lbafs, lbaf); @@ -516,7 +533,8 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtFrmHost, (uint64_t)lsbLifWrtFrmHost); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifWrtFrmHost, (uint64_t)lsbLifWrtFrmHost); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(lbafs, lbaf); @@ -527,7 +545,8 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(lbafs, lbaf); @@ -538,7 +557,8 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_TRIM_COUNT_MSB << 8 | VS_ATTR_ID_TRIM_COUNT_LSB); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(lbafs, lbaf); } @@ -605,16 +625,16 @@ static void json_print_smart_log_CF(struct json_object *root, vendor_log_page_CF lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "Data units read to DRAM namespace"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.MS__u64), - le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.LS__u64)); + u128_hex_str(buf, sizeof(buf), + &pLogPageCF->AttrCF.DataUnitsReadToDramNamespace); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "Data units written to DRAM namespace"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.MS__u64), - le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.LS__u64)); + u128_hex_str(buf, sizeof(buf), + &pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); @@ -737,8 +757,9 @@ static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_ json_object_add_value_string(lbaf, "attribute_name", "Physical Media Units Written"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.MS__u64), - le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.LS__u64)); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.MS__u64), + le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.LS__u64)); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); @@ -746,8 +767,9 @@ static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_ lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "Physical Media Units Read"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->phyMediaUnitsRd.MS__u64), - le64_to_cpu(pLogPageC0->phyMediaUnitsRd.LS__u64)); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(pLogPageC0->phyMediaUnitsRd.MS__u64), + le64_to_cpu(pLogPageC0->phyMediaUnitsRd.LS__u64)); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); @@ -804,10 +826,10 @@ static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_ lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "DSSD Specification Version"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "%d.%d.%d.%d", pLogPageC0->dssdSpecVerMajor, - le16_to_cpu(pLogPageC0->dssdSpecVerMinor), - le16_to_cpu(pLogPageC0->dssdSpecVerPoint), - pLogPageC0->dssdSpecVerErrata); + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", pLogPageC0->dssdSpecVerMajor, + le16_to_cpu(pLogPageC0->dssdSpecVerMinor), + le16_to_cpu(pLogPageC0->dssdSpecVerPoint), + pLogPageC0->dssdSpecVerErrata); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); @@ -854,16 +876,18 @@ static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_ lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "PLP Start Count"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->plpStartCnt.MS__u64), - le64_to_cpu(pLogPageC0->plpStartCnt.LS__u64)); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(pLogPageC0->plpStartCnt.MS__u64), + le64_to_cpu(pLogPageC0->plpStartCnt.LS__u64)); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "Endurance Estimate"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->enduranceEstimate.MS__u64), - le64_to_cpu(pLogPageC0->enduranceEstimate.LS__u64)); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(pLogPageC0->enduranceEstimate.MS__u64), + le64_to_cpu(pLogPageC0->enduranceEstimate.LS__u64)); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); @@ -885,8 +909,9 @@ static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_ lbaf = json_create_object(); json_object_add_value_string(lbaf, "attribute_name", "Log Page GUID"); memset(buf, 0, sizeof(buf)); - sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->logPageGUID.MS__u64), - le64_to_cpu(pLogPageC0->logPageGUID.LS__u64)); + snprintf(buf, sizeof(buf), "0x%016"PRIx64"%016"PRIx64"", + le64_to_cpu(pLogPageC0->logPageGUID.MS__u64), + le64_to_cpu(pLogPageC0->logPageGUID.LS__u64)); json_object_add_value_string(lbaf, "attribute_value", buf); json_array_add_value_object(logPages, lbaf); } From d2ece3cc9b84769a321ea4d116afd55710f51dc9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:37:14 +0000 Subject: [PATCH 224/263] micron: bound the string buffers Use snprintf() instead of sprintf() for the log page and drive info strings; micron_readFixedBytesFromBuffer() takes the size of the datastr of hyperscale_BSSD_nand_stats, which all its callers pass. Copy the fixed size identify fields and the file path prefix with memcpy() of their size instead of strncpy() with a number. The output is unchanged. Signed-off-by: Daniel Wagner --- plugins/micron/micron-nvme.c | 129 ++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 55 deletions(-) diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 74049300cb..874d5a21a1 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -361,7 +361,7 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, err = -1; goto exit_status; } - strncpy(fileLocation, strFilePath, length); + memcpy(fileLocation, strFilePath, length); fileLocation[length] = '\0'; while (fileLocation[k] != '\0') { @@ -833,7 +833,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, root = json_create_object(); logPages = json_create_array(); json_object_add_value_array(root, "Micron temperature information", logPages); - sprintf(tempstr, "%u C", temperature); + snprintf(tempstr, sizeof(tempstr), "%u C", temperature); json_object_add_value_string(stats, "Current Composite Temperature", tempstr); for (i = 0; i < SensorCount; i++) { char sensor_str[256] = { 0 }; @@ -841,8 +841,10 @@ static int micron_temp_stats(int argc, char **argv, struct command *acmd, if (!smart_log.temp_sensor[i]) continue; - sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); - sprintf(datastr, "%u C", tempSensors[i]); + snprintf(sensor_str, sizeof(sensor_str), + "Temperature Sensor #%d", (i + 1)); + snprintf(datastr, sizeof(datastr), "%u C", + tempSensors[i]); json_object_add_value_string(stats, sensor_str, datastr); } json_array_add_value_object(logPages, stats); @@ -1100,28 +1102,36 @@ static void init_d0_log_page(__u8 *buf, __u8 nsze) count = ((__u64)logD0[45] << 32) | logD0[44]; - sprintf(d0_log_page[0].datastr, "0x%"PRIx64, le64_to_cpu(count)); + snprintf(d0_log_page[0].datastr, sizeof(d0_log_page[0].datastr), + "0x%"PRIx64, le64_to_cpu(count)); count_hi = ((__u64)logD0[39] << 32) | logD0[38]; count_lo = ((__u64)logD0[37] << 32) | logD0[36]; if (count_hi) - sprintf(d0_log_page[1].datastr, "0x%"PRIx64"%016"PRIx64, - le64_to_cpu(count_hi), le64_to_cpu(count_lo)); + snprintf(d0_log_page[1].datastr, sizeof(d0_log_page[1].datastr), + "0x%"PRIx64"%016"PRIx64, le64_to_cpu(count_hi), + le64_to_cpu(count_lo)); else - sprintf(d0_log_page[1].datastr, "0x%"PRIx64, le64_to_cpu(count_lo)); + snprintf(d0_log_page[1].datastr, sizeof(d0_log_page[1].datastr), + "0x%"PRIx64, le64_to_cpu(count_lo)); count = ((__u64)logD0[25] << 32) | logD0[24]; - sprintf(d0_log_page[2].datastr, "0x%"PRIx64, le64_to_cpu(count)); + snprintf(d0_log_page[2].datastr, sizeof(d0_log_page[2].datastr), + "0x%"PRIx64, le64_to_cpu(count)); - sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]); + snprintf(d0_log_page[3].datastr, sizeof(d0_log_page[3].datastr), "0x%x", + logD0[3]); count_lo = ((__u64)logD0[37] << 32) | logD0[36]; count = ((__u64)logD0[25] << 32) | logD0[24]; count = (__u64)logD0[3] - (count_lo + count); - sprintf(d0_log_page[4].datastr, "0x%"PRIx64, le64_to_cpu(count)); + snprintf(d0_log_page[4].datastr, sizeof(d0_log_page[4].datastr), + "0x%"PRIx64, le64_to_cpu(count)); - sprintf(d0_log_page[5].datastr, "0x%x", nsze); - sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); + snprintf(d0_log_page[5].datastr, sizeof(d0_log_page[5].datastr), "0x%x", + nsze); + snprintf(d0_log_page[6].datastr, sizeof(d0_log_page[6].datastr), "0x%x", + logD0[1]); } static struct nand_stats { @@ -1152,8 +1162,10 @@ static struct nand_stats { {"Boot SSD Spec Version", {0}}, }; +/* @datastr is the datastr of an entry of hyperscale_BSSD_nand_stats */ static void micron_readFixedBytesFromBuffer(__u8 *buf, int offset, int numBytes, char *datastr) { + size_t size = sizeof(hyperscale_BSSD_nand_stats[0].datastr); uint64_t count_lo, count_hi, count; switch (numBytes) { @@ -1162,30 +1174,30 @@ static void micron_readFixedBytesFromBuffer(__u8 *buf, int offset, int numBytes, count_lo = shr_get_unaligned_le64(&buf[offset]); count_hi = shr_get_unaligned_le64(&buf[offset + 8]); if (count_hi) - sprintf(datastr, "0x%"PRIx64"%016"PRIx64"", - count_hi, count_lo); + snprintf(datastr, size, "0x%"PRIx64"%016"PRIx64"", + count_hi, count_lo); else - sprintf(datastr, "0x%"PRIx64"", count_lo); + snprintf(datastr, size, "0x%"PRIx64"", count_lo); } break; case 8: { count = shr_get_unaligned_le64(&buf[offset]); - sprintf(datastr, "0x%"PRIx64"", count); + snprintf(datastr, size, "0x%"PRIx64"", count); } break; case 6: { count = shr_get_unaligned_le16(&buf[offset + 4]); count = (count << 32) | shr_get_unaligned_le32(&buf[offset]); - sprintf(datastr, "0x%"PRIx64"", count); + snprintf(datastr, size, "0x%"PRIx64"", count); } break; case 2: { - sprintf(datastr, "0x%04x", - shr_get_unaligned_le16(&buf[offset])); + snprintf(datastr, size, "0x%04x", + shr_get_unaligned_le16(&buf[offset])); } break; } @@ -1222,12 +1234,10 @@ static void init_hyperscale_BSSD_nand_stats(__u8 *buf) minorVer = *((__u16 *)(&buf[302])); pointVer = *((__u16 *)(&buf[304])); errataVer = *((__u16 *)(&buf[306])); - sprintf(hyperscale_BSSD_nand_stats[21].datastr, - "%x.%x.%x.%x", - le16_to_cpu(majorVer), - le16_to_cpu(minorVer), - le16_to_cpu(pointVer), - le16_to_cpu(errataVer)); + snprintf(hyperscale_BSSD_nand_stats[21].datastr, + sizeof(hyperscale_BSSD_nand_stats[21].datastr), "%x.%x.%x.%x", + le16_to_cpu(majorVer), le16_to_cpu(minorVer), + le16_to_cpu(pointVer), le16_to_cpu(errataVer)); } @@ -1756,19 +1766,23 @@ static void print_log(__u8 *buf, bool is_json, unsigned char ucLogID) if (ucLogID == 0xE1) { log_pageID = e1_log_page; field_count = ARRAY_SIZE(e1_log_page); - sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID); + snprintf(tempStr, sizeof(tempStr), "SMART Extended Log:0x%X", + ucLogID); } else if (ucLogID == 0xD0) { log_pageID = D0_log_page; field_count = ARRAY_SIZE(D0_log_page); - sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID); + snprintf(tempStr, sizeof(tempStr), "SMART Extended Log:0x%X", + ucLogID); } else if (ucLogID == 0xC5) { log_pageID = C5_log_page; field_count = ARRAY_SIZE(C5_log_page); - sprintf(tempStr, "Micron Workload Log:0x%X", ucLogID); + snprintf(tempStr, sizeof(tempStr), "Micron Workload Log:0x%X", + ucLogID); } else if (ucLogID == 0xC6) { log_pageID = C6_log_page; field_count = ARRAY_SIZE(C6_log_page); - sprintf(tempStr, "Vendor Telemetry Log:0x%X", ucLogID); + snprintf(tempStr, sizeof(tempStr), "Vendor Telemetry Log:0x%X", + ucLogID); } else { log_pageID = NULL; } @@ -2217,9 +2231,9 @@ static void GetDriveInfo(const char *strOSDirName, int nFD, FILE *fpOutFile = NULL; __cleanup_free char *tempFile = NULL; char strBuffer[1024] = { 0 }; - char model[41] = { 0 }; - char serial[21] = { 0 }; - char fwrev[9] = { 0 }; + char model[sizeof(ctrlp->mn) + 1] = { 0 }; + char serial[sizeof(ctrlp->sn) + 1] = { 0 }; + char fwrev[sizeof(ctrlp->fr) + 1] = { 0 }; __cleanup_free char *strPDir = NULL; char *strDest = NULL; @@ -2241,9 +2255,9 @@ static void GetDriveInfo(const char *strOSDirName, int nFD, return; } - strncpy(model, ctrlp->mn, 40); - strncpy(serial, ctrlp->sn, 20); - strncpy(fwrev, ctrlp->fr, 8); + memcpy(model, ctrlp->mn, sizeof(ctrlp->mn)); + memcpy(serial, ctrlp->sn, sizeof(ctrlp->sn)); + memcpy(fwrev, ctrlp->fr, sizeof(ctrlp->fr)); snprintf(strBuffer, sizeof(strBuffer), "********************\nDrive Info\n********************\n"); @@ -2686,42 +2700,45 @@ static int micron_drive_info(int argc, char **argv, struct command *acmd, driveInfo = json_create_array(); json_object_add_value_array(root, "Micron Drive HW Information", driveInfo); - sprintf(tempstr, "%hhu.%hhu", dinfo.hw_ver_major, dinfo.hw_ver_minor); + snprintf(tempstr, sizeof(tempstr), "%hhu.%hhu", + dinfo.hw_ver_major, dinfo.hw_ver_minor); json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr); if (custId == MICRON_CUST_ID_GG) { if (dinfo.ftl_unit_size) { - sprintf(tempstr, "%u B", - (unsigned int)(dinfo.ftl_unit_size * 1024)); + snprintf(tempstr, sizeof(tempstr), "%u B", + dinfo.ftl_unit_size * 1024U); json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); } if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { - sprintf(tempstr, - "HyperScale Boot Version Spec.%hhu.%hhu" - , (unsigned char)dinfo.bs_ver_major, - (unsigned char)dinfo.bs_ver_minor); + snprintf(tempstr, sizeof(tempstr), + "HyperScale Boot Version Spec.%hhu.%hhu", + (unsigned char)dinfo.bs_ver_major, + (unsigned char)dinfo.bs_ver_minor); json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); } if (dinfo.ownership_status == 0) - sprintf(tempstr, "N/A"); + snprintf(tempstr, sizeof(tempstr), "N/A"); else if (dinfo.ownership_status == 1) - sprintf(tempstr, "UNSET"); + snprintf(tempstr, sizeof(tempstr), "UNSET"); else if (dinfo.ownership_status == 2) - sprintf(tempstr, "SET"); + snprintf(tempstr, sizeof(tempstr), "SET"); else if (dinfo.ownership_status == 3) - sprintf(tempstr, "BLOCKED"); + snprintf(tempstr, sizeof(tempstr), "BLOCKED"); json_object_add_value_string(pinfo, "Drive Ownership Status", tempstr); } else { if (dinfo.ftl_unit_size) { - sprintf(tempstr, "%hhu KB", dinfo.ftl_unit_size); + snprintf(tempstr, sizeof(tempstr), "%hhu KB", + dinfo.ftl_unit_size); json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); } if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { - sprintf(tempstr, "%hhu.%hhu", (unsigned char)dinfo.bs_ver_major, - (unsigned char)dinfo.bs_ver_minor); + snprintf(tempstr, sizeof(tempstr), "%hhu.%hhu", + (unsigned char)dinfo.bs_ver_major, + (unsigned char)dinfo.bs_ver_minor); json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); } } @@ -3323,11 +3340,13 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *acmd char start[32] = { 0 }; char end[32] = { 0 }; - sprintf(start, "%u%s", thresholds[b].start, thresholds[b].unit); + snprintf(start, sizeof(start), "%u%s", thresholds[b].start, + thresholds[b].unit); if (thresholds[b].end == INT_MAX) - sprintf(end, "INF"); + snprintf(end, sizeof(end), "INF"); else - sprintf(end, "%u%s", thresholds[b].end, thresholds[b].unit); + snprintf(end, sizeof(end), "%u%s", thresholds[b].end, + thresholds[b].unit); printf("%2d %8s %8s %8"PRIu64"\n", bucket, start, end, cmd_stats[b]); } return err; @@ -3837,7 +3856,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, printf("Preparing log package. This will take a few seconds...\n"); - strncpy(safe_sn, ctrl.sn, sizeof(safe_sn) - 1); + memcpy(safe_sn, ctrl.sn, sizeof(ctrl.sn)); shr_sanitize_name(shr_rtrim(safe_sn)); err = SetupDebugDataDirectories(safe_sn, cfg.package, strMainDirName, sizeof(strMainDirName), @@ -4441,7 +4460,7 @@ static void print_micron_health_log_json(struct nvme_smart_log *smart, char key[32]; if (ts) { - sprintf(key, "temp_sensor_%d", i + 1); + snprintf(key, sizeof(key), "temp_sensor_%d", i + 1); json_object_add_value_int(root, key, ts - 273); } } From 1ba43701fccd5f9ad71309c2836e1ebf01721352 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:45:05 +0000 Subject: [PATCH 225/263] wdc: bound the string buffers Use snprintf(), shr_strscpy() and shr_strscat() instead of sprintf(), strcpy(), strncpy() and strcat(), and pass wdc_get_commit_action_bin() the size of the buffer. The 128-bit GUIDs are read with shr_get_unaligned_le64() instead of an unaligned load. stringify_log_page_guid() printed each byte of the C0 log page GUID with "%x", which dropped the leading zero of the bytes below 0x10, so the printed GUID was too short and ambiguous. It prints two digits per byte with shr_bin_to_hex() now. The other output is unchanged. Signed-off-by: Daniel Wagner --- plugins/wdc/wdc-nvme.c | 333 ++++++++++++++++++++++++----------------- 1 file changed, 197 insertions(+), 136 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 7844a49aaa..1a5b0f7c2f 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -41,11 +41,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "global-ctx.h" #include "nvme-cmds.h" @@ -2185,7 +2187,7 @@ static int wdc_get_serial_name(struct libnvme_transport_handle *hdl, char *file, struct nvme_id_ctrl ctrl; char sn[sizeof(ctrl.sn) + 1] = {0}; - strncpy(orig, file, PATH_MAX - 1); + shr_strscpy(orig, file, sizeof(orig)); memset(file, 0, len); memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); nvme_init_identify_ctrl(&cmd, &ctrl); @@ -3903,7 +3905,7 @@ static int wdc_cap_diag(int argc, char **argv, struct command *acmd, return ret; if (cfg.file) - strncpy(f, cfg.file, PATH_MAX - 1); + shr_strscpy(f, cfg.file, sizeof(f)); if (cfg.xfer_size) xfer_size = cfg.xfer_size; ret = wdc_get_serial_name(hdl, f, PATH_MAX, "cap_diag"); @@ -3916,7 +3918,7 @@ static int wdc_cap_diag(int argc, char **argv, struct command *acmd, nvme_show_error("ERROR: WDC: file name overflow"); return -1; } - strcat(f, ".bin"); + shr_strscat(f, ".bin", sizeof(f)); } capabilities = wdc_get_drive_capabilities(ctx, hdl); @@ -4313,7 +4315,7 @@ static int dump_internal_logs(struct libnvme_transport_handle *hdl, const char * } memset(hdr, 0, bs); - sprintf(file_path, "%s/telemetry.bin", dir_name); + snprintf(file_path, sizeof(file_path), "%s/telemetry.bin", dir_name); output = shr_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { nvme_show_error("Failed to open output file %s: %s!", file_path, libnvme_strerror(errno)); @@ -4492,7 +4494,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, goto out; } close(verify_file); - strncpy(f, cfg.file, PATH_MAX - 1); + shr_strscpy(f, cfg.file, sizeof(f)); } else { wdc_UtilsGetTime(&timeInfo); memset(timeStamp, 0, sizeof(timeStamp)); @@ -4516,7 +4518,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, ret = -1; goto out; } - strcat(f, ".bin"); + shr_strscat(f, ".bin", sizeof(f)); } nvme_show_error("%s: filename = %s", __func__, f); @@ -4535,7 +4537,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, telemetry_data_area = cfg.data_area; } else { if (cfg.file) { - strncpy(fb, cfg.file, PATH_MAX/2 - 8); + shr_strscpy(fb, cfg.file, sizeof(fb)); } else { wdc_UtilsGetTime(&timeInfo); memset(timeStamp, 0, sizeof(timeStamp)); @@ -4743,7 +4745,7 @@ static int wdc_crash_dump(struct libnvme_transport_handle *hdl, const char *file int ret; if (file) - strncpy(f, file, PATH_MAX - 1); + shr_strscpy(f, file, sizeof(f)); if (type == WDC_NVME_PFAIL_DUMP_TYPE) dump_type = "_pfail_dump"; @@ -4834,7 +4836,7 @@ static int wdc_drive_log(int argc, char **argv, struct command *acmd, ret = -1; } else { if (cfg.file) - strncpy(f, cfg.file, PATH_MAX - 1); + shr_strscpy(f, cfg.file, sizeof(f)); ret = wdc_get_serial_name(hdl, f, PATH_MAX, "drive_log"); if (ret) nvme_show_error("ERROR: WDC: failed to generate file name"); @@ -5340,44 +5342,56 @@ static void wdc_print_latency_monitor_log_json(struct wdc_ssd_latency_monitor_lo char guid[40]; memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); for (i = 0; i <= 3; i++) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Active Bucket Counter: Bucket %d %s", i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Active Bucket Counter: Bucket %d %s", i, + operation[2-j]); json_object_add_value_int(root, buf, le32_to_cpu(log_data->active_bucket_counter[i][j+1])); } } for (i = 3; i >= 0; i--) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Active Measured Latency: Bucket %d %s", 3-i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Active Measured Latency: Bucket %d %s", 3-i, + operation[2-j]); json_object_add_value_int(root, buf, le16_to_cpu(log_data->active_measured_latency[i][j])); } } for (i = 3; i >= 0; i--) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Active Latency Time Stamp: Bucket %d %s", 3-i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Active Latency Time Stamp: Bucket %d %s", 3-i, + operation[2-j]); json_object_add_value_int(root, buf, le64_to_cpu(log_data->active_latency_timestamp[i][j])); } } for (i = 0; i <= 3; i++) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Static Bucket Counter: Bucket %d %s", i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Static Bucket Counter: Bucket %d %s", i, + operation[2-j]); json_object_add_value_int(root, buf, le32_to_cpu(log_data->static_bucket_counter[i][j+1])); } } for (i = 3; i >= 0; i--) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Static Measured Latency: Bucket %d %s", 3-i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Static Measured Latency: Bucket %d %s", 3-i, + operation[2-j]); json_object_add_value_int(root, buf, le16_to_cpu(log_data->static_measured_latency[i][j])); } } for (i = 3; i >= 0; i--) { for (j = 2; j >= 0; j--) { - sprintf(buf, "Static Latency Time Stamp: Bucket %d %s", 3-i, operation[2-j]); + snprintf(buf, sizeof(buf), + "Static Latency Time Stamp: Bucket %d %s", 3-i, + operation[2-j]); json_object_add_value_int(root, buf, le64_to_cpu(log_data->static_latency_timestamp[i][j])); } } @@ -5443,7 +5457,8 @@ static void wdc_print_error_rec_log_json(struct wdc_ocp_c1_error_recovery_log *l if (le16_to_cpu(log_data->log_page_version) >= 3) { json_object_add_value_int(root, "Panic Count", log_data->panic_count); for (j = 0; j < 4; j++) { - sprintf(buf, "Previous Panic ID N-%d", j+1); + snprintf(buf, sizeof(buf), "Previous Panic ID N-%d", + j+1); json_object_add_value_int(root, buf, le64_to_cpu(log_data->prev_panic_ids[j])); } @@ -5454,9 +5469,9 @@ static void wdc_print_error_rec_log_json(struct wdc_ocp_c1_error_recovery_log *l char guid[40]; memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); json_print_object(root, NULL); @@ -5508,7 +5523,8 @@ static void wdc_print_dev_cap_log_json(struct wdc_ocp_C4_dev_cap_log *log_data) memset((void *)dssd_descr_str, 0, 40); for (j = 0; j < WDC_OCP_C4_NUM_PS_DESCR; j++) { - sprintf((char *)dssd_descr_str, "DSSD Power State %d Descriptor", j); + snprintf((char *)dssd_descr_str, sizeof(dssd_descr_str), + "DSSD Power State %d Descriptor", j); json_object_add_value_int(root, dssd_descr_str, log_data->dssd_ps_descr[j]); } @@ -5516,9 +5532,9 @@ static void wdc_print_dev_cap_log_json(struct wdc_ocp_C4_dev_cap_log *log_data) char guid[40]; memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); json_print_object(root, NULL); @@ -5558,7 +5574,8 @@ static void wdc_print_unsupported_reqs_log_json(struct wdc_ocp_C5_unsupported_re memset((void *)unsup_req_list_str, 0, 41); for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) { - sprintf((char *)unsup_req_list_str, "Unsupported Requirement List %d", j); + snprintf((char *)unsup_req_list_str, sizeof(unsup_req_list_str), + "Unsupported Requirement List %d", j); json_object_add_value_string(root, unsup_req_list_str, (char *)log_data->unsupported_req_list[j]); } @@ -5567,9 +5584,9 @@ static void wdc_print_unsupported_reqs_log_json(struct wdc_ocp_C5_unsupported_re char guid[40]; memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&log_data->log_page_guid[8]), + shr_get_unaligned_le64(&log_data->log_page_guid[0])); json_object_add_value_string(root, "Log page GUID", guid); json_print_object(root, NULL); @@ -6024,36 +6041,37 @@ static void wdc_print_d0_log_json(struct wdc_ssd_d0_smart_log *perf) json_free_object(root); } -static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin) +static void wdc_get_commit_action_bin(__u8 commit_action_type, + char *action_bin, size_t size) { switch (commit_action_type) { case 0: - strcpy(action_bin, "000b"); + shr_strscpy(action_bin, "000b", size); break; case 1: - strcpy(action_bin, "001b"); + shr_strscpy(action_bin, "001b", size); break; case 2: - strcpy(action_bin, "010b"); + shr_strscpy(action_bin, "010b", size); break; case 3: - strcpy(action_bin, "011b"); + shr_strscpy(action_bin, "011b", size); break; case 4: - strcpy(action_bin, "100b"); + shr_strscpy(action_bin, "100b", size); break; case 5: - strcpy(action_bin, "101b"); + shr_strscpy(action_bin, "101b", size); break; case 6: - strcpy(action_bin, "110b"); + shr_strscpy(action_bin, "110b", size); break; case 7: - strcpy(action_bin, "111b"); + shr_strscpy(action_bin, "111b", size); break; default: - strcpy(action_bin, "INVALID"); + shr_strscpy(action_bin, "INVALID", size); } } @@ -6126,10 +6144,11 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, if (cust_id == WDC_CUSTOMER_ID_0x1005) { printf(" "); memset((void *)time_str, 0, 9); - sprintf((char *)time_str, "%"PRIu32":%u:%u", - (__u32)(timestamp_sec/3600), - (__u8)(timestamp_sec%3600/60), - (__u8)(timestamp_sec%60)); + snprintf((char *)time_str, sizeof(time_str), + "%"PRIu32":%u:%u", + (__u32)(timestamp_sec/3600), + (__u8)(timestamp_sec%3600/60), + (__u8)(timestamp_sec%60)); printf("%s", time_str); printf(" "); @@ -6137,10 +6156,11 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, printf(" "); memset((void *)time_str, 0, 9); - sprintf((char *)time_str, "%"PRIu32":%u:%u", - (__u32)((timestamp_sec/3600)%24), - (__u8)((timestamp_sec/60)%60), - (__u8)(timestamp_sec%60)); + snprintf((char *)time_str, sizeof(time_str), + "%"PRIu32":%u:%u", + (__u32)((timestamp_sec/3600)%24), + (__u8)((timestamp_sec/60)%60), + (__u8)(timestamp_sec%60)); printf("%s", time_str); printf(" "); } else { @@ -6159,7 +6179,7 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, printf(" "); wdc_get_commit_action_bin( fw_act_history_entry->entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, sizeof(commit_action_bin)); printf(" %s", (char *)commit_action_bin); printf(" "); if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) @@ -6222,7 +6242,8 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, printf("%2"PRIu8"", (uint8_t)fw_act_history_entry[entryIdx].slot_number); printf(" "); wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, + sizeof(commit_action_bin)); printf(" %s", (char *)commit_action_bin); printf(" "); if (!le16_to_cpu(fw_act_history_entry[entryIdx].result)) @@ -6284,6 +6305,9 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, entryIdx = oldestEntryIdx; for (i = 0; i < num_entries; i++) { + struct wdc_fw_act_history_log_entry_c2 *e = + &fw_act_history_entry->entry[entryIdx]; + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); @@ -6302,18 +6326,20 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, fw_act_history_entry->entry[entryIdx].timestamp)); timestamp_sec = timestamp / 1000; if (cust_id == WDC_CUSTOMER_ID_0x1005) { - sprintf((char *)time_str, "%"PRIu32":%u:%u", - (__u32)(timestamp_sec/3600), - (__u8)(timestamp_sec%3600/60), - (__u8)(timestamp_sec%60)); + snprintf((char *)time_str, sizeof(time_str), + "%"PRIu32":%u:%u", + (__u32)(timestamp_sec/3600), + (__u8)(timestamp_sec%3600/60), + (__u8)(timestamp_sec%60)); json_object_add_value_string(root, "Power on Hour", time_str); } else if (vendor_id == WDC_NVME_SNDK_VID) { - sprintf((char *)time_str, "%"PRIu32":%u:%u", - (__u32)((timestamp_sec/3600)%24), - (__u8)((timestamp_sec/60)%60), - (__u8)(timestamp_sec%60)); + snprintf((char *)time_str, sizeof(time_str), + "%"PRIu32":%u:%u", + (__u32)((timestamp_sec/3600)%24), + (__u8)((timestamp_sec/60)%60), + (__u8)(timestamp_sec%60)); json_object_add_value_string(root, "Power on Hour", time_str); } else { json_object_add_value_uint64(root, "Timestamp", timestamp); @@ -6330,13 +6356,15 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, wdc_get_commit_action_bin( fw_act_history_entry->entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, sizeof(commit_action_bin)); json_object_add_value_string(root, "Action", commit_action_bin); if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) { json_object_add_value_string(root, "Result", "pass"); } else { - sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->entry[entryIdx].result))); + snprintf((char *)fail_str, sizeof(fail_str), + "fail #%d", + (int)(le16_to_cpu(e->result))); json_object_add_value_string(root, "Result", fail_str); } @@ -6366,6 +6394,10 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, entryIdx = oldestEntryIdx; for (i = 0; i < num_entries; i++) { + struct wdc_fw_act_history_log_entry *e = + &fw_act_history_entry[entryIdx]; + __u64 secs = le64_to_cpu(e->power_on_seconds); + memcpy(previous_fw, (char *)&(fw_act_history_entry[entryIdx].previous_fw_version), 8); if (strlen((char *)&(fw_act_history_entry[entryIdx].new_fw_version)) > 1) @@ -6377,9 +6409,10 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, json_object_add_value_int(root, "Entry", le32_to_cpu(fw_act_history_entry[entryIdx].entry_num)); - sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%60)); + snprintf((char *)time_str, sizeof(time_str), + "%04d:%02d:%02d", + (int)(secs / 3600), (int)((secs % 3600) / 60), + (int)(secs % 60)); json_object_add_value_string(root, "Power on Hour", time_str); json_object_add_value_int(root, "Power Cycle Count", @@ -6392,13 +6425,16 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, fw_act_history_entry[entryIdx].slot_number); wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, + sizeof(commit_action_bin)); json_object_add_value_string(root, "Action", commit_action_bin); if (!le16_to_cpu(fw_act_history_entry[entryIdx].result)) { json_object_add_value_string(root, "Result", "pass"); } else { - sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry[entryIdx].result))); + snprintf((char *)fail_str, sizeof(fail_str), + "fail #%d", + (int)(le16_to_cpu(e->result))); json_object_add_value_string(root, "Result", fail_str); } @@ -6825,97 +6861,117 @@ static void wdc_print_hw_rev_log_json(void *data) log_data->hw_rev_c9_mc); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[0])); json_object_add_value_string(root, "Device Manf Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[0])); json_object_add_value_string(root, "ASIC Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[0])); json_object_add_value_string(root, "PCB Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[0])); json_object_add_value_string(root, "DRAM Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[0])); json_object_add_value_string(root, "NAND Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[0])); json_object_add_value_string(root, "PMIC 1 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[0])); json_object_add_value_string(root, "PMIC 2 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[0])); json_object_add_value_string(root, "Component 1 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[0])); json_object_add_value_string(root, "Component 2 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[0])); json_object_add_value_string(root, "Component 3 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[0])); json_object_add_value_string(root, "Component 4 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[0])); json_object_add_value_string(root, "Component 5 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[0])); json_object_add_value_string(root, "Component 6 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[0])); json_object_add_value_string(root, "Component 7 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[0])); json_object_add_value_string(root, "Component 8 Detailed Info", json_data); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[0])); json_object_add_value_string(root, "Component 9 Detailed Info", json_data); memset((void *)json_data, 0, 80); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"%"PRIx64"%"PRIx64"", - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[0]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[16]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[24])); + snprintf((char *)json_data, sizeof(json_data), + "0x%"PRIx64"%"PRIx64"%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[0]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[16]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[24])); json_object_add_value_string(root, "Serial Number", json_data); json_object_add_value_uint(root, "Log Page Version", le16_to_cpu(log_data->hw_rev_version)); memset((void *)json_data, 0, 40); - sprintf((char *)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]), - le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0])); + snprintf((char *)json_data, sizeof(json_data), "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0])); json_object_add_value_string(root, "Log Page GUID", json_data); json_print_object(root, NULL); @@ -7152,14 +7208,14 @@ static void wdc_print_ext_smart_cloud_log_json(void *data, int mask) char vers_str[40]; memset((void *)vers_str, 0, 40); - sprintf((char *)vers_str, "%d.%d.%d.%d", - le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), - le16_to_cpu(ext_smart_log_ptr->ext_smart_min), - le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), - le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); + snprintf((char *)vers_str, sizeof(vers_str), "%d.%d.%d.%d", + le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), + le16_to_cpu(ext_smart_log_ptr->ext_smart_min), + le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), + le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); json_object_add_value_string(root, "cloud_boot_ssd_spec_ver", vers_str); memset((void *)vers_str, 0, 40); - sprintf((char *)vers_str, "%d.%d.%d.%d", 0, 0, 0, 0); + snprintf((char *)vers_str, sizeof(vers_str), "%d.%d.%d.%d", 0, 0, 0, 0); json_object_add_value_string(root, "cloud_boot_ssd_hw_ver", vers_str); if (mask == WDC_SCA_V1_ALL) { @@ -7170,11 +7226,12 @@ static void wdc_print_ext_smart_cloud_log_json(void *data, int mask) json_object_add_value_uint(root, "log_page_ver", le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv)); char guid[40]; + __u8 *lpg; - memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[8]), - le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[0])); + lpg = ext_smart_log_ptr->ext_smart_lpg; + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&lpg[8]), + shr_get_unaligned_le64(&lpg[0])); json_object_add_value_string(root, "log_page_guid", guid); } @@ -8164,16 +8221,11 @@ static long double le_to_float(__u8 *data, int byte_len) return result; } -static void stringify_log_page_guid(__u8 *guid, char *buf) +/* The GUID as "0x" and its bytes in hex, the most significant first */ +static void stringify_log_page_guid(__u8 *guid, char *buf, size_t size) { - char *ptr = buf; - int i; - - memset(buf, 0, sizeof(char) * (2 * 16 + 1)); - - ptr += sprintf(ptr, "0x"); - for (i = 0; i < 16; i++) - ptr += sprintf(ptr, "%x", guid[15 - i]); + shr_strscpy(buf, "0x", size); + shr_bin_to_hex(buf + 2, size - 2, guid, 16, SHR_HEX_REVERSE); } static const char *const cloud_smart_log_thermal_status[] = { @@ -8283,12 +8335,15 @@ static void wdc_show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) le16_to_cpu(log->capacitor_health)); if (smart_log_ver >= 3) { if (smart_log_ver >= 4) { - sprintf(buf, "%c", log->nvme_base_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_base_errata_ver); json_object_add_value_string(root, "nvme_base_errata_version", buf); - sprintf(buf, "%c", log->nvme_cmd_set_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_cmd_set_errata_ver); json_object_add_value_string(root, "nvme_cmd_set_errata_version", buf); } else { - sprintf(buf, "%c", log->nvme_base_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_base_errata_ver); json_object_add_value_string(root, "nvme_errata_version", buf); } } @@ -8319,7 +8374,7 @@ static void wdc_show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) } json_object_add_value_uint(root, "log_page_version", smart_log_ver); - stringify_log_page_guid(log->log_page_guid, buf); + stringify_log_page_guid(log->log_page_guid, buf, sizeof(buf)); json_object_add_value_string(root, "log_page_guid", buf); json_print_object(root, NULL); @@ -8423,7 +8478,7 @@ static void wdc_show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, } printf("Log Page Version : %" PRIu16 "\n", smart_log_ver); - stringify_log_page_guid(log->log_page_guid, buf); + stringify_log_page_guid(log->log_page_guid, buf, sizeof(buf)); printf("Log Page GUID : %s\n", buf); printf("\n\n"); } @@ -8943,8 +8998,10 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *acmd, (phys_media_units_written_slc/data_units_written)); } else if (fmt == JSON) { root = json_create_object(); - sprintf(tlc_waf_str, "%4.2Lf", (phys_media_units_written_tlc/data_units_written)); - sprintf(slc_waf_str, "%4.2Lf", (phys_media_units_written_slc/data_units_written)); + snprintf(tlc_waf_str, sizeof(tlc_waf_str), "%4.2Lf", + (phys_media_units_written_tlc/data_units_written)); + snprintf(slc_waf_str, sizeof(slc_waf_str), "%4.2Lf", + (phys_media_units_written_slc/data_units_written)); json_object_add_value_string(root, "Device Write Amplification Factor TLC", tlc_waf_str); json_object_add_value_string(root, "Device Write Amplification Factor SLC", slc_waf_str); @@ -10601,7 +10658,7 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *acmd, } if (cfg.dirName) { - strncpy(d, cfg.dirName, PATH_MAX - 1); + shr_strscpy(d, cfg.dirName, sizeof(d)); d_ptr = d; } else { d_ptr = NULL; @@ -10821,7 +10878,7 @@ static int wdc_reason_identifier(int argc, char **argv, return -1; } close(verify_file); - strncpy(f, cfg.file, PATH_MAX - 1); + shr_strscpy(f, cfg.file, sizeof(f)); } else { wdc_UtilsGetTime(&timeInfo); memset(timeStamp, 0, sizeof(timeStamp)); @@ -10841,7 +10898,7 @@ static int wdc_reason_identifier(int argc, char **argv, nvme_show_error("ERROR: WDC: file name overflow"); return -1; } - strcat(f, ".bin"); + shr_strscat(f, ".bin", sizeof(f)); } nvme_show_error("%s: filename = %s", __func__, f); @@ -12059,7 +12116,8 @@ static int wdc_vs_drive_info(int argc, char **argv, printf("Customer SN: %-.*s\n", (int)sizeof(ctrl.sn), &ctrl.sn[0]); } else if (fmt == JSON) { root = json_create_object(); - sprintf(rev_str, "%4.1f", (.1 * rev)); + snprintf(rev_str, sizeof(rev_str), + "%4.1f", (.1 * rev)); json_object_add_value_string(root, "Drive HW Revision", rev_str); json_object_add_value_int(root, "FTL Unit Size", le16_to_cpu(size)); @@ -12084,7 +12142,8 @@ static int wdc_vs_drive_info(int argc, char **argv, printf("Customer SN: %-.*s\n", 14, &ctrl.sn[0]); } else if (fmt == JSON) { root = json_create_object(); - sprintf(rev_str, "%c.%c", major_rev, minor_rev); + snprintf(rev_str, sizeof(rev_str), "%c.%c", + major_rev, minor_rev); json_object_add_value_string(root, "Drive HW Revison", rev_str); wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], 14); json_object_add_value_string(root, "Customer SN", formatter); @@ -12152,7 +12211,8 @@ static int wdc_vs_drive_info(int argc, char **argv, json_object_add_value_int(root, "Drive HW Revison", major_rev); json_object_add_value_int(root, "FTL Unit Size", ftl_unit_size); - sprintf(rev_str, "%d.%d", boot_spec_major, boot_spec_minor); + snprintf(rev_str, sizeof(rev_str), "%d.%d", + boot_spec_major, boot_spec_minor); json_object_add_value_string(root, "HyperScale Boot Version Spec", rev_str); json_object_add_value_int(root, "TCG Device Ownership Status", tcg_dev_ownership); @@ -12195,8 +12255,9 @@ static int wdc_vs_drive_info(int argc, char **argv, root = json_create_object(); memset((void *)buf, 0, 20); - sprintf(buf, "%" PRIu32 ".%" PRIu32, - hw_rev_major, hw_rev_minor); + snprintf(buf, sizeof(buf), + "%" PRIu32 ".%" PRIu32, + hw_rev_major, hw_rev_minor); json_object_add_value_string(root, "hw_revision", buf); From 575996d63d5e22aacb5a4102696fa99e28edd761 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:45:05 +0000 Subject: [PATCH 226/263] sandisk: bound the string buffers Use snprintf(), shr_strscpy() and shr_strscat() instead of sprintf(), strcpy(), strncpy() and strcat(), and pass sndk_get_commit_action_bin() the size of the buffer. The 128-bit GUID of the extended SMART log is read with shr_get_unaligned_le64() instead of an unaligned load. sndk_stringify_log_page_guid() printed each byte of the C0 log page GUID with "%x", which dropped the leading zero of the bytes below 0x10. It prints two digits per byte with shr_bin_to_hex() now. The other output is unchanged. Signed-off-by: Daniel Wagner --- plugins/sandisk/sandisk-nvme.c | 18 ++++---- plugins/sandisk/sandisk-utils.c | 73 +++++++++++++++++---------------- plugins/sandisk/sandisk-utils.h | 2 +- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index fcf722c97b..2d5d4c3b48 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "global-ctx.h" #include "nvme-cmds.h" @@ -475,7 +476,7 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, } close(verify_file); remove(cfg.file); - strncpy(f, cfg.file, PATH_MAX - 5); + shr_strscpy(f, cfg.file, sizeof(f)); } else { sndk_UtilsGetTime(&timeInfo); memset(timeStamp, 0, sizeof(timeStamp)); @@ -499,7 +500,7 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, ret = -1; goto out; } - strcat(f, ".bin"); + shr_strscat(f, ".bin", sizeof(f)); } nvme_show_error("%s: filename = %s", __func__, f); @@ -905,7 +906,7 @@ static void sndk_print_fw_act_history_log_normal(__u8 *data, int num_entries) printf(" "); sndk_get_commit_action_bin( fw_act_hist_log->entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, sizeof(commit_action_bin)); printf(" %s", (char *)commit_action_bin); printf(" "); if (!le16_to_cpu(fw_act_hist_log->entry[entryIdx].result)) @@ -970,6 +971,9 @@ static void sndk_print_fw_act_history_log_json(__u8 *data, int num_entries) entryIdx = oldestEntryIdx; for (i = 0; i < num_entries; i++) { + struct sndk_fw_act_history_log_entry_c2 *e = + &fw_act_hist_log->entry[entryIdx]; + memcpy(previous_fw, (char *)& (fw_act_hist_log->entry[entryIdx].previous_fw_version), @@ -1004,15 +1008,15 @@ static void sndk_print_fw_act_history_log_json(__u8 *data, int num_entries) sndk_get_commit_action_bin( fw_act_hist_log->entry[entryIdx].commit_action_type, - (char *)&commit_action_bin); + commit_action_bin, sizeof(commit_action_bin)); json_object_add_value_string(root, "Action", commit_action_bin); if (!le16_to_cpu(fw_act_hist_log->entry[entryIdx].result)) { json_object_add_value_string(root, "Result", "pass"); } else { - sprintf((char *)fail_str, "fail #%d", - (int)(le16_to_cpu( - fw_act_hist_log->entry[entryIdx].result))); + snprintf((char *)fail_str, sizeof(fail_str), + "fail #%d", + (int)(le16_to_cpu(e->result))); json_object_add_value_string(root, "Result", fail_str); } diff --git a/plugins/sandisk/sandisk-utils.c b/plugins/sandisk/sandisk-utils.c index 55d04d0efa..5cd705966b 100644 --- a/plugins/sandisk/sandisk-utils.c +++ b/plugins/sandisk/sandisk-utils.c @@ -16,7 +16,10 @@ #include #include +#include +#include #include +#include #include "nvme-cmds.h" #include "nvme-pci-ids.h" @@ -117,35 +120,36 @@ __u32 sndk_get_fw_cust_id(struct libnvme_global_ctx *ctx, return cust_id; } -void sndk_get_commit_action_bin(__u8 commit_action_type, char *action_bin) +void sndk_get_commit_action_bin(__u8 commit_action_type, char *action_bin, + size_t size) { switch (commit_action_type) { case 0: - strcpy(action_bin, "000b"); + shr_strscpy(action_bin, "000b", size); break; case 1: - strcpy(action_bin, "001b"); + shr_strscpy(action_bin, "001b", size); break; case 2: - strcpy(action_bin, "010b"); + shr_strscpy(action_bin, "010b", size); break; case 3: - strcpy(action_bin, "011b"); + shr_strscpy(action_bin, "011b", size); break; case 4: - strcpy(action_bin, "100b"); + shr_strscpy(action_bin, "100b", size); break; case 5: - strcpy(action_bin, "101b"); + shr_strscpy(action_bin, "101b", size); break; case 6: - strcpy(action_bin, "110b"); + shr_strscpy(action_bin, "110b", size); break; case 7: - strcpy(action_bin, "111b"); + shr_strscpy(action_bin, "111b", size); break; default: - strcpy(action_bin, "INVALID"); + shr_strscpy(action_bin, "INVALID", size); } } @@ -834,7 +838,7 @@ int sndk_get_serial_name(struct libnvme_transport_handle *hdl, char *file, int ctrl_sn_len = sizeof(ctrl.sn); i = sizeof(ctrl.sn) - 1; - strncpy(orig, file, PATH_MAX - 1); + shr_strscpy(orig, file, sizeof(orig)); memset(file, 0, len); memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); nvme_init_identify_ctrl(&cmd, &ctrl); @@ -934,16 +938,11 @@ static long double sndk_le_to_float(__u8 *data, int byte_len) return result; } -static void sndk_stringify_log_page_guid(__u8 *guid, char *buf) +/* The GUID as "0x" and its bytes in hex, the most significant first */ +static void sndk_stringify_log_page_guid(__u8 *guid, char *buf, size_t size) { - char *ptr = buf; - int i; - - memset(buf, 0, sizeof(char) * (2 * 16 + 1)); - - ptr += sprintf(ptr, "0x"); - for (i = 0; i < 16; i++) - ptr += sprintf(ptr, "%x", guid[15 - i]); + shr_strscpy(buf, "0x", size); + shr_bin_to_hex(buf + 2, size - 2, guid, 16, SHR_HEX_REVERSE); } static const char *const sndk_cloud_smart_log_thermal_status[] = { @@ -1053,12 +1052,15 @@ static void sndk_show_cloud_smart_log_json(struct sndk_ocp_cloud_smart_log *log) le16_to_cpu(log->capacitor_health)); if (smart_log_ver >= 3) { if (smart_log_ver >= 4) { - sprintf(buf, "%c", log->nvme_base_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_base_errata_ver); json_object_add_value_string(root, "nvme_base_errata_version", buf); - sprintf(buf, "%c", log->nvme_cmd_set_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_cmd_set_errata_ver); json_object_add_value_string(root, "nvme_cmd_set_errata_version", buf); } else { - sprintf(buf, "%c", log->nvme_base_errata_ver); + snprintf(buf, sizeof(buf), "%c", + log->nvme_base_errata_ver); json_object_add_value_string(root, "nvme_errata_version", buf); } } @@ -1090,7 +1092,7 @@ static void sndk_show_cloud_smart_log_json(struct sndk_ocp_cloud_smart_log *log) } json_object_add_value_uint(root, "log_page_version", smart_log_ver); - sndk_stringify_log_page_guid(log->log_page_guid, buf); + sndk_stringify_log_page_guid(log->log_page_guid, buf, sizeof(buf)); json_object_add_value_string(root, "log_page_guid", buf); json_print_object(root, NULL); @@ -1195,7 +1197,7 @@ static void sndk_show_cloud_smart_log_normal(struct sndk_ocp_cloud_smart_log *lo } printf("Log Page Version : %" PRIu16 "\n", smart_log_ver); - sndk_stringify_log_page_guid(log->log_page_guid, buf); + sndk_stringify_log_page_guid(log->log_page_guid, buf, sizeof(buf)); printf("Log Page GUID : %s\n", buf); printf("\n\n"); } @@ -1522,18 +1524,19 @@ static void sndk_print_ext_smart_cloud_log_json(void *data, int mask) } memset((void *)vers_str, 0, sizeof(vers_str)); - sprintf((char *)vers_str, "%d.%d.%d.%d", - le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), - le16_to_cpu(ext_smart_log_ptr->ext_smart_min), - le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), - le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); + snprintf((char *)vers_str, sizeof(vers_str), "%d.%d.%d.%d", + le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), + le16_to_cpu(ext_smart_log_ptr->ext_smart_min), + le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), + le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); json_object_add_value_string(root, "cloud_boot_ssd_spec_ver", vers_str); memset((void *)vers_str, 0, sizeof(vers_str)); - sprintf((char *)vers_str, "%d.%d.%d.%d", 0, 0, 0, 0); + snprintf((char *)vers_str, sizeof(vers_str), "%d.%d.%d.%d", 0, 0, 0, 0); json_object_add_value_string(root, "cloud_boot_ssd_hw_ver", vers_str); if (mask == SNDK_SCA_V1_ALL) { char guid[40]; + __u8 *lpg; json_object_add_value_uint(root, "ftl_unit_size", le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus)); @@ -1542,10 +1545,10 @@ static void sndk_print_ext_smart_cloud_log_json(void *data, int mask) json_object_add_value_uint(root, "log_page_ver", le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv)); - memset((void *)guid, 0, sizeof(guid)); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[8]), - le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[0])); + lpg = ext_smart_log_ptr->ext_smart_lpg; + snprintf((char *)guid, sizeof(guid), "0x%"PRIx64"%"PRIx64"", + shr_get_unaligned_le64(&lpg[8]), + shr_get_unaligned_le64(&lpg[0])); json_object_add_value_string(root, "log_page_guid", guid); } diff --git a/plugins/sandisk/sandisk-utils.h b/plugins/sandisk/sandisk-utils.h index 62b74e4c98..a4f8e39ced 100644 --- a/plugins/sandisk/sandisk-utils.h +++ b/plugins/sandisk/sandisk-utils.h @@ -531,7 +531,7 @@ __u32 sndk_get_fw_cust_id(struct libnvme_global_ctx *ctx, struct libnvme_transport_handle *hdl); void sndk_get_commit_action_bin(__u8 commit_action_type, - char *action_bin); + char *action_bin, size_t size); bool sndk_parse_dev_mng_log_entry(void *data, __u32 entry_id, From aa5c2e681c6b45fe8d619dd48d56e5bba62a8829 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:45:35 +0000 Subject: [PATCH 227/263] rpmb: bound the string buffers Use snprintf() instead of sprintf() for the file path and the request message. Signed-off-by: Daniel Wagner --- plugins/rpmb/rpmb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/rpmb/rpmb.c b/plugins/rpmb/rpmb.c index 084dd7f4fa..e86f29734a 100644 --- a/plugins/rpmb/rpmb.c +++ b/plugins/rpmb/rpmb.c @@ -95,9 +95,9 @@ static void write_file(unsigned char *data, size_t len, const char *dir, __cleanup_file FILE *fp = NULL; if (dir) - sprintf(temp_folder, "%s/%s", dir, file); + snprintf(temp_folder, sizeof(temp_folder), "%s/%s", dir, file); else - sprintf(temp_folder, "./%s", file); + snprintf(temp_folder, sizeof(temp_folder), "./%s", file); fp = fopen(temp_folder, "ab+"); if (fp) { @@ -298,7 +298,8 @@ rpmb_read_request(struct libnvme_transport_handle *hdl, struct rpmb_data_frame_t unsigned char msg[1024] = { 0 }; int error; - sprintf((char *)msg, "RPMB request 0x%04x to target 0x%x", req->type, req->target); + snprintf((char *)msg, sizeof(msg), "RPMB request 0x%04x to target 0x%x", + req->type, req->target); error = send_rpmb_req(hdl, req->target, req_size, req); if (error != 0) { From f06c42a6eca2a2a6c984c2a1e2132e044a4fd54d Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:46:10 +0000 Subject: [PATCH 228/263] ymtc: bound the sysfs path buffer Use snprintf() with the path formats as literals, which the compiler checks, instead of sprintf() with formats in variables. Signed-off-by: Daniel Wagner --- plugins/ymtc/ymtc-nvme.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index d90d155f69..d0907d0316 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -31,11 +31,6 @@ #include "src/cleanup.h" #include "ymtc-utils.h" - /* sysfs paths for vendor ID and device ID */ -static char *vendor_id_path1 = "/sys/class/nvme/nvme%d/device/vendor"; -static char *vendor_id_path2 = "/sys/class/misc/nvme%d/device/vendor"; -static char *device_id_path1 = "/sys/class/nvme/nvme%d/device/device"; -static char *device_id_path2 = "/sys/class/misc/nvme%d/device/device"; static unsigned short vendor_id; static unsigned short device_id; @@ -76,14 +71,18 @@ static enum ySSDModel GetSSDModel(int idx) enum ySSDModel model = UNKNOWN_SSD; char path[512]; - sprintf(path, vendor_id_path1, idx); + snprintf(path, sizeof(path), + "/sys/class/nvme/nvme%d/device/vendor", idx); if (ReadSysFile(path, &vendor_id) < 0) { - sprintf(path, vendor_id_path2, idx); + snprintf(path, sizeof(path), + "/sys/class/misc/nvme%d/device/vendor", idx); ReadSysFile(path, &vendor_id); } - sprintf(path, device_id_path1, idx); + snprintf(path, sizeof(path), + "/sys/class/nvme/nvme%d/device/device", idx); if (ReadSysFile(path, &device_id) < 0) { - sprintf(path, device_id_path2, idx); + snprintf(path, sizeof(path), + "/sys/class/misc/nvme%d/device/device", idx); ReadSysFile(path, &device_id); } if (vendor_id == YMTC_VENDOR_ID) { From d91ed811b6febd89c18b3b751a69457d9edd58c9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:46:50 +0000 Subject: [PATCH 229/263] memblaze: don't copy string literals into small buffers The latency histogram units are only ever string literals: point to them instead of copying them with strcpy() into three byte buffers. Copy the firmware version with shr_strscpy(). Signed-off-by: Daniel Wagner --- plugins/memblaze/memblaze-nvme.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 391c1a3ef7..bb492114ba 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "cleanup.h" #include "global-ctx.h" @@ -224,8 +225,7 @@ static void show_memblaze_smart_log_old(struct nvme_memblaze_smart_log *smart, char fw_ver_local[STR_VER_SIZE + 1]; struct nvme_memblaze_smart_log_item *item; - strncpy(fw_ver_local, fw_ver, STR_VER_SIZE); - *(fw_ver_local + STR_VER_SIZE) = '\0'; + shr_strscpy(fw_ver_local, fw_ver, sizeof(fw_ver_local)); printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); @@ -876,8 +876,9 @@ static int mb_selective_download(int argc, char **argv, struct command *acmd, st return err; } -static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, char *unit0, - char *unit1, unsigned int *pHistogram, int print) +static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, + const char *unit0, const char *unit1, unsigned int *pHistogram, + int print) { int len; char string[64], subString0[12], subString1[12]; @@ -900,7 +901,7 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) FILE *fdi = fopen(file, "w+"); int saved_errno = fdi ? 0 : errno; int i, index; - char unit[2][3]; + const char *unit[2]; unsigned int *revision = (unsigned int *)buf; if (!fdi) @@ -919,11 +920,11 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) fPRINT_PARAM1("Bucket Start End Value\n"); fPRINT_PARAM1("--------------------------------------------------\n"); index = 0; - strcpy(unit[0], "us"); - strcpy(unit[1], "us"); + unit[0] = "us"; + unit[1] = "us"; for (i = 0; i < 32; i++, index++) { if (i == 31) { - strcpy(unit[1], "ms"); + unit[1] = "ms"; ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); } else { @@ -932,15 +933,15 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) } } - strcpy(unit[0], "ms"); - strcpy(unit[1], "ms"); + unit[0] = "ms"; + unit[1] = "ms"; for (i = 1; i < 32; i++, index++) ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); for (i = 1; i < 32; i++, index++) { if (i == 31) { - strcpy(unit[1], "s"); + unit[1] = "s"; ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); } else { @@ -949,8 +950,8 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) } } - strcpy(unit[0], "s"); - strcpy(unit[1], "s"); + unit[0] = "s"; + unit[1] = "s"; for (i = 1; i < 4; i++, index++) ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); From 401b549d3e08b884543ec40de10686b10c1f7ab4 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:48:17 +0000 Subject: [PATCH 230/263] scaleflux: fix the device name and sysfs buffer overflows The status command copied the controller name with strcpy() into an 8 byte buffer, which a name like nvme10n1 already overflows. It read the PCIe AER files with read() into a 512 byte buffer without terminating it, so sscanf() could read past the end, and closed the AER file a second time when the fatal error count couldn't be parsed. Make the name buffer large enough and copy it with shr_strscpy(), read the AER files with shr_read_all(), which terminates the buffer, drop the second close(), and copy the other strings with shr_strscpy() instead of strcpy() and strncpy(). Signed-off-by: Daniel Wagner --- plugins/scaleflux/sfx-nvme.c | 46 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index e0dd61f877..a36f5e1b7c 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -1120,7 +1122,8 @@ static int nvme_parse_evtlog(void *pevent_log_info, __u32 log_len, char *output) shr_format_ts(fw_time, str_buffer + str_pos); str_pos = strlen(str_buffer); - strcpy(str_buffer + str_pos, "] event-log:\n"); + shr_strscpy(str_buffer + str_pos, "] event-log:\n", + sizeof(str_buffer) - str_pos); str_pos = strlen(str_buffer); snprintf(str_buffer + str_pos, 128, @@ -1550,7 +1553,8 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin int err, fd, len, sector_size; char pci_vid[7], pci_did[7], pci_ssvid[7], link_speed[20], link_width[5], link_string[40]; char path[512], numa_node[5], vendor[10], form_factor[15], temperature[10], io_speed[15]; - char chr_dev[8], serial_number[21], model_number[41], firmware_revision[9], pcie_status[9]; + char chr_dev[32], serial_number[21], model_number[41]; + char firmware_revision[9], pcie_status[9]; struct json_object *root, *dev_stats, *link_stats, *crit_stats; nvme_print_flags_t flags; __u64 get_feat_result; @@ -1594,9 +1598,10 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin else capacity = capacity / (1000 * 1000 * 1000); //B --> GB - memset(&chr_dev, 0, 8); - strcpy(chr_dev, libnvme_transport_handle_get_name(hdl)); - for (len = 2; len < 8; len++) { + memset(&chr_dev, 0, sizeof(chr_dev)); + shr_strscpy(chr_dev, libnvme_transport_handle_get_name(hdl), + sizeof(chr_dev)); + for (len = 2; len < sizeof(chr_dev); len++) { if (chr_dev[len] == 'n') chr_dev[len] = '\0'; } @@ -1633,9 +1638,9 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin close(fd); if (strncmp("0xcc53", pci_vid, 6) == 0) - strncpy(vendor, "ScaleFlux", 10); + shr_strscpy(vendor, "ScaleFlux", sizeof(vendor)); else if (strncmp("0x1dfd", pci_vid, 6) == 0) - strncpy(vendor, "DIGISTOR", 10); + shr_strscpy(vendor, "DIGISTOR", sizeof(vendor)); else { nvme_show_error("Please use on a ScaleFlux device"); return -1; @@ -1728,8 +1733,8 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin } if (pcie_cor_valid) { - len = read(fd, path, 512); - if (len < 1) { + /* a terminated string for sscanf() */ + if (shr_read_all(fd, path, sizeof(path)) || !path[0]) { nvme_show_perror("Could not read PCIe AER Correctable errors in /sys/"); pcie_cor_valid = false; } @@ -1752,8 +1757,8 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin } if (pcie_nonfatal_valid) { - len = read(fd, path, 512); - if (len < 1) { + /* a terminated string for sscanf() */ + if (shr_read_all(fd, path, sizeof(path)) || !path[0]) { nvme_show_perror("Could not read PCIe AER Non-Fatal errors in /sys/"); pcie_nonfatal_valid = false; } @@ -1775,8 +1780,8 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin } if (pcie_fatal_valid) { - len = read(fd, path, 512); - if (len < 1) { + /* a terminated string for sscanf() */ + if (shr_read_all(fd, path, sizeof(path)) || !path[0]) { nvme_show_perror("Could not read PCIe AER Fatal errors in /sys/"); pcie_fatal_valid = false; } @@ -1786,7 +1791,6 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin len = sscanf(path, "%*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d TOTAL_ERR_FATAL %d", &pcie_fatal); if (len < 1) { nvme_show_perror("Could not parse PCIe AER Fatal errors in /sys/"); - close(fd); pcie_fatal_valid = false; } } @@ -1873,21 +1877,21 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin switch (sfx_smart.io_speed) { case '1': if (strncmp("0x0020", pci_did, 6)) - strncpy(io_speed, "2.5MB/s", 15); + shr_strscpy(io_speed, "2.5MB/s", sizeof(io_speed)); else - strncpy(io_speed, "10MB/s", 15); + shr_strscpy(io_speed, "10MB/s", sizeof(io_speed)); break; case '2': if (strncmp("0x0020", pci_did, 6)) - strncpy(io_speed, "128KB/s", 15); + shr_strscpy(io_speed, "128KB/s", sizeof(io_speed)); else - strncpy(io_speed, "512KB/s", 15); + shr_strscpy(io_speed, "512KB/s", sizeof(io_speed)); break; case '3': - strncpy(io_speed, "Write Reject", 15); + shr_strscpy(io_speed, "Write Reject", sizeof(io_speed)); break; default: - strncpy(io_speed, "Normal", 15); + shr_strscpy(io_speed, "Normal", sizeof(io_speed)); } if (sfx_smart.comp_ratio < 100) @@ -1985,7 +1989,7 @@ static int sfx_status(int argc, char **argv, struct command *acmd, struct plugin (sfx_smart.sfx_critical_warning & SFX_CRIT_RW_LOCK) ? "\tRead/Write lock mode\n" : "" \ ); if (len < 11) - strcpy(path, "None"); + shr_strscpy(path, "None", sizeof(path)); printf("%-35s%s%s\n", "ScaleFlux Drive:", "/dev/", libnvme_transport_handle_get_name(hdl)); printf("%-35s%s\n", "PCIe Vendor ID:", pci_vid); From 7a75cd330bd3bd9083e91ad218490f847e68483a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:50:05 +0000 Subject: [PATCH 231/263] virtium: bound the string buffers The smart log and header lines were built with strcpy() and strcat() into the caller's buffer without its size, through a temporary buffer, and the file name with strcat() into a static buffer. The hex dump helper didn't know the size of its output either. Pass the sizes and append with shr_snprintf_append(), write the hex dumps with shr_bin_to_hex(), build the file name with one snprintf() and copy the header strings with shr_strscpy(). The serial number and model number are fixed size fields without terminator: print them with their size instead of %s, which read past a field which is full. Signed-off-by: Daniel Wagner --- plugins/virtium/virtium-nvme.c | 424 +++++++++++++++++++-------------- 1 file changed, 242 insertions(+), 182 deletions(-) diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index e0a87a8f16..f58975c907 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include "cleanup.h" @@ -61,59 +63,41 @@ static void vt_initialize_header_buffer(struct vtview_log_header *pbuff) memset(pbuff->test_name, 0, sizeof(pbuff->test_name)); } -static void vt_convert_data_buffer_to_hex_string(const unsigned char *bufPtr, - const unsigned int size, const bool isReverted, char *output) -{ - unsigned int i, pos; - const char hextable[16] = { - '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', 'A', 'B', - 'C', 'D', 'E', 'F', - }; - - memset(output, 0, (size * 2) + 1); - - for (i = 0; i < size; i++) { - if (isReverted) - pos = size - 1 - i; - else - pos = i; - output[2 * i] = hextable[(bufPtr[pos] & 0xF0) >> 4]; - output[2 * i + 1] = hextable[(bufPtr[pos] & 0x0F)]; - } -} - /* * Generate log file name. * Log file name will be generated automatically if user leave log file option blank. * Log file name will be generated as vtView-Smart-log-date-time.txt */ -static void vt_generate_vtview_log_file_name(char *fname) +static void vt_generate_vtview_log_file_name(char *fname, size_t size) { - time_t current; - struct tm tstamp; - char temp[256]; - - time(¤t); - - tstamp = *localtime(¤t); - snprintf(temp, sizeof(temp), "./vtView-Smart-log-"); - strcat(fname, temp); - strftime(temp, sizeof(temp), "%Y-%m-%d", &tstamp); - strcat(fname, temp); - snprintf(temp, sizeof(temp), ".txt"); - strcat(fname, temp); + time_t current = time(NULL); + struct tm tstamp = *localtime(¤t); + char date[32]; + + strftime(date, sizeof(date), "%Y-%m-%d", &tstamp); + snprintf(fname, size, "./vtView-Smart-log-%s.txt", date); } -static void vt_convert_smart_data_to_human_readable_format(struct vtview_smart_log_entry *smart, char *text) +/* A 128 bit counter of the SMART log as "name;value;" */ +static void vt_append_u128(char *text, size_t size, size_t *len, + const char *name, __u8 *value) { - char tempbuff[1024] = ""; + shr_snprintf_append(text, size, len, "%s;%s;", name, + uint128_t_to_string(le128_to_cpu(value))); +} + +static void vt_convert_smart_data_to_human_readable_format( + struct vtview_smart_log_entry *smart, char *text, size_t size) +{ + struct nvme_smart_log *log = &smart->raw_smart; + struct nvme_id_ctrl *ctrl = &smart->raw_ctrl; int i; - int temperature = ((smart->raw_smart.temperature[1] << 8) | smart->raw_smart.temperature[0]) - 273; + int temperature = ((log->temperature[1] << 8) | + log->temperature[0]) - 273; double capacity; char *curlocale; char *templocale; + size_t len = 0; __u8 lba_index; nvme_id_ns_flbas_to_lbaf_inuse(smart->raw_ns.flbas, &lba_index); @@ -130,94 +114,101 @@ static void vt_convert_smart_data_to_human_readable_format(struct vtview_smart_l capacity = le64_to_cpu(smart->raw_ns.nsze) * lba; - snprintf(tempbuff, sizeof(tempbuff), "log;%s;%llu;%s;%s;%-.*s;", smart->raw_ctrl.sn, - (unsigned long long)smart->time_stamp, smart->path, - smart->raw_ctrl.mn, (int)sizeof(smart->raw_ctrl.fr), - smart->raw_ctrl.fr); - strcpy(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Capacity;%lf;", capacity / 1000000000); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Critical_Warning;%u;", smart->raw_smart.critical_warning); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Temperature;%u;", temperature); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Available_Spare;%u;", smart->raw_smart.avail_spare); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Available_Spare_Threshold;%u;", smart->raw_smart.spare_thresh); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Percentage_Used;%u;", smart->raw_smart.percent_used); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Read;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_read))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Written;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_written))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Host_Read_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_reads))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Host_Write_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_writes))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Controller_Busy_Time;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.ctrl_busy_time))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Power_Cycles;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_cycles))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Power_On_Hours;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_on_hours))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Unsafe_Shutdowns;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.unsafe_shutdowns))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Media_Errors;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.media_errors))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Num_Err_Log_Entries;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.num_err_log_entries))); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Warning_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.warning_temp_time)); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Critical_Composite_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.critical_comp_time)); - strcat(text, tempbuff); + text[0] = '\0'; + shr_snprintf_append(text, size, &len, "log;%-.*s;%llu;%s;%-.*s;%-.*s;", + (int)sizeof(ctrl->sn), ctrl->sn, + (unsigned long long)smart->time_stamp, smart->path, + (int)sizeof(ctrl->mn), ctrl->mn, + (int)sizeof(ctrl->fr), ctrl->fr); + shr_snprintf_append(text, size, &len, "Capacity;%lf;", + capacity / 1000000000); + shr_snprintf_append(text, size, &len, "Critical_Warning;%u;", + log->critical_warning); + shr_snprintf_append(text, size, &len, "Temperature;%u;", temperature); + shr_snprintf_append(text, size, &len, "Available_Spare;%u;", + log->avail_spare); + shr_snprintf_append(text, size, &len, "Available_Spare_Threshold;%u;", + log->spare_thresh); + shr_snprintf_append(text, size, &len, "Percentage_Used;%u;", + log->percent_used); + vt_append_u128(text, size, &len, "Data_Units_Read", + log->data_units_read); + vt_append_u128(text, size, &len, "Data_Units_Written", + log->data_units_written); + vt_append_u128(text, size, &len, "Host_Read_Commands", log->host_reads); + vt_append_u128(text, size, &len, "Host_Write_Commands", + log->host_writes); + vt_append_u128(text, size, &len, "Controller_Busy_Time", + log->ctrl_busy_time); + vt_append_u128(text, size, &len, "Power_Cycles", log->power_cycles); + vt_append_u128(text, size, &len, "Power_On_Hours", log->power_on_hours); + vt_append_u128(text, size, &len, "Unsafe_Shutdowns", + log->unsafe_shutdowns); + vt_append_u128(text, size, &len, "Media_Errors", log->media_errors); + vt_append_u128(text, size, &len, "Num_Err_Log_Entries", + log->num_err_log_entries); + shr_snprintf_append(text, size, &len, "Warning_Temperature_Time;%u;", + le32_to_cpu(log->warning_temp_time)); + shr_snprintf_append(text, size, &len, + "Critical_Composite_Temperature_Time;%u;", + le32_to_cpu(log->critical_comp_time)); for (i = 0; i < 8; i++) { - __s32 temp = le16_to_cpu(smart->raw_smart.temp_sensor[i]); + __s32 temp = le16_to_cpu(log->temp_sensor[i]); if (!temp) { - snprintf(tempbuff, sizeof(tempbuff), "Temperature_Sensor_%d;NC;", i); - strcat(text, tempbuff); + shr_snprintf_append(text, size, &len, + "Temperature_Sensor_%d;NC;", i); continue; } - snprintf(tempbuff, sizeof(tempbuff), "Temperature_Sensor_%d;%d;", i, temp - 273); - strcat(text, tempbuff); + shr_snprintf_append(text, size, &len, + "Temperature_Sensor_%d;%d;", i, + temp - 273); } - snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T1_Trans_Count;%u;", le32_to_cpu(smart->raw_smart.thm_temp1_trans_count)); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T2_Trans_Count;%u;", le32_to_cpu(smart->raw_smart.thm_temp2_trans_count)); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T1_Total_Time;%u;", le32_to_cpu(smart->raw_smart.thm_temp1_total_time)); - strcat(text, tempbuff); - snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T2_Total_Time;%u;", le32_to_cpu(smart->raw_smart.thm_temp2_total_time)); - strcat(text, tempbuff); - - snprintf(tempbuff, sizeof(tempbuff), "NandWrites;%d;\n", 0); - strcat(text, tempbuff); + shr_snprintf_append(text, size, &len, + "Thermal_Management_T1_Trans_Count;%u;", + le32_to_cpu(log->thm_temp1_trans_count)); + shr_snprintf_append(text, size, &len, + "Thermal_Management_T2_Trans_Count;%u;", + le32_to_cpu(log->thm_temp2_trans_count)); + shr_snprintf_append(text, size, &len, + "Thermal_Management_T1_Total_Time;%u;", + le32_to_cpu(log->thm_temp1_total_time)); + shr_snprintf_append(text, size, &len, + "Thermal_Management_T2_Total_Time;%u;", + le32_to_cpu(log->thm_temp2_total_time)); + + shr_snprintf_append(text, size, &len, "NandWrites;%d;\n", 0); setlocale(LC_ALL, templocale); free(templocale); } -static void vt_header_to_string(const struct vtview_log_header *header, char *text) +static void vt_header_to_string(const struct vtview_log_header *header, + char *text, size_t size) { + const struct nvme_id_ctrl *ctrl = &header->raw_ctrl; char timebuff[50] = ""; - char tempbuff[MAX_HEADER_BUFF] = ""; char identext[16384] = ""; char fwtext[2048] = ""; + size_t len = 0; - strftime(timebuff, 50, "%Y-%m-%d %H:%M:%S", localtime(&(header->time_stamp))); - snprintf(tempbuff, MAX_HEADER_BUFF, "header;{\"session\":{\"testName\":\"%s\",\"dateTime\":\"%s\"},", + strftime(timebuff, sizeof(timebuff), "%Y-%m-%d %H:%M:%S", + localtime(&(header->time_stamp))); + text[0] = '\0'; + shr_snprintf_append(text, size, &len, + "header;{\"session\":{\"testName\":\"%s\",\"dateTime\":\"%s\"},", header->test_name, timebuff); - strcpy(text, tempbuff); - - vt_convert_data_buffer_to_hex_string((unsigned char *)&(header->raw_ctrl), sizeof(header->raw_ctrl), false, identext); - vt_convert_data_buffer_to_hex_string((unsigned char *)&(header->raw_fw), sizeof(header->raw_fw), false, fwtext); - snprintf(tempbuff, MAX_HEADER_BUFF, - "\"devices\":[{\"model\":\"%s\",\"port\":\"%s\",\"SN\":\"%s\",\"type\":\"NVMe\",\"identify\":\"%s\",\"firmwareSlot\":\"%s\"}]}\n", - header->raw_ctrl.mn, header->path, header->raw_ctrl.sn, identext, fwtext); - strcat(text, tempbuff); + + shr_bin_to_hex(identext, sizeof(identext), ctrl, sizeof(*ctrl), + SHR_HEX_UPPER); + shr_bin_to_hex(fwtext, sizeof(fwtext), &header->raw_fw, + sizeof(header->raw_fw), SHR_HEX_UPPER); + shr_snprintf_append(text, size, &len, + "\"devices\":[{\"model\":\"%-.*s\",\"port\":\"%s\",\"SN\":\"%-.*s\",\"type\":\"NVMe\",\"identify\":\"%s\",\"firmwareSlot\":\"%s\"}]}\n", + (int)sizeof(ctrl->mn), ctrl->mn, header->path, + (int)sizeof(ctrl->sn), ctrl->sn, identext, fwtext); } static int vt_append_text_file(const char *text, const char *filename) @@ -239,7 +230,8 @@ static int vt_append_log(struct vtview_smart_log_entry *smart, const char *filen { char sm_log_text[MAX_LOG_BUFF] = ""; - vt_convert_smart_data_to_human_readable_format(smart, sm_log_text); + vt_convert_smart_data_to_human_readable_format(smart, sm_log_text, + sizeof(sm_log_text)); return vt_append_text_file(sm_log_text, filename); } @@ -247,7 +239,7 @@ static int vt_append_header(const struct vtview_log_header *header, const char * { char header_text[MAX_HEADER_BUFF] = ""; - vt_header_to_string(header, header_text); + vt_header_to_string(header, header_text, sizeof(header_text)); return vt_append_text_file(header_text, filename); } @@ -331,17 +323,19 @@ vt_update_vtview_log_header(struct libnvme_transport_handle *hdl, const char *pa errno = EINVAL; return -1; } - strcpy(header.path, path); + shr_strscpy(header.path, path, sizeof(header.path)); if (!cfg->test_name) { - strcpy(header.test_name, DEFAULT_TEST_NAME); + shr_strscpy(header.test_name, DEFAULT_TEST_NAME, + sizeof(header.test_name)); } else { if (strlen(cfg->test_name) >= sizeof(header.test_name)) { nvme_show_error("test name too long"); errno = EINVAL; return -1; } - strcpy(header.test_name, cfg->test_name); + shr_strscpy(header.test_name, cfg->test_name, + sizeof(header.test_name)); } if (!cfg->output_file) @@ -438,39 +432,51 @@ static void vt_build_power_state_descriptor(const struct nvme_id_ctrl *ctrl) printf("%6d", i); buf = (unsigned char *) (&ctrl->psd[i]); - vt_convert_data_buffer_to_hex_string(&buf[0], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[0], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); temp = ctrl->psd[i].flags; printf("%4ub", ((unsigned char)temp & 0x02)); printf("%3ub", ((unsigned char)temp & 0x01)); - vt_convert_data_buffer_to_hex_string(&buf[0], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[0], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%5sh", s); - vt_convert_data_buffer_to_hex_string(&buf[4], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[4], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); - vt_convert_data_buffer_to_hex_string(&buf[8], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[8], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); - vt_convert_data_buffer_to_hex_string(&buf[12], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[12], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); - vt_convert_data_buffer_to_hex_string(&buf[15], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[15], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%3sh", s); - vt_convert_data_buffer_to_hex_string(&buf[14], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[14], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%3sh", s); - vt_convert_data_buffer_to_hex_string(&buf[13], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[13], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%3sh", s); - vt_convert_data_buffer_to_hex_string(&buf[12], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[12], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%3sh", s); - vt_convert_data_buffer_to_hex_string(&buf[16], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[16], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); temp = ctrl->psd[i].ips; snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01)); printf("%3sb", s); - vt_convert_data_buffer_to_hex_string(&buf[16], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[16], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%5sh", s); - vt_convert_data_buffer_to_hex_string(&buf[20], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[20], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%9sh", s); temp = ctrl->psd[i].apws; @@ -479,7 +485,8 @@ static void vt_build_power_state_descriptor(const struct nvme_id_ctrl *ctrl) snprintf(s, sizeof(s), "%u%u%u", (((unsigned char)temp) & 0x01), (((unsigned char)temp >> 1) & 0x01), (((unsigned char)temp >> 2) & 0x01)); printf("%4sb", s); - vt_convert_data_buffer_to_hex_string(&buf[20], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[20], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf("%5sh", s); printf("\n"); } @@ -705,113 +712,144 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) buf = (unsigned char *)(ctrl); printf("{\n"); - vt_convert_data_buffer_to_hex_string(buf, 2, true, s); + shr_bin_to_hex(s, sizeof(s), buf, 2, SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"PCI Vendor ID\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[2], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[2], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"PCI Subsystem Vendor ID\":\"%sh\",\n", s); printf(" \"Serial Number\":\"%s\",\n", ctrl->sn); printf(" \"Model Number\":\"%s\",\n", ctrl->mn); printf(" \"Firmware Revision\":\"%-.*s\",\n", (int)sizeof(ctrl->fr), ctrl->fr); - vt_convert_data_buffer_to_hex_string(&buf[72], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[72], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Recommended Arbitration Burst\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[73], 3, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[73], 3, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"IEEE OUI Identifier\":\"%sh\",\n", s); temp = ctrl->cmic; printf(" \"Controller Multi-Path I/O and Namespace Sharing Capabilities\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[76], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[76], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 3, CMICtable, true); - vt_convert_data_buffer_to_hex_string(&buf[77], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[77], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Maximum Data Transfer Size\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[78], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[78], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Controller ID\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[80], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[80], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Version\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[84], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[84], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"RTD3 Resume Latency\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[88], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[88], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"RTD3 Entry Latency\":\"%sh\",\n", s); temp = le32_to_cpu(ctrl->oaes); printf(" \"Optional Asynchronous Events Supported\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[92], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[92], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 8, 2, OAEStable, true); temp = le32_to_cpu(ctrl->ctratt); printf(" \"Controller Attributes\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[96], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[96], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 2, CTRATTtable, true); - vt_convert_data_buffer_to_hex_string(&buf[122], 16, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[122], 16, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"FRU Globally Unique Identifier\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[240], 16, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[240], 16, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"NVMe Management Interface Specification\":\"%sh\",\n", s); temp = le16_to_cpu(ctrl->oacs); printf(" \"Optional Admin Command Support\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[256], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[256], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 9, OACStable, true); - vt_convert_data_buffer_to_hex_string(&buf[258], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[258], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Abort Command Limit\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[259], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[259], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Asynchronous Event Request Limit\":\"%sh\",\n", s); temp = ctrl->frmw; printf(" \"Firmware Updates\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[260], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, FRMWtable, false); - vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[260], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Firmware Slot\":\"%uh\",\n", ((ctrl->frmw >> 1) & 0x07)); vt_build_identify_lv2(temp, 4, 1, FRMWtable, true); temp = ctrl->lpa; printf(" \"Log Page Attributes\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[261], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[261], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 4, LPAtable, true); - vt_convert_data_buffer_to_hex_string(&buf[262], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[262], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Error Log Page Entries\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[263], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[263], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Number of Power States Support\":\"%sh\",\n", s); temp = ctrl->avscc; printf(" \"Admin Vendor Specific Command Configuration\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[264], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[264], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, AVSCCtable, true); temp = ctrl->apsta; printf(" \"Autonomous Power State Transition Attributes\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[265], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[265], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, APSTAtable, true); - vt_convert_data_buffer_to_hex_string(&buf[266], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[266], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Warning Composite Temperature Threshold\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[268], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[268], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Critical Composite Temperature Threshold\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[270], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[270], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Maximum Time for Firmware Activation\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[272], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[272], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Host Memory Buffer Preferred Size\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[276], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[276], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Host Memory Buffer Minimum Size\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[280], 16, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[280], 16, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Total NVM Capacity\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[296], 16, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[296], 16, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Unallocated NVM Capacity\":\"%sh\",\n", s); temp = le32_to_cpu(ctrl->rpmbs); printf(" \"Replay Protected Memory Block Support\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[312], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[312], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); printf(" \"Number of RPMB Units\":\"%u\",\n", (temp & 0x00000003)); snprintf(s, sizeof(s), ((temp >> 3) & 0x00000007) ? "Reserved" : "HMAC SHA-256"); @@ -820,40 +858,49 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) printf(" \"Access Size\":\"%u\",\n", ((temp >> 24) & 0x000000FF)); printf(" },\n"); - vt_convert_data_buffer_to_hex_string(&buf[316], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[316], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Extended Device Self-test Time\":\"%sh\",\n", s); temp = ctrl->dsto; printf(" \"Device Self-test Options\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[318], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[318], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, DSTOtable, true); - vt_convert_data_buffer_to_hex_string(&buf[319], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[319], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Firmware Update Granularity\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[320], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[320], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Keep Alive Support\":\"%sh\",\n", s); temp = le16_to_cpu(ctrl->hctma); printf(" \"Host Controlled Thermal Management Attributes\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[322], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[322], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, HCTMAtable, true); - vt_convert_data_buffer_to_hex_string(&buf[324], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[324], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Minimum Thermal Management Temperature\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[326], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[326], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Maximum Thermal Management Temperature\":\"%sh\",\n", s); temp = le16_to_cpu(ctrl->sanicap); printf(" \"Sanitize Capabilities\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[328], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[328], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 3, SANICAPtable, true); temp = ctrl->sqes; printf(" \"Submission Queue Entry Size\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[512], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[512], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F)); printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F)); @@ -861,65 +908,77 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) temp = ctrl->cqes; printf(" \"Completion Queue Entry Size\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[513], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[513], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F)); printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F)); printf(" }\n"); - vt_convert_data_buffer_to_hex_string(&buf[514], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[514], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Maximum Outstanding Commands\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[516], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[516], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Number of Namespaces\":\"%sh\",\n", s); temp = le16_to_cpu(ctrl->oncs); printf(" \"Optional NVM Command Support\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[520], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[520], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 7, ONCStable, true); temp = le16_to_cpu(ctrl->fuses); printf(" \"Fused Operation Support\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[522], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[522], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, FUSEStable, true); temp = ctrl->fna; printf(" \"Format NVM Attributes\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[524], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[524], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 3, FNAtable, true); temp = ctrl->vwc; printf(" \"Volatile Write Cache\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[525], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[525], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, VWCtable, true); - vt_convert_data_buffer_to_hex_string(&buf[526], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[526], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Atomic Write Unit Normal\":\"%sh\",\n", s); - vt_convert_data_buffer_to_hex_string(&buf[528], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[528], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Atomic Write Unit Power Fail\":\"%sh\",\n", s); temp = ctrl->icsvscc; printf(" \"NVM Vendor Specific Command Configuration\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[530], 1, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[530], 1, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); vt_build_identify_lv2(temp, 0, 1, ICSVSCCtable, true); - vt_convert_data_buffer_to_hex_string(&buf[532], 2, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[532], 2, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Atomic Compare 0 Write Unit\":\"%sh\",\n", s); temp = le32_to_cpu(ctrl->sgls); printf(" \"SGL Support\":{\n"); - vt_convert_data_buffer_to_hex_string(&buf[536], 4, true, s); + shr_bin_to_hex(s, sizeof(s), &buf[536], 4, + SHR_HEX_UPPER | SHR_HEX_REVERSE); printf(" \"Value\":\"%sh\",\n", s); pos = (temp & 0x00000003); printf(" \"bit 1:0\":\"%s\",\n", SGLSSubtable[pos]); vt_build_identify_lv2(temp, 2, 1, SGLStable, false); vt_build_identify_lv2(temp, 16, 5, SGLStable, true); - vt_convert_data_buffer_to_hex_string(&buf[768], 256, false, s); + shr_bin_to_hex(s, sizeof(s), &buf[768], 256, SHR_HEX_UPPER); printf(" \"NVM Subsystem NVMe Qualified Name\":\"%s\",\n", s); printf("}\n\n"); @@ -974,7 +1033,8 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, OPT_END() }; - vt_generate_vtview_log_file_name(vt_default_log_file_name); + vt_generate_vtview_log_file_name(vt_default_log_file_name, + sizeof(vt_default_log_file_name)); err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) From 05ec83100cb9b479ab7740f1189514426f371c7b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:50:38 +0000 Subject: [PATCH 232/263] solidigm: don't append the field names to an uninitialized buffer workload-tracker appended the names of the trigger fields with strcat() to field_options, which was never initialized, so the list in the error message started with whatever was on the stack and could overflow the buffer. Start the list with shr_strscpy() and append with shr_strscat(), bounded by the size of the buffer, in join_fields() and join_options(). Signed-off-by: Daniel Wagner --- plugins/solidigm/solidigm-workload-tracker.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c index 316159eb3e..eab2d5e2ae 100644 --- a/plugins/solidigm/solidigm-workload-tracker.c +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "cleanup.h" #include "global-ctx.h" @@ -473,10 +474,10 @@ static int find_option(char const *list[], int size, const char *val) static void join_options(char *dest, size_t dest_size, char const *list[], size_t list_size) { - strncat(dest, list[0], dest_size - strlen(dest) - 1); + shr_strscpy(dest, list[0], dest_size); for (int i = 1; i < list_size; i++) { - strncat(dest, "|", dest_size - strlen(dest) - 1); - strncat(dest, list[i], dest_size - strlen(dest) - 1); + shr_strscat(dest, "|", dest_size); + shr_strscat(dest, list[i], dest_size); } } @@ -489,15 +490,15 @@ static int find_field(struct field *fields, const char *val) return -EINVAL; } -static void join_fields(char *dest, struct field *fields) +static void join_fields(char *dest, size_t dest_size, struct field *fields) { - strcat(dest, fields[0].name); + shr_strscpy(dest, fields[0].name, dest_size); for (int i = 1; i < MAX_FIELDS; i++) { char *name = fields[i].name; if (name) { - strcat(dest, "|"); - strcat(dest, name); + shr_strscat(dest, "|", dest_size); + shr_strscat(dest, name, dest_size); } } } @@ -607,7 +608,8 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *acmd, stru if (field_pos < 0) { char field_options[256]; - join_fields(field_options, group_fields[opt]); + join_fields(field_options, sizeof(field_options), + group_fields[opt]); nvme_show_error( "Invalid field name: %s. For type: \"%s\", valid fields are: %s", cfg.trigger_field, cfg.tracker_type, field_options); From 1ba69201cfe64ee399cce8fa11b3b5a5b79c2afd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 13:51:33 +0000 Subject: [PATCH 233/263] exclusion: parse the entry to remove strictly remove read the number of the entry with sscanf("%zu"), which takes "3abc" as 3 and a negative number as a huge one, so a typo could delete an entry. Parse the answer with shr_parse_u64() up to the number of entries, whitespace around it trimmed: anything else cancels. Signed-off-by: Daniel Wagner --- plugins/exclusion/exclusion-nvme.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/exclusion/exclusion-nvme.c b/plugins/exclusion/exclusion-nvme.c index b90bffe437..371f0fd5e5 100644 --- a/plugins/exclusion/exclusion-nvme.c +++ b/plugins/exclusion/exclusion-nvme.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "global-ctx.h" #include "nvme-print.h" @@ -276,7 +278,8 @@ static int excl_remove(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; struct entry_collection ec = { 0 }; char answer[32]; - size_t i, choice; + size_t i; + uint64_t choice; int ret; struct config { @@ -324,8 +327,7 @@ static int excl_remove(int argc, char **argv, struct command *acmd, fflush(stdout); if (!fgets(answer, sizeof(answer), stdin) || - sscanf(answer, "%zu", &choice) != 1 || - choice < 1 || choice > ec.len) { + shr_parse_u64(shr_trim(answer), ec.len, &choice) || choice < 1) { printf("cancelled\n"); ret = 0; goto out; From 1be05bcee898f3d1744ba756f487594ecf7d98dd Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sat, 17 Oct 2026 14:46:02 +0000 Subject: [PATCH 234/263] nvme-print: bound the host metadata element walk The Controller and Namespace Metadata features are printed element by element. The value was copied with strncpy() into an uninitialized buffer, which left it unterminated, so the output included whatever was on the stack. The next element was found with the length field without le16_to_cpu(), and nothing stopped the walk at the end of the 4096 byte structure, so a large length made it read and print past the buffer. Stop at the end of the structure, cut the length of a value off there and print it with its length, in the normal and the JSON output. Signed-off-by: Daniel Wagner --- src/nvme-print-json.c | 15 ++-- src/nvme-print-stdout.c | 14 +-- tests/cli/meson.build | 7 ++ tests/cli/nvme_host_metadata_test.py | 129 +++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 tests/cli/nvme_host_metadata_test.py diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 5670555ae7..86f27b0f76 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -4216,6 +4216,7 @@ static void json_host_metadata(struct json_object *r, enum nvme_features_id fid, struct nvme_host_metadata *data) { struct nvme_metadata_element_desc *desc = &data->descs[0]; + __u8 *end = (__u8 *)data + sizeof(*data); int i; char val[VAL_LEN]; __u16 len; @@ -4228,12 +4229,16 @@ static void json_host_metadata(struct json_object *r, enum nvme_features_id fid, obj_add_array(r, "Metadata Element Descriptors", desca); for (i = 0; i < data->ndesc; i++) { + /* the descriptors and their values end with the structure */ + if (desc->val > end) + break; + desce = json_create_object(); array_add_obj(desca, desce); obj_add_int(desce, "Element", i); - sprintf(json_str, "0x%02x", desc->type); + snprintf(json_str, sizeof(json_str), "0x%02x", desc->type); obj_add_str(desce, "Type", json_str); obj_add_str(desce, "Type definition", @@ -4241,13 +4246,13 @@ static void json_host_metadata(struct json_object *r, enum nvme_features_id fid, obj_add_int(desce, "Revision", desc->rev); - len = le16_to_cpu(desc->len); - obj_add_int(desce, "Length", len); + obj_add_int(desce, "Length", le16_to_cpu(desc->len)); - strncpy(val, (char *)desc->val, min(sizeof(val) - 1, len)); + len = min_t(size_t, le16_to_cpu(desc->len), end - desc->val); + snprintf(val, sizeof(val), "%.*s", len, (char *)desc->val); obj_add_str(desce, "Value", val); - desc = (struct nvme_metadata_element_desc *)&desc->val[desc->len]; + desc = (struct nvme_metadata_element_desc *)&desc->val[len]; } } diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 516509c472..ec3116409b 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -2534,8 +2534,8 @@ static void stdout_host_metadata(enum nvme_features_id fid, struct nvme_host_metadata *data) { struct nvme_metadata_element_desc *desc = &data->descs[0]; + __u8 *end = (__u8 *)data + sizeof(*data); int i; - char val[4096]; __u16 len; struct shr_table *t; @@ -2548,8 +2548,10 @@ static void stdout_host_metadata(enum nvme_features_id fid, stdout_kv_table_finish(t, "host-metadata"); for (i = 0; i < data->ndesc; i++) { - len = le16_to_cpu(desc->len); - strncpy(val, (char *)desc->val, min(sizeof(val) - 1, len)); + /* the descriptors and their values end with the structure */ + if (desc->val > end) + break; + len = min_t(size_t, le16_to_cpu(desc->len), end - desc->val); printf("\tElement[%-3d]:\n", i); @@ -2562,12 +2564,12 @@ static void stdout_host_metadata(enum nvme_features_id fid, nvme_host_metadata_type_to_string(fid, desc->type)); stdout_kv_add(t, "Revision", "%d", desc->rev); - stdout_kv_add(t, "Length", "%d", len); - stdout_kv_add(t, "Value", "%s", val); + stdout_kv_add(t, "Length", "%d", le16_to_cpu(desc->len)); + stdout_kv_add(t, "Value", "%.*s", len, (char *)desc->val); stdout_kv_table_finish(t, "host-metadata"); - desc = (struct nvme_metadata_element_desc *)&desc->val[desc->len]; + desc = (struct nvme_metadata_element_desc *)&desc->val[len]; } } diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 261a6a253b..ac8d81eaf6 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -125,6 +125,13 @@ if python3.found() env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) + test('nvme-cli - host-metadata-cli', + python3, + args: [files('nvme_host_metadata_test.py'), nvme_exe, + mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib]) + # The host and AVE discovery printers need the fabrics code. if want_fabrics and json_c_dep.found() and 'log' in selected_plugins test('nvme-cli - discovery-logs-cli', diff --git a/tests/cli/nvme_host_metadata_test.py b/tests/cli/nvme_host_metadata_test.py new file mode 100644 index 0000000000..0e31e6f5d8 --- /dev/null +++ b/tests/cli/nvme_host_metadata_test.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This file is part of nvme-cli. +# Copyright (c) 2026 SUSE LLC +# +# Authors: Daniel Wagner +"""Tests the printing of the Controller Metadata feature: the element +descriptors are walked within the 4096 byte structure, whatever lengths +the device reports, and each value is printed with its length. + +Usage: python3 nvme_host_metadata_test.py +""" +import json +import os +import shutil +import struct +import subprocess +import sys +import tempfile +import unittest + +from nvme_mock_ipc import (MockIPCServer, make_mock_env, + resolve_mock_lib_path, run_nvme) + +_NVME_BIN = (sys.argv[1] + if len(sys.argv) > 1 and not sys.argv[1].startswith('-') + else 'nvme') +_MOCK_LIB = resolve_mock_lib_path("./libmock_nvme.so") + +_NVME_ADMIN_GET_FEATURES = 0x0a + + +def json_c_available(): + """True if nvme was built with json-c: the help of --output-format + lists json only then.""" + proc = subprocess.run([_NVME_BIN, 'get-feature', '--help'], + capture_output=True, text=True) + return 'json' in proc.stdout + proc.stderr +_HOST_METADATA_SIZE = 4096 + + +def element(etype, value, length=None): + """An element descriptor: type, revision, length and value.""" + return struct.pack(' Date: Fri, 16 Oct 2026 14:54:31 -0400 Subject: [PATCH 235/263] ci: attach the static Linux and Windows binaries to the release A release created with GITHUB_TOKEN does not trigger other workflows. The upload job in upload.yml waits for the release event, so it never ran and the releases have no binaries. Build them in the release workflow and upload them there. Signed-off-by: Martin Belanger --- .github/workflows/release.yml | 81 +++++++++++++++++++++++++++++++++-- .github/workflows/upload.yml | 31 +------------- scripts/build.sh | 21 +++++++++ 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8dbc637e41..bd67bf1570 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,14 +10,17 @@ name: Release on: push: - branches: [master] tags: - - '**' + - 'v*' + pull_request: + paths: + - .github/workflows/release.yml + - scripts/build.sh jobs: - build: + create-release: runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'linux-nvme/nvme-cli' + if: github.event_name == 'push' && github.repository == 'linux-nvme/nvme-cli' permissions: contents: write steps: @@ -35,3 +38,73 @@ jobs: gh release create "${GITHUB_REF_NAME}" "${args[@]}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build-linux-static: + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh static + VERSION="$(git describe --always --abbrev=12 --dirty)" + mkdir upload + cp .build-ci/nvme "upload/nvme-cli-${VERSION}-x86_64" + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: linux-static + path: upload/* + + build-windows-static: + runs-on: windows-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2.33.0 + with: + msystem: UCRT64 + update: true + install: >- + git + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-pkgconf + mingw-w64-ucrt-x86_64-meson + mingw-w64-ucrt-x86_64-ninja + mingw-w64-ucrt-x86_64-json-c + - name: build + shell: msys2 {0} + run: | + set -euxo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + scripts/build.sh -c /ucrt64/bin/gcc static_windows + VERSION="$(git describe --always --abbrev=12 --dirty)" + mkdir upload + cp .build-ci/nvme.exe "upload/nvme-cli-${VERSION}-x86_64.exe" + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: windows-static + path: upload/* + + upload-release-assets: + runs-on: ubuntu-latest + needs: [create-release, build-linux-static, build-windows-static] + permissions: + contents: write + steps: + - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + path: upload + merge-multiple: true + - name: Upload binaries + run: | + gh release upload "${GITHUB_REF_NAME}" upload/* --clobber + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index 9f5e4971e1..04c94f6699 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -13,8 +13,6 @@ on: branches: [master] tags: - 'v*' - release: - types: [published] jobs: build-static: @@ -22,8 +20,6 @@ jobs: runs-on: ubuntu-latest container: image: ghcr.io/linux-nvme/debian:latest - outputs: - VERSION: ${{ steps.build.outputs.VERSION }} steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -33,13 +29,8 @@ jobs: run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: build - id: build run: | git config --global --add safe.directory "$GITHUB_WORKSPACE" - VERSION="$(git describe --always --abbrev=12 --dirty)" - echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT - echo $VERSION - scripts/build.sh static mkdir upload cp .build-ci/nvme upload/nvme-cli-latest-x86_64 @@ -59,7 +50,7 @@ jobs: name: upload test binary runs-on: ubuntu-latest needs: build-static - if: ${{ (github.event_name == 'push' || github.event_name == 'release') && github.repository == 'linux-nvme/nvme-cli' }} + if: ${{ github.event_name == 'push' && github.repository == 'linux-nvme/nvme-cli' }} steps: - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: @@ -86,23 +77,3 @@ jobs: echo "put $f $(basename "$f")" >> "$SFTP_BATCH" done sftp -b "$SFTP_BATCH" "${SFTP_USERNAME}@${SFTP_SERVER}" - - upload-release-assets: - name: upload GitHub release assets - runs-on: ubuntu-latest - needs: build-static - env: - VERSION: ${{ needs.build-static.outputs.VERSION }} - if: ${{ github.event_name == 'release' && github.repository == 'linux-nvme/nvme-cli' }} - steps: - - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 - with: - name: nvme-cli - path: upload - - name: upload versioned binary - uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1.0.2 - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: upload/nvme-cli-${{ env.VERSION }}-x86_64 - asset_name: nvme-cli-${{ env.VERSION }}-x86_64 - asset_content_type: application/octet-stream diff --git a/scripts/build.sh b/scripts/build.sh index 3c610d919f..ad7589100a 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -55,6 +55,7 @@ usage() { echo " html_docs build html documentation only" echo " rst_docs build rst documentation only" echo " static build a static binary" + echo " static_windows build a static binary on MSYS2" echo " minimal_static build a static binary without fabrics support" echo " nofabrics build without fabrics support, run unit tests" echo " libnvme build only libnvme" @@ -334,6 +335,26 @@ config_meson_static() { "${BUILDDIR}" } +config_meson_static_windows() { + CC="${CC}" "${MESON}" setup \ + --werror \ + --buildtype=release \ + --default-library=static \ + -Dc_link_args="-static" \ + -Dlibarchive=disabled \ + -Dpython=disabled \ + -Dopenssl=disabled \ + -Dtests=false \ + -Dexamples=false \ + "${BUILDDIR}" +} + +test_meson_static_windows() { + # Fail if nvme.exe needs a DLL that is not part of Windows. + ldd "${BUILDDIR}/nvme.exe" + ! ldd "${BUILDDIR}/nvme.exe" | grep -v -i '/c/windows/' +} + config_meson_minimal_static() { local cflags=( -U_GNU_SOURCE From dd807ed79858a30eb15eebf115190d620a77fb0c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:06:09 +0000 Subject: [PATCH 236/263] tests/cli: define each mock function once for both builds Since the device fuzz target builds libmock_nvme.c with MOCK_FUZZ, ipc_call() and connect_ipc_socket() each have two definitions, one per build and at different lines. A coverage build has data from both, and gcovr fails to merge a function it finds on two lines: GcovrMergeAssertionError: tests/cli/libmock_nvme.c:737 Got function ipc_call on multiple lines: 604, 737. This breaks the coverage report of all nightly test runs. Define each function once and move the MOCK_FUZZ condition into the body. The fuzz variant of ipc_call() becomes fuzz_ipc_call(). Fixes: c60974b6b ("tests/cli: answer the commands from a fuzz input") Signed-off-by: Daniel Wagner --- tests/cli/libmock_nvme.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/cli/libmock_nvme.c b/tests/cli/libmock_nvme.c index 6c8733dce0..fbf0c46335 100644 --- a/tests/cli/libmock_nvme.c +++ b/tests/cli/libmock_nvme.c @@ -319,19 +319,20 @@ static void init_orig_functions(void) orig_get_nprocs = (orig_get_nprocs_t)dlsym(RTLD_NEXT, "get_nprocs"); } -#ifdef MOCK_FUZZ /* * Built into a fuzz target (tests/fuzz/fuzz-device.c), the shim has no * IPC server: the fuzz input answers every command instead. A "socket" * is a /dev/null fd, so the callers can close it as usual. + * + * Both builds define each function once, at the same line, and keep + * MOCK_FUZZ inside the body: gcovr merges the coverage of both and + * fails on a function it finds on two lines. */ static int connect_ipc_socket(void) { +#ifdef MOCK_FUZZ return orig_open("/dev/null", O_RDWR); -} #else -static int connect_ipc_socket(void) -{ const char *sock_path = getenv("MOCK_IPC_SOCK"); struct sockaddr_un addr; int fd; @@ -358,8 +359,8 @@ static int connect_ipc_socket(void) } return fd; -} #endif +} const char *default_opts = "instance,cntlid,concat,ctrl_loss_tmo,data_digest," @@ -601,15 +602,13 @@ static void fuzz_read(void *buf, size_t len) * failed ioctl(). Everything that is no device command fails, so the * real function takes over where there is one. */ -static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data, - struct ipc_response *resp) +static void *fuzz_ipc_call(struct ipc_request *req, + struct ipc_response *resp) { uint8_t ctl; uint16_t sc; void *buf; - (void)ipc_fd; - (void)data; memset(resp, 0, sizeof(*resp)); switch (req->type) { @@ -730,6 +729,8 @@ static void *read_ipc_response(int ipc_fd, struct ipc_response *resp) return payload; } +#endif + /* * Send a request and its data_len bytes of data, and read the response. * A failed send is reported like a failed read. @@ -737,6 +738,11 @@ static void *read_ipc_response(int ipc_fd, struct ipc_response *resp) static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data, struct ipc_response *resp) { +#ifdef MOCK_FUZZ + (void)ipc_fd; + (void)data; + return fuzz_ipc_call(req, resp); +#else size_t len = req->data_len; ipc_request_encode(req); @@ -750,8 +756,8 @@ static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data, } return read_ipc_response(ipc_fd, resp); -} #endif +} ssize_t write(int fd, const void *buf, size_t count) { From a4802abd87e1a776502ce6db67dab60d882a5d64 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:13:50 +0000 Subject: [PATCH 237/263] build: keep the internal functions in the coverage report gcovr drops all functions starting with '__' as compiler internal. In libnvme these are the internal helpers, so their coverage is missing from the report. And gcovr 8.7 fails on any such C function: KeyError: 1083 Keep them with include-internal-functions. A gcovr.cfg in the root directory applies it to all callers, the nightly runs as well as scripts/build.sh. Link: https://github.com/gcovr/gcovr/issues/1319 Signed-off-by: Daniel Wagner --- gcovr.cfg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 gcovr.cfg diff --git a/gcovr.cfg b/gcovr.cfg new file mode 100644 index 0000000000..d4af40dbfc --- /dev/null +++ b/gcovr.cfg @@ -0,0 +1,6 @@ +# gcovr reads this file from the --root directory. + +# gcovr drops all functions starting with '__' as compiler internal, +# which are the internal helpers of libnvme. And gcovr 8.7 fails on +# such C functions (https://github.com/gcovr/gcovr/issues/1319). +include-internal-functions = yes From 25ba2adb09179832c69c4549bfe29340200b12b1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:14 +0000 Subject: [PATCH 238/263] tests: drop the -cli suffix and use kebab-case for the nvme-cli tests Half of the tests of the nvme binary end in -cli, the other half do not, and some names use underscores or a space. Name each test after the feature it covers, in kebab-case like the other tests: nvme-cli - print-cli -> nvme-cli - print nvme-cli - fabrics-mock-cli -> nvme-cli - fabrics nvme-cli - argconfig_parse -> nvme-cli - argconfig-parse nvme-cli - plugin dispatch -> nvme-cli - plugin-dispatch The test of the e2e runner is no e2e test, but its name starts like one. Name it after the runner, nvme-cli-e2e - tap. Signed-off-by: Daniel Wagner --- tests/cli/meson.build | 28 ++++++++++++++-------------- tests/unit/c/meson.build | 6 +++--- tests/unit/py/meson.build | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/cli/meson.build b/tests/cli/meson.build index ac8d81eaf6..83279d6118 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -11,7 +11,7 @@ if python3.found() # Standalone CLI test for the ownership registry. It needs no device # (it uses NVME_REGISTRY_DIR). if want_fabrics and 'registry' in selected_plugins - test('nvme-cli - registry-cli', + test('nvme-cli - registry', python3, args: [files('nvme_registry_test.py'), nvme_exe], env: cli_test_env, @@ -22,7 +22,7 @@ if python3.found() # to the commands that need no keyring access (the test's docstring # lists them), so it only needs OpenSSL, not a working kernel keyring. if want_fabrics and 'keys' in selected_plugins and openssl_dep.found() - test('nvme-cli - keys-cli', + test('nvme-cli - keys', python3, args: [files('nvme_keys_test.py'), nvme_exe], env: cli_test_env, @@ -33,7 +33,7 @@ if python3.found() # an explicit --config/--output pair under /tmp instead. Requires # json-c since the legacy config.json reader is a no-op without it. if want_fabrics and json_c_dep.found() - test('nvme-cli - config-convert-cli', + test('nvme-cli - config-convert', python3, args: [files('nvme_config_convert_test.py'), nvme_exe], env: cli_test_env, @@ -44,7 +44,7 @@ if python3.found() # explicit --output under /tmp instead. "config create" lives in the # "config" plugin, so it needs that plugin selected too. if want_fabrics and 'config' in selected_plugins - test('nvme-cli - config-create-cli', + test('nvme-cli - config-create', python3, args: [files('nvme_config_create_test.py'), nvme_exe], env: cli_test_env, @@ -87,45 +87,45 @@ if python3.found() endif if want_fabrics - test('nvme-cli - fabrics-mock-cli', + test('nvme-cli - fabrics', python3, args: [files('nvme_fabrics_mock_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) endif - test('nvme-cli - pif-sts-cli', + test('nvme-cli - pif-sts', python3, args: [files('nvme_pif_sts_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) - test('nvme-cli - init-pi-tags-cli', + test('nvme-cli - init-pi-tags', python3, args: [files('nvme_init_pi_tags_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) if 'id' in selected_plugins - test('nvme-cli - usage-cli', + test('nvme-cli - usage', python3, args: [files('nvme_usage_test.py'), nvme_exe], depends: nvme_exe) endif - test('nvme-cli - dry-run-cli', + test('nvme-cli - dry-run', python3, args: [files('nvme_dry_run_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) - test('nvme-cli - sel-cli', + test('nvme-cli - sel', python3, args: [files('nvme_sel_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib]) - test('nvme-cli - host-metadata-cli', + test('nvme-cli - host-metadata', python3, args: [files('nvme_host_metadata_test.py'), nvme_exe, mock_nvme_lib], @@ -134,7 +134,7 @@ if python3.found() # The host and AVE discovery printers need the fabrics code. if want_fabrics and json_c_dep.found() and 'log' in selected_plugins - test('nvme-cli - discovery-logs-cli', + test('nvme-cli - discovery-logs', python3, args: [files('nvme_discovery_logs_test.py'), nvme_exe, mock_nvme_lib], @@ -147,7 +147,7 @@ if python3.found() print_test_env = environment() print_test_env.prepend('PYTHONPATH', meson.project_source_root()) print_test_env.set('NVME_TEST_JSON', json_c_dep.found() ? '1' : '0') - test('nvme-cli - print-cli', + test('nvme-cli - print', python3, args: [files('nvme_print_test.py'), nvme_exe, mock_nvme_lib], env: print_test_env, @@ -165,7 +165,7 @@ if python3.found() endforeach sed_test_env = cli_test_env sed_test_env.set('NVME_SED_IOCTLS', ','.join(sed_ioctls)) - test('nvme-cli - sed-cli', + test('nvme-cli - sed', python3, args: [files('nvme_sed_test.py'), nvme_exe, mock_nvme_lib], env: sed_test_env, diff --git a/tests/unit/c/meson.build b/tests/unit/c/meson.build index cb9d384c7a..6a87e66339 100644 --- a/tests/unit/c/meson.build +++ b/tests/unit/c/meson.build @@ -11,7 +11,7 @@ test_argconfig_parse = executable( ], ) -test('nvme-cli - argconfig_parse', test_argconfig_parse) +test('nvme-cli - argconfig-parse', test_argconfig_parse) test_argconfig_diff = executable( 'test-argconfig-diff', @@ -28,7 +28,7 @@ test_argconfig_diff = executable( ], ) -test('nvme-cli - argconfig_diff', test_argconfig_diff) +test('nvme-cli - argconfig-diff', test_argconfig_diff) test_global_config = executable( 'test-global-config', @@ -60,4 +60,4 @@ test_plugin = executable( ], ) -test('nvme-cli - plugin dispatch', test_plugin) +test('nvme-cli - plugin-dispatch', test_plugin) diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index e0eb685754..2d64522e5a 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -119,7 +119,7 @@ if python3.found() # Test e2e runner's TAP/JSON generation. test( - 'nvme-cli - e2e-runner-tap', + 'nvme-cli-e2e - tap', python3, args: [ files('test_e2e_runner_tap.py'), From 51cf099314acc815f9b9c0723d117f5152e013f0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:16 +0000 Subject: [PATCH 239/263] tests/cli: name the plugin tests after their plugin The plugin tests run as part of nvme-cli and repeat the plugin name in the test name and in two suites: micron+micron-cli - nvme-cli:nvme-cli - micron_latency_mock_test Use the plugin as the component, as the e2e plugin tests do, and name the test after the command it covers, without the _mock_test suffix: nvme-cli:micron - latency The micron and ocp names are derived from the file names, so the files keep their names. Drop the plugin suites, nothing uses them: a name pattern selects the tests of a plugin, e.g. meson test 'micron - *'. Signed-off-by: Daniel Wagner --- tests/cli/micron/meson.build | 10 +++++----- tests/cli/ocp/meson.build | 5 +++-- tests/cli/samsung/meson.build | 10 ++++------ tests/cli/seagate/meson.build | 5 ++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/cli/micron/meson.build b/tests/cli/micron/meson.build index 131260323e..e86a55cd98 100644 --- a/tests/cli/micron/meson.build +++ b/tests/cli/micron/meson.build @@ -25,20 +25,20 @@ micron_mock_tests = [ if json_c_dep.found() if have_mock_nvme foreach t : micron_mock_tests - test('nvme-cli - @0@'.format(t.split('.')[0]), + name = t.split('.')[0].replace('micron_', '') + name = name.replace('_mock_test', '').replace('_', '-') + test('micron - ' + name, python3, args: [files(t), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib], - suite: ['micron', 'micron-cli'], timeout: 120) endforeach endif - test('nvme-cli - micron-plugin-version', + test('micron - version', python3, args: [files('micron_plugin_version_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe, - suite: ['micron', 'micron-cli']) + depends: nvme_exe) endif diff --git a/tests/cli/ocp/meson.build b/tests/cli/ocp/meson.build index 4ba35f0ef6..526e4f0bb3 100644 --- a/tests/cli/ocp/meson.build +++ b/tests/cli/ocp/meson.build @@ -23,12 +23,13 @@ endif if have_mock_nvme foreach t : ocp_mock_tests - test('nvme-cli - @0@'.format(t.split('.')[0]), + name = t.split('.')[0].replace('ocp_', '') + name = name.replace('_mock_test', '').replace('_', '-') + test('ocp - ' + name, python3, args: [files(t), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib], - suite: ['ocp', 'ocp-cli'], timeout: 120) endforeach endif diff --git a/tests/cli/samsung/meson.build b/tests/cli/samsung/meson.build index eccf5305d5..7670e072fc 100644 --- a/tests/cli/samsung/meson.build +++ b/tests/cli/samsung/meson.build @@ -7,19 +7,17 @@ # Hardware-free Samsung plugin tests. if have_mock_nvme - test('nvme-cli - samsung-cli', + test('samsung - vs-internal-log', python3, args: [files('nvme_samsung_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib], - suite: ['samsung', 'samsung-cli']) + depends: [nvme_exe, mock_nvme_lib]) if want_mi - test('nvme-cli - samsung-mctp-cli', + test('samsung - vs-internal-log-mctp', python3, args: [files('nvme_samsung_mctp_test.py'), nvme_exe, mock_mctp_lib], env: cli_test_env, - depends: [nvme_exe, mock_mctp_lib], - suite: ['samsung', 'samsung-cli']) + depends: [nvme_exe, mock_mctp_lib]) endif endif diff --git a/tests/cli/seagate/meson.build b/tests/cli/seagate/meson.build index 54f71a6531..f79e5d3198 100644 --- a/tests/cli/seagate/meson.build +++ b/tests/cli/seagate/meson.build @@ -3,11 +3,10 @@ # Hardware-free Seagate plugin tests. if have_mock_nvme and json_c_dep.found() - test('nvme-cli - seagate-vs-smart-add-log-cli', + test('seagate - vs-smart-add-log', python3, args: [files('seagate_vs_smart_add_log_mock_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib], - suite: ['seagate', 'seagate-cli']) + depends: [nvme_exe, mock_nvme_lib]) endif From f48e2a25b26ac1e29ad299b463a8318bb2d9e1c0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:17 +0000 Subject: [PATCH 240/263] tests/fuzz: name the fabrics-conf target after libnvme The fabrics-conf target fuzzes the libnvme config parser, but its test is named after nvme-cli. Let a target name its component, and name the fabrics-conf test libnvme - fuzz-fabrics-conf. Signed-off-by: Daniel Wagner --- tests/fuzz/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 541f10c14d..4d387be36e 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -39,6 +39,7 @@ fuzz_targets = { if want_fabrics and want_libnvme fuzz_targets += { 'fabrics-conf': { + 'component': 'libnvme', 'sources': ['fuzz-fabrics-conf.c'], 'deps': [config_dep, ccan_dep, shared_dep, libnvme_test_dep], }, @@ -81,7 +82,7 @@ foreach name, t : fuzz_targets link_args: t.get('link_args', []), ) test( - 'nvme-cli - fuzz-' + name, + t.get('component', 'nvme-cli') + ' - fuzz-' + name, exe, args: [meson.current_source_dir() / 'corpus' / name], workdir: meson.current_build_dir(), From c132286bc458f991d7429f9aa091e509dacdb2b8 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:19 +0000 Subject: [PATCH 241/263] discoverd, keysd: use kebab-case test names Some daemon test names contain spaces or the option they test, e.g. 'nvme-keysd - missing --config'. Use kebab-case like the other tests: nvme-keysd - --help -> nvme-keysd - help nvme-keysd - unknown option -> nvme-keysd - unknown-option nvme-keysd - missing --config -> nvme-keysd - missing-config nvme-keysd - kmip tls -> nvme-keysd - kmip-tls Signed-off-by: Daniel Wagner --- discoverd/tests/meson.build | 8 ++++---- keysd/tests/meson.build | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/discoverd/tests/meson.build b/discoverd/tests/meson.build index dcd8b012d8..9fbe4f052e 100644 --- a/discoverd/tests/meson.build +++ b/discoverd/tests/meson.build @@ -78,11 +78,11 @@ test_inventory = executable( test('nvme-discoverd - inventory', test_inventory) -test('nvme-discoverd - --help', nvme_discoverd, args: ['--help']) -test('nvme-discoverd - unknown option', nvme_discoverd, +test('nvme-discoverd - help', nvme_discoverd, args: ['--help']) +test('nvme-discoverd - unknown-option', nvme_discoverd, args: ['--bogus'], should_fail: true) -test('nvme-discoverd - missing --nvme-path', nvme_discoverd, +test('nvme-discoverd - missing-nvme-path', nvme_discoverd, args: ['--nvme-path', '/nonexistent/nvme'], should_fail: true) -test('nvme-discoverd - missing --config', nvme_discoverd, +test('nvme-discoverd - missing-config', nvme_discoverd, args: ['--config', '/nonexistent/nvme-discoverd.conf'], should_fail: true) diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index 142aa55bbf..109d91f7c6 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -39,7 +39,7 @@ test_kmip_tls = executable( ], ) -test('nvme-keysd - kmip tls', test_kmip_tls) +test('nvme-keysd - kmip-tls', test_kmip_tls) if want_kmip test_kmip_psk = executable( @@ -64,7 +64,7 @@ if want_kmip ], ) - test('nvme-keysd - kmip psk', test_kmip_psk) + test('nvme-keysd - kmip-psk', test_kmip_psk) test_kmip_client = executable( 'test-keysd-kmip-client', @@ -78,21 +78,21 @@ if want_kmip ], ) - test('nvme-keysd - kmip client', test_kmip_client) + test('nvme-keysd - kmip-client', test_kmip_client) - test('nvme-keysd-kmip - --help', nvme_keysd_kmip, args: ['--help']) - test('nvme-keysd-kmip - not socket activated', nvme_keysd_kmip, + test('nvme-keysd-kmip - help', nvme_keysd_kmip, args: ['--help']) + test('nvme-keysd-kmip - not-socket-activated', nvme_keysd_kmip, should_fail: true) endif -test('nvme-keysd - --help', nvme_keysd, args: ['--help']) -test('nvme-keysd - unknown option', nvme_keysd, +test('nvme-keysd - help', nvme_keysd, args: ['--help']) +test('nvme-keysd - unknown-option', nvme_keysd, args: ['--bogus'], should_fail: true) -test('nvme-keysd - missing --config', nvme_keysd, +test('nvme-keysd - missing-config', nvme_keysd, args: ['--config', '/nonexistent/nvme-keysd.conf'], should_fail: true) -test('nvme-keysd - missing --creds-dir', nvme_keysd, +test('nvme-keysd - missing-creds-dir', nvme_keysd, args: ['--creds-dir', '/nonexistent/creds'], should_fail: true) -test('nvme-keysd - missing --fabrics-config', nvme_keysd, +test('nvme-keysd - missing-fabrics-config', nvme_keysd, args: ['--fabrics-config', '/nonexistent/config.ini'], should_fail: true) From 26392ef7051b0686b0ae3c171ee2bf0c5114e4e0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:21 +0000 Subject: [PATCH 242/263] libnvme: group the NBFT and Python binding tests The NBFT tests are named after their table file, e.g. libnvme - NBFT-rhpoc, the Python binding tests start with python-. Group them with a / like the header tests: libnvme - nbft/rhpoc and libnvme - python/config. Signed-off-by: Daniel Wagner --- libnvme/libnvme3/meson.build | 4 ++-- libnvme/tests/nbft/meson.build | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libnvme/libnvme3/meson.build b/libnvme/libnvme3/meson.build index 2104fce4b9..124b8acbc5 100644 --- a/libnvme/libnvme3/meson.build +++ b/libnvme/libnvme3/meson.build @@ -89,7 +89,7 @@ if want_python test_env.set('PYTHONMALLOC', 'malloc') # Test section - test('libnvme - python-import-libnvme', python3, args: ['-c', 'from libnvme3 import nvme'], env: test_env, depends: pynvme_clib) + test('libnvme - python/import-libnvme', python3, args: ['-c', 'from libnvme3 import nvme'], env: test_env, depends: pynvme_clib) py_tests = [ [ 'create-ctrl-object', [ files('tests/create-ctrl-obj.py'), ] ], @@ -104,6 +104,6 @@ if want_python foreach test: py_tests description = test[0] args = test[1] - test('libnvme - python-' + description, python3, args: args, env: test_env, depends: pynvme_clib) + test('libnvme - python/' + description, python3, args: args, env: test_env, depends: pynvme_clib) endforeach endif diff --git a/libnvme/tests/nbft/meson.build b/libnvme/tests/nbft/meson.build index 27a0e056f5..6f44f3ae37 100644 --- a/libnvme/tests/nbft/meson.build +++ b/libnvme/tests/nbft/meson.build @@ -85,7 +85,7 @@ diffcmd = find_program( if diffcmd.found() foreach table: tables test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), dump_helper, args: [ files(tables_dir/table), @@ -98,7 +98,7 @@ endif foreach table: tables_bad if meson.version().version_compare('>=1.11.0') test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), nbft_dump, args: [ files(tables_bad_dir/table), @@ -107,7 +107,7 @@ foreach table: tables_bad ) else test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), nbft_dump, args: [ files(tables_bad_dir/table), @@ -119,7 +119,7 @@ endforeach foreach table: tables_rejected test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), files('nbft-reject.sh'), args: [ nbft_dump, From 5e1b29c1abcd097a6718d78a5fd8792d55375eef Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:22 +0000 Subject: [PATCH 243/263] tests/e2e: prefix the e2e tests and put them in an e2e suite The e2e test names are the file names, e.g. nvme-cli - nvme_id_ctrl_test, and the plugin e2e tests are in a suite of their plugin. A mock test and an e2e test of the same command cannot be told apart by their name once the file name suffixes are gone. Name the e2e tests e2e-, e.g. nvme-cli - e2e-id-ctrl and micron - e2e-id-ctrl, and put all of them in the e2e suite. Update the README of the plugin e2e tests to select them with it. Signed-off-by: Daniel Wagner --- tests/e2e/meson.build | 4 +++- tests/e2e/plugins/README.md | 12 +++++++++--- tests/e2e/plugins/micron/meson.build | 5 +++-- tests/e2e/plugins/ocp/meson.build | 5 +++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/e2e/meson.build b/tests/e2e/meson.build index b41b96c24a..fe9a1b104b 100644 --- a/tests/e2e/meson.build +++ b/tests/e2e/meson.build @@ -80,8 +80,9 @@ test_env.prepend('PATH', meson.project_build_root()) foreach t : tests t_name = t.split('.')[0] + name = t_name.replace('nvme_', '').replace('_test', '').replace('_', '-') test( - 'nvme-cli - @0@'.format(t_name), + 'nvme-cli - e2e-' + name, python3, args: [ e2e_runner, @@ -93,6 +94,7 @@ foreach t : tests timeout: 500, protocol: 'tap', is_parallel: false, + suite: 'e2e', ) endforeach diff --git a/tests/e2e/plugins/README.md b/tests/e2e/plugins/README.md index aab89d2963..30ea3342dc 100644 --- a/tests/e2e/plugins/README.md +++ b/tests/e2e/plugins/README.md @@ -71,16 +71,22 @@ meson setup .build -De2e-tests=true -Dplugin-tests=ocp,micron \ meson test -C .build ``` -### Run a specific plugin suite +### Run only the e2e tests ```bash -meson test -C .build --suite ocp +meson test -C .build --suite e2e +``` + +### Run the e2e tests of one plugin + +```bash +meson test -C .build "ocp - e2e-*" ``` ### Run a single test by name ```bash -meson test -C .build "ocp - ocp_smart_add_log_test" +meson test -C .build "ocp - e2e-smart-add-log" ``` ## Writing a New Test diff --git a/tests/e2e/plugins/micron/meson.build b/tests/e2e/plugins/micron/meson.build index 63ff071965..e0aaa20e9d 100644 --- a/tests/e2e/plugins/micron/meson.build +++ b/tests/e2e/plugins/micron/meson.build @@ -26,8 +26,9 @@ test_env.prepend('PATH', meson.project_build_root()) foreach t : micron_tests t_name = t.split('.')[0] + name = t_name.replace('micron_', '').replace('_test', '').replace('_', '-') test( - 'micron - @0@'.format(t_name), + 'micron - e2e-' + name, python3, args: [ e2e_runner, @@ -39,6 +40,6 @@ foreach t : micron_tests timeout: 500, protocol: 'tap', is_parallel: false, - suite: 'micron', + suite: 'e2e', ) endforeach diff --git a/tests/e2e/plugins/ocp/meson.build b/tests/e2e/plugins/ocp/meson.build index ddb852c3ea..2decbafaf7 100644 --- a/tests/e2e/plugins/ocp/meson.build +++ b/tests/e2e/plugins/ocp/meson.build @@ -12,8 +12,9 @@ test_env.prepend('PATH', meson.project_build_root()) foreach t : ocp_tests t_name = t.split('.')[0] + name = t_name.replace('ocp_', '').replace('_test', '').replace('_', '-') test( - 'ocp - @0@'.format(t_name), + 'ocp - e2e-' + name, python3, args: [ e2e_runner, @@ -25,6 +26,6 @@ foreach t : ocp_tests timeout: 500, protocol: 'tap', is_parallel: false, - suite: 'ocp', + suite: 'e2e', ) endforeach From 01892bbc8b729386a784c881d28d3d2130dbca7d Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:31 +0000 Subject: [PATCH 244/263] build: put every test in exactly one group suite Only the fuzz and e2e tests are in a suite, so there is no way to run, e.g., just the fast tests which need neither a device nor a mock. Put every test in exactly one group, which says how the test runs: unit a test program for library or internal code mock the nvme binary or a daemon, with a mock where it needs a device e2e the nvme binary against a real device fuzz a fuzz target over its corpus check a consistency check of the tree: headers, symbols, docs, install tags, generated completions meson test --suite unit --suite mock then runs all behaviour tests, and meson test prints the group in front of every test. Signed-off-by: Daniel Wagner --- completions/meson.build | 3 ++ daemon-util/tests/meson.build | 4 +- discoverd/tests/meson.build | 21 ++++++----- keysd/tests/meson.build | 31 +++++++++------- libnvme/doc/meson.build | 2 +- libnvme/libnvme3/meson.build | 4 +- libnvme/tests/ioctl/meson.build | 18 ++++----- libnvme/tests/meson.build | 48 ++++++++++++------------ libnvme/tests/nbft/meson.build | 4 ++ libnvme/tests/privsep/meson.build | 2 + libnvme/tests/sysfs/meson.build | 2 + meson.build | 1 + shared/tests/meson.build | 62 +++++++++++++++---------------- tests/cli/meson.build | 45 ++++++++++++++-------- tests/cli/micron/meson.build | 6 ++- tests/cli/ocp/meson.build | 3 +- tests/cli/samsung/meson.build | 6 ++- tests/cli/seagate/meson.build | 3 +- tests/unit/c/meson.build | 8 ++-- tests/unit/py/meson.build | 9 +++++ 20 files changed, 167 insertions(+), 115 deletions(-) diff --git a/completions/meson.build b/completions/meson.build index 3072747400..f32b004fd6 100644 --- a/completions/meson.build +++ b/completions/meson.build @@ -72,6 +72,7 @@ if want_tests and _bash.found() and _py3.found() args: [files('test-bash-completion.sh')], env: {'PYTHON3': _py3.full_path()}, timeout: 120, + suite: 'mock', ) endif @@ -83,6 +84,7 @@ if want_tests and _zsh.found() and _py3.found() args: [files('test-zsh-completion.sh')], env: {'PYTHON3': _py3.full_path()}, timeout: 120, + suite: 'mock', ) endif @@ -93,5 +95,6 @@ if want_tests and _pwsh.found() _pwsh, args: ['-File', files('test-powershell-completion.ps1')], timeout: 120, + suite: 'mock', ) endif diff --git a/daemon-util/tests/meson.build b/daemon-util/tests/meson.build index 345fa4dc63..191e8bcd75 100644 --- a/daemon-util/tests/meson.build +++ b/daemon-util/tests/meson.build @@ -14,7 +14,7 @@ test_resolve = executable( ], ) -test('daemon-util - resolve', test_resolve) +test('daemon-util - resolve', test_resolve, suite: 'unit') test_work = executable( 'test-daemon-util-work', @@ -25,4 +25,4 @@ test_work = executable( ], ) -test('daemon-util - work', test_work) +test('daemon-util - work', test_work, suite: 'unit') diff --git a/discoverd/tests/meson.build b/discoverd/tests/meson.build index 9fbe4f052e..32c052b1df 100644 --- a/discoverd/tests/meson.build +++ b/discoverd/tests/meson.build @@ -13,7 +13,7 @@ test_dlp = executable( ], ) -test('nvme-discoverd - dlp', test_dlp) +test('nvme-discoverd - dlp', test_dlp, suite: 'unit') test_tid = executable( 'test-tid', @@ -27,7 +27,7 @@ test_tid = executable( ], ) -test('nvme-discoverd - tid', test_tid) +test('nvme-discoverd - tid', test_tid, suite: 'unit') test_config = executable( 'test-config', @@ -42,7 +42,7 @@ test_config = executable( ], ) -test('nvme-discoverd - config', test_config) +test('nvme-discoverd - config', test_config, suite: 'unit') test_netif = executable( 'test-netif', @@ -57,7 +57,7 @@ test_netif = executable( ], ) -test('nvme-discoverd - netif', test_netif) +test('nvme-discoverd - netif', test_netif, suite: 'unit') test_inventory = executable( 'test-inventory', @@ -76,13 +76,16 @@ test_inventory = executable( ], ) -test('nvme-discoverd - inventory', test_inventory) +test('nvme-discoverd - inventory', test_inventory, suite: 'unit') -test('nvme-discoverd - help', nvme_discoverd, args: ['--help']) +test('nvme-discoverd - help', nvme_discoverd, args: ['--help'], suite: 'mock') test('nvme-discoverd - unknown-option', nvme_discoverd, - args: ['--bogus'], should_fail: true) + args: ['--bogus'], should_fail: true, + suite: 'mock') test('nvme-discoverd - missing-nvme-path', nvme_discoverd, - args: ['--nvme-path', '/nonexistent/nvme'], should_fail: true) + args: ['--nvme-path', '/nonexistent/nvme'], should_fail: true, + suite: 'mock') test('nvme-discoverd - missing-config', nvme_discoverd, args: ['--config', '/nonexistent/nvme-discoverd.conf'], - should_fail: true) + should_fail: true, + suite: 'mock') diff --git a/keysd/tests/meson.build b/keysd/tests/meson.build index 109d91f7c6..d7e479408c 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -11,7 +11,7 @@ test_config = executable( ], ) -test('nvme-keysd - config', test_config) +test('nvme-keysd - config', test_config, suite: 'unit') test_ttlv = executable( 'test-keysd-ttlv', @@ -19,7 +19,7 @@ test_ttlv = executable( include_directories: keysd_inc, ) -test('nvme-keysd - ttlv', test_ttlv) +test('nvme-keysd - ttlv', test_ttlv, suite: 'unit') test_kmip = executable( 'test-keysd-kmip', @@ -27,7 +27,7 @@ test_kmip = executable( include_directories: keysd_inc, ) -test('nvme-keysd - kmip', test_kmip) +test('nvme-keysd - kmip', test_kmip, suite: 'unit') test_kmip_tls = executable( 'test-keysd-kmip-tls', @@ -39,7 +39,7 @@ test_kmip_tls = executable( ], ) -test('nvme-keysd - kmip-tls', test_kmip_tls) +test('nvme-keysd - kmip-tls', test_kmip_tls, suite: 'unit') if want_kmip test_kmip_psk = executable( @@ -64,7 +64,7 @@ if want_kmip ], ) - test('nvme-keysd - kmip-psk', test_kmip_psk) + test('nvme-keysd - kmip-psk', test_kmip_psk, suite: 'unit') test_kmip_client = executable( 'test-keysd-kmip-client', @@ -78,21 +78,26 @@ if want_kmip ], ) - test('nvme-keysd - kmip-client', test_kmip_client) + test('nvme-keysd - kmip-client', test_kmip_client, suite: 'unit') - test('nvme-keysd-kmip - help', nvme_keysd_kmip, args: ['--help']) + test('nvme-keysd-kmip - help', nvme_keysd_kmip, args: ['--help'], suite: 'mock') test('nvme-keysd-kmip - not-socket-activated', nvme_keysd_kmip, - should_fail: true) + should_fail: true, + suite: 'mock') endif -test('nvme-keysd - help', nvme_keysd, args: ['--help']) +test('nvme-keysd - help', nvme_keysd, args: ['--help'], suite: 'mock') test('nvme-keysd - unknown-option', nvme_keysd, - args: ['--bogus'], should_fail: true) + args: ['--bogus'], should_fail: true, + suite: 'mock') test('nvme-keysd - missing-config', nvme_keysd, args: ['--config', '/nonexistent/nvme-keysd.conf'], - should_fail: true) + should_fail: true, + suite: 'mock') test('nvme-keysd - missing-creds-dir', nvme_keysd, - args: ['--creds-dir', '/nonexistent/creds'], should_fail: true) + args: ['--creds-dir', '/nonexistent/creds'], should_fail: true, + suite: 'mock') test('nvme-keysd - missing-fabrics-config', nvme_keysd, args: ['--fabrics-config', '/nonexistent/config.ini'], - should_fail: true) + should_fail: true, + suite: 'mock') diff --git a/libnvme/doc/meson.build b/libnvme/doc/meson.build index 641298e857..c6e663b745 100644 --- a/libnvme/doc/meson.build +++ b/libnvme/doc/meson.build @@ -113,7 +113,7 @@ kernel_doc_check = files(top_source_dir + 'scripts/kernel-doc-check') bash_prog = find_program('bash', required: false) if bash_prog.found() - test('libnvme - kdoc', bash_prog, args: [kernel_doc_check] + api_paths + sed_api_paths) + test('libnvme - kdoc', bash_prog, args: [kernel_doc_check] + api_paths + sed_api_paths, suite: 'check') endif if want_docs != 'false' diff --git a/libnvme/libnvme3/meson.build b/libnvme/libnvme3/meson.build index 124b8acbc5..38112d8c08 100644 --- a/libnvme/libnvme3/meson.build +++ b/libnvme/libnvme3/meson.build @@ -89,7 +89,7 @@ if want_python test_env.set('PYTHONMALLOC', 'malloc') # Test section - test('libnvme - python/import-libnvme', python3, args: ['-c', 'from libnvme3 import nvme'], env: test_env, depends: pynvme_clib) + test('libnvme - python/import-libnvme', python3, args: ['-c', 'from libnvme3 import nvme'], env: test_env, depends: pynvme_clib, suite: 'unit') py_tests = [ [ 'create-ctrl-object', [ files('tests/create-ctrl-obj.py'), ] ], @@ -104,6 +104,6 @@ if want_python foreach test: py_tests description = test[0] args = test[1] - test('libnvme - python/' + description, python3, args: args, env: test_env, depends: pynvme_clib) + test('libnvme - python/' + description, python3, args: args, env: test_env, depends: pynvme_clib, suite: 'unit') endforeach endif diff --git a/libnvme/tests/ioctl/meson.build b/libnvme/tests/ioctl/meson.build index 1785cb14b5..a84a107b6c 100644 --- a/libnvme/tests/ioctl/meson.build +++ b/libnvme/tests/ioctl/meson.build @@ -14,7 +14,7 @@ ana = executable( libnvme_test_dep, ], ) -test('libnvme - ana', ana) +test('libnvme - ana', ana, suite: 'unit') async = executable( 'test-async', @@ -25,7 +25,7 @@ async = executable( libnvme_test_dep, ], ) -test('libnvme - async', async) +test('libnvme - async', async, suite: 'unit') if want_fabrics discovery = executable( @@ -37,7 +37,7 @@ if want_fabrics libnvme_test_dep, ], ) - test('libnvme - discovery', discovery) + test('libnvme - discovery', discovery, suite: 'unit') endif features = executable( @@ -49,7 +49,7 @@ features = executable( libnvme_test_dep, ], ) -test('libnvme - features', features) +test('libnvme - features', features, suite: 'unit') identify = executable( 'test-identify', @@ -60,7 +60,7 @@ identify = executable( libnvme_test_dep, ], ) -test('libnvme - identify', identify) +test('libnvme - identify', identify, suite: 'unit') logs = executable( 'test-logs', @@ -71,7 +71,7 @@ logs = executable( libnvme_test_dep, ], ) -test('libnvme - logs', logs) +test('libnvme - logs', logs, suite: 'unit') zns = executable( 'test-zns', @@ -82,7 +82,7 @@ zns = executable( libnvme_test_dep, ], ) -test('libnvme - zns', zns) +test('libnvme - zns', zns, suite: 'unit') misc = executable( 'test-misc', @@ -93,7 +93,7 @@ misc = executable( libnvme_test_dep, ], ) -test('libnvme - misc', misc) +test('libnvme - misc', misc, suite: 'unit') if want_sed sed = executable( @@ -105,5 +105,5 @@ if want_sed libnvme_test_dep, ], ) - test('libnvme - sed', sed) + test('libnvme - sed', sed, suite: 'unit') endif diff --git a/libnvme/tests/meson.build b/libnvme/tests/meson.build index cc0fc9cb08..d21bac3a35 100644 --- a/libnvme/tests/meson.build +++ b/libnvme/tests/meson.build @@ -14,6 +14,7 @@ if python3_prog.found() files('../tools/check-public-symbols.py'), meson.project_source_root() / 'libnvme', ], + suite: 'check', ) _ld_args = ['--ld', nvme_ld, '--ld', accessors_ld, '--ld', attr_accessors_ld] if want_fabrics @@ -30,6 +31,7 @@ if python3_prog.found() 'libnvme - check-public-headers', python3_prog, args: [files('../tools/check-public-headers.py')] + _ld_args + _hdr_args, + suite: 'check', ) endif @@ -58,7 +60,7 @@ if cxx_available ], ) - test('libnvme - cpp-dump', cpp) + test('libnvme - cpp-dump', cpp, suite: 'unit') misc = executable( 'test-misc', @@ -69,7 +71,7 @@ if cxx_available libnvme_dep, ], ) - test('libnvme - cpp-misc', misc) + test('libnvme - cpp-misc', misc, suite: 'unit') endif @@ -106,7 +108,7 @@ if want_mi ], ) - test('libnvme - mi', mi) + test('libnvme - mi', mi, suite: 'unit') mi_mctp = executable( 'test-mi-mctp', @@ -118,7 +120,7 @@ if want_mi ], ) - test('libnvme - mi-mctp', mi_mctp) + test('libnvme - mi-mctp', mi_mctp, suite: 'unit') endif uuid = executable( @@ -131,7 +133,7 @@ uuid = executable( ], ) -test('libnvme - uuid', uuid) +test('libnvme - uuid', uuid, suite: 'unit') if host_system == 'linux' and (not is_static) mem = executable( @@ -145,7 +147,7 @@ if host_system == 'linux' and (not is_static) ], ) - test('libnvme - mem', mem) + test('libnvme - mem', mem, suite: 'unit') endif tree = executable( @@ -158,7 +160,7 @@ tree = executable( ], ) -test('libnvme - tree', tree) +test('libnvme - tree', tree, suite: 'unit') if want_fabrics test_tid = executable( @@ -171,7 +173,7 @@ if want_fabrics ], ) - test('libnvme - tid', test_tid) + test('libnvme - tid', test_tid, suite: 'unit') registry = executable( 'test-registry', @@ -183,7 +185,7 @@ if want_fabrics ], ) - test('libnvme - registry', registry) + test('libnvme - registry', registry, suite: 'unit') exclusion = executable( 'test-exclusion', @@ -195,7 +197,7 @@ if want_fabrics ], ) - test('libnvme - exclusion', exclusion) + test('libnvme - exclusion', exclusion, suite: 'unit') config_ini = executable( 'test-config-ini', @@ -208,7 +210,7 @@ if want_fabrics ], ) - test('libnvme - config-ini', config_ini) + test('libnvme - config-ini', config_ini, suite: 'unit') config_api = executable( 'test-config-api', @@ -220,7 +222,7 @@ if want_fabrics ], ) - test('libnvme - config-api', config_api) + test('libnvme - config-api', config_api, suite: 'unit') config_emit = executable( 'test-config-emit', @@ -232,7 +234,7 @@ if want_fabrics ], ) - test('libnvme - config-emit', config_emit) + test('libnvme - config-emit', config_emit, suite: 'unit') uriparser = executable( 'test-uriparser', @@ -244,7 +246,7 @@ if want_fabrics ], ) - test('libnvme - uriparser', uriparser) + test('libnvme - uriparser', uriparser, suite: 'unit') test_fabrics = executable( 'test-fabrics', @@ -268,7 +270,7 @@ if want_fabrics test_fabrics_env = environment() test_fabrics_env.set('ASAN_OPTIONS', 'detect_odr_violation=0') - test('libnvme - fabrics', test_fabrics, env: test_fabrics_env) + test('libnvme - fabrics', test_fabrics, env: test_fabrics_env, suite: 'unit') psk = executable( 'test-psk', @@ -280,7 +282,7 @@ if want_fabrics ], ) - test('libnvme - psk', psk) + test('libnvme - psk', psk, suite: 'unit') if openssl_dep.found() raw_secret = executable( @@ -293,7 +295,7 @@ if want_fabrics ], ) - test('libnvme - raw-secret', raw_secret) + test('libnvme - raw-secret', raw_secret, suite: 'unit') endif endif @@ -319,7 +321,7 @@ if want_fabrics link_with: mock_ifaddrs, ) - test('libnvme - tree-fabrics', tree_fabrics, env: mock_ifaddrs_env) + test('libnvme - tree-fabrics', tree_fabrics, env: mock_ifaddrs_env, suite: 'unit') test_util = executable( 'test-util', @@ -330,7 +332,7 @@ if want_fabrics libnvme_test_dep, ], ) - test('libnvme - util', test_util) + test('libnvme - util', test_util, suite: 'unit') endif if want_ioctl_tests @@ -353,7 +355,7 @@ if host_system == 'linux' libnvme_test_dep, ], ) - test('libnvme - regs', regs) + test('libnvme - regs', regs, suite: 'unit') if want_fabrics subdir('nbft') @@ -407,7 +409,7 @@ foreach hdr : [ src, dependencies: [libnvme_dep], ) - test('libnvme - header/' + hdr, exe) + test('libnvme - header/' + hdr, exe, suite: 'check') endforeach if want_sed @@ -428,7 +430,7 @@ if want_sed src, dependencies: [libnvme_dep], ) - test('libnvme - header/sed/' + hdr, exe) + test('libnvme - header/sed/' + hdr, exe, suite: 'check') endforeach endif @@ -446,5 +448,5 @@ if want_privsep src, dependencies: [libnvme_dep], ) - test('libnvme - header/privsep', exe) + test('libnvme - header/privsep', exe, suite: 'check') endif diff --git a/libnvme/tests/nbft/meson.build b/libnvme/tests/nbft/meson.build index 6f44f3ae37..26e5a8ef1b 100644 --- a/libnvme/tests/nbft/meson.build +++ b/libnvme/tests/nbft/meson.build @@ -91,6 +91,7 @@ if diffcmd.found() files(tables_dir/table), files(diff_dir/table), ], + suite: 'unit', ) endforeach endif @@ -104,6 +105,7 @@ foreach table: tables_bad files(tables_bad_dir/table), ], expected_fail: true, + suite: 'unit', ) else test( @@ -113,6 +115,7 @@ foreach table: tables_bad files(tables_bad_dir/table), ], should_fail: true, + suite: 'unit', ) endif endforeach @@ -125,5 +128,6 @@ foreach table: tables_rejected nbft_dump, files(tables_bad_dir/table), ], + suite: 'unit', ) endforeach diff --git a/libnvme/tests/privsep/meson.build b/libnvme/tests/privsep/meson.build index 3691fdb366..d64893a2e6 100644 --- a/libnvme/tests/privsep/meson.build +++ b/libnvme/tests/privsep/meson.build @@ -43,6 +43,7 @@ test( args: [privsep_helper.full_path(), mock_ioctl.full_path()], depends: [privsep_helper, mock_ioctl], env: privsep_env, + suite: 'unit', ) privsep_proto_test = executable( @@ -63,4 +64,5 @@ test( args: [privsep_helper.full_path(), mock_ioctl.full_path()], depends: [privsep_helper, mock_ioctl], env: privsep_env, + suite: 'unit', ) diff --git a/libnvme/tests/sysfs/meson.build b/libnvme/tests/sysfs/meson.build index 3ac154eda7..57749ce22e 100644 --- a/libnvme/tests/sysfs/meson.build +++ b/libnvme/tests/sysfs/meson.build @@ -34,6 +34,7 @@ if diff.found() and bash_prog.found() files('data'/t_file + '.out'), ], depends : tree_scan, + suite: 'unit', ) endforeach @@ -56,6 +57,7 @@ if diff.found() and bash_prog.found() files('data'/t_file + '-hostnqn.out'), ], depends : tree_scan_hostnqn, + suite: 'unit', ) endforeach endif diff --git a/meson.build b/meson.build index fc7c1b509e..ea30d51ab5 100644 --- a/meson.build +++ b/meson.build @@ -930,6 +930,7 @@ if want_tests and py3_prog.found() files('scripts/check-install-tags.py'), meson.project_build_root(), ], + suite: 'check', ) endif diff --git a/shared/tests/meson.build b/shared/tests/meson.build index de2ae0ca19..1558cbf26b 100644 --- a/shared/tests/meson.build +++ b/shared/tests/meson.build @@ -10,7 +10,7 @@ test_array_util = executable( ], ) -test('shared - array-util', test_array_util) +test('shared - array-util', test_array_util, suite: 'unit') test_base64_util = executable( 'test-base64-util', @@ -22,7 +22,7 @@ test_base64_util = executable( ], ) -test('shared - base64-util', test_base64_util) +test('shared - base64-util', test_base64_util, suite: 'unit') test_crc32_util = executable( 'test-crc32-util', @@ -33,7 +33,7 @@ test_crc32_util = executable( ], ) -test('shared - crc32-util', test_crc32_util) +test('shared - crc32-util', test_crc32_util, suite: 'unit') test_nqn_util = executable( 'test-nqn-util', @@ -44,7 +44,7 @@ test_nqn_util = executable( ], ) -test('shared - nqn-util', test_nqn_util) +test('shared - nqn-util', test_nqn_util, suite: 'unit') test_ini_util = executable( 'test-ini-util', @@ -55,7 +55,7 @@ test_ini_util = executable( ], ) -test('shared - ini-util', test_ini_util) +test('shared - ini-util', test_ini_util, suite: 'unit') test_parse_util = executable( 'test-parse-util', @@ -67,7 +67,7 @@ test_parse_util = executable( ], ) -test('shared - parse-util', test_parse_util) +test('shared - parse-util', test_parse_util, suite: 'unit') test_hex_util = executable( 'test-hex-util', @@ -78,7 +78,7 @@ test_hex_util = executable( ], ) -test('shared - hex-util', test_hex_util) +test('shared - hex-util', test_hex_util, suite: 'unit') test_cleanup_util = executable( 'test-cleanup-util', @@ -89,7 +89,7 @@ test_cleanup_util = executable( ], ) -test('shared - cleanup-util', test_cleanup_util) +test('shared - cleanup-util', test_cleanup_util, suite: 'unit') test_wrap_util = executable( 'test-wrap-util', @@ -100,7 +100,7 @@ test_wrap_util = executable( ], ) -test('shared - wrap-util', test_wrap_util) +test('shared - wrap-util', test_wrap_util, suite: 'unit') test_temp_util = executable( 'test-temp-util', @@ -111,7 +111,7 @@ test_temp_util = executable( ], ) -test('shared - temp-util', test_temp_util) +test('shared - temp-util', test_temp_util, suite: 'unit') test_time_util = executable( 'test-time-util', @@ -122,7 +122,7 @@ test_time_util = executable( ], ) -test('shared - time-util', test_time_util) +test('shared - time-util', test_time_util, suite: 'unit') test_progress_util = executable( 'test-progress-util', @@ -133,7 +133,7 @@ test_progress_util = executable( ], ) -test('shared - progress-util', test_progress_util) +test('shared - progress-util', test_progress_util, suite: 'unit') test_table_util = executable( 'test-table-util', @@ -145,7 +145,7 @@ test_table_util = executable( ], ) -test('shared - table-util', test_table_util) +test('shared - table-util', test_table_util, suite: 'unit') test_suffix_util = executable( 'test-suffix-util', @@ -157,7 +157,7 @@ test_suffix_util = executable( ], ) -test('shared - suffix-util', test_suffix_util) +test('shared - suffix-util', test_suffix_util, suite: 'unit') test_uint128_util = executable( 'test-uint128-util', @@ -169,7 +169,7 @@ test_uint128_util = executable( ], ) -test('shared - uint128-util', test_uint128_util) +test('shared - uint128-util', test_uint128_util, suite: 'unit') test_int_util = executable( 'test-int-util', @@ -180,7 +180,7 @@ test_int_util = executable( ], ) -test('shared - int-util', test_int_util) +test('shared - int-util', test_int_util, suite: 'unit') test_uuid_util = executable( 'test-uuid-util', @@ -191,7 +191,7 @@ test_uuid_util = executable( ], ) -test('shared - uuid-util', test_uuid_util) +test('shared - uuid-util', test_uuid_util, suite: 'unit') test_hash_util = executable( 'test-hash-util', @@ -202,7 +202,7 @@ test_hash_util = executable( ], ) -test('shared - hash-util', test_hash_util) +test('shared - hash-util', test_hash_util, suite: 'unit') test_string_util = executable( 'test-string-util', @@ -213,7 +213,7 @@ test_string_util = executable( ], ) -test('shared - string-util', test_string_util) +test('shared - string-util', test_string_util, suite: 'unit') test_io_util = executable( 'test-io-util', @@ -224,7 +224,7 @@ test_io_util = executable( ], ) -test('shared - io-util', test_io_util) +test('shared - io-util', test_io_util, suite: 'unit') test_fs_util = executable( 'test-fs-util', @@ -235,7 +235,7 @@ test_fs_util = executable( ], ) -test('shared - fs-util', test_fs_util) +test('shared - fs-util', test_fs_util, suite: 'unit') test_mmio_util = executable( 'test-mmio-util', @@ -247,7 +247,7 @@ test_mmio_util = executable( ], ) -test('shared - mmio-util', test_mmio_util) +test('shared - mmio-util', test_mmio_util, suite: 'unit') test_unaligned_util = executable( 'test-unaligned-util', @@ -259,7 +259,7 @@ test_unaligned_util = executable( ], ) -test('shared - unaligned-util', test_unaligned_util) +test('shared - unaligned-util', test_unaligned_util, suite: 'unit') if host_system != 'windows' test_net_util = executable( @@ -271,7 +271,7 @@ if host_system != 'windows' ], ) - test('shared - net-util', test_net_util) + test('shared - net-util', test_net_util, suite: 'unit') test_pci_util = executable( 'test-pci-util', @@ -282,7 +282,7 @@ if host_system != 'windows' ], ) - test('shared - pci-util', test_pci_util) + test('shared - pci-util', test_pci_util, suite: 'unit') endif test_crypto_util_deps = [ @@ -299,7 +299,7 @@ test_crypto_util = executable( dependencies: test_crypto_util_deps, ) -test('shared - crypto-util', test_crypto_util) +test('shared - crypto-util', test_crypto_util, suite: 'unit') test_sig_util = executable( 'test-sig-util', @@ -310,7 +310,7 @@ test_sig_util = executable( ], ) -test('shared - sig-util', test_sig_util) +test('shared - sig-util', test_sig_util, suite: 'unit') test_assert_util = executable( 'test-assert-util', @@ -321,7 +321,7 @@ test_assert_util = executable( ], ) -test('shared - assert-util', test_assert_util) +test('shared - assert-util', test_assert_util, suite: 'unit') if host_system != 'windows' test_machine_id_util = executable( @@ -333,7 +333,7 @@ if host_system != 'windows' ], ) - test('shared - machine-id-util', test_machine_id_util) + test('shared - machine-id-util', test_machine_id_util, suite: 'unit') endif test_sha256_util = executable( @@ -345,7 +345,7 @@ test_sha256_util = executable( ], ) -test('shared - sha256-util', test_sha256_util) +test('shared - sha256-util', test_sha256_util, suite: 'unit') test_mem_util = executable( 'test-mem-util', @@ -356,4 +356,4 @@ test_mem_util = executable( ], ) -test('shared - mem-util', test_mem_util) +test('shared - mem-util', test_mem_util, suite: 'unit') diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 83279d6118..64ee17546b 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -15,7 +15,8 @@ if python3.found() python3, args: [files('nvme_registry_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for the "keys" plugin. Needs no device. Restricted @@ -26,7 +27,8 @@ if python3.found() python3, args: [files('nvme_keys_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for "nvme config-convert". Needs no device; uses @@ -37,7 +39,8 @@ if python3.found() python3, args: [files('nvme_config_convert_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for "nvme config-create". Needs no device; uses an @@ -48,7 +51,8 @@ if python3.found() python3, args: [files('nvme_config_create_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif if want_fabrics @@ -62,7 +66,8 @@ if python3.found() files('nbft/NBFT-unlinked-discovery'), ], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif have_mock_nvme = (not is_static) and (host_system == 'linux') @@ -91,46 +96,53 @@ if python3.found() python3, args: [files('nvme_fabrics_mock_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') endif test('nvme-cli - pif-sts', python3, args: [files('nvme_pif_sts_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') test('nvme-cli - init-pi-tags', python3, args: [files('nvme_init_pi_tags_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') if 'id' in selected_plugins test('nvme-cli - usage', python3, args: [files('nvme_usage_test.py'), nvme_exe], - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif test('nvme-cli - dry-run', python3, args: [files('nvme_dry_run_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') test('nvme-cli - sel', python3, args: [files('nvme_sel_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') test('nvme-cli - host-metadata', python3, args: [files('nvme_host_metadata_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') # The host and AVE discovery printers need the fabrics code. if want_fabrics and json_c_dep.found() and 'log' in selected_plugins @@ -139,7 +151,8 @@ if python3.found() args: [files('nvme_discovery_logs_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') endif if 'id' in selected_plugins and 'log' in selected_plugins and \ @@ -152,7 +165,8 @@ if python3.found() args: [files('nvme_print_test.py'), nvme_exe, mock_nvme_lib], env: print_test_env, depends: [nvme_exe, mock_nvme_lib], - timeout: 300) + timeout: 300, + suite: 'mock') endif if 'sed' in selected_plugins and want_sed and \ @@ -169,7 +183,8 @@ if python3.found() python3, args: [files('nvme_sed_test.py'), nvme_exe, mock_nvme_lib], env: sed_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') endif endif diff --git a/tests/cli/micron/meson.build b/tests/cli/micron/meson.build index e86a55cd98..beb9316a8e 100644 --- a/tests/cli/micron/meson.build +++ b/tests/cli/micron/meson.build @@ -32,7 +32,8 @@ if json_c_dep.found() args: [files(t), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib], - timeout: 120) + timeout: 120, + suite: 'mock') endforeach endif @@ -40,5 +41,6 @@ if json_c_dep.found() python3, args: [files('micron_plugin_version_test.py'), nvme_exe], env: cli_test_env, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif diff --git a/tests/cli/ocp/meson.build b/tests/cli/ocp/meson.build index 526e4f0bb3..7ec5dd41ab 100644 --- a/tests/cli/ocp/meson.build +++ b/tests/cli/ocp/meson.build @@ -30,6 +30,7 @@ if have_mock_nvme args: [files(t), nvme_exe, mock_nvme_lib], env: cli_test_env, depends: [nvme_exe, mock_nvme_lib], - timeout: 120) + timeout: 120, + suite: 'mock') endforeach endif diff --git a/tests/cli/samsung/meson.build b/tests/cli/samsung/meson.build index 7670e072fc..0400254741 100644 --- a/tests/cli/samsung/meson.build +++ b/tests/cli/samsung/meson.build @@ -11,13 +11,15 @@ if have_mock_nvme python3, args: [files('nvme_samsung_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') if want_mi test('samsung - vs-internal-log-mctp', python3, args: [files('nvme_samsung_mctp_test.py'), nvme_exe, mock_mctp_lib], env: cli_test_env, - depends: [nvme_exe, mock_mctp_lib]) + depends: [nvme_exe, mock_mctp_lib], + suite: 'mock') endif endif diff --git a/tests/cli/seagate/meson.build b/tests/cli/seagate/meson.build index f79e5d3198..5ab4c7b612 100644 --- a/tests/cli/seagate/meson.build +++ b/tests/cli/seagate/meson.build @@ -8,5 +8,6 @@ if have_mock_nvme and json_c_dep.found() args: [files('seagate_vs_smart_add_log_mock_test.py'), nvme_exe, mock_nvme_lib], env: cli_test_env, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') endif diff --git a/tests/unit/c/meson.build b/tests/unit/c/meson.build index 6a87e66339..f24de8f196 100644 --- a/tests/unit/c/meson.build +++ b/tests/unit/c/meson.build @@ -11,7 +11,7 @@ test_argconfig_parse = executable( ], ) -test('nvme-cli - argconfig-parse', test_argconfig_parse) +test('nvme-cli - argconfig-parse', test_argconfig_parse, suite: 'unit') test_argconfig_diff = executable( 'test-argconfig-diff', @@ -28,7 +28,7 @@ test_argconfig_diff = executable( ], ) -test('nvme-cli - argconfig-diff', test_argconfig_diff) +test('nvme-cli - argconfig-diff', test_argconfig_diff, suite: 'unit') test_global_config = executable( 'test-global-config', @@ -46,7 +46,7 @@ test_global_config = executable( ], ) -test('nvme-cli - global-config', test_global_config) +test('nvme-cli - global-config', test_global_config, suite: 'unit') test_plugin = executable( 'test-plugin', @@ -60,4 +60,4 @@ test_plugin = executable( ], ) -test('nvme-cli - plugin-dispatch', test_plugin) +test('nvme-cli - plugin-dispatch', test_plugin, suite: 'unit') diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index 2d64522e5a..2be470c90a 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -21,6 +21,7 @@ if python3.found() ], depends: nvme_exe, timeout: 120, + suite: 'check', ) # The global options are one table; the documentation has to list @@ -35,6 +36,7 @@ if python3.found() ], depends: nvme_exe, timeout: 60, + suite: 'check', ) # The global options are looked up first, -h is the help; a @@ -48,6 +50,7 @@ if python3.found() ], depends: nvme_exe, timeout: 60, + suite: 'check', ) endif @@ -61,6 +64,7 @@ if python3.found() meson.project_source_root() / 'completions' / 'generate-completions.py', ], timeout: 60, + suite: 'check', ) # The generated completion scripts must not depend on the order commands, @@ -75,6 +79,7 @@ if python3.found() meson.project_source_root() / 'completions' / 'test-command-metadata.json', ], timeout: 60, + suite: 'check', ) # Commands, plugins, sub-commands, and options must be listed @@ -89,6 +94,7 @@ if python3.found() meson.project_source_root() / 'completions' / 'test-command-metadata.json', ], timeout: 60, + suite: 'check', ) # The options complete what their type and range in the metadata say. @@ -101,6 +107,7 @@ if python3.found() meson.project_source_root() / 'completions' / 'test-command-metadata.json', ], timeout: 60, + suite: 'check', ) # The completion test fixture is a hand-written, synthetic metadata model; @@ -115,6 +122,7 @@ if python3.found() meson.project_source_root() / 'plugins' / 'utils' / 'command-metadata-schema.json', ], timeout: 60, + suite: 'check', ) # Test e2e runner's TAP/JSON generation. @@ -126,5 +134,6 @@ if python3.found() meson.project_source_root() / 'tests' / 'e2e' / 'runner.py', ], timeout: 60, + suite: 'unit', ) endif From 9530e56646be3c3893cb861c6b314b038004b9ab Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:32:33 +0000 Subject: [PATCH 245/263] doc: document the test names and groups Describe how tests are named, what the groups mean and how to select tests by group or by name. Signed-off-by: Daniel Wagner --- Documentation/TESTING.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/TESTING.md b/Documentation/TESTING.md index 58ae125069..417415ef0a 100644 --- a/Documentation/TESTING.md +++ b/Documentation/TESTING.md @@ -132,6 +132,38 @@ Example: Cross-build for `s390x`: The exact supported targets depend on the toolchains installed in the container. +## Test names + +Every test is named ` - ` and is in exactly one +group. `meson test` prints it as ` - nvme-cli: - `: + +- `` is what the test covers: `nvme-cli`, `libnvme`, + `shared`, a daemon (`nvme-discoverd`, `nvme-keysd`, ...) or a plugin + (`micron`, `ocp`, ...). +- `` is in kebab-case and names the feature, usually the command + it covers. It does not repeat the component and has no `-test` or + `-cli` suffix. Fuzz and e2e tests start with `fuzz-` and `e2e-`. +- A group of tests over a set of inputs uses a `/`, e.g. + `libnvme - header/tree` or `libnvme - nbft/rhpoc`. + +The group is the meson suite and says how the test runs: + +| Group | What runs | +|---------|---------------------------------------------------------------| +| `unit` | a test program for library or internal code | +| `mock` | the nvme binary or a daemon, with a mock where it needs a device | +| `e2e` | the nvme binary against a real device (`-De2e-tests=true`) | +| `fuzz` | a fuzz target over its corpus (see [Fuzzing](#fuzzing)) | +| `check` | a consistency check of the tree: headers, symbols, docs, tags | + +Select tests by group or by a name pattern: + +```shell +meson test -C .build --suite unit --suite mock +meson test -C .build 'micron - *' +meson test -C .build 'nvme-cli - print' +``` + ## Memory Testing ### Valgrind From 52af1cbf3f707be0c20b9e218061402739b27a20 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:50:48 +0000 Subject: [PATCH 246/263] build: require one toolchain for C and C++ in sanitizer builds With only CC=clang, the C++ tests of libnvme are built and linked by the gcc C++ compiler, with the gcc sanitizer runtime. Where that is not installed, meson fails while generating the targets: ERROR: Linker cc does not support sanitizer arguments ['-fsanitize=address,undefined'] And mixing the runtimes of two toolchains does not work anyway. Fail at the start of the setup instead, and name the fix: CC=clang CXX=clang++. Signed-off-by: Daniel Wagner --- meson.build | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/meson.build b/meson.build index ea30d51ab5..d5fa0f296f 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,22 @@ cc = meson.get_compiler('c') cxx_available = add_languages('cpp', required: false, native: false) host_system = host_machine.system() +# Sanitizer and fuzzing builds fail late, with misleading linker errors, +# in some setups. Catch them here. +if get_option('b_sanitize') != 'none' or get_option('fuzzing') + # The C++ tests link with the C++ compiler. With a different toolchain + # than the C compiler, the sanitizer runtimes do not match. + if cxx_available + cxx_id = meson.get_compiler('cpp').get_id() + if cxx_id != cc.get_id() + error('The C compiler is @0@, but the C++ compiler is @1@. '.format( + cc.get_id(), cxx_id) + + 'Sanitizer and fuzzing builds need both from the same ' + + 'toolchain, e.g. CC=clang CXX=clang++') + endif + endif +endif + ################################################################################ # Determine which features we need to build: The nvme executable, the libnvme # library, and/or the python bindings. These are controlled by the following From 35574438f2e73b1ae18c11e880b78ae05880f266 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:51:01 +0000 Subject: [PATCH 247/263] build: require b_lundef=false for clang sanitizer builds clang links the sanitizer runtime only into executables, so a shared library of a sanitizer build has undefined references to it, which -Wl,--no-undefined rejects: FAILED: libnvme/tests/libmock-ifaddrs.so undefined reference to `__asan_report_load8' meson only warns about b_lundef with a clang sanitizer build. Fail the setup with the option to set instead. Signed-off-by: Daniel Wagner --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build index d5fa0f296f..00d43a87b4 100644 --- a/meson.build +++ b/meson.build @@ -49,6 +49,11 @@ if get_option('b_sanitize') != 'none' or get_option('fuzzing') 'toolchain, e.g. CC=clang CXX=clang++') endif endif + # clang links the sanitizer runtime only into executables, so a shared + # library has undefined symbols, which -Wl,--no-undefined rejects. + if cc.get_id() == 'clang' and get_option('b_lundef') + error('Sanitizer and fuzzing builds with clang need -Db_lundef=false') + endif endif ################################################################################ From 4299d15468321783700aa053fcd9d22dba1db504 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:51:10 +0000 Subject: [PATCH 248/263] build: require a sanitizer for fuzzing builds -Dfuzzing=true adds -fsanitize=fuzzer-no-link to all C code. The fuzz targets get the coverage hooks from libFuzzer, every other program from the sanitizer runtime. Without a sanitizer the test programs fail to link: undefined reference to `__sanitizer_cov_trace_const_cmp4' Fail the setup instead. Signed-off-by: Daniel Wagner --- meson.build | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/meson.build b/meson.build index 00d43a87b4..08660288f5 100644 --- a/meson.build +++ b/meson.build @@ -682,6 +682,12 @@ if get_option('fuzzing') if cc.get_id() != 'clang' error('-Dfuzzing=true needs clang') endif + # The coverage hooks of all code but the fuzz targets come from the + # sanitizer runtime. + if get_option('b_sanitize') == 'none' + error('-Dfuzzing=true needs a sanitizer, ' + + 'e.g. -Db_sanitize=address,undefined') + endif add_project_arguments('-fsanitize=fuzzer-no-link', language : 'c') endif From 390c6d0945dbc7c368798045c5614e107f12568c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:51:29 +0000 Subject: [PATCH 249/263] scripts/build.sh: use clang++ and b_lundef=false for clang sanitizer builds build.sh -c clang -s sets only CC, so the build mixes the clang and gcc toolchains, and with clang the shared libraries need b_lundef=false. Set CXX to the clang++ next to the clang in CC, unless CXX is set, and pass -Db_lundef=false. Signed-off-by: Daniel Wagner --- scripts/build.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/build.sh b/scripts/build.sh index ad7589100a..6e491bb3c6 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -193,6 +193,12 @@ config_meson_default() { local extra_args=() if [ "${use_asan:-0}" -eq 1 ]; then extra_args+=(-Db_sanitize=address,undefined) + # The C++ tests need clang++ to match, and clang links the + # sanitizer runtime only into executables. + if [[ "${CC}" == *clang* ]]; then + export CXX="${CXX:-${CC/clang/clang++}}" + extra_args+=(-Db_lundef=false) + fi fi # Build nvme-keysd in CI. "enabled" fails without libsystemd >= 257. if pkg-config --atleast-version=257 libsystemd 2>/dev/null; then From a1cead5aa8d93716c394d3b6a71e28aa474770f3 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 09:51:29 +0000 Subject: [PATCH 250/263] doc: complete the clang sanitizer and fuzzing instructions The libFuzzer setup misses CXX=clang++ and -Db_lundef=false, so it does not build. The device example runs on the argconfig corpus, and both examples write the inputs libFuzzer finds into the source tree; the comment in tests/fuzz/meson.build runs a target on the seeds in the tree directly. And allocator_may_return_null alone does not stop the device target from aborting: libFuzzer reports a device sized allocation as out of memory before ASan can fail it. Add the clang setup to the sanitizer section and use it for the libFuzzer build. Run each target on a copy of its own seeds in the build directory, cap the ASan allocation size for the device target, and say where a crashing input goes. Point the comment to the documentation. Signed-off-by: Daniel Wagner --- Documentation/TESTING.md | 48 ++++++++++++++++++++++++++++++---------- tests/fuzz/meson.build | 9 ++++---- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Documentation/TESTING.md b/Documentation/TESTING.md index 417415ef0a..c5c442cac8 100644 --- a/Documentation/TESTING.md +++ b/Documentation/TESTING.md @@ -225,6 +225,15 @@ meson test -C .build-asanubsan --setup asanubsan No wrapper binary is needed — sanitizer reports are emitted directly to stderr when a violation is detected. +With clang, set the C++ compiler too, and let shared libraries have the +symbols of the sanitizer runtime undefined: clang links the runtime only into +executables. + +```bash +CC=clang CXX=clang++ meson setup .build-asanubsan \ + -Db_sanitize=address,undefined -Db_lundef=false +``` + The same known limitations apply as for Valgrind: Python tests and shell script tests may produce false positives. Focus triage on the `nvme-cli - *` unit tests. @@ -255,25 +264,40 @@ FUZZ_SECONDS=60 FUZZ_SEED=7 meson test -C .build --suite fuzz Run in a sanitizer build (see above) they also catch memory errors which don't crash. -For longer, coverage guided runs, build the targets for libFuzzer with clang: +For longer, coverage guided runs, build the targets for libFuzzer with clang, +in a sanitizer build as described above: ```bash -CC=clang meson setup .build-fuzz -Dfuzzing=true -Db_sanitize=address,undefined +CC=clang CXX=clang++ meson setup .build-fuzz -Dfuzzing=true \ + -Db_sanitize=address,undefined -Db_lundef=false meson compile -C .build-fuzz -mkdir corpus && cp tests/fuzz/corpus/argconfig/* corpus/ +``` + +Run a target on a copy of its seeds in the build directory, so the seeds in +the tree stay as they are. The new inputs libFuzzer finds go to the copy: + +```bash +mkdir -p .build-fuzz/corpus/argconfig +cp tests/fuzz/corpus/argconfig/* .build-fuzz/corpus/argconfig/ .build-fuzz/tests/fuzz/fuzz-argconfig -close_fd_mask=3 \ - -dict=tests/fuzz/argconfig.dict corpus + -dict=tests/fuzz/argconfig.dict .build-fuzz/corpus/argconfig ``` -The config targets use `tests/fuzz/config.dict`. The `device` target -reads log pages, so give it longer inputs, and let allocations of the sizes -a device reports fail instead of aborting: +The config targets (`ini`, `global-config`, `fabrics-conf`) use +`tests/fuzz/config.dict`. The `device` target reads log pages, so give it +longer inputs. A device reports sizes of up to gigabytes, which nvme-cli +allocates: let ASan fail such an allocation instead of libFuzzer aborting the +run as out of memory: ```bash -ASAN_OPTIONS=allocator_may_return_null=1 .build-fuzz/tests/fuzz/fuzz-device \ - -close_fd_mask=3 -max_len=16384 -timeout=10 corpus +mkdir -p .build-fuzz/corpus/device +cp tests/fuzz/corpus/device/* .build-fuzz/corpus/device/ +ASAN_OPTIONS=allocator_may_return_null=1:max_allocation_size_mb=1024 \ + .build-fuzz/tests/fuzz/fuzz-device -close_fd_mask=3 -max_len=16384 \ + -timeout=10 .build-fuzz/corpus/device ``` - The targets use the + +A crashing input is written to `crash-*` in the current directory. Add an +input which found a bug to the seeds of its target. The targets use the libFuzzer interface, so AFL++ can run them too (`afl-clang-fast` with -`-fsanitize=fuzzer`). Add an input which found a bug to the seeds of its -target. +`-fsanitize=fuzzer`). diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 4d387be36e..c182fa5056 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -4,12 +4,11 @@ # config files and the data from the device. Without -Dfuzzing=true each # target runs as a test, with the seeds in corpus/ and a few # seconds of mutations of them (FUZZ_SECONDS, FUZZ_SEED). With -# -Dfuzzing=true and clang, the targets are built for libFuzzer: +# -Dfuzzing=true and clang, the targets are built for libFuzzer, see +# Documentation/TESTING.md: # -# CC=clang meson setup fuzz -Dfuzzing=true -Db_sanitize=address,undefined -# ninja -C fuzz -# fuzz/tests/fuzz/fuzz-argconfig -dict=tests/fuzz/argconfig.dict \ -# tests/fuzz/corpus/argconfig +# CC=clang CXX=clang++ meson setup fuzz -Dfuzzing=true \ +# -Db_sanitize=address,undefined -Db_lundef=false # # The targets use the libFuzzer interface, which AFL++ (afl-clang-fast # -fsanitize=fuzzer) runs as well. From 75ff051c358a8cc1325f7a8dba0f60c7cbc2642c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 10:20:25 +0000 Subject: [PATCH 251/263] scripts/build.sh: check the config specific options in one place The --e2e-* and --plugin-tests options are only valid with the tests config, which one condition over all their variables checks. Let every config specific option record its config instead, and check them all in one loop. Another config can then have options of its own, and the error names the option which does not fit. Signed-off-by: Daniel Wagner --- scripts/build.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 6e491bb3c6..a4b15e490f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -85,6 +85,12 @@ E2E_LOG_LEVEL="" E2E_NVME_BIN="" PLUGIN_TESTS="" +# The options which only one config takes, as "config option" +CONFIG_OPTIONS=() +config_option() { + CONFIG_OPTIONS+=("$1 $2") +} + ARGS=() while [[ $# -gt 0 ]]; do case "$1" in @@ -134,26 +140,32 @@ while [[ $# -gt 0 ]]; do ;; --e2e-controller) E2E_CONTROLLER="$2" + config_option tests "$1" shift 2 ;; --e2e-ns1) E2E_NS1="$2" + config_option tests "$1" shift 2 ;; --e2e-log-dir) E2E_LOG_DIR="$2" + config_option tests "$1" shift 2 ;; --e2e-log-level) E2E_LOG_LEVEL="$2" + config_option tests "$1" shift 2 ;; --e2e-nvme-bin) E2E_NVME_BIN="$2" + config_option tests "$1" shift 2 ;; --plugin-tests) PLUGIN_TESTS="$2" + config_option tests "$1" shift 2 ;; -h|--help) @@ -174,13 +186,12 @@ set -- "${ARGS[@]}" CONFIG=${1:-"default"} -if [[ "${CONFIG}" != "tests" ]]; then - if [[ -n "${E2E_CONTROLLER}" || -n "${E2E_NS1}" || -n "${E2E_LOG_DIR}" || \ - -n "${E2E_LOG_LEVEL}" || -n "${E2E_NVME_BIN}" || -n "${PLUGIN_TESTS}" ]]; then - echo "error: --e2e-* and --plugin-tests are only valid with the 'tests' config" >&2 +for opt in "${CONFIG_OPTIONS[@]}"; do + if [[ "${opt%% *}" != "${CONFIG}" ]]; then + echo "error: ${opt#* } is only valid with the '${opt%% *}' config" >&2 exit 1 fi -fi +done cd "$(git rev-parse --show-toplevel)" || exit 1 From f4378105b0c42986fd62ede6d47b7a1326889755 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 10:22:31 +0000 Subject: [PATCH 252/263] scripts/build.sh: add a fuzz config for long libFuzzer runs A long fuzzing run needs the clang sanitizer build, a copy of the seeds per target, the right options per target and a place for the results. Add a fuzz config which builds the targets for libFuzzer and runs each of them in turn: --fuzz-seconds n seconds per target (600) --fuzz-jobs n libFuzzer jobs per target (one per CPU) --fuzz-targets list the targets (all) --fuzz-dir dir the results (.build-ci/fuzz) The jobs run as independent processes on a shared corpus (-jobs). The newer -fork mode would do the same, but the libFuzzer of clang 22 crashes in it. The results are summary.txt with the findings, and per target the logs of the jobs, the inputs which found a bug and the corpus. A finding stops its job, a UBSan finding like a crash. The exit status is not zero if a target had a finding. Signed-off-by: Daniel Wagner --- Documentation/TESTING.md | 23 ++++-- scripts/build.sh | 152 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/Documentation/TESTING.md b/Documentation/TESTING.md index c5c442cac8..54484ceca1 100644 --- a/Documentation/TESTING.md +++ b/Documentation/TESTING.md @@ -297,7 +297,22 @@ ASAN_OPTIONS=allocator_may_return_null=1:max_allocation_size_mb=1024 \ -timeout=10 .build-fuzz/corpus/device ``` -A crashing input is written to `crash-*` in the current directory. Add an -input which found a bug to the seeds of its target. The targets use the -libFuzzer interface, so AFL++ can run them too (`afl-clang-fast` with -`-fsanitize=fuzzer`). +`scripts/build.sh fuzz` does all of this for every target in turn, each with +one libFuzzer job per CPU on a shared corpus, for 600 seconds by default: + +```bash +scripts/build.sh --fuzz-seconds 3600 fuzz +scripts/build.sh --fuzz-targets device --fuzz-jobs 8 fuzz +``` + +It writes the results to `.build-ci/fuzz`: `summary.txt`, and per target the +logs of the jobs in `logs//`, the inputs which found a bug in +`artifacts//` and the corpus in `corpus//`. A finding stops +only its job, a UBSan finding like a crash. The next run of `build.sh` removes +the results; pass a `--fuzz-dir` outside of `.build-ci` to keep them, and to +grow the corpus across runs. `scripts/build.sh -h` lists all options. + +When run by hand, a crashing input is written to `crash-*` in the current +directory. Add an input which found a bug to the seeds of its target. The +targets use the libFuzzer interface, so AFL++ can run them too +(`afl-clang-fast` with `-fsanitize=fuzzer`). diff --git a/scripts/build.sh b/scripts/build.sh index a4b15e490f..03f1baa3ac 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -8,6 +8,8 @@ set -e +ALL_FUZZ_TARGETS="argconfig ini global-config fabrics-conf device" + usage() { echo "Usage: build.sh [-b [release|debug]] " echo " [-c [gcc|clang]]" @@ -18,6 +20,8 @@ usage() { echo " [--e2e-log-level level]" echo " [--e2e-nvme-bin path]" echo " [--plugin-tests plugin1,plugin2]" + echo " [--fuzz-seconds n] [--fuzz-jobs n]" + echo " [--fuzz-targets target1,target2] [--fuzz-dir dir]" echo " [config]" echo "" echo "CI build script." @@ -42,6 +46,14 @@ usage() { echo " --plugin-tests list comma-separated list of plugin test suites to" echo " run against real hardware, e.g. micron,ocp" echo "" + echo "options for the 'fuzz' config:" + echo " --fuzz-seconds n run each target for n seconds (600)" + echo " --fuzz-jobs n run n libFuzzer jobs per target (all CPUs)" + echo " --fuzz-targets list comma-separated list of targets (all):" + echo " ${ALL_FUZZ_TARGETS// /, }" + echo " --fuzz-dir dir directory for the results (.build-ci/fuzz):" + echo " summary.txt, logs/, artifacts/ and corpus/" + echo "" echo "configs with meson:" echo " [default] default settings" echo " libdbus build with libdbus" @@ -61,6 +73,8 @@ usage() { echo " libnvme build only libnvme" echo " libnvme_abi build only libnvme3.so, no tests/examples" echo " tests build for nightly build" + echo " fuzz build the fuzz targets for libFuzzer with clang" + echo " and run them, see the options above" echo "" echo "configs with muon:" echo " [default] minimal static build" @@ -85,6 +99,11 @@ E2E_LOG_LEVEL="" E2E_NVME_BIN="" PLUGIN_TESTS="" +fuzz_seconds=600 +fuzz_jobs="" +fuzz_targets="${ALL_FUZZ_TARGETS}" +fuzz_dir="" + # The options which only one config takes, as "config option" CONFIG_OPTIONS=() config_option() { @@ -168,6 +187,40 @@ while [[ $# -gt 0 ]]; do config_option tests "$1" shift 2 ;; + --fuzz-seconds|--fuzz-jobs) + if ! [[ "$2" =~ ^[1-9][0-9]*$ ]]; then + echo "error: $1 takes a positive number" >&2 + exit 1 + fi + if [[ "$1" == --fuzz-seconds ]]; then + fuzz_seconds="$2" + else + fuzz_jobs="$2" + fi + config_option fuzz "$1" + shift 2 + ;; + --fuzz-targets) + fuzz_targets="${2//,/ }" + for t in ${fuzz_targets}; do + if [[ " ${ALL_FUZZ_TARGETS} " != *" ${t} "* ]]; then + echo "error: unknown fuzz target '${t}'" >&2 + exit 1 + fi + done + config_option fuzz "$1" + shift 2 + ;; + --fuzz-dir) + # relative to where build.sh runs, not to the top directory + if [[ "$2" == /* ]]; then + fuzz_dir="$2" + else + fuzz_dir="$(pwd)/$2" + fi + config_option fuzz "$1" + shift 2 + ;; -h|--help) usage exit 0 @@ -479,6 +532,105 @@ test_meson_libnvme_abi() { : } +# Coverage guided fuzzing with libFuzzer, see Documentation/TESTING.md. +# The results are in the --fuzz-dir, by default in the build directory: +# per target the corpus, the logs and the inputs which found a bug, and a +# summary. A --fuzz-dir outside of it grows the corpus across runs. +config_meson_fuzz() { + if [[ "${CC}" != *clang* ]]; then + CC=clang + fi + CXX="${CXX:-${CC/clang/clang++}}" + + CC="${CC}" CXX="${CXX}" "${MESON}" setup \ + --werror \ + --buildtype="${BUILDTYPE}" \ + -Dfuzzing=true \ + -Db_sanitize=address,undefined \ + -Db_lundef=false \ + "${BUILDDIR}" +} + +test_meson_fuzz() { + local root + root="$(pwd)" + local dir="${fuzz_dir:-${BUILDDIR}/fuzz}" + local seconds="${fuzz_seconds}" + local jobs="${fuzz_jobs:-$(nproc 2>/dev/null || echo 1)}" + local failed=() + local t f exe corpus artifacts logs args status summary + # A device reports sizes of up to gigabytes: let ASan fail such an + # allocation instead of libFuzzer aborting as out of memory. + local asan="allocator_may_return_null=1:max_allocation_size_mb=1024" + + mkdir -p "${dir}" + summary="${dir}/summary.txt" + : > "${summary}" + + # A UBSan finding is a crash too + export UBSAN_OPTIONS="${UBSAN_OPTIONS:-halt_on_error=1:print_stacktrace=1}" + + for t in ${fuzz_targets}; do + exe="${BUILDDIR}/tests/fuzz/fuzz-${t}" + if [ ! -x "${exe}" ]; then + echo "fuzz-${t}: not built, skipped" + continue + fi + + corpus="${dir}/corpus/${t}" + artifacts="${dir}/artifacts/${t}" + logs="${dir}/logs/${t}" + rm -rf "${logs}" + mkdir -p "${corpus}" "${artifacts}" "${logs}" + # Add the seeds, but keep what earlier runs found + for f in "${root}"/tests/fuzz/corpus/"${t}"/*; do + [ -e "${corpus}/${f##*/}" ] || cp "${f}" "${corpus}/" + done + + # Run the jobs as independent processes on the shared corpus. The + # -fork mode is newer, but the libFuzzer of clang 22 crashes in it. + args=(-close_fd_mask=3 -max_total_time="${seconds}" + -jobs="${jobs}" -workers="${jobs}" + -artifact_prefix="${artifacts}/") + case "${t}" in + argconfig) + args+=(-dict="${root}/tests/fuzz/argconfig.dict") + ;; + device) + args+=(-max_len=16384 -timeout=10) + ;; + *) + args+=(-dict="${root}/tests/fuzz/config.dict") + ;; + esac + + echo "fuzz-${t}: ${seconds}s, ${jobs} jobs, logs in ${logs}" + # Each job writes fuzz-.log to the current directory + status=0 + (cd "${logs}" && + ASAN_OPTIONS="${ASAN_OPTIONS:+${ASAN_OPTIONS}:}${asan}" \ + "${exe}" "${args[@]}" "${corpus}" > main.log 2>&1) || + status=$? + if [ "${status}" -ne 0 ]; then + failed+=("${t}") + echo "fuzz-${t}: finding, exit status ${status}" | + tee -a "${summary}" + { + grep -hE '^(SUMMARY|==[0-9]+==ERROR)' "${logs}"/fuzz-*.log || + true + ls "${artifacts}"/* 2>/dev/null || true + } >> "${summary}" + else + echo "fuzz-${t}: no finding in ${seconds}s" | tee -a "${summary}" + fi + done + + if [ "${#failed[@]}" -gt 0 ]; then + echo "fuzz targets with findings: ${failed[*]}, see ${summary}" + return 1 + fi +} + build_meson() { if [ "${use_analyzer:-0}" -eq 1 ]; then "${SCAN_BUILD}" -o "${BUILDDIR}/scan-results" \ From f246661f3a3c47a49e24d12ab84ea6f6151007c6 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 10:41:33 +0000 Subject: [PATCH 253/263] scaleflux: initialize the options of query-cap The --raw-binary flag only sets cfg.raw_binary when it is given, so without it query-cap reads an uninitialized bool. UBSan reports it while fuzzing the device data: sfx-nvme.c:731:12: runtime error: load of value 192, which is not a valid value for type 'bool' Initialize the options like the other commands do. Signed-off-by: Daniel Wagner --- plugins/scaleflux/sfx-nvme.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index a36f5e1b7c..aeac1a0434 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -711,7 +711,9 @@ static int query_cap_info(int argc, char **argv, struct command *acmd, struct pl struct config { bool raw_binary; }; - struct config cfg; + struct config cfg = { + .raw_binary = false, + }; int err = 0; OPT_ARGS(opts) = { From dc6b232781c79efe006279ca97be879c2c4a682b Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 11:10:58 +0000 Subject: [PATCH 254/263] ocp: bound the descriptors of the hardware component log The stdout and the JSON printer walk the descriptors with the sizes the device reports, without checking them against the length of the log. A descriptor at the end of the log is read beyond it, and a size larger than the rest of the log moves the walk out of it: ERROR: AddressSanitizer: heap-buffer-overflow READ of size 8 #0 json_hwcomp_log plugins/ocp/ocp-print-json.c:131 Add hwcomp_desc_entry(), which returns a descriptor only if its header, date/lot code and additional information fit into the rest of the log, and walk the descriptors with it in both printers. Signed-off-by: Daniel Wagner --- plugins/ocp/ocp-hardware-component-log.c | 43 ++++++++++++++++++++++++ plugins/ocp/ocp-hardware-component-log.h | 2 ++ plugins/ocp/ocp-print-json.c | 21 ++++-------- plugins/ocp/ocp-print-stdout.c | 19 ++++------- 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/plugins/ocp/ocp-hardware-component-log.c b/plugins/ocp/ocp-hardware-component-log.c index 6c2043302a..e075fa8340 100644 --- a/plugins/ocp/ocp-hardware-component-log.c +++ b/plugins/ocp/ocp-hardware-component-log.c @@ -175,6 +175,49 @@ const char *hwcomp_id_to_string(__u32 id) return "Reserved"; } +/* + * The descriptor at offset of the descriptors of log. Returns false at the + * end, and when the descriptor does not fit into the rest of the log. + */ +bool hwcomp_desc_entry(struct hwcomp_log *log, __u64 offset, + struct hwcomp_desc_entry *e) +{ + __u64 remain, date_lot_size, add_info_size; + __u8 *p; + + if (offset >= log->desc_len) + return false; + remain = log->desc_len - offset; + if (remain < sizeof(struct hwcomp_desc)) + return false; + remain -= sizeof(struct hwcomp_desc); + + p = (__u8 *)log->desc + offset; + e->desc = (struct hwcomp_desc *)p; + p += sizeof(struct hwcomp_desc); + + /* The sizes count dwords */ + date_lot_size = le64_to_cpu(e->desc->date_lot_size); + if (date_lot_size > remain / sizeof(__le32)) + return false; + date_lot_size *= sizeof(__le32); + remain -= date_lot_size; + + add_info_size = le64_to_cpu(e->desc->add_info_size); + if (add_info_size > remain / sizeof(__le32)) + return false; + add_info_size *= sizeof(__le32); + + e->date_lot_size = date_lot_size; + e->date_lot_code = date_lot_size ? p : NULL; + e->add_info_size = add_info_size; + e->add_info = add_info_size ? p + date_lot_size : NULL; + e->desc_size = sizeof(struct hwcomp_desc) + date_lot_size + + add_info_size; + + return true; +} + static int get_hwcomp_log_data(struct libnvme_transport_handle *hdl, struct hwcomp_log *log) { size_t desc_offset = offsetof(struct hwcomp_log, desc); diff --git a/plugins/ocp/ocp-hardware-component-log.h b/plugins/ocp/ocp-hardware-component-log.h index 0418d00a42..9fc1a0113a 100644 --- a/plugins/ocp/ocp-hardware-component-log.h +++ b/plugins/ocp/ocp-hardware-component-log.h @@ -62,3 +62,5 @@ enum hwcomp_id { int ocp_hwcomp_log(int argc, char **argv, struct command *acmd, struct plugin *plugin); const char *hwcomp_id_to_string(__u32 id); +bool hwcomp_desc_entry(struct hwcomp_log *log, __u64 offset, + struct hwcomp_desc_entry *e); diff --git a/plugins/ocp/ocp-print-json.c b/plugins/ocp/ocp-print-json.c index 572ea3c9f8..e9baa7ba1a 100644 --- a/plugins/ocp/ocp-print-json.c +++ b/plugins/ocp/ocp-print-json.c @@ -117,26 +117,17 @@ static void print_hwcomp_desc_list_json(struct json_object *r, struct hwcomp_des print_hwcomp_desc_json(e, obj_create_array_obj(r, k)); } -static void print_hwcomp_descs_json(struct hwcomp_desc *desc, long double log_size, __u32 id, +static void print_hwcomp_descs_json(struct hwcomp_log *log, __u32 id, bool list, struct json_object *r) { - size_t date_lot_code_offset = sizeof(struct hwcomp_desc); - struct hwcomp_desc_entry e = { desc }; + struct hwcomp_desc_entry e; + __u64 offset; int num = 1; - while (log_size > 0) { - e.date_lot_size = le64_to_cpu(e.desc->date_lot_size) * sizeof(__le32); - e.date_lot_code = e.date_lot_size ? - (__u8 *)e.desc + date_lot_code_offset : NULL; - e.add_info_size = le64_to_cpu(e.desc->add_info_size) * sizeof(__le32); - e.add_info = e.add_info_size ? e.date_lot_code ? - e.date_lot_code + e.date_lot_size : - (__u8 *)e.desc + date_lot_code_offset : NULL; + for (offset = 0; hwcomp_desc_entry(log, offset, &e); + offset += e.desc_size) { if (!id || id == le32_to_cpu(e.desc->id)) print_hwcomp_desc_list_json(r, &e, list, num++); - e.desc_size = date_lot_code_offset + e.date_lot_size + e.add_info_size; - e.desc = (struct hwcomp_desc *)((__u8 *)e.desc + e.desc_size); - log_size -= e.desc_size; } } @@ -151,7 +142,7 @@ static void json_hwcomp_log(struct hwcomp_log *log, __u32 id, bool list) json_object_add_nprix64(r, "Hardware Component Log Size", (unsigned long long)log->desc_len); json_object_add_byte_array(r, "Reserved48", log->rsvd48, ARRAY_SIZE(log->rsvd48)); - print_hwcomp_descs_json(log->desc, log->desc_len, id, list, + print_hwcomp_descs_json(log, id, list, obj_create_array_obj(r, "Component Descriptions")); json_print(r); diff --git a/plugins/ocp/ocp-print-stdout.c b/plugins/ocp/ocp-print-stdout.c index f6f33b4578..20e6420c80 100644 --- a/plugins/ocp/ocp-print-stdout.c +++ b/plugins/ocp/ocp-print-stdout.c @@ -40,29 +40,22 @@ static void print_hwcomp_desc(struct hwcomp_desc_entry *e, bool list, int num) static void stdout_hwcomp_log(struct hwcomp_log *log, __u32 id, bool list) { - size_t date_lot_code_offset = sizeof(struct hwcomp_desc); + struct hwcomp_desc_entry e; + __u64 offset; int num = 1; - __u64 desc_remain = log->desc_len; - struct hwcomp_desc_entry e = { log->desc }; printf("Log Identifier: 0x%02xh\n", OCP_LID_HWCOMP); printf("Log Page Version: 0x%x\n", le16_to_cpu(log->ver)); print_array("Reserved2", log->rsvd2, ARRAY_SIZE(log->rsvd2)); print_array("Log page GUID", log->guid, ARRAY_SIZE(log->guid)); - printf("Hardware Component Log Size: 0x%"PRIx64"\n", (uint64_t)desc_remain); + printf("Hardware Component Log Size: 0x%"PRIx64"\n", + (uint64_t)log->desc_len); print_array("Reserved48", log->rsvd48, ARRAY_SIZE(log->rsvd48)); printf("Component Descriptions\n"); - while (desc_remain > 0) { - e.date_lot_size = le64_to_cpu(e.desc->date_lot_size) * sizeof(__le32); - e.date_lot_code = e.date_lot_size ? (__u8 *)e.desc + date_lot_code_offset : NULL; - e.add_info_size = le64_to_cpu(e.desc->add_info_size) * sizeof(__le32); - e.add_info = e.add_info_size ? e.date_lot_code ? e.date_lot_code + e.date_lot_size : - (__u8 *)e.desc + date_lot_code_offset : NULL; + for (offset = 0; hwcomp_desc_entry(log, offset, &e); + offset += e.desc_size) { if (!id || id == le32_to_cpu(e.desc->id)) print_hwcomp_desc(&e, list, num++); - e.desc_size = date_lot_code_offset + e.date_lot_size + e.add_info_size; - e.desc = (struct hwcomp_desc *)((__u8 *)e.desc + e.desc_size); - desc_remain -= e.desc_size; } } From e59490d69231cc80b18c4017e6eef95f739efe19 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 19 Oct 2026 11:17:10 +0000 Subject: [PATCH 255/263] nvme-print: stop at a persistent event with a short header The Event Header Length counts the bytes of the event header after the first 3, but the printers of the Persistent Event log read all header fields of an event however short its header is. An event of a zeroed log has only 3 bytes, so a log of 2 MiB is printed as some 700000 events, which takes minutes: ALARM: working on the last Unit for 15 seconds #4 shr_table_align_column shared/table-util.c:353 #5 stdout_kv_table_finish src/nvme-print-stdout.c:609 #6 nvme_show_pel_event_header src/nvme-print-stdout-log.c:244 The same walk is in five printers. Add nvme_pel_event(), which returns an event only if its header holds all header fields, its vendor specific information fits into the event and the event into the log, and use it in all of them. Signed-off-by: Daniel Wagner --- plugins/ibm/ibm-nvme.c | 9 +++------ plugins/ocp/ocp-print-json.c | 9 +++------ plugins/ocp/ocp-print-stdout.c | 9 +++------ src/nvme-print-json.c | 9 +++------ src/nvme-print-stdout-log.c | 9 +++------ src/nvme-print.c | 29 +++++++++++++++++++++++++++++ src/nvme-print.h | 2 ++ 7 files changed, 46 insertions(+), 30 deletions(-) diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 26e4b1279c..dd0a5f0766 100644 --- a/plugins/ibm/ibm-nvme.c +++ b/plugins/ibm/ibm-nvme.c @@ -463,12 +463,9 @@ void nvme_show_ibm_persistent_event_log(void *pevent_log_info, printf("\n"); printf(" i=%d tnev=%d\n", i, le32_to_cpu(pevent_log_head->tnev)); for (i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { - if (offset + sizeof(*pevent_entry_head) > size) - break; - - pevent_entry_head = pevent_log_info + offset; - if ((offset + pevent_entry_head->ehl + 3 + - le16_to_cpu(pevent_entry_head->el)) > size) + pevent_entry_head = nvme_pel_event(pevent_log_info, size, + offset); + if (!pevent_entry_head) break; offset += pevent_entry_head->ehl + 3; diff --git a/plugins/ocp/ocp-print-json.c b/plugins/ocp/ocp-print-json.c index e9baa7ba1a..010020684c 100644 --- a/plugins/ocp/ocp-print-json.c +++ b/plugins/ocp/ocp-print-json.c @@ -1241,17 +1241,14 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co struct json_object *valid_attrs; for (i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { - if (offset + sizeof(*pevent_entry_head) > size) + pevent_entry_head = nvme_pel_event(pevent_log_info, size, + offset); + if (!pevent_entry_head) break; - pevent_entry_head = pevent_log_info + offset; vsil = le16_to_cpu(pevent_entry_head->vsil); el = le16_to_cpu(pevent_entry_head->el); - if (vsil > el || - offset + pevent_entry_head->ehl + 3 + el > size) - break; - valid_attrs = json_create_object(); obj_add_uint(valid_attrs, "event_number", i); diff --git a/plugins/ocp/ocp-print-stdout.c b/plugins/ocp/ocp-print-stdout.c index 20e6420c80..6cf20c96fd 100644 --- a/plugins/ocp/ocp-print-stdout.c +++ b/plugins/ocp/ocp-print-stdout.c @@ -839,17 +839,14 @@ static void stdout_persistent_event_log(void *pevent_log_info, __u8 action, printf("\n"); printf("\nPersistent Event Entries:\n"); for (int i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { - if (offset + sizeof(*pevent_entry_head) > size) + pevent_entry_head = nvme_pel_event(pevent_log_info, size, + offset); + if (!pevent_entry_head) break; - pevent_entry_head = pevent_log_info + offset; vsil = le16_to_cpu(pevent_entry_head->vsil); el = le16_to_cpu(pevent_entry_head->el); - if (vsil > el || - offset + pevent_entry_head->ehl + 3 + el > size) - break; - nvme_show_pel_event_header(i, pevent_entry_head, human); offset += pevent_entry_head->ehl + vsil + 3; diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 86f27b0f76..9b1b159a98 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -2033,17 +2033,14 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co struct json_object *valid_attrs; for (i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { - if (offset + sizeof(*pevent_entry_head) > size) + pevent_entry_head = nvme_pel_event(pevent_log_info, size, + offset); + if (!pevent_entry_head) break; - pevent_entry_head = pevent_log_info + offset; vsil = le16_to_cpu(pevent_entry_head->vsil); el = le16_to_cpu(pevent_entry_head->el); - if (vsil > el || - offset + pevent_entry_head->ehl + 3 + el > size) - break; - valid_attrs = json_create_object(); obj_add_uint(valid_attrs, "event_number", i); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index ab7aa1b8e1..917ceb14ed 100644 --- a/src/nvme-print-stdout-log.c +++ b/src/nvme-print-stdout-log.c @@ -685,17 +685,14 @@ void stdout_persistent_event_log(void *pevent_log_info, __u8 action, __u32 size, printf("\n"); printf("\nPersistent Event Entries:\n"); for (int i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { - if (offset + sizeof(*pevent_entry_head) > size) + pevent_entry_head = nvme_pel_event(pevent_log_info, size, + offset); + if (!pevent_entry_head) break; - pevent_entry_head = pevent_log_info + offset; vsil = le16_to_cpu(pevent_entry_head->vsil); el = le16_to_cpu(pevent_entry_head->el); - if (vsil > el || - offset + pevent_entry_head->ehl + 3 + el > size) - break; - nvme_show_pel_event_header(i, pevent_entry_head, verbose); offset += pevent_entry_head->ehl + vsil + 3; diff --git a/src/nvme-print.c b/src/nvme-print.c index 2e43dbe606..fbd4fbbba9 100644 --- a/src/nvme-print.c +++ b/src/nvme-print.c @@ -337,6 +337,35 @@ const char *nvme_nss_hw_error_to_string(__u16 error_code) * of the event data, zero padded to the largest event structure, or NULL. * The caller frees the copy. */ +/* + * The event at offset of a Persistent Event log of size bytes, or NULL if + * there is no complete event: its header must hold all header fields, its + * vendor specific information must fit into the event, and the event into + * the log. + */ +struct nvme_persistent_event_entry *nvme_pel_event(void *pevent_log_info, + __u32 size, __u32 offset) +{ + struct nvme_persistent_event_entry *e; + __u64 len; + + if ((__u64)offset + sizeof(*e) > size) + return NULL; + + e = pevent_log_info + offset; + /* The Event Header Length does not count the first 3 bytes */ + if (e->ehl + 3 < sizeof(*e)) + return NULL; + if (le16_to_cpu(e->vsil) > le16_to_cpu(e->el)) + return NULL; + + len = (__u64)e->ehl + 3 + le16_to_cpu(e->el); + if (offset + len > size) + return NULL; + + return e; +} + void *nvme_pel_event_data(void *pevent_log_info, __u32 offset, __u32 len, __u32 *buf_len) { diff --git a/src/nvme-print.h b/src/nvme-print.h index a97d0196d9..8bb80db719 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -828,6 +828,8 @@ void nvme_show_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, void nvme_show_log(const char *devname, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u16 lsi, __u8 lsp, void *log, __u32 len, nvme_print_flags_t flags); void nvme_show_pel_header(struct nvme_persistent_event_log *pevent_log_head, int human); +struct nvme_persistent_event_entry *nvme_pel_event(void *pevent_log_info, + __u32 size, __u32 offset); void *nvme_pel_event_data(void *pevent_log_info, __u32 offset, __u32 len, __u32 *buf_len); void nvme_show_pel_event_header(int i, struct nvme_persistent_event_entry *pevent_entry_head, From 4ec3ddf84ddb89ed76af3e0369f1bf90eabbb3e0 Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 16:15:23 +0530 Subject: [PATCH 256/263] fabrics: disconnect the disc controller when open fails nvmf_create_discovery_ctrl() reaches the libnvme_open() call only after __create_discovery_ctrl() has already connected the controller and linked it into the subsystem tree. The identify-failure path right below handles this correctly by calling libnvmf_disconnect_ctrl() followed by libnvme_free_ctrl(). The libnvme_open() failure path, however, returns the error without tearing anything down, so the freshly created discovery connection is left established even though the operation failed. Disconnect and free the controller on this path too, matching the identify-failure path. Signed-off-by: Martin George --- libnvme/src/nvme/fabrics.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 44905a7ca8..a1885b29a7 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -3799,6 +3799,8 @@ static int nvmf_create_discovery_ctrl(struct libnvme_global_ctx *ctx, ret = libnvme_open(ctx, c->name, O_RDONLY, &c->hdl); if (ret) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "failed to open %s\n", c->name); + libnvmf_disconnect_ctrl(c); + libnvme_free_ctrl(c); return ret; } From 1b1675e74910e804802143b2bb18877a1d8ad527 Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 16:35:21 +0530 Subject: [PATCH 257/263] tree: propagate ns scan errors in libnvme_ctrl_scan_namespaces() libnvme_ctrl_scan_namespaces() stores the return of libnvme_scan_ctrl_namespaces() into a signed count and loops over it without first checking for a negative error. On a scan failure the negative count simply skips the loop and the function returns success, silently hiding the error. Its siblings libnvme_ctrl_scan_paths() and nvme_subsystem_scan_namespaces() both bail out on a negative count. Check for the error and return it, matching the other scanners. Signed-off-by: Martin George --- libnvme/src/nvme/tree.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index b9741bab6f..0b5f8a68d9 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -1391,6 +1391,9 @@ int libnvme_ctrl_scan_namespaces(struct libnvme_global_ctx *ctx, return 0; } namespaces.num = libnvme_scan_ctrl_namespaces(c, &namespaces.ents); + if (namespaces.num < 0) + return namespaces.num; + for (i = 0; i < namespaces.num; i++) { err = libnvme_ctrl_scan_namespace(ctx, c, namespaces.ents[i]->d_name); From 7c7cf5c741d1b4e0f2ffaa3617287118d51b9ef4 Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 16:40:18 +0530 Subject: [PATCH 258/263] libnvme: fix submit_entry setter guarding the wrong field libnvme_transport_handle_set_submit_entry() assigns the caller-provided submit_entry hook, but the subsequent NULL check and default assignment operate on submit_exit instead of submit_entry (a copy-paste slip; the sibling set_submit_exit()/set_decide_retry() setters correctly guard their own field). As a result, passing NULL to reset the entry hook to the library default leaves hdl->submit_entry NULL rather than restoring __libnvme_submit_entry. The next command execution then calls hdl->submit_entry() and dereferences a NULL function pointer. Guard and default submit_entry itself. Signed-off-by: Martin George --- libnvme/src/nvme/lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libnvme/src/nvme/lib.c b/libnvme/src/nvme/lib.c index f9b60fe0f5..f1fa613f36 100644 --- a/libnvme/src/nvme/lib.c +++ b/libnvme/src/nvme/lib.c @@ -143,8 +143,8 @@ __shr_public void libnvme_transport_handle_set_submit_entry( struct libnvme_passthru_cmd *cmd)) { hdl->submit_entry = submit_entry; - if (!hdl->submit_exit) - hdl->submit_exit = __libnvme_submit_exit; + if (!hdl->submit_entry) + hdl->submit_entry = __libnvme_submit_entry; } __shr_public void libnvme_transport_handle_set_submit_exit( From 21793787fb7891d98e38830f9e8b37d9e1c75d2d Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 16:45:49 +0530 Subject: [PATCH 259/263] fabrics: avoid truncating discovery log length when saving save_discovery_log() computes the discovery log size (header plus numrec * sizeof(entry)) into an int before passing it to shr_write_all(), whose length argument is a size_t. numrec is a 64-bit field read from the discovery log header, so a sufficiently large device-controlled record count can overflow the int and truncate the length, causing a short or corrupted saved log. Compute the length as size_t so the full byte count is preserved. Signed-off-by: Martin George --- src/fabrics.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fabrics.c b/src/fabrics.c index bedca44e9e..6f257c36c5 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -227,8 +227,9 @@ static void save_discovery_log(char *raw, const struct nvmf_discovery_log *log) { __cleanup_free char *path = NULL; static unsigned int save_count; - int fd, len, ret; + int fd, ret; uint64_t numrec; + size_t len; if (save_count) { if (asprintf(&path, "%s.%u", raw, save_count) < 0) { From 7dd0ce00ade546363be616f5c4f96bcccbf68d41 Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 17:04:44 +0530 Subject: [PATCH 260/263] fabrics: do not lose dim errors across controllers in fabrics_dim() fabrics_dim() overwrites ret on every dim_operation() call, so when several controllers or devices are addressed only the status of the last one is returned; an earlier failure is masked by a later success. Keep the first error in a separate variable and return that, so a failure on any controller is reported. Signed-off-by: Martin George --- src/fabrics.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/fabrics.c b/src/fabrics.c index 6f257c36c5..e6cb5e59e5 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -1824,7 +1824,7 @@ int fabrics_dim(const char *desc, int argc, char **argv) enum nvmf_dim_tas tas; struct libnvme_ctrl *c; char *p; - int ret; + int ret, err = 0; struct { char *nqn; @@ -1892,8 +1892,11 @@ int fabrics_dim(const char *desc, int argc, char **argv) libnvme_for_each_subsystem(h, s) { if (strcmp(libnvme_subsystem_get_subsysnqn(s), p)) continue; - libnvme_subsystem_for_each_ctrl(s, c) + libnvme_subsystem_for_each_ctrl(s, c) { ret = dim_operation(c, tas, p); + if (ret && !err) + err = ret; + } } } } @@ -1913,8 +1916,10 @@ int fabrics_dim(const char *desc, int argc, char **argv) return ret; } ret = dim_operation(c, tas, p); + if (ret && !err) + err = ret; } } - return ret; + return err; } From c650026efd8508d3e6fa710e24887af8de022494 Mon Sep 17 00:00:00 2001 From: Martin George Date: Mon, 19 Oct 2026 17:24:02 +0530 Subject: [PATCH 261/263] print-json: fix invalid free of trimmed key pointer json_output_key_value() passed strdup(key) through trim_white_space() and stored the result in a __cleanup_free pointer. trim_white_space() skips leading whitespace by advancing the pointer and returning that advanced position. When the key has leading whitespace, key_trim points into the middle of the strdup() allocation rather than to its start, so the __cleanup_free at scope exit is called on a pointer that was not returned by malloc()/strdup(), leading to undefined behavior and potential heap corruption. Keep the original allocation in its own __cleanup_free variable and let key_trim be a mere view into it, so the correct base pointer is freed. Signed-off-by: Martin George --- src/nvme-print-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvme-print-json.c b/src/nvme-print-json.c index 9b1b159a98..f46aef9e49 100644 --- a/src/nvme-print-json.c +++ b/src/nvme-print-json.c @@ -5850,7 +5850,8 @@ static void json_output_key_value(const char *key, const char *val, va_list ap) struct json_object *r = json_r; __cleanup_free char *value = NULL; - __cleanup_free char *key_trim = trim_white_space(strdup(key)); + __cleanup_free char *key_dup = strdup(key); + char *key_trim = trim_white_space(key_dup); if (vasprintf(&value, val, ap) < 0) value = NULL; From 73ee42f5afe70cd7522c730883eab21c3ab861bb Mon Sep 17 00:00:00 2001 From: Iain Date: Sat, 17 Oct 2026 02:10:39 +0100 Subject: [PATCH 262/263] nvme-print-stdout: do not truncate the 16-bit feature fields stdout_feature_show_fields() stores the decoded fields in a __u8, but TMPTH, TMT1, TMT2 and IIELL are 16 bits wide. Only the low byte is printed, so a temperature threshold of 343 K is shown as 87 K, and an IIELL of 30000us as 4400us. The JSON output is correct. Fixes: cdce9c00aa3c ("libnvme, nvme: add Idle I/O Exit Latency Limit (TP4204)") Fixes: 20b0e89cbd85 ("nvme-print-stdout: convert stdout_feature_show_fields() to shr_table") Signed-off-by: Iain --- src/nvme-print-stdout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index ec3116409b..15455b1684 100644 --- a/src/nvme-print-stdout.c +++ b/src/nvme-print-stdout.c @@ -2617,7 +2617,7 @@ void stdout_feature_show_fields(enum nvme_features_id fid, unsigned int result, const char *async = "Send async event"; const char *no_async = "Do not send async event"; struct shr_table *t; - __u8 field; + __u16 field; switch (fid) { case NVME_FEAT_FID_ARBITRATION: From 96e258b91c91f3f679873064c6e7cc5c0ab7ca3d Mon Sep 17 00:00:00 2001 From: Jeff Lien Date: Mon, 12 Oct 2026 15:38:00 -0500 Subject: [PATCH 263/263] ocp: Fix OCP telemetry timestamp field decode The decoded timestamp value is incorrect because bytes 6 and 7 of the timestamp field used for timestamp attributes are not masked off. test: cover OCP telemetry timestamp attributes Added test with non-zero values in timestamp bytes 6 and 7 to verify change. Signed-off-by: Jeff Lien Reviewed-by: Brandon Paupore --- src/field-parser.c | 12 ++++++++++-- tests/cli/ocp/ocp_internal_log_mock_test.py | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/field-parser.c b/src/field-parser.c index 41c31c9a28..e8a5dbacbd 100644 --- a/src/field-parser.c +++ b/src/field-parser.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -88,8 +89,15 @@ char *process_field_size_8(int offset, char *sfield, __u8 *buf) datastr = shr_hex_to_ascii(buffer); } else if (strstr(sfield, "Timestamp")) { char ts_buf[128]; - - shr_format_ts(shr_get_unaligned_le64(&buf[offset]), ts_buf); + /* + * OCP defines the NVMe Timestamp field in the + * (Feature Identifier 0Eh) get features structure, + * It's not a flat 64-bit value. + */ + struct nvme_timestamp *ts = + (struct nvme_timestamp *)&buf[offset]; + + shr_format_ts(int48_to_long(ts->timestamp), ts_buf); datastr = strdup(ts_buf); } else { if (asprintf(&datastr, "0x%"PRIx64"", diff --git a/tests/cli/ocp/ocp_internal_log_mock_test.py b/tests/cli/ocp/ocp_internal_log_mock_test.py index b9c3672109..38a5f5a0c9 100644 --- a/tests/cli/ocp/ocp_internal_log_mock_test.py +++ b/tests/cli/ocp/ocp_internal_log_mock_test.py @@ -80,6 +80,7 @@ LSP_CREATE = 1 LPA_TELEMETRY_DA4 = 0x40 +DA1_TIMESTAMP_MS = 1700000000123 OPC_SET_FEATURES = 0x09 OPC_GET_FEATURES = 0x0A @@ -374,6 +375,8 @@ def _telemetry(self, lid=layout.LID_TELEMETRY_HOST): reason + layout.REASON_VALID_FLAGS: b'\x07', reason + layout.REASON_VU_EXTENSION: bytes(range(0x80, 0xA0)), da1 + layout.DA1_MAJOR_VERSION: struct.pack('