LCOV - code coverage report
Current view: top level - plugins/nvme - nvme-fabrics.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 59.6 % 260 155
Test Date: 2026-01-23 09:12:16 Functions: 100.0 % 11 11
Legend: Lines: hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1