Line data Source code
1 : /*
2 : * Copyright (C) 2014-2021 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Tomas Bzatek <tbzatek@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <string.h>
22 : #include <stdio.h>
23 : #include <unistd.h>
24 : #include <sys/ioctl.h>
25 : #include <sys/stat.h>
26 : #include <errno.h>
27 : #include <fcntl.h>
28 : #include <malloc.h>
29 : #include <linux/fs.h>
30 : #include <glib/gstdio.h>
31 :
32 : #include <libnvme.h>
33 :
34 : #include <blockdev/utils.h>
35 : #include <check_deps.h>
36 : #include "nvme.h"
37 : #include "nvme-private.h"
38 :
39 :
40 : /* nvme-cli defaults */
41 : #define PATH_NVMF_CONFIG "/etc/nvme/config.json"
42 : #define MAX_DISC_RETRIES 10
43 :
44 :
45 14 : static void parse_extra_args (const BDExtraArg **extra, struct nvme_fabrics_config *cfg, const gchar **config_file, const gchar **hostkey, const gchar **ctrlkey, const gchar **hostsymname) {
46 : const BDExtraArg **extra_i;
47 :
48 14 : if (!extra)
49 14 : return;
50 :
51 : #define SAFE_INT_CONV(target) { \
52 : gint64 v; \
53 : gchar *endptr = NULL; \
54 : \
55 : v = g_ascii_strtoll ((*extra_i)->val, &endptr, 0); \
56 : if (endptr != (*extra_i)->val) \
57 : target = v; \
58 : }
59 : #define SAFE_BOOL_CONV(target) { \
60 : if (g_ascii_strcasecmp ((*extra_i)->val, "on") == 0 || \
61 : g_ascii_strcasecmp ((*extra_i)->val, "1") == 0 || \
62 : g_ascii_strcasecmp ((*extra_i)->val, "true") == 0) \
63 : target = TRUE; \
64 : else \
65 : if (g_ascii_strcasecmp ((*extra_i)->val, "off") == 0 || \
66 : g_ascii_strcasecmp ((*extra_i)->val, "0") == 0 || \
67 : g_ascii_strcasecmp ((*extra_i)->val, "false") == 0) \
68 : target = FALSE; \
69 : }
70 :
71 0 : for (extra_i = extra; *extra_i; extra_i++) {
72 0 : if (g_strcmp0 ((*extra_i)->opt, "config") == 0 && config_file) {
73 0 : if (g_ascii_strcasecmp ((*extra_i)->val, "none") == 0)
74 0 : *config_file = NULL;
75 : else
76 0 : *config_file = (*extra_i)->val;
77 : } else
78 0 : if (g_strcmp0 ((*extra_i)->opt, "dhchap_key") == 0 && hostkey)
79 0 : *hostkey = (*extra_i)->val;
80 : else
81 0 : if (g_strcmp0 ((*extra_i)->opt, "dhchap_ctrl_key") == 0 && ctrlkey)
82 0 : *ctrlkey = (*extra_i)->val;
83 : else
84 0 : if (g_strcmp0 ((*extra_i)->opt, "hostsymname") == 0 && hostsymname)
85 0 : *hostsymname = (*extra_i)->val;
86 : else
87 0 : if (g_strcmp0 ((*extra_i)->opt, "nr_io_queues") == 0)
88 0 : SAFE_INT_CONV (cfg->nr_io_queues)
89 : else
90 0 : if (g_strcmp0 ((*extra_i)->opt, "nr_write_queues") == 0)
91 0 : SAFE_INT_CONV (cfg->nr_write_queues)
92 : else
93 0 : if (g_strcmp0 ((*extra_i)->opt, "nr_poll_queues") == 0)
94 0 : SAFE_INT_CONV (cfg->nr_poll_queues)
95 : else
96 0 : if (g_strcmp0 ((*extra_i)->opt, "queue_size") == 0)
97 0 : SAFE_INT_CONV (cfg->queue_size)
98 : else
99 0 : if (g_strcmp0 ((*extra_i)->opt, "keep_alive_tmo") == 0)
100 0 : SAFE_INT_CONV (cfg->keep_alive_tmo)
101 : else
102 0 : if (g_strcmp0 ((*extra_i)->opt, "reconnect_delay") == 0)
103 0 : SAFE_INT_CONV (cfg->reconnect_delay)
104 : else
105 0 : if (g_strcmp0 ((*extra_i)->opt, "ctrl_loss_tmo") == 0)
106 0 : SAFE_INT_CONV (cfg->ctrl_loss_tmo)
107 : else
108 0 : if (g_strcmp0 ((*extra_i)->opt, "fast_io_fail_tmo") == 0)
109 0 : SAFE_INT_CONV (cfg->fast_io_fail_tmo)
110 : else
111 0 : if (g_strcmp0 ((*extra_i)->opt, "tos") == 0)
112 0 : SAFE_INT_CONV (cfg->tos)
113 : else
114 0 : if (g_strcmp0 ((*extra_i)->opt, "duplicate_connect") == 0)
115 0 : SAFE_BOOL_CONV (cfg->duplicate_connect)
116 : else
117 0 : if (g_strcmp0 ((*extra_i)->opt, "disable_sqflow") == 0)
118 0 : SAFE_BOOL_CONV (cfg->disable_sqflow)
119 : else
120 0 : if (g_strcmp0 ((*extra_i)->opt, "hdr_digest") == 0)
121 0 : SAFE_BOOL_CONV (cfg->hdr_digest)
122 : else
123 0 : if (g_strcmp0 ((*extra_i)->opt, "data_digest") == 0)
124 0 : SAFE_BOOL_CONV (cfg->data_digest)
125 : else
126 0 : if (g_strcmp0 ((*extra_i)->opt, "tls") == 0)
127 0 : SAFE_BOOL_CONV (cfg->tls)
128 : #ifdef HAVE_LIBNVME_1_4
129 : else
130 0 : if (g_strcmp0 ((*extra_i)->opt, "keyring") == 0) {
131 : int keyring;
132 :
133 0 : keyring = nvme_lookup_keyring ((*extra_i)->val);
134 0 : if (keyring) {
135 0 : cfg->keyring = keyring;
136 0 : nvme_set_keyring (cfg->keyring);
137 : }
138 : } else
139 0 : if (g_strcmp0 ((*extra_i)->opt, "tls_key") == 0) {
140 : int key;
141 :
142 0 : key = nvme_lookup_key ("psk", (*extra_i)->val);
143 0 : if (key)
144 0 : cfg->tls_key = key;
145 : }
146 : #endif
147 : }
148 :
149 : #undef SAFE_INT_CONV
150 : #undef SAFE_BOOL_CONV
151 : }
152 :
153 :
154 : /**
155 : * bd_nvme_connect:
156 : * @subsysnqn: The name for the NVMe subsystem to connect to.
157 : * @transport: The network fabric used for a NVMe-over-Fabrics network.
158 : * @transport_addr: (nullable): The network address of the Controller. For transports using IP addressing (e.g. `rdma`) this should be an IP-based address.
159 : * @transport_svcid: (nullable): The transport service ID. For transports using IP addressing (e.g. `tcp`, `rdma`) this field is the port number. The default port number for the `tcp` and `rdma` transports is `4420` and `8009` respectively when the well-known Discovery NQN is specified.
160 : * @host_traddr: (nullable): The network address used on the host to connect to the Controller. For TCP, this sets the source address on the socket.
161 : * @host_iface: (nullable): The network interface used on the host to connect to the Controller (e.g. IP `eth1`, `enp2s0`). This forces the connection to be made on a specific interface instead of letting the system decide.
162 : * @host_nqn: (nullable): Overrides the default Host NQN that identifies the NVMe Host. If this option is %NULL, the default is read from `/etc/nvme/hostnqn` first.
163 : * If that does not exist, the autogenerated NQN value from the NVMe Host kernel module is used next. The Host NQN uniquely identifies the NVMe Host.
164 : * @host_id: (nullable): User-defined host UUID or %NULL to use default (as defined in `/etc/nvme/hostid`).
165 : * @extra: (nullable) (array zero-terminated=1): Additional arguments.
166 : * @error: (out) (nullable): Place to store error (if any).
167 : *
168 : * Creates a transport connection to a remote system (specified by @transport_addr and @transport_svcid)
169 : * and creates a NVMe over Fabrics controller for the NVMe subsystem specified by the @subsysnqn option.
170 : *
171 : * Valid values for @transport include:
172 : * - `"rdma"`: An rdma network (RoCE, iWARP, Infiniband, basic rdma, etc.)
173 : * - `"fc"`: A Fibre Channel network.
174 : * - `"tcp"`: A TCP/IP network.
175 : * - `"loop"`: A NVMe over Fabrics target on the local host.
176 : *
177 : * In addition to the primary options it's possible to supply @extra arguments:
178 : * - `"config"`: Use the specified JSON configuration file instead of the default file (see below) or
179 : * specify `"none"` to avoid reading any configuration file.
180 : * - `"dhchap_key"`: NVMe In-band authentication secret in ASCII format as described
181 : * in the NVMe 2.0 specification. When not specified, the secret is by default read
182 : * from `/etc/nvme/hostkey`. In case that file does not exist no in-band authentication
183 : * is attempted.
184 : * - `"dhchap_ctrl_key"`: NVMe In-band authentication controller secret for bi-directional authentication.
185 : * When not specified, no bi-directional authentication is attempted.
186 : * - `"nr_io_queues"`: The number of I/O queues.
187 : * - `"nr_write_queues"`: Number of additional queues that will be used for write I/O.
188 : * - `"nr_poll_queues"`: Number of additional queues that will be used for polling latency sensitive I/O.
189 : * - `"queue_size"`: Number of elements in the I/O queues.
190 : * - `"keep_alive_tmo"`: The keep alive timeout (in seconds).
191 : * - `"reconnect_delay"`: The delay (in seconds) before reconnect is attempted after a connect loss.
192 : * - `"ctrl_loss_tmo"`: The controller loss timeout period (in seconds). A special value of `-1` will cause reconnecting forever.
193 : * - `"fast_io_fail_tmo"`: Fast I/O Fail timeout (in seconds).
194 : * - `"tos"`: Type of service.
195 : * - `"duplicate_connect"`: Allow duplicated connections between same transport host and subsystem port. Boolean value.
196 : * - `"disable_sqflow"`: Disables SQ flow control to omit head doorbell update for submission queues when sending nvme completions. Boolean value.
197 : * - `"hdr_digest"`: Generates/verifies header digest (TCP). Boolean value.
198 : * - `"data_digest"`: Generates/verifies data digest (TCP). Boolean value.
199 : * - `"tls"`: Enable TLS encryption (TCP). Boolean value.
200 : * - `"hostsymname"`: TP8010: NVMe host symbolic name.
201 : * - `"keyring"`: Keyring to store and lookup keys. String value.
202 : * - `"tls_key"`: TLS PSK for the connection. String value.
203 : *
204 : * Boolean values can be expressed by "0"/"1", "on"/"off" or "True"/"False" case-insensitive
205 : * strings. Failed numerical or boolean string conversions will result in the option being ignored.
206 : *
207 : * By default additional options are read from the default configuration file `/etc/nvme/config.json`.
208 : * This follows the default behaviour of `nvme-cli`. Use the @extra `"config"` argument
209 : * to either specify a different config file or disable use of it. The JSON configuration
210 : * file format is documented in [https://raw.githubusercontent.com/linux-nvme/libnvme/master/doc/config-schema.json](https://raw.githubusercontent.com/linux-nvme/libnvme/master/doc/config-schema.json).
211 : * As a rule @extra key names are kept consistent with the JSON config file schema.
212 : * Any @extra option generally overrides particular option specified in a configuration file.
213 : *
214 : * Returns: %TRUE if the subsystem was connected successfully, %FALSE otherwise with @error set.
215 : *
216 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
217 : */
218 14 : gboolean bd_nvme_connect (const gchar *subsysnqn, const gchar *transport, const gchar *transport_addr, const gchar *transport_svcid, const gchar *host_traddr, const gchar *host_iface, const gchar *host_nqn, const gchar *host_id, const BDExtraArg **extra, GError **error) {
219 : int ret;
220 14 : const gchar *config_file = PATH_NVMF_CONFIG;
221 : gchar *host_nqn_val;
222 : gchar *host_id_val;
223 14 : const gchar *hostkey = NULL;
224 14 : const gchar *ctrlkey = NULL;
225 14 : const gchar *hostsymname = NULL;
226 : nvme_root_t root;
227 : nvme_host_t host;
228 : nvme_ctrl_t ctrl;
229 : struct nvme_fabrics_config cfg;
230 :
231 14 : if (subsysnqn == NULL) {
232 0 : g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
233 : "Invalid value specified for the subsysnqn argument");
234 0 : return FALSE;
235 : }
236 14 : if (transport == NULL) {
237 0 : g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
238 : "Invalid value specified for the transport argument");
239 0 : return FALSE;
240 : }
241 14 : if (transport_addr == NULL && !g_str_equal (transport, "loop") && !g_str_equal (transport, "pcie")) {
242 0 : g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
243 : "Invalid value specified for the transport address argument");
244 0 : return FALSE;
245 : }
246 :
247 : /* HostNQN checks */
248 14 : host_nqn_val = g_strdup (host_nqn);
249 14 : host_id_val = g_strdup (host_id);
250 14 : if (host_nqn_val == NULL)
251 7 : host_nqn_val = nvmf_hostnqn_from_file ();
252 14 : if (host_id_val == NULL)
253 12 : host_id_val = nvmf_hostid_from_file ();
254 14 : if (host_nqn_val == NULL)
255 1 : host_nqn_val = nvmf_hostnqn_generate ();
256 14 : if (host_nqn_val == NULL) {
257 0 : g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
258 : "Could not determine HostNQN");
259 0 : g_free (host_nqn_val);
260 0 : g_free (host_id_val);
261 0 : return FALSE;
262 : }
263 14 : if (host_id_val == NULL) {
264 : /* derive hostid from hostnqn, newer kernels refuse empty hostid */
265 1 : host_id_val = g_strrstr (host_nqn_val, "uuid:");
266 1 : if (host_id_val)
267 2 : host_id_val = g_strdup (host_id_val + strlen ("uuid:"));
268 : /* TODO: in theory generating arbitrary uuid might work as a fallback */
269 : }
270 14 : if (host_id_val == NULL) {
271 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
272 : "Could not determine HostID value from HostNQN '%s'",
273 : host_nqn_val);
274 0 : g_free (host_nqn_val);
275 0 : g_free (host_id_val);
276 0 : return FALSE;
277 : }
278 :
279 : /* parse extra arguments */
280 14 : nvmf_default_config (&cfg);
281 14 : parse_extra_args (extra, &cfg, &config_file, &hostkey, &ctrlkey, &hostsymname);
282 :
283 14 : root = nvme_scan (config_file);
284 14 : g_assert (root != NULL);
285 14 : nvme_init_logging (root, -1, false, false);
286 14 : host = nvme_lookup_host (root, host_nqn_val, host_id_val);
287 14 : if (host == NULL) {
288 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
289 : "Unable to lookup host for HostNQN '%s' and HostID '%s'",
290 : host_nqn_val, host_id_val);
291 0 : g_free (host_nqn_val);
292 0 : g_free (host_id_val);
293 0 : nvme_free_tree (root);
294 0 : return FALSE;
295 : }
296 14 : g_free (host_nqn_val);
297 14 : g_free (host_id_val);
298 14 : if (hostkey)
299 0 : nvme_host_set_dhchap_key (host, hostkey);
300 14 : if (hostsymname)
301 0 : nvme_host_set_hostsymname (host, hostsymname);
302 :
303 : /* tr_svcid defaults */
304 14 : if (!transport_svcid) {
305 14 : if (g_strcmp0 (transport, "tcp") == 0) {
306 0 : if (g_strcmp0 (subsysnqn, NVME_DISC_SUBSYS_NAME) == 0)
307 0 : transport_svcid = G_STRINGIFY (NVME_DISC_IP_PORT);
308 : else
309 0 : transport_svcid = G_STRINGIFY (NVME_RDMA_IP_PORT);
310 : } else
311 14 : if (g_strcmp0(transport, "rdma") == 0)
312 0 : transport_svcid = G_STRINGIFY (NVME_RDMA_IP_PORT);
313 : }
314 :
315 14 : ctrl = nvme_create_ctrl (root, subsysnqn, transport, transport_addr, host_traddr, host_iface, transport_svcid);
316 14 : if (ctrl == NULL) {
317 0 : _nvme_fabrics_errno_to_gerror (-1, errno, error);
318 0 : g_prefix_error (error, "Error creating the controller: ");
319 0 : nvme_free_tree (root);
320 0 : return FALSE;
321 : }
322 14 : if (ctrlkey)
323 0 : nvme_ctrl_set_dhchap_key (ctrl, ctrlkey);
324 :
325 14 : ret = nvmf_add_ctrl (host, ctrl, &cfg);
326 14 : if (ret != 0) {
327 6 : _nvme_fabrics_errno_to_gerror (ret, errno, error);
328 6 : g_prefix_error (error, "Error connecting the controller: ");
329 6 : nvme_free_ctrl (ctrl);
330 6 : nvme_free_tree (root);
331 6 : return FALSE;
332 : }
333 8 : nvme_free_ctrl (ctrl);
334 8 : nvme_free_tree (root);
335 :
336 8 : return TRUE;
337 : }
338 :
339 13 : static gboolean _disconnect (const gchar *subsysnqn, const gchar *path, GError **error, gboolean *found) {
340 : nvme_root_t root;
341 : nvme_host_t host;
342 : nvme_subsystem_t subsys;
343 : nvme_ctrl_t ctrl;
344 : int ret;
345 :
346 13 : root = nvme_create_root (NULL, -1);
347 13 : if (root == NULL) {
348 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
349 : "Failed to create topology root: %s",
350 0 : strerror_l (errno, _C_LOCALE));
351 0 : return FALSE;
352 : }
353 13 : ret = nvme_scan_topology (root, NULL, NULL);
354 13 : if (ret < 0) {
355 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
356 : "Failed to scan topology: %s",
357 0 : strerror_l (errno, _C_LOCALE));
358 0 : nvme_free_tree (root);
359 0 : return FALSE;
360 : }
361 28 : nvme_for_each_host (root, host)
362 39 : nvme_for_each_subsystem (host, subsys)
363 24 : if (!subsysnqn || g_strcmp0 (nvme_subsystem_get_nqn (subsys), subsysnqn) == 0)
364 26 : nvme_subsystem_for_each_ctrl (subsys, ctrl)
365 14 : if (!path || g_strcmp0 (nvme_ctrl_get_name (ctrl), path) == 0) {
366 8 : ret = nvme_disconnect_ctrl (ctrl);
367 8 : if (ret != 0) {
368 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
369 : "Error disconnecting the controller: %s",
370 0 : strerror_l (errno, _C_LOCALE));
371 0 : nvme_free_tree (root);
372 0 : return FALSE;
373 : }
374 8 : *found = TRUE;
375 : }
376 13 : nvme_free_tree (root);
377 13 : return TRUE;
378 : }
379 :
380 : /**
381 : * bd_nvme_disconnect:
382 : * @subsysnqn: The name of the NVMe subsystem to disconnect.
383 : * @error: (out) (nullable): Place to store error (if any).
384 : *
385 : * Disconnects and removes one or more existing NVMe over Fabrics controllers.
386 : * This may disconnect multiple controllers with matching @subsysnqn and %TRUE
387 : * is only returned when all controllers were disconnected successfully.
388 : *
389 : * Returns: %TRUE if all matching controllers were disconnected successfully, %FALSE with @error
390 : * set in case of a disconnect error or when no matching controllers were found.
391 : *
392 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
393 : */
394 10 : gboolean bd_nvme_disconnect (const gchar *subsysnqn, GError **error) {
395 10 : gboolean found = FALSE;
396 :
397 10 : if (!_disconnect (subsysnqn, NULL, error, &found))
398 0 : return FALSE;
399 :
400 10 : if (!found) {
401 4 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_NO_MATCH,
402 : "No subsystems matching '%s' NQN found.", subsysnqn);
403 4 : return FALSE;
404 : }
405 6 : return TRUE;
406 : }
407 :
408 : /**
409 : * bd_nvme_disconnect_by_path:
410 : * @path: NVMe controller device to disconnect (e.g. `/dev/nvme0`).
411 : * @error: (out) (nullable): Place to store error (if any).
412 : *
413 : * Disconnects and removes a NVMe over Fabrics controller represented
414 : * by a block device path.
415 : *
416 : * Returns: %TRUE if the controller was disconnected successfully,
417 : * %FALSE otherwise with @error set.
418 : *
419 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
420 : */
421 3 : gboolean bd_nvme_disconnect_by_path (const gchar *path, GError **error) {
422 : const gchar *p;
423 3 : gboolean found = FALSE;
424 :
425 3 : p = path;
426 3 : if (g_str_has_prefix (p, "/dev/"))
427 3 : p += 5;
428 :
429 3 : if (!_disconnect (NULL, p, error, &found))
430 0 : return FALSE;
431 :
432 3 : if (!found) {
433 2 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_NO_MATCH,
434 : "No controllers matching the %s device name found.", path);
435 2 : return FALSE;
436 : }
437 1 : return TRUE;
438 : }
439 :
440 :
441 : /**
442 : * bd_nvme_find_ctrls_for_ns:
443 : * @ns_sysfs_path: NVMe namespace device file.
444 : * @subsysnqn: (nullable): Limit matching to the specified subsystem NQN.
445 : * @host_nqn: (nullable): Limit matching to the specified host NQN.
446 : * @host_id: (nullable): Limit matching to the specified host ID.
447 : * @error: (out) (nullable): Place to store error (if any).
448 : *
449 : * A convenient utility function to look up all controllers associated
450 : * with a NVMe subsystem the specified namespace is part of.
451 : *
452 : * Returns: (transfer full) (array zero-terminated=1): list of controller sysfs paths
453 : * or %NULL in case of an error (with @error set).
454 : *
455 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
456 : */
457 27 : gchar ** bd_nvme_find_ctrls_for_ns (const gchar *ns_sysfs_path, const gchar *subsysnqn, const gchar *host_nqn, const gchar *host_id, GError **error G_GNUC_UNUSED) {
458 : GPtrArray *ptr_array;
459 : nvme_root_t root;
460 : nvme_host_t h;
461 : nvme_subsystem_t s;
462 : nvme_ctrl_t c;
463 : nvme_ns_t n;
464 : char realp[PATH_MAX];
465 : gchar *subsysnqn_p;
466 :
467 : /* libnvme strips trailing spaces and newlines when reading values from sysfs */
468 27 : subsysnqn_p = g_strdup (subsysnqn);
469 27 : if (subsysnqn_p)
470 15 : g_strchomp (subsysnqn_p);
471 :
472 27 : ptr_array = g_ptr_array_new ();
473 :
474 27 : root = nvme_scan (NULL);
475 27 : g_warn_if_fail (root != NULL);
476 :
477 54 : nvme_for_each_host (root, h) {
478 27 : if (host_nqn && g_strcmp0 (nvme_host_get_hostnqn (h), host_nqn) != 0)
479 6 : continue;
480 21 : if (host_id && g_strcmp0 (nvme_host_get_hostid (h), host_id) != 0)
481 6 : continue;
482 :
483 45 : nvme_for_each_subsystem (h, s) {
484 30 : gboolean found = FALSE;
485 :
486 30 : if (subsysnqn && g_strcmp0 (nvme_subsystem_get_nqn (s), subsysnqn_p) != 0)
487 12 : continue;
488 :
489 36 : nvme_subsystem_for_each_ctrl (s, c)
490 24 : nvme_ctrl_for_each_ns (c, n)
491 12 : if (realpath (nvme_ns_get_sysfs_dir (n), realp) &&
492 6 : g_strcmp0 (realp, ns_sysfs_path) == 0) {
493 0 : if (realpath (nvme_ctrl_get_sysfs_dir (c), realp)) {
494 0 : g_ptr_array_add (ptr_array, g_strdup (realp));
495 0 : break;
496 : }
497 : }
498 :
499 36 : nvme_subsystem_for_each_ns (s, n)
500 54 : if (realpath (nvme_ns_get_sysfs_dir (n), realp) &&
501 27 : g_strcmp0 (realp, ns_sysfs_path) == 0) {
502 9 : found = TRUE;
503 : /* at least one of the namespaces match, don't care about the rest */
504 9 : break;
505 : }
506 :
507 18 : if (found)
508 : /* add all controllers in the subsystem */
509 18 : nvme_subsystem_for_each_ctrl (s, c) {
510 9 : if (realpath (nvme_ctrl_get_sysfs_dir (c), realp)) {
511 9 : g_ptr_array_add (ptr_array, g_strdup (realp));
512 : }
513 : }
514 : }
515 : }
516 27 : nvme_free_tree (root);
517 27 : g_free (subsysnqn_p);
518 :
519 27 : g_ptr_array_add (ptr_array, NULL); /* trailing NULL element */
520 27 : return (gchar **) g_ptr_array_free (ptr_array, FALSE);
521 : }
522 :
523 :
524 : /**
525 : * bd_nvme_get_host_nqn:
526 : * @error: (out) (nullable): Place to store error (if any).
527 : *
528 : * Reads the Host NQN (NVM Qualified Name) value from the global `/etc/nvme/hostnqn`
529 : * file. An empty string is an indication that no Host NQN has been set.
530 : *
531 : * Returns: (transfer full): the Host NQN string or an empty string if none set.
532 : *
533 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
534 : */
535 2 : gchar * bd_nvme_get_host_nqn (G_GNUC_UNUSED GError **error) {
536 : char *hostnqn;
537 :
538 : /* FIXME: libnvme SYSCONFDIR might be different from PACKAGE_SYSCONF_DIR */
539 2 : hostnqn = nvmf_hostnqn_from_file ();
540 3 : return hostnqn ? hostnqn : g_strdup ("");
541 : }
542 :
543 : /**
544 : * bd_nvme_generate_host_nqn:
545 : * @error: (out) (nullable): Place to store error (if any).
546 : *
547 : * Compute new Host NQN (NVM Qualified Name) value for the current system. This
548 : * takes in account various system identifiers (DMI, device tree) with the goal
549 : * of a stable unique identifier whenever feasible.
550 : *
551 : * Returns: (transfer full): the Host NQN string or %NULL with @error set.
552 : *
553 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
554 : */
555 2 : gchar * bd_nvme_generate_host_nqn (GError **error) {
556 : char *nqn;
557 :
558 2 : nqn = nvmf_hostnqn_generate ();
559 2 : if (!nqn)
560 0 : g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
561 : "Unable to generate Host NQN.");
562 :
563 2 : return nqn;
564 : }
565 :
566 : /**
567 : * bd_nvme_get_host_id:
568 : * @error: (out) (nullable): Place to store error (if any).
569 : *
570 : * Reads the Host ID value from the global `/etc/nvme/hostid` file. An empty
571 : * string is an indication that no Host ID has been set.
572 : *
573 : * Returns: (transfer full): the Host ID string or an empty string if none set.
574 : *
575 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
576 : */
577 2 : gchar * bd_nvme_get_host_id (G_GNUC_UNUSED GError **error) {
578 : char *hostid;
579 :
580 2 : hostid = nvmf_hostid_from_file ();
581 3 : return hostid ? hostid : g_strdup ("");
582 : }
583 :
584 : /**
585 : * bd_nvme_set_host_nqn:
586 : * @host_nqn: The Host NVM Qualified Name.
587 : * @error: (out) (nullable): Place to store error (if any).
588 : *
589 : * Writes the Host NQN (NVM Qualified Name) value to the system `/etc/nvme/hostnqn` file.
590 : * No validation of the string is performed.
591 : *
592 : * Returns: %TRUE if the value was set successfully or %FALSE otherwise with @error set.
593 : *
594 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
595 : */
596 1 : gboolean bd_nvme_set_host_nqn (const gchar *host_nqn, GError **error) {
597 : gchar *path;
598 : gchar *filename;
599 : gchar *s;
600 : gboolean ret;
601 :
602 1 : g_return_val_if_fail (host_nqn != NULL, FALSE);
603 :
604 1 : path = g_build_path (G_DIR_SEPARATOR_S, PACKAGE_SYSCONF_DIR, "nvme", NULL);
605 1 : if (g_mkdir_with_parents (path, 0755) != 0 && errno != EEXIST) {
606 0 : g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
607 : "Error creating %s: %s",
608 0 : path, strerror_l (errno, _C_LOCALE));
609 0 : g_free (path);
610 0 : return FALSE;
611 : }
612 1 : filename = g_build_filename (path, "hostnqn", NULL);
613 1 : if (host_nqn[strlen (host_nqn) - 1] != '\n')
614 1 : s = g_strdup_printf ("%s\n", host_nqn);
615 : else
616 0 : s = g_strdup (host_nqn);
617 1 : ret = g_file_set_contents (filename, s, -1, error);
618 1 : if (ret)
619 1 : g_chmod (filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
620 :
621 1 : g_free (s);
622 1 : g_free (path);
623 1 : g_free (filename);
624 :
625 1 : return ret;
626 : }
627 :
628 : /**
629 : * bd_nvme_set_host_id:
630 : * @host_id: The Host ID.
631 : * @error: (out) (nullable): Place to store error (if any).
632 : *
633 : * Writes the Host ID value to the system `/etc/nvme/hostid` file.
634 : * No validation of the string is performed.
635 : *
636 : * Returns: %TRUE if the value was set successfully or %FALSE otherwise with @error set.
637 : *
638 : * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR
639 : */
640 1 : gboolean bd_nvme_set_host_id (const gchar *host_id, GError **error) {
641 : gchar *path;
642 : gchar *filename;
643 : gchar *s;
644 : gboolean ret;
645 :
646 1 : g_return_val_if_fail (host_id != NULL, FALSE);
647 :
648 1 : path = g_build_path (G_DIR_SEPARATOR_S, PACKAGE_SYSCONF_DIR, "nvme", NULL);
649 1 : if (g_mkdir_with_parents (path, 0755) != 0 && errno != EEXIST) {
650 0 : g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
651 : "Error creating %s: %s",
652 0 : path, strerror_l (errno, _C_LOCALE));
653 0 : g_free (path);
654 0 : return FALSE;
655 : }
656 1 : filename = g_build_filename (path, "hostid", NULL);
657 1 : if (host_id[strlen (host_id) - 1] != '\n')
658 1 : s = g_strdup_printf ("%s\n", host_id);
659 : else
660 0 : s = g_strdup (host_id);
661 1 : ret = g_file_set_contents (filename, s, -1, error);
662 1 : if (ret)
663 1 : g_chmod (filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
664 :
665 1 : g_free (s);
666 1 : g_free (path);
667 1 : g_free (filename);
668 :
669 1 : return ret;
670 : }
|