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/.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 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/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/.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/Documentation/TESTING.md b/Documentation/TESTING.md index b295e44d02..54484ceca1 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 @@ -193,5 +225,94 @@ 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. + +## 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`). + +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 +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, +in a sanitizer build as described above: + +```bash +CC=clang CXX=clang++ meson setup .build-fuzz -Dfuzzing=true \ + -Db_sanitize=address,undefined -Db_lundef=false +meson compile -C .build-fuzz +``` + +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 .build-fuzz/corpus/argconfig +``` + +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 +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 +``` + +`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/Documentation/global-options.txt b/Documentation/global-options.txt index 632a02491e..256975a497 100644 --- a/Documentation/global-options.txt +++ b/Documentation/global-options.txt @@ -22,10 +22,20 @@ 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 + --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. + '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..c32c55fc16 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]=:: @@ -129,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/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-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-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-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. 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/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-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-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/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-arbitration.txt b/Documentation/nvme-feat-arbitration.txt index 34d2f70e83..2ca3283cec 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. @@ -49,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 fc31be94ce..1e8ae1a940 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=:: @@ -51,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 9f6cbbf13e..cde43bc00b 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). @@ -56,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 186661f185..8811bda5d6 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 ----------- @@ -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=:: @@ -49,7 +50,6 @@ OPTIONS --lbaros=:: LBA range overwriteable list. --h :: --hlbars=:: hide LBA range list. @@ -69,8 +69,16 @@ 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= | -S :: 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=:: --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=:: @@ -47,6 +56,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..6dbefde583 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 :: @@ -38,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 9cc40e1885..b33e7b6f38 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 :: @@ -38,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-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-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..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=:: @@ -66,7 +68,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 +105,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/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-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-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-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/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/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/NEWS.md b/NEWS.md index 0e09e21d2c..94e30bb4bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,143 @@ ### 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. + +* 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. + +* `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. + +* `--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 + 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. + +* `--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. + +* 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. + +* 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. + +* 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. + +* 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/). + +* 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. `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 + 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 + 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 + 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` + 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. @@ -100,6 +237,43 @@ ### 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`, + `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. + +* `--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 @@ -139,13 +313,46 @@ * 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 + 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 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. @@ -194,6 +401,20 @@ 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. + +* 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/completions/TESTING.md b/completions/TESTING.md index 09797e56a1..1c2412b363 100644 --- a/completions/TESTING.md +++ b/completions/TESTING.md @@ -69,8 +69,11 @@ 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`. +- [ ] **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/_nvme b/completions/_nvme index 55050f041e..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 @@ -296,8 +315,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _amzn_id_ctrl _amzn_id_ctrl=( --dry-run':show command instead of executing' @@ -324,8 +344,9 @@ _nvme () { ;; (stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _amzn_stats _amzn_stats=( --details':Detail IO histogram of each block size ranges' @@ -373,26 +394,39 @@ _nvme () { case ${words[2]} in (convert) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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)' -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' ) _arguments '*:: :->subcmds' _nvme_describe "nvme config convert options" _config_convert - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output" && return local _config_create _config_create=( @@ -467,12 +501,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config create options" _config_create - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (show) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" && return local _config_show _config_show=( @@ -494,25 +528,57 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme config show options" _config_show - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (status) - _nvme_has_device || compadd -- /dev/nvme*(N) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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 ;; (validate) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) _arguments '*:: :->subcmds' _nvme_describe "nvme config validate options" _config_validate - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -533,8 +599,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -580,8 +648,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dell_id_ctrl _dell_id_ctrl=( --dry-run':show command instead of executing' @@ -626,8 +695,9 @@ _nvme () { case ${words[2]} in (smart-log-add|stat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -668,8 +738,10 @@ _nvme () { case ${words[2]} in (receive) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -706,8 +778,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -771,8 +845,9 @@ _nvme () { case ${words[2]} in (add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_add _exclusion_add=( --dry-run':show command instead of executing' @@ -795,12 +870,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion add options" _exclusion_add - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_create _exclusion_create=( --dry-run':show command instead of executing' @@ -821,12 +896,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion create options" _exclusion_create - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_delete _exclusion_delete=( --dry-run':show command instead of executing' @@ -847,12 +922,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion delete options" _exclusion_delete - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (edit) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_edit _exclusion_edit=( --dry-run':show command instead of executing' @@ -873,12 +948,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion edit options" _exclusion_edit - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_list _exclusion_list=( --dry-run':show command instead of executing' @@ -899,12 +974,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion list options" _exclusion_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (remove) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _exclusion_remove _exclusion_remove=( --dry-run':show command instead of executing' @@ -925,7 +1000,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme exclusion remove options" _exclusion_remove - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -953,15 +1027,14 @@ _nvme () { case ${words[2]} in (configs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_configs _fdp_configs=( --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' @@ -983,8 +1056,9 @@ _nvme () { ;; (events) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_events _fdp_events=( --dry-run':show command instead of executing' @@ -1013,8 +1087,9 @@ _nvme () { ;; (feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_feature _fdp_feature=( --disable':Disable current FDP configuration' @@ -1043,8 +1118,10 @@ _nvme () { ;; (set-events) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -1077,8 +1154,9 @@ _nvme () { ;; (stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_stats _fdp_stats=( --dry-run':show command instead of executing' @@ -1105,8 +1183,10 @@ _nvme () { ;; (status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -1133,8 +1213,10 @@ _nvme () { ;; (update) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -1161,8 +1243,9 @@ _nvme () { ;; (usage) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fdp_usage _fdp_usage=( --dry-run':show command instead of executing' @@ -1224,16 +1307,16 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_arbitration _feat_arbitration=( --ab=':arbitration burst' -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' @@ -1261,9 +1344,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_async_event_conf _feat_async_event_conf=( --dry-run':show command instead of executing' @@ -1292,9 +1376,11 @@ _nvme () { ;; (err-recovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_err_recovery _feat_err_recovery=( --dry-run':show command instead of executing' @@ -1327,9 +1413,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_hctm _feat_hctm=( --dry-run':show command instead of executing' @@ -1360,9 +1447,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_host_behavior_support _feat_host_behavior_support=( --acre=':Advanced Command Retry Enable (0 or 1)' @@ -1373,7 +1461,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' @@ -1399,9 +1486,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_int_coalesce _feat_int_coalesce=( --dry-run':show command instead of executing' @@ -1432,9 +1520,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_int_vector_config _feat_int_vector_config=( --cd':coalescing disable' @@ -1465,9 +1554,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_keep_alive_timer _feat_keep_alive_timer=( --dry-run':show command instead of executing' @@ -1496,16 +1586,17 @@ _nvme () { ;; (lba-range-type) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_lba_range_type _feat_lba_range_type=( --dry-run':show command instead of executing' --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 +1612,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,...]);' @@ -1541,9 +1631,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_num_queues _feat_num_queues=( --dry-run':show command instead of executing' @@ -1574,9 +1665,11 @@ _nvme () { ;; (perf-characteristics) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--vs-data -V" && return local _feat_perf_characteristics _feat_perf_characteristics=( @@ -1618,9 +1711,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_power_limit _feat_power_limit=( --dry-run':show command instead of executing' @@ -1653,9 +1747,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_power_meas _feat_power_meas=( --act=':action [0-1]: stop|start' @@ -1687,9 +1782,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_power_mgmt _feat_power_mgmt=( --dry-run':show command instead of executing' @@ -1720,9 +1816,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_power_thresh _feat_power_thresh=( --dry-run':show command instead of executing' @@ -1759,9 +1856,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_temp_thresh _feat_temp_thresh=( --dry-run':show command instead of executing' @@ -1777,7 +1875,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' @@ -1796,9 +1893,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_timestamp _feat_timestamp=( --dry-run':show command instead of executing' @@ -1827,9 +1925,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_volatile_wc _feat_volatile_wc=( --dry-run':show command instead of executing' @@ -1858,9 +1957,10 @@ _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 + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _feat_write_atom_normal _feat_write_atom_normal=( --dn':disable normal' @@ -1909,8 +2009,9 @@ _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-version" "1 2" && return + "--output-format -o" "normal json binary" \ + "--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' @@ -1941,8 +2042,9 @@ _nvme () { ;; (download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _fw_download _fw_download=( @@ -1997,8 +2099,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _huawei_id_ctrl _huawei_id_ctrl=( --dry-run':show command instead of executing' @@ -2025,14 +2128,15 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' @@ -2045,7 +2149,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme huawei list options" _huawei_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -2068,8 +2171,9 @@ _nvme () { case ${words[2]} in (crit-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ibm_crit_log _ibm_crit_log=( --dry-run':show command instead of executing' @@ -2094,8 +2198,9 @@ _nvme () { ;; (persist-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -2124,8 +2229,9 @@ _nvme () { ;; (vpd) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ibm_vpd _ibm_vpd=( --dry-run':show command instead of executing' @@ -2184,8 +2290,9 @@ _nvme () { case ${words[2]} in (ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ctrl _id_ctrl=( --dry-run':show command instead of executing' @@ -2212,8 +2319,10 @@ _nvme () { ;; (ctrl-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2223,7 +2332,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' @@ -2240,8 +2349,9 @@ _nvme () { ;; (domain) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_domain _id_domain=( --dom-id=':identifier of desired domain' @@ -2266,8 +2376,9 @@ _nvme () { ;; (endgrp-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_endgrp_list _id_endgrp_list=( --dry-run':show command instead of executing' @@ -2275,7 +2386,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' @@ -2292,8 +2403,9 @@ _nvme () { ;; (iocs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_iocs _id_iocs=( --controller-id=':identifier of desired controller' @@ -2318,8 +2430,10 @@ _nvme () { ;; (ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2349,8 +2463,10 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2377,8 +2493,9 @@ _nvme () { ;; (ns-granularity) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_granularity _id_ns_granularity=( --dry-run':show command instead of executing' @@ -2401,8 +2518,10 @@ _nvme () { ;; (ns-ind) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2429,8 +2548,9 @@ _nvme () { ;; (ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -2457,8 +2577,10 @@ _nvme () { ;; (ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2470,7 +2592,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' @@ -2487,8 +2609,9 @@ _nvme () { ;; (nvm-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvm_ctrl _id_nvm_ctrl=( --dry-run':show command instead of executing' @@ -2511,8 +2634,10 @@ _nvme () { ;; (nvm-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2539,8 +2664,9 @@ _nvme () { ;; (nvm-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -2567,8 +2693,9 @@ _nvme () { ;; (nvmset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvmset _id_nvmset=( --dry-run':show command instead of executing' @@ -2593,8 +2720,9 @@ _nvme () { ;; (primary-ctrl-caps) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_primary_ctrl_caps _id_primary_ctrl_caps=( --cntlid=':Controller ID' @@ -2619,8 +2747,9 @@ _nvme () { ;; (secondary-ctrl-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_secondary_ctrl_list _id_secondary_ctrl_list=( --cntid=':lowest controller identifier to display' @@ -2647,8 +2776,9 @@ _nvme () { ;; (uuid) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_uuid _id_uuid=( --dry-run':show command instead of executing' @@ -2691,8 +2821,9 @@ _nvme () { case ${words[2]} in (get-cdump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _innogrit_get_cdump _innogrit_get_cdump=( --dry-run':show command instead of executing' @@ -2715,8 +2846,9 @@ _nvme () { ;; (get-eventlog) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _innogrit_get_eventlog _innogrit_get_eventlog=( --dry-run':show command instead of executing' @@ -2756,8 +2888,9 @@ _nvme () { case ${words[2]} in (nvme-vendor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -2804,8 +2937,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_id_ctrl _intel_id_ctrl=( --dry-run':show command instead of executing' @@ -2832,8 +2966,10 @@ _nvme () { ;; (internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -2869,8 +3005,9 @@ _nvme () { ;; (lat-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_lat_stats _intel_lat_stats=( --dry-run':show command instead of executing' @@ -2899,12 +3036,26 @@ _nvme () { ;; (lat-stats-tracking) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -2914,8 +3065,9 @@ _nvme () { ;; (market-name) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_market_name _intel_market_name=( --dry-run':show command instead of executing' @@ -2940,8 +3092,9 @@ _nvme () { ;; (set-bucket-thresholds) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 ...' @@ -2968,8 +3121,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -2998,8 +3153,9 @@ _nvme () { ;; (temp-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _intel_temp_stats _intel_temp_stats=( --dry-run':show command instead of executing' @@ -3042,8 +3198,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -3077,8 +3235,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -3136,8 +3296,9 @@ _nvme () { case ${words[2]} in (check-kxchap-secret) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -3164,12 +3325,12 @@ _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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -3202,12 +3363,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys check-tls-psk options" _keys_check_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (export) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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' @@ -3230,12 +3392,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys export options" _keys_export - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-kxchap-secret) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -3260,12 +3422,13 @@ _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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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.' @@ -3304,12 +3467,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys gen-tls-psk options" _keys_gen_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (import) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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' @@ -3336,12 +3500,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys import options" _keys_import - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (insert-tls-psk) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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.' @@ -3376,12 +3541,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys insert-tls-psk options" _keys_insert_tls_psk - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (revoke) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _keys_revoke _keys_revoke=( --dry-run':show command instead of executing' @@ -3406,7 +3571,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme keys revoke options" _keys_revoke - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -3433,8 +3597,9 @@ _nvme () { case ${words[2]} in (create-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_create_cdq _lm_create_cdq=( --cntlid=':Controller ID' @@ -3464,8 +3629,9 @@ _nvme () { ;; (delete-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_delete_cdq _lm_delete_cdq=( --cdqid=':Controller Data Queue ID' @@ -3490,8 +3656,9 @@ _nvme () { ;; (get-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_get_cdq _lm_get_cdq=( --cdqid=':Controller Data Queue ID' @@ -3516,9 +3683,9 @@ _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 + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _lm_migration_recv _lm_migration_recv=( @@ -3529,8 +3696,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)' @@ -3560,9 +3725,9 @@ _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 + "--timeout" "infinite -1" && return _nvme_opt_files "--input-file -f" && return local _lm_migration_send _lm_migration_send=( @@ -3608,15 +3773,15 @@ _nvme () { ;; (set-cdq) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lm_set_cdq _lm_set_cdq=( --cdqid=':Controller Data Queue ID' -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' @@ -3638,9 +3803,9 @@ _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 + "--timeout" "infinite -1" && return local _lm_track_send _lm_track_send=( --cdqid=':Controller Data Queue ID' @@ -3720,8 +3885,9 @@ _nvme () { case ${words[2]} in (ana) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_ana _log_ana=( --dry-run':show command instead of executing' @@ -3746,8 +3912,9 @@ _nvme () { ;; (ave-discovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_ave_discovery _log_ave_discovery=( --dry-run':show command instead of executing' @@ -3772,8 +3939,9 @@ _nvme () { ;; (boot-part) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _log_boot_part _log_boot_part=( @@ -3801,8 +3969,9 @@ _nvme () { ;; (changed-alloc-ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -3827,8 +3996,9 @@ _nvme () { ;; (changed-ns-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -3853,8 +4023,10 @@ _nvme () { ;; (dispersed-ns-participating-nss) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -3879,8 +4051,9 @@ _nvme () { ;; (effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_effects _log_effects=( --csi=':command set identifier' @@ -3907,8 +4080,9 @@ _nvme () { ;; (endurance) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_endurance _log_endurance=( --dry-run':show command instead of executing' @@ -3933,8 +4107,9 @@ _nvme () { ;; (endurance-event-agg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -3963,8 +4138,10 @@ _nvme () { ;; (error) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -4007,8 +4184,9 @@ _nvme () { ;; (fid-support-effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4031,8 +4209,9 @@ _nvme () { ;; (fw) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_fw _log_fw=( --dry-run':show command instead of executing' @@ -4057,8 +4236,9 @@ _nvme () { ;; (host-discovery) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_host_discovery _log_host_discovery=( --all-host-entries':All Host Entries' @@ -4085,8 +4265,9 @@ _nvme () { ;; (lba-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_lba_status _log_lba_status=( --dry-run':show command instead of executing' @@ -4111,8 +4292,9 @@ _nvme () { ;; (media-unit-stat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_media_unit_stat _log_media_unit_stat=( --domain-id=':Domain Identifier' @@ -4139,8 +4321,9 @@ _nvme () { ;; (mgmt-addr-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4163,8 +4346,9 @@ _nvme () { ;; (mi-cmd-support-effects) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4187,8 +4371,9 @@ _nvme () { ;; (persistent-event) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -4217,8 +4402,9 @@ _nvme () { ;; (phy-rx-eom) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_phy_rx_eom _log_phy_rx_eom=( --controller=':Target Controller ID.' @@ -4245,8 +4431,9 @@ _nvme () { ;; (power-measurement) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_power_measurement _log_power_measurement=( --dry-run':show command instead of executing' @@ -4271,8 +4458,9 @@ _nvme () { ;; (pred-lat-event-agg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4301,8 +4489,9 @@ _nvme () { ;; (predictable-lat) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_predictable_lat _log_predictable_lat=( --dry-run':show command instead of executing' @@ -4329,8 +4518,9 @@ _nvme () { ;; (pull-model-ddc-req) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4355,8 +4545,9 @@ _nvme () { ;; (reachability-associations) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_reachability_associations _log_reachability_associations=( --associations-only':Return Associations Only' @@ -4383,8 +4574,9 @@ _nvme () { ;; (reachability-groups) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_reachability_groups _log_reachability_groups=( --dry-run':show command instead of executing' @@ -4411,8 +4603,9 @@ _nvme () { ;; (resv-notif) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_resv_notif _log_resv_notif=( --dry-run':show command instead of executing' @@ -4435,8 +4628,9 @@ _nvme () { ;; (rotational-media-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4461,8 +4655,9 @@ _nvme () { ;; (sanitize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_sanitize _log_sanitize=( --dry-run':show command instead of executing' @@ -4489,8 +4684,9 @@ _nvme () { ;; (self-test) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_self_test _log_self_test=( --dry-run':show command instead of executing' @@ -4515,8 +4711,10 @@ _nvme () { ;; (smart) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -4543,8 +4741,9 @@ _nvme () { ;; (supported-cap-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_supported_cap_config _log_supported_cap_config=( --domain-id=':Domain Identifier' @@ -4571,8 +4770,9 @@ _nvme () { ;; (supported-pages) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _log_supported_pages _log_supported_pages=( --dry-run':show command instead of executing' @@ -4595,8 +4795,9 @@ _nvme () { ;; (telemetry) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _log_telemetry _log_telemetry=( @@ -4650,8 +4851,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _mangoboost_id_ctrl _mangoboost_id_ctrl=( --dry-run':show command instead of executing' @@ -4709,8 +4911,9 @@ _nvme () { case ${words[2]} in (clear-error-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4733,8 +4936,9 @@ _nvme () { ;; (get-pm-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4757,8 +4961,9 @@ _nvme () { ;; (lat-get-feature-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4781,8 +4986,9 @@ _nvme () { ;; (lat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _memblaze_lat_log _memblaze_lat_log=( --dry-run':show command instead of executing' @@ -4807,8 +5013,9 @@ _nvme () { ;; (lat-log-print) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4831,8 +5038,9 @@ _nvme () { ;; (lat-log-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4857,8 +5065,9 @@ _nvme () { ;; (lat-set-feature-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4870,14 +5079,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' @@ -4891,12 +5100,26 @@ _nvme () { ;; (lat-stats) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -4906,8 +5129,9 @@ _nvme () { ;; (lat-stats-print) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4932,8 +5156,9 @@ _nvme () { ;; (lat-stats-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -4958,12 +5183,13 @@ _nvme () { ;; (perf-stats-print-x) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' - --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' @@ -4986,8 +5212,10 @@ _nvme () { ;; (select-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _memblaze_select_download=( @@ -5015,8 +5243,9 @@ _nvme () { ;; (set-pm-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5043,8 +5272,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -5071,10 +5302,24 @@ _nvme () { ;; (smart-log-add-x) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' + --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' ) @@ -5126,8 +5371,9 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5150,8 +5396,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5174,8 +5421,9 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5194,12 +5442,12 @@ _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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5222,8 +5470,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_id_ctrl _micron_id_ctrl=( --dry-run':show command instead of executing' @@ -5246,8 +5495,9 @@ _nvme () { ;; (latency-logs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_latency_logs _micron_latency_logs=( --dry-run':show command instead of executing' @@ -5270,8 +5520,10 @@ _nvme () { ;; (latency-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _micron_latency_stats=( --command=':command to display stats - all|read|write|trim, default is all' @@ -5296,8 +5548,11 @@ _nvme () { ;; (latency-tracking) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _micron_latency_tracking=( --command=':commands to monitor for - all|read|write|trim, default is all i.e, enabled for all commands' @@ -5326,8 +5581,9 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5350,8 +5606,9 @@ _nvme () { ;; (plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_plugin_version _micron_plugin_version=( --dry-run':show command instead of executing' @@ -5370,12 +5627,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme micron plugin-version options" _micron_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (select-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _micron_select_download=( @@ -5403,8 +5661,9 @@ _nvme () { ;; (smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _micron_smart_log _micron_smart_log=( --dry-run':show command instead of executing' @@ -5427,8 +5686,9 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5451,8 +5711,9 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -5475,8 +5736,9 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5499,8 +5761,9 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5523,8 +5786,10 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" \ + "--type -t" "host controller" && return _nvme_opt_files "--package -p" && return local _micron_vs_internal_log _micron_vs_internal_log=( @@ -5554,8 +5819,9 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5578,8 +5844,9 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5602,8 +5869,9 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5626,8 +5894,9 @@ _nvme () { ;; (vs-smart-ext-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5650,8 +5919,10 @@ _nvme () { ;; (vs-smbus-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _micron_vs_smbus_option=( --dry-run':show command instead of executing' @@ -5680,8 +5951,10 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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 _micron_vs_telemetry_controller_option=( --dry-run':show command instead of executing' @@ -5708,8 +5981,9 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5732,8 +6006,9 @@ _nvme () { ;; (vs-vendor-telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5756,8 +6031,9 @@ _nvme () { ;; (vs-work-load-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' @@ -5797,15 +6073,16 @@ _nvme () { case ${words[2]} in (show) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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' -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' @@ -5824,7 +6101,6 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme nbft show options" _nbft_show - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -5846,14 +6122,15 @@ _nvme () { case ${words[2]} in (ontapdevices) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal column json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' @@ -5870,14 +6147,15 @@ _nvme () { ;; (smdevices) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal column json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' @@ -5915,8 +6193,10 @@ _nvme () { case ${words[2]} in (attach) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -5945,8 +6225,9 @@ _nvme () { ;; (create) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_create _ns_create=( --anagrp-id=':ANA Group Identifier (ANAGRPID)' @@ -6009,8 +6290,10 @@ _nvme () { ;; (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -6037,8 +6320,10 @@ _nvme () { ;; (detach) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -6067,8 +6352,9 @@ _nvme () { ;; (get-id) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_get_id _ns_get_id=( --dry-run':show command instead of executing' @@ -6108,8 +6394,9 @@ _nvme () { case ${words[2]} in (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvidia_id_ctrl _nvidia_id_ctrl=( --dry-run':show command instead of executing' @@ -6154,8 +6441,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -6193,8 +6482,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -6278,8 +6569,9 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6304,8 +6596,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6330,8 +6623,9 @@ _nvme () { ;; (device-capability-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6356,9 +6650,10 @@ _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 + "--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=( --dry-run':show command instead of executing' @@ -6389,8 +6684,9 @@ _nvme () { ;; (error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6415,8 +6711,9 @@ _nvme () { ;; (fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6439,9 +6736,11 @@ _nvme () { ;; (get-clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _ocp_get_clear_pcie_correctable_errors _ocp_get_clear_pcie_correctable_errors=( --dry-run':show command instead of executing' @@ -6470,9 +6769,10 @@ _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 + "--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=( --dry-run':show command instead of executing' @@ -6497,9 +6797,10 @@ _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 + "--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=( --all':Print out all 3 values at once - Current, Default, and Saved' @@ -6528,9 +6829,10 @@ _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 + "--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=( --dry-run':show command instead of executing' @@ -6557,9 +6859,10 @@ _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 + "--sel -s" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" && return local _ocp_get_error_injection _ocp_get_error_injection=( --all-ns':Apply to all namespaces' @@ -6588,9 +6891,11 @@ _nvme () { ;; (get-idle-wakeup-time) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _ocp_get_idle_wakeup_time _ocp_get_idle_wakeup_time=( --dry-run':show command instead of executing' @@ -6619,9 +6924,11 @@ _nvme () { ;; (get-latency-monitor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _ocp_get_latency_monitor _ocp_get_latency_monitor=( --dry-run':show command instead of executing' @@ -6650,9 +6957,10 @@ _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 + "--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=( --dry-run':show command instead of executing' @@ -6677,9 +6985,11 @@ _nvme () { ;; (get-telemetry-profile) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _ocp_get_telemetry_profile _ocp_get_telemetry_profile=( --dry-run':show command instead of executing' @@ -6709,8 +7019,9 @@ _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-version" "1 2" && return + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ocp_hardware_component_log _ocp_hardware_component_log=( --comp-id=':component identifier' @@ -6737,9 +7048,13 @@ _nvme () { ;; (internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return - _nvme_opt_files "--output-file -f" && return + _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" \ + "--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.'\'';' @@ -6776,8 +7091,9 @@ _nvme () { ;; (latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6802,8 +7118,9 @@ _nvme () { ;; (persistent-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -6832,8 +7149,9 @@ _nvme () { ;; (set-dssd-async-event-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6860,8 +7178,9 @@ _nvme () { ;; (set-dssd-power-state-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -6890,12 +7209,13 @@ _nvme () { ;; (set-enable-ieee1667-silo) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' - --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' @@ -6920,8 +7240,9 @@ _nvme () { ;; (set-error-injection) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--data -d" && return local _ocp_set_error_injection _ocp_set_error_injection=( @@ -6957,8 +7278,9 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -7001,8 +7323,9 @@ _nvme () { ;; (set-plp-health-check-interval) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7031,8 +7354,9 @@ _nvme () { ;; (set-telemetry-profile) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7057,8 +7381,9 @@ _nvme () { ;; (smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7083,8 +7408,9 @@ _nvme () { ;; (tcg-configuration-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7109,8 +7435,9 @@ _nvme () { ;; (telemetry-string-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -7138,8 +7465,9 @@ _nvme () { ;; (unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7184,8 +7512,9 @@ _nvme () { case ${words[2]} in (delete) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_delete _registry_delete=( --attr=':attribute to remove (default: whole entry)' @@ -7210,8 +7539,9 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_list _registry_list=( --dry-run':show command instead of executing' @@ -7230,12 +7560,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme registry list options" _registry_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (retrieve) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_retrieve _registry_retrieve=( --attr=':attribute name (e.g. owner, note)' @@ -7260,8 +7590,9 @@ _nvme () { ;; (update) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _registry_update _registry_update=( --attr=':attribute name (e.g. note, owner)' @@ -7308,8 +7639,10 @@ _nvme () { case ${words[2]} in (acquire) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7344,8 +7677,10 @@ _nvme () { ;; (register) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7380,8 +7715,10 @@ _nvme () { ;; (release) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7414,8 +7751,10 @@ _nvme () { ;; (report) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7469,8 +7808,9 @@ _nvme () { case ${words[2]} in (info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _rpmb_info _rpmb_info=( --dry-run':show command instead of executing' @@ -7493,8 +7833,9 @@ _nvme () { ;; (program-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" && return local _rpmb_program_key _rpmb_program_key=( @@ -7524,8 +7865,9 @@ _nvme () { ;; (read-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--msgfile -f" && return local _rpmb_read_config _rpmb_read_config=( @@ -7551,8 +7893,9 @@ _nvme () { ;; (read-counter) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _rpmb_read_counter _rpmb_read_counter=( --dry-run':show command instead of executing' @@ -7577,13 +7920,13 @@ _nvme () { ;; (read-data) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--msgfile -f" && return 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' @@ -7610,8 +7953,9 @@ _nvme () { ;; (write-config) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return local _rpmb_write_config @@ -7644,14 +7988,14 @@ _nvme () { ;; (write-data) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--keyfile -g" \ "--msgfile -f" && return 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' @@ -7701,8 +8045,10 @@ _nvme () { case ${words[2]} in (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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=( @@ -7712,7 +8058,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.' @@ -7764,8 +8109,9 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7790,8 +8136,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7816,8 +8163,9 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7836,12 +8184,13 @@ _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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7868,8 +8217,10 @@ _nvme () { ;; (get-host-tele) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -7898,8 +8249,9 @@ _nvme () { ;; (plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _seagate_plugin_version _seagate_plugin_version=( --dry-run':show command instead of executing' @@ -7918,12 +8270,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme seagate plugin-version options" _seagate_plugin_version - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7946,8 +8298,10 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -7975,8 +8329,9 @@ _nvme () { ;; (vs-log-page-sup) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -7999,8 +8354,9 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8023,8 +8379,9 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8047,8 +8404,9 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8089,8 +8447,10 @@ _nvme () { case ${words[2]} in (recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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)' @@ -8129,8 +8489,10 @@ _nvme () { ;; (send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -8191,12 +8553,24 @@ _nvme () { case ${words[2]} in (discover|1) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -8206,10 +8580,24 @@ _nvme () { ;; (initialize) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -8219,12 +8607,26 @@ _nvme () { ;; (lock) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -8234,16 +8636,51 @@ _nvme () { ;; (password) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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" \ + "--timeout" "infinite -1" && 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' ) @@ -8253,12 +8690,26 @@ _nvme () { ;; (unlock) + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' ) @@ -8294,8 +8745,9 @@ _nvme () { case ${words[2]} in (change-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_change_cap _sfx_change_cap=( --cap=':cap size in GB' @@ -8324,8 +8776,9 @@ _nvme () { ;; (dump-evtlog) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--file -f" \ "--output -O" && return local _sfx_dump_evtlog @@ -8360,8 +8813,9 @@ _nvme () { ;; (expand-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_expand_cap _sfx_expand_cap=( --dry-run':show command instead of executing' @@ -8394,8 +8848,9 @@ _nvme () { ;; (get-bad-block) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8418,8 +8873,10 @@ _nvme () { ;; (get-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -8446,8 +8903,9 @@ _nvme () { ;; (lat-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_lat_stats _sfx_lat_stats=( --dry-run':show command instead of executing' @@ -8474,8 +8932,9 @@ _nvme () { ;; (query-cap) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_query_cap _sfx_query_cap=( --dry-run':show command instead of executing' @@ -8500,8 +8959,10 @@ _nvme () { ;; (set-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -8532,8 +8993,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -8562,8 +9025,9 @@ _nvme () { ;; (status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sfx_status _sfx_status=( --dry-run':show command instead of executing' @@ -8608,9 +9072,11 @@ _nvme () { case ${words[2]} in (get-additional-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ + _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 current default saved supported" \ + "--timeout" "infinite -1" && return local _shannon_get_additional_feature _shannon_get_additional_feature=( --cdw11=':dword 11 for interrupt vector config' @@ -8620,8 +9086,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' @@ -8647,8 +9111,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _shannon_id_ctrl _shannon_id_ctrl=( --dry-run':show command instead of executing' @@ -8675,8 +9140,10 @@ _nvme () { ;; (set-additioal-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -8712,8 +9179,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -8784,8 +9253,9 @@ _nvme () { case ${words[2]} in (capabilities) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_capabilities _sndk_capabilities=( --dry-run':show command instead of executing' @@ -8808,8 +9278,9 @@ _nvme () { ;; (clear-assert-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8832,8 +9303,9 @@ _nvme () { ;; (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8856,8 +9328,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8880,8 +9353,9 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8904,8 +9378,10 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -8930,8 +9406,9 @@ _nvme () { ;; (cu-smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -8956,8 +9433,9 @@ _nvme () { ;; (drive-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sndk_drive_resize _sndk_drive_resize=( --dry-run':show command instead of executing' @@ -8982,8 +9460,9 @@ _nvme () { ;; (get-dev-capabilities-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9006,8 +9485,9 @@ _nvme () { ;; (get-drive-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9030,8 +9510,9 @@ _nvme () { ;; (get-error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9054,8 +9535,9 @@ _nvme () { ;; (get-latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9078,8 +9560,9 @@ _nvme () { ;; (get-unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9102,8 +9585,9 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9126,8 +9610,10 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9154,8 +9640,9 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -9198,8 +9685,10 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9224,8 +9713,10 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9250,8 +9741,9 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9274,8 +9766,9 @@ _nvme () { ;; (vs-error-reason-identifier) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -9303,8 +9796,9 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9327,8 +9821,10 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9353,24 +9849,36 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_files "--output-file -O" \ - "--type -t" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--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.' -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' ) @@ -9380,8 +9888,9 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9404,8 +9913,9 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9428,8 +9938,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9460,8 +9972,9 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -9490,8 +10003,9 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9546,8 +10060,9 @@ _nvme () { case ${words[2]} in (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9572,8 +10087,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9598,8 +10114,9 @@ _nvme () { ;; (cloud-SSDplugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9618,12 +10135,12 @@ _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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9646,8 +10163,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_id_ctrl _solidigm_id_ctrl=( --dry-run':show command instead of executing' @@ -9674,8 +10192,9 @@ _nvme () { ;; (latency-tracking-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_latency_tracking_log _solidigm_latency_tracking_log=( --disable':Disable Latency Tracking' @@ -9708,8 +10227,9 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9732,8 +10252,9 @@ _nvme () { ;; (market-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_market_log _solidigm_market_log=( --dry-run':show command instead of executing' @@ -9758,8 +10279,9 @@ _nvme () { ;; (parse-telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config-file -j" \ "--source-file -s" && return local _solidigm_parse_telemetry_log @@ -9794,8 +10316,10 @@ _nvme () { ;; (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -9820,8 +10344,9 @@ _nvme () { ;; (temp-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_temp_stats _solidigm_temp_stats=( --dry-run':show command instead of executing' @@ -9846,14 +10371,15 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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' --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' @@ -9870,8 +10396,9 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9894,8 +10421,10 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--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=( @@ -9923,8 +10452,9 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -9949,8 +10479,9 @@ _nvme () { ;; (workload-tracker) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _solidigm_workload_tracker _solidigm_workload_tracker=( --disable':tracker disable' @@ -10014,8 +10545,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -10063,8 +10596,9 @@ _nvme () { case ${words[2]} in (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10087,8 +10621,9 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--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_internal_log _toshiba_vs_internal_log=( @@ -10116,8 +10651,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -10165,8 +10702,9 @@ _nvme () { case ${words[2]} in (badblock) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _transcend_badblock _transcend_badblock=( --dry-run':show command instead of executing' @@ -10189,8 +10727,9 @@ _nvme () { ;; (healthvalue) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _transcend_healthvalue _transcend_healthvalue=( --dry-run':show command instead of executing' @@ -10230,7 +10769,6 @@ _nvme () { case ${words[2]} in (dump-command-metadata) - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (*) @@ -10252,8 +10790,9 @@ _nvme () { case ${words[2]} in (save-smart-to-vtview-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -10285,8 +10824,9 @@ _nvme () { ;; (show-identify) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _virtium_show_identify _virtium_show_identify=( --dry-run':show command instead of executing' @@ -10362,8 +10902,9 @@ _nvme () { case ${words[2]} in (cap-diag) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_cap_diag _wdc_cap_diag=( @@ -10391,8 +10932,9 @@ _nvme () { ;; (capabilities) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_capabilities _wdc_capabilities=( --dry-run':show command instead of executing' @@ -10415,8 +10957,9 @@ _nvme () { ;; (clear-assert-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10439,8 +10982,9 @@ _nvme () { ;; (clear-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10463,8 +11007,9 @@ _nvme () { ;; (clear-pcie-correctable-errors) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10487,8 +11032,9 @@ _nvme () { ;; (cloud-SSD-plugin-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10511,8 +11057,10 @@ _nvme () { ;; (cloud-boot-SSD-version) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -10537,8 +11085,9 @@ _nvme () { ;; (cu-smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10563,8 +11112,9 @@ _nvme () { ;; (drive-essentials) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--dir-name -d" && return local _wdc_drive_essentials _wdc_drive_essentials=( @@ -10590,8 +11140,9 @@ _nvme () { ;; (drive-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _wdc_drive_log _wdc_drive_log=( @@ -10617,8 +11168,9 @@ _nvme () { ;; (drive-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_drive_resize _wdc_drive_resize=( --dry-run':show command instead of executing' @@ -10643,8 +11195,9 @@ _nvme () { ;; (enc-get-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -10674,8 +11227,9 @@ _nvme () { ;; (get-crash-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -10701,8 +11255,9 @@ _nvme () { ;; (get-dev-capabilities-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10725,8 +11280,9 @@ _nvme () { ;; (get-drive-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10749,8 +11305,9 @@ _nvme () { ;; (get-error-recovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10773,8 +11330,9 @@ _nvme () { ;; (get-latency-monitor-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10797,8 +11355,9 @@ _nvme () { ;; (get-pfail-dump) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -10824,8 +11383,9 @@ _nvme () { ;; (get-unsupported-reqs-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10848,8 +11408,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_id_ctrl _wdc_id_ctrl=( --dry-run':show command instead of executing' @@ -10876,8 +11437,9 @@ _nvme () { ;; (log-page-directory) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -10900,8 +11462,10 @@ _nvme () { ;; (namespace-resize) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -10928,8 +11492,9 @@ _nvme () { ;; (purge) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_purge _wdc_purge=( --dry-run':show command instead of executing' @@ -10952,8 +11517,9 @@ _nvme () { ;; (purge-monitor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _wdc_purge_monitor _wdc_purge_monitor=( --dry-run':show command instead of executing' @@ -10976,8 +11542,9 @@ _nvme () { ;; (set-latency-monitor-feature) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -11020,8 +11587,10 @@ _nvme () { ;; (vs-cloud-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11046,8 +11615,10 @@ _nvme () { ;; (vs-device-waf) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11072,8 +11643,9 @@ _nvme () { ;; (vs-drive-info) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -11096,8 +11668,9 @@ _nvme () { ;; (vs-error-reason-identifier) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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=( @@ -11125,8 +11698,9 @@ _nvme () { ;; (vs-fw-activate-history) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -11149,8 +11723,10 @@ _nvme () { ;; (vs-hw-rev-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11175,24 +11751,36 @@ _nvme () { ;; (vs-internal-log) - _nvme_opt_files "--output-file -O" \ - "--type -t" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--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.' -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' ) @@ -11202,8 +11790,9 @@ _nvme () { ;; (vs-nand-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -11226,8 +11815,9 @@ _nvme () { ;; (vs-pcie-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -11250,8 +11840,10 @@ _nvme () { ;; (vs-smart-add-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11282,8 +11874,9 @@ _nvme () { ;; (vs-telemetry-controller-option) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -11312,8 +11905,9 @@ _nvme () { ;; (vs-temperature-stats) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -11353,8 +11947,10 @@ _nvme () { case ${words[2]} in (smart-log-add) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11412,8 +12008,10 @@ _nvme () { case ${words[2]} in (changed-zone-list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11440,8 +12038,10 @@ _nvme () { ;; (close-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11470,8 +12070,10 @@ _nvme () { ;; (finish-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11500,8 +12102,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_id_ctrl _zns_id_ctrl=( --dry-run':show command instead of executing' @@ -11524,13 +12127,13 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' - --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' @@ -11554,8 +12157,9 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _zns_list _zns_list=( --dry-run':show command instead of executing' @@ -11574,12 +12178,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme zns list options" _zns_list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (offline-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11608,8 +12213,10 @@ _nvme () { ;; (open-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11640,8 +12247,10 @@ _nvme () { ;; (report-zones) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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)' @@ -11665,8 +12274,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' @@ -11678,8 +12285,10 @@ _nvme () { ;; (reset-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11708,8 +12317,10 @@ _nvme () { ;; (set-zone-desc) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -11741,8 +12352,10 @@ _nvme () { ;; (zone-append) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -11789,8 +12402,10 @@ _nvme () { ;; (zone-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11825,8 +12440,10 @@ _nvme () { ;; (zone-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -11864,8 +12481,10 @@ _nvme () { ;; (zrwa-flush-zone) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -11898,8 +12517,10 @@ _nvme () { ;; (admin-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -11968,8 +12589,9 @@ _nvme () { ;; (ana-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ana_log _ana_log=( --dry-run':show command instead of executing' @@ -11994,8 +12616,10 @@ _nvme () { ;; (attach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12024,8 +12648,9 @@ _nvme () { ;; (ave-discovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ave_discovery_log _ave_discovery_log=( --dry-run':show command instead of executing' @@ -12050,8 +12675,9 @@ _nvme () { ;; (boot-part-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -f" && return local _boot_part_log _boot_part_log=( @@ -12079,8 +12705,9 @@ _nvme () { ;; (capacity-mgmt) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -12113,8 +12740,9 @@ _nvme () { ;; (changed-alloc-ns-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -12139,8 +12767,9 @@ _nvme () { ;; (changed-ns-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -12165,8 +12794,9 @@ _nvme () { ;; (check-dhchap-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _check_dhchap_key _check_dhchap_key=( --dry-run':show command instead of executing' @@ -12187,12 +12817,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme check-dhchap-key options" _check_dhchap_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (check-tls-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -12225,12 +12855,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme check-tls-key options" _check_tls_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (cmdset-ind-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12257,8 +12888,10 @@ _nvme () { ;; (compare) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -12326,8 +12959,9 @@ _nvme () { ;; (connect) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ "--devid-file" && return local _connect @@ -12405,13 +13039,14 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme connect options" _connect - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (connect-all) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ + "--nbft-path" \ "--raw -r" && return local _connect_all _connect_all=( @@ -12430,7 +13065,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)' @@ -12466,7 +13100,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' @@ -12496,12 +13130,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme connect-all options" _connect_all - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (copy) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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)' @@ -12566,8 +13201,9 @@ _nvme () { ;; (create-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _create_ns _create_ns=( --anagrp-id=':ANA Group Identifier (ANAGRPID)' @@ -12630,8 +13266,10 @@ _nvme () { ;; (delete-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12658,8 +13296,10 @@ _nvme () { ;; (detach-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12688,8 +13328,10 @@ _nvme () { ;; (device-self-test) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12720,8 +13362,9 @@ _nvme () { ;; (dim) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _dim _dim=( --device=':Comma-separated list of DC nvme device handle.' @@ -12746,12 +13389,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme dim options" _dim - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (dir-receive) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -12788,8 +13432,10 @@ _nvme () { ;; (dir-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -12831,8 +13477,9 @@ _nvme () { ;; (disconnect) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _disconnect _disconnect=( --concat':enable secure concatenation' @@ -12906,12 +13553,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme disconnect options" _disconnect - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (disconnect-all) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _disconnect_all _disconnect_all=( --dry-run':show command instead of executing' @@ -12934,13 +13581,14 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme disconnect-all options" _disconnect_all - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (discover) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--config -J" \ + "--nbft-path" \ "--raw -r" && return local _discover _discover=( @@ -12959,7 +13607,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)' @@ -12995,7 +13642,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' @@ -13025,12 +13672,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme discover options" _discover - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (dispersed-ns-participating-nss-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13055,8 +13703,10 @@ _nvme () { ;; (dsm) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13095,8 +13745,9 @@ _nvme () { ;; (effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _effects_log _effects_log=( --csi=':command set identifier' @@ -13123,8 +13774,9 @@ _nvme () { ;; (endurance-event-agg-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -13153,8 +13805,9 @@ _nvme () { ;; (endurance-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _endurance_log _endurance_log=( --dry-run':show command instead of executing' @@ -13179,8 +13832,10 @@ _nvme () { ;; (error-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13223,8 +13878,9 @@ _nvme () { ;; (fid-support-effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -13247,8 +13903,10 @@ _nvme () { ;; (flush) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13273,8 +13931,10 @@ _nvme () { ;; (format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13317,8 +13977,9 @@ _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-version" "1 2" && return + "--output-format -o" "normal json binary" \ + "--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' @@ -13349,8 +14010,9 @@ _nvme () { ;; (fw-download) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--fw -f" && return local _fw_download _fw_download=( @@ -13387,8 +14049,9 @@ _nvme () { ;; (fw-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _fw_log _fw_log=( --dry-run':show command instead of executing' @@ -13413,8 +14076,9 @@ _nvme () { ;; (gen-dhchap-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _gen_dhchap_key _gen_dhchap_key=( --dry-run':show command instead of executing' @@ -13441,12 +14105,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-dhchap-key options" _gen_dhchap_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-hostnqn) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _gen_hostnqn _gen_hostnqn=( --dry-run':show command instead of executing' @@ -13465,12 +14129,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-hostnqn options" _gen_hostnqn - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (gen-tls-key) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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.' @@ -13509,14 +14174,15 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme gen-tls-key options" _gen_tls_key - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (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" \ + "--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 current default saved supported" \ + "--timeout" "infinite -1" && return local _get_feature _get_feature=( --cdw11=':feature specific dword 11' @@ -13555,8 +14221,10 @@ _nvme () { ;; (get-lba-status) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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.' @@ -13592,8 +14260,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" \ - "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_log _get_log=( --aen=':result of the aen, use to override log id' @@ -13644,8 +14314,9 @@ _nvme () { ;; (get-ns-id) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_ns_id _get_ns_id=( --dry-run':show command instead of executing' @@ -13668,8 +14339,9 @@ _nvme () { ;; (get-property) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_property _get_property=( --dry-run':show command instead of executing' @@ -13694,8 +14366,9 @@ _nvme () { ;; (get-reg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _get_reg _get_reg=( --acq':ACQ=0x30 register offset' @@ -13748,8 +14421,9 @@ _nvme () { ;; (host-discovery-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _host_discovery_log _host_discovery_log=( --all-host-entries':All Host Entries' @@ -13776,8 +14450,9 @@ _nvme () { ;; (id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ctrl _id_ctrl=( --dry-run':show command instead of executing' @@ -13804,8 +14479,9 @@ _nvme () { ;; (id-domain) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_domain _id_domain=( --dom-id=':identifier of desired domain' @@ -13830,8 +14506,9 @@ _nvme () { ;; (id-iocs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_iocs _id_iocs=( --controller-id=':identifier of desired controller' @@ -13856,8 +14533,10 @@ _nvme () { ;; (id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -13887,8 +14566,9 @@ _nvme () { ;; (id-ns-granularity) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_ns_granularity _id_ns_granularity=( --dry-run':show command instead of executing' @@ -13911,8 +14591,9 @@ _nvme () { ;; (id-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -13939,8 +14620,9 @@ _nvme () { ;; (id-nvmset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_nvmset _id_nvmset=( --dry-run':show command instead of executing' @@ -13965,8 +14647,9 @@ _nvme () { ;; (id-uuid) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _id_uuid _id_uuid=( --dry-run':show command instead of executing' @@ -13991,8 +14674,10 @@ _nvme () { ;; (io-mgmt-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -14026,8 +14711,10 @@ _nvme () { ;; (io-mgmt-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -14061,8 +14748,10 @@ _nvme () { ;; (io-passthru) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -14131,8 +14820,9 @@ _nvme () { ;; (lba-status-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _lba_status_log _lba_status_log=( --dry-run':show command instead of executing' @@ -14157,14 +14847,15 @@ _nvme () { ;; (list) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' @@ -14177,12 +14868,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme list options" _list - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (list-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -14192,7 +14884,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' @@ -14209,8 +14901,9 @@ _nvme () { ;; (list-endgrp) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_endgrp _list_endgrp=( --dry-run':show command instead of executing' @@ -14218,7 +14911,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' @@ -14235,8 +14928,10 @@ _nvme () { ;; (list-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -14248,7 +14943,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' @@ -14265,8 +14960,9 @@ _nvme () { ;; (list-secondary) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _list_secondary _list_secondary=( --cntid=':lowest controller identifier to display' @@ -14293,14 +14989,15 @@ _nvme () { ;; (list-subsys) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && 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' @@ -14317,8 +15014,9 @@ _nvme () { ;; (lockdown) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -14355,8 +15053,9 @@ _nvme () { ;; (media-unit-stat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _media_unit_stat_log _media_unit_stat_log=( --domain-id=':Domain Identifier' @@ -14383,8 +15082,9 @@ _nvme () { ;; (mgmt-addr-list-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -14407,8 +15107,9 @@ _nvme () { ;; (mi-cmd-support-effects-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -14431,8 +15132,10 @@ _nvme () { ;; (ns-descs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -14459,8 +15162,9 @@ _nvme () { ;; (ns-rescan) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _ns_rescan _ns_rescan=( --dry-run':show command instead of executing' @@ -14483,8 +15187,9 @@ _nvme () { ;; (nvm-id-ctrl) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _nvm_id_ctrl _nvm_id_ctrl=( --dry-run':show command instead of executing' @@ -14507,8 +15212,10 @@ _nvme () { ;; (nvm-id-ns) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -14535,8 +15242,9 @@ _nvme () { ;; (nvm-id-ns-lba-format) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -14563,8 +15271,10 @@ _nvme () { ;; (nvme-mi-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -14602,8 +15312,10 @@ _nvme () { ;; (nvme-mi-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -14641,8 +15353,9 @@ _nvme () { ;; (persistent-event-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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.' @@ -14671,8 +15384,9 @@ _nvme () { ;; (phy-rx-eom-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _phy_rx_eom_log _phy_rx_eom_log=( --controller=':Target Controller ID.' @@ -14699,8 +15413,9 @@ _nvme () { ;; (power-measurement-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _power_measurement_log _power_measurement_log=( --dry-run':show command instead of executing' @@ -14725,8 +15440,9 @@ _nvme () { ;; (pred-lat-event-agg-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -14755,8 +15471,9 @@ _nvme () { ;; (predictable-lat-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _predictable_lat_log _predictable_lat_log=( --dry-run':show command instead of executing' @@ -14783,8 +15500,9 @@ _nvme () { ;; (primary-ctrl-caps) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _primary_ctrl_caps _primary_ctrl_caps=( --cntlid=':Controller ID' @@ -14809,8 +15527,9 @@ _nvme () { ;; (pull-model-ddc-req-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -14835,8 +15554,9 @@ _nvme () { ;; (reachability-associations-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reachability_associations_log _reachability_associations_log=( --associations-only':Return Associations Only' @@ -14863,8 +15583,9 @@ _nvme () { ;; (reachability-groups-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reachability_groups_log _reachability_groups_log=( --dry-run':show command instead of executing' @@ -14891,8 +15612,10 @@ _nvme () { ;; (read) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -14960,8 +15683,9 @@ _nvme () { ;; (reset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _reset _reset=( --dry-run':show command instead of executing' @@ -14984,8 +15708,10 @@ _nvme () { ;; (resv-acquire) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15020,8 +15746,9 @@ _nvme () { ;; (resv-notif-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _resv_notif_log _resv_notif_log=( --dry-run':show command instead of executing' @@ -15044,8 +15771,10 @@ _nvme () { ;; (resv-register) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15080,8 +15809,10 @@ _nvme () { ;; (resv-release) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15114,8 +15845,10 @@ _nvme () { ;; (resv-report) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15146,8 +15879,9 @@ _nvme () { ;; (rotational-media-info-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -15172,9 +15906,10 @@ _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 + "--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.' @@ -15219,8 +15954,9 @@ _nvme () { ;; (sanitize-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _sanitize_log _sanitize_log=( --dry-run':show command instead of executing' @@ -15247,9 +15983,10 @@ _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 + "--sanact -a" "exit-failure start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" && return local _sanitize_ns _sanitize_ns=( --ause':Allow unrestricted sanitize exit.' @@ -15282,8 +16019,10 @@ _nvme () { ;; (security-recv) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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)' @@ -15322,8 +16061,10 @@ _nvme () { ;; (security-send) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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=( @@ -15361,8 +16102,9 @@ _nvme () { ;; (self-test-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _self_test_log _self_test_log=( --dry-run':show command instead of executing' @@ -15388,8 +16130,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" \ - "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + "--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 _set_feature _set_feature=( @@ -15429,8 +16173,9 @@ _nvme () { ;; (set-property) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _set_property _set_property=( --dry-run':show command instead of executing' @@ -15457,8 +16202,9 @@ _nvme () { ;; (set-reg) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _set_reg _set_reg=( --acq=':ACQ=0x30 register offset' @@ -15502,8 +16248,9 @@ _nvme () { ;; (show-hostnqn) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _show_hostnqn _show_hostnqn=( --dry-run':show command instead of executing' @@ -15522,12 +16269,12 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme show-hostnqn options" _show_hostnqn - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (show-regs) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _show_regs _show_regs=( --dry-run':show command instead of executing' @@ -15550,8 +16297,10 @@ _nvme () { ;; (show-topology) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _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=( --dry-run':show command instead of executing' @@ -15561,7 +16310,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' @@ -15576,8 +16325,10 @@ _nvme () { ;; (smart-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15604,8 +16355,9 @@ _nvme () { ;; (subsystem-reset) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _subsystem_reset _subsystem_reset=( --dry-run':show command instead of executing' @@ -15628,8 +16380,9 @@ _nvme () { ;; (supported-cap-config-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _supported_cap_config_log _supported_cap_config_log=( --domain-id=':Domain Identifier' @@ -15656,8 +16409,9 @@ _nvme () { ;; (supported-log-pages) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _supported_log_pages _supported_log_pages=( --dry-run':show command instead of executing' @@ -15680,8 +16434,9 @@ _nvme () { ;; (telemetry-log) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return _nvme_opt_files "--output-file -O" && return local _telemetry_log _telemetry_log=( @@ -15718,12 +16473,46 @@ _nvme () { ;; (tls-key) - _nvme_has_device || compadd -- /dev/nvme*(N) + _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' + --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) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" && return local _top _top=( --delay=':refresh interval in seconds' @@ -15731,7 +16520,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' @@ -15744,12 +16533,13 @@ _nvme () { ) _arguments '*:: :->subcmds' _nvme_describe "nvme top options" _top - _nvme_has_device || compadd -- /dev/nvme*(N) ;; (verify) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15794,8 +16584,9 @@ _nvme () { ;; (virt-mgmt) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" && return + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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' @@ -15826,8 +16617,10 @@ _nvme () { ;; (write) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 @@ -15895,8 +16688,10 @@ _nvme () { ;; (write-uncor) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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' @@ -15929,8 +16724,10 @@ _nvme () { ;; (write-zeroes) - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--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 c1dc18105c..e11133018b 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 @@ -123,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 @@ -145,6 +151,7 @@ _nvme_finish_completion () { nvme_list_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -154,14 +161,17 @@ nvme_list_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "admin-passthru") 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" \ - "--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" ;; @@ -169,32 +179,37 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "boot-part-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -202,56 +217,67 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-alloc-ns-list-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-ns-list-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "check-dhchap-key") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "check-tls-key") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "cmdset-ind-id-ns") 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" \ - "--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 tabular" \ - "--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" ;; @@ -259,82 +285,101 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" "--devid-file" + nodevice=1 ;; "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 tabular" \ - "--output-format-version" "1 2" - _nvme_opt_files "--config -J" "--raw -r" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--config -J" "--nbft-path" "--raw -r" + nodevice=1 ;; "copy") 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" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "dir-receive") 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" \ - "--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 tabular" \ - "--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" ;; @@ -342,97 +387,117 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "disconnect-all") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "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 tabular" \ - "--output-format-version" "1 2" - _nvme_opt_files "--config -J" "--raw -r" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--config -J" "--nbft-path" "--raw -r" + nodevice=1 ;; "dispersed-ns-participating-nss-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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-event-agg-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") @@ -440,16 +505,18 @@ 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-version" "1 2" + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw-download") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; @@ -457,32 +524,40 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "gen-dhchap-key") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "gen-hostnqn") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "gen-tls-key") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" + nodevice=1 ;; "get-feature") @@ -490,17 +565,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" \ - "--output-format -o" "normal json binary tabular" \ + "--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" ;; "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 tabular" \ - "--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") @@ -508,112 +587,129 @@ 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-version" "1 2" + "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-ns-id") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-property") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-reg") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "host-discovery-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-domain") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-iocs") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ns-lba-format") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-nvmset") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-uuid") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -621,8 +717,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 tabular" \ - "--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" ;; @@ -630,8 +728,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 tabular" \ - "--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" ;; @@ -639,136 +739,159 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "list-ctrl") 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" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--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 binary tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list-subsys") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lockdown") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "media-unit-stat-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mgmt-addr-list-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mi-cmd-support-effects-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvm-id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -776,8 +899,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 tabular" \ - "--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" ;; @@ -785,80 +910,91 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "phy-rx-eom-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "power-measurement-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pred-lat-event-agg-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "predictable-lat-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "primary-ctrl-caps") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pull-model-ddc-req-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-associations-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-groups-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -866,98 +1002,116 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize") 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" + "--sanact -a" "exit-failure start-block-erase start-overwrite start-crypto-erase exit-media-verification" \ + "--timeout" "infinite -1" ;; "sanitize-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize-ns") 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" + "--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 tabular" \ - "--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 tabular" \ - "--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" ;; @@ -965,8 +1119,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-feature") @@ -974,8 +1129,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" \ - "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" + "--namespace-id -n" "all -1" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -983,113 +1140,144 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-reg") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "show-hostnqn") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "show-regs") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "show-topology") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json tabular" \ + "--output-format-version" "1 2" \ + "--ranking -r" "namespace ctrl multipath" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-cap-config-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-log-pages") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; + "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" + _nvme_opt_files "--keyfile -f" + 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 json binary tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "verify") 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" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -1097,16 +1285,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 tabular" \ - "--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 tabular" \ - "--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 @@ -1115,6 +1307,7 @@ nvme_list_opts () { plugin_amzn_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1124,22 +1317,25 @@ plugin_amzn_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1148,6 +1344,7 @@ plugin_amzn_opts () { plugin_config_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1157,38 +1354,61 @@ plugin_config_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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= --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_files "--config -J" "--output -o" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--config -J" "--output" + nodevice=1 ;; "create") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output" + nodevice=1 ;; "show") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" + nodevice=1 + ;; + + "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" \ + "--timeout" "infinite -1" + nodevice=1 ;; "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" \ + "--timeout" "infinite -1" _nvme_opt_files "--config -J" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1197,6 +1417,7 @@ plugin_config_opts () { plugin_dapustor_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1206,14 +1427,17 @@ plugin_dapustor_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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 @@ -1222,6 +1446,7 @@ plugin_dapustor_opts () { plugin_dell_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1231,14 +1456,16 @@ plugin_dell_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1247,6 +1474,7 @@ plugin_dell_opts () { plugin_dera_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1256,14 +1484,16 @@ plugin_dera_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "smart-log-add"|"stat") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1272,6 +1502,7 @@ plugin_dera_opts () { plugin_dir_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1281,22 +1512,27 @@ plugin_dir_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "receive") 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" \ - "--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 tabular" \ - "--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 @@ -1306,6 +1542,7 @@ plugin_dir_opts () { plugin_exclusion_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1315,54 +1552,67 @@ plugin_exclusion_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "add") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "create") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "delete") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "edit") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "remove") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1371,6 +1621,7 @@ plugin_exclusion_opts () { plugin_fdp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1380,70 +1631,82 @@ plugin_fdp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "events") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "feature") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1452,6 +1715,7 @@ plugin_fdp_opts () { plugin_feat_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1461,105 +1725,120 @@ plugin_feat_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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 tabular" \ + _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" ;; "async-event-conf") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--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 tabular" \ + _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" ;; "hctm") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "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 tabular" \ + _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" ;; "int-coalesce") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "int-vector-config") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "keep-alive-timer") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "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" \ + _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" ;; "num-queues") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--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 tabular" \ + _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" ;; @@ -1567,72 +1846,80 @@ 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "power-meas") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "power-mgmt") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "power-thresh") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "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 tabular" \ + _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" ;; "timestamp") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "volatile-wc") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "write-atom-normal") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1641,6 +1928,7 @@ plugin_feat_opts () { plugin_fw_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1650,6 +1938,7 @@ plugin_fw_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "commit"|"activate") @@ -1657,16 +1946,18 @@ 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-version" "1 2" + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "download") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--fw -f" ;; esac @@ -1676,6 +1967,7 @@ plugin_fw_opts () { plugin_huawei_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1685,22 +1977,26 @@ plugin_huawei_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -1709,6 +2005,7 @@ plugin_huawei_opts () { plugin_ibm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1718,30 +2015,34 @@ plugin_ibm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "crit-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persist-event-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vpd") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1750,6 +2051,7 @@ plugin_ibm_opts () { plugin_id_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1759,150 +2061,175 @@ plugin_id_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 binary tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endgrp-list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "iocs") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 binary tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "nvmset") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "primary-ctrl-caps") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "secondary-ctrl-list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "uuid") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1911,6 +2238,7 @@ plugin_id_opts () { plugin_innogrit_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1920,22 +2248,25 @@ plugin_innogrit_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "get-cdump") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-eventlog") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1944,6 +2275,7 @@ plugin_innogrit_opts () { plugin_inspur_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1953,14 +2285,16 @@ plugin_inspur_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "nvme-vendor-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -1969,6 +2303,7 @@ plugin_inspur_opts () { plugin_intel_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -1978,22 +2313,26 @@ plugin_intel_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -2001,44 +2340,55 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "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" \ + "--timeout" "infinite -1" ;; "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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-bucket-thresholds") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2047,6 +2397,7 @@ plugin_intel_opts () { plugin_io_mgmt_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2056,14 +2407,17 @@ plugin_io_mgmt_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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" ;; @@ -2071,8 +2425,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 tabular" \ - "--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 @@ -2082,6 +2438,7 @@ plugin_io_mgmt_opts () { plugin_keys_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2091,70 +2448,91 @@ plugin_keys_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "check-kxchap-secret") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "check-tls-psk") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "export") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" + nodevice=1 ;; "gen-kxchap-secret") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "gen-tls-psk") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" + nodevice=1 ;; "import") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" + nodevice=1 ;; "insert-tls-psk") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + _nvme_opt_files "--keyfile -f" + nodevice=1 ;; "revoke") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; esac _nvme_finish_completion "$1" 3 @@ -2163,6 +2541,7 @@ plugin_keys_opts () { plugin_lm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2172,39 +2551,43 @@ plugin_lm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "create-cdq") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "delete-cdq") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-cdq") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "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 tabular" \ + _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" ;; @@ -2212,27 +2595,28 @@ 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" + "--timeout" "infinite -1" _nvme_opt_files "--input-file -f" ;; "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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "track-send") 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" + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2241,6 +2625,7 @@ plugin_lm_opts () { plugin_log_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2250,30 +2635,34 @@ plugin_log_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ana") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "ave-discovery") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "boot-part") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -2281,240 +2670,273 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "changed-ns-list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "endurance-event-agg") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "host-discovery") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lba-status") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "media-unit-stat") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mgmt-addr-list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "mi-cmd-support-effects") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persistent-event") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "phy-rx-eom") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "power-measurement") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pred-lat-event-agg") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "predictable-lat") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "pull-model-ddc-req") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-associations") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "reachability-groups") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "resv-notif") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "rotational-media-info") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "sanitize") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "self-test") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "supported-pages") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; esac @@ -2524,6 +2946,7 @@ plugin_log_opts () { plugin_mangoboost_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2533,14 +2956,16 @@ plugin_mangoboost_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2549,6 +2974,7 @@ plugin_mangoboost_opts () { plugin_memblaze_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2558,98 +2984,116 @@ plugin_memblaze_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-error-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-pm-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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-get-feature-x") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log-print") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-log-print-x") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-set-feature-x") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "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" \ + "--timeout" "infinite -1" ;; "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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "lat-stats-print-x") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "perf-stats-print-x") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "select-download") 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" \ - "--output-format-version" "1 2" + _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" ;; @@ -2657,20 +3101,28 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") - 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" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2679,6 +3131,7 @@ plugin_memblaze_opts () { plugin_micron_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2688,94 +3141,112 @@ plugin_micron_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "cloud-boot-SSD-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-logs") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--command -c" "all read write trim" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-tracking") 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" \ - "--output-format-version" "1 2" + _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" ;; "log-page-directory") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "plugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "select-download") 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" \ - "--output-format-version" "1 2" + _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" ;; @@ -2783,48 +3254,55 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-cloud-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-device-waf") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" \ + "--type -t" "host controller" _nvme_opt_files "--package -p" ;; @@ -2832,72 +3310,83 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-ext-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smbus-option") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-telemetry-controller-option") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--option -O" "enable disable status" \ + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-vendor-telemetry-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-work-load-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2906,6 +3395,7 @@ plugin_micron_opts () { plugin_nbft_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2915,14 +3405,18 @@ plugin_nbft_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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 tabular" \ - "--output-format-version" "1 2" + _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 _nvme_finish_completion "$1" 3 @@ -2931,6 +3425,7 @@ plugin_nbft_opts () { plugin_netapp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2940,22 +3435,25 @@ plugin_netapp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "ontapdevices") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal column json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smdevices") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal column json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -2964,6 +3462,7 @@ plugin_netapp_opts () { plugin_ns_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -2973,46 +3472,55 @@ plugin_ns_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "attach") 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" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3021,6 +3529,7 @@ plugin_ns_opts () { plugin_nvidia_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3030,14 +3539,16 @@ plugin_nvidia_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3046,6 +3557,7 @@ plugin_nvidia_opts () { plugin_nvme_mi_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3055,14 +3567,17 @@ plugin_nvme_mi_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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" ;; @@ -3070,8 +3585,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 tabular" \ - "--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 @@ -3081,6 +3598,7 @@ plugin_nvme_mi_opts () { plugin_ocp_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3090,136 +3608,156 @@ plugin_ocp_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "device-capability-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "eol-plp-failure-mode") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "error-recovery-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ + _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" ;; "get-dssd-async-event-config") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "get-dssd-power-state-feature") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "get-enable-ieee1667-silo") 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" + "--sel -s" "0 1 2 3 current default saved supported" \ + "--timeout" "infinite -1" ;; "get-error-injection") 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" + "--sel -s" "0 1 2 3 current default saved supported" \ + "--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 tabular" \ + _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" ;; "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 tabular" \ + _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" ;; "get-plp-health-check-interval") 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" + "--sel -S" "0 1 2 3 current default saved supported" \ + "--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 tabular" \ + _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" ;; "hardware-component-log") @@ -3227,65 +3765,74 @@ 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-version" "1 2" + "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "internal-log") 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" \ - "--output-format-version" "1 2" - _nvme_opt_files "--output-file -f" + _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" "--string-log -s" "--telemetry-log -l" ;; "latency-monitor-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "persistent-event-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-dssd-async-event-config") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-dssd-power-state-feature") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-enable-ieee1667-silo") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-error-injection") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--data -d" ;; @@ -3293,48 +3840,54 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-plp-health-check-interval") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-telemetry-profile") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "smart-add-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "tcg-configuration-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "telemetry-string-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -f" ;; @@ -3342,8 +3895,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3352,6 +3906,7 @@ plugin_ocp_opts () { plugin_registry_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3361,38 +3916,44 @@ plugin_registry_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "delete") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "list") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "retrieve") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "update") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3401,6 +3962,7 @@ plugin_registry_opts () { plugin_resv_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3410,38 +3972,47 @@ plugin_resv_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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 tabular" \ - "--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 tabular" \ - "--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 tabular" \ - "--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 @@ -3450,6 +4021,7 @@ plugin_resv_opts () { plugin_rpmb_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3459,22 +4031,25 @@ plugin_rpmb_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "info") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "program-key") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" ;; @@ -3482,8 +4057,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--msgfile -f" ;; @@ -3491,16 +4067,18 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--msgfile -f" ;; @@ -3508,17 +4086,19 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; "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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--keyfile -g" "--msgfile -f" ;; esac @@ -3528,6 +4108,7 @@ plugin_rpmb_opts () { plugin_samsung_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3537,14 +4118,18 @@ plugin_samsung_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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" + listopts+=" --dump-type -t" - _nvme_opt_vals "--output-format -o" "normal json binary tabular" \ - "--output-format-version" "1 2" + _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" ;; esac @@ -3554,6 +4139,7 @@ plugin_samsung_opts () { plugin_seagate_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3563,70 +4149,84 @@ plugin_seagate_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "get-ctrl-tele") 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" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "vs-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -3634,32 +4234,36 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-smart-add-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3668,6 +4272,7 @@ plugin_seagate_opts () { plugin_security_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3677,22 +4282,27 @@ plugin_security_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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 tabular" \ - "--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 @@ -3702,6 +4312,7 @@ plugin_security_opts () { plugin_sed_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3711,26 +4322,61 @@ plugin_sed_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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" + valopts+=" --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" ;; "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" \ + "--timeout" "infinite -1" ;; "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" \ + "--timeout" "infinite -1" + ;; + + "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" \ + "--timeout" "infinite -1" ;; "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" \ + "--timeout" "infinite -1" ;; "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" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3739,6 +4385,7 @@ plugin_sed_opts () { plugin_sfx_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3748,22 +4395,25 @@ plugin_sfx_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "change-cap") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "dump-evtlog") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -f" "--output -O" ;; @@ -3771,64 +4421,75 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-bad-block") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "query-cap") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -3837,6 +4498,7 @@ plugin_sfx_opts () { plugin_shannon_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3846,31 +4508,37 @@ plugin_shannon_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _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 "--output-format -o" "normal json binary tabular" \ + _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" ;; "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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" ;; @@ -3878,8 +4546,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 tabular" \ - "--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 @@ -3888,6 +4558,7 @@ plugin_shannon_opts () { plugin_sndk_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -3897,166 +4568,191 @@ plugin_sndk_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "capabilities") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-assert-dump") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "drive-resize") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-dev-capabilities-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-drive-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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-error-recovery-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-latency-monitor-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-unsupported-reqs-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-error-reason-identifier") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -O" ;; @@ -4064,63 +4760,76 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") - 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" + 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_files "--output-file -O" "--type -t" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER BOTH" + _nvme_opt_files "--output-file -O" ;; "vs-nand-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4129,6 +4838,7 @@ plugin_sndk_opts () { plugin_solidigm_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4138,78 +4848,89 @@ plugin_solidigm_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSDplugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "garbage-collect-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "latency-tracking-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "market-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "parse-telemetry-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--config-file -j" "--source-file -s" ;; @@ -4217,40 +4938,47 @@ 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" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-drive-info") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" \ + "--type -t" "ALL CIT HIT NLOG ASSERT EVENT EXTENDED" _nvme_opt_files "--dir-name -d" ;; @@ -4258,16 +4986,18 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "workload-tracker") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4276,6 +5006,7 @@ plugin_solidigm_opts () { plugin_ssstc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4285,14 +5016,17 @@ plugin_ssstc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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 @@ -4301,6 +5035,7 @@ plugin_ssstc_opts () { plugin_toshiba_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4310,22 +5045,25 @@ plugin_toshiba_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-internal-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4333,8 +5071,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 tabular" \ - "--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 @@ -4344,6 +5084,7 @@ plugin_toshiba_opts () { plugin_transcend_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4353,22 +5094,25 @@ plugin_transcend_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "badblock") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "healthvalue") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4377,6 +5121,7 @@ plugin_transcend_opts () { plugin_utils_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4386,13 +5131,20 @@ 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 } plugin_virtium_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4402,14 +5154,16 @@ 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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4417,8 +5171,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4427,6 +5182,7 @@ plugin_virtium_opts () { plugin_wdc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4436,14 +5192,16 @@ plugin_wdc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "cap-diag") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4451,64 +5209,73 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-assert-dump") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-fw-activate-history") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "clear-pcie-correctable-errors") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "cloud-SSD-plugin-version") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "drive-essentials") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--dir-name -d" ;; @@ -4516,8 +5283,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4525,16 +5293,18 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "enc-get-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4542,8 +5312,9 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4551,40 +5322,45 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-drive-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 tabular" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-error-recovery-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-latency-monitor-log") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "get-pfail-dump") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--output-file -O" ;; @@ -4592,88 +5368,102 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "id-ctrl") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "log-page-directory") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "purge-monitor") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "set-latency-monitor-feature") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-error-reason-identifier") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" _nvme_opt_files "--file -O" ;; @@ -4681,63 +5471,76 @@ 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") - 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" + 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_files "--output-file -O" "--type -t" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" \ + "--type -t" "NONE HOST CONTROLLER" + _nvme_opt_files "--output-file -O" ;; "vs-nand-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-pcie-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; "vs-temperature-stats") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" ;; esac _nvme_finish_completion "$1" 3 @@ -4746,6 +5549,7 @@ plugin_wdc_opts () { plugin_ymtc_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4755,14 +5559,17 @@ plugin_ymtc_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "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 tabular" \ - "--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 @@ -4771,6 +5578,7 @@ plugin_ymtc_opts () { plugin_zns_opts () { local opts="" local valopts="" + local listopts="" local vals="" local opt="" local val="" @@ -4780,94 +5588,116 @@ plugin_zns_opts () { local completing_value=0 local wantfiles=0 + local nodevice=0 _nvme_detect_value_completion case "$1" in "changed-zone-list") 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" \ - "--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 tabular" \ - "--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 tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal json binary" \ + "--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" + 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 "--output-format -o" "normal json binary tabular" \ - "--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") 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" \ - "--output-format-version" "1 2" + _nvme_opt_vals "--output-format -o" "normal" \ + "--output-format-version" "1 2" \ + "--timeout" "infinite -1" + nodevice=1 ;; "offline-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 tabular" \ - "--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 tabular" \ - "--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" + 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 "--output-format -o" "normal json binary tabular" \ - "--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 tabular" \ - "--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 tabular" \ - "--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" ;; @@ -4875,8 +5705,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 tabular" \ - "--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" ;; @@ -4884,16 +5716,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 tabular" \ - "--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 tabular" \ - "--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" ;; @@ -4901,8 +5737,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 tabular" \ - "--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/generate-completions.py b/completions/generate-completions.py index c468ed3b29..820726eed1 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 # --------------------------------------------------------------------------- @@ -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 @@ -217,6 +212,7 @@ def sorted_options(opts): {func} () {{ \tlocal opts="" \tlocal valopts="" +\tlocal listopts="" \tlocal vals="" \tlocal opt="" \tlocal val="" @@ -226,6 +222,7 @@ def sorted_options(opts): \tlocal completing_value=0 \tlocal wantfiles=0 +\tlocal nodevice=0 \t_nvme_detect_value_completion ''' @@ -269,6 +266,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 @@ -276,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 @@ -345,9 +348,14 @@ 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("metavar") in ("FILE", "DIRECTORY") + return opt.get("type") in ("file", "directory") def opt_spellings(opt): @@ -371,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: @@ -389,6 +400,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 +419,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)) @@ -547,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 @@ -600,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): @@ -641,9 +682,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 +709,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") @@ -816,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 | @@ -838,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 | @@ -858,8 +915,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 +977,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): @@ -943,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/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/completions/nvme-completion.ps1 b/completions/nvme-completion.ps1 index 071454c94d..5ea914e609 100644 --- a/completions/nvme-completion.ps1 +++ b/completions/nvme-completion.ps1 @@ -249,13 +249,13 @@ $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', '--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') + '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') @@ -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,22 +293,22 @@ $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') - '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') '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') @@ -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') @@ -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') @@ -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') @@ -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,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', '--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') + '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') @@ -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', '--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', '--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') @@ -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') @@ -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', '--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') @@ -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 @@ -742,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 @@ -752,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 @@ -760,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 @@ -769,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 @@ -783,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 @@ -797,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 @@ -806,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 @@ -848,9 +911,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 @@ -859,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 @@ -880,9 +943,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 @@ -897,1517 +958,2316 @@ $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 --output-format' = @('normal', 'json', 'binary', 'tabular') + 'admin-passthru --namespace-id' = @('all', '-1') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'amzn stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ana-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'attach-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ave-discovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'boot-part-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'capacity-mgmt --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'changed-alloc-ns-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'changed-ns-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'check-dhchap-key --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'check-tls-key --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'cmdset-ind-id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'compare --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'config create --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'config show --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'connect --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'connect-all --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'copy --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'create-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dapustor smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'delete-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dell id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dera smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dera stat --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'detach-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'device-self-test --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dim --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dir receive --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dir send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dir-receive --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dir-send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'disconnect --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'disconnect-all --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'discover --output-format' = @('normal', 'json', 'binary', 'tabular') + 'disconnect-all --timeout' = @('infinite', '-1') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'dsm --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'effects-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'endurance-event-agg-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'endurance-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'error-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion create --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion delete --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion edit --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'exclusion remove --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp configs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp events --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp set-events --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp update --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'fdp usage --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'feat arbitration --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -S' = @('0', '1', '2', '3') - 'feat arbitration -o' = @('normal', 'json', 'binary', 'tabular') - 'feat async-event-conf --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat arbitration --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat arbitration --timeout' = @('infinite', '-1') + '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 -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 --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', '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 -S' = @('0', '1', '2', '3') - 'feat err-recovery -o' = @('normal', 'json', 'binary', 'tabular') - 'feat hctm --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -S' = @('0', '1', '2', '3') - 'feat hctm -o' = @('normal', 'json', 'binary', 'tabular') - 'feat host-behavior-support --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat hctm --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat hctm --timeout' = @('infinite', '-1') + '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 -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 --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', '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat int-coalesce --timeout' = @('infinite', '-1') + '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 -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 --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', '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 -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 --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', '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 -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 --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', '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat num-queues --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat perf-characteristics --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat power-limit --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat power-meas --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat power-mgmt --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat power-thresh --timeout' = @('infinite', '-1') + '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 -S' = @('0', '1', '2', '3') - 'feat temp-thresh -o' = @('normal', 'json', 'binary', 'tabular') - 'feat timestamp --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -S' = @('0', '1', '2', '3') - 'feat timestamp -o' = @('normal', 'json', 'binary', 'tabular') - 'feat volatile-wc --output-format' = @('normal', 'json', 'binary', 'tabular') + 'feat timestamp --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat timestamp --timeout' = @('infinite', '-1') + '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 -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 --sel' = @('0', '1', '2', '3', 'current', 'default', 'saved', 'supported') + 'feat volatile-wc --timeout' = @('infinite', '-1') + '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 -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 --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', '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') - 'fid-support-effects-log -o' = @('normal', 'json', 'binary', 'tabular') - 'flush --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'format --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') + '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', 'tabular') + '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', '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 --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', '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 --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', 'tabular') + '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', '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 --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', '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-dhchap-key --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-hostnqn --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'gen-tls-key --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') + '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 --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'get-feature -s' = @('0', '1', '2', '3') - 'get-lba-status --output-format' = @('normal', 'json', 'binary', 'tabular') + 'get-feature -n' = @('all', '-1') + 'get-feature -o' = @('normal', 'json', 'binary') + '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') - 'get-lba-status -o' = @('normal', 'json', 'binary', 'tabular') + '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 --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'get-ns-id --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'get-property --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'get-reg --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'host-discovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'huawei id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'huawei list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm crit-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm persist-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ibm vpd --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ctrl-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id domain --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id endgrp-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id iocs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-descs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-granularity --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-ind --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvm-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id nvmset --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id primary-ctrl-caps --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id secondary-ctrl-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id uuid --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-domain --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-iocs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns-granularity --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-nvmset --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'id-uuid --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'innogrit get-cdump --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'innogrit get-eventlog --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'inspur nvme-vendor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel lat-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel market-name --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel set-bucket-thresholds --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'intel temp-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'io-mgmt-send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'io-passthru --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys check-kxchap-secret --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys check-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys export --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys gen-kxchap-secret --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys gen-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys import --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys insert-tls-psk --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'keys revoke --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lba-status-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list-endgrp --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list-secondary --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'list-subsys --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm create-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm delete-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm get-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm migration-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm migration-recv -s' = @('0', '1', '2', '3') - 'lm migration-send --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm migration-recv --timeout' = @('infinite', '-1') + 'lm migration-recv -o' = @('normal', 'json', 'binary') + '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 -s' = @('0', '1', '2', '3') - 'lm set-cdq --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm migration-send --timeout' = @('infinite', '-1') + 'lm migration-send -o' = @('normal', 'json', 'binary') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'lm track-send -s' = @('0', '1', '2', '3') - 'lockdown --output-format' = @('normal', 'json', 'binary', 'tabular') + 'lm track-send --timeout' = @('infinite', '-1') + 'lm track-send -o' = @('normal', 'json', 'binary') + '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 --timeout' = @('infinite', '-1') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log boot-part --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log changed-alloc-ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log changed-ns-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log dispersed-ns-participating-nss --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log effects --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log endurance --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log endurance-event-agg --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log error --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log fid-support-effects --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log fw --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log host-discovery --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log lba-status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log media-unit-stat --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log mgmt-addr-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log mi-cmd-support-effects --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log persistent-event --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log phy-rx-eom --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log power-measurement --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log pred-lat-event-agg --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log predictable-lat --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log pull-model-ddc-req --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log reachability-associations --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log reachability-groups --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log resv-notif --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log rotational-media-info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log sanitize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log self-test --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log smart --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log supported-cap-config --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log supported-pages --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'log telemetry --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'mangoboost id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'media-unit-stat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze clear-error-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze get-pm-status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-get-feature-x --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log-print --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-log-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-set-feature-x --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-stats-print --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze lat-stats-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze perf-stats-print-x --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze select-download --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze set-pm-status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'memblaze smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'mgmt-addr-list-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'mi-cmd-support-effects-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-logs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron latency-tracking --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + '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') - 'micron log-page-directory -o' = @('normal', 'json', 'binary', 'tabular') - 'micron plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron select-download --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smart-ext-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-smbus-option --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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') - 'micron vs-temperature-stats -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-vendor-telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'micron vs-work-load-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nbft show --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'netapp ontapdevices --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'netapp smdevices --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns attach --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns create --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns delete --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns detach --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns get-id --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns-descs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ns-rescan --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvidia id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvm-id-ns-lba-format --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'nvme-mi-send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp device-capability-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp eol-plp-failure-mode --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -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 --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', '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') - 'ocp error-recovery-log -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - '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-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', '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 -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 --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', '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 -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 --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', '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-enable-ieee1667-silo -s' = @('0', '1', '2', '3') - 'ocp get-error-injection --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-error-injection -s' = @('0', '1', '2', '3') - 'ocp get-idle-wakeup-time --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-idle-wakeup-time -s' = @('0', '1', '2', '3') - 'ocp get-latency-monitor --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-latency-monitor -s' = @('0', '1', '2', '3') - 'ocp get-plp-health-check-interval --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', '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 -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 --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', '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp get-telemetry-profile -s' = @('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', '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', 'tabular') + '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', '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp persistent-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-dssd-async-event-config --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-dssd-power-state-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-enable-ieee1667-silo --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-error-injection --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-plp-health-check-interval --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp set-telemetry-profile --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp tcg-configuration-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp telemetry-string-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ocp unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'persistent-event-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'phy-rx-eom-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'power-measurement-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'pred-lat-event-agg-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'predictable-lat-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'primary-ctrl-caps --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'pull-model-ddc-req-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'reachability-associations-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'reachability-groups-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'read --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'registry delete --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'registry list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'registry retrieve --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'registry update --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'reset --output-format' = @('normal', 'json', 'binary', 'tabular') + 'registry update --timeout' = @('infinite', '-1') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv register --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv release --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv report --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-acquire --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-notif-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-register --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-release --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'resv-report --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rotational-media-info-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb program-key --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-config --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-counter --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb read-data --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb write-config --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'rpmb write-data --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'samsung vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sanitize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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') + 'sanitize --timeout' = @('infinite', '-1') '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 --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', '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate get-ctrl-tele --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate get-host-tele --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-log-page-sup --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'seagate vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'security recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'security send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'security-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'security-send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'self-test-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') + '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 --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'set-property --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'set-reg --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx change-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx dump-evtlog --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx expand-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx get-bad-block --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx get-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx lat-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx query-cap --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx set-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sfx status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon get-additional-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon get-additional-feature -s' = @('0', '1', '2', '3') - 'shannon id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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', 'current', 'default', 'saved', 'supported') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'shannon smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'show-hostnqn --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'show-regs --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'show-topology --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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') - 'smart-log -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk capabilities --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-assert-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk cu-smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk drive-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-drive-status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-error-recovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-latency-monitor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk namespace-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-hw-rev-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'sndk vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm cloud-SSDplugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm garbage-collect-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm latency-tracking-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm market-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm parse-telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm temp-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'solidigm workload-tracker --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ssstc smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'subsystem-reset --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'supported-cap-config-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'supported-log-pages --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'telemetry-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'top --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba vs-internal-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'toshiba vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'transcend badblock --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'transcend healthvalue --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'verify --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'virt-mgmt --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'virtium save-smart-to-vtview-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'virtium show-identify --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cap-diag --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc capabilities --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-assert-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc clear-pcie-correctable-errors --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cloud-SSD-plugin-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cloud-boot-SSD-version --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc cu-smart-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-essentials --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc drive-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc enc-get-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-crash-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-dev-capabilities-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-drive-status --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-error-recovery-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-latency-monitor-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-pfail-dump --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc get-unsupported-reqs-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc log-page-directory --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc namespace-resize --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc purge --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc purge-monitor --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc set-latency-monitor-feature --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-cloud-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-device-waf --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-drive-info --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-error-reason-identifier --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-fw-activate-history --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-hw-rev-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-nand-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 --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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-pcie-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-smart-add-log --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-telemetry-controller-option --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'wdc vs-temperature-stats --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'write --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'write-uncor --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'write-zeroes --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'ymtc smart-log-add --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns changed-zone-list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns close-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns finish-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns id-ctrl --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns id-ns --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns list --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns offline-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns open-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns report-zones --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns reset-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns set-zone-desc --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-append --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-mgmt-recv --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zone-mgmt-send --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') - 'zns zrwa-flush-zone --output-format' = @('normal', 'json', 'binary', 'tabular') + '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 -o' = @('normal', 'json', 'binary', 'tabular') + 'zns zrwa-flush-zone --timeout' = @('infinite', '-1') + 'zns zrwa-flush-zone -n' = @('all', '-1') + 'zns zrwa-flush-zone -o' = @('normal', 'json', 'binary') } Register-ArgumentCompleter -Native -CommandName nvme -ScriptBlock { @@ -2466,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 | @@ -2488,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 | @@ -2508,8 +3381,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)') } 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..65adde1d27 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": [ @@ -13,6 +14,7 @@ "long": "mode", "short": "m", "argument": "required", + "type": "enum", "metavar": "MODE", "description": "operating mode", "values": ["fast", "slow"] @@ -21,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, @@ -35,29 +39,34 @@ { "long": "secret", "argument": "none", + "type": "flag", "hidden": true, "description": "undocumented, hidden from help" }, { "long": "verbose-level", "argument": "optional", + "type": "string", "description": "verbosity level" } ] }, { "name": "no-opt-cmd", + "device": "required", "description": "builtin command with no options", "options": [] }, { "name": "alpha-cmd", + "device": "optional", "description": "alpha builtin command", "options": [ { "long": "wait-time", "short": "w", "argument": "required", + "type": "u32", "metavar": "NUM", "description": "milliseconds to wait" }, @@ -65,6 +74,7 @@ "long": "format", "short": "o", "argument": "required", + "type": "string", "metavar": "FMT", "description": "output format", "global": true, @@ -74,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" } ] @@ -100,19 +113,22 @@ "commands": [ { "name": "report", + "device": "required", "alias": "rpt", "description": "report a thing", "options": [ { "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, @@ -122,20 +138,34 @@ "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" + }, + { + "long": "kinds", + "short": "k", + "argument": "required", + "type": "enum-list", + "metavar": "NAME,...", + "description": "kinds to report", + "values": ["red", "green", "blue"] } ] }, { "name": "no-opt-sub", + "device": "required", "description": "sub-command with no options", "options": [] } @@ -147,12 +177,14 @@ "commands": [ { "name": "local-sub", + "device": "none", "description": "sub-command with local options but no globals", "options": [ { "long": "mode", "short": "m", "argument": "required", + "type": "enum", "metavar": "MODE", "description": "operating mode", "values": ["fast", "slow"] @@ -160,6 +192,7 @@ { "long": "secret", "argument": "none", + "type": "flag", "hidden": true, "description": "undocumented, hidden from help" } 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/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..191e8bcd75 100644 --- a/daemon-util/tests/meson.build +++ b/daemon-util/tests/meson.build @@ -14,4 +14,15 @@ test_resolve = executable( ], ) -test('daemon-util - resolve', test_resolve) +test('daemon-util - resolve', test_resolve, suite: 'unit') + +test_work = executable( + 'test-daemon-util-work', + ['test-work.c'], + dependencies: [ + ccan_dep, + daemon_util_dep, + ], +) + +test('daemon-util - work', test_work, suite: 'unit') 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); 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/ctx.h b/discoverd/src/ctx.h index 99bdea133a..15ce8871f0 100644 --- a/discoverd/src/ctx.h +++ b/discoverd/src/ctx.h @@ -20,6 +20,8 @@ struct inventory; 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 @@ -40,5 +42,7 @@ 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 + 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/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/inventory.c b/discoverd/src/inventory.c index f4988ff2a3..1e447b1c8c 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) +static bool add_config_conn(struct inventory *inv, + const struct discoverd_ctx *dctx, + const struct libnvmf_config_conn *conn, 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) -{ - 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,181 @@ 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); + + // 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; + } + + add_lookup(args->inv, args->dctx, conn); } void inventory_load_config(struct inventory *inv, @@ -646,6 +788,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 +801,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..ff432182c9 100644 --- a/discoverd/src/main.c +++ b/discoverd/src/main.c @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include #include @@ -57,6 +59,10 @@ #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 +#define DLP_WORKERS 4 + struct active_ctrl { struct list_node entry; char *unit_name; // "nvme-discoverd-<12hex>.service" @@ -90,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 }; /* @@ -136,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); @@ -382,18 +392,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); @@ -601,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); @@ -612,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); @@ -661,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); } } } @@ -835,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", @@ -883,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); @@ -892,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 @@ -1121,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, @@ -1178,20 +1236,64 @@ 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; } 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, @@ -1578,9 +1680,21 @@ 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; + int level = log_level(); dmn_log_set_level(level); libnvme_set_logging_level(ctx.nvme_ctx, level, false, false); @@ -1771,9 +1885,22 @@ 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; + } + + 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; + 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 +1966,8 @@ int main(int argc, char **argv) free(nvme_path_abs); 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); 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/meson.build b/discoverd/tests/meson.build index dcd8b012d8..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 - unknown option', nvme_discoverd, - args: ['--bogus'], should_fail: true) -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 - help', nvme_discoverd, args: ['--help'], suite: 'mock') +test('nvme-discoverd - unknown-option', nvme_discoverd, + args: ['--bogus'], should_fail: true, + suite: 'mock') +test('nvme-discoverd - missing-nvme-path', nvme_discoverd, + 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/discoverd/tests/test-inventory.c b/discoverd/tests/test-inventory.c index f25a095b8d..16941ff3c3 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,274 @@ 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; +} + +#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 = { @@ -377,6 +648,19 @@ 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); + 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. + 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..b3b1b88d2d 100755 --- a/discoverd/tests/wringer.sh +++ b/discoverd/tests/wringer.sh @@ -179,6 +179,20 @@ 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 + +# 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. @@ -196,6 +210,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" @@ -331,13 +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}"; 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}"; do + "${MDNS_NQN}" "${NL_NQN}" "${MAP_NQN}" "${REF_NQN}" \ + "${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 @@ -393,6 +412,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 +454,37 @@ 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 + 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? @@ -855,7 +906,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 +930,9 @@ 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 +disconnect_unowned # ... and left its desired controllers saved. rm -f "${DESIRED_FILE}" @@ -1323,6 +1377,111 @@ 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" + +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/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 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. 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/meson.build b/keysd/meson.build index 0891485a78..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', ) @@ -31,6 +32,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/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/src/import.c b/keysd/src/import.c index f40b352fad..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 @@ -193,41 +198,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 +257,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 +269,131 @@ 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 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) { @@ -299,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_one(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, @@ -346,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/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-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/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/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/src/main.c b/keysd/src/main.c index 701692fb8d..4810edae6f 100644 --- a/keysd/src/main.c +++ b/keysd/src/main.c @@ -12,19 +12,30 @@ #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 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 @@ -50,6 +61,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 = ctx.kmip_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" @@ -60,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" @@ -73,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' }, @@ -81,7 +141,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) { @@ -109,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; @@ -133,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) { @@ -169,14 +237,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/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/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/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/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 a849f1cab1..d7e479408c 100644 --- a/keysd/tests/meson.build +++ b/keysd/tests/meson.build @@ -11,16 +11,93 @@ test_config = executable( ], ) -test('nvme-keysd - config', test_config) +test('nvme-keysd - config', test_config, suite: 'unit') -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_ttlv = executable( + 'test-keysd-ttlv', + ['test-ttlv.c', '../src/ttlv.c'], + include_directories: keysd_inc, +) + +test('nvme-keysd - ttlv', test_ttlv, suite: 'unit') + +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, suite: 'unit') + +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, suite: 'unit') + +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, suite: 'unit') + + 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, suite: 'unit') + + 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, + suite: 'mock') +endif + +test('nvme-keysd - help', nvme_keysd, args: ['--help'], suite: 'mock') +test('nvme-keysd - unknown-option', nvme_keysd, + args: ['--bogus'], should_fail: true, + suite: 'mock') +test('nvme-keysd - missing-config', nvme_keysd, args: ['--config', '/nonexistent/nvme-keysd.conf'], - should_fail: true) -test('nvme-keysd - missing --creds-dir', nvme_keysd, - args: ['--creds-dir', '/nonexistent/creds'], should_fail: true) -test('nvme-keysd - missing --fabrics-config', nvme_keysd, + should_fail: true, + suite: 'mock') +test('nvme-keysd - missing-creds-dir', nvme_keysd, + 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/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/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); 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/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/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/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); +} 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); +} 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); diff --git a/keysd/tests/wringer.sh b/keysd/tests/wringer.sh index 8c0558e2a2..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,13 +271,30 @@ 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}" } # --------------------------------------------------------------------------- @@ -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 @@ -396,25 +491,230 @@ 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}" + wait_until 10 journal_match -u "${UNIT}" "$1" "Deactivated successfully" +} + +# 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. # --------------------------------------------------------------------------- @@ -423,6 +723,8 @@ cleanup() { log "Cleanup" disconnect keysd_uninstall + kmip_uninstall + kms_stop revoke_test_keys nvmet_teardown tlshd_stop @@ -439,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 @@ -451,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" \ @@ -465,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" \ @@ -473,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}" @@ -487,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}" @@ -496,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}" @@ -520,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" \ @@ -532,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" \ @@ -543,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)" \ @@ -551,31 +862,27 @@ 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 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" \ 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" \ @@ -592,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'" @@ -600,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 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/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/libnvme3/meson.build b/libnvme/libnvme3/meson.build index 2104fce4b9..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/privsep/helper.c b/libnvme/privsep/helper.c index 3241cc3ade..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; @@ -372,6 +384,85 @@ 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 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; @@ -450,6 +541,12 @@ 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; + case LIBNVME_PRIVSEP_OP_DELETE_CTRL: + ret = handle_delete_ctrl(id, payload, len); + break; default: ret = -EOPNOTSUPP; break; 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/fabrics.c b/libnvme/src/nvme/fabrics.c index 2773b1504c..a1885b29a7 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1620,6 +1620,47 @@ 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; + + 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) + 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 +1668,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 +1680,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,20 +1702,19 @@ 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: "); - 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); @@ -1868,25 +1906,29 @@ 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); + 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", - 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,18 +1950,15 @@ 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) { + while ((p = shr_strsep(&options, ",\n")) != NULL) { if (!*p) continue; if (sscanf(p, "instance=%d", &instance) == 1) { @@ -2073,11 +2112,20 @@ __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->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 + 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", @@ -3751,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; } @@ -4506,7 +4556,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)); @@ -4592,7 +4643,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/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/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/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( diff --git a/libnvme/src/nvme/mem-freebsd.c b/libnvme/src/nvme/mem-freebsd.c index 517e1ce9b2..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; @@ -63,6 +68,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..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; @@ -92,6 +95,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..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; @@ -76,6 +81,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/src/nvme/private.h b/libnvme/src/nvme/private.h index 5db79eb3e2..b7b55afb13 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -458,6 +458,14 @@ 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 { + 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, @@ -473,6 +481,11 @@ 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); +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/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); 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/libnvme/src/privsep/no-privsep.c b/libnvme/src/privsep/no-privsep.c index cddcf7837f..88f12552dd 100644 --- a/libnvme/src/privsep/no-privsep.c +++ b/libnvme/src/privsep/no-privsep.c @@ -47,3 +47,17 @@ 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; +} + +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 fc90c4deec..2206abd022 100644 --- a/libnvme/src/privsep/privsep-proto.h +++ b/libnvme/src/privsep/privsep-proto.h @@ -41,6 +41,16 @@ * 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. + * + * 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 @@ -50,6 +60,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 +80,8 @@ 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, + LIBNVME_PRIVSEP_OP_DELETE_CTRL = 17, }; struct libnvme_privsep_req { @@ -102,6 +115,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..04999b1e8b 100644 --- a/libnvme/src/privsep/privsep.c +++ b/libnvme/src/privsep/privsep.c @@ -382,3 +382,84 @@ 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; +} + +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/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/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/mem.c b/libnvme/tests/mem.c index 71abef3c04..3618b6a042 100644 --- a/libnvme/tests/mem.c +++ b/libnvme/tests/mem.c @@ -62,5 +62,18 @@ 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); + + /* a zero length allocation gets a page */ + buf = libnvme_alloc(0); + shr_assert(buf); + shr_assert(buf[0] == 0); + libnvme_free(buf); + return 0; } 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 27a0e056f5..26e5a8ef1b 100644 --- a/libnvme/tests/nbft/meson.build +++ b/libnvme/tests/nbft/meson.build @@ -85,12 +85,13 @@ 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), files(diff_dir/table), ], + suite: 'unit', ) endforeach endif @@ -98,32 +99,35 @@ 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), ], expected_fail: true, + suite: 'unit', ) else test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), nbft_dump, args: [ files(tables_bad_dir/table), ], should_fail: true, + suite: 'unit', ) endif endforeach foreach table: tables_rejected test( - 'libnvme - @0@'.format(table), + 'libnvme - nbft/' + table.replace('NBFT-', ''), files('nbft-reject.sh'), args: [ 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 be016d2c5b..d64893a2e6 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', @@ -40,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( @@ -47,6 +51,7 @@ privsep_proto_test = executable( ['proto.c', '../ioctl/util.c'], c_args: privsep_test_args, dependencies: [ + ccan_dep, config_dep, libnvme_dep, ], @@ -59,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/privsep/mock-ioctl.c b/libnvme/tests/privsep/mock-ioctl.c index 8fc11faba8..33608edd82 100644 --- a/libnvme/tests/privsep/mock-ioctl.c +++ b/libnvme/tests/privsep/mock-ioctl.c @@ -25,6 +25,13 @@ * - 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. + * - 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 @@ -99,9 +106,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, short_fd = -1; +static bool fabrics_connected; static int mock_ret(int err) { @@ -166,23 +176,34 @@ 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"); + bool short_write = + !strcmp(path, "/sys/class/nvme/nvme6/delete_controller"); int fd; if (!real_open) { errno = EIO; return -1; } - if (ctrl || ns) + 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) ctrl_fd = fd; else if (fd >= 0 && ns) ns_fd = fd; + else if (fd >= 0 && fabrics) { + fabrics_fd = fd; + fabrics_connected = false; + } else if (fd >= 0 && short_write) + short_fd = fd; return fd; } @@ -223,6 +244,10 @@ int close(int fd) ctrl_fd = -1; else if (fd == ns_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"); @@ -234,6 +259,69 @@ 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 == short_fd) + return 0; + 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..50d7cebd80 100644 --- a/libnvme/tests/privsep/privsep.c +++ b/libnvme/tests/privsep/privsep.c @@ -21,6 +21,7 @@ #include +#include #include #include @@ -381,6 +382,144 @@ static void test_sed(void) } #endif +#ifdef TEST_FABRICS +static char sysfs[64], connect_log[64], delete_ctrl[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"); + + 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, + 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; + int 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)); + + read_file(connect_log, args, sizeof(args)); + 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); + + 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) +{ +} + +static void test_fabrics(void) +{ +} +#endif + static int retries; static bool retry_always(struct libnvme_transport_handle *hdl, @@ -424,6 +563,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 +572,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 +592,17 @@ 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); + unlink(delete_ctrl); +#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..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" @@ -346,6 +348,76 @@ 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"); +} + +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]); @@ -360,6 +432,8 @@ int main(int argc, char **argv) test_device_op_len(); test_regs_len(); test_sed(); + test_fabrics(); + test_delete_ctrl(); printf("privsep protocol tests passed\n"); 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/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) { diff --git a/meson.build b/meson.build index 25f50bfd0f..08660288f5 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,27 @@ 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 + # 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 + ################################################################################ # Determine which features we need to build: The nvme executable, the libnvme # library, and/or the python bindings. These are controlled by the following @@ -448,6 +469,9 @@ 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_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') @@ -653,6 +677,20 @@ 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 + # 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 + substs = configuration_data() substs.set('VERSION', meson.project_version()) substs.set('NVMECLI_LICENSE', meson.project_license()[0]) @@ -670,6 +708,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) @@ -722,7 +765,7 @@ if want_nvme nvme_exe = executable( 'nvme', - sources, + sources + ['src/main.c'], include_directories: 'src', dependencies: link_deps, link_args: link_args_list, @@ -914,6 +957,7 @@ if want_tests and py3_prog.found() files('scripts/check-install-tags.py'), meson.project_build_root(), ], + suite: 'check', ) endif @@ -964,6 +1008,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..b985141d34 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', @@ -125,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/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/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/dapustor/dapustor-nvme.c b/plugins/dapustor/dapustor-nvme.c index 1512c4f410..d72d1b47d7 100644 --- a/plugins/dapustor/dapustor-nvme.c +++ b/plugins/dapustor/dapustor-nvme.c @@ -526,20 +526,19 @@ static int dapustor_additional_smart_log(int argc, char **argv, struct command * .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_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) 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/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index 626e4e0af9..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; @@ -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) @@ -156,19 +158,26 @@ 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")); - 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]); @@ -182,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: diff --git a/plugins/dir/dir-plugin.c b/plugins/dir/dir-plugin.c index 723b3c8c53..24313271a5 100644 --- a/plugins/dir/dir-plugin.c +++ b/plugins/dir/dir-plugin.c @@ -71,16 +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), - 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) @@ -197,14 +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), - 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 f55c398428..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" @@ -53,8 +55,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 +91,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 +170,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 +219,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) @@ -268,15 +278,18 @@ 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 { 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) @@ -314,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; @@ -491,8 +503,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) @@ -613,42 +627,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/fdp/fdp.c b/plugins/fdp/fdp.c index 0777216efb..8b0db724ff 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; @@ -35,11 +34,11 @@ 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 { __u16 egid; - bool human_readable; bool raw_binary; }; @@ -48,23 +47,22 @@ static int fdp_configs(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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_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) 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; - if (cfg.human_readable) + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.egid) { @@ -79,18 +77,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; } @@ -121,17 +120,17 @@ static int fdp_usage(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; @@ -185,9 +184,11 @@ static int fdp_stats(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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) @@ -245,18 +246,18 @@ static int fdp_events(int argc, char **argv, struct command *acmd, struct plugin .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; @@ -305,17 +306,18 @@ 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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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; @@ -377,9 +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_LIST("pids", 'p', &cfg.pids, _pids)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_id, + nvme_nsid_values), + OPT_LIST("pids", 'p', &cfg.pids, _pids), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -446,12 +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_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_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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -522,16 +530,18 @@ 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) 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 ba518ff461..fbf5d22127 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -37,9 +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" -#define FEAT_ARGS(n, ...) \ - NVME_ARGS(n, ##__VA_ARGS__, OPT_FLAG("save", 's', NULL, save), \ - OPT_BYTE("sel", 'S', &cfg.sel, sel)) +/* --save and --sel, which every feature command takes */ +struct feat_args { + __u8 sel; + bool save; +}; + #define STR(x) #x #define TMT(n) "thermal management temperature " STR(n) @@ -52,7 +55,7 @@ struct perfc_config { char *paid; __u16 attrl; char *vs_data; - __u8 sel; + struct feat_args feat; }; struct temp_thresh_config { @@ -60,7 +63,7 @@ struct temp_thresh_config { __u8 tmpsel; __u8 thsel; __u8 tmpthh; - __u8 sel; + struct feat_args feat; }; struct arbitration_config { @@ -68,13 +71,13 @@ struct arbitration_config { __u8 lpw; __u8 mpw; __u8 hpw; - __u8 sel; + struct feat_args feat; }; struct err_recovery_config { __u8 tler; __u8 dulbe; - __u8 sel; + struct feat_args feat; }; struct host_behavior_config { @@ -83,7 +86,7 @@ struct host_behavior_config { __u8 lbafee; __u8 hdisns; __u16 cdfe; - __u8 sel; + struct feat_args feat; }; struct lba_range_type_config { @@ -95,34 +98,66 @@ struct lba_range_type_config { char *slbas; char *nlbs; char *guids; - __u8 sel; + struct feat_args feat; }; struct int_coalesce_config { __u8 thr; __u8 time; - __u8 sel; + struct feat_args feat; }; struct int_vec_conf { __u16 iv; bool cd; - __u8 sel; + struct feat_args feat; }; struct write_atom_normal_config { bool dn; - __u8 sel; + struct feat_args feat; }; struct keep_alive_timer_config { __u32 kato; - __u8 sel; + 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, + 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, + nvme_sel_values, .max = NVME_SEL_MAX), + 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"; @@ -169,11 +204,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; @@ -224,24 +255,33 @@ static int feat_power_mgmt(int argc, char **argv, struct command *acmd, struct p struct config { __u8 ps; __u8 wh; - __u8 sel; + 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) 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.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; } @@ -316,14 +356,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), - 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) @@ -331,13 +381,13 @@ 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.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; } @@ -375,25 +425,34 @@ static int feat_hctm(int argc, char **argv, struct command *acmd, struct plugin struct config { __u16 tmt1; __u16 tmt2; - __u8 sel; + 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) 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.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; } @@ -433,22 +492,32 @@ static int feat_timestamp(int argc, char **argv, struct command *acmd, struct pl struct config { __u64 tstmp; - __u8 sel; + 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) 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.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; } @@ -466,7 +535,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->feat.save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -475,9 +544,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; } @@ -513,23 +582,32 @@ 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", 'H', &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) 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) | - 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; } @@ -544,7 +622,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->feat.save; if (sv) sel = NVME_GET_FEATURES_SEL_SAVED; @@ -553,13 +631,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; } @@ -597,18 +675,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", 'H', &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_parse_seen(opts, "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); } @@ -645,22 +733,32 @@ static int feat_volatile_wc(int argc, char **argv, struct command *acmd, struct struct config { bool wce; - __u8 sel; + 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) 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.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; } @@ -704,25 +802,34 @@ static int feat_power_limit(int argc, char **argv, struct command *acmd, __u8 plv; __u8 pls; __u8 uidx; - __u8 sel; + 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) 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.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; @@ -774,31 +881,40 @@ static int feat_power_thresh(int argc, char **argv, struct command *acmd, __u8 pmts; __u8 ept; __u8 uidx; - __u8 sel; + 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) 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.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; @@ -846,29 +962,38 @@ static int feat_power_meas(int argc, char **argv, struct command *cmd, __u8 pmts; __u16 smt; __u8 uidx; - __u8 sel; + 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) 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.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; } @@ -913,15 +1038,25 @@ static int feat_err_recovery(int argc, char **argv, struct command *acmd, __u32 nsid; __u16 tler; bool dulbe; - __u8 sel; + struct feat_args feat; }; struct config cfg = { 0 }; - FEAT_ARGS(opts, - OPT_UINT("nsid", 'n', &cfg.nsid, namespace_id_desired), - 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) @@ -935,20 +1070,20 @@ 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.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; } 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) | @@ -964,10 +1099,10 @@ 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")) - cdw11 |= NVME_SET(ncqr, FEAT_NRQS_NSQR); + if (!ncqr_given) + cdw11 |= NVME_SET(ncqr, FEAT_NRQS_NCQR); } err = nvme_set_features(hdl, 0, fid, sv, cdw11, 0, 0, 0, 0, NULL, 0, @@ -999,25 +1134,36 @@ static int feat_num_queues(int argc, char **argv, struct command *acmd, struct config { __u16 nsqr; __u16 ncqr; - __u8 sel; + 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) 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.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; } @@ -1041,15 +1187,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); @@ -1082,26 +1228,36 @@ 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", 'H', &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) 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.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; } @@ -1173,7 +1329,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; } @@ -1223,15 +1379,26 @@ 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, - 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("slbas", 's', &cfg.slbas, slbas_desc), - OPT_LIST("nlbs", 'b', &cfg.nlbs, nlbs_desc), - OPT_LIST("guids", 'g', &cfg.guids, guids_desc)); + /* -s is --slbas and -h is help, --save and --hlbars are long only */ + __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) @@ -1245,11 +1412,11 @@ 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.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; @@ -1290,20 +1457,29 @@ 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) 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.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; } @@ -1342,20 +1518,29 @@ 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) 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.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; } @@ -1393,18 +1578,29 @@ 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); 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.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; } @@ -1441,23 +1637,34 @@ static int feat_async_event_conf(int argc, char **argv, struct command *acmd, struct config { __u32 result; - __u8 sel; + 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); 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.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; } @@ -1494,18 +1701,29 @@ 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); 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.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; } diff --git a/plugins/fw/fw-plugin.c b/plugins/fw/fw-plugin.c index cd7ba532cb..005ef03d89 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; @@ -192,25 +191,21 @@ 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) 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 +394,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 { @@ -430,22 +424,18 @@ 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) 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/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index acfe7221e6..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, @@ -327,19 +325,16 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); 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,7 +406,8 @@ static struct command huawei_list_cmd = { .name = "list", .help = "List all Huawei NVMe devices and namespaces on machine", .fn = huawei_list, - .no_device = true, + .output_formats = NORMAL | JSON, + .device = COMMAND_NO_DEVICE, }; static struct command huawei_id_ctrl_cmd = { diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c index 5320e6ad2e..dd0a5f0766 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) @@ -364,18 +366,20 @@ 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 = { .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 < 0) + if (err) return err; memset(&vpd_log, 0, sizeof(vpd_log)); @@ -459,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; @@ -531,9 +532,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 { @@ -550,10 +552,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) @@ -582,11 +586,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, diff --git a/plugins/id/id-plugin.c b/plugins/id/id-plugin.c index d704a1b185..503d4928c1 100644 --- a/plugins/id/id-plugin.c +++ b/plugins/id/id-plugin.c @@ -50,19 +50,18 @@ 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)); + 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) 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) @@ -113,20 +112,19 @@ 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), - 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) 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"); @@ -185,19 +183,18 @@ static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct .uuid_index = NVME_UUID_NONE, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; @@ -242,18 +239,16 @@ 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) 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) @@ -291,17 +286,15 @@ 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) 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; @@ -347,19 +340,18 @@ 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), - 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) 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; @@ -424,19 +416,18 @@ static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, str .uuid_index = NVME_UUID_NONE, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; @@ -495,19 +486,18 @@ 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), - 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) 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; @@ -569,21 +559,21 @@ 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), - 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) 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; @@ -648,19 +638,18 @@ 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), - 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) 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; @@ -707,17 +696,15 @@ 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) 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) @@ -758,18 +745,16 @@ 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) 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) @@ -810,18 +795,16 @@ 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) 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; @@ -867,18 +850,16 @@ 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) 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; @@ -922,18 +903,16 @@ 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) 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) @@ -975,18 +954,16 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct .cntlid = 0, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; @@ -1031,19 +1008,17 @@ 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) 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"); @@ -1095,12 +1070,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 = { @@ -1174,6 +1151,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/innogrit/innogrit-nvme.c b/plugins/innogrit/innogrit-nvme.c index f60697cf16..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" @@ -181,20 +182,23 @@ 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) 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+"); @@ -227,7 +231,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) @@ -235,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); @@ -264,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+"); } } @@ -282,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+"); } @@ -297,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); @@ -327,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++; @@ -357,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+"); 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 f8a1d7f36c..42bbf7c5ff 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -362,20 +362,19 @@ 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_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) 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) { @@ -409,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) @@ -469,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) @@ -1058,20 +1061,18 @@ 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) 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. @@ -1289,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; @@ -1306,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; } @@ -1383,13 +1385,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), + 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) @@ -1408,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; @@ -1564,10 +1569,10 @@ 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} + 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); @@ -1645,10 +1650,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 38c283298c..e57a795ece 100644 --- a/plugins/io-mgmt/io-mgmt-plugin.c +++ b/plugins/io-mgmt/io-mgmt-plugin.c @@ -56,12 +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), - 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) @@ -136,12 +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), - 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 a6187b85c6..e2172ccacf 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_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), + 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_FILE("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_FILE("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_FILE("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) @@ -1097,56 +1113,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/lm/lm-nvme.c b/plugins/lm/lm-nvme.c index cbecbc366b..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) @@ -171,7 +175,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,25 +183,27 @@ 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, .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) return err; - if (cfg.sel == -1) { + if (!argconfig_given(opts, &cfg.sel)) { nvme_show_error("Select field required"); return -EINVAL; } @@ -272,7 +278,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 +292,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, @@ -299,24 +305,26 @@ 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) return err; - if (cfg.sel == -1) { + if (!argconfig_given(opts, &cfg.sel)) { nvme_show_error("Select field required"); return -EINVAL; } @@ -391,7 +399,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,11 +419,10 @@ 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 = { - .sel = -1, + .sel = 0, .cntlid = 0, .csuuidi = 0, .csvi = 0, @@ -424,36 +430,37 @@ 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, - 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_FLAG("human-readable", 'H', &cfg.human_readable, human_readable_info)); + 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) return err; - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - 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)) { nvme_show_error("cannot parse non-zero offset"); return -EINVAL; } - if (cfg.human_readable) + if (nvme_args.verbose) flags |= VERBOSE; if (cfg.output && strlen(cfg.output)) { @@ -519,26 +526,29 @@ 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, - OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid), - OPT_UINT("hp", 'H', &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) 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)); @@ -569,18 +579,16 @@ 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) 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 f43ef15ec2..9dd7e1446f 100644 --- a/plugins/log/log-plugin.c +++ b/plugins/log/log-plugin.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -84,17 +83,15 @@ 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) 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; @@ -140,19 +137,18 @@ 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), - 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) 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; @@ -201,19 +197,17 @@ 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); 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 +460,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; @@ -474,7 +467,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; @@ -485,28 +478,24 @@ 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, - 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) 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; @@ -514,7 +503,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; @@ -620,19 +609,17 @@ 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); 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) @@ -699,20 +686,18 @@ 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); 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; @@ -800,27 +785,26 @@ 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), - 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) 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; @@ -879,18 +863,16 @@ 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) 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; @@ -931,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", @@ -943,11 +927,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; @@ -1014,19 +994,17 @@ 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) 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; @@ -1066,7 +1044,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; }; @@ -1077,20 +1055,18 @@ 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) 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; @@ -1162,20 +1138,18 @@ 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) 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; @@ -1197,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; @@ -1263,7 +1245,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; }; @@ -1274,20 +1256,18 @@ 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) 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; @@ -1342,6 +1322,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; @@ -1353,25 +1334,24 @@ 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) 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)); + 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) @@ -1405,17 +1385,15 @@ 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) 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) @@ -1462,19 +1440,17 @@ 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) 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!"); @@ -1555,19 +1531,17 @@ 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) 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); @@ -1621,15 +1595,74 @@ 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 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; @@ -1643,41 +1676,33 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; - 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; } -#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 +1756,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 +1782,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; @@ -1776,57 +1800,27 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, .raw_binary = false, }; - NVME_ARGS(opts, - OPT_UINT("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) 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; - /* - * 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); @@ -1855,19 +1849,17 @@ 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) 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; @@ -1904,17 +1896,15 @@ 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) 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; @@ -1949,17 +1939,15 @@ 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) 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; @@ -2002,18 +1990,16 @@ 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) 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; @@ -2046,17 +2032,15 @@ 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) 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) @@ -2094,18 +2078,16 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * .endgid = 0, }; - NVME_ARGS(opts, - OPT_UINT("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) 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) @@ -2124,13 +2106,16 @@ 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); - __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; @@ -2140,13 +2125,24 @@ 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; + *lenp = header_len; + 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; + *lenp = header_len + psub_list_len; nvme_init_get_log_dispersed_ns_participating_nss(&cmd, nsid, (void *)log->participating_nss, psub_list_len); @@ -2156,6 +2152,7 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi if (err) goto err_free; +out: *logp = log; return 0; @@ -2174,6 +2171,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; @@ -2183,25 +2181,25 @@ 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)); + 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) 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); + 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; } @@ -2230,18 +2228,16 @@ 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) 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; @@ -2269,9 +2265,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); @@ -2286,20 +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; - - *log = libnvme_realloc(*log, *offset); - if (!*log) - return -ENOMEM; + *offset = need; args->log = (char *)*log + args->lpo; @@ -2326,21 +2338,36 @@ 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; + 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, size); 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, + size); if (err) goto err_free; } @@ -2362,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, @@ -2374,11 +2402,11 @@ 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; - 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; @@ -2413,19 +2441,17 @@ 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) 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) { @@ -2438,22 +2464,37 @@ 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; + 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, size); 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, + size); if (err) goto err_free; } @@ -2475,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, @@ -2491,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; @@ -2526,19 +2569,17 @@ 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) 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) { @@ -2552,12 +2593,15 @@ 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; 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, .nsid = NVME_NSID_ALL, @@ -2569,18 +2613,22 @@ 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, &size); + if (err) + goto err_free; + } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2595,6 +2643,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; @@ -2608,39 +2657,39 @@ 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); 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); + 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; 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, .nsid = NVME_NSID_ALL, @@ -2656,13 +2705,17 @@ 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, &size); + if (err) + goto err_free; + } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2677,6 +2730,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; @@ -2688,36 +2742,39 @@ 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) 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); + 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; 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, .nsid = NVME_NSID_ALL, @@ -2733,13 +2790,17 @@ 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, &size); + if (err) + goto err_free; + } *logp = log; + *lenp = log_len; return 0; err_free: @@ -2755,6 +2816,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; @@ -2766,25 +2828,24 @@ 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) 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); + 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/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 91cd74404e..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); @@ -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"; @@ -438,24 +442,28 @@ 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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) 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); @@ -487,7 +495,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) @@ -526,9 +536,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) @@ -575,8 +587,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) @@ -701,7 +715,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) @@ -760,46 +776,40 @@ 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_ARGS(opts) = { + OPT_FILE("fw", 'f', &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) 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) { @@ -866,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]; @@ -890,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) @@ -909,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 { @@ -922,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 { @@ -939,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); @@ -976,8 +987,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) @@ -1017,7 +1030,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) @@ -1064,10 +1079,10 @@ 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} + 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); @@ -1234,6 +1249,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 @@ -1248,23 +1269,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; @@ -1304,7 +1327,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; @@ -1454,11 +1479,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; @@ -1559,11 +1584,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; @@ -1792,24 +1817,23 @@ 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 { - int duration; + __u32 duration; bool raw_binary; }; 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 6dfc17446d..874d5a21a1 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 ! @@ -331,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') { @@ -590,42 +620,38 @@ 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_ARGS(opts) = { + OPT_FILE("fw", 'f', &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) 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,21 +735,23 @@ static int micron_smbus_option(int argc, char **argv, int err = 0; struct { - char *option; - int value; - int save; + 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_UINT("value", 'V', &opt.value, value), - OPT_UINT("save", 's', &opt.save, save)); + 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_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -734,7 +762,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 +770,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 +778,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 +786,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; @@ -776,7 +800,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,17 +807,15 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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); @@ -812,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 }; @@ -820,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); @@ -913,22 +936,19 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 */ @@ -1022,7 +1042,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) @@ -1080,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 { @@ -1132,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) { @@ -1142,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; } @@ -1202,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)); } @@ -1658,20 +1688,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 */ @@ -1739,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; } @@ -1787,20 +1818,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 || @@ -1833,20 +1861,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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) { @@ -1877,20 +1902,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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) { @@ -2209,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; @@ -2233,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"); @@ -2618,10 +2640,11 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -2632,11 +2655,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) { @@ -2681,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); } } @@ -2770,7 +2792,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) @@ -2787,7 +2811,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) @@ -2957,20 +2983,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 */ @@ -3068,33 +3091,31 @@ 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_UINT("threshold", 't', &opt.threshold, thrtime)); + 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_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); 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) { @@ -3137,29 +3158,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; @@ -3207,7 +3232,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) @@ -3268,32 +3295,31 @@ 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_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) 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)); @@ -3314,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; @@ -3336,21 +3364,18 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 */ @@ -3402,7 +3427,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); @@ -3443,16 +3470,18 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct { - char *option; - int select; + 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_UINT("select", 's', &opt.select, select)); + OPT_ARGS(opts) = { + OPT_ENUM("option", 'O', &opt.option, option, micron_options), + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model); if (err) @@ -3465,21 +3494,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) @@ -3487,10 +3516,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; @@ -3734,24 +3759,32 @@ 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; - int data_area; + __u32 data_area; int log; }; struct config cfg = { - .type = "", + .type = 0x07, .package = "", - .data_area = -1, + .data_area = 0, .log = 0x07, }; - NVME_ARGS(opts, - OPT_STRING("type", 't', "log type", &cfg.type, type), - OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_UINT("data_area", 'd', &cfg.data_area, data_area)); + OPT_ARGS(opts) = { + OPT_ENUM("type", 't', &cfg.type, type, types), + OPT_FILE("package", 'p', &cfg.package, package), + 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) @@ -3759,20 +3792,15 @@ 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 <= 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; @@ -3828,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), @@ -4055,7 +4083,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) @@ -4121,7 +4151,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) @@ -4173,7 +4205,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) @@ -4226,20 +4260,17 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); 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 */ @@ -4429,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); } } @@ -4458,14 +4489,15 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel); if (err) @@ -4475,11 +4507,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); @@ -4490,9 +4518,9 @@ 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, nvme_args.device); else - print_micron_health_log_normal(&smart_log, argv[optind]); + print_micron_health_log_normal(&smart_log, nvme_args.device); return 0; } @@ -4536,7 +4564,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) @@ -4547,11 +4577,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); @@ -4576,12 +4602,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 = { @@ -4606,32 +4634,35 @@ 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 = { .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 = { @@ -4644,6 +4675,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 = { @@ -4668,6 +4700,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 = { @@ -4698,24 +4731,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/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index c3ac480041..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: @@ -664,19 +661,22 @@ 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", 'H', &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_DIR("nbft-path", 0, &nbft_path, + "user-defined path for NBFT tables"), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); 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) @@ -703,7 +703,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 9100651bd0..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; } @@ -934,6 +939,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,10 +974,12 @@ 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 < 0) + if (ret) return ret; ret = nvme_create_global_ctx(&ctx); @@ -986,8 +999,8 @@ static int netapp_smdevices(int argc, char **argv, struct command *acmd, return devs.num; } - if (optind < argc) - devname = basename(argv[optind++]); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_num, nsid; @@ -1060,10 +1073,12 @@ 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 < 0) + if (ret) return ret; ret = nvme_create_global_ctx(&ctx); @@ -1077,8 +1092,8 @@ static int netapp_ontapdevices(int argc, char **argv, struct command *acmd, return -EINVAL; } - if (optind < argc) - devname = basename(argv[optind++]); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_num, nsid; @@ -1148,14 +1163,18 @@ static struct command netapp_smdevices_cmd = { .name = "smdevices", .help = "NetApp SMdevices", .fn = netapp_smdevices, - .no_device = true, + .output_formats = OUTPUT_FORMATS_CUSTOM, + .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command netapp_ontapdevices_cmd = { .name = "ontapdevices", .help = "NetApp ONTAPdevices", .fn = netapp_ontapdevices, - .no_device = true, + .output_formats = OUTPUT_FORMATS_CUSTOM, + .output_format_desc = NETAPP_OUTPUT_FORMAT_DESC, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command *commands[] = { diff --git a/plugins/ns/ns-plugin.c b/plugins/ns/ns-plugin.c index d1fd93eba7..6e89f6f47c 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 { @@ -89,20 +88,17 @@ 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)); + 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) 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 +128,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"; @@ -150,21 +145,18 @@ 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), - 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) 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); @@ -186,7 +178,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 }; @@ -366,7 +358,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; @@ -417,38 +408,34 @@ 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) 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,20 +593,15 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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/nvme-mi/nvme-mi-plugin.c b/plugins/nvme-mi/nvme-mi-plugin.c index 95631fe354..67b872864a 100644 --- a/plugins/nvme-mi/nvme-mi-plugin.c +++ b/plugins/nvme-mi/nvme-mi-plugin.c @@ -69,20 +69,23 @@ 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), - 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) 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..2581655a3f 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -20,6 +20,9 @@ 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; __u64 result = 0; @@ -28,15 +31,16 @@ 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', NULL, - "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) return err; - if (argconfig_parse_seen(opts, "no-uuid")) + if (no_uuid_flag) uuid = false; if (uuid) { @@ -64,6 +68,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"; @@ -86,16 +91,20 @@ 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_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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), + OPT_END() + }; 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-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index 40ab955443..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; @@ -71,12 +73,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..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); @@ -264,11 +307,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) { @@ -314,8 +353,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-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-nvme.c b/plugins/ocp/ocp-nvme.c index 4ced2d032d..1c8cb321d4 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -266,20 +266,23 @@ 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_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) 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", @@ -337,17 +340,33 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, .latency_monitor_feature_enable = 0x1, }; - 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_ARGS(opts) = { + 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), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -403,6 +422,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"; @@ -425,16 +445,20 @@ 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_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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), + OPT_END() + }; 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 */ @@ -547,6 +571,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"; @@ -568,23 +593,26 @@ 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', NULL, 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, + nvme_sel_values, .max = NVME_SEL_MAX), + OPT_FLAG("no-uuid", 'n', &no_uuid_flag, no_uuid), + OPT_END() + }; 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; } @@ -936,6 +964,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'"; @@ -951,6 +980,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; @@ -960,7 +996,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]; @@ -968,22 +1004,23 @@ 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_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_ARGS(opts) = { + 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, + tele_types), + 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) 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) @@ -1011,31 +1048,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"); @@ -1054,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, @@ -1080,10 +1106,11 @@ 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, - !argconfig_parse_seen(opts, "no-uuid")); + !no_uuid_flag); if (err) { nvme_show_error("Failed to fetch string-log from the drive."); goto out; @@ -1092,7 +1119,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) { @@ -1201,6 +1228,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; @@ -1214,15 +1242,17 @@ static int ocp_unsupported_requirements_log(int argc, char **argv, struct comman .output_format = "normal", }; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, 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) 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); @@ -1303,6 +1333,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; @@ -1316,15 +1347,17 @@ 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', NULL, 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) 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); @@ -1404,20 +1437,23 @@ 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_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) 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); @@ -1472,14 +1508,16 @@ 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) 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"); @@ -1495,6 +1533,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"; @@ -1517,16 +1556,20 @@ 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_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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), + OPT_END() + }; 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 */ @@ -1593,6 +1636,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"; @@ -1611,18 +1655,20 @@ 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', NULL, 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) 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; } @@ -1666,6 +1712,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/"; @@ -1685,25 +1733,28 @@ 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', NULL, all), - OPT_FLAG("no-uuid", 'n', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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() + }; 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."); @@ -1719,6 +1770,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"; @@ -1731,7 +1783,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; }; @@ -1740,17 +1792,20 @@ static int set_plp_health_check_interval(int argc, char **argv, struct command * .sv = false, }; - NVME_ARGS(opts, - OPT_BYTE("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_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_END() + }; 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) { @@ -1792,8 +1847,11 @@ 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, + nvme_sel_values, .max = NVME_SEL_MAX), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1844,9 +1902,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) @@ -1892,8 +1952,11 @@ 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, + nvme_sel_values, .max = NVME_SEL_MAX), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1954,6 +2017,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; @@ -1974,21 +2038,25 @@ 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', NULL, 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) 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, - !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); @@ -2072,6 +2140,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; @@ -2085,15 +2154,17 @@ static int ocp_tcg_configuration_log(int argc, char **argv, struct command *acmd .output_format = "normal", }; - NVME_ARGS(opts, - OPT_FLAG("no-uuid", 'n', NULL, 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) 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); @@ -2188,6 +2259,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; @@ -2199,10 +2272,13 @@ 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_FLAG("no-uuid", 'n', NULL, no_uuid), - OPT_FLAG("all-ns", 'a', NULL, all_ns)); + OPT_ARGS(opts) = { + 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() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -2214,9 +2290,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) @@ -2282,6 +2358,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; @@ -2292,13 +2370,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', NULL, no_uuid), - OPT_FLAG("all-ns", 'a', NULL, 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) @@ -2310,8 +2390,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) @@ -2350,6 +2430,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 { @@ -2360,22 +2441,23 @@ 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', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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() + }; 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; @@ -2383,7 +2465,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) { @@ -2410,21 +2492,28 @@ 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; 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_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) 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) @@ -2463,20 +2552,18 @@ 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) 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; @@ -2563,6 +2650,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"; @@ -2586,16 +2674,20 @@ 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_UINT("namespace-id", 'n', &cfg.nsid, nsid), - OPT_FLAG("no-uuid", 'u', NULL, no_uuid)); + OPT_ARGS(opts) = { + 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), + OPT_END() + }; 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-print-json.c b/plugins/ocp/ocp-print-json.c index a81c0412bc..010020684c 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" @@ -114,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; } } @@ -148,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); @@ -198,9 +192,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 +280,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 +449,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 +598,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 +611,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 +629,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 +645,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 +658,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 +676,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 +689,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 +718,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 +736,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 +757,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 +798,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 +843,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 +853,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 +881,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 +899,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 +932,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 +1020,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 +1051,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 +1082,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 +1112,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 +1160,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); @@ -1274,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); @@ -1389,10 +1353,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); diff --git a/plugins/ocp/ocp-print-stdout.c b/plugins/ocp/ocp-print-stdout.c index 2550a09abd..6cf20c96fd 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 @@ -39,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; } } @@ -325,10 +319,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"); @@ -846,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/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index c826ff47db..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, @@ -114,14 +113,17 @@ 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; __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_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) @@ -129,7 +131,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/ocp/ocp-telemetry-decode.c b/plugins/ocp/ocp-telemetry-decode.c index 4e7d0ee29d..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" @@ -126,6 +128,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 }, @@ -317,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); } @@ -325,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); @@ -468,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; } @@ -499,6 +619,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; + + shr_strscpy(description, str, OCP_TELEMETRY_DESCRIPTION_MAX); + 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 +646,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) @@ -1194,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 @@ -1952,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); @@ -2194,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) { diff --git a/plugins/registry/registry-nvme.c b/plugins/registry/registry-nvme.c index 5beb3b9cfa..9f82b84b19 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 (!nvme_args.device) return NULL; - device = argv[optind]; + device = nvme_args.device; strip_dev_prefix(&device); return device; } @@ -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) @@ -256,7 +264,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/resv/resv-plugin.c b/plugins/resv/resv-plugin.c index 763984a4dc..7af17efed2 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; @@ -62,24 +61,21 @@ 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), - 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) 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 +113,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; @@ -138,24 +133,21 @@ 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), - 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) 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 +194,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; @@ -211,7 +202,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,26 +210,23 @@ 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, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), - 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) 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) { @@ -308,7 +296,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; }; @@ -319,21 +307,20 @@ 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), - 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) 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/rpmb/rpmb.c b/plugins/rpmb/rpmb.c index 35d60b4bb5..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) { @@ -778,7 +779,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 +815,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_FILE("keyfile", 'g', &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 +862,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 +910,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", 'o', &cfg.address, address), - OPT_UINT("blocks", 'b', &cfg.blocks, blocks), - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + 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), + OPT_END() + }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -969,14 +978,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", 'o', &cfg.address, address), - OPT_UINT("blocks", 'b', &cfg.blocks, blocks), - OPT_BYTE("target", 't', &cfg.target, target)); + OPT_ARGS(opts) = { + 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), + 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 +1047,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_FILE("msgfile", 'f', &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 +1114,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_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() + }; __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 279927b505..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,32 +1294,41 @@ 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; - int compress; - int hide_progress; + bool compress; + bool hide_progress; }; struct config cfg = { - .type = NULL, + .types = 0, .area = 0, .file = NULL, - .compress = 0, - .hide_progress = 0, + .compress = false, + .hide_progress = false, }; - NVME_ARGS_OUTPUT_FORMATS(opts, NORMAL, "Output format: normal", - 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", 'H', &cfg.hide_progress, hide_progress)); + OPT_ARGS(opts) = { + 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), + OPT_FLAG("hide-progress", 0, &cfg.hide_progress, hide_progress), + OPT_END() + }; samsung_initialize(); @@ -1375,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 @@ -1440,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); @@ -1510,6 +1473,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/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index 7691318dcf..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" @@ -394,7 +395,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 = @@ -411,14 +411,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; - int data_area; + __u32 data_area; __u64 file_size; __u64 offset; - char *type; - bool verbose; + int type; }; struct config cfg = { @@ -427,19 +434,18 @@ static int sndk_vs_internal_fw_log(int argc, char **argv, .data_area = 0, .file_size = 0, .offset = 0, - .type = NULL, - .verbose = false, + .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_FILE("type", 't', &cfg.type, type), - OPT_FLAG("verbose", 'V', &cfg.verbose, verbose), + 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) @@ -470,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)); @@ -494,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); @@ -506,24 +512,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); @@ -569,7 +562,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; } @@ -619,11 +612,16 @@ static int sndk_vs_smart_add_log(int argc, char **argv, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("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_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) @@ -788,8 +786,10 @@ static int sndk_drive_resize(int argc, char **argv, .size = 0, }; - NVME_ARGS(opts, - OPT_UINT("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) @@ -906,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)) @@ -971,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), @@ -1005,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); } @@ -1135,7 +1138,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) @@ -1183,7 +1188,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) @@ -1253,7 +1260,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/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, diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index baa5c2982c..aeac1a0434 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 @@ -347,20 +349,19 @@ 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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG_JSON("json", 'j', &cfg.json, json)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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)); @@ -518,9 +519,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) @@ -657,7 +660,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,11 +711,15 @@ 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; - 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) @@ -836,10 +845,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) @@ -911,11 +922,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), - 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) @@ -998,9 +1012,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), - 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) @@ -1107,7 +1124,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, @@ -1333,12 +1351,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) @@ -1469,12 +1490,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) @@ -1530,7 +1555,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; @@ -1543,18 +1569,16 @@ 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) 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) && @@ -1576,9 +1600,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'; } @@ -1615,9 +1640,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; @@ -1710,8 +1735,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; } @@ -1734,8 +1759,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; } @@ -1757,8 +1782,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; } @@ -1768,7 +1793,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; } } @@ -1855,21 +1879,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) @@ -1967,7 +1991,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); diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index c1777b4b72..ea21943ee0 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -158,17 +158,15 @@ 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) 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) { @@ -313,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; @@ -334,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) { @@ -394,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); } @@ -496,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); @@ -507,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); @@ -518,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); @@ -529,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); @@ -540,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); } @@ -607,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); @@ -739,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); @@ -748,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); @@ -806,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); @@ -856,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); @@ -887,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); } @@ -901,33 +924,33 @@ 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; - NVME_ARGS(opts); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); - if (err) { - nvme_show_error("Device not found"); - return -1; - } - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); + if (err) return err; - } - if (flags & JSON) - printf("Seagate Extended SMART Information :\n"); + flags = nvme_args.output_flags; + 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. @@ -947,6 +970,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++) @@ -955,11 +979,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); } @@ -971,16 +993,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); @@ -989,18 +1006,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); } } @@ -1008,45 +1020,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; @@ -1061,7 +1037,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); @@ -1091,19 +1067,15 @@ 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) { - nvme_show_error("Device not found"); - return -1; - } - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); + if (err) return err; - } + + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate Temperature Stats Information :\n"); @@ -1209,7 +1181,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; @@ -1254,19 +1226,15 @@ 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) { - nvme_show_error("Device not found"); - return -1; - } - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); + if (err) return err; - } + + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate PCIe error counters Information :\n"); @@ -1391,19 +1359,15 @@ 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 < 0) { - nvme_show_error("Device not found"); - return -1; - } - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); + if (err) return err; - } + + flags = nvme_args.output_flags; if (!(flags & JSON)) printf("Seagate FW Activation History Information :\n"); @@ -1451,14 +1415,14 @@ 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 < 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); @@ -1511,14 +1475,14 @@ 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) { - nvme_show_error("Device not found"); - return -1; - } + if (err) + return err; nvme_init_identify_ctrl(&cmd, &ctrl); @@ -1576,10 +1540,13 @@ static int get_host_tele(int argc, char **argv, struct command *acmd, struct plu .log_id = 0, }; - 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_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) @@ -1681,9 +1648,12 @@ static int get_ctrl_tele(int argc, char **argv, struct command *acmd, struct plu .namespace_id = 0xffffffff, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1794,9 +1764,12 @@ static int vs_internal_log(int argc, char **argv, struct command *acmd, struct p .file = "", }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_FILE("dump-file", 'f', &cfg.file, file)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -1876,7 +1849,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) @@ -1895,7 +1870,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) @@ -1972,14 +1949,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/security/security-plugin.c b/plugins/security/security-plugin.c index 12c7ce2a7b..8b83b01aeb 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; @@ -72,25 +71,22 @@ 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), - 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) 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 +169,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 { @@ -198,26 +193,23 @@ 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), - 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) 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/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..e94547f353 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 @@ -1042,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"); @@ -1080,7 +1081,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/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index dac9d6bcdd..579af38a30 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -134,9 +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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -177,7 +180,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; @@ -191,7 +193,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 = { @@ -202,14 +203,17 @@ 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), - 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_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + 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, + 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), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -281,13 +285,16 @@ static int set_additional_feature(int argc, char **argv, struct command *acmd, s .save = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - 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_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_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 54d46cd64c..d5e51ef022 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -78,18 +78,15 @@ 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) 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-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index 8a07597c99..bbab9ad182 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,24 +29,24 @@ 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; - __u16 lba_size; - __u16 ftl_unit_size; + unsigned int lba_size; + uint64_t 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) 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) @@ -52,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); @@ -71,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; diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index 8c52928918..335f1c608c 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,49 +857,40 @@ 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_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir)); + OPT_ARGS(opts) = { + OPT_ENUM("type", 't', &cfg.type, type, types), + OPT_DIR("dir-name", 'd', &cfg.out_dir, out_dir), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) 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) diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 8a7fb5941e..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) @@ -376,12 +380,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..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) @@ -247,12 +249,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-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-nvme.c b/plugins/solidigm/solidigm-nvme.c index 58a35d9cab..c619df4406 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" @@ -161,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 = { @@ -197,13 +199,14 @@ 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 = { .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-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 82ee827e07..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) { @@ -269,20 +270,17 @@ 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")); + 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) 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/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 42a14b2ff1..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); @@ -78,20 +80,18 @@ 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. - // GNU getopt() permutes the contents of argv as it scans, - // so that eventually all the nonoptions are at the end. - if (argc > optind) { + if (nvme_args.device) { nvme_show_error( "Device path not allowed when using --source-file"); return -EINVAL; @@ -115,7 +115,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-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 bdf9fdbde7..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); } } } @@ -543,23 +544,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) @@ -593,14 +601,15 @@ 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; 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); diff --git a/plugins/ssstc/ssstc-nvme.c b/plugins/ssstc/ssstc-nvme.c index 81bce480f7..eb14aba546 100644 --- a/plugins/ssstc/ssstc-nvme.c +++ b/plugins/ssstc/ssstc-nvme.c @@ -403,10 +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_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_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 b4b6be9622..836348840f 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 = { @@ -428,16 +428,17 @@ static int vendor_log(int argc, char **argv, struct command *acmd, struct plugin .log = 0xca }; - 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_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) { - 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); @@ -475,15 +476,15 @@ 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) { - nvme_show_error("%s: failed to parse arguments", __func__); - return -EINVAL; - } + if (err) + return err; if (cfg.prev_log) printf("Getting previous log\n"); @@ -514,13 +515,13 @@ 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) { - 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..b770ef68a8 100644 --- a/plugins/transcend/transcend-nvme.c +++ b/plugins/transcend/transcend-nvme.c @@ -29,13 +29,13 @@ 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) { - 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: "); @@ -62,13 +62,13 @@ 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) { - 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/utils/command-metadata-schema.json b/plugins/utils/command-metadata-schema.json index b1e3ea74eb..a84e6b5808 100644 --- a/plugins/utils/command-metadata-schema.json +++ b/plugins/utils/command-metadata-schema.json @@ -58,16 +58,30 @@ "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." }, + "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." @@ -99,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\"), 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", + "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." @@ -117,9 +146,17 @@ "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 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 115bc20b62..123726a7ed 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 @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #endif #include +#include #include "command-metadata.h" #include "nvme-json.h" @@ -55,7 +55,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() @@ -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; @@ -321,7 +325,9 @@ 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; + dst[i].min = opts[i].min; + dst[i].max = opts[i].max; } *out = dst; @@ -329,20 +335,79 @@ static int copy_options(const struct argconfig_commandline_options *opts, return 0; } +/* 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 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 * +flatten(struct argconfig_commandline_options *root) +{ + struct argconfig_commandline_options *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 into the model, then returns the sentinel so the parser - * unwinds before the command opens a device. + * 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; (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 = + flatten(root); + int err = -ENOMEM; + + if (all) + err = copy_options(all, &command_metadata_cur_command->options, &command_metadata_cur_command->num_options); if (err) @@ -366,6 +431,10 @@ 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->deprecated = cmd->deprecated; + mc->replacement_plugin = cmd->replacement.plugin; + mc->replacement_command = cmd->replacement.command; mc->captured = false; /* @@ -379,6 +448,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_prepare(cmd); (void)cmd->fn(2, argv, cmd, plugin); capture_current_command = NULL; command_metadata_cur_command = NULL; @@ -529,9 +599,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"; @@ -540,7 +610,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; } /* @@ -564,24 +634,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 - * 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. + * 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: 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) { + 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) @@ -593,6 +669,46 @@ 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_ENUM_LIST: + return "enum-list"; + 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) @@ -608,6 +724,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) @@ -616,6 +741,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) @@ -625,6 +754,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; @@ -637,12 +773,22 @@ 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]); + 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++) { /* - * 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/plugins/utils/command-metadata.h b/plugins/utils/command-metadata.h index e24629b75b..280b97b939 100644 --- a/plugins/utils/command-metadata.h +++ b/plugins/utils/command-metadata.h @@ -24,10 +24,13 @@ 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 */ + 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 { @@ -37,6 +40,11 @@ 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 */ + bool deprecated; + /* "nvme " which replaces it, or NULL */ + const char *replacement_plugin; + const char *replacement_command; }; struct command_metadata_plugin { 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/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index f9db4c15ca..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 = 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); +} + +/* 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) { - 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); + 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) +static void vt_convert_smart_data_to_human_readable_format( + struct vtview_smart_log_entry *smart, char *text, size_t size) { - char tempbuff[1024] = ""; + 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); } @@ -326,22 +318,24 @@ 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; } - 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)) { + 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"); @@ -943,7 +1002,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" @@ -967,34 +1025,28 @@ 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); - - if (argc >= 2) { - if (strlen(argv[1]) > sizeof(path) - 1) { - nvme_show_error("Filename too long"); - return -1; - } - strcpy(path, argv[1]); - } + 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) { - 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); + 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; @@ -1014,7 +1066,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; @@ -1040,13 +1092,13 @@ 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) { - 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 81dfed3500..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); @@ -3888,9 +3890,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) @@ -3901,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"); @@ -3914,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); @@ -4311,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)); @@ -4409,7 +4413,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 = @@ -4428,14 +4431,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; - int data_area; + __u32 data_area; __u64 file_size; __u64 offset; - char *type; - bool verbose; + int type; }; struct config cfg = { @@ -4444,19 +4453,18 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *acmd, .data_area = 0, .file_size = 0, .offset = 0, - .type = NULL, - .verbose = false, + .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_FILE("type", 't', &cfg.type, type), - OPT_FLAG("verbose", 'V', &cfg.verbose, verbose), + 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) @@ -4486,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)); @@ -4510,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); @@ -4522,24 +4530,14 @@ 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); + shr_strscpy(fb, cfg.file, sizeof(fb)); } else { wdc_UtilsGetTime(&timeInfo); memset(timeStamp, 0, sizeof(timeStamp)); @@ -4747,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"; @@ -4818,8 +4816,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) @@ -4836,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"); @@ -4864,8 +4864,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) @@ -4905,8 +4907,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) @@ -4985,7 +4989,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) @@ -5037,7 +5043,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) @@ -5334,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])); } } @@ -5437,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])); } @@ -5448,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); @@ -5502,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]); } @@ -5510,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); @@ -5552,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]); } @@ -5561,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); @@ -6018,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); } } @@ -6120,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(" "); @@ -6131,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 { @@ -6153,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)) @@ -6216,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)) @@ -6278,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); @@ -6296,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); @@ -6324,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); } @@ -6360,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) @@ -6371,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", @@ -6386,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); } @@ -6819,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); @@ -7146,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) { @@ -7164,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); } @@ -8158,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[] = { @@ -8277,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); } } @@ -8313,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); @@ -8417,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"); } @@ -8454,11 +8515,16 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *acmd, .namespace_id = NVME_NSID_ALL, }; - NVME_ARGS(opts, - OPT_UINT("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_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) @@ -8525,13 +8591,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, @@ -8582,15 +8642,17 @@ 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 = { .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) @@ -8611,12 +8673,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, @@ -8701,8 +8758,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)); + 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) @@ -8755,10 +8815,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; @@ -8770,8 +8831,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)); + 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) @@ -8800,7 +8864,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) { @@ -8823,9 +8887,6 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *acmd, ret = -1; } -free_buf: - free(data); - out: return ret; } @@ -8860,8 +8921,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)); + 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) @@ -8934,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); @@ -8967,7 +9033,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) @@ -9011,7 +9079,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) @@ -9056,7 +9126,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) @@ -9101,7 +9173,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) @@ -9179,7 +9253,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) @@ -9225,7 +9301,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) @@ -9369,7 +9447,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) @@ -9592,7 +9672,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) @@ -9677,7 +9759,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) @@ -9731,10 +9815,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) @@ -10549,8 +10635,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_DIR("dir-name", 'd', &cfg.dirName, dirName), + OPT_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); @@ -10570,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; @@ -10646,8 +10734,10 @@ static int wdc_drive_resize(int argc, char **argv, .size = 0, }; - NVME_ARGS(opts, - OPT_UINT("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) @@ -10694,9 +10784,12 @@ static int wdc_namespace_resize(int argc, char **argv, .op_option = 0xF, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("op-option", 'O', &cfg.op_option, op_option)); + 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_END() + }; ret = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (ret) @@ -10746,7 +10839,7 @@ static int wdc_reason_identifier(int argc, char **argv, struct config { - int log_id; + __u32 log_id; char *file; }; struct config cfg = { @@ -10754,9 +10847,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); @@ -10783,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)); @@ -10803,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); @@ -11014,7 +11109,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) @@ -11779,7 +11876,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) @@ -11858,7 +11957,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) @@ -11948,7 +12049,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) @@ -12013,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)); @@ -12038,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); @@ -12106,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); @@ -12149,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); @@ -12202,7 +12309,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) @@ -12308,7 +12417,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) @@ -12413,7 +12524,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) @@ -12457,8 +12570,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)); + 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) @@ -12522,10 +12638,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) @@ -12819,34 +12937,37 @@ int wdc_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, .latency_monitor_feature_enable = 0x7, }; - 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_ARGS(opts) = { + 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), + OPT_END() + }; 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 190763ae91..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) { @@ -132,13 +131,11 @@ 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(argv[optind], "/dev/nvme%d", &idx) != 1) + if (sscanf(nvme_args.device, "/dev/nvme%d", &idx) != 1) idx = 0; *model = GetSSDModel(idx); } @@ -414,9 +411,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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw)); + 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_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 68bc877653..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_OUTPUT_FORMATS(opts, NORMAL, "Output format: normal"); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_args(argc, argv, desc, opts); if (err) @@ -148,16 +150,16 @@ 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) - 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); if (!err) @@ -174,7 +176,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; @@ -186,27 +187,27 @@ 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; }; struct config cfg = { }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_FLAG("vendor-specific", 'V', &cfg.vendor_specific, vendor_specific), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + 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), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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) + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.namespace_id) { @@ -253,10 +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_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("select-all", 'a', &cfg.select_all, select_all)); + 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_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -341,24 +345,27 @@ 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; }; struct config cfg = {}; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - 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_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_END() + }; 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); @@ -463,15 +470,18 @@ 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_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_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_END() + }; 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); @@ -531,15 +541,18 @@ 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_SUFFIX("start-lba", 's', &cfg.zslba, zslba), - OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), - OPT_FILE("data", 'd', &cfg.file, data)); + 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_END() + }; 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); @@ -613,13 +626,16 @@ 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_SUFFIX("lba", 'l', &cfg.lba, slba)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_SUFFIX("lba", 'l', &cfg.lba, slba), + OPT_END() + }; 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); @@ -653,7 +669,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; @@ -668,20 +683,19 @@ 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_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) - return errno; - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) return err; if (!cfg.namespace_id) { @@ -726,7 +740,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; @@ -752,34 +765,33 @@ 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; - bool verbose; + __u32 num_descs; + __u32 state; bool extended; bool partial; }; struct config cfg = { - .num_descs = -1, + .num_descs = 0, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - 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)); + 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) - return errno; - - err = validate_output_format(nvme_args.output_format, &flags); - if (err < 0) return err; - if (cfg.verbose) + + flags = nvme_args.output_flags; + if (nvme_args.verbose) flags |= VERBOSE; if (!cfg.namespace_id) { @@ -830,7 +842,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; @@ -930,22 +942,27 @@ 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_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_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_END() + }; 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"); @@ -1089,18 +1106,19 @@ 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_FLAG("rae", 'r', &cfg.rae, rae)); + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id, + nvme_nsid_values), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); 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); if (err < 0) { @@ -1124,7 +1142,8 @@ static struct command list_cmd = { .name = "list", .help = "List all NVMe devices with Zoned Namespace Command Set support", .fn = list, - .no_device = true, + .output_formats = OUTPUT_FORMATS_NORMAL, + .device = COMMAND_NO_DEVICE, }; static struct command id_ctrl_cmd = { diff --git a/scripts/build.sh b/scripts/build.sh index 3c610d919f..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" @@ -55,11 +67,14 @@ 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" 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" @@ -84,6 +99,17 @@ 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() { + CONFIG_OPTIONS+=("$1 $2") +} + ARGS=() while [[ $# -gt 0 ]]; do case "$1" in @@ -133,26 +159,66 @@ 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 + ;; + --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) @@ -173,13 +239,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 @@ -192,6 +257,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 @@ -334,6 +405,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 @@ -441,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" \ 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; } 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. * 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/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/net-util-linux.c b/shared/net-util-linux.c index 90adddf15c..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,26 +28,55 @@ #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. + */ +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 +88,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 = scope_to_index(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 +155,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 +187,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 +210,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 +227,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 +266,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,22 +279,36 @@ 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; } 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; @@ -288,42 +326,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; } 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/parse-util.c b/shared/parse-util.c index a3e42bb1ca..83b3c2b9e0 100644 --- a/shared/parse-util.c +++ b/shared/parse-util.c @@ -6,8 +6,12 @@ * Authors: Martin Belanger */ +#include #include #include +#include +#include +#include #include #include #include @@ -15,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) @@ -43,50 +50,134 @@ int shr_parse_bool(const char *value, bool *out) return -EINVAL; } -#define DEFINE_SHR_PARSE_CSV_FUNC(name, ret_t) \ +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) || shr_has_leading_zero(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) || shr_has_leading_zero(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; +} + +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; +} + +/* + * 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) @@ -98,15 +189,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; @@ -115,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') @@ -139,8 +236,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/parse-util.h b/shared/parse-util.h index 08cfc4f892..c94b811618 100644 --- a/shared/parse-util.h +++ b/shared/parse-util.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include @@ -25,6 +26,39 @@ typedef unsigned __int128 uint128_t; */ int shr_parse_bool(const char *value, bool *out); +/* + * 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. + */ +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 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/string-util.c b/shared/string-util.c new file mode 100644 index 0000000000..1afd4aa7ca --- /dev/null +++ b/shared/string-util.c @@ -0,0 +1,94 @@ +// 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 +#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; +} + +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 886239f280..059a81aa16 100644 --- a/shared/string-util.h +++ b/shared/string-util.h @@ -12,11 +12,19 @@ #include #include #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 +40,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 @@ -238,3 +246,35 @@ 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); + +/* + * 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/suffix-util.c b/shared/suffix-util.c index 211735c179..ed85103cbc 100644 --- a/shared/suffix-util.c +++ b/shared/suffix-util.c @@ -15,12 +15,14 @@ #include #include #include +#include #include #include #include #include +#include "parse-util.h" #include "suffix-util.h" static struct si_suffix { @@ -50,101 +52,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) || shr_has_leading_zero(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 +199,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) || shr_has_leading_zero(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/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/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/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); 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; } diff --git a/shared/tests/test-parse-util.c b/shared/tests/test-parse-util.c index a954ac461c..b3f159f2ca 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) @@ -344,12 +345,177 @@ 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 */ #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); + /* 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); + 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("-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); + 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; +} + +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; +} + +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,10", 2 }, + { "0x10,010", -1 }, + }; + 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; @@ -366,6 +532,10 @@ int main(void) pass &= test_csv_u32(); pass &= test_csv_u64(); pass &= test_csv_u128(); + pass &= test_u64(); + pass &= test_s64(); + pass &= test_double(); + pass &= test_csv_strict(); fflush(stdout); exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); diff --git a/shared/tests/test-string-util.c b/shared/tests/test-string-util.c index 7c33a5dacc..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 @@ -256,6 +257,116 @@ 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; +} + +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; @@ -271,6 +382,10 @@ int main(void) pass &= test_sanitize_name(); 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); diff --git a/shared/tests/test-suffix-util.c b/shared/tests/test-suffix-util.c index 90d187e086..ef3a3887bc 100644 --- a/shared/tests/test-suffix-util.c +++ b/shared/tests/test-suffix-util.c @@ -79,6 +79,20 @@ 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 }, + { "010k", 0, -EINVAL }, + { "0.5k", 500ULL, 0 }, + { "", 0, -EINVAL }, }; bool pass = true; unsigned int i; @@ -110,6 +124,15 @@ 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 }, + { "010Ki", 0, -EINVAL }, + { "0", 0, 0 }, + { "1ki", 1024ULL, 0 }, + { "", 0, -EINVAL }, }; bool pass = true; unsigned int i; 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); diff --git a/src/argconfig.c b/src/argconfig.c index 5c651e4e34..65d643f681 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -1,45 +1,29 @@ // 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 #include -#include +#include +#include #include #include #include #include #include #include +#include #include +#include +#include #include #include @@ -53,145 +37,386 @@ 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. 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; + +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 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) +{ + 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) { append_usage_str = str; } -static void show_option(const struct argconfig_commandline_options *option) +/* "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; +} + +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) +{ + 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; + + print_label(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) +{ + __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 + snprintf(names, len, "\nNames:"); + for (n = v; n->str; n++) + p += snprintf(p, len - (p - names), " %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) +{ + 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'; + shr_snprintf_append(buffer, sizeof(buffer), &len, " [ "); if (option->option) { - b += sprintf(b, " --%s", option->option); - if (option->argument_type == optional_argument) - b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == required_argument) - b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg"); + shr_snprintf_append(buffer, sizeof(buffer), &len, " --%s", + option->option); + if (arg == ARGCONFIG_ARG_OPTIONAL) + shr_snprintf_append(buffer, sizeof(buffer), &len, + "[=<%s>]", meta); + if (arg == ARGCONFIG_ARG_REQUIRED) + shr_snprintf_append(buffer, sizeof(buffer), &len, + "=<%s>", meta); if (option->short_option) - b += sprintf(b, ","); + shr_snprintf_append(buffer, sizeof(buffer), &len, ","); } if (option->short_option) { - b += sprintf(b, " -%c", option->short_option); - if (option->argument_type == optional_argument) - b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg"); - if (option->argument_type == required_argument) - b += sprintf(b, " <%s>", option->meta ? option->meta : "arg"); + shr_snprintf_append(buffer, sizeof(buffer), &len, " -%c", + option->short_option); + if (arg == ARGCONFIG_ARG_OPTIONAL) + shr_snprintf_append(buffer, sizeof(buffer), &len, + " [<%s>]", meta); + if (arg == ARGCONFIG_ARG_REQUIRED) + shr_snprintf_append(buffer, sizeof(buffer), &len, + " <%s>", meta); } - b += sprintf(b, " ] "); + shr_snprintf_append(buffer, sizeof(buffer), &len, " ] "); - 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, len, out); + shr_print_word_wrapped(option->help, 44, 44, out); + show_value_names(out, option->opt_val); } - fprintf(stderr, "\n"); + fprintf(out, "\n"); } -void argconfig_print_help(const char *program_desc, - struct argconfig_commandline_options *s) +/* + * 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 *pending_header = "Options"; - bool header_printed = false; + const char *no_color = getenv("NO_COLOR"); - fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n", - append_usage_str); + if (!isatty(fileno(out)) || (no_color && *no_color)) + return ""; - shr_print_word_wrapped(program_desc, 0, 0, stderr); - fprintf(stderr, "\n"); + return seq; +} - if (!s || !s->option) - return; +/* "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)); +} - for (; s->option; s++) { +/* A heading, in bold on a terminal */ +void argconfig_print_heading(FILE *out, const char *fmt, const char *text) +{ + fputs(argconfig_esc(out, ARGCONFIG_BOLD), out); + fprintf(out, fmt, text); + fputs(argconfig_esc(out, ARGCONFIG_RESET), out); +} + +/* A group header is printed before the first option which follows it */ +static void print_options(FILE *out, struct argconfig_commandline_options *s, + const char *header) +{ + 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) { - 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); + fputc('\n', out); + argconfig_print_heading(out, "%s:", header); + fputc('\n', out); header_printed = true; } - if (!s->hidden) - show_option(s); + if (!s->deprecated) + show_option(out, s); } } -static int argconfig_error(char *type, const char *opt, const char *arg) +static void show_help(FILE *out, const char *program_desc, + struct argconfig_commandline_options *root) { - fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", type, opt, arg); - return -EINVAL; + argconfig_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, root, "Options"); +} + +static bool help_requested; + +/* True once argconfig_parse() printed the help -h or --help asked for */ +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) +/* @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); +} + +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) +{ + 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) { 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 */ + /* only a string option can do without a value */ + 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: - *(char **)value = optarg; + case CFG_FILE: + case CFG_DIR: + *(char **)value = arg; break; case CFG_INT: - *(int *)value = strtol(optarg, &endptr, 0); - if (errno || optarg == endptr) - ret = argconfig_error("integer", s->option, optarg); + ret = shr_parse_s64(arg, INT_MIN, INT_MAX, &i); + if (!ret) + *(int *)value = i; break; - case CFG_BYTE: { - unsigned long tmp = strtoul(optarg, &endptr, 0); - - if (errno || tmp >= 1 << 8 || optarg == endptr) - ret = argconfig_error("byte", s->option, optarg); - else - *(uint8_t *)value = tmp; + case CFG_BYTE: + ret = argconfig_parse_u64(s, arg, UINT8_MAX, &u); + if (!ret) + *(uint8_t *)value = u; break; - } - case CFG_SHORT: { - unsigned long tmp = strtoul(optarg, &endptr, 0); - - if (errno || tmp >= 1 << 16 || optarg == endptr) - ret = argconfig_error("short", s->option, optarg); - else - *(uint16_t *)value = tmp; + case CFG_SHORT: + ret = argconfig_parse_u64(s, arg, UINT16_MAX, &u); + if (!ret) + *(uint16_t *)value = u; break; - } - case CFG_POSITIVE: { - uint32_t tmp = strtoul(optarg, &endptr, 0); - - if (errno || optarg == endptr) - ret = argconfig_error("word", s->option, optarg); - else - *(uint32_t *)value = tmp; + case CFG_POSITIVE: + ret = argconfig_parse_u64(s, arg, UINT32_MAX, &u); + if (!ret) + *(uint32_t *)value = u; break; - } case CFG_INCREMENT: *(int *)value += 1; break; + case CFG_ENUM: + /* 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: - *(unsigned long *)value = strtoul(optarg, &endptr, 0); - if (errno || optarg == endptr) - ret = argconfig_error("long integer", s->option, optarg); + ret = argconfig_parse_u64(s, arg, UINT64_MAX, &u); + if (!ret) + *(uint64_t *)value = u; break; - case CFG_LONG_SUFFIX: - ret = shr_suffix_binary_parse(optarg, &endptr, (uint64_t *)value); - if (ret) - argconfig_error("long suffixed integer", s->option, optarg); + 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 && !argconfig_in_range(s, u)) + ret = -ERANGE; + if (!ret) + *(uint64_t *)value = u; break; + } case CFG_DOUBLE: - *(double *)value = strtod(optarg, &endptr); - if (errno || optarg == endptr) - ret = argconfig_error("float", s->option, optarg); + ret = shr_parse_double(arg, &d); + if (!ret) + *(double *)value = d; break; case CFG_FLAG: *(bool *)value = true; break; case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } @@ -211,10 +436,11 @@ 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: - *(unsigned long *)val = opt_val->long_val; + *(uint64_t *)val = opt_val->long_val; break; case CFG_DOUBLE: *(double *)val = opt_val->double_val; @@ -229,9 +455,13 @@ 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_ENUM_LIST: case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: break; } } @@ -242,6 +472,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; @@ -258,222 +492,632 @@ argconfig_match_val(struct argconfig_opt_val *v, const char *str) return match; } -static int argconfig_parse_val(struct argconfig_commandline_options *s) +/* + * 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, optarg); + v = argconfig_match_val(v, str); if (!v) - return argconfig_parse_type(s); + return false; argconfig_set_opt_val(v->type, &v->val, s->default_value); - return 0; + return true; } -static bool argconfig_check_verbose(struct argconfig_commandline_options *s) +static int argconfig_parse_val(struct argconfig_commandline_options *s, + char *arg) { - for (; s && s->option; s++) { - if (!strcmp(s->option, "verbose") && - s->config_type == CFG_INCREMENT) - return s->seen; - } + /* 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 false; + return argconfig_parse_type(s, arg); } -static argconfig_parse_hook_fn argconfig_parse_hook; +/* The non-option arguments of the last successful argconfig_parse() */ +static struct { + char **argv; + int argc; +} operands; -void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) +int argconfig_nr_operands(void) { - argconfig_parse_hook = hook; + return operands.argc; } -int argconfig_parse(int argc, char *argv[], const char *program_desc, - struct argconfig_commandline_options *options) +char *argconfig_operand(int i) { - __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; + if (i < 0 || i >= operands.argc) + return NULL; - if (argconfig_parse_hook) - return argconfig_parse_hook(argc, argv, program_desc, options); + return operands.argv[i]; +} - errno = 0; - for (s = options; s->option; s++) - options_count++; +/* + * 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; + /* 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 + * goes on. + */ + bool global; + bool help; + bool version; + char **operands; + int nr_operands; +}; - 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)); +/* + * Internal result of argconfig_parse_args(): an option was malformed. Like + * ARGCONFIG_HELP, it prints the help of a command. + */ +#define ARGCONFIG_USAGE 4 - if (!long_opts || !short_opts || !long_opt_map) { - fprintf(stderr, "failed to allocate memory for opts: %s\n", libnvme_strerror(errno)); - return -errno; - } +/* + * 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, ...) +{ + va_list ap; - for (s = options, opt_index = 0; s->option; s++, opt_index++) { - s->seen = false; - if (s->config_type == CFG_GROUP_SEPARATOR) + print_label(command_name()); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + return ARGCONFIG_USAGE; +} + +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->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++; } + 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; + if (s->old_name && strlen(s->old_name) == len && + !strncmp(s->old_name, name, len)) + return s; } - 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 NULL; +} - s = &options[opt_index]; - s->seen = true; +static struct argconfig_commandline_options * +argconfig_find_short(struct argconfig_commandline_options *s, char c) +{ + struct argconfig_commandline_options *o; - if (!s->default_value) + 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) + return s; + } - ret = argconfig_parse_val(s); - if (ret) - break; + return NULL; +} + +/* + * 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 *root, + const char *name, size_t len) +{ + struct argconfig_commandline_options *o; + + o = argconfig_find_long(&root[1], name, len); + + return o ? o : argconfig_find_long(root, name, len); +} + +static struct argconfig_commandline_options * +argconfig_find_short_any(struct argconfig_commandline_options *root, char c) +{ + struct argconfig_commandline_options *o; + + o = argconfig_find_short(&root[1], c); + + return o ? o : argconfig_find_short(root, 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; } - if (!argconfig_check_verbose(options)) - setlocale(LC_ALL, "C"); + 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) +{ + __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; + + print_label(command_name()); + fprintf(stderr, "invalid value '%s' for ", e ? e : ""); + if (c) + fprintf(stderr, "-%c", c); + else + fprintf(stderr, "--%s", s->option); + fprintf(stderr, ": expected "); + 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 " : ""); + + /* 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, "%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 ? ", " : + 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; +} + +/* @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) +{ + int ret; + + if (s->deprecated && !s->seen) { + argconfig_print_warning(); + if (c) + fprintf(stderr, " '-%c'", c); + else + fprintf(stderr, " '--%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) + return 0; + + ret = argconfig_parse_val(s, arg); + if (ret == -EINVAL || ret == -ERANGE) + return argconfig_value_error(s, c, arg); return ret; } /* - * 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). - * - * Returns 0 on success, negative errno on failure. + * 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. */ -int argconfig_parse_global(int argc, char *argv[], - struct argconfig_commandline_options *options) +struct argconfig_commandline_options * +argconfig_bind(const struct argconfig_commandline_options *tmpl, + const void *tmpl_base, size_t size, void *base) { - __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; + 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) +{ + for (; v && v->str; v++) { + if (v->type == CFG_ENUM && v->val.int_val == val) + return v->str; + } - errno = 0; - for (s = options; s->option; s++) - options_count++; + return NULL; +} - 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)); +/* 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; - if (!long_opts || !short_opts || !long_opt_map) { - fprintf(stderr, "failed to allocate memory for opts: %s\n", - libnvme_strerror(errno)); - return -errno; + return p->argv[++p->ind]; +} + +/* "--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_any(p->root, name, len); + if (!s) { + bool help = len == 4 && !strncmp(name, "help", 4); + bool version = len == 7 && !strncmp(name, "version", 7); + + if (help && !p->global) + return ARGCONFIG_HELP; + if (!p->global || (!help && !version)) { + __cleanup_free char *e = shr_escape(arg); + + return argconfig_parse_error(p, + "unrecognized option '%s'", e ? e : ""); + } + 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; } - short_opts[short_index++] = '+'; + switch (s->argument_type) { + case ARGCONFIG_ARG_NONE: + if (eq) + return argconfig_parse_error(p, + "option '--%s' doesn't allow an argument", + s->option); + break; + 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 ARGCONFIG_ARG_OPTIONAL: + /* Only an attached value, "--name value" is an operand */ + if (eq) + value = eq + 1; + break; + } - for (s = options, opt_index = 0; s->option; s++, opt_index++) { - s->seen = false; - if (s->config_type == CFG_GROUP_SEPARATOR) + if (s->old_name && !s->seen && + (strlen(s->option) != len || strncmp(s->option, name, len))) { + argconfig_print_warning(); + fprintf(stderr, + " '--%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); +} + +/* "-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_any(p->root, *c); + if (!s && p->global && (*c == 'h' || *c == 'V')) { + p->help |= *c == 'h'; + p->version |= *c == 'V'; 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++; + if (!s) { + char bad[2] = { *c, '\0' }; + __cleanup_free char *e = shr_escape(bad); + + return argconfig_parse_error(p, + "invalid option -- '%s'", e ? e : ""); } - } - 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]; + value = NULL; + switch (s->argument_type) { + case ARGCONFIG_ARG_NONE: + break; + 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 ARGCONFIG_ARG_OPTIONAL: + if (c[1]) + value = c + 1; + break; } - s = &options[opt_index]; - s->seen = true; + ret = argconfig_apply(s, *c, value); + if (ret || s->argument_type != ARGCONFIG_ARG_NONE) + return ret; + } + + return 0; +} - if (!s->default_value) +/* + * 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. + */ +/* 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) +{ + char *arg; + int ret; + + /* + * 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++) { + arg = p->argv[p->ind]; + + if (!strcmp(arg, "--")) { + p->ind++; + if (p->global) + return 0; + while (p->ind < p->argc && p->argv[p->ind]) + p->operands[p->nr_operands++] = + p->argv[p->ind++]; + return 0; + } + + if (arg[0] != '-' || !arg[1]) { + if (p->global) + return 0; + p->operands[p->nr_operands++] = arg; continue; + } - ret = argconfig_parse_val(s); + if (arg[1] == '-') + ret = argconfig_parse_long(p, arg); + else + ret = argconfig_parse_short(p, arg); if (ret) - break; + return ret; } - return ret; + return 0; +} + +static argconfig_parse_hook_fn argconfig_parse_hook; + +void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) +{ + argconfig_parse_hook = hook; } -bool argconfig_parse_seen(struct argconfig_commandline_options *s, - const char *option) +static bool given_in(struct argconfig_commandline_options *s, + const void *value) { + bool given = false; + for (; s && s->option; s++) { - if (!strcmp(s->option, option)) - return s->seen; + 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 *root) +{ + struct argconfig_commandline_options *v; + + v = argconfig_find_long_any(root, "verbose", strlen("verbose")); + if (!v || v->config_type != CFG_INCREMENT) + return false; + + return given_in(root, v->default_value); +} + +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, + }; + int ret; + + argconfig_root(p.root, options); + parse_argv0 = argc > 0 ? argv[0] : ""; + free(operand_buf); + operand_buf = NULL; + operands.argv = NULL; + operands.argc = 0; + + if (argconfig_parse_hook) + return argconfig_parse_hook(argc, argv, program_desc, p.root); + + p.operands = calloc(argc > 0 ? argc : 1, sizeof(*p.operands)); + if (!p.operands) + return -ENOMEM; + + ret = argconfig_parse_args(&p); + if (ret) { + /* + * 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, p.root); + help_requested = true; + } else if (ret > 0) { + argconfig_print_usage(); + ret = -EINVAL; + } + free(p.operands); + } else { + operand_buf = p.operands; + operands.argv = p.operands; + operands.argc = p.nr_operands; } - return false; + if (!argconfig_check_verbose(p.root)) + setlocale(LC_ALL, "C"); + + return ret; +} + +/* + * 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, *argind is the index of the first non-option argument (the + * subcommand), or argc if there is none. + * + * 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, + .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; + if (ret) + return ret > 0 ? -EINVAL : ret; + + return (p.help ? ARGCONFIG_HELP : 0) | + (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 root[ARGCONFIG_ROOT_LEN]; + + if (!value) + return false; + argconfig_root(root, s); + + return given_in(root, value); } diff --git a/src/argconfig.h b/src/argconfig.h index 11ab23ff4d..e33da1cb99 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -1,38 +1,14 @@ /* 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 -#include #include #include #include @@ -50,46 +26,107 @@ enum argconfig_types { CFG_SHORT, CFG_POSITIVE, CFG_INCREMENT, + CFG_ENUM, + CFG_ENUM_LIST, + /* strings which name a file or a directory */ + CFG_FILE, + CFG_DIR, CFG_GROUP_SEPARATOR, + CFG_INCLUDE, }; #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 or + * signedness. Every option needs a variable, which argconfig_given() + * takes. + */ +#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), \ + default : \ + _Generic(v, unsigned long long * : (v), \ + default : \ + (void)0)) +#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)) + #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, 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, v, no_argument, d, false, __VA_ARGS__} + {l, s, NULL, CFG_FLAG, OPT_CHECK_BOOL(v), \ + ARGCONFIG_ARG_NONE, 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), \ + ARGCONFIG_ARG_REQUIRED, 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 *), \ + ARGCONFIG_ARG_REQUIRED, 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 *), \ + 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__} + +/* + * 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, v, required_argument, d, false, __VA_ARGS__} + {l, s, "NUM", CFG_LONG, OPT_CHECK_64(v), \ + ARGCONFIG_ARG_REQUIRED, 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), \ + ARGCONFIG_ARG_REQUIRED, 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 *), \ + ARGCONFIG_ARG_REQUIRED, 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 *), \ + ARGCONFIG_ARG_REQUIRED, 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 *), \ + ARGCONFIG_ARG_NONE, 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), \ + ARGCONFIG_ARG_REQUIRED, d, false, __VA_ARGS__} /* * Like OPT_STRING(), but the value is optional. getopt_long() only @@ -99,17 +136,30 @@ 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), \ + 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__) #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) \ @@ -124,11 +174,14 @@ 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} +#define VAL_ENUM(s, v) \ + {s, CFG_ENUM, .val.int_val = v} + #define VAL_LONG(s, v) \ {s, CFG_LONG, .val.long_val = v} @@ -156,7 +209,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; @@ -168,31 +221,85 @@ 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; - bool hidden; + /* + * 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; + /* + * 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); -void argconfig_print_help(const char *program_desc, - struct argconfig_commandline_options *options); +#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_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); +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); +/* 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); + 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); +/* + * 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_parse_seen(struct argconfig_commandline_options *options, - const char *option); +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, + 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/src/args.c b/src/args.c index 96d1109b24..3ca2626095 100644 --- a/src/args.c +++ b/src/args.c @@ -11,9 +11,13 @@ #include +#include + #include "argconfig.h" #include "args.h" +#include "cleanup.h" #include "nvme-print.h" +#include "plugin.h" struct nvme_args nvme_args = { .output_format = "normal", @@ -25,15 +29,60 @@ 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(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) { + e = shr_escape(argconfig_operand(max)); + return argconfig_usage_error("unexpected argument '%s'", + e ? e : ""); + } + + nvme_args.device = argconfig_operand(0); + return 0; +} + +int parse_cmdline(int argc, char *argv[], const char *desc, + struct argconfig_commandline_options *opts) +{ + int ret; + + ret = argconfig_parse(argc, argv, desc, opts); + if (ret) + return ret; + + return check_device(); +} + int parse_args(int argc, char *argv[], const char *desc, struct argconfig_commandline_options *opts) { int ret; - ret = argconfig_parse(argc, argv, desc, opts); + ret = parse_cmdline(argc, argv, desc, opts); 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 544246af43..febf242431 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; @@ -25,7 +25,13 @@ 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; + /* 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; @@ -38,6 +44,17 @@ 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) +/* + * 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) #define DEFAULT_OUTPUT_FORMAT_DESC "Output format: normal|json|binary" @@ -47,46 +64,30 @@ 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 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_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_FLAG("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(n, ...) \ - NVME_ARGS_OUTPUT_FORMATS(n, \ - DEFAULT_OUTPUT_FORMATS, \ - DEFAULT_OUTPUT_FORMAT_DESC, \ - ##__VA_ARGS__) - +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; +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/config-convert.c b/src/config-convert.c index 18d9af7032..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", 'o', "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 4951d8fe63..891540dc59 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,22 +195,28 @@ 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_FILE("output", 0, &output_file, desc_output), + OPT_END() + }; - nvmf_default_args(&fa); + if (!nvmf) + return -ENOMEM; err = parse_args(argc, argv, desc, opts); 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 62748280c2..e6cb5e59e5 100644 --- a/src/fabrics.c +++ b/src/fabrics.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -54,6 +53,7 @@ #include #include #include +#include #include "cleanup.h" #include "config-convert.h" @@ -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) { - fa->tos = -1; - fa->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; + return (struct nvmf_args) { + .tos = -1, + .ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO, + }; +} + +struct argconfig_commandline_options *nvmf_options(struct nvmf_args *fa) +{ + return argconfig_bind(nvmf_template, &nvmf_tmpl, sizeof(nvmf_tmpl), + fa); } void nvmf_args_to_params(struct libnvmf_params *params, @@ -149,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) { @@ -542,7 +621,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: @@ -554,10 +633,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"; @@ -565,9 +643,10 @@ 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()). + * 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,29 +655,30 @@ const char *nvmf_resolve_persistent_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"), - OPT_FLAG("force", 0, &no_reuse, - "deprecated, see --no-reuse")); + 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 @@ -606,7 +686,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; @@ -618,7 +699,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, @@ -815,9 +898,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 @@ -861,7 +945,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; @@ -870,25 +956,29 @@ 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"), - OPT_FLAG("force", 0, &no_reuse, - "deprecated, see --no-reuse"), - 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_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"), + 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_DIR("nbft-path", 0, &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) @@ -896,13 +986,10 @@ 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); + persistent = nvmf_resolve_persistent_arg( + argconfig_given(opts, &persistent_arg), persistent_arg); if (!strcmp(config_file, "none")) config_file = NULL; @@ -994,6 +1081,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; @@ -1070,19 +1162,26 @@ 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_FILE("config", 'J', &config_file, nvmf_config_file), + OPT_STRING("owner", 0, "NAME", &owner, + "record this owner in the registry"), + 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, + "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) @@ -1090,11 +1189,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; @@ -1196,6 +1291,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 @@ -1255,7 +1352,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) { @@ -1345,7 +1442,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; @@ -1420,7 +1517,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; @@ -1438,7 +1535,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 +1548,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) @@ -1544,10 +1647,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) @@ -1625,7 +1731,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() }; @@ -1659,19 +1765,16 @@ int fabrics_config_show(const char *desc, int argc, char **argv) nvme_print_flags_t flags; int ret; - NVME_ARGS(opts, - OPT_STRING("config", 'J', "FILE", &config_file, - nvmf_config_file_ro)); + OPT_ARGS(opts) = { + OPT_FILE("config", 'J', &config_file, nvmf_config_file_ro), + OPT_END() + }; ret = parse_args(argc, argv, desc, opts); 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) @@ -1721,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; @@ -1729,10 +1832,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) @@ -1777,15 +1885,18 @@ 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) { 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; + } } } } @@ -1794,7 +1905,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); @@ -1805,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; } diff --git a/src/fabrics.h b/src/fabrics.h index 763c44a564..ada1d570c2 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; @@ -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,58 +70,26 @@ 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, ...) \ - NVME_ARGS(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), \ - 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("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),\ - OPT_INT("fast_io_fail_tmo", 'F', &f.fast_io_fail_tmo, DESC_NVMF_FAST_IO_FAIL_TMO, NULL, true), \ - 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__ \ - ) +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 * 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); @@ -131,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; 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/src/global-config.c b/src/global-config.c index 4cc08bda24..5ade7707cf 100644 --- a/src/global-config.c +++ b/src/global-config.c @@ -7,109 +7,153 @@ */ /* - * /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 +#include #include #include #include +#include #include #include +#include -#include "args.h" +#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) +/* 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) { - unsigned long v; - char *end; + char **var = s->default_value; + unsigned int i; + char *dup; - if (!value || !*value || *value == '-') - return -EINVAL; + if (nr_owned == ARRAY_SIZE(owned)) + return -ENOMEM; - errno = 0; - v = strtoul(value, &end, 0); - if (errno || *end || v > (unsigned long)~0U) - return -EINVAL; + 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; - *out = (unsigned int)v; 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) +{ + return shr_parse_u64(value, max, out) ? -EINVAL : 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->deprecated) + 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) { - unsigned int uval; + struct argconfig_commandline_options *s = find_key(key); + __cleanup_free char *k = shr_escape(key); + uint64_t uval; bool bval; - char *dup; int err; - if (!strcmp(key, "verbose")) { - err = parse_uint(value, &uval); - if (err) - goto bad_value; - nvme_args.verbose = uval; - } else if (!strcmp(key, "quiet")) { - 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")) { - err = parse_uint(value, &uval); - if (err) - goto bad_value; - nvme_args.timeout = uval; - } else if (!strcmp(key, "dry-run")) { + if (!s || !s->default_value) { + fprintf(stderr, "%s:%u: unknown key \"%s\", ignoring\n", + gc->path, line, k ? k : ""); + 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); if (err) goto bad_value; - nvme_args.dry_run = bval; - } else if (!strcmp(key, "no-retries")) { - err = shr_parse_bool(value, &bval); + *(bool *)s->default_value = bval; + break; + case CFG_INCREMENT: + /* the number of times the option is given */ + err = parse_uint(value, INT_MAX, &uval); if (err) goto bad_value; - nvme_args.no_retries = bval; - } else if (!strcmp(key, "no-ioctl-probing")) { - err = shr_parse_bool(value, &bval); + *(int *)s->default_value = uval; + 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; - nvme_args.no_ioctl_probing = bval; - } else if (!strcmp(key, "output-format-version")) { - err = parse_uint(value, &uval); + *(uint32_t *)s->default_value = uval; + break; + case CFG_STRING: + case CFG_FILE: + case CFG_DIR: + err = set_string(s, value); if (err) - goto bad_value; - nvme_args.output_format_ver = uval; - } else if (!strcmp(key, "set-options")) { - dup = strdup(value); - if (!dup) - return -ENOMEM; - nvme_args.set_options = dup; - } else { - fprintf(stderr, "%s:%u: unknown key \"%s\", ignoring\n", - gc->path, line, key); + return err; + break; + default: + fprintf(stderr, "%s:%u: key \"%s\" can't be set, ignoring\n", + gc->path, line, k ? k : ""); + break; } return 0; 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; @@ -120,6 +164,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: @@ -127,13 +172,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; } @@ -151,6 +198,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..a06cf21e64 100644 --- a/src/global-config.h +++ b/src/global-config.h @@ -19,3 +19,12 @@ 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); + +/* + * 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/src/global-ctx.c b/src/global-ctx.c index 5f3db97333..3734dcdded 100644 --- a/src/global-ctx.c +++ b/src/global-ctx.c @@ -7,7 +7,6 @@ */ #include #include -#include #include #include #include @@ -27,7 +26,7 @@ static int check_arg_dev(int argc, char **argv) { - if (optind >= argc) { + if (!nvme_args.device) { errno = EINVAL; nvme_show_perror(argv[0]); return -EINVAL; @@ -46,7 +45,7 @@ static int get_transport_handle(struct libnvme_global_ctx *ctx, int argc, if (ret) return ret; - devname = argv[optind]; + devname = nvme_args.device; ret = libnvme_open(ctx, devname, flags, hdl); if (ret) @@ -179,6 +178,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; @@ -188,7 +188,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); @@ -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/global-options.c b/src/global-options.c new file mode 100644 index 0000000000..11bcfb53db --- /dev/null +++ b/src/global-options.c @@ -0,0 +1,94 @@ +// 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 + +#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() +}; + +/* 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), + VAL_UINT("-1", UINT32_MAX), + VAL_END() +}; + +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", 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, + "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", .min = 1, .max = 2), + /* the spelling of --verbose of older versions */ + 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() +}; + +/* 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/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/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', 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-ctrl.c b/src/nvme-cmds-ctrl.c index 7c3466581e..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) @@ -162,9 +168,10 @@ 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); + OPT_ARGS(opts) = { + OPT_END() + }; err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); if (err) @@ -175,12 +182,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-deprecated.c b/src/nvme-cmds-deprecated.c index d3734b5691..182c297494 100644 --- a/src/nvme-cmds-deprecated.c +++ b/src/nvme-cmds-deprecated.c @@ -7,91 +7,24 @@ * 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. @@ -128,36 +61,39 @@ 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 = argconfig_parse(argc, argv, desc, opts); + err = parse_cmdline(argc, argv, desc, opts); 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; } - return forward_to_keys_plugin("gen-dhchap-key", "gen-kxchap-secret", nargs, args); + return run_replacement_command(plugin, acmd, acmd->replacement.command, + nargs, args); } /* @@ -183,29 +119,22 @@ 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 = argconfig_parse(argc, argv, desc, opts); + err = parse_cmdline(argc, argv, desc, opts); if (err) return err; - if (argconfig_parse_seen(opts, "key")) { + if (argconfig_given(opts, &cfg.key)) { args[nargs++] = "--keydata"; 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); } /* @@ -217,1429 +146,302 @@ static int check_tls_key(int argc, char **argv, struct command *acmd, struct plu */ 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; - struct plugin *keys; - int nsub = 0, c; + int nargs = 1, err; - 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; - } + struct config { + char *keyring; + char *keytype; + char *keyfile; + bool import; + bool export; + char *revoke; + }; + + struct config cfg = { 0 }; + + OPT_ARGS(opts) = { + 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() + }; + + 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; } - 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)); - if (!sub_argv) - return -ENOMEM; - - sub_argv[nsub++] = (char *)keys->name; - sub_argv[nsub++] = (char *)subcmd; - - 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; } - 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, nargs, args); } #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, - .no_device = true, -}; - -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, - .no_device = true, -}; - -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, - .no_device = true, -}; - -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, - .no_device = true, -}; - -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, - .no_device = true, -}; - -#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/nvme-cmds-discovery.c b/src/nvme-cmds-discovery.c index 54c5e552d6..514dbfa2fd 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; @@ -136,32 +135,29 @@ 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), - 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) 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 76594a0175..1557b46b84 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 @@ -32,6 +31,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) { @@ -43,21 +48,19 @@ 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) return err; devname = NULL; - if (optind < argc) - devname = basename(argv[optind++]); + if (nvme_args.device) + devname = basename(nvme_args.device); - 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; @@ -94,63 +97,47 @@ 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; -#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; + int ranking; }; struct config cfg = { - .ranking = "namespace", + .ranking = NVME_CLI_TOPO_NAMESPACE, }; - NVME_ARGS_OUTPUT_FORMATS(opts, supported_formats, supported_formats_desc, - OPT_FMT("ranking", 'r', &cfg.ranking, ranking)); + OPT_ARGS(opts) = { + OPT_ENUM("ranking", 'r', &cfg.ranking, ranking, rankings), + OPT_END() + }; err = parse_args(argc, argv, desc, opts); 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; - 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; - if (optind < argc) - devname = basename(argv[optind++]); + if (nvme_args.device) + devname = basename(nvme_args.device); if (devname) { int subsys_id, nsid; @@ -170,7 +157,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; } @@ -192,18 +179,16 @@ 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) 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"); @@ -229,17 +214,15 @@ 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) 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; @@ -261,14 +244,16 @@ static struct command list_cmd = { .name = "list", .help = "List all NVMe devices and namespaces on machine", .fn = list, - .no_device = true, + .output_formats = NORMAL | JSON, + .device = COMMAND_NO_DEVICE, }; static struct command list_subsys_cmd = { .name = "list-subsys", .help = "List nvme subsystems", .fn = list_subsys, - .no_device = true, + .output_formats = NORMAL | JSON, + .device = COMMAND_DEVICE_OPTIONAL, }; #ifdef CONFIG_TOP @@ -277,7 +262,8 @@ static struct command top_cmd = { .name = "top", .help = "nvme top", .fn = top, - .no_device = true, + .output_formats = OUTPUT_FORMATS_NORMAL, + .device = COMMAND_NO_DEVICE, }; #endif /* CONFIG_TOP */ @@ -286,7 +272,8 @@ static struct command show_topology_cmd_cmd = { .name = "show-topology", .help = "Show the topology", .fn = show_topology_cmd, - .no_device = true, + .output_formats = TOPOLOGY_OUTPUT_FORMATS, + .device = COMMAND_DEVICE_OPTIONAL, }; static struct command *commands[] = { diff --git a/src/nvme-cmds-fabrics.c b/src/nvme-cmds-fabrics.c index edf4f7629d..1c9832dd73 100644 --- a/src/nvme-cmds-fabrics.c +++ b/src/nvme-cmds-fabrics.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -58,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) @@ -98,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) @@ -170,56 +173,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-feature.c b/src/nvme-cmds-feature.c index 1bddf1bdf2..590ad716a5 100644 --- a/src/nvme-cmds-feature.c +++ b/src/nvme-cmds-feature.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -261,27 +260,29 @@ 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), - 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, + 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), + 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) 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")) { + if (!argconfig_given(opts, &cfg.namespace_id)) { err = libnvme_get_nsid(hdl, &cfg.namespace_id); if (err < 0) { if (errno != ENOTTY) { @@ -336,7 +337,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; @@ -359,27 +359,24 @@ 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), - 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) 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")) { + if (!argconfig_given(opts, &cfg.nsid)) { err = libnvme_get_nsid(hdl, &cfg.nsid); if (err < 0) { if (errno != ENOTTY) { @@ -419,7 +416,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 d2843300cf..91af3e5800 100644 --- a/src/nvme-cmds-io.c +++ b/src/nvme-cmds-io.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -127,12 +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), - 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,22 +481,27 @@ static int write_zeroes(int argc, char **argv, .nsz = false, }; - NVME_ARGS(opts, - OPT_UINT("namespace-id", 'n', &cfg.nsid, namespace_desired), - 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) @@ -577,7 +584,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; @@ -603,15 +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), - 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) @@ -621,17 +630,11 @@ 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)); 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; } @@ -768,28 +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), - 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) @@ -906,8 +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)); + 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) @@ -1026,29 +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), - 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); @@ -1061,13 +1078,12 @@ 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(nvme_args.device)); 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; } @@ -1164,7 +1180,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 { @@ -1349,18 +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), - 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) @@ -1445,23 +1466,22 @@ 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), - 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) 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"); @@ -1509,7 +1529,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; @@ -1527,23 +1546,19 @@ 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) 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..46c68646b7 100644 --- a/src/nvme-cmds-passthru.c +++ b/src/nvme-cmds-passthru.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -113,7 +112,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, @@ -140,41 +138,38 @@ 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), - 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) 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")) { + 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 b0b597ca66..ac07295e60 100644 --- a/src/nvme-cmds-registers.c +++ b/src/nvme-cmds-registers.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -151,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) @@ -162,11 +163,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; @@ -256,11 +253,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; @@ -271,7 +271,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; @@ -337,17 +338,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) @@ -391,39 +397,41 @@ 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, - 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) @@ -434,24 +442,20 @@ 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; 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; } @@ -508,63 +512,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: @@ -580,12 +586,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; @@ -604,98 +610,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) @@ -716,28 +722,30 @@ 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, - 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) @@ -748,7 +756,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) @@ -771,24 +779,22 @@ 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, }; - 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) 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) { + if (!argconfig_given(opts, &cfg.offset)) { nvme_show_error("offset required param"); return -EINVAL; } @@ -813,32 +819,27 @@ 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, - .value = -1, + .offset = 0, + .value = 0, }; - NVME_ARGS(opts, - OPT_UINT("offset", 'O', &cfg.offset, offset), - OPT_UINT("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) 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) { + 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; } diff --git a/src/nvme-cmds-sanitize.c b/src/nvme-cmds-sanitize.c index 79f29a080a..4b4a029c2a 100644 --- a/src/nvme-cmds-sanitize.c +++ b/src/nvme-cmds-sanitize.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -182,7 +181,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 { @@ -199,22 +197,19 @@ 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), - 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) 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 +443,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; @@ -488,29 +482,25 @@ 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) 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 +609,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 { @@ -647,23 +636,19 @@ 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) 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: @@ -771,17 +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), - 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; @@ -793,21 +781,16 @@ 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(nvme_args.device)); 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; } - 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-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-print-binary.c b/src/nvme-print-binary.c index 847a6b7d23..18298ef176 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) @@ -314,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) @@ -330,19 +332,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 6c75a1df7c..f46aef9e49 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); @@ -2031,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); @@ -2136,10 +2135,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); @@ -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(); @@ -2530,28 +2534,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); } @@ -2781,6 +2804,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]; @@ -2815,7 +2840,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(); @@ -2856,6 +2881,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]; @@ -3674,7 +3701,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])); } @@ -4186,6 +4213,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; @@ -4198,12 +4226,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", @@ -4211,13 +4243,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]; } } @@ -4284,7 +4316,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); @@ -4781,11 +4814,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); @@ -5445,32 +5479,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: @@ -5491,7 +5534,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"); @@ -5807,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; @@ -5900,17 +5944,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]); @@ -5918,60 +5964,85 @@ 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]; - 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)); - 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)); + nsids = json_create_array(); + nsid = nvme_reachability_ids(log, off, sizeof(desc)); + for (j = 0; j < le32_to_cpu(desc.nnid); 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); } } 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]; - 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)); - 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); + rgids = json_create_array(); + rgid = nvme_reachability_ids(log, off, sizeof(desc)); + for (j = 0; j < le32_to_cpu(desc.nrid); 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); } } #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; + __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; @@ -5987,17 +6058,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: @@ -6018,8 +6093,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)); @@ -6027,7 +6103,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); @@ -6055,12 +6131,14 @@ 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; + __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; @@ -6075,15 +6153,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(); @@ -6099,22 +6182,30 @@ 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); - __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); 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; @@ -6158,6 +6249,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-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(); diff --git a/src/nvme-print-stdout-log.c b/src/nvme-print-stdout-log.c index 9060d4b2b2..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; @@ -1239,17 +1236,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 +1259,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 +1268,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"); } @@ -1409,7 +1417,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(); @@ -1470,7 +1480,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; @@ -1695,7 +1705,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); @@ -2701,12 +2711,13 @@ 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; + __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; @@ -2725,9 +2736,9 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log) 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); @@ -2738,12 +2749,14 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log) 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", @@ -2776,8 +2789,9 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log) 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(); @@ -2794,7 +2808,7 @@ void stdout_host_discovery_log(struct nvme_host_discovery_log *log) 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); } } } @@ -2821,9 +2835,9 @@ 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; + __u64 i; __u8 j; struct nvme_ave_discovery_log_entry *adlpe; struct nvme_ave_tr_record *atr; @@ -2844,9 +2858,9 @@ void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log) 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; @@ -2855,11 +2869,15 @@ void stdout_ave_discovery_log(struct nvme_ave_discovery_log *log) 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); @@ -2947,11 +2965,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) @@ -2960,7 +2978,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, @@ -2975,7 +2994,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; @@ -2988,18 +3010,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"); @@ -3008,7 +3031,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; @@ -3021,29 +3047,30 @@ 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"); } -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 = - 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(); @@ -3167,6 +3194,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/src/nvme-print-stdout.c b/src/nvme-print-stdout.c index 885f06df27..15455b1684 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" @@ -287,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++) { @@ -1397,7 +1400,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 +1441,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 +1449,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) @@ -2123,6 +2135,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 +2216,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"); @@ -2522,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; @@ -2536,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); @@ -2550,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]; } } @@ -2603,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: @@ -3093,7 +3107,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]; @@ -4192,6 +4207,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"); @@ -4200,11 +4217,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/src/nvme-print-stdout.h b/src/nvme-print-stdout.h index 9ede1fe921..05a42d02bf 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); @@ -91,17 +92,18 @@ 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( 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 5df0f0c008..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) { @@ -419,9 +448,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, @@ -1968,9 +1998,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, @@ -1986,21 +2016,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, @@ -2060,7 +2090,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); @@ -2080,7 +2110,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); @@ -2116,13 +2146,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 6351c95dc8..8bb80db719 100644 --- a/src/nvme-print.h +++ b/src/nvme-print.h @@ -89,6 +89,166 @@ 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; +} + +/* + * 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; +} + +/* + * 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 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); +} + +/* + * 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) { @@ -187,7 +347,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); @@ -225,13 +385,17 @@ 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); - 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); @@ -418,12 +582,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); @@ -573,20 +814,22 @@ 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, __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); +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, diff --git a/src/nvme.c b/src/nvme.c index a62689b581..60b4f0ef96 100644 --- a/src/nvme.c +++ b/src/nvme.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -96,19 +96,18 @@ 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) 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; @@ -144,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; @@ -162,7 +163,7 @@ static void free_plugins(void) } } -int main(int argc, char **argv) +int nvme_main(int argc, char **argv) { int err; @@ -176,12 +177,12 @@ int main(int argc, char **argv) #endif nvme.extensions->parent = &nvme; - atexit(free_plugins); if (argc < 2) { general_help(&builtin, NULL); return 0; } setlocale(LC_ALL, ""); + nvme_args_register(); err = nvme_load_global_config(); if (err) @@ -192,10 +193,12 @@ 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(); + /* the command stopped after printing the help it was asked for */ + if (argconfig_help_requested()) + return 0; + return err ? 1 : 0; } diff --git a/src/plugin.c b/src/plugin.c index ca1a14f778..57b42d999c 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -7,6 +7,7 @@ #include +#include #include #include "argconfig.h" @@ -31,37 +32,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; } @@ -137,13 +178,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, @@ -188,6 +234,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; @@ -199,7 +253,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; } @@ -256,7 +310,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); @@ -279,7 +333,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, @@ -379,142 +433,220 @@ void general_help(struct plugin *plugin, char *str) } } -int handle_plugin(int argc, char **argv, struct plugin *plugin) +/* Where the commands of @plugin are listed, after an error */ +static void commands_hint(struct plugin *plugin) { - char use[0x100]; - 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; - char *str; + 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) +{ + nvme_args_set_output_formats(cmd->output_formats ? + cmd->output_formats : + DEFAULT_OUTPUT_FORMATS, + cmd->output_format_desc); + nvme_args.device_operand = cmd->device; +} + +static int run_command(int argc, char **argv, struct plugin *plugin, + struct command *cmd) +{ + char use[0x100], path[0x80]; int err; - if (!argc) { - general_help(plugin, NULL); - return 0; + 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); + + err = cmd->fn(argc, argv, cmd, plugin); + + /* use and path go out of scope */ + argconfig_append_usage(""); + argconfig_set_command(NULL, NULL); + + return err; +} + +/* + * "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) { + __cleanup_free char *e = shr_escape(argv[0]); - /* - * look for global options before the sub command parser and - * pre-fill the global nvme_args variable. - */ - 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); - if (err) { - general_help(plugin, NULL); - return err; + fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", + e ? e : ""); + commands_hint(plugin); + return -ENOTTY; } - argc -= optind; - argv += optind; + sub_argv = malloc((argc + 1) * sizeof(*sub_argv)); + name = strdup(extension->name); + if (!sub_argv || !name) + return -ENOMEM; - if (opt_help) { - __cleanup_free char **help_argv = NULL; - __cleanup_free char *help_name = NULL; + argv[0] += strlen(extension->name); + while (*argv[0] == '-') + argv[0]++; - if (argc <= 0) { - general_help(plugin, NULL); - return 0; - } + sub_argv[0] = name; + memcpy(&sub_argv[1], argv, argc * sizeof(*argv)); - help_argv = malloc((argc + 1) * sizeof(*help_argv)); - if (!help_argv) - return -ENOMEM; + return handle_plugin(argc + 1, sub_argv, extension); +} - help_name = strdup("help"); - if (!help_name) - return -ENOMEM; +/* + * 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; - help_argv[0] = help_name; - memcpy(&help_argv[1], argv, argc * sizeof(*argv)); - return help(argc + 1, help_argv, plugin); + 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; } - if (opt_version) - return version_cmd(plugin); + argconfig_print_warning(); + fprintf(stderr, + " '%s' is deprecated and will be removed in the next major version, use 'nvme %s %s' instead\n", + cmd->name, name, command); - if (!argc) { + /* + * 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) +{ + __cleanup_free char **help_argv = NULL; + __cleanup_free char *help_name = NULL; + + if (argc <= 0) { general_help(plugin, NULL); return 0; } - str = argv[0]; - - if (!strcmp(str, "help")) - return help(argc, argv, plugin); - if (!strcmp(str, "version")) - return version_cmd(plugin); + help_argv = malloc((argc + 1) * sizeof(*help_argv)); + help_name = strdup("help"); + if (!help_argv || !help_name) + return -ENOMEM; - 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); - 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++; - } + help_argv[0] = help_name; + memcpy(&help_argv[1], argv, argc * sizeof(*argv)); - if (cr && cr_valid) { - build_command_usage(use, sizeof(use), prog, plugin, cr); - argconfig_append_usage(use); - return cr->fn(argc, argv, cr, plugin); - } + return help(argc + 1, help_argv, plugin); +} - /* Check extensions only if this is running the built-in plugin */ - if (plugin->name) { - fprintf(stderr, "ERROR: Invalid sub-command '%s' for plugin %s\n", str, plugin->name); - return -ENOTTY; - } +/* + * 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; - extension = plugin->next; - while (extension) { - if (!strcmp(str, extension->name)) - return handle_plugin(argc, argv, extension); - extension = extension->next; + if (!argc) { + general_help(plugin, NULL); + return 0; } /* - * If the command is executed with the extension name and - * command together ("plugin-command"), run the plug in + * -h and --help show the help of the command which follows, if any, + * -V and --version replace the command. */ - 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; + err = argconfig_parse_global(argc, argv, NULL, &argind); + if (err < 0) { + commands_hint(plugin); + return err; + } - sub_argv = malloc((argc + 1) * sizeof(*sub_argv)); - if (!sub_argv) - return -ENOMEM; + argc -= argind; + argv += argind; - argv[0] += strlen(extension->name); - while (*argv[0] == '-') - argv[0]++; + if (err & ARGCONFIG_HELP) + return run_help(argc, argv, plugin); + if (err & ARGCONFIG_VERSION) + return version_cmd(plugin); + + if (!argc) { + general_help(plugin, NULL); + return 0; + } + + if (!strcmp(argv[0], "help")) + return help(argc, argv, plugin); + if (!strcmp(argv[0], "version")) + return version_cmd(plugin); - name_copy = strdup(extension->name); - if (!name_copy) - return -ENOMEM; + cmd = find_command(plugin, argv[0]); + if (cmd) + return run_command(argc, argv, plugin, cmd); - sub_argv[0] = name_copy; - memcpy(&sub_argv[1], argv, argc * sizeof(*argv)); + /* Only the built-in plugin has extensions */ + if (plugin->name) { + __cleanup_free char *e = shr_escape(argv[0]); - return handle_plugin(argc + 1, sub_argv, extension); - } - extension = extension->next; + fprintf(stderr, + "ERROR: Invalid sub-command '%s' for plugin %s\n", + e ? e : "", plugin->name); + commands_hint(plugin); + return -ENOTTY; } - fprintf(stderr, "ERROR: Invalid sub-command '%s'\n", str); - return -ENOTTY; + + extension = find_extension(plugin, argv[0]); + if (extension) + return handle_plugin(argc, argv, extension); + + return run_joined_command(argc, argv, plugin); } diff --git a/src/plugin.h b/src/plugin.h index 562d44aec8..145a234715 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -40,14 +40,33 @@ 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; + /* "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 + * 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,10 +79,18 @@ struct command { 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); +/* 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 diff --git a/tests/cli/libmock_nvme.c b/tests/cli/libmock_nvme.c index e402c76085..fbf0c46335 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,8 +319,20 @@ static void init_orig_functions(void) orig_get_nprocs = (orig_get_nprocs_t)dlsym(RTLD_NEXT, "get_nprocs"); } +/* + * 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 const char *sock_path = getenv("MOCK_IPC_SOCK"); struct sockaddr_un addr; int fd; @@ -341,6 +359,7 @@ static int connect_ipc_socket(void) } return fd; +#endif } const char *default_opts = @@ -544,6 +563,107 @@ 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 *fuzz_ipc_call(struct ipc_request *req, + struct ipc_response *resp) +{ + uint8_t ctl; + uint16_t sc; + void *buf; + + 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; @@ -609,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. @@ -616,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); @@ -629,6 +756,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 +834,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"); @@ -749,7 +878,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); diff --git a/tests/cli/meson.build b/tests/cli/meson.build index 3e6167bf58..64ee17546b 100644 --- a/tests/cli/meson.build +++ b/tests/cli/meson.build @@ -11,44 +11,48 @@ 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, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for the "keys" plugin. Needs no device. Restricted # 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, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for "nvme config-convert". Needs no device; uses # 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, - depends: nvme_exe) + depends: nvme_exe, + suite: 'mock') endif # Standalone CLI test for "nvme config-create". Needs no device; uses an # 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, - 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') @@ -87,37 +92,81 @@ 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]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') 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]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') - 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]) + 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, + 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], + 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], + 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], + 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 + test('nvme-cli - discovery-logs', + python3, + args: [files('nvme_discovery_logs_test.py'), nvme_exe, + mock_nvme_lib], + env: cli_test_env, + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') + endif - # 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') - 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, depends: [nvme_exe, mock_nvme_lib], - timeout: 300) + timeout: 300, + suite: 'mock') endif if 'sed' in selected_plugins and want_sed and \ @@ -130,11 +179,12 @@ 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, - depends: [nvme_exe, mock_nvme_lib]) + depends: [nvme_exe, mock_nvme_lib], + suite: 'mock') endif endif @@ -149,4 +199,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/micron/meson.build b/tests/cli/micron/meson.build index 131260323e..beb9316a8e 100644 --- a/tests/cli/micron/meson.build +++ b/tests/cli/micron/meson.build @@ -25,20 +25,22 @@ 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) + timeout: 120, + suite: 'mock') 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']) + suite: 'mock') endif 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): 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/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_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/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( diff --git a/tests/cli/nvme_discovery_logs_test.py b/tests/cli/nvme_discovery_logs_test.py new file mode 100644 index 0000000000..7a8400bb8e --- /dev/null +++ b/tests/cli/nvme_discovery_logs_test.py @@ -0,0 +1,407 @@ +#!/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, 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 +""" +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 +_LID_POWER_MEASUREMENT = 0x25 +_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 +_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 + + +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) + 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(self, *args): + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'log', *args, self.DEVICE) + self.assertEqual(result.returncode, 0, + f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}') + self.assertEqual(self.server.errors, []) + return result.stdout + + def _run_json(self, *args): + return json.loads(self._run(*args, '-o', 'json')) + + def _run_both(self, *args): + """Runs the command with text and JSON output, for a page whose + lengths don't add up. Returns the JSON output.""" + self._run(*args) + return self._run_json(*args) + + 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_host_discovery_zero_record_length(self): + """A record length of 0 must not make the walk loop forever.""" + self.server.logs[_LID_HOST_DISCOVERY] = \ + _host_discovery_log(['nqn.2014-08.org.example:host1'], tel=0) + self._run_both('host-discovery') + + def test_host_discovery_record_past_the_page(self): + """A record which says it is longer than the page is not printed.""" + self.server.logs[_LID_HOST_DISCOVERY] = \ + _host_discovery_log(['nqn.2014-08.org.example:host1'], + tel=_HEDLPE_LEN * 4) + out = self._run_both('host-discovery') + 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_ave_discovery_zero_record_length(self): + self.server.logs[_LID_AVE_DISCOVERY] = \ + _ave_discovery_log(['nqn.2014-08.org.example:ave1'], tel=0) + out = self._run_both('ave-discovery') + self.assertFalse([k for k in out if k.startswith('adlpe')]) + + def test_ave_discovery_transport_records_past_the_record(self): + """More transport records than the record holds: only those in it + are printed.""" + self.server.logs[_LID_AVE_DISCOVERY] = \ + _ave_discovery_log(['nqn.2014-08.org.example:ave1'], numatr=200) + out = self._run_both('ave-discovery') + self.assertEqual(out['adlpe: 0']['numatr'], 200) + self.assertIn('atr: 0', out['adlpe: 0']) + self.assertNotIn('atr: 1', out['adlpe: 0']) + + def test_ave_discovery_nqn_fills_its_field(self): + """An NQN without a NUL ends at the end of its field.""" + nqn = 'nqn.2014-08.org.example:' + 'a' * 200 + self.server.logs[_LID_AVE_DISCOVERY] = _ave_discovery_log([nqn]) + out = self._run_both('ave-discovery') + self.assertEqual(out['adlpe: 0']['avenqn'], nqn[:224]) + + 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!']) + + def test_pull_model_ddc_req_short_length(self): + """A length shorter than the header has no OSP to print.""" + self.server.logs[_LID_PULL_MODEL_DDC_REQ] = \ + _pull_model_ddc_req_log(b'', tpdrpl=4) + out = self._run_both('pull-model-ddc-req') + self.assertEqual(out['osp'], []) + + def test_pull_model_ddc_req_get_log_short_buffer(self): + """get-log --verbose decodes the page in a buffer of the length + asked for, not of the length the page says.""" + self.server.logs[_LID_PULL_MODEL_DDC_REQ] = \ + _pull_model_ddc_req_log(b'OSPDATA!' * 8, tpdrpl=0x10000) + result = run_nvme(_NVME_BIN, self.env, self.sysfs_dir, self.base_dir, + 'get-log', self.DEVICE, '--log-id=0x73', + '--log-len=16', '--verbose') + self.assertEqual(result.returncode, 0, + f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}') + osp = result.stdout.split('osp:\n')[-1].splitlines() + self.assertEqual(len(osp), 2, result.stdout) # offset ruler, 8 bytes + self.assertIn('OSPDATA!', osp[1]) + + def test_power_measurement_more_descriptors_than_the_page(self): + """More histogram descriptors than the page holds: only those in + it are printed.""" + self.server.logs[_LID_POWER_MEASUREMENT] = _power_meas_log(100, 1) + self._run('power-measurement', '--verbose') + out = self._run_json('power-measurement') + self.assertEqual(out['nphd'], 100) + self.assertEqual(len(out['descs']), 1) + + def test_reachability_groups_descriptors_at_their_offsets(self): + """The descriptors differ in length: the second follows the + namespace IDs of the first.""" + self.server.logs[_LID_REACHABILITY_GROUPS] = \ + _reachability_log([(1, [10, 11]), (2, [20])]) + kv = _kv(self._run('reachability-groups')) + self.assertEqual([v for k, v in kv if k == 'rgid'], ['1', '2']) + self.assertEqual([v for k, v in kv if k.startswith('nsid')], + ['10', '11', '20']) + + def test_reachability_groups_no_zero_length_read(self): + self.server.logs[_LID_REACHABILITY_GROUPS] = \ + _reachability_log([(1, []), (2, [])]) + self._run('reachability-groups', '--groups-only') + lengths = [r[3] for r in self.server.requests + if r[0] == _LID_REACHABILITY_GROUPS] + self.assertNotIn(0, lengths) + + def test_reachability_groups_too_many_namespace_ids(self): + """A namespace ID count which no log can hold fails the command + instead of being read or printed.""" + log = bytearray(_reachability_log([(1, [10])])) + struct.pack_into(' +"""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:]) diff --git a/tests/cli/nvme_fabrics_mock_test.py b/tests/cli/nvme_fabrics_mock_test.py index 8eed565b43..e365d5b1d1 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 @@ -747,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_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('' 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/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 = " +"""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:]) diff --git a/tests/cli/nvme_usage_test.py b/tests/cli/nvme_usage_test.py new file mode 100644 index 0000000000..3fd9dc91df --- /dev/null +++ b/tests/cli/nvme_usage_test.py @@ -0,0 +1,311 @@ +#!/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 os +import subprocess +import sys +import tempfile +import unittest + +_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) + + +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("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_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) + 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): + 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 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:\033[0m 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.""" + + 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_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) + + 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): + result = run('id', 'ns', '--help') + self.assertIn('Names: all, -1', result.stdout) + 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 + 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.""" + + 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_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'") + 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') + self.assertIn('Usage: nvme list-subsys [] [OPTIONS]', + result.stdout) + self.assert_usage_error(run('list-subsys', 'nvme0', 'extra'), + "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]]) diff --git a/tests/cli/ocp/meson.build b/tests/cli/ocp/meson.build index 4ba35f0ef6..7ec5dd41ab 100644 --- a/tests/cli/ocp/meson.build +++ b/tests/cli/ocp/meson.build @@ -23,12 +23,14 @@ 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) + timeout: 120, + suite: 'mock') endforeach endif 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(' +"""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]]) 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/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 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 diff --git a/tests/e2e/plugins/ocp/ocp_internal_log_test.py b/tests/e2e/plugins/ocp/ocp_internal_log_test.py index dd34ac17d5..ff98234a09 100644 --- a/tests/e2e/plugins/ocp/ocp_internal_log_test.py +++ b/tests/e2e/plugins/ocp/ocp_internal_log_test.py @@ -26,9 +26,10 @@ * The report names every event FIFO the Data Area 1 header assigns to Data Area 1 or 2, using the string log's FIFO names. * Each FIFO's events match the raw FIFO by class, identifier and size, - their Event Strings match the string log, and any Virtual FIFO events - (class 0Bh) carry the identifier, subfields and names the raw bytes - and string log call for. + their Event Strings match the string log or, for an Event ID the + spec defines and the string log omits, the spec's name, and any + Virtual FIFO events (class 0Bh) carry the identifier, subfields and + names the raw bytes and string log call for. * Each data area's statistics match the raw descriptors by identifier, size and Context Index flag, down to the descriptors a Context Statistic Descriptor encapsulates. @@ -86,6 +87,15 @@ def _stats(da): return f"Data Area {da} Statistics" +def _expected_event_string(event, tables): + """The string log's name for @event, else the spec's name for a + non-vendor-unique class (TELSLG-14), else empty.""" + key = (event.cls, event.event_id) + if event.cls >= 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) 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 0000000000..a942f4514f Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-00 differ diff --git a/tests/fuzz/corpus/argconfig/seed-01 b/tests/fuzz/corpus/argconfig/seed-01 new file mode 100644 index 0000000000..a784966882 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-01 differ diff --git a/tests/fuzz/corpus/argconfig/seed-02 b/tests/fuzz/corpus/argconfig/seed-02 new file mode 100644 index 0000000000..a6029e62b4 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-02 differ diff --git a/tests/fuzz/corpus/argconfig/seed-03 b/tests/fuzz/corpus/argconfig/seed-03 new file mode 100644 index 0000000000..13910d8905 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-03 differ diff --git a/tests/fuzz/corpus/argconfig/seed-04 b/tests/fuzz/corpus/argconfig/seed-04 new file mode 100644 index 0000000000..b03f7587c3 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-04 differ diff --git a/tests/fuzz/corpus/argconfig/seed-05 b/tests/fuzz/corpus/argconfig/seed-05 new file mode 100644 index 0000000000..6373cf1625 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-05 differ diff --git a/tests/fuzz/corpus/argconfig/seed-06 b/tests/fuzz/corpus/argconfig/seed-06 new file mode 100644 index 0000000000..18b297ae07 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-06 differ diff --git a/tests/fuzz/corpus/argconfig/seed-07 b/tests/fuzz/corpus/argconfig/seed-07 new file mode 100644 index 0000000000..cfe462bb17 Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-07 differ diff --git a/tests/fuzz/corpus/argconfig/seed-08 b/tests/fuzz/corpus/argconfig/seed-08 new file mode 100644 index 0000000000..5dc6358d9b Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-08 differ diff --git a/tests/fuzz/corpus/argconfig/seed-09 b/tests/fuzz/corpus/argconfig/seed-09 new file mode 100644 index 0000000000..dba65af4fe Binary files /dev/null and b/tests/fuzz/corpus/argconfig/seed-09 differ 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 0000000000..99eb17718f Binary files /dev/null and b/tests/fuzz/corpus/device/regress-changed-ns-list-leak differ diff --git a/tests/fuzz/corpus/device/regress-dera-counter b/tests/fuzz/corpus/device/regress-dera-counter new file mode 100644 index 0000000000..68ba2a23b9 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-dera-counter differ diff --git a/tests/fuzz/corpus/device/regress-dera-status b/tests/fuzz/corpus/device/regress-dera-status new file mode 100644 index 0000000000..7700d08f5d Binary files /dev/null and b/tests/fuzz/corpus/device/regress-dera-status differ diff --git a/tests/fuzz/corpus/device/regress-dispersed-ns b/tests/fuzz/corpus/device/regress-dispersed-ns new file mode 100644 index 0000000000..e6baed0865 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-dispersed-ns differ 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 0000000000..bd574ef94b Binary files /dev/null and b/tests/fuzz/corpus/device/regress-dispersed-ns-leak differ diff --git a/tests/fuzz/corpus/device/regress-fdp-status b/tests/fuzz/corpus/device/regress-fdp-status new file mode 100644 index 0000000000..338759fa22 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-fdp-status differ diff --git a/tests/fuzz/corpus/device/regress-fdp-usage b/tests/fuzz/corpus/device/regress-fdp-usage new file mode 100644 index 0000000000..94e040223d Binary files /dev/null and b/tests/fuzz/corpus/device/regress-fdp-usage differ diff --git a/tests/fuzz/corpus/device/regress-fdp-usage-stdout b/tests/fuzz/corpus/device/regress-fdp-usage-stdout new file mode 100644 index 0000000000..b41e403da5 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-fdp-usage-stdout differ diff --git a/tests/fuzz/corpus/device/regress-host-discovery b/tests/fuzz/corpus/device/regress-host-discovery new file mode 100644 index 0000000000..4857f7beba --- /dev/null +++ b/tests/fuzz/corpus/device/regress-host-discovery @@ -0,0 +1 @@ +*ë€ \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-host-discovery-stdout b/tests/fuzz/corpus/device/regress-host-discovery-stdout new file mode 100644 index 0000000000..a16fe003f8 --- /dev/null +++ b/tests/fuzz/corpus/device/regress-host-discovery-stdout @@ -0,0 +1 @@ +*”g” \ No newline at end of file diff --git a/tests/fuzz/corpus/device/regress-ibm-persistent-event b/tests/fuzz/corpus/device/regress-ibm-persistent-event new file mode 100644 index 0000000000..63108166ad Binary files /dev/null and b/tests/fuzz/corpus/device/regress-ibm-persistent-event differ 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 0000000000..ee71dcc915 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-id-ctrl-rpmbbpwps differ 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 0000000000..5cb821899c Binary files /dev/null and b/tests/fuzz/corpus/device/regress-lba-status-leak differ 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 0000000000..229d5525bc Binary files /dev/null and b/tests/fuzz/corpus/device/regress-ocp-persistent-event-leak differ 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 0000000000..913d29bd36 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-persistent-event-empty differ 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 0000000000..271dbbfd57 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-power-measurement differ 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 0000000000..525eb06c8c Binary files /dev/null and b/tests/fuzz/corpus/device/regress-reachability-misaligned differ diff --git a/tests/fuzz/corpus/device/regress-reachability-slow b/tests/fuzz/corpus/device/regress-reachability-slow new file mode 100644 index 0000000000..5554ff0513 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-reachability-slow differ diff --git a/tests/fuzz/corpus/device/regress-reachability-table-slow b/tests/fuzz/corpus/device/regress-reachability-table-slow new file mode 100644 index 0000000000..148aa51235 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-reachability-table-slow differ 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 0000000000..eb681b65a3 Binary files /dev/null and b/tests/fuzz/corpus/device/regress-seagate-pcie-leak differ 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 0000000000..f091bc906f Binary files /dev/null and b/tests/fuzz/corpus/device/regress-seagate-vs-smart differ 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 0000000000..440782a16f Binary files /dev/null and b/tests/fuzz/corpus/device/regress-solidigm-drive-info-leak differ 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 0000000000..a3ec954422 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-0 differ diff --git a/tests/fuzz/corpus/device/seed-1 b/tests/fuzz/corpus/device/seed-1 new file mode 100644 index 0000000000..2ea8258d48 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-1 differ diff --git a/tests/fuzz/corpus/device/seed-2 b/tests/fuzz/corpus/device/seed-2 new file mode 100644 index 0000000000..332a425c71 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-2 differ diff --git a/tests/fuzz/corpus/device/seed-3 b/tests/fuzz/corpus/device/seed-3 new file mode 100644 index 0000000000..8b80425019 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-3 differ diff --git a/tests/fuzz/corpus/device/seed-4 b/tests/fuzz/corpus/device/seed-4 new file mode 100644 index 0000000000..351e015cb3 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-4 differ diff --git a/tests/fuzz/corpus/device/seed-5 b/tests/fuzz/corpus/device/seed-5 new file mode 100644 index 0000000000..3e1b3cb3b8 Binary files /dev/null and b/tests/fuzz/corpus/device/seed-5 differ diff --git a/tests/fuzz/corpus/fabrics-conf/seed-00 b/tests/fuzz/corpus/fabrics-conf/seed-00 new file mode 100644 index 0000000000..a08e289b6d --- /dev/null +++ b/tests/fuzz/corpus/fabrics-conf/seed-00 @@ -0,0 +1,46 @@ +# Example NVMe-oF connection configuration. +# +# This file is NOT read by nvme-cli -- it is a reference only. The real +# configuration file is @SYSCONFDIR@/nvme/nvme-fabrics.conf (plus an +# optional @SYSCONFDIR@/nvme/nvme-fabrics.conf.d/ drop-in directory). +# +# See nvme-fabrics.conf(5) for the full key reference and precedence +# rules, and https://github.com/linux-nvme/nvme-cli/blob/master/libnvme/design/CONFIG.md +# for the complete design rationale. + +# Type defaults: applied unless a more specific scope below overrides them. +[Discovery Controller Defaults] +keep-alive-tmo = 30 # DC keep-alive default +ctrl-loss-tmo = 600 + +[I/O Controller Defaults] +keep-alive-tmo = 5 # IOC keep-alive default (kernel default) +ctrl-loss-tmo = 600 + +# Optional: state this host's identity explicitly. Omit this whole +# section to fall back to @SYSCONFDIR@/nvme/hostnqn and hostid. +[Host] +hostsymname = lab-host-01 + +# A Discovery Controller with an explicit NQN. +[Discovery Controller] +nqn = nqn.2014-08.org.example:cdc.main +controller = transport=tcp;traddr=192.168.1.10;trsvcid=8009 + +# A Discovery Controller with no nqn = : defaults to the well-known +# discovery NQN. +[Discovery Controller] +controller = transport=tcp;traddr=192.168.1.11;trsvcid=8009 + +# A subsystem reachable over a single path. +[Subsystem] +nqn = nqn.2024-01.com.example:data.vol1 +controller = transport=tcp;traddr=192.168.1.20;trsvcid=4420 + +# A subsystem reachable over two paths (multipath), overriding the +# I/O Controller Defaults' ctrl-loss-tmo for this subsystem only. +[Subsystem] +nqn = nqn.2024-01.com.example:data.vol2 +ctrl-loss-tmo = 1800 +controller = transport=tcp;traddr=192.168.1.21;trsvcid=4420 +controller = transport=tcp;traddr=192.168.1.22;trsvcid=4420 diff --git a/tests/fuzz/corpus/fabrics-conf/seed-01 b/tests/fuzz/corpus/fabrics-conf/seed-01 new file mode 100644 index 0000000000..8bae01dc81 --- /dev/null +++ b/tests/fuzz/corpus/fabrics-conf/seed-01 @@ -0,0 +1,10 @@ +[Host] +hostnqn = nqn.2014-08.org.nvmexpress:uuid:00000000-0000-0000-0000-000000000000 +hostid = 00000000-0000-0000-0000-000000000000 +[Subsystem] +nqn = nqn.io-1 +controller = transport=tcp;traddr=10.0.0.1;trsvcid=4420;host-traddr=10.0.0.2;host-iface=eth0 +nr-io-queues = 4 +tls = yes +kxchap-secret = DHHC-1:00:abc: +persistent = force diff --git a/tests/fuzz/corpus/fabrics-conf/seed-02 b/tests/fuzz/corpus/fabrics-conf/seed-02 new file mode 100644 index 0000000000..a85c9c8dfd --- /dev/null +++ b/tests/fuzz/corpus/fabrics-conf/seed-02 @@ -0,0 +1,4 @@ +[Discovery Controller] +controller = transport=rdma;traddr=fe80::1 +[Subsystem] +controller = transport=tcp diff --git a/tests/fuzz/corpus/fabrics-conf/seed-03 b/tests/fuzz/corpus/fabrics-conf/seed-03 new file mode 100644 index 0000000000..d2a3e1a6b1 --- /dev/null +++ b/tests/fuzz/corpus/fabrics-conf/seed-03 @@ -0,0 +1,5 @@ +[Subsystem] +nqn = a +controller = transport=loop;traddr=x;;;=; +tos = -1 +queue-size = 0x10 diff --git a/tests/fuzz/corpus/global-config/seed-00 b/tests/fuzz/corpus/global-config/seed-00 new file mode 100644 index 0000000000..1a0cf3ddb6 --- /dev/null +++ b/tests/fuzz/corpus/global-config/seed-00 @@ -0,0 +1,10 @@ +[Global] +verbose = 2 +quiet = no +output-format = json +timeout = 5000 +dry-run = yes +no-retries = 1 +no-ioctl-probing = off +output-format-version = 2 +set-options = a=b diff --git a/tests/fuzz/corpus/global-config/seed-01 b/tests/fuzz/corpus/global-config/seed-01 new file mode 100644 index 0000000000..a6badbbbf6 --- /dev/null +++ b/tests/fuzz/corpus/global-config/seed-01 @@ -0,0 +1,4 @@ +[Global] +timeout = infinite +output-format = normal +output-format = binary diff --git a/tests/fuzz/corpus/global-config/seed-02 b/tests/fuzz/corpus/global-config/seed-02 new file mode 100644 index 0000000000..0018fee462 --- /dev/null +++ b/tests/fuzz/corpus/global-config/seed-02 @@ -0,0 +1,6 @@ +timeout = 1 +[Global] +human-readable = 1 +verbose = 4294967296 +timeout = -1 +bogus = x diff --git a/tests/fuzz/corpus/ini/seed-00 b/tests/fuzz/corpus/ini/seed-00 new file mode 100644 index 0000000000..a08e289b6d --- /dev/null +++ b/tests/fuzz/corpus/ini/seed-00 @@ -0,0 +1,46 @@ +# Example NVMe-oF connection configuration. +# +# This file is NOT read by nvme-cli -- it is a reference only. The real +# configuration file is @SYSCONFDIR@/nvme/nvme-fabrics.conf (plus an +# optional @SYSCONFDIR@/nvme/nvme-fabrics.conf.d/ drop-in directory). +# +# See nvme-fabrics.conf(5) for the full key reference and precedence +# rules, and https://github.com/linux-nvme/nvme-cli/blob/master/libnvme/design/CONFIG.md +# for the complete design rationale. + +# Type defaults: applied unless a more specific scope below overrides them. +[Discovery Controller Defaults] +keep-alive-tmo = 30 # DC keep-alive default +ctrl-loss-tmo = 600 + +[I/O Controller Defaults] +keep-alive-tmo = 5 # IOC keep-alive default (kernel default) +ctrl-loss-tmo = 600 + +# Optional: state this host's identity explicitly. Omit this whole +# section to fall back to @SYSCONFDIR@/nvme/hostnqn and hostid. +[Host] +hostsymname = lab-host-01 + +# A Discovery Controller with an explicit NQN. +[Discovery Controller] +nqn = nqn.2014-08.org.example:cdc.main +controller = transport=tcp;traddr=192.168.1.10;trsvcid=8009 + +# A Discovery Controller with no nqn = : defaults to the well-known +# discovery NQN. +[Discovery Controller] +controller = transport=tcp;traddr=192.168.1.11;trsvcid=8009 + +# A subsystem reachable over a single path. +[Subsystem] +nqn = nqn.2024-01.com.example:data.vol1 +controller = transport=tcp;traddr=192.168.1.20;trsvcid=4420 + +# A subsystem reachable over two paths (multipath), overriding the +# I/O Controller Defaults' ctrl-loss-tmo for this subsystem only. +[Subsystem] +nqn = nqn.2024-01.com.example:data.vol2 +ctrl-loss-tmo = 1800 +controller = transport=tcp;traddr=192.168.1.21;trsvcid=4420 +controller = transport=tcp;traddr=192.168.1.22;trsvcid=4420 diff --git a/tests/fuzz/corpus/ini/seed-01 b/tests/fuzz/corpus/ini/seed-01 new file mode 100644 index 0000000000..f134393559 --- /dev/null +++ b/tests/fuzz/corpus/ini/seed-01 @@ -0,0 +1,3 @@ +[Global] +verbose = 2 +timeout = infinite diff --git a/tests/fuzz/corpus/ini/seed-02 b/tests/fuzz/corpus/ini/seed-02 new file mode 100644 index 0000000000..f5c2266c37 --- /dev/null +++ b/tests/fuzz/corpus/ini/seed-02 @@ -0,0 +1,9 @@ +# comment +[a] +k=v +[ b ] + = x +[c +junk +[d]] +key = diff --git a/tests/fuzz/corpus/ini/seed-03 b/tests/fuzz/corpus/ini/seed-03 new file mode 100644 index 0000000000..22f29be3fb --- /dev/null +++ b/tests/fuzz/corpus/ini/seed-03 @@ -0,0 +1,3 @@ +[Global] +quiet = yes + dry-run=1 diff --git a/tests/fuzz/fuzz-argconfig.c b/tests/fuzz/fuzz-argconfig.c new file mode 100644 index 0000000000..583dfa16cf --- /dev/null +++ b/tests/fuzz/fuzz-argconfig.c @@ -0,0 +1,159 @@ +// 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 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-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/fuzz-driver.c b/tests/fuzz/fuzz-driver.c new file mode 100644 index 0000000000..1d5f38ac3c --- /dev/null +++ b/tests/fuzz/fuzz-driver.c @@ -0,0 +1,272 @@ +// 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). An input which takes longer than FUZZ_TIMEOUT seconds + * (default 10) counts as a crash. + */ + +#include +#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; +} + +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) +{ + 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); +} + +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) +{ + 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"); + const char *tmo = getenv("FUZZ_TIMEOUT"); + 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; + snprintf(crash_path, sizeof(crash_path), "fuzz-crash-%s", target); + + 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); + signal(SIGALRM, hang); + if (tmo) + timeout = strtoul(tmo, NULL, 0); + + 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..c182fa5056 --- /dev/null +++ b/tests/fuzz/meson.build @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# 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, see +# Documentation/TESTING.md: +# +# 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. + +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], + }, +} + +# The target parses with the libnvme internals, so it needs the in-tree +# libnvme. +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], + }, + } +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: 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( + t.get('component', '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 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/argconfig-getopt-ref.c b/tests/unit/c/argconfig-getopt-ref.c new file mode 100644 index 0000000000..072afcb9ae --- /dev/null +++ b/tests/unit/c/argconfig-getopt-ref.c @@ -0,0 +1,376 @@ +// 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. Help output and the locale side effect + * are left out. The test compares the parsing of the command line, so the + * value conversion is the one of argconfig, which uses the strict number + * parsers of shared/parse-util. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "../../../src/argconfig.h" +#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; +} + +/* 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; + + 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: + case CFG_FILE: + case CFG_DIR: + *(char **)value = arg; + break; + case CFG_INT: + ret = shr_parse_s64(arg, INT_MIN, INT_MAX, &i); + if (!ret) + *(int *)value = i; + break; + case CFG_BYTE: + ret = shr_parse_u64(arg, UINT8_MAX, &u); + if (!ret) + *(uint8_t *)value = u; + break; + case CFG_SHORT: + ret = shr_parse_u64(arg, UINT16_MAX, &u); + if (!ret) + *(uint16_t *)value = u; + break; + 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: + case CFG_ENUM_LIST: + ret = -EINVAL; + break; + case CFG_LONG: + ret = shr_parse_u64(arg, UINT64_MAX, &u); + if (!ret) + *(uint64_t *)value = u; + break; + case CFG_LONG_SUFFIX: + 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: + ret = shr_parse_double(arg, &d); + if (!ret) + *(double *)value = d; + break; + case CFG_FLAG: + *(bool *)value = true; + break; + case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: + break; + } + + return ret ? -EINVAL : 0; +} + +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: + case CFG_ENUM: + *(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: + case CFG_FILE: + case CFG_DIR: + *(char **)val = opt_val->string; + break; + case CFG_ENUM_LIST: + case CFG_GROUP_SEPARATOR: + case CFG_INCLUDE: + 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 == ARGCONFIG_ARG_REQUIRED || + s->argument_type == ARGCONFIG_ARG_OPTIONAL) + short_opts[short_index++] = ':'; + 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 = has_arg(s); + 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 == ARGCONFIG_ARG_REQUIRED || + s->argument_type == ARGCONFIG_ARG_OPTIONAL) + short_opts[short_index++] = ':'; + 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 = has_arg(s); + 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..f24de8f196 100644 --- a/tests/unit/c/meson.build +++ b/tests/unit/c/meson.build @@ -11,19 +11,42 @@ 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', + [ + '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, suite: 'unit') 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, ], ) -test('nvme-cli - global-config', test_global_config) +test('nvme-cli - global-config', test_global_config, suite: 'unit') test_plugin = executable( 'test-plugin', @@ -37,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/c/test-argconfig-diff.c b/tests/unit/c/test-argconfig-diff.c new file mode 100644 index 0000000000..2ecf85b63f --- /dev/null +++ b/tests/unit/c/test-argconfig-diff.c @@ -0,0 +1,468 @@ +// 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 + +#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; + bool noval; +}; + +struct global_cfg { + int verbose; + bool dry_run; + char *format; + uint32_t timeout; + bool help; +}; + +/* The report; stdout gets the help argconfig prints */ +static FILE *report; + +#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', &cfg->noval, "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', &cfg->help, "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; + fprintf(report, "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); + /* 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); + } + 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; + + fprintf(report, "ERROR: %s parse differs for:", + global ? "global" : "command"); + for (i = 0; i < nargs; i++) + 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; +} + +/* + * 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"); + 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) { + fprintf(report, "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); + + 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; +} + +#else /* __GLIBC__ */ + +int main(void) +{ + return 77; +} + +#endif /* __GLIBC__ */ diff --git a/tests/unit/c/test-argconfig-parse.c b/tests/unit/c/test-argconfig-parse.c index bb909acd64..667bceaea7 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; @@ -105,8 +107,45 @@ 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), + /* 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), + 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("--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("--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), 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), @@ -116,6 +155,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), @@ -139,12 +180,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. @@ -153,6 +194,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) @@ -181,6 +234,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), @@ -252,7 +306,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) @@ -287,15 +341,552 @@ 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; + } +} + +/* 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; + } + } +} + +/* + * Options registered with argconfig_set_global_options() are accepted by + * 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. + */ +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"), + /* -d is the global --dry-run */ + 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", + "--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") || + 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)) { + 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_given(local, &format) || + !argconfig_given(local, &dry_run)) { + printf("ERROR: global options: seen not kept\n"); + test_rc = 1; + } + } + + 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; + } + } +} + +/* 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) +{ + 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) +{ + 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) +{ + 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. + */ +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), + OPT_UINT("uint", 'u', &cfg.uint, "uint"), + 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); +} + +/* + * 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; + } + } +} + +/* + * 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. + */ +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 { @@ -354,12 +945,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; @@ -428,11 +1018,27 @@ 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[], struct argconfig_commandline_options *opts, - char *buf, size_t buf_size) + int *argind, char *buf, size_t buf_size) { FILE *tmp = NULL; int ret; @@ -463,7 +1069,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) { @@ -493,7 +1099,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) = { @@ -507,9 +1113,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", @@ -518,9 +1126,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; } @@ -557,14 +1165,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); @@ -572,6 +1180,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; @@ -590,6 +1276,18 @@ int main(void) optional_string_test(); combined_opts_test(); + operands_test(); + global_options_test(); + given_test(); + old_name_test(); + enum_test(); + enum_list_test(); + include_test(); + bind_test(); + 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]); @@ -598,6 +1296,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); diff --git a/tests/unit/c/test-global-config.c b/tests/unit/c/test-global-config.c index baa63f2080..04612fdb0d 100644 --- a/tests/unit/c/test-global-config.c +++ b/tests/unit/c/test-global-config.c @@ -113,6 +113,96 @@ 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_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"); + } + + 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"); + 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; +} + +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; @@ -140,6 +230,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 +312,15 @@ int main(void) { bool pass = true; + nvme_args_register(); + pass &= test_valid_keys(); 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(); pass &= test_missing_file_is_noop(); 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 { diff --git a/tests/unit/py/meson.build b/tests/unit/py/meson.build index c3ba7d311b..2be470c90a 100644 --- a/tests/unit/py/meson.build +++ b/tests/unit/py/meson.build @@ -21,6 +21,36 @@ if python3.found() ], depends: nvme_exe, timeout: 120, + suite: 'check', + ) + + # 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, + suite: 'check', + ) + + # 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, + suite: 'check', ) endif @@ -34,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, @@ -48,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 @@ -62,6 +94,20 @@ 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. + 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, + suite: 'check', ) # The completion test fixture is a hand-written, synthetic metadata model; @@ -76,16 +122,18 @@ if python3.found() meson.project_source_root() / 'plugins' / 'utils' / 'command-metadata-schema.json', ], timeout: 60, + suite: 'check', ) # 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'), meson.project_source_root() / 'tests' / 'e2e' / 'runner.py', ], timeout: 60, + suite: 'unit', ) endif diff --git a/tests/unit/py/test_command_metadata_schema.py b/tests/unit/py/test_command_metadata_schema.py index a47825ce31..7505ae661f 100644 --- a/tests/unit/py/test_command_metadata_schema.py +++ b/tests/unit/py/test_command_metadata_schema.py @@ -95,6 +95,11 @@ 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"), + 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) @@ -138,8 +143,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.""" @@ -149,6 +161,38 @@ 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") + + 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. 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): 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..605ad2074d --- /dev/null +++ b/tests/unit/py/test_generate_completions_values.py @@ -0,0 +1,71 @@ +#!/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. 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 +""" +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.zsh = generate("zsh") + 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_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) + + +if __name__ == "__main__": + unittest.main(argv=[sys.argv[0]] + sys.argv[3:]) 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]]) 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]])