Bug Summary

File:.build-ci/../src/fabrics.c
Warning:line 1049, column 10
Potential leak of memory pointed to by 'fa.traddr'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name fabrics.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../src/fabrics.c
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2016 Intel Corporation. All rights reserved.
4 * Copyright (c) 2016 HGST, a Western Digital Company.
5 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 *
18 * This file implements the discovery controller feature of NVMe over
19 * Fabrics specification standard.
20 */
21
22#include <errno(*__errno_location ()).h>
23#include <fcntl.h>
24#include <inttypes.h>
25#include <libgen.h>
26#include <net/if.h>
27#include <stdbool.h>
28#include <stddef.h>
29#include <stdint.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <sys/stat.h>
33#include <sys/sysinfo.h>
34#include <sys/types.h>
35#include <time.h>
36#include <unistd.h>
37
38#ifdef NVME_HAVE_NETDB
39#include <arpa/inet.h>
40#include <netdb.h>
41#include <sys/socket.h>
42#endif
43
44#ifdef NVME_HAVE_LIBKMOD
45#include <libkmod.h>
46#endif
47
48#include <libnvme.h>
49
50#include <ccan/endian/endian.h>
51#include <ccan/minmax/minmax.h>
52#include <ccan/str/str.h>
53#include <shared/io-util.h>
54#include <shared/net-util.h>
55#include <shared/sig-util.h>
56#include <shared/string-util.h>
57
58#include "cleanup.h"
59#include "config-convert.h"
60#include "fabrics.h"
61#include "global-ctx.h"
62#include "logging.h"
63#include "nvme-print.h"
64
65#define MAX_DISC_ARGS32 32
66#define MAX_DISC_RETRIES10 10
67
68#define NVMF_DEF_DISC_TMO30 30
69
70/* Name of file to output log pages in their raw format */
71static char *raw;
72
73static const char *nvmf_config_file = "Use specified INI configuration file (auto-converts a legacy .json) or 'none' to disable";
74static const char *nvmf_persistent =
75 "persistent discovery connection (default: auto; =no disables)";
76static const char *nvmf_config_file_ro = "INI configuration file (default: " PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf" ")";
77
78/*
79 * The fabrics options, which store into nvmf_tmpl. nvmf_options() binds
80 * them to the struct nvmf_args of a command.
81 */
82static struct nvmf_args nvmf_tmpl;
83
84static struct argconfig_commandline_options nvmf_template[] = {
85 OPT_STRING("transport", 't', "STR", &nvmf_tmpl.transport,{"transport", 't', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.transport, char ** : (&nvmf_tmpl.transport), const char *
* : (&nvmf_tmpl.transport)), ARGCONFIG_ARG_REQUIRED, "transport type"
, 0, }
86 DESC_NVMF_TPORT){"transport", 't', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.transport, char ** : (&nvmf_tmpl.transport), const char *
* : (&nvmf_tmpl.transport)), ARGCONFIG_ARG_REQUIRED, "transport type"
, 0, }
,
87 OPT_STRING("nqn", 'n', "STR", &nvmf_tmpl.subsysnqn,{"nqn", 'n', "STR", CFG_STRING, _Generic(&nvmf_tmpl.subsysnqn
, char ** : (&nvmf_tmpl.subsysnqn), const char ** : (&
nvmf_tmpl.subsysnqn)), ARGCONFIG_ARG_REQUIRED, "subsystem nqn"
, 0, }
88 DESC_NVMF_NQN){"nqn", 'n', "STR", CFG_STRING, _Generic(&nvmf_tmpl.subsysnqn
, char ** : (&nvmf_tmpl.subsysnqn), const char ** : (&
nvmf_tmpl.subsysnqn)), ARGCONFIG_ARG_REQUIRED, "subsystem nqn"
, 0, }
,
89 OPT_STRING("traddr", 'a', "STR", &nvmf_tmpl.traddr,{"traddr", 'a', "STR", CFG_STRING, _Generic(&nvmf_tmpl.traddr
, char ** : (&nvmf_tmpl.traddr), const char ** : (&nvmf_tmpl
.traddr)), ARGCONFIG_ARG_REQUIRED, "transport address", 0, }
90 DESC_NVMF_TRADDR){"traddr", 'a', "STR", CFG_STRING, _Generic(&nvmf_tmpl.traddr
, char ** : (&nvmf_tmpl.traddr), const char ** : (&nvmf_tmpl
.traddr)), ARGCONFIG_ARG_REQUIRED, "transport address", 0, }
,
91 OPT_STRING("trsvcid", 's', "STR", &nvmf_tmpl.trsvcid,{"trsvcid", 's', "STR", CFG_STRING, _Generic(&nvmf_tmpl.trsvcid
, char ** : (&nvmf_tmpl.trsvcid), const char ** : (&nvmf_tmpl
.trsvcid)), ARGCONFIG_ARG_REQUIRED, "transport service id (e.g. IP port)"
, 0, }
92 DESC_NVMF_TRSVCID){"trsvcid", 's', "STR", CFG_STRING, _Generic(&nvmf_tmpl.trsvcid
, char ** : (&nvmf_tmpl.trsvcid), const char ** : (&nvmf_tmpl
.trsvcid)), ARGCONFIG_ARG_REQUIRED, "transport service id (e.g. IP port)"
, 0, }
,
93 OPT_STRING("host-traddr", 'w', "STR", &nvmf_tmpl.host_traddr,{"host-traddr", 'w', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.host_traddr, char ** : (&nvmf_tmpl.host_traddr), const char
** : (&nvmf_tmpl.host_traddr)), ARGCONFIG_ARG_REQUIRED, "host traddr (e.g. FC WWN's)"
, 0, }
94 DESC_NVMF_HTRADDR){"host-traddr", 'w', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.host_traddr, char ** : (&nvmf_tmpl.host_traddr), const char
** : (&nvmf_tmpl.host_traddr)), ARGCONFIG_ARG_REQUIRED, "host traddr (e.g. FC WWN's)"
, 0, }
,
95 OPT_STRING("host-iface", 'f', "STR", &nvmf_tmpl.host_iface,{"host-iface", 'f', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.host_iface, char ** : (&nvmf_tmpl.host_iface), const char
** : (&nvmf_tmpl.host_iface)), ARGCONFIG_ARG_REQUIRED, "host interface (for tcp transport)"
, 0, }
96 DESC_NVMF_HIFACE){"host-iface", 'f', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.host_iface, char ** : (&nvmf_tmpl.host_iface), const char
** : (&nvmf_tmpl.host_iface)), ARGCONFIG_ARG_REQUIRED, "host interface (for tcp transport)"
, 0, }
,
97 OPT_STRING("hostnqn", 'q', "STR", &nvmf_tmpl.hostnqn,{"hostnqn", 'q', "STR", CFG_STRING, _Generic(&nvmf_tmpl.hostnqn
, char ** : (&nvmf_tmpl.hostnqn), const char ** : (&nvmf_tmpl
.hostnqn)), ARGCONFIG_ARG_REQUIRED, "user-defined hostnqn", 0
, }
98 DESC_NVMF_HOSTNQN){"hostnqn", 'q', "STR", CFG_STRING, _Generic(&nvmf_tmpl.hostnqn
, char ** : (&nvmf_tmpl.hostnqn), const char ** : (&nvmf_tmpl
.hostnqn)), ARGCONFIG_ARG_REQUIRED, "user-defined hostnqn", 0
, }
,
99 OPT_STRING("hostid", 'I', "STR", &nvmf_tmpl.hostid,{"hostid", 'I', "STR", CFG_STRING, _Generic(&nvmf_tmpl.hostid
, char ** : (&nvmf_tmpl.hostid), const char ** : (&nvmf_tmpl
.hostid)), ARGCONFIG_ARG_REQUIRED, "user-defined hostid (if default not used)"
, 0, }
100 DESC_NVMF_HOSTID){"hostid", 'I', "STR", CFG_STRING, _Generic(&nvmf_tmpl.hostid
, char ** : (&nvmf_tmpl.hostid), const char ** : (&nvmf_tmpl
.hostid)), ARGCONFIG_ARG_REQUIRED, "user-defined hostid (if default not used)"
, 0, }
,
101 OPT_STRING("kxchap-secret", 'S', "STR", &nvmf_tmpl.hostkey,{"kxchap-secret", 'S', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.hostkey, char ** : (&nvmf_tmpl.hostkey), const char ** :
(&nvmf_tmpl.hostkey)), ARGCONFIG_ARG_REQUIRED, "user-defined kxchap secret (if default not used)"
, 0, .old_name = "dhchap-secret"}
102 DESC_NVMF_HOSTKEY, .old_name = "dhchap-secret"){"kxchap-secret", 'S', "STR", CFG_STRING, _Generic(&nvmf_tmpl
.hostkey, char ** : (&nvmf_tmpl.hostkey), const char ** :
(&nvmf_tmpl.hostkey)), ARGCONFIG_ARG_REQUIRED, "user-defined kxchap secret (if default not used)"
, 0, .old_name = "dhchap-secret"}
,
103 OPT_STRING("kxchap-ctrl-secret", 'C', "STR", &nvmf_tmpl.ctrlkey,{"kxchap-ctrl-secret", 'C', "STR", CFG_STRING, _Generic(&
nvmf_tmpl.ctrlkey, char ** : (&nvmf_tmpl.ctrlkey), const char
** : (&nvmf_tmpl.ctrlkey)), ARGCONFIG_ARG_REQUIRED, "user-defined kxchap controller secret (for bi-directional authentication)"
, 0, .old_name = "dhchap-ctrl-secret"}
104 DESC_NVMF_CTRLKEY, .old_name = "dhchap-ctrl-secret"){"kxchap-ctrl-secret", 'C', "STR", CFG_STRING, _Generic(&
nvmf_tmpl.ctrlkey, char ** : (&nvmf_tmpl.ctrlkey), const char
** : (&nvmf_tmpl.ctrlkey)), ARGCONFIG_ARG_REQUIRED, "user-defined kxchap controller secret (for bi-directional authentication)"
, 0, .old_name = "dhchap-ctrl-secret"}
,
105 OPT_STRING("keyring", 0, "STR", &nvmf_tmpl.keyring,{"keyring", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl.keyring
, char ** : (&nvmf_tmpl.keyring), const char ** : (&nvmf_tmpl
.keyring)), ARGCONFIG_ARG_REQUIRED, "Keyring for TLS key lookup (key id or keyring name)"
, 0, }
106 DESC_NVMF_KEYRING){"keyring", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl.keyring
, char ** : (&nvmf_tmpl.keyring), const char ** : (&nvmf_tmpl
.keyring)), ARGCONFIG_ARG_REQUIRED, "Keyring for TLS key lookup (key id or keyring name)"
, 0, }
,
107 OPT_STRING("tls-key", 0, "STR", &nvmf_tmpl.tls_key,{"tls-key", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl.tls_key
, char ** : (&nvmf_tmpl.tls_key), const char ** : (&nvmf_tmpl
.tls_key)), ARGCONFIG_ARG_REQUIRED, "TLS key to use (key id or key in interchange format)"
, 0, }
108 DESC_NVMF_TLS_KEY){"tls-key", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl.tls_key
, char ** : (&nvmf_tmpl.tls_key), const char ** : (&nvmf_tmpl
.tls_key)), ARGCONFIG_ARG_REQUIRED, "TLS key to use (key id or key in interchange format)"
, 0, }
,
109 OPT_STRING("tls-key-identity", 0, "STR", &nvmf_tmpl.tls_key_identity,{"tls-key-identity", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl
.tls_key_identity, char ** : (&nvmf_tmpl.tls_key_identity
), const char ** : (&nvmf_tmpl.tls_key_identity)), ARGCONFIG_ARG_REQUIRED
, "TLS key identity", 0, }
110 DESC_NVMF_TLS_KEY_IDENTITY){"tls-key-identity", 0, "STR", CFG_STRING, _Generic(&nvmf_tmpl
.tls_key_identity, char ** : (&nvmf_tmpl.tls_key_identity
), const char ** : (&nvmf_tmpl.tls_key_identity)), ARGCONFIG_ARG_REQUIRED
, "TLS key identity", 0, }
,
111 OPT_INT("nr-io-queues", 'i', &nvmf_tmpl.nr_io_queues,{"nr-io-queues", 'i', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_io_queues, int * : (&nvmf_tmpl.nr_io_queues)), ARGCONFIG_ARG_REQUIRED
, "number of io queues to use (default is core count)", 0, }
112 DESC_NVMF_NR_IO_QUEUES){"nr-io-queues", 'i', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_io_queues, int * : (&nvmf_tmpl.nr_io_queues)), ARGCONFIG_ARG_REQUIRED
, "number of io queues to use (default is core count)", 0, }
,
113 OPT_INT("nr-write-queues", 'W', &nvmf_tmpl.nr_write_queues,{"nr-write-queues", 'W', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_write_queues, int * : (&nvmf_tmpl.nr_write_queues)), ARGCONFIG_ARG_REQUIRED
, "number of write queues to use (default 0)", 0, }
114 DESC_NVMF_NR_WRITE_QUEUES){"nr-write-queues", 'W', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_write_queues, int * : (&nvmf_tmpl.nr_write_queues)), ARGCONFIG_ARG_REQUIRED
, "number of write queues to use (default 0)", 0, }
,
115 OPT_INT("nr-poll-queues", 'P', &nvmf_tmpl.nr_poll_queues,{"nr-poll-queues", 'P', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_poll_queues, int * : (&nvmf_tmpl.nr_poll_queues)), ARGCONFIG_ARG_REQUIRED
, "number of poll queues to use (default 0)", 0, }
116 DESC_NVMF_NR_POLL_QUEUES){"nr-poll-queues", 'P', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.nr_poll_queues, int * : (&nvmf_tmpl.nr_poll_queues)), ARGCONFIG_ARG_REQUIRED
, "number of poll queues to use (default 0)", 0, }
,
117 OPT_INT("queue-size", 'Q', &nvmf_tmpl.queue_size,{"queue-size", 'Q', "NUM", CFG_INT, _Generic(&nvmf_tmpl.queue_size
, int * : (&nvmf_tmpl.queue_size)), ARGCONFIG_ARG_REQUIRED
, "number of io queue elements to use (default 128)", 0, }
118 DESC_NVMF_QUEUE_SIZE){"queue-size", 'Q', "NUM", CFG_INT, _Generic(&nvmf_tmpl.queue_size
, int * : (&nvmf_tmpl.queue_size)), ARGCONFIG_ARG_REQUIRED
, "number of io queue elements to use (default 128)", 0, }
,
119 OPT_INT("keep-alive-tmo", 'k', &nvmf_tmpl.keep_alive_tmo,{"keep-alive-tmo", 'k', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.keep_alive_tmo, int * : (&nvmf_tmpl.keep_alive_tmo)), ARGCONFIG_ARG_REQUIRED
, "keep alive timeout period in seconds", 0, }
120 DESC_NVMF_KEEP_ALIVE_TMO){"keep-alive-tmo", 'k', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.keep_alive_tmo, int * : (&nvmf_tmpl.keep_alive_tmo)), ARGCONFIG_ARG_REQUIRED
, "keep alive timeout period in seconds", 0, }
,
121 OPT_INT("reconnect-delay", 'c', &nvmf_tmpl.reconnect_delay,{"reconnect-delay", 'c', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.reconnect_delay, int * : (&nvmf_tmpl.reconnect_delay)), ARGCONFIG_ARG_REQUIRED
, "reconnect timeout period in seconds", 0, }
122 DESC_NVMF_RECONNECT_DELAY){"reconnect-delay", 'c', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.reconnect_delay, int * : (&nvmf_tmpl.reconnect_delay)), ARGCONFIG_ARG_REQUIRED
, "reconnect timeout period in seconds", 0, }
,
123 OPT_INT("ctrl-loss-tmo", 'l', &nvmf_tmpl.ctrl_loss_tmo,{"ctrl-loss-tmo", 'l', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.ctrl_loss_tmo, int * : (&nvmf_tmpl.ctrl_loss_tmo)), ARGCONFIG_ARG_REQUIRED
, "controller loss timeout period in seconds", 0, }
124 DESC_NVMF_CTRL_LOSS_TMO){"ctrl-loss-tmo", 'l', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.ctrl_loss_tmo, int * : (&nvmf_tmpl.ctrl_loss_tmo)), ARGCONFIG_ARG_REQUIRED
, "controller loss timeout period in seconds", 0, }
,
125 OPT_INT("fast-io-fail-tmo", 'F', &nvmf_tmpl.fast_io_fail_tmo,{"fast-io-fail-tmo", 'F', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.fast_io_fail_tmo, int * : (&nvmf_tmpl.fast_io_fail_tmo))
, ARGCONFIG_ARG_REQUIRED, "fast I/O fail timeout (default off)"
, 0, .old_name = "fast_io_fail_tmo"}
126 DESC_NVMF_FAST_IO_FAIL_TMO, .old_name = "fast_io_fail_tmo"){"fast-io-fail-tmo", 'F', "NUM", CFG_INT, _Generic(&nvmf_tmpl
.fast_io_fail_tmo, int * : (&nvmf_tmpl.fast_io_fail_tmo))
, ARGCONFIG_ARG_REQUIRED, "fast I/O fail timeout (default off)"
, 0, .old_name = "fast_io_fail_tmo"}
,
127 OPT_INT("tos", 'T', &nvmf_tmpl.tos,{"tos", 'T', "NUM", CFG_INT, _Generic(&nvmf_tmpl.tos, int
* : (&nvmf_tmpl.tos)), ARGCONFIG_ARG_REQUIRED, "type of service"
, 0, }
128 DESC_NVMF_TOS){"tos", 'T', "NUM", CFG_INT, _Generic(&nvmf_tmpl.tos, int
* : (&nvmf_tmpl.tos)), ARGCONFIG_ARG_REQUIRED, "type of service"
, 0, }
,
129 OPT_INT("tls_key", 0, &nvmf_tmpl.tls_key_id,{"tls_key", 0, "NUM", CFG_INT, _Generic(&nvmf_tmpl.tls_key_id
, int * : (&nvmf_tmpl.tls_key_id)), ARGCONFIG_ARG_REQUIRED
, "TLS key to use (key id)", 0, }
130 DESC_NVMF_TLS_KEY_LEGACY){"tls_key", 0, "NUM", CFG_INT, _Generic(&nvmf_tmpl.tls_key_id
, int * : (&nvmf_tmpl.tls_key_id)), ARGCONFIG_ARG_REQUIRED
, "TLS key to use (key id)", 0, }
,
131 OPT_FLAG("duplicate-connect", 'D', &nvmf_tmpl.duplicate_connect,{"duplicate-connect", 'D', ((void*)0), CFG_FLAG, _Generic(&
nvmf_tmpl.duplicate_connect, _Bool * : (&nvmf_tmpl.duplicate_connect
)), ARGCONFIG_ARG_NONE, "allow duplicate connections between same transport host and subsystem port"
, 0, }
132 DESC_NVMF_DUP_CONNECT){"duplicate-connect", 'D', ((void*)0), CFG_FLAG, _Generic(&
nvmf_tmpl.duplicate_connect, _Bool * : (&nvmf_tmpl.duplicate_connect
)), ARGCONFIG_ARG_NONE, "allow duplicate connections between same transport host and subsystem port"
, 0, }
,
133 OPT_FLAG("disable-sqflow", 0, &nvmf_tmpl.disable_sqflow,{"disable-sqflow", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.disable_sqflow, _Bool * : (&nvmf_tmpl.disable_sqflow)), ARGCONFIG_ARG_NONE
, "disable controller sq flow control (default false)", 0, }
134 DESC_NVMF_DISABLE_SQFLOW){"disable-sqflow", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.disable_sqflow, _Bool * : (&nvmf_tmpl.disable_sqflow)), ARGCONFIG_ARG_NONE
, "disable controller sq flow control (default false)", 0, }
,
135 OPT_FLAG("hdr-digest", 'g', &nvmf_tmpl.hdr_digest,{"hdr-digest", 'g', ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.hdr_digest, _Bool * : (&nvmf_tmpl.hdr_digest)), ARGCONFIG_ARG_NONE
, "enable transport protocol header digest (TCP transport)", 0
, }
136 DESC_NVMF_HDR_DIGEST){"hdr-digest", 'g', ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.hdr_digest, _Bool * : (&nvmf_tmpl.hdr_digest)), ARGCONFIG_ARG_NONE
, "enable transport protocol header digest (TCP transport)", 0
, }
,
137 OPT_FLAG("data-digest", 'G', &nvmf_tmpl.data_digest,{"data-digest", 'G', ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.data_digest, _Bool * : (&nvmf_tmpl.data_digest)), ARGCONFIG_ARG_NONE
, "enable transport protocol data digest (TCP transport)", 0,
}
138 DESC_NVMF_DATA_DIGEST){"data-digest", 'G', ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl
.data_digest, _Bool * : (&nvmf_tmpl.data_digest)), ARGCONFIG_ARG_NONE
, "enable transport protocol data digest (TCP transport)", 0,
}
,
139 OPT_FLAG("tls", 0, &nvmf_tmpl.tls,{"tls", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl.tls,
_Bool * : (&nvmf_tmpl.tls)), ARGCONFIG_ARG_NONE, "enable TLS"
, 0, }
140 DESC_NVMF_TLS){"tls", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl.tls,
_Bool * : (&nvmf_tmpl.tls)), ARGCONFIG_ARG_NONE, "enable TLS"
, 0, }
,
141 OPT_FLAG("concat", 0, &nvmf_tmpl.concat,{"concat", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl.concat
, _Bool * : (&nvmf_tmpl.concat)), ARGCONFIG_ARG_NONE, "enable secure concatenation"
, 0, }
142 DESC_NVMF_CONCAT){"concat", 0, ((void*)0), CFG_FLAG, _Generic(&nvmf_tmpl.concat
, _Bool * : (&nvmf_tmpl.concat)), ARGCONFIG_ARG_NONE, "enable secure concatenation"
, 0, }
,
143 OPT_END(){ ((void*)0) }
144};
145
146struct nvmf_args nvmf_args_defaults(void)
147{
148 return (struct nvmf_args) {
149 .tos = -1,
150 .ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO600,
151 };
152}
153
154struct argconfig_commandline_options *nvmf_options(struct nvmf_args *fa)
155{
156 return argconfig_bind(nvmf_template, &nvmf_tmpl, sizeof(nvmf_tmpl),
157 fa);
158}
159
160void nvmf_args_to_params(struct libnvmf_params *params,
161 const struct nvmf_args *fa)
162{
163 char buf[32];
164
165 if (fa->nr_io_queues) {
166 snprintf(buf, sizeof(buf), "%d", fa->nr_io_queues);
167 libnvmf_params_set(params, "nr-io-queues", buf);
168 }
169 if (fa->nr_write_queues) {
170 snprintf(buf, sizeof(buf), "%d", fa->nr_write_queues);
171 libnvmf_params_set(params, "nr-write-queues", buf);
172 }
173 if (fa->nr_poll_queues) {
174 snprintf(buf, sizeof(buf), "%d", fa->nr_poll_queues);
175 libnvmf_params_set(params, "nr-poll-queues", buf);
176 }
177 if (fa->queue_size) {
178 snprintf(buf, sizeof(buf), "%d", fa->queue_size);
179 libnvmf_params_set(params, "queue-size", buf);
180 }
181 if (fa->keep_alive_tmo) {
182 snprintf(buf, sizeof(buf), "%d", fa->keep_alive_tmo);
183 libnvmf_params_set(params, "keep-alive-tmo", buf);
184 }
185 if (fa->reconnect_delay) {
186 snprintf(buf, sizeof(buf), "%d", fa->reconnect_delay);
187 libnvmf_params_set(params, "reconnect-delay", buf);
188 }
189 if (fa->ctrl_loss_tmo != NVMF_DEF_CTRL_LOSS_TMO600) {
190 snprintf(buf, sizeof(buf), "%d", fa->ctrl_loss_tmo);
191 libnvmf_params_set(params, "ctrl-loss-tmo", buf);
192 }
193 if (fa->fast_io_fail_tmo) {
194 snprintf(buf, sizeof(buf), "%d", fa->fast_io_fail_tmo);
195 libnvmf_params_set(params, "fast-io-fail-tmo", buf);
196 }
197 if (fa->tos != -1) {
198 snprintf(buf, sizeof(buf), "%d", fa->tos);
199 libnvmf_params_set(params, "tos", buf);
200 }
201 if (fa->duplicate_connect)
202 libnvmf_params_set(params, "duplicate-connect", "true");
203 if (fa->disable_sqflow)
204 libnvmf_params_set(params, "disable-sqflow", "true");
205 if (fa->hdr_digest)
206 libnvmf_params_set(params, "hdr-digest", "true");
207 if (fa->data_digest)
208 libnvmf_params_set(params, "data-digest", "true");
209 if (fa->tls)
210 libnvmf_params_set(params, "tls", "true");
211 if (fa->concat)
212 libnvmf_params_set(params, "concat", "true");
213 if (fa->hostkey)
214 libnvmf_params_set(params, "kxchap-secret", fa->hostkey);
215 if (fa->ctrlkey)
216 libnvmf_params_set(params, "kxchap-ctrl-secret", fa->ctrlkey);
217 if (fa->keyring)
218 libnvmf_params_set(params, "keyring", fa->keyring);
219 if (fa->tls_key)
220 libnvmf_params_set(params, "tls-key", fa->tls_key);
221 if (fa->tls_key_identity)
222 libnvmf_params_set(params, "tls-key-identity",
223 fa->tls_key_identity);
224}
225
226static void save_discovery_log(char *raw, const struct nvmf_discovery_log *log)
227{
228 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
229 static unsigned int save_count;
230 int fd, ret;
231 uint64_t numrec;
232 size_t len;
233
234 if (save_count) {
235 if (asprintf(&path, "%s.%u", raw, save_count) < 0) {
236 nvme_show_error("failed to allocate path for %s", raw)nvme_show_message(1, "failed to allocate path for %s", raw);
237 return;
238 }
239 } else {
240 path = strdup(raw);
241 if (!path) {
242 nvme_show_error("failed to allocate path for %s", raw)nvme_show_message(1, "failed to allocate path for %s", raw);
243 return;
244 }
245 }
246
247 fd = open(path, O_CREAT0100 | O_RDWR02 | O_TRUNC01000, 0600);
248 if (fd < 0) {
249 nvme_show_error("failed to open %s: %s",nvme_show_message(1, "failed to open %s: %s", path, libnvme_strerror
((*__errno_location ())))
250 path, libnvme_strerror(errno))nvme_show_message(1, "failed to open %s: %s", path, libnvme_strerror
((*__errno_location ())))
;
251 return;
252 }
253
254 numrec = le64_to_cpu(log->numrec);
255 len = sizeof(struct nvmf_discovery_log) +
256 numrec * sizeof(struct nvmf_disc_log_entry);
257
258 ret = shr_write_all(fd, log, len);
259 if (ret < 0)
260 nvme_show_error("failed to write to %s: %s",nvme_show_message(1, "failed to write to %s: %s", path, libnvme_strerror
(-ret))
261 path, libnvme_strerror(-ret))nvme_show_message(1, "failed to write to %s: %s", path, libnvme_strerror
(-ret))
;
262 else
263 nvme_show_verbose_info("Discovery log is saved to %s", path)nvme_show_verbose_message("Discovery log is saved to %s", path
)
;
264
265 close(fd);
266
267 save_count++;
268}
269
270static int setup_common_context(struct libnvmf_context *fctx,
271 struct nvmf_args *fa);
272
273struct hook_fabrics_data {
274 nvme_print_flags_t flags;
275 char *raw;
276};
277
278static bool_Bool hook_decide_retry(struct libnvmf_context *fctx, int err,
279 void *user_data)
280{
281 if (err == -EAGAIN11 || (err == -EINTR4 && !shr_sigint_received)) {
282 print_debug("libnvmf_add_ctrl returned '%s'\n", libnvme_strerror(-err))do { if (is_printable_at_level(LIBNVME_LOG_DEBUG)) printf("libnvmf_add_ctrl returned '%s'\n"
, libnvme_strerror(-err)); } while (0)
;
283 return true1;
284 }
285
286 return false0;
287}
288
289static void hook_connected(struct libnvmf_context *fctx,
290 struct libnvme_ctrl *c, void *user_data)
291{
292 struct hook_fabrics_data *hfd = user_data;
293
294 if (hfd->flags == NORMAL) {
295 nvme_show_verbose_info("connecting to device: %s", libnvme_ctrl_get_name(c))nvme_show_verbose_message("connecting to device: %s", libnvme_ctrl_get_name
(c))
;
296 return;
297 }
298
299#ifdef CONFIG_JSONC
300 if (hfd->flags == JSON) {
301 struct json_object *root;
302
303 root = json_create_object()json_object_new_object();
304
305 json_object_add_value_string(root, "device",
306 libnvme_ctrl_get_name(c));
307
308 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
309 printf("\n");
310 json_free_object(root)json_object_put(root);
311 }
312#endif
313}
314
315static void hook_already_connected(struct libnvmf_context *fctx,
316 struct libnvme_host *host, const char *subsysnqn,
317 const char *transport, const char *traddr,
318 const char *trsvcid, void *user_data)
319{
320 /*
321 * Already-connected is reported through the exit code alone; this is
322 * just extra detail for --verbose, not a message a script should
323 * have to filter out of normal output.
324 */
325 nvme_show_verbose_info(nvme_show_verbose_message("already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s"
, libnvme_host_get_hostnqn(host), subsysnqn, transport, traddr
, trsvcid)
326 "already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s",nvme_show_verbose_message("already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s"
, libnvme_host_get_hostnqn(host), subsysnqn, transport, traddr
, trsvcid)
327 libnvme_host_get_hostnqn(host), subsysnqn,nvme_show_verbose_message("already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s"
, libnvme_host_get_hostnqn(host), subsysnqn, transport, traddr
, trsvcid)
328 transport, traddr, trsvcid)nvme_show_verbose_message("already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s"
, libnvme_host_get_hostnqn(host), subsysnqn, transport, traddr
, trsvcid)
;
329}
330
331static void hook_discovery_log(struct libnvmf_context *fctx,
332 const struct nvmf_discovery_log *log,
333 uint64_t numrec, void *user_data)
334{
335 struct hook_fabrics_data *hfd = user_data;
336
337 if (hfd->raw)
338 save_discovery_log(hfd->raw, log);
339 else if (!libnvmf_context_get_connect(fctx))
340 nvme_show_discovery_log(log, numrec, hfd->flags);
341}
342
343/*
344 * Resolve *addr in place if it names a tcp/rdma hostname; left untouched for
345 * any other transport, a NULL/"none" address, or one that is already
346 * numeric. Resolution is the caller's job, not libnvme's -- this is where
347 * that happens.
348 *
349 * Deliberately crude and sequential: one blocking getaddrinfo() call at a
350 * time, no threads. nvme-cli is a one-shot tool, so multiple discovery.conf
351 * lines simply resolve one after another.
352 */
353/*
354 * Resolve @addr to a canonical (numeric) address when @transport is tcp or
355 * rdma and @addr is a hostname; otherwise just copy it through unchanged.
356 *
357 * On success (0), *resolved always holds a fresh, caller-owned copy of the
358 * address to use from then on -- NULL only if @addr itself was NULL. The
359 * caller never needs to compare it against @addr to work out whether
360 * resolution actually happened: *resolved is always safe to free (e.g.
361 * __cleanup_free) once this returns 0.
362 */
363static int nvmf_resolve_addr(const char *transport, const char *addr, char **resolved)
364{
365 *resolved = NULL((void*)0);
366
367 if (!addr)
17
Assuming 'addr' is non-null
368 return 0;
369
370 if (!transport || (strcmp(transport, "tcp") && strcmp(transport, "rdma")) ||
18
Assuming 'transport' is null
371 !strcmp(addr, "none") || libnvmf_traddr_is_numeric(addr))
372 goto copy_through;
19
Control jumps to line 425
373
374#ifdef NVME_HAVE_NETDB
375 {
376 struct addrinfo hints = { .ai_family = AF_UNSPEC0 };
377 struct addrinfo *host_info = NULL((void*)0);
378 char addrstr[NVMF_TRADDR_SIZE];
379 const char *p = NULL((void*)0);
380 int ret;
381
382 ret = getaddrinfo(addr, NULL((void*)0), &hints, &host_info);
383 if (ret) {
384 nvme_show_error("failed to resolve host '%s': %s",nvme_show_message(1, "failed to resolve host '%s': %s", addr,
gai_strerror(ret))
385 addr, gai_strerror(ret))nvme_show_message(1, "failed to resolve host '%s': %s", addr,
gai_strerror(ret))
;
386 return -EINVAL22;
387 }
388
389 switch (host_info->ai_family) {
390 case AF_INET2:
391 p = inet_ntop(host_info->ai_family,
392 &(((struct sockaddr_in *)host_info->ai_addr)->sin_addr),
393 addrstr, NVMF_TRADDR_SIZE);
394 break;
395 case AF_INET610:
396 p = inet_ntop(host_info->ai_family,
397 &(((struct sockaddr_in6 *)
398 host_info->ai_addr)->sin6_addr),
399 addrstr, NVMF_TRADDR_SIZE);
400 break;
401 default:
402 break;
403 }
404
405 if (!p) {
406 nvme_show_error(nvme_show_message(1, "failed to resolve host '%s': unrecognized address family"
, addr)
407 "failed to resolve host '%s': unrecognized address family",nvme_show_message(1, "failed to resolve host '%s': unrecognized address family"
, addr)
408 addr)nvme_show_message(1, "failed to resolve host '%s': unrecognized address family"
, addr)
;
409 freeaddrinfo(host_info);
410 return -EINVAL22;
411 }
412
413 freeaddrinfo(host_info);
414
415 *resolved = strdup(addrstr);
416 return *resolved ? 0 : -ENOMEM12;
417 }
418#else /* NVME_HAVE_NETDB */
419 nvme_show_error(nvme_show_message(1, "hostname resolution is not available in this build; use a numeric address"
)
420 "hostname resolution is not available in this build; use a numeric address")nvme_show_message(1, "hostname resolution is not available in this build; use a numeric address"
)
;
421 return -ENOTSUP95;
422#endif /* NVME_HAVE_NETDB */
423
424copy_through:
425 *resolved = strdup(addr);
20
Memory is allocated
426 return *resolved ? 0 : -ENOMEM12;
21
Assuming the condition is true
22
'?' condition is true
427}
428
429static int set_fabrics_options(struct libnvmf_context *fctx,
430 struct nvmf_args *fa)
431{
432 libnvmf_context_set_io_queues(fctx, fa->nr_io_queues,
433 fa->nr_write_queues, fa->nr_poll_queues,
434 fa->queue_size, fa->disable_sqflow);
435 libnvmf_context_set_reconnect_policy(fctx, fa->ctrl_loss_tmo,
436 fa->reconnect_delay, fa->fast_io_fail_tmo);
437 libnvmf_context_set_keep_alive_tmo(fctx, fa->keep_alive_tmo);
438 libnvmf_context_set_tos(fctx, fa->tos);
439 libnvmf_context_set_keyring_id(fctx, fa->keyring_id);
440 libnvmf_context_set_tls_key_id(fctx, fa->tls_key_id);
441 libnvmf_context_set_tls_configured_key_id(fctx,
442 fa->tls_configured_key_id);
443 libnvmf_context_set_duplicate_connect(fctx, fa->duplicate_connect);
444 libnvmf_context_set_hdr_digest(fctx, fa->hdr_digest);
445 libnvmf_context_set_data_digest(fctx, fa->data_digest);
446 libnvmf_context_set_tls(fctx, fa->tls);
447 libnvmf_context_set_concat(fctx, fa->concat);
448
449 return 0;
450}
451
452enum consume_mode {
453 /* connect-all/discover: DC entries discover; IOC entries connect,
454 * but only for connect-all.
455 */
456 CONSUME_ROLE_BASED,
457 /* nvme connect -J: every entry connects directly, no discovery. */
458 CONSUME_CONNECT_ONLY,
459};
460
461struct consume_state {
462 struct libnvme_global_ctx *ctx;
463 const struct libnvmf_config *cfg;
464 enum consume_mode mode;
465 bool_Bool connect;
466 bool_Bool no_reuse;
467 nvme_print_flags_t flags;
468 char *raw;
469 const char *hostnqn;
470 const char *hostid;
471 int err;
472};
473
474/*
475 * Resolve traddr (if it's a hostname), settle host identity (falling back
476 * to @default_hostnqn/@default_hostid when @conn's own persona doesn't set
477 * one), then build the TID -- which doubles as validator/sanitizer:
478 * rejects a non-numeric traddr or host_traddr on tcp/rdma, canonicalizes a
479 * numeric one.
480 */
481static int build_conn_tid(const struct libnvmf_config_conn *conn,
482 const char *default_hostnqn, const char *default_hostid,
483 struct libnvmf_tid **tid)
484{
485 const char *transport, *hostnqn, *hostid;
486 __cleanup_free__attribute__((cleanup(shr_freep))) char *traddr = NULL((void*)0);
487 int err;
488
489 transport = libnvmf_config_conn_get_transport(conn);
490
491 /* Only traddr (remote target) may be a hostname (not host_traddr) */
492 err = nvmf_resolve_addr(transport, libnvmf_config_conn_get_traddr(conn), &traddr);
493 if (err)
494 return err;
495
496 hostnqn = libnvmf_config_conn_get_hostnqn(conn);
497 if (!hostnqn)
498 hostnqn = default_hostnqn;
499 hostid = libnvmf_config_conn_get_hostid(conn);
500 if (!hostid)
501 hostid = default_hostid;
502
503 return libnvmf_tid_from_fields(transport, traddr,
504 libnvmf_config_conn_get_trsvcid(conn),
505 libnvmf_config_conn_get_subsysnqn(conn),
506 libnvmf_config_conn_get_host_traddr(conn),
507 libnvmf_config_conn_get_host_iface(conn),
508 hostnqn, hostid, tid);
509}
510
511/*
512 * Return the number of I/O queues on success. Return 0 if the number
513 * cannot be determined or the NIC has only one hardware queue, in
514 * which case the host should determine the number of I/O queues.
515 */
516static int fabrics_connect_nr_io_queues(const char *transport,
517 const char *traddr,
518 const char *host_traddr,
519 const char *host_iface)
520{
521 uint32_t combined_count, tx_count, rx_count;
522 char ifname[IF_NAMESIZE16] = {};
523 int nr_cpus, nr_hw_queues = 0;
524
525 if (!transport || strcmp(transport, "tcp"))
526 return 0;
527
528 nr_cpus = get_nprocs();
529 if (nr_cpus <= 0)
530 return 0;
531
532 if (host_iface && host_iface[0]) {
533 strncpy(ifname, host_iface, IF_NAMESIZE16 - 1);
534 } else {
535 if (shr_route_get_egress_iface(host_traddr, traddr,
536 ifname, IF_NAMESIZE16))
537 return 0;
538 }
539
540 if (shr_netdev_get_hw_queues(ifname, &combined_count,
541 &tx_count, &rx_count))
542 return 0;
543
544 nr_hw_queues = combined_count + min(tx_count, rx_count)({ typeof(tx_count) _a = (tx_count); typeof(rx_count) _b = (rx_count
); do { } while (0); _a < _b ? _a : _b; })
;
545
546 if (nr_hw_queues <= 1)
547 return 0;
548
549 return min(nr_cpus, nr_hw_queues)({ typeof(nr_cpus) _a = (nr_cpus); typeof(nr_hw_queues) _b = (
nr_hw_queues); do { } while (0); _a < _b ? _a : _b; })
;
550}
551
552/* libnvmf_config_conn_for_each() callback: settle addressing/identity,
553 * check exclusion, then discover or connect.
554 */
555static void consume_conn(const struct libnvmf_config_conn *conn,
556 void *user_data)
557{
558 struct consume_state *st = user_data;
559 bool_Bool is_dc = libnvmf_config_conn_is_dc(conn);
560 struct hook_fabrics_data hfd = { .flags = st->flags, .raw = st->raw };
561 __cleanup_nvmf_context__attribute__((cleanup(cleanup_nvmf_context))) struct libnvmf_context *fctx = NULL((void*)0);
562 __cleanup_nvmf_tid__attribute__((cleanup(cleanup_nvmf_tid))) struct libnvmf_tid *tid = NULL((void*)0);
563 const struct libnvmf_params *params;
564 const char *key = "nr-io-queues";
565 int err;
566
567 if (st->mode == CONSUME_ROLE_BASED && !is_dc && !st->connect)
568 return;
569
570 err = build_conn_tid(conn, st->hostnqn, st->hostid, &tid);
571 if (err)
572 goto record_err;
573
574 /* Auto-connect (both modes) enforces exclusion, unlike a single
575 * explicit "nvme connect" (see fabrics_connect()'s exempt-with-note
576 * case).
577 */
578 if (libnvmf_exclusion_match(st->ctx, tid)) {
579 nvme_show_verbose_info("%s: on the exclusion list, skipping",nvme_show_verbose_message("%s: on the exclusion list, skipping"
, libnvmf_config_conn_get_source(conn))
580 libnvmf_config_conn_get_source(conn))nvme_show_verbose_message("%s: on the exclusion list, skipping"
, libnvmf_config_conn_get_source(conn))
;
581 return;
582 }
583
584 err = libnvmf_context_create(st->ctx, hook_decide_retry, hook_connected,
585 hook_already_connected, &hfd, &fctx);
586 if (err)
587 goto record_err;
588
589 err = libnvmf_context_set_connection_from_tid(fctx, tid);
590 if (err)
591 goto record_err;
592
593 params = libnvmf_config_conn_get_params(conn);
594 if (!libnvmf_params_get(params, key)) {
595 int nr_io_queues;
596
597 nr_io_queues = fabrics_connect_nr_io_queues(
598 libnvmf_tid_get_transport(tid),
599 libnvmf_tid_get_traddr(tid),
600 libnvmf_tid_get_host_traddr(tid),
601 libnvmf_tid_get_host_iface(tid));
602
603 if (nr_io_queues > 0)
604 libnvmf_context_set_nr_io_queues(fctx, nr_io_queues);
605 }
606
607 err = libnvmf_context_apply_params(fctx, params);
608 if (err)
609 goto record_err;
610
611 if (st->mode == CONSUME_CONNECT_ONLY) {
612 err = libnvmf_connect(st->ctx, fctx);
613 } else if (is_dc) {
614 libnvmf_context_set_discovery_hooks(fctx, hook_discovery_log);
615 libnvmf_context_set_default_max_discovery_retries(fctx,
616 MAX_DISC_RETRIES10);
617 libnvmf_context_set_default_keep_alive_timeout(fctx,
618 NVMF_DEF_DISC_TMO30);
619 libnvmf_context_set_connect(fctx, st->connect);
620 libnvmf_context_set_no_reuse(fctx, st->no_reuse);
621 libnvmf_context_set_config(fctx, st->cfg, conn);
622 err = libnvmf_discover(st->ctx, fctx);
623 } else {
624 err = libnvmf_connect(st->ctx, fctx);
625 }
626 if (err == -ENVME_CONNECT_ALREADY || err == -ENVME_CONNECT_DRY_RUN)
627 err = 0;
628
629record_err:
630 if (err) {
631 nvme_show_error("%s: %s", libnvmf_config_conn_get_source(conn),nvme_show_message(1, "%s: %s", libnvmf_config_conn_get_source
(conn), libnvme_strerror(-err))
632 libnvme_strerror(-err))nvme_show_message(1, "%s: %s", libnvmf_config_conn_get_source
(conn), libnvme_strerror(-err))
;
633 if (!st->err)
634 st->err = err;
635 }
636}
637
638const char *nvmf_resolve_persistent_arg(bool_Bool given, const char *arg)
639{
640 if (!given)
641 return "auto";
642
643 return arg ? arg : "auto";
644}
645
646/*
647 * Parse one discovery.conf line -- the same argv-style syntax 'nvme
648 * discover'/'connect-all' accept, reusing nvmf_options() so every short
649 * and long form works identically to real usage -- and hand the parsed
650 * arguments to nvme_config_convert_discovery_args(). @line is modified in
651 * place (shr_strsep()).
652 *
653 * A blank line, a comment, or one with neither transport nor traddr is
654 * silently skipped (0, nothing added), tolerating a discovery.conf that
655 * mixes real entries with commentary.
656 */
657int nvmf_convert_discovery_line(struct libnvmf_config_emitter *emitter,
658 char *line)
659{
660 struct nvmf_args fa = nvmf_args_defaults();
661 __cleanup_free__attribute__((cleanup(shr_freep))) struct argconfig_commandline_options *nvmf =
662 nvmf_options(&fa);
663 char *argv[MAX_DISC_ARGS32] = { "discovery.conf" };
664 char *ptr, *p = line;
665 int argc = 1;
666 bool_Bool no_reuse = false0;
667 char *persistent_arg = NULL((void*)0);
668
669 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
670 OPT_INCLUDE(nvmf, "Fabrics options"){"", 0, ((void*)0), CFG_INCLUDE, nvmf, ARGCONFIG_ARG_NONE, "Fabrics options"
, 0, ((void*)0)}
,
671 OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force",{"persistent", 'p', "no|auto|force", CFG_STRING, _Generic(&
persistent_arg, char ** : (&persistent_arg), const char *
* : (&persistent_arg)), ARGCONFIG_ARG_OPTIONAL, nvmf_persistent
, 0, }
672 &persistent_arg, nvmf_persistent){"persistent", 'p', "no|auto|force", CFG_STRING, _Generic(&
persistent_arg, char ** : (&persistent_arg), const char *
* : (&persistent_arg)), ARGCONFIG_ARG_OPTIONAL, nvmf_persistent
, 0, }
,
673 OPT_FLAG("no-reuse", 0, &no_reuse,{"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
674 "always create a new connection, never reuse an existing one",{"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
675 .old_name = "force"){"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
,
676 OPT_END(){ ((void*)0) }
677 };
678
679 if (line[0] == '#' || line[0] == '\n' || line[0] == '\0')
680 return 0;
681
682 if (!nvmf)
683 return -ENOMEM12;
684
685 /*
686 * Split on whitespace only, not '=' -- a glued "--opt=value" token
687 * (e.g. "--persistent=force") must reach argconfig_parse() intact.
688 * getopt_long() parses '=' within a single token natively for
689 * every option shape, so splitting it out here is never required.
690 */
691 while ((ptr = shr_strsep(&p, "\n ")) != NULL((void*)0) &&
692 argc < MAX_DISC_ARGS32 - 1)
693 argv[argc++] = ptr;
694 argv[argc] = NULL((void*)0);
695
696 fa.subsysnqn = NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery";
697 if (argconfig_parse(argc, argv, "discovery.conf", opts))
698 return 0;
699
700 if (!fa.transport && !fa.traddr)
701 return 0;
702
703 return nvme_config_convert_discovery_args(emitter, &fa,
704 nvmf_resolve_persistent_arg(
705 argconfig_given(opts, &persistent_arg),
706 persistent_arg));
707}
708
709static int setup_common_context(struct libnvmf_context *fctx,
710 struct nvmf_args *fa)
711{
712 int err;
713
714 err = libnvmf_context_set_connection(fctx,
715 fa->subsysnqn, fa->transport,
716 fa->traddr, fa->trsvcid,
717 fa->host_traddr, fa->host_iface);
718 if (err)
719 return err;
720
721 err = libnvmf_context_set_hostnqn(fctx,
722 fa->hostnqn, fa->hostid);
723 if (err)
724 return err;
725
726 err = libnvmf_context_set_crypto(fctx,
727 fa->hostkey, fa->ctrlkey,
728 fa->keyring, fa->tls_key,
729 fa->tls_key_identity);
730 if (err)
731 return err;
732
733 return set_fabrics_options(fctx, fa);
734}
735
736static int create_common_context(struct libnvme_global_ctx *ctx, struct nvmf_args *fa,
737 void *user_data, struct libnvmf_context **fctxp)
738{
739 struct libnvmf_context *fctx;
740 int err;
741
742 err = libnvmf_context_create(ctx, hook_decide_retry, hook_connected,
743 hook_already_connected, user_data, &fctx);
744 if (err)
745 return err;
746
747 err = setup_common_context(fctx, fa);
748 if (err)
749 goto err;
750
751 *fctxp = fctx;
752
753 return 0;
754
755err:
756 libnvmf_context_free(fctx);
757 return err;
758}
759
760static int create_discovery_context(struct libnvme_global_ctx *ctx,
761 const char *device, struct nvmf_args *fa,
762 void *user_data, struct libnvmf_context **fctxp)
763{
764 struct libnvmf_context *fctx;
765 int err;
766
767 err = create_common_context(ctx, fa, user_data, &fctx);
768 if (err)
769 return err;
770
771 err = libnvmf_context_set_discovery_hooks(fctx, hook_discovery_log);
772 if (err)
773 goto err;
774
775 libnvmf_context_set_default_max_discovery_retries(fctx,
776 MAX_DISC_RETRIES10);
777 libnvmf_context_set_default_keep_alive_timeout(fctx, NVMF_DEF_DISC_TMO30);
778
779 err = libnvmf_context_set_device(fctx, device);
780 if (err)
781 goto err;
782
783 *fctxp = fctx;
784 return 0;
785
786err:
787 libnvmf_context_free(fctx);
788 return err;
789}
790
791static void load_nvme_fabrics_module(void)
792{
793#ifdef NVME_HAVE_LIBKMOD
794 struct kmod_ctx *ctx;
795 struct kmod_module *mod;
796 int err, state;
797 int timeout = 20; /* 2 seconds */
798
799 ctx = kmod_new(NULL((void*)0), NULL((void*)0));
800 if (!ctx)
801 return;
802
803 err = kmod_module_new_from_name(ctx, "nvme-fabrics", &mod);
804 if (err)
805 goto unref;
806
807 state = kmod_module_get_initstate(mod);
808 if (state != KMOD_MODULE_LIVE && state != KMOD_MODULE_BUILTIN) {
809 err = kmod_module_probe_insert_module(mod,
810 KMOD_PROBE_APPLY_BLACKLIST, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0));
811 if (err)
812 goto mod_unref;
813
814 while (timeout--) {
815 state = kmod_module_get_initstate(mod);
816 if (state == KMOD_MODULE_LIVE)
817 goto mod_unref;
818
819 /* 100 ms */
820 usleep(100 * 1000);
821 }
822 err = -ENOENT2;
823 }
824
825mod_unref:
826 kmod_module_unref(mod);
827unref:
828 kmod_unref(ctx);
829
830 if (err)
831 nvme_show_error("Couldn't load the nvme-fabrics module")nvme_show_message(1, "Couldn't load the nvme-fabrics module");
832#endif
833}
834
835/*
836 * The config-driven half of a bare "connect-all"/"discover": auto-convert
837 * a legacy file if needed, settle host identity once, then discover or
838 * connect every resolved connection in @config_file.
839 */
840static int fabrics_discovery_config(struct libnvme_global_ctx *ctx,
841 char *config_file, const char *hostnqn_arg,
842 const char *hostid_arg, bool_Bool connect, bool_Bool no_reuse,
843 nvme_print_flags_t flags)
844{
845 __cleanup_free__attribute__((cleanup(shr_freep))) char *ini_path = NULL((void*)0);
846 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostnqn = NULL((void*)0);
847 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostid = NULL((void*)0);
848 struct libnvmf_config *cfg;
849 struct consume_state st;
850 bool_Bool is_default_config;
851 int ret;
852
853 /* Custom --config failing is fatal (explicit ask). Default path
854 * failing isn't: boot-time connect-all must keep going.
855 */
856 is_default_config = !strcmp(config_file, PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf");
857
858 ret = nvme_config_convert_auto(ctx, config_file, &ini_path);
859 if (ret) {
860 nvme_show_error("auto-conversion failed: %s",nvme_show_message(1, "auto-conversion failed: %s", libnvme_strerror
(-ret))
861 libnvme_strerror(-ret))nvme_show_message(1, "auto-conversion failed: %s", libnvme_strerror
(-ret))
;
862 if (!is_default_config)
863 return ret;
864 }
865 config_file = ini_path;
866
867 ret = libnvmf_host_get_ids(ctx, hostnqn_arg, hostid_arg,
868 &hostnqn, &hostid);
869 if (ret) {
870 nvme_show_error("failed to determine host identity: %s",nvme_show_message(1, "failed to determine host identity: %s",
libnvme_strerror(-ret))
871 libnvme_strerror(-ret))nvme_show_message(1, "failed to determine host identity: %s",
libnvme_strerror(-ret))
;
872 return ret;
873 }
874
875 ret = libnvmf_config_read(ctx, config_file, &cfg);
876 if (ret) {
877 nvme_show_error("failed to read %s: %s", config_file,nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
878 libnvme_strerror(-ret))nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
;
879 return ret;
880 }
881
882 st = (struct consume_state){
883 .ctx = ctx,
884 .cfg = cfg,
885 .mode = CONSUME_ROLE_BASED,
886 .connect = connect,
887 .no_reuse = no_reuse,
888 .flags = flags,
889 .raw = raw,
890 .hostnqn = hostnqn,
891 .hostid = hostid,
892 };
893 libnvmf_config_conn_for_each(cfg, consume_conn, &st);
894 libnvmf_config_free(cfg);
895
896 return st.err;
897}
898
899#define NBFT_SYSFS_PATH"/sys/firmware/acpi/tables" "/sys/firmware/acpi/tables"
900
901/*
902 * A controller's ownership extends to everything done through it, so the
903 * caller's operation may not proceed unless the invoking owner matches
904 * (mirrors disconnect_all_match()'s registry check). A controller with
905 * no owner proceeds for any caller. A controller with an owner is
906 * skipped unless --owner names that owner; a missing --owner does not
907 * match.
908 *
909 * --no-reuse skips the check entirely: it means the caller will never
910 * reuse an existing controller, so there is nothing to check ownership
911 * against.
912 *
913 * Returns 0 to proceed, 1 to skip, or a negative errno on a registry
914 * read failure.
915 */
916static int check_ctrl_owner(struct libnvme_global_ctx *ctx,
917 struct libnvmf_context *fctx,
918 const char *owner, bool_Bool no_reuse)
919{
920 __cleanup_free__attribute__((cleanup(shr_freep))) char *reg_owner = NULL((void*)0);
921 int ret;
922
923 if (no_reuse)
924 return 0;
925
926 ret = libnvmf_get_owner_from_fctx(ctx, fctx, &reg_owner);
927 if (ret)
928 return ret;
929 if (!reg_owner) /* no owner in registry */
930 return 0;
931
932 if (owner && streq(reg_owner, owner)(strcmp((reg_owner),(owner)) == 0))
933 return 0;
934
935 nvme_show_message(false0,
936 "owned by '%s'; skipping -- owner handles discovery",
937 reg_owner);
938 return 1;
939}
940
941int fabrics_discover(const char *desc, int argc, char **argv, bool_Bool connect)
942{
943 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
944 __cleanup_free__attribute__((cleanup(shr_freep))) char *hid = NULL((void*)0);
945 char *config_file = PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf";
946 nvme_print_flags_t flags;
947 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
948 __cleanup_nvmf_context__attribute__((cleanup(cleanup_nvmf_context))) struct libnvmf_context *fctx = NULL((void*)0);
949 __cleanup_free__attribute__((cleanup(shr_freep))) char *resolved_traddr = NULL((void*)0);
950 int ret;
951 struct nvmf_args fa = nvmf_args_defaults();
952 __cleanup_free__attribute__((cleanup(shr_freep))) struct argconfig_commandline_options *nvmf =
953 nvmf_options(&fa);
954 char *device = NULL((void*)0);
955 bool_Bool no_reuse = false0;
956 char *persistent_arg = NULL((void*)0);
957 const char *persistent;
958 bool_Bool nbft = false0, nonbft = false0;
959 char *nbft_path = NBFT_SYSFS_PATH"/sys/firmware/acpi/tables";
960 char *owner = NULL((void*)0);
961
962 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
963 OPT_INCLUDE(nvmf, "Fabrics options"){"", 0, ((void*)0), CFG_INCLUDE, nvmf, ARGCONFIG_ARG_NONE, "Fabrics options"
, 0, ((void*)0)}
,
964 OPT_STRING("device", 'd', "DEV", &device,{"device", 'd', "DEV", CFG_STRING, _Generic(&device, char
** : (&device), const char ** : (&device)), ARGCONFIG_ARG_REQUIRED
, "use existing discovery controller device", 0, }
965 "use existing discovery controller device"){"device", 'd', "DEV", CFG_STRING, _Generic(&device, char
** : (&device), const char ** : (&device)), ARGCONFIG_ARG_REQUIRED
, "use existing discovery controller device", 0, }
,
966 OPT_FILE("raw", 'r', &raw, "save raw output to file"){"raw", 'r', "FILE", CFG_FILE, _Generic(&raw, char ** : (
&raw), const char ** : (&raw)), ARGCONFIG_ARG_REQUIRED
, "save raw output to file", 0, }
,
967 OPT_STRING_OPTIONAL("persistent", 'p', "no|auto|force",{"persistent", 'p', "no|auto|force", CFG_STRING, _Generic(&
persistent_arg, char ** : (&persistent_arg), const char *
* : (&persistent_arg)), ARGCONFIG_ARG_OPTIONAL, nvmf_persistent
, 0, }
968 &persistent_arg, nvmf_persistent){"persistent", 'p', "no|auto|force", CFG_STRING, _Generic(&
persistent_arg, char ** : (&persistent_arg), const char *
* : (&persistent_arg)), ARGCONFIG_ARG_OPTIONAL, nvmf_persistent
, 0, }
,
969 OPT_FILE("config", 'J', &config_file, nvmf_config_file){"config", 'J', "FILE", CFG_FILE, _Generic(&config_file, char
** : (&config_file), const char ** : (&config_file))
, ARGCONFIG_ARG_REQUIRED, nvmf_config_file, 0, }
,
970 OPT_FLAG("no-reuse", 0, &no_reuse,{"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
971 "always create a new connection, never reuse an existing one",{"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
972 .old_name = "force"){"no-reuse", 0, ((void*)0), CFG_FLAG, _Generic(&no_reuse,
_Bool * : (&no_reuse)), ARGCONFIG_ARG_NONE, "always create a new connection, never reuse an existing one"
, 0, .old_name = "force"}
,
973 OPT_FLAG("nbft", 0, &nbft, "Only look at NBFT tables"){"nbft", 0, ((void*)0), CFG_FLAG, _Generic(&nbft, _Bool *
: (&nbft)), ARGCONFIG_ARG_NONE, "Only look at NBFT tables"
, 0, }
,
974 OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"){"no-nbft", 0, ((void*)0), CFG_FLAG, _Generic(&nonbft, _Bool
* : (&nonbft)), ARGCONFIG_ARG_NONE, "Do not look at NBFT tables"
, 0, }
,
975 OPT_STRING("owner", 0, "NAME", &owner,{"owner", 0, "NAME", CFG_STRING, _Generic(&owner, char **
: (&owner), const char ** : (&owner)), ARGCONFIG_ARG_REQUIRED
, "record this owner in the registry", 0, }
976 "record this owner in the registry"){"owner", 0, "NAME", CFG_STRING, _Generic(&owner, char **
: (&owner), const char ** : (&owner)), ARGCONFIG_ARG_REQUIRED
, "record this owner in the registry", 0, }
,
977 OPT_DIR("nbft-path", 0, &nbft_path,{"nbft-path", 0, "DIRECTORY", CFG_DIR, _Generic(&nbft_path
, char ** : (&nbft_path), const char ** : (&nbft_path
)), ARGCONFIG_ARG_REQUIRED, "user-defined path for NBFT tables"
, 0, }
978 "user-defined path for NBFT tables"){"nbft-path", 0, "DIRECTORY", CFG_DIR, _Generic(&nbft_path
, char ** : (&nbft_path), const char ** : (&nbft_path
)), ARGCONFIG_ARG_REQUIRED, "user-defined path for NBFT tables"
, 0, }
,
979 OPT_END(){ ((void*)0) }
980 };
981
982 fa.subsysnqn = NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery";
983 if (!nvmf)
1
Assuming 'nvmf' is non-null
2
Taking false branch
984 return -ENOMEM12;
985
986 ret = parse_args(argc, argv, desc, opts);
987 if (ret)
3
Assuming 'ret' is 0
4
Taking false branch
988 return ret;
989
990 load_nvme_fabrics_module();
991
992 flags = nvme_args.output_flags;
993
994 persistent = nvmf_resolve_persistent_arg(
995 argconfig_given(opts, &persistent_arg), persistent_arg);
996
997 if (!strcmp(config_file, "none"))
5
Assuming the condition is false
6
Taking false branch
998 config_file = NULL((void*)0);
999
1000 ret = nvme_create_global_ctx_hostnqn(&ctx,
1001 fa.hostnqn, fa.hostid, &hnqn, &hid);
1002 if (ret)
7
Assuming 'ret' is 0
8
Taking false branch
1003 return ret;
1004 fa.hostnqn = hnqn;
1005 fa.hostid = hid;
1006
1007 /*
1008 * --nbft defaults the owner to "nbft" so legacy boot scripts that
1009 * call "connect-all --nbft" record ownership unchanged. An explicit
1010 * --owner overrides that default.
1011 */
1012 if (owner || nbft) {
9
Assuming 'owner' is null
10
Assuming 'nbft' is false
11
Taking false branch
1013 ret = libnvme_set_owner(ctx, owner ? owner : "nbft");
1014 if (ret) {
1015 nvme_show_error("failed to set owner: %s",nvme_show_message(1, "failed to set owner: %s", libnvme_strerror
(-ret))
1016 libnvme_strerror(-ret))nvme_show_message(1, "failed to set owner: %s", libnvme_strerror
(-ret))
;
1017 return ret;
1018 }
1019 }
1020
1021 libnvme_skip_namespaces(ctx);
1022 ret = libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0));
1023 if (ret < 0) {
12
Assuming 'ret' is >= 0
13
Taking false branch
1024 nvme_show_error("Failed to scan topology: %s",nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
1025 libnvme_strerror(-ret))nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
;
1026 return ret;
1027 }
1028
1029 if (device) {
14
Assuming 'device' is null
15
Taking false branch
1030 if (!strcmp(device, "none"))
1031 device = NULL((void*)0);
1032 else if (!strncmp(device, "/dev/", 5))
1033 device += 5;
1034 }
1035
1036 /* Only traddr may be a hostname; host_traddr never is. */
1037 ret = nvmf_resolve_addr(fa.transport, fa.traddr, &resolved_traddr);
16
Calling 'nvmf_resolve_addr'
23
Returned allocated memory via 3rd parameter
1038 if (ret
23.1
'ret' is 0
)
24
Taking false branch
1039 return ret;
1040 fa.traddr = resolved_traddr;
1041
1042 struct hook_fabrics_data dld = {
1043 .flags = flags,
1044 .raw = raw,
1045 };
1046
1047 ret = create_discovery_context(ctx, device, &fa, &dld, &fctx);
1048 if (ret
24.1
'ret' is not equal to 0
)
25
Taking true branch
1049 return ret;
26
Potential leak of memory pointed to by 'fa.traddr'
1050
1051 libnvmf_context_set_connect(fctx, connect);
1052 libnvmf_context_set_no_reuse(fctx, no_reuse);
1053
1054 if (persistent && libnvmf_context_set_persistent(fctx, persistent)) {
1055 nvme_show_error(nvme_show_message(1, "invalid --persistent value '%s' (expected no, auto, or force)"
, persistent)
1056 "invalid --persistent value '%s' (expected no, auto, or force)",nvme_show_message(1, "invalid --persistent value '%s' (expected no, auto, or force)"
, persistent)
1057 persistent)nvme_show_message(1, "invalid --persistent value '%s' (expected no, auto, or force)"
, persistent)
;
1058 return -EINVAL22;
1059 }
1060
1061 if (!device && !fa.transport && !fa.traddr) {
1062 if (!nonbft) {
1063 if (!nbft_path) {
1064 nvme_show_error("no nbft_path set")nvme_show_message(1, "no nbft_path set");
1065 return -EINVAL22;
1066 }
1067 libnvmf_context_set_nbft_path(fctx, nbft_path);
1068 ret = libnvmf_discover_nbft(ctx, fctx);
1069 }
1070 if (!nbft && config_file)
1071 ret = fabrics_discovery_config(ctx, config_file,
1072 fa.hostnqn, fa.hostid, connect, no_reuse,
1073 flags);
1074 } else {
1075 ret = check_ctrl_owner(ctx, fctx,
1076 owner ? owner : (nbft ? "nbft" : NULL((void*)0)),
1077 no_reuse);
1078 if (ret < 0) {
1079 nvme_show_error("failed to check owner: %s",nvme_show_message(1, "failed to check owner: %s", libnvme_strerror
(-ret))
1080 libnvme_strerror(-ret))nvme_show_message(1, "failed to check owner: %s", libnvme_strerror
(-ret))
;
1081 return ret;
1082 }
1083 if (ret)
1084 return 0;
1085
1086 ret = libnvmf_discover(ctx, fctx);
1087 if (ret == -ENVME_CONNECT_DRY_RUN) {
1088 nvme_show_error(nvme_show_message(1, "dry-run: the discovery needs a connection to the discovery controller, nothing discovered"
)
1089 "dry-run: the discovery needs a connection to the discovery controller, nothing discovered")nvme_show_message(1, "dry-run: the discovery needs a connection to the discovery controller, nothing discovered"
)
;
1090 ret = 0;
1091 }
1092 }
1093
1094 return ret;
1095}
1096
1097/*
1098 * The config-driven half of "nvme connect -J": auto-convert a legacy file
1099 * if needed, settle host identity once, then connect every resolved
1100 * connection in @config_file.
1101 */
1102static int fabrics_connect_config(struct libnvme_global_ctx *ctx,
1103 char *config_file, const char *hostnqn_arg,
1104 const char *hostid_arg, nvme_print_flags_t flags)
1105{
1106 __cleanup_free__attribute__((cleanup(shr_freep))) char *ini_path = NULL((void*)0);
1107 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostnqn = NULL((void*)0);
1108 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostid = NULL((void*)0);
1109 struct libnvmf_config *cfg;
1110 struct consume_state st;
1111 int ret;
1112
1113 /* Always explicit here (no implicit default read), so a
1114 * conversion failure is always fatal.
1115 */
1116 ret = nvme_config_convert_auto(ctx, config_file, &ini_path);
1117 if (ret) {
1118 nvme_show_error("auto-conversion failed: %s",nvme_show_message(1, "auto-conversion failed: %s", libnvme_strerror
(-ret))
1119 libnvme_strerror(-ret))nvme_show_message(1, "auto-conversion failed: %s", libnvme_strerror
(-ret))
;
1120 return ret;
1121 }
1122 config_file = ini_path;
1123
1124 ret = libnvmf_host_get_ids(ctx, hostnqn_arg, hostid_arg,
1125 &hostnqn, &hostid);
1126 if (ret) {
1127 nvme_show_error("failed to determine host identity: %s",nvme_show_message(1, "failed to determine host identity: %s",
libnvme_strerror(-ret))
1128 libnvme_strerror(-ret))nvme_show_message(1, "failed to determine host identity: %s",
libnvme_strerror(-ret))
;
1129 return ret;
1130 }
1131
1132 ret = libnvmf_config_read(ctx, config_file, &cfg);
1133 if (ret) {
1134 nvme_show_error("failed to read %s: %s", config_file,nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
1135 libnvme_strerror(-ret))nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
;
1136 return ret;
1137 }
1138
1139 st = (struct consume_state){
1140 .ctx = ctx,
1141 .mode = CONSUME_CONNECT_ONLY,
1142 .flags = flags,
1143 .raw = raw,
1144 .hostnqn = hostnqn,
1145 .hostid = hostid,
1146 };
1147 libnvmf_config_conn_for_each(cfg, consume_conn, &st);
1148 libnvmf_config_free(cfg);
1149
1150 return st.err;
1151}
1152
1153int fabrics_connect(const char *desc, int argc, char **argv)
1154{
1155 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
1156 __cleanup_free__attribute__((cleanup(shr_freep))) char *hid = NULL((void*)0);
1157 char *config_file = NULL((void*)0);
1158 char *owner = NULL((void*)0);
1159 char *devid_file = NULL((void*)0);
1160 bool_Bool idempotent = false0;
1161 bool_Bool discovery = false0;
1162 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1163 __cleanup_nvmf_context__attribute__((cleanup(cleanup_nvmf_context))) struct libnvmf_context *fctx = NULL((void*)0);
1164 __cleanup_nvme_ctrl__attribute__((cleanup(cleanup_nvme_ctrl))) struct libnvme_ctrl *c = NULL((void*)0);
1165 __cleanup_free__attribute__((cleanup(shr_freep))) char *resolved_traddr = NULL((void*)0);
1166 int ret;
1167 nvme_print_flags_t flags;
1168 struct nvmf_args fa = nvmf_args_defaults();
1169 __cleanup_free__attribute__((cleanup(shr_freep))) struct argconfig_commandline_options *nvmf =
1170 nvmf_options(&fa);
1171
1172 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1173 OPT_INCLUDE(nvmf, "Fabrics options"){"", 0, ((void*)0), CFG_INCLUDE, nvmf, ARGCONFIG_ARG_NONE, "Fabrics options"
, 0, ((void*)0)}
,
1174 OPT_FILE("config", 'J', &config_file, nvmf_config_file){"config", 'J', "FILE", CFG_FILE, _Generic(&config_file, char
** : (&config_file), const char ** : (&config_file))
, ARGCONFIG_ARG_REQUIRED, nvmf_config_file, 0, }
,
1175 OPT_STRING("owner", 0, "NAME", &owner,{"owner", 0, "NAME", CFG_STRING, _Generic(&owner, char **
: (&owner), const char ** : (&owner)), ARGCONFIG_ARG_REQUIRED
, "record this owner in the registry", 0, }
1176 "record this owner in the registry"){"owner", 0, "NAME", CFG_STRING, _Generic(&owner, char **
: (&owner), const char ** : (&owner)), ARGCONFIG_ARG_REQUIRED
, "record this owner in the registry", 0, }
,
1177 OPT_FILE("devid-file", 0, &devid_file,{"devid-file", 0, "FILE", CFG_FILE, _Generic(&devid_file,
char ** : (&devid_file), const char ** : (&devid_file
)), ARGCONFIG_ARG_REQUIRED, "write connected device name to FILE"
, 0, }
1178 "write connected device name to FILE"){"devid-file", 0, "FILE", CFG_FILE, _Generic(&devid_file,
char ** : (&devid_file), const char ** : (&devid_file
)), ARGCONFIG_ARG_REQUIRED, "write connected device name to FILE"
, 0, }
,
1179 OPT_FLAG("idempotent", 0, &idempotent,{"idempotent", 0, ((void*)0), CFG_FLAG, _Generic(&idempotent
, _Bool * : (&idempotent)), ARGCONFIG_ARG_NONE, "exit 0 if already connected"
, 0, }
1180 "exit 0 if already connected"){"idempotent", 0, ((void*)0), CFG_FLAG, _Generic(&idempotent
, _Bool * : (&idempotent)), ARGCONFIG_ARG_NONE, "exit 0 if already connected"
, 0, }
,
1181 OPT_FLAG("discovery", 0, &discovery,{"discovery", 0, ((void*)0), CFG_FLAG, _Generic(&discovery
, _Bool * : (&discovery)), ARGCONFIG_ARG_NONE, "the target is a discovery controller"
, 0, }
1182 "the target is a discovery controller"){"discovery", 0, ((void*)0), CFG_FLAG, _Generic(&discovery
, _Bool * : (&discovery)), ARGCONFIG_ARG_NONE, "the target is a discovery controller"
, 0, }
,
1183 OPT_END(){ ((void*)0) }
1184 };
1185
1186 if (!nvmf)
1187 return -ENOMEM12;
1188
1189 ret = parse_args(argc, argv, desc, opts);
1190 if (ret)
1191 return ret;
1192
1193 load_nvme_fabrics_module();
1194
1195 flags = nvme_args.output_flags;
1196
1197 if (config_file && !strcmp(config_file, "none"))
1198 config_file = NULL((void*)0);
1199
1200 if (config_file)
1201 goto do_connect;
1202
1203 if (!fa.subsysnqn) {
1204 nvme_show_error(nvme_show_message(1, "required argument [--nqn | -n] not specified\n"
)
1205 "required argument [--nqn | -n] not specified\n")nvme_show_message(1, "required argument [--nqn | -n] not specified\n"
)
;
1206 return -EINVAL22;
1207 }
1208
1209 if (!fa.transport) {
1210 nvme_show_error(nvme_show_message(1, "required argument [--transport | -t] not specified\n"
)
1211 "required argument [--transport | -t] not specified\n")nvme_show_message(1, "required argument [--transport | -t] not specified\n"
)
;
1212 return -EINVAL22;
1213 }
1214
1215 if (strcmp(fa.transport, "loop")) {
1216 if (!fa.traddr) {
1217 nvme_show_error(nvme_show_message(1, "required argument [--traddr | -a] not specified for transport %s\n"
, fa.transport)
1218 "required argument [--traddr | -a] not specified for transport %s\n",nvme_show_message(1, "required argument [--traddr | -a] not specified for transport %s\n"
, fa.transport)
1219 fa.transport)nvme_show_message(1, "required argument [--traddr | -a] not specified for transport %s\n"
, fa.transport)
;
1220 return -EINVAL22;
1221 }
1222 }
1223
1224 /* Only traddr may be a hostname; host_traddr never is. */
1225 ret = nvmf_resolve_addr(fa.transport, fa.traddr, &resolved_traddr);
1226 if (ret)
1227 return ret;
1228 fa.traddr = resolved_traddr;
1229
1230 if (!fa.nr_io_queues)
1231 fa.nr_io_queues = fabrics_connect_nr_io_queues(fa.transport,
1232 fa.traddr, fa.host_traddr, fa.host_iface);
1233do_connect:
1234 ret = nvme_create_global_ctx_hostnqn(&ctx,
1235 fa.hostnqn, fa.hostid, &hnqn, &hid);
1236 if (ret)
1237 return ret;
1238 fa.hostnqn = hnqn;
1239 fa.hostid = hid;
1240
1241 if (owner) {
1242 ret = libnvme_set_owner(ctx, owner);
1243 if (ret) {
1244 nvme_show_error("failed to set owner: %s",nvme_show_message(1, "failed to set owner: %s", libnvme_strerror
(-ret))
1245 libnvme_strerror(-ret))nvme_show_message(1, "failed to set owner: %s", libnvme_strerror
(-ret))
;
1246 return ret;
1247 }
1248 }
1249
1250 libnvme_skip_namespaces(ctx);
1251 ret = libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0));
1252 if (ret < 0) {
1253 nvme_show_error("Failed to scan topology: %s",nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
1254 libnvme_strerror(-ret))nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
;
1255 return ret;
1256 }
1257
1258 if (config_file)
1259 return fabrics_connect_config(ctx, config_file, fa.hostnqn,
1260 fa.hostid, flags);
1261
1262 struct hook_fabrics_data hfd = {
1263 .flags = flags,
1264 .raw = raw,
1265 };
1266 ret = create_common_context(ctx, &fa, &hfd, &fctx);
1267 if (ret)
1268 return ret;
1269
1270 if (devid_file)
1271 libnvmf_context_set_devid_file(fctx, devid_file);
1272 libnvmf_context_set_discovery_ctrl(fctx, discovery);
1273
1274 /*
1275 * The exclusion list governs auto-connecting orchestrators, not an
1276 * explicit "nvme connect", so we never block it here. But under
1277 * --verbose, note when the target matches an exclusion entry so the
1278 * operator knows they are overriding their own opt-out.
1279 */
1280 if (nvme_args.verbose) {
1281 struct libnvmf_tid *tid;
1282
1283 libnvmf_tid_from_fields(fa.transport, fa.traddr,
1284 fa.trsvcid, fa.subsysnqn,
1285 fa.host_traddr, fa.host_iface,
1286 fa.hostnqn, fa.hostid, &tid);
1287 if (tid && libnvmf_exclusion_match(ctx, tid))
1288 nvme_show_error(nvme_show_message(1, "Note: %s is on the exclusion list; connecting anyway\n"
, fa.subsysnqn ? fa.subsysnqn : "this controller")
1289 "Note: %s is on the exclusion list; connecting anyway\n",nvme_show_message(1, "Note: %s is on the exclusion list; connecting anyway\n"
, fa.subsysnqn ? fa.subsysnqn : "this controller")
1290 fa.subsysnqn ? fa.subsysnqn : "this controller")nvme_show_message(1, "Note: %s is on the exclusion list; connecting anyway\n"
, fa.subsysnqn ? fa.subsysnqn : "this controller")
;
1291 libnvmf_tid_free(tid);
1292 }
1293
1294 ret = libnvmf_connect(ctx, fctx);
1295 if (ret == -ENVME_CONNECT_ALREADY && idempotent)
1296 ret = 0;
1297 if (ret == -ENVME_CONNECT_DRY_RUN)
1298 ret = 0;
1299 if (ret) {
1300 /*
1301 * hook_already_connected() already reported the specific
1302 * reason; the generic message here would just overwrite it.
1303 */
1304 if (ret != -ENVME_CONNECT_ALREADY)
1305 nvme_show_error("failed to connect: %s",nvme_show_message(1, "failed to connect: %s", libnvme_strerror
(-ret))
1306 libnvme_strerror(-ret))nvme_show_message(1, "failed to connect: %s", libnvme_strerror
(-ret))
;
1307 return ret;
1308 }
1309
1310 return 0;
1311}
1312
1313static struct libnvme_ctrl *lookup_nvme_ctrl(struct libnvme_global_ctx *ctx,
1314 const char *name)
1315{
1316 struct libnvme_host *h;
1317 struct libnvme_subsystem *s;
1318 struct libnvme_ctrl *c;
1319
1320 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1321 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1322 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1323 if (!strcmp(libnvme_ctrl_get_name(c), name))
1324 return c;
1325 }
1326 }
1327 }
1328 return NULL((void*)0);
1329}
1330
1331static bool_Bool opt_matches(const char *want, const char *have)
1332{
1333 return !want || (have && !strcmp(want, have));
1334}
1335
1336static bool_Bool nvmf_ctrl_matches_args(struct libnvme_host *h, struct libnvme_ctrl *c,
1337 const struct nvmf_args *fa)
1338{
1339 return opt_matches(fa->transport, libnvme_ctrl_get_transport(c)) &&
1340 opt_matches(fa->subsysnqn, libnvme_ctrl_get_subsysnqn(c)) &&
1341 opt_matches(fa->traddr, libnvme_ctrl_get_traddr(c)) &&
1342 opt_matches(fa->trsvcid, libnvme_ctrl_get_trsvcid(c)) &&
1343 opt_matches(fa->host_traddr, libnvme_ctrl_get_host_traddr(c)) &&
1344 opt_matches(fa->host_iface, libnvme_ctrl_get_host_iface(c)) &&
1345 opt_matches(fa->hostnqn, libnvme_host_get_hostnqn(h)) &&
1346 opt_matches(fa->hostid, libnvme_host_get_hostid(h));
1347}
1348
1349static int nvmf_disconnect_nqn(struct libnvme_global_ctx *ctx, char *nqn)
1350{
1351 int i = 0;
1352 char *n = nqn;
1353 char *p;
1354 struct libnvme_host *h;
1355 struct libnvme_subsystem *s;
1356 struct libnvme_ctrl *c;
1357
1358 while ((p = shr_strsep(&n, ",")) != NULL((void*)0)) {
1359 if (!strlen(p))
1360 continue;
1361 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1362 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1363 if (strcmp(libnvme_subsystem_get_subsysnqn(s), p))
1364 continue;
1365 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1366 if (!libnvmf_disconnect_ctrl(c))
1367 i++;
1368 }
1369 }
1370 }
1371 }
1372 nvme_show_verbose_result("NQN:%s disconnected %d controller(s)", nqn, i)nvme_show_verbose_message("NQN:%s disconnected %d controller(s)"
, nqn, i)
;
1373
1374 return 0;
1375}
1376
1377static int disconnect_validate_args(bool_Bool has_device, bool_Bool has_subsysnqn,
1378 bool_Bool match_args)
1379{
1380 if (has_device && (has_subsysnqn || match_args)) {
1381 nvme_show_error(nvme_show_message(1, "Device name [--device | -d] cannot be combined with other identifying options\n"
)
1382 "Device name [--device | -d] cannot be combined with other identifying options\n")nvme_show_message(1, "Device name [--device | -d] cannot be combined with other identifying options\n"
)
;
1383 return -EINVAL22;
1384 }
1385
1386 if (!has_device && !has_subsysnqn && match_args) {
1387 nvme_show_error(nvme_show_message(1, "Fabrics identifying options require an NQN [--nqn | -n]\n"
)
1388 "Fabrics identifying options require an NQN [--nqn | -n]\n")nvme_show_message(1, "Fabrics identifying options require an NQN [--nqn | -n]\n"
)
;
1389 return -EINVAL22;
1390 }
1391
1392 if (!has_device && !has_subsysnqn) {
1393 nvme_show_error(nvme_show_message(1, "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n"
)
1394 "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n")nvme_show_message(1, "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n"
)
;
1395 return -EINVAL22;
1396 }
1397
1398 return 0;
1399}
1400
1401static int add_exclusion_ctrl(struct libnvme_global_ctx *ctx,
1402 struct libnvme_ctrl *c)
1403{
1404 int err;
1405
1406 /*
1407 * Write exclusion entry before disconnecting so that
1408 * orchestrators see the exclusion in place before the
1409 * device removal event fires.
1410 */
1411 err = libnvmf_exclusion_add_ctrl(ctx, NULL((void*)0), c);
1412 if (!err)
1413 return 0;
1414
1415 nvme_show_error("Warning: failed to write exclusion entry: %s\n",nvme_show_message(1, "Warning: failed to write exclusion entry: %s\n"
, libnvme_strerror(-err))
1416 libnvme_strerror(-err))nvme_show_message(1, "Warning: failed to write exclusion entry: %s\n"
, libnvme_strerror(-err))
;
1417
1418 return err;
1419}
1420
1421static int add_exclusion_subsysnqn(struct libnvme_global_ctx *ctx,
1422 const char *subsysnqn)
1423{
1424 int err;
1425
1426 /*
1427 * Write exclusion entry before disconnecting so that
1428 * orchestrators see the exclusion in place before the
1429 * device removal event fires.
1430 */
1431 err = libnvmf_exclusion_add_subsysnqn(ctx, NULL((void*)0), subsysnqn);
1432 if (!err)
1433 return 0;
1434
1435 nvme_show_error("Warning: failed to write exclusion entry: %s\n",nvme_show_message(1, "Warning: failed to write exclusion entry: %s\n"
, libnvme_strerror(-err))
1436 libnvme_strerror(-err))nvme_show_message(1, "Warning: failed to write exclusion entry: %s\n"
, libnvme_strerror(-err))
;
1437
1438 return err;
1439}
1440
1441static int disconnect_by_device(struct libnvme_global_ctx *ctx,
1442 char *device, bool_Bool exclude)
1443{
1444 struct libnvme_ctrl *c;
1445 char *p;
1446 int err;
1447
1448 while ((p = shr_strsep(&device, ",")) != NULL((void*)0)) {
1449 if (!strncmp(p, "/dev/", 5))
1450 p += 5;
1451
1452 c = lookup_nvme_ctrl(ctx, p);
1453 if (!c) {
1454 nvme_show_error("Did not find device %s\n", p)nvme_show_message(1, "Did not find device %s\n", p);
1455 return -ENODEV19;
1456 }
1457
1458 if (exclude)
1459 add_exclusion_ctrl(ctx, c);
1460
1461 err = libnvmf_disconnect_ctrl(c);
1462 if (err)
1463 nvme_show_error("Failed to disconnect %s: %s\n",nvme_show_message(1, "Failed to disconnect %s: %s\n", p, libnvme_strerror
(-err))
1464 p, libnvme_strerror(-err))nvme_show_message(1, "Failed to disconnect %s: %s\n", p, libnvme_strerror
(-err))
;
1465 }
1466
1467 return 0;
1468}
1469
1470static int disconnect_by_args(struct libnvme_global_ctx *ctx,
1471 const struct nvmf_args *fa, bool_Bool exclude)
1472{
1473 struct libnvme_host *h;
1474 struct libnvme_subsystem *s;
1475 struct libnvme_ctrl *c;
1476 int err, i = 0;
1477
1478 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1479 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1480 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1481 if (!nvmf_ctrl_matches_args(h, c, fa))
1482 continue;
1483
1484 if (exclude)
1485 add_exclusion_ctrl(ctx, c);
1486
1487 err = libnvmf_disconnect_ctrl(c);
1488 if (err)
1489 nvme_show_error("Failed to disconnect %s: %s\n",nvme_show_message(1, "Failed to disconnect %s: %s\n", libnvme_ctrl_get_name
(c), libnvme_strerror(-err))
1490 libnvme_ctrl_get_name(c),nvme_show_message(1, "Failed to disconnect %s: %s\n", libnvme_ctrl_get_name
(c), libnvme_strerror(-err))
1491 libnvme_strerror(-err))nvme_show_message(1, "Failed to disconnect %s: %s\n", libnvme_ctrl_get_name
(c), libnvme_strerror(-err))
;
1492 else
1493 i++;
1494 }
1495 }
1496 }
1497
1498 if (!i)
1499 nvme_show_error("Did not find a matching controller\n")nvme_show_message(1, "Did not find a matching controller\n");
1500
1501 nvme_show_verbose_result("disconnected %d controller(s)", i)nvme_show_verbose_message("disconnected %d controller(s)", i);
1502
1503 return 0;
1504}
1505
1506static int disconnect_by_nqn(struct libnvme_global_ctx *ctx, const char *nqn,
1507 bool_Bool exclude)
1508{
1509 __cleanup_free__attribute__((cleanup(shr_freep))) char *n = NULL((void*)0);
1510
1511 n = strdup(nqn);
1512 if (!n)
1513 return -ENOMEM12;
1514
1515 if (exclude) {
1516 __cleanup_free__attribute__((cleanup(shr_freep))) char *excl = strdup(nqn);
1517 char *t, *p;
1518
1519 if (!excl)
1520 return -ENOMEM12;
1521
1522 t = excl;
1523 while ((p = shr_strsep(&t, ",")) != NULL((void*)0)) {
1524 if (!*p)
1525 continue;
1526
1527 add_exclusion_subsysnqn(ctx, p);
1528 }
1529 }
1530
1531 return nvmf_disconnect_nqn(ctx, n);
1532}
1533
1534int fabrics_disconnect(const char *desc, int argc, char **argv)
1535{
1536 const char *device = "nvme device handle";
1537 const char *exclude_help = "write exclusion entry before disconnecting";
1538
1539 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1540 __cleanup_free__attribute__((cleanup(shr_freep))) char *resolved_traddr = NULL((void*)0);
1541 struct nvmf_args fa = nvmf_args_defaults();
1542 __cleanup_free__attribute__((cleanup(shr_freep))) struct argconfig_commandline_options *nvmf =
1543 nvmf_options(&fa);
1544 bool_Bool match_args;
1545 int err;
1546
1547 struct config {
1548 char *device;
1549 bool_Bool exclude;
1550 };
1551
1552 struct config cfg = { 0 };
1553
1554 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1555 OPT_INCLUDE(nvmf, "Fabrics options"){"", 0, ((void*)0), CFG_INCLUDE, nvmf, ARGCONFIG_ARG_NONE, "Fabrics options"
, 0, ((void*)0)}
,
1556 OPT_STRING("device", 'd', "DEV", &cfg.device, device){"device", 'd', "DEV", CFG_STRING, _Generic(&cfg.device, char
** : (&cfg.device), const char ** : (&cfg.device)), ARGCONFIG_ARG_REQUIRED
, device, 0, }
,
1557 OPT_FLAG("exclude", 'x', &cfg.exclude, exclude_help){"exclude", 'x', ((void*)0), CFG_FLAG, _Generic(&cfg.exclude
, _Bool * : (&cfg.exclude)), ARGCONFIG_ARG_NONE, exclude_help
, 0, }
,
1558 OPT_END(){ ((void*)0) }
1559 };
1560
1561 if (!nvmf)
1562 return -ENOMEM12;
1563
1564 err = parse_args(argc, argv, desc, opts);
1565 if (err)
1566 return err;
1567
1568 /*
1569 * Any of the "connect"-style options beyond bare --nqn narrow the
1570 * lookup to a specific controller instead of a whole subsystem.
1571 */
1572 match_args = fa.transport || fa.traddr || fa.trsvcid ||
1573 fa.host_traddr || fa.host_iface || fa.hostnqn || fa.hostid;
1574
1575 err = disconnect_validate_args(!!cfg.device,
1576 !!fa.subsysnqn, match_args);
1577 if (err)
1578 return err;
1579
1580 err = nvmf_resolve_addr(fa.transport, fa.traddr, &resolved_traddr);
1581 if (err)
1582 return err;
1583 fa.traddr = resolved_traddr;
1584
1585 err = nvme_create_global_ctx_hostnqn(&ctx,
1586 fa.hostnqn, fa.hostid, NULL((void*)0), NULL((void*)0));
1587 if (err)
1588 return err;
1589
1590 libnvme_skip_namespaces(ctx);
1591 err = libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0));
1592 if (err < 0) {
1593 /*
1594 * Do not report an error when the modules are not
1595 * loaded, this allows the user to unconditionally call
1596 * disconnect.
1597 */
1598 if (err == -ENOENT2)
1599 return 0;
1600
1601 nvme_show_error("Failed to scan topology: %s",nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-err))
1602 libnvme_strerror(-err))nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-err))
;
1603 return err;
1604 }
1605
1606 if (cfg.device)
1607 return disconnect_by_device(ctx, cfg.device, cfg.exclude);
1608
1609 if (match_args)
1610 return disconnect_by_args(ctx, &fa, cfg.exclude);
1611
1612 return disconnect_by_nqn(ctx, fa.subsysnqn, cfg.exclude);
1613}
1614
1615/* disconnect-all policy: should controller @c be torn down? */
1616static bool_Bool disconnect_all_match(struct libnvme_global_ctx *ctx,
1617 struct libnvme_ctrl *c, const char *transport,
1618 const char *owner, bool_Bool force)
1619{
1620 if (transport && strcmp(transport, libnvme_ctrl_get_transport(c)))
1621 return false0;
1622 if (!libnvme_ctrl_is_transport_fabric(c))
1623 return false0;
1624 if (force)
1625 return true1;
1626
1627 /*
1628 * attr_equal() returns 0 only on an exact match; a read error (<0)
1629 * compares as "not a match", so we never disconnect on error.
1630 */
1631 return libnvmf_registry_attr_equal(ctx, libnvme_ctrl_get_name(c),
1632 "owner", owner) == 0;
1633}
1634
1635int fabrics_disconnect_all(const char *desc, int argc, char **argv)
1636{
1637 const char *owner_help = "disconnect only controllers owned by NAME";
1638 const char *force_help = "disconnect all controllers regardless of ownership";
1639 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1640 struct libnvme_host *h;
1641 struct libnvme_subsystem *s;
1642 struct libnvme_ctrl *c;
1643 int ret;
1644
1645 struct config {
1646 char *transport;
1647 char *owner;
1648 bool_Bool force;
1649 };
1650
1651 struct config cfg = { 0 };
1652
1653 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1654 OPT_STRING("transport", 't', "STR", &cfg.transport,{"transport", 't', "STR", CFG_STRING, _Generic(&cfg.transport
, char ** : (&cfg.transport), const char ** : (&cfg.transport
)), ARGCONFIG_ARG_REQUIRED, "transport type", 0, }
1655 DESC_NVMF_TPORT){"transport", 't', "STR", CFG_STRING, _Generic(&cfg.transport
, char ** : (&cfg.transport), const char ** : (&cfg.transport
)), ARGCONFIG_ARG_REQUIRED, "transport type", 0, }
,
1656 OPT_STRING("owner", 0, "NAME", &cfg.owner, owner_help){"owner", 0, "NAME", CFG_STRING, _Generic(&cfg.owner, char
** : (&cfg.owner), const char ** : (&cfg.owner)), ARGCONFIG_ARG_REQUIRED
, owner_help, 0, }
,
1657 OPT_FLAG("force", 0, &cfg.force, force_help){"force", 0, ((void*)0), CFG_FLAG, _Generic(&cfg.force, _Bool
* : (&cfg.force)), ARGCONFIG_ARG_NONE, force_help, 0, }
,
1658 OPT_END(){ ((void*)0) }
1659 };
1660
1661 ret = parse_args(argc, argv, desc, opts);
1662 if (ret)
1663 return ret;
1664
1665 if (cfg.force && cfg.owner) {
1666 nvme_show_error("--force and --owner are mutually exclusive")nvme_show_message(1, "--force and --owner are mutually exclusive"
)
;
1667 return -EINVAL22;
1668 }
1669
1670 if ((cfg.force || cfg.owner) && isatty(STDIN_FILENO0)) {
1671 char ans[8] = { 0 };
1672
1673 if (cfg.force)
1674 fprintf(stderrstderr,
1675 "WARNING: --force disconnects all NVMeoF controllers\n"
1676 "regardless of ownership. Type 'yes' to confirm: ");
1677 else
1678 fprintf(stderrstderr,
1679 "WARNING: --owner disconnects all NVMeoF controllers\n"
1680 "owned by '%s'. Type 'yes' to confirm: ",
1681 cfg.owner);
1682 if (!fgets(ans, sizeof(ans), stdinstdin)) {
1683 nvme_show_error("Aborted.")nvme_show_message(1, "Aborted.");
1684 return -EINVAL22;
1685 }
1686 ans[strcspn(ans, "\n")] = '\0';
1687 if (strcmp(ans, "yes") != 0) {
1688 nvme_show_error("Aborted.")nvme_show_message(1, "Aborted.");
1689 return -EINVAL22;
1690 }
1691 }
1692
1693 ret = nvme_create_global_ctx(&ctx);
1694 if (ret)
1695 return ret;
1696
1697 libnvme_skip_namespaces(ctx);
1698 ret = libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0));
1699 if (ret < 0) {
1700 /*
1701 * Do not report an error when the modules are not
1702 * loaded, this allows the user to unconditionally call
1703 * disconnect.
1704 */
1705 if (ret == -ENOENT2)
1706 return 0;
1707
1708 nvme_show_error("Failed to scan topology: %s",nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
1709 libnvme_strerror(-ret))nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
;
1710 return ret;
1711 }
1712
1713 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1714 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1715 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1716 if (!disconnect_all_match(ctx, c, cfg.transport,
1717 cfg.owner, cfg.force))
1718 continue;
1719 if (libnvmf_disconnect_ctrl(c))
1720 nvme_show_error(nvme_show_message(1, "failed to disconnect %s\n", libnvme_ctrl_get_name
(c))
1721 "failed to disconnect %s\n",nvme_show_message(1, "failed to disconnect %s\n", libnvme_ctrl_get_name
(c))
1722 libnvme_ctrl_get_name(c))nvme_show_message(1, "failed to disconnect %s\n", libnvme_ctrl_get_name
(c))
;
1723 }
1724 }
1725 }
1726
1727 return 0;
1728}
1729
1730int fabrics_config_validate(const char *desc, int argc, char **argv)
1731{
1732 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1733 char *config_file = PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf";
1734 int ret;
1735
1736 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1737 OPT_FILE("config", 'J', &config_file, nvmf_config_file_ro){"config", 'J', "FILE", CFG_FILE, _Generic(&config_file, char
** : (&config_file), const char ** : (&config_file))
, ARGCONFIG_ARG_REQUIRED, nvmf_config_file_ro, 0, }
,
1738 OPT_END(){ ((void*)0) }
1739 };
1740
1741 ret = parse_args(argc, argv, desc, opts);
1742 if (ret)
1743 return ret;
1744
1745 ret = nvme_create_global_ctx(&ctx);
1746 if (ret)
1747 return ret;
1748
1749 if (access(config_file, F_OK0)) {
1750 nvme_show_error("%s: no such file", config_file)nvme_show_message(1, "%s: no such file", config_file);
1751 return -ENOENT2;
1752 }
1753
1754 ret = libnvmf_config_validate(ctx, config_file);
1755 if (ret)
1756 nvme_show_error("%s: invalid configuration", config_file)nvme_show_message(1, "%s: invalid configuration", config_file
)
;
1757 else
1758 nvme_show_result("%s: OK", config_file)nvme_show_message(0, "%s: OK", config_file);
1759
1760 return ret;
1761}
1762
1763int fabrics_config_show(const char *desc, int argc, char **argv)
1764{
1765 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1766 struct libnvmf_config *cfg;
1767 char *config_file = PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf";
1768 nvme_print_flags_t flags;
1769 int ret;
1770
1771 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1772 OPT_FILE("config", 'J', &config_file, nvmf_config_file_ro){"config", 'J', "FILE", CFG_FILE, _Generic(&config_file, char
** : (&config_file), const char ** : (&config_file))
, ARGCONFIG_ARG_REQUIRED, nvmf_config_file_ro, 0, }
,
1773 OPT_END(){ ((void*)0) }
1774 };
1775
1776 ret = parse_args(argc, argv, desc, opts);
1777 if (ret)
1778 return ret;
1779
1780 flags = nvme_args.output_flags;
1781
1782 ret = nvme_create_global_ctx(&ctx);
1783 if (ret)
1784 return ret;
1785
1786 ret = libnvmf_config_read(ctx, config_file, &cfg);
1787 if (ret) {
1788 nvme_show_error("failed to read %s: %s", config_file,nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
1789 libnvme_strerror(-ret))nvme_show_message(1, "failed to read %s: %s", config_file, libnvme_strerror
(-ret))
;
1790 return ret;
1791 }
1792
1793 nvme_show_config_conn_list(cfg, flags);
1794 libnvmf_config_free(cfg);
1795
1796 return 0;
1797}
1798
1799static int dim_operation(struct libnvme_ctrl *c, enum nvmf_dim_tas tas, const char *name)
1800{
1801 static const char * const task[] = {
1802 [NVMF_DIM_TAS_REGISTER] = "register",
1803 [NVMF_DIM_TAS_DEREGISTER] = "deregister",
1804 };
1805 const char *t;
1806 int status;
1807 __u32 result;
1808
1809 t = (tas > NVMF_DIM_TAS_DEREGISTER || !task[tas]) ? "reserved" : task[tas];
1810 status = libnvmf_register_ctrl(c, tas, &result);
1811 if (status == NVME_SC_SUCCESS) {
1812 nvme_show_verbose_result("%s DIM %s command success", name, t)nvme_show_verbose_message("%s DIM %s command success", name, t
)
;
1813 } else if (status < NVME_SC_SUCCESS) {
1814 nvme_show_error("%s DIM %s command error. Status:0x%04x - %s",nvme_show_message(1, "%s DIM %s command error. Status:0x%04x - %s"
, name, t, status, libnvme_status_to_string(status, 0))
1815 name, t, status, libnvme_status_to_string(status, false))nvme_show_message(1, "%s DIM %s command error. Status:0x%04x - %s"
, name, t, status, libnvme_status_to_string(status, 0))
;
1816 } else {
1817 nvme_show_error("%s DIM %s command error. Result:0x%04x, Status:0x%04x - %s",nvme_show_message(1, "%s DIM %s command error. Result:0x%04x, Status:0x%04x - %s"
, name, t, result, status, libnvme_status_to_string(status, 0
))
1818 name, t, result, status, libnvme_status_to_string(status, false))nvme_show_message(1, "%s DIM %s command error. Result:0x%04x, Status:0x%04x - %s"
, name, t, result, status, libnvme_status_to_string(status, 0
))
;
1819 }
1820
1821 return libnvme_status_to_errno(status, true1);
1822}
1823
1824int fabrics_dim(const char *desc, int argc, char **argv)
1825{
1826 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1827 enum nvmf_dim_tas tas;
1828 struct libnvme_ctrl *c;
1829 char *p;
1830 int ret, err = 0;
1831
1832 struct {
1833 char *nqn;
1834 char *device;
1835 char *tas;
1836 } cfg = { 0 };
1837
1838 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1839 OPT_STRING("nqn", 'n', "NAME", &cfg.nqn,{"nqn", 'n', "NAME", CFG_STRING, _Generic(&cfg.nqn, char *
* : (&cfg.nqn), const char ** : (&cfg.nqn)), ARGCONFIG_ARG_REQUIRED
, "Comma-separated list of DC nqn", 0, }
1840 "Comma-separated list of DC nqn"){"nqn", 'n', "NAME", CFG_STRING, _Generic(&cfg.nqn, char *
* : (&cfg.nqn), const char ** : (&cfg.nqn)), ARGCONFIG_ARG_REQUIRED
, "Comma-separated list of DC nqn", 0, }
,
1841 OPT_STRING("device", 'd', "DEV", &cfg.device,{"device", 'd', "DEV", CFG_STRING, _Generic(&cfg.device, char
** : (&cfg.device), const char ** : (&cfg.device)), ARGCONFIG_ARG_REQUIRED
, "Comma-separated list of DC nvme device handle.", 0, }
1842 "Comma-separated list of DC nvme device handle."){"device", 'd', "DEV", CFG_STRING, _Generic(&cfg.device, char
** : (&cfg.device), const char ** : (&cfg.device)), ARGCONFIG_ARG_REQUIRED
, "Comma-separated list of DC nvme device handle.", 0, }
,
1843 OPT_STRING("task", 't', "TASK", &cfg.tas,{"task", 't', "TASK", CFG_STRING, _Generic(&cfg.tas, char
** : (&cfg.tas), const char ** : (&cfg.tas)), ARGCONFIG_ARG_REQUIRED
, "[register|deregister]", 0, }
1844 "[register|deregister]"){"task", 't', "TASK", CFG_STRING, _Generic(&cfg.tas, char
** : (&cfg.tas), const char ** : (&cfg.tas)), ARGCONFIG_ARG_REQUIRED
, "[register|deregister]", 0, }
,
1845 OPT_END(){ ((void*)0) }
1846 };
1847
1848 ret = parse_args(argc, argv, desc, opts);
1849 if (ret)
1850 return ret;
1851
1852 if (!cfg.nqn && !cfg.device) {
1853 nvme_show_error(nvme_show_message(1, "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n"
)
1854 "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n")nvme_show_message(1, "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n"
)
;
1855 return -EINVAL22;
1856 }
1857
1858 if (!cfg.tas) {
1859 nvme_show_error(nvme_show_message(1, "Task [--task | -t] must be specified\n"
)
1860 "Task [--task | -t] must be specified\n")nvme_show_message(1, "Task [--task | -t] must be specified\n"
)
;
1861 return -EINVAL22;
1862 }
1863
1864 /* Allow partial name (e.g. "reg" for "register" */
1865 if (strstarts("register", cfg.tas)(strncmp(("register"),(cfg.tas),strlen(cfg.tas)) == 0)) {
1866 tas = NVMF_DIM_TAS_REGISTER;
1867 } else if (strstarts("deregister", cfg.tas)(strncmp(("deregister"),(cfg.tas),strlen(cfg.tas)) == 0)) {
1868 tas = NVMF_DIM_TAS_DEREGISTER;
1869 } else {
1870 nvme_show_error("Invalid --task: %s", cfg.tas)nvme_show_message(1, "Invalid --task: %s", cfg.tas);
1871 return -EINVAL22;
1872 }
1873
1874 ret = nvme_create_global_ctx(&ctx);
1875 if (ret)
1876 return ret;
1877
1878 libnvme_skip_namespaces(ctx);
1879 ret = libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0));
1880 if (ret < 0) {
1881 nvme_show_error("Failed to scan topology: %s",nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
1882 libnvme_strerror(-ret))nvme_show_message(1, "Failed to scan topology: %s", libnvme_strerror
(-ret))
;
1883 return ret;
1884 }
1885
1886 if (cfg.nqn) {
1887 struct libnvme_host *h;
1888 struct libnvme_subsystem *s;
1889 char *n = cfg.nqn;
1890
1891 while ((p = shr_strsep(&n, ",")) != NULL((void*)0)) {
1892 if (!strlen(p))
1893 continue;
1894 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1895 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1896 if (strcmp(libnvme_subsystem_get_subsysnqn(s), p))
1897 continue;
1898 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1899 ret = dim_operation(c, tas, p);
1900 if (ret && !err)
1901 err = ret;
1902 }
1903 }
1904 }
1905 }
1906 }
1907
1908 if (cfg.device) {
1909 char *d = cfg.device;
1910
1911 while ((p = shr_strsep(&d, ",")) != NULL((void*)0)) {
1912 if (!strncmp(p, "/dev/", 5))
1913 p += 5;
1914 ret = libnvme_scan_ctrl(ctx, p, &c);
1915 if (ret) {
1916 nvme_show_error(nvme_show_message(1, "Did not find device %s: %s\n", p, libnvme_strerror
(ret))
1917 "Did not find device %s: %s\n",nvme_show_message(1, "Did not find device %s: %s\n", p, libnvme_strerror
(ret))
1918 p, libnvme_strerror(ret))nvme_show_message(1, "Did not find device %s: %s\n", p, libnvme_strerror
(ret))
;
1919 return ret;
1920 }
1921 ret = dim_operation(c, tas, p);
1922 if (ret && !err)
1923 err = ret;
1924 }
1925 }
1926
1927 return err;
1928}