| File: | .build-ci/../src/config-convert.c |
| Warning: | line 376, column 9 Opened stream never closed. Potential resource leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2026, Dell Technologies Inc. or its subsidiaries. | |||
| 4 | * | |||
| 5 | * Authors: Martin Belanger <Martin.Belanger@dell.com> | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #include <errno(*__errno_location ()).h> | |||
| 9 | #include <stdio.h> | |||
| 10 | #include <string.h> | |||
| 11 | #include <unistd.h> | |||
| 12 | ||||
| 13 | #include <libnvme.h> | |||
| 14 | ||||
| 15 | #include "common.h" | |||
| 16 | #include "config-convert.h" | |||
| 17 | #include "fabrics.h" | |||
| 18 | #include "logging.h" | |||
| 19 | #include "nvme-print.h" | |||
| 20 | #include "argconfig.h" | |||
| 21 | #include "cleanup.h" | |||
| 22 | ||||
| 23 | #ifdef CONFIG_JSONC | |||
| 24 | #include "nvme-json.h" | |||
| 25 | ||||
| 26 | struct legacy_key { | |||
| 27 | const char *json_key; | |||
| 28 | const char *ini_key; | |||
| 29 | }; | |||
| 30 | ||||
| 31 | /* Map config.json keys from underscore to hyphen notation. */ | |||
| 32 | static const struct legacy_key int_keys[] = { | |||
| 33 | { "nr_io_queues", "nr-io-queues" }, | |||
| 34 | { "nr_write_queues", "nr-write-queues" }, | |||
| 35 | { "nr_poll_queues", "nr-poll-queues" }, | |||
| 36 | { "queue_size", "queue-size" }, | |||
| 37 | { "keep_alive_tmo", "keep-alive-tmo" }, | |||
| 38 | { "reconnect_delay", "reconnect-delay" }, | |||
| 39 | { "ctrl_loss_tmo", "ctrl-loss-tmo" }, | |||
| 40 | { "fast_io_fail_tmo", "fast-io-fail-tmo" }, | |||
| 41 | { "tos", "tos" }, | |||
| 42 | }; | |||
| 43 | ||||
| 44 | static const struct legacy_key bool_keys[] = { | |||
| 45 | { "duplicate_connect", "duplicate-connect" }, | |||
| 46 | { "disable_sqflow", "disable-sqflow" }, | |||
| 47 | { "hdr_digest", "hdr-digest" }, | |||
| 48 | { "data_digest", "data-digest" }, | |||
| 49 | { "tls", "tls" }, | |||
| 50 | { "concat", "concat" }, | |||
| 51 | }; | |||
| 52 | ||||
| 53 | static const struct legacy_key string_keys[] = { | |||
| 54 | { "tls_key", "tls-key" }, | |||
| 55 | { "tls_key_identity", "tls-key-identity" }, | |||
| 56 | { "keyring", "keyring" }, | |||
| 57 | { "dhchap_key", "dhchap-secret" }, | |||
| 58 | { "dhchap_ctrl_key", "dhchap-ctrl-secret" }, | |||
| 59 | }; | |||
| 60 | ||||
| 61 | static const char *map_key(const struct legacy_key *table, size_t n, | |||
| 62 | const char *json_key) | |||
| 63 | { | |||
| 64 | size_t i; | |||
| 65 | ||||
| 66 | for (i = 0; i < n; i++) | |||
| 67 | if (!strcmp(table[i].json_key, json_key)) | |||
| 68 | return table[i].ini_key; | |||
| 69 | ||||
| 70 | return NULL((void*)0); | |||
| 71 | } | |||
| 72 | ||||
| 73 | #define MAP_KEY(table, json_key)map_key(table, (sizeof(table) / sizeof((table)[0])), json_key ) map_key(table, ARRAY_SIZE(table)(sizeof(table) / sizeof((table)[0])), json_key) | |||
| 74 | ||||
| 75 | static const char *json_get_string(struct json_object *obj, const char *key) | |||
| 76 | { | |||
| 77 | struct json_object *val = json_object_object_get(obj, key); | |||
| 78 | ||||
| 79 | return val ? json_object_get_string(val) : NULL((void*)0); | |||
| 80 | } | |||
| 81 | ||||
| 82 | static bool_Bool json_get_bool(struct json_object *obj, const char *key) | |||
| 83 | { | |||
| 84 | struct json_object *val = json_object_object_get(obj, key); | |||
| 85 | ||||
| 86 | return val && json_object_get_boolean(val); | |||
| 87 | } | |||
| 88 | ||||
| 89 | /* Copy supported tunable and security parameters into @params. */ | |||
| 90 | static void apply_port_params(struct libnvmf_params *params, | |||
| 91 | struct json_object *port_obj) | |||
| 92 | { | |||
| 93 | json_object_object_foreach(port_obj, key_str, val_obj)char *key_str = ((void*)0); struct json_object *val_obj __attribute__ ((__unused__)) = ((void*)0); for (struct lh_entry *entrykey_str = lh_table_head(json_object_get_object(port_obj)), *entry_nextkey_str = ((void*)0); ({ if (entrykey_str) { key_str = (char *)lh_entry_k (entrykey_str); val_obj = (struct json_object *)lh_entry_v(entrykey_str ); entry_nextkey_str = lh_entry_next(entrykey_str); }; entrykey_str ; }); entrykey_str = entry_nextkey_str) { | |||
| 94 | const char *ini_key; | |||
| 95 | char buf[32]; | |||
| 96 | ||||
| 97 | ini_key = MAP_KEY(int_keys, key_str)map_key(int_keys, (sizeof(int_keys) / sizeof((int_keys)[0])), key_str); | |||
| 98 | if (ini_key) { | |||
| 99 | snprintf(buf, sizeof(buf), "%d", | |||
| 100 | json_object_get_int(val_obj)); | |||
| 101 | libnvmf_params_set(params, ini_key, buf); | |||
| 102 | continue; | |||
| 103 | } | |||
| 104 | ini_key = MAP_KEY(bool_keys, key_str)map_key(bool_keys, (sizeof(bool_keys) / sizeof((bool_keys)[0] )), key_str); | |||
| 105 | if (ini_key) { | |||
| 106 | libnvmf_params_set(params, ini_key, | |||
| 107 | json_object_get_boolean(val_obj) ? | |||
| 108 | "true" : "false"); | |||
| 109 | continue; | |||
| 110 | } | |||
| 111 | ini_key = MAP_KEY(string_keys, key_str)map_key(string_keys, (sizeof(string_keys) / sizeof((string_keys )[0])), key_str); | |||
| 112 | if (ini_key) | |||
| 113 | libnvmf_params_set(params, ini_key, | |||
| 114 | json_object_get_string(val_obj)); | |||
| 115 | } | |||
| 116 | } | |||
| 117 | ||||
| 118 | /* | |||
| 119 | * The DH-HMAC-CHAP secret is defined per (hostnqn, subsysnqn), not per path | |||
| 120 | * (NVMe Base Specification 2.3, section 8.3.5.5.7). If a port does not | |||
| 121 | * specify a secret, inherit the host-level default. If both are present but | |||
| 122 | * differ, keep the port-specific value and log the mismatch. | |||
| 123 | */ | |||
| 124 | static void apply_dhchap_default(struct libnvmf_params *params, | |||
| 125 | struct json_object *port_obj, const char *host_default) | |||
| 126 | { | |||
| 127 | const char *port_value = json_get_string(port_obj, "dhchap_key"); | |||
| 128 | ||||
| 129 | if (!host_default) | |||
| 130 | return; | |||
| 131 | ||||
| 132 | if (!port_value) { | |||
| 133 | libnvmf_params_set(params, "dhchap-secret", host_default); | |||
| 134 | } else if (strcmp(port_value, host_default)) { | |||
| 135 | nvme_show_verbose_info(nvme_show_verbose_message("config convert: dhchap_key differs between host default and one connection; keeping the connection's own value" ) | |||
| 136 | "config convert: dhchap_key differs between host default and one connection; keeping the connection's own value")nvme_show_verbose_message("config convert: dhchap_key differs between host default and one connection; keeping the connection's own value" ); | |||
| 137 | } | |||
| 138 | } | |||
| 139 | ||||
| 140 | static void apply_dc_persistent(struct libnvmf_params *params, | |||
| 141 | struct json_object *port_obj) | |||
| 142 | { | |||
| 143 | struct json_object *val = json_object_object_get(port_obj, "persistent"); | |||
| 144 | ||||
| 145 | if (val) | |||
| 146 | libnvmf_params_set(params, "persistent", | |||
| 147 | json_object_get_boolean(val) ? "true" : "false"); | |||
| 148 | } | |||
| 149 | ||||
| 150 | static int convert_port(struct libnvmf_config_emitter *emitter, | |||
| 151 | const char *hostnqn, const char *hostid, | |||
| 152 | const char *hostsymname, const char *host_dhchap_key, | |||
| 153 | const char *subsysnqn, struct json_object *port_obj) | |||
| 154 | { | |||
| 155 | struct libnvmf_params *params; | |||
| 156 | bool_Bool is_dc = json_get_bool(port_obj, "discovery"); | |||
| 157 | int ret; | |||
| 158 | ||||
| 159 | params = libnvmf_params_new(); | |||
| 160 | if (!params) | |||
| 161 | return -ENOMEM12; | |||
| 162 | ||||
| 163 | apply_port_params(params, port_obj); | |||
| 164 | apply_dhchap_default(params, port_obj, host_dhchap_key); | |||
| 165 | if (is_dc) | |||
| 166 | apply_dc_persistent(params, port_obj); | |||
| 167 | ||||
| 168 | ret = libnvmf_config_emit_add(emitter, is_dc, | |||
| 169 | json_get_string(port_obj, "transport"), | |||
| 170 | json_get_string(port_obj, "traddr"), | |||
| 171 | json_get_string(port_obj, "trsvcid"), | |||
| 172 | subsysnqn, | |||
| 173 | json_get_string(port_obj, "host_traddr"), | |||
| 174 | json_get_string(port_obj, "host_iface"), | |||
| 175 | hostnqn, hostid, params, hostsymname); | |||
| 176 | ||||
| 177 | libnvmf_params_free(params); | |||
| 178 | ||||
| 179 | /* | |||
| 180 | * A rejected entry (for example, invalid addressing or a conflicting | |||
| 181 | * persona) affects only that entry. Log the error and continue | |||
| 182 | * converting, matching json_parse_port()'s own tolerance. Stop only | |||
| 183 | * if memory allocation fails. | |||
| 184 | */ | |||
| 185 | if (ret == -ENOMEM12) | |||
| 186 | return ret; | |||
| 187 | if (ret) | |||
| 188 | nvme_show_error(nvme_show_message(1, "config convert: skipping an entry that could not be added: %s" , libnvme_strerror(-ret)) | |||
| 189 | "config convert: skipping an entry that could not be added: %s",nvme_show_message(1, "config convert: skipping an entry that could not be added: %s" , libnvme_strerror(-ret)) | |||
| 190 | libnvme_strerror(-ret))nvme_show_message(1, "config convert: skipping an entry that could not be added: %s" , libnvme_strerror(-ret)); | |||
| 191 | ||||
| 192 | return 0; | |||
| 193 | } | |||
| 194 | ||||
| 195 | static int convert_subsys(struct libnvmf_config_emitter *emitter, | |||
| 196 | const char *hostnqn, const char *hostid, | |||
| 197 | const char *hostsymname, const char *host_dhchap_key, | |||
| 198 | struct json_object *subsys_obj) | |||
| 199 | { | |||
| 200 | struct json_object *port_array; | |||
| 201 | const char *nqn = json_get_string(subsys_obj, "nqn"); | |||
| 202 | int p, ret; | |||
| 203 | ||||
| 204 | /* The well-known discovery NQN is the emitter's default value. */ | |||
| 205 | if (nqn && (!*nqn || !strcmp(nqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"))) | |||
| 206 | nqn = NULL((void*)0); | |||
| 207 | ||||
| 208 | port_array = json_object_object_get(subsys_obj, "ports"); | |||
| 209 | if (!port_array) | |||
| 210 | return 0; | |||
| 211 | ||||
| 212 | for (p = 0; p < json_object_array_length(port_array); p++) { | |||
| 213 | struct json_object *port_obj = | |||
| 214 | json_object_array_get_idx(port_array, p); | |||
| 215 | ||||
| 216 | if (!port_obj) | |||
| 217 | continue; | |||
| 218 | ret = convert_port(emitter, hostnqn, hostid, hostsymname, | |||
| 219 | host_dhchap_key, nqn, port_obj); | |||
| 220 | if (ret) | |||
| 221 | return ret; | |||
| 222 | } | |||
| 223 | ||||
| 224 | return 0; | |||
| 225 | } | |||
| 226 | ||||
| 227 | static int convert_host(struct libnvmf_config_emitter *emitter, | |||
| 228 | struct json_object *host_obj) | |||
| 229 | { | |||
| 230 | struct json_object *subsys_array; | |||
| 231 | const char *hostnqn = json_get_string(host_obj, "hostnqn"); | |||
| 232 | const char *hostid = json_get_string(host_obj, "hostid"); | |||
| 233 | const char *hostsymname = json_get_string(host_obj, "hostsymname"); | |||
| 234 | const char *host_dhchap_key = json_get_string(host_obj, "dhchap_key"); | |||
| 235 | int s, ret; | |||
| 236 | ||||
| 237 | subsys_array = json_object_object_get(host_obj, "subsystems"); | |||
| 238 | if (!subsys_array) | |||
| 239 | return 0; | |||
| 240 | ||||
| 241 | for (s = 0; s < json_object_array_length(subsys_array); s++) { | |||
| 242 | struct json_object *subsys_obj = | |||
| 243 | json_object_array_get_idx(subsys_array, s); | |||
| 244 | ||||
| 245 | if (!subsys_obj) | |||
| 246 | continue; | |||
| 247 | ret = convert_subsys(emitter, hostnqn, hostid, hostsymname, | |||
| 248 | host_dhchap_key, subsys_obj); | |||
| 249 | if (ret) | |||
| 250 | return ret; | |||
| 251 | } | |||
| 252 | ||||
| 253 | return 0; | |||
| 254 | } | |||
| 255 | ||||
| 256 | int nvme_config_convert_json(struct libnvmf_config_emitter *emitter, | |||
| 257 | const char *json_file) | |||
| 258 | { | |||
| 259 | struct json_object *json_root, *host_array, *host_obj; | |||
| 260 | int h, ret; | |||
| 261 | ||||
| 262 | json_root = json_object_from_file(json_file); | |||
| 263 | if (!json_root) { | |||
| 264 | nvme_show_error("failed to parse %s: %s", json_file,nvme_show_message(1, "failed to parse %s: %s", json_file, json_util_get_last_err ()) | |||
| 265 | json_util_get_last_err())nvme_show_message(1, "failed to parse %s: %s", json_file, json_util_get_last_err ()); | |||
| 266 | return -EPROTO71; | |||
| 267 | } | |||
| 268 | ||||
| 269 | if (json_object_is_type(json_root, json_type_object)) { | |||
| 270 | /* Current format: { "hosts": [ ... ] } */ | |||
| 271 | host_array = json_object_object_get(json_root, "hosts"); | |||
| 272 | if (!host_array || | |||
| 273 | !json_object_is_type(host_array, json_type_array)) { | |||
| 274 | nvme_show_error("%s: expected a 'hosts' array",nvme_show_message(1, "%s: expected a 'hosts' array", json_file ) | |||
| 275 | json_file)nvme_show_message(1, "%s: expected a 'hosts' array", json_file ); | |||
| 276 | json_object_put(json_root); | |||
| 277 | return -EPROTO71; | |||
| 278 | } | |||
| 279 | } else if (json_object_is_type(json_root, json_type_array)) { | |||
| 280 | /* Legacy pre-3.0 format: a bare top-level array of hosts. */ | |||
| 281 | host_array = json_root; | |||
| 282 | } else { | |||
| 283 | nvme_show_error("%s: expected a JSON object or array",nvme_show_message(1, "%s: expected a JSON object or array", json_file ) | |||
| 284 | json_file)nvme_show_message(1, "%s: expected a JSON object or array", json_file ); | |||
| 285 | json_object_put(json_root); | |||
| 286 | return -EPROTO71; | |||
| 287 | } | |||
| 288 | ||||
| 289 | for (h = 0; h < json_object_array_length(host_array); h++) { | |||
| 290 | host_obj = json_object_array_get_idx(host_array, h); | |||
| 291 | if (!host_obj) | |||
| 292 | continue; | |||
| 293 | ret = convert_host(emitter, host_obj); | |||
| 294 | if (ret) { | |||
| 295 | json_object_put(json_root); | |||
| 296 | return ret; | |||
| 297 | } | |||
| 298 | } | |||
| 299 | ||||
| 300 | json_object_put(json_root); | |||
| 301 | ||||
| 302 | return 0; | |||
| 303 | } | |||
| 304 | ||||
| 305 | #else /* CONFIG_JSONC */ | |||
| 306 | ||||
| 307 | int nvme_config_convert_json(struct libnvmf_config_emitter *emitter, | |||
| 308 | const char *json_file) | |||
| 309 | { | |||
| 310 | nvme_show_error(nvme_show_message(1, "built without json-c; config.json conversion unavailable" ) | |||
| 311 | "built without json-c; config.json conversion unavailable")nvme_show_message(1, "built without json-c; config.json conversion unavailable" ); | |||
| 312 | return -ENOTSUP95; | |||
| 313 | } | |||
| 314 | ||||
| 315 | #endif /* CONFIG_JSONC */ | |||
| 316 | ||||
| 317 | int nvme_config_convert_discovery_args(struct libnvmf_config_emitter *emitter, | |||
| 318 | const struct nvmf_args *fa, enum libnvmf_tristate persistent, | |||
| 319 | enum libnvmf_tristate epcsd) | |||
| 320 | { | |||
| 321 | struct libnvmf_params *params; | |||
| 322 | int ret; | |||
| 323 | ||||
| 324 | params = libnvmf_params_new(); | |||
| 325 | if (!params) | |||
| 326 | return -ENOMEM12; | |||
| 327 | ||||
| 328 | nvmf_args_to_params(params, fa); | |||
| 329 | if (persistent != LIBNVMF_TRISTATE_UNSET) | |||
| 330 | libnvmf_params_set(params, "persistent", | |||
| 331 | persistent == LIBNVMF_TRISTATE_TRUE ? "true" : "false"); | |||
| 332 | if (epcsd != LIBNVMF_TRISTATE_UNSET) | |||
| 333 | libnvmf_params_set(params, "epcsd", | |||
| 334 | epcsd == LIBNVMF_TRISTATE_TRUE ? "true" : "false"); | |||
| 335 | ||||
| 336 | ret = libnvmf_config_emit_add(emitter, true1, fa->transport, fa->traddr, | |||
| 337 | fa->trsvcid, fa->subsysnqn, fa->host_traddr, | |||
| 338 | fa->host_iface, fa->hostnqn, fa->hostid, params, NULL((void*)0)); | |||
| 339 | ||||
| 340 | libnvmf_params_free(params); | |||
| 341 | ||||
| 342 | /* | |||
| 343 | * A rejected entry affects only that entry. Log the error and | |||
| 344 | * continue converting. Stop only if memory allocation fails. | |||
| 345 | */ | |||
| 346 | if (ret == -ENOMEM12) | |||
| 347 | return ret; | |||
| 348 | if (ret) | |||
| 349 | nvme_show_error(nvme_show_message(1, "discovery.conf: skipping a line that could not be added: %s" , libnvme_strerror(-ret)) | |||
| 350 | "discovery.conf: skipping a line that could not be added: %s",nvme_show_message(1, "discovery.conf: skipping a line that could not be added: %s" , libnvme_strerror(-ret)) | |||
| 351 | libnvme_strerror(-ret))nvme_show_message(1, "discovery.conf: skipping a line that could not be added: %s" , libnvme_strerror(-ret)); | |||
| 352 | ||||
| 353 | return 0; | |||
| 354 | } | |||
| 355 | ||||
| 356 | int nvme_config_convert_discovery(struct libnvmf_config_emitter *emitter, | |||
| 357 | const char *disc_file) | |||
| 358 | { | |||
| 359 | __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *f = NULL((void*)0); | |||
| 360 | static char line[4096]; | |||
| 361 | int ret; | |||
| 362 | ||||
| 363 | f = fopen(disc_file, "r"); | |||
| ||||
| 364 | if (!f
| |||
| 365 | nvme_show_error("failed to open %s: %s", disc_file,nvme_show_message(1, "failed to open %s: %s", disc_file, strerror ((*__errno_location ()))) | |||
| 366 | strerror(errno))nvme_show_message(1, "failed to open %s: %s", disc_file, strerror ((*__errno_location ()))); | |||
| 367 | return -errno(*__errno_location ()); | |||
| 368 | } | |||
| 369 | ||||
| 370 | while (fgets(line, sizeof(line), f)) { | |||
| 371 | ret = nvmf_convert_discovery_line(emitter, line); | |||
| 372 | if (ret) | |||
| 373 | return ret; | |||
| 374 | } | |||
| 375 | ||||
| 376 | return 0; | |||
| ||||
| 377 | } | |||
| 378 | ||||
| 379 | /* | |||
| 380 | * Best effort. The configuration has already been installed. @path is | |||
| 381 | * left untouched so a rollback to a pre-INI version still finds it; the | |||
| 382 | * symlink marks it as already converted. | |||
| 383 | */ | |||
| 384 | static void mark_converted(const char *path) | |||
| 385 | { | |||
| 386 | __cleanup_free__attribute__((cleanup(shr_freep))) char *dst = NULL((void*)0); | |||
| 387 | ||||
| 388 | if (asprintf(&dst, "%s.converted", path) < 0) | |||
| 389 | return; | |||
| 390 | ||||
| 391 | if (symlink(path, dst)) | |||
| 392 | nvme_show_error(nvme_show_message(1, "converted %s but failed to mark it as converted (%s): %s" , path, dst, strerror((*__errno_location ()))) | |||
| 393 | "converted %s but failed to mark it as converted (%s): %s",nvme_show_message(1, "converted %s but failed to mark it as converted (%s): %s" , path, dst, strerror((*__errno_location ()))) | |||
| 394 | path, dst, strerror(errno))nvme_show_message(1, "converted %s but failed to mark it as converted (%s): %s" , path, dst, strerror((*__errno_location ()))); | |||
| 395 | } | |||
| 396 | ||||
| 397 | /* | |||
| 398 | * True if the @path.converted symlink exists and resolves. A dangling | |||
| 399 | * symlink (its target since removed) makes this return false, but that | |||
| 400 | * alone does not mean @path needs converting -- callers must also check | |||
| 401 | * whether @path itself exists. | |||
| 402 | */ | |||
| 403 | static bool_Bool already_converted(const char *path) | |||
| 404 | { | |||
| 405 | __cleanup_free__attribute__((cleanup(shr_freep))) char *converted = NULL((void*)0); | |||
| 406 | ||||
| 407 | if (asprintf(&converted, "%s.converted", path) < 0) | |||
| 408 | return false0; | |||
| 409 | ||||
| 410 | return !access(converted, F_OK0); | |||
| 411 | } | |||
| 412 | ||||
| 413 | static int install_converted(struct libnvmf_config_emitter *emitter, | |||
| 414 | const char *output_file, const char *json_path, | |||
| 415 | const char *disc_path, bool_Bool converted_json, | |||
| 416 | bool_Bool converted_disc, bool_Bool force) | |||
| 417 | { | |||
| 418 | int ret; | |||
| 419 | ||||
| 420 | ret = libnvmf_config_emit_install(emitter, output_file, force); | |||
| 421 | if (ret == -EEXIST17) { | |||
| 422 | nvme_show_error("%s already exists; refusing to overwrite",nvme_show_message(1, "%s already exists; refusing to overwrite" , output_file) | |||
| 423 | output_file)nvme_show_message(1, "%s already exists; refusing to overwrite" , output_file); | |||
| 424 | return ret; | |||
| 425 | } | |||
| 426 | if (ret) { | |||
| 427 | nvme_show_error("failed to write %s: %s", output_file,nvme_show_message(1, "failed to write %s: %s", output_file, libnvme_strerror (-ret)) | |||
| 428 | libnvme_strerror(-ret))nvme_show_message(1, "failed to write %s: %s", output_file, libnvme_strerror (-ret)); | |||
| 429 | return ret; | |||
| 430 | } | |||
| 431 | ||||
| 432 | if (converted_json) | |||
| 433 | mark_converted(json_path); | |||
| 434 | if (converted_disc) | |||
| 435 | mark_converted(disc_path); | |||
| 436 | ||||
| 437 | return 0; | |||
| 438 | } | |||
| 439 | ||||
| 440 | int nvme_config_convert_auto(struct libnvme_global_ctx *ctx, | |||
| 441 | const char *config_file, char **ini_path) | |||
| 442 | { | |||
| 443 | struct libnvmf_config_emitter *emitter; | |||
| 444 | const char *json_path = config_file; | |||
| 445 | const char *ext; | |||
| 446 | bool_Bool is_default; | |||
| 447 | bool_Bool json_exists, json_done; | |||
| 448 | bool_Bool disc_exists, disc_done; | |||
| 449 | bool_Bool have_json, have_disc; | |||
| 450 | bool_Bool converted_json = false0, converted_disc = false0; | |||
| 451 | int ret; | |||
| 452 | ||||
| 453 | *ini_path = NULL((void*)0); | |||
| 454 | ||||
| 455 | is_default = !strcmp(config_file, PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf") || | |||
| 456 | !strcmp(config_file, PATH_NVMF_CONFIG"/usr/local/etc" "/nvme/config.json"); | |||
| 457 | if (is_default) { | |||
| 458 | json_path = PATH_NVMF_CONFIG"/usr/local/etc" "/nvme/config.json"; | |||
| 459 | *ini_path = strdup(PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf"); | |||
| 460 | } else { | |||
| 461 | ext = strrchr(config_file, '.')_Generic (0 ? (config_file) : (void *) 1, const void *: (const char *) (strrchr (config_file, '.')), default: strrchr (config_file , '.')); | |||
| 462 | if (!ext || strcmp(ext, ".json")) { | |||
| 463 | *ini_path = strdup(config_file); | |||
| 464 | return *ini_path ? 0 : -ENOMEM12; | |||
| 465 | } | |||
| 466 | ||||
| 467 | if (asprintf(ini_path, "%.*s.conf", | |||
| 468 | (int)(ext - config_file), config_file) < 0) | |||
| 469 | return -ENOMEM12; | |||
| 470 | } | |||
| 471 | if (!*ini_path) | |||
| 472 | return -ENOMEM12; | |||
| 473 | ||||
| 474 | if (!access(*ini_path, F_OK0)) | |||
| 475 | return 0; | |||
| 476 | ||||
| 477 | json_exists = !access(json_path, F_OK0); | |||
| 478 | json_done = already_converted(json_path); | |||
| 479 | have_json = json_exists && !json_done; | |||
| 480 | ||||
| 481 | disc_exists = is_default && !access(PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf", F_OK0); | |||
| 482 | disc_done = is_default && already_converted(PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf"); | |||
| 483 | have_disc = disc_exists && !disc_done; | |||
| 484 | ||||
| 485 | if (!have_json && !have_disc) { | |||
| 486 | /* Default path: nothing to convert is fine, proceed empty. | |||
| 487 | * Custom path: never existed and never converted is a | |||
| 488 | * real error, not silent-empty. | |||
| 489 | */ | |||
| 490 | if (!is_default && !json_exists && !json_done) { | |||
| 491 | nvme_show_error("%s: no such file", json_path)nvme_show_message(1, "%s: no such file", json_path); | |||
| 492 | return -ENOENT2; | |||
| 493 | } | |||
| 494 | return 0; | |||
| 495 | } | |||
| 496 | ||||
| 497 | emitter = libnvmf_config_emit_new(ctx); | |||
| 498 | if (!emitter) | |||
| 499 | return -ENOMEM12; | |||
| 500 | ||||
| 501 | if (have_json) { | |||
| 502 | ret = nvme_config_convert_json(emitter, json_path); | |||
| 503 | if (ret) | |||
| 504 | goto out; | |||
| 505 | converted_json = true1; | |||
| 506 | } | |||
| 507 | ||||
| 508 | if (have_disc) { | |||
| 509 | ret = nvme_config_convert_discovery(emitter, PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf"); | |||
| 510 | if (ret) | |||
| 511 | goto out; | |||
| 512 | converted_disc = true1; | |||
| 513 | } | |||
| 514 | ||||
| 515 | ret = install_converted(emitter, *ini_path, json_path, PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf", | |||
| 516 | converted_json, converted_disc, false0); | |||
| 517 | if (ret) | |||
| 518 | goto out; | |||
| 519 | ||||
| 520 | nvme_show_error(nvme_show_message(1, "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on" , *ini_path, converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", *ini_path) | |||
| 521 | "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on",nvme_show_message(1, "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on" , *ini_path, converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", *ini_path) | |||
| 522 | *ini_path, converted_json ? json_path : "",nvme_show_message(1, "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on" , *ini_path, converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", *ini_path) | |||
| 523 | (converted_json && converted_disc) ? " and " : "",nvme_show_message(1, "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on" , *ini_path, converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", *ini_path) | |||
| 524 | converted_disc ? PATH_NVMF_DISC : "", *ini_path)nvme_show_message(1, "no %s found; converted legacy %s%s%s to it -- the original is preserved for rollback, marked converted by a *.converted symlink; use %s from now on" , *ini_path, converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", *ini_path); | |||
| 525 | ||||
| 526 | out: | |||
| 527 | libnvmf_config_emit_free(emitter); | |||
| 528 | ||||
| 529 | return ret; | |||
| 530 | } | |||
| 531 | ||||
| 532 | int nvme_config_convert(const char *desc, int argc, char **argv) | |||
| 533 | { | |||
| 534 | char *config_file = NULL((void*)0); | |||
| 535 | char *output_file = NULL((void*)0); | |||
| 536 | const char *target; | |||
| 537 | const char *json_path; | |||
| 538 | bool_Bool force = false0; | |||
| 539 | bool_Bool converted_json = false0, converted_disc = false0; | |||
| 540 | bool_Bool json_already_done = false0, disc_already_done = false0; | |||
| 541 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 542 | struct libnvmf_config_emitter *emitter = NULL((void*)0); | |||
| 543 | int ret; | |||
| 544 | ||||
| 545 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 546 | OPT_STRING("config", 'J', "FILE", &config_file,{"config", 'J', "FILE", CFG_STRING, &config_file, 1, "convert this JSON file (default: config.json)" , 0, } | |||
| 547 | "convert this JSON file (default: config.json)"){"config", 'J', "FILE", CFG_STRING, &config_file, 1, "convert this JSON file (default: config.json)" , 0, }, | |||
| 548 | OPT_STRING("output", 'o', "FILE", &output_file,{"output", 'o', "FILE", CFG_STRING, &output_file, 1, "write result here (default: nvme-fabrics.conf)" , 0, } | |||
| 549 | "write result here (default: nvme-fabrics.conf)"){"output", 'o', "FILE", CFG_STRING, &output_file, 1, "write result here (default: nvme-fabrics.conf)" , 0, }, | |||
| 550 | OPT_FLAG("force", 0, &force,{"force", 0, ((void*)0), CFG_FLAG, &force, 0, "overwrite an existing target" , 0, } | |||
| 551 | "overwrite an existing target"){"force", 0, ((void*)0), CFG_FLAG, &force, 0, "overwrite an existing target" , 0, }, | |||
| 552 | OPT_END(){ ((void*)0) } | |||
| 553 | }; | |||
| 554 | ||||
| 555 | ret = parse_args(argc, argv, desc, opts); | |||
| 556 | if (ret) | |||
| 557 | return ret; | |||
| 558 | ||||
| 559 | ret = nvme_create_global_ctx(&ctx); | |||
| 560 | if (ret) | |||
| 561 | return ret; | |||
| 562 | ||||
| 563 | emitter = libnvmf_config_emit_new(ctx); | |||
| 564 | if (!emitter) | |||
| 565 | return -ENOMEM12; | |||
| 566 | ||||
| 567 | json_path = config_file ? config_file : PATH_NVMF_CONFIG"/usr/local/etc" "/nvme/config.json"; | |||
| 568 | if (already_converted(json_path)) { | |||
| 569 | json_already_done = true1; | |||
| 570 | } else if (!access(json_path, F_OK0)) { | |||
| 571 | ret = nvme_config_convert_json(emitter, json_path); | |||
| 572 | if (ret) | |||
| 573 | goto out; | |||
| 574 | converted_json = true1; | |||
| 575 | } else if (config_file) { | |||
| 576 | /* | |||
| 577 | * An explicit --config to a file that neither exists nor was | |||
| 578 | * ever converted: let the JSON parser produce its own | |||
| 579 | * "failed to parse" error instead of silently no-op'ing, | |||
| 580 | * since this was an explicit ask. | |||
| 581 | */ | |||
| 582 | ret = nvme_config_convert_json(emitter, json_path); | |||
| 583 | if (ret) | |||
| 584 | goto out; | |||
| 585 | converted_json = true1; | |||
| 586 | } | |||
| 587 | ||||
| 588 | if (already_converted(PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf")) { | |||
| 589 | disc_already_done = true1; | |||
| 590 | } else if (!access(PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf", F_OK0)) { | |||
| 591 | ret = nvme_config_convert_discovery(emitter, PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf"); | |||
| 592 | if (ret) | |||
| 593 | goto out; | |||
| 594 | converted_disc = true1; | |||
| 595 | } | |||
| 596 | ||||
| 597 | if (!converted_json && !converted_disc) { | |||
| 598 | if (json_already_done || disc_already_done) { | |||
| 599 | nvme_show_result("already converted; nothing to do")nvme_show_message(0, "already converted; nothing to do"); | |||
| 600 | ret = 0; | |||
| 601 | goto out; | |||
| 602 | } | |||
| 603 | nvme_show_error("nothing to convert: neither %s nor %s exists",nvme_show_message(1, "nothing to convert: neither %s nor %s exists" , json_path, "/usr/local/etc" "/nvme/discovery.conf") | |||
| 604 | json_path, PATH_NVMF_DISC)nvme_show_message(1, "nothing to convert: neither %s nor %s exists" , json_path, "/usr/local/etc" "/nvme/discovery.conf"); | |||
| 605 | ret = -ENOENT2; | |||
| 606 | goto out; | |||
| 607 | } | |||
| 608 | ||||
| 609 | target = output_file ? output_file : PATH_NVMF_INI"/usr/local/etc" "/nvme/nvme-fabrics.conf"; | |||
| 610 | ret = install_converted(emitter, target, json_path, PATH_NVMF_DISC"/usr/local/etc" "/nvme/discovery.conf", | |||
| 611 | converted_json, converted_disc, force); | |||
| 612 | if (!ret) { | |||
| 613 | nvme_show_result(nvme_show_message(0, "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink" , converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", target) | |||
| 614 | "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink",nvme_show_message(0, "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink" , converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", target) | |||
| 615 | converted_json ? json_path : "",nvme_show_message(0, "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink" , converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", target) | |||
| 616 | (converted_json && converted_disc) ? " and " : "",nvme_show_message(0, "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink" , converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", target) | |||
| 617 | converted_disc ? PATH_NVMF_DISC : "", target)nvme_show_message(0, "converted legacy %s%s%s to %s -- the original is preserved for rollback, marked converted by a *.converted symlink" , converted_json ? json_path : "", (converted_json && converted_disc) ? " and " : "", converted_disc ? "/usr/local/etc" "/nvme/discovery.conf" : "", target); | |||
| 618 | } | |||
| 619 | ||||
| 620 | out: | |||
| 621 | libnvmf_config_emit_free(emitter); | |||
| 622 | ||||
| 623 | return ret; | |||
| 624 | } |