]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/storagetm/storagetm.c
siphash24: introduce siphash24_compress_typesafe() macro
[thirdparty/systemd.git] / src / storagetm / storagetm.c
CommitLineData
1761066b
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <getopt.h>
4#include <sys/file.h>
5
6#include "af-list.h"
7#include "alloc-util.h"
8#include "blockdev-util.h"
9#include "build.h"
10#include "daemon-util.h"
11#include "device-util.h"
12#include "fd-util.h"
13#include "fileio.h"
14#include "format-util.h"
15#include "fs-util.h"
abc19a6f 16#include "id128-util.h"
1761066b
LP
17#include "local-addresses.h"
18#include "loop-util.h"
19#include "main-func.h"
abc19a6f 20#include "os-util.h"
1761066b
LP
21#include "parse-argument.h"
22#include "path-util.h"
95d54802 23#include "plymouth-util.h"
1761066b
LP
24#include "pretty-print.h"
25#include "process-util.h"
26#include "random-util.h"
27#include "recurse-dir.h"
28#include "socket-util.h"
29#include "terminal-util.h"
30#include "udev-util.h"
31
32static char **arg_devices = NULL;
33static char *arg_nqn = NULL;
34static int arg_all = 0;
35
36STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
37STATIC_DESTRUCTOR_REGISTER(arg_nqn, freep);
38
39static int help(void) {
40 _cleanup_free_ char *link = NULL;
41 int r;
42
43 r = terminal_urlify_man("systemd-storagetm", "8", &link);
44 if (r < 0)
45 return log_oom();
46
47 printf("%s [OPTIONS...] [DEVICE...]\n"
48 "\n%sExpose a block device or regular file as NVMe-TCP volume.%s\n\n"
49 " -h --help Show this help\n"
50 " --version Show package version\n"
51 " --nqn=STRING Select NQN (NVMe Qualified Name)\n"
52 " -a --all Expose all devices\n"
53 "\nSee the %s for details.\n",
54 program_invocation_short_name,
55 ansi_highlight(),
56 ansi_normal(),
57 link);
58
59 return 0;
60}
61
62static int parse_argv(int argc, char *argv[]) {
63
64 enum {
65 ARG_NQN = 0x100,
66 ARG_VERSION,
67 };
68
69 static const struct option options[] = {
70 { "help", no_argument, NULL, 'h' },
71 { "version", no_argument, NULL, ARG_VERSION },
72 { "nqn", required_argument, NULL, ARG_NQN },
73 { "all", no_argument, NULL, 'a' },
74 {}
75 };
76
77 int r, c;
78
79 assert(argc >= 0);
80 assert(argv);
81
82 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0)
83
84 switch (c) {
85
86 case 'h':
87 return help();
88
89 case ARG_VERSION:
90 return version();
91
92 case ARG_NQN:
93 if (!filename_is_valid(optarg))
94 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "NQN invalid: %s", optarg);
95
96 if (free_and_strdup(&arg_nqn, optarg) < 0)
97 return log_oom();
98
99 break;
100
101 case 'a':
102 arg_all++;
103 break;
104
105 case '?':
106 return -EINVAL;
107
108 default:
109 assert_not_reached();
110 }
111
112 if (arg_all > 0) {
113 if (argc > optind)
114 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expects no further arguments if --all/-a is specified.");
115 } else {
116 if (optind >= argc)
117 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expecting device name or --all/-a.");
118
119 for (int i = optind; i < argc; i++)
120 if (!path_is_valid(argv[i]))
121 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid path: %s", argv[i]);
122
123 arg_devices = strv_copy(argv + optind);
124 }
125
126 if (!arg_nqn) {
127 sd_id128_t id;
128
129 r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(b4,f9,4e,52,b8,e2,45,db,88,84,6e,2e,c3,f4,ef,18), &id);
130 if (r < 0)
131 return log_error_errno(r, "Failed to get machine ID: %m");
132
133 /* See NVM Express Base Specification 2.0c, 4.5 "NVMe Qualified Names" */
134 if (asprintf(&arg_nqn, "nqn.2023-10.io.systemd:storagetm." SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
135 return log_oom();
136 }
137
138 return 1;
139}
140
141typedef struct NvmeSubsystem {
142 char *name;
143 struct stat device_stat;
144 int device_fd;
145 int nvme_all_subsystems_fd; /* The /sys/kernel/config/nvmet/subsystems/ dir, that contains all subsystems */
146 int nvme_our_subsystem_fd; /* Our private subsystem dir below it. */
147 char *device;
148} NvmeSubsystem;
149
150static NvmeSubsystem* nvme_subsystem_free(NvmeSubsystem *s) {
151 if (!s)
152 return NULL;
153
154 free(s->name);
155 safe_close(s->nvme_all_subsystems_fd);
156 safe_close(s->nvme_our_subsystem_fd);
157 safe_close(s->device_fd);
158 free(s->device);
159
160 return mfree(s);
161}
162
163static int nvme_subsystem_unlink(NvmeSubsystem *s) {
164 int r;
165
166 assert(s);
167
168 if (s->nvme_our_subsystem_fd >= 0) {
169 _cleanup_close_ int namespaces_fd = -EBADF;
170
171 namespaces_fd = openat(s->nvme_our_subsystem_fd, "namespaces", O_CLOEXEC|O_DIRECTORY|O_RDONLY);
172 if (namespaces_fd < 0)
173 log_warning_errno(errno, "Failed to open 'namespaces' directory of subsystem '%s': %m", s->name);
174 else {
175 _cleanup_free_ DirectoryEntries *de = NULL;
176
177 r = readdir_all(namespaces_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
178 if (r < 0)
179 log_warning_errno(r, "Failed to read 'namespaces' dir of subsystem '%s', ignoring: %m", s->name);
180 else {
181 FOREACH_ARRAY(ee, de->entries, de->n_entries) {
182 _cleanup_free_ char *enable_fn = NULL;
183 const struct dirent *e = *ee;
184
185 enable_fn = path_join(e->d_name, "enable");
186 if (!enable_fn)
187 return log_oom();
188
189 r = write_string_file_at(namespaces_fd, enable_fn, "0", WRITE_STRING_FILE_DISABLE_BUFFER);
190 if (r < 0)
191 log_warning_errno(r, "Failed to disable namespace '%s' of NVME subsystem '%s', ignoring: %m", e->d_name, s->name);
192
193 if (unlinkat(namespaces_fd, e->d_name, AT_REMOVEDIR) < 0 && errno != ENOENT)
194 log_warning_errno(errno, "Failed to remove namespace '%s' of NVME subsystem '%s', ignoring: %m", e->d_name, s->name);
195 }
196 }
197 }
198
199 s->nvme_our_subsystem_fd = safe_close(s->nvme_our_subsystem_fd);
200 }
201
202 if (s->nvme_all_subsystems_fd >= 0 && s->name) {
203 if (unlinkat(s->nvme_all_subsystems_fd, s->name, AT_REMOVEDIR) < 0 && errno != ENOENT)
204 log_warning_errno(errno, "Failed to remove NVME subsystem '%s', ignoring: %m", s->name);
205
206 s->nvme_all_subsystems_fd = safe_close(s->nvme_all_subsystems_fd); /* Invalidate the subsystems/ dir fd, to remember we unlinked the thing already */
207
208 log_info("NVME subsystem '%s' removed.", s->name);
209 }
210
211 return 0;
212}
213
214static NvmeSubsystem *nvme_subsystem_destroy(NvmeSubsystem *s) {
215 if (!s)
216 return NULL;
217
218 (void) nvme_subsystem_unlink(s);
219
220 return nvme_subsystem_free(s);
221}
222
223DEFINE_TRIVIAL_CLEANUP_FUNC(NvmeSubsystem*, nvme_subsystem_destroy);
224
abc19a6f
LP
225static int nvme_subsystem_write_metadata(int subsystem_fd, sd_device *device) {
226 _cleanup_free_ char *image_id = NULL, *image_version = NULL, *os_id = NULL, *os_version = NULL, *combined_model = NULL, *synthetic_serial = NULL;
227 const char *hwmodel = NULL, *hwserial = NULL, *w;
228 int r;
229
230 assert(subsystem_fd >= 0);
231
232 (void) parse_os_release(
233 /* root= */ NULL,
234 "IMAGE_ID", &image_id,
235 "IMAGE_VERSION", &image_version,
236 "ID", &os_id,
237 "VERSION_ID", &os_version);
238
239 if (device) {
240 (void) device_get_model_string(device, &hwmodel);
241 (void) sd_device_get_property_value(device, "ID_SERIAL_SHORT", &hwserial);
242 }
243
244 w = secure_getenv("SYSTEMD_NVME_MODEL");
245 if (!w) {
246 if (hwmodel && (image_id || os_id)) {
247 if (asprintf(&combined_model, "%s (%s)", hwmodel, image_id ?: os_id) < 0)
248 return log_oom();
249 w = combined_model;
250 } else
251 w = hwmodel ?: image_id ?: os_id;
252 }
253 if (w) {
254 _cleanup_free_ char *truncated = strndup(w, 40); /* kernel refuses more than 40 chars (as per nvme spec) */
255
256 /* The default string stored in 'attr_model' is "Linux" btw. */
257 r = write_string_file_at(subsystem_fd, "attr_model", truncated, WRITE_STRING_FILE_DISABLE_BUFFER);
258 if (r < 0)
259 log_warning_errno(r, "Failed to set model of subsystem to '%s', ignoring: %m", w);
260 }
261
262 w = secure_getenv("SYSTEMD_NVME_FIRMWARE");
263 if (!w)
264 w = image_version ?: os_version;
265 if (w) {
266 _cleanup_free_ char *truncated = strndup(w, 8); /* kernel refuses more than 8 chars (as per nvme spec) */
267 if (!truncated)
268 return log_oom();
269
270 /* The default string stored in 'attr_firmware' is `uname -r` btw, but truncated to 8 chars. */
271 r = write_string_file_at(subsystem_fd, "attr_firmware", truncated, WRITE_STRING_FILE_DISABLE_BUFFER);
272 if (r < 0)
273 log_warning_errno(r, "Failed to set model of subsystem to '%s', ignoring: %m", truncated);
274 }
275
276 w = secure_getenv("SYSTEMD_NVME_SERIAL");
277 if (!w) {
278 if (hwserial)
279 w = hwserial;
280 else {
281 sd_id128_t mid;
282
283 r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(39,7f,4d,bf,1e,bf,46,6d,b3,cb,45,b8,0d,49,5b,c1), &mid);
284 if (r < 0)
285 log_warning_errno(r, "Failed to get machine ID, ignoring: %m");
286 else {
287 if (asprintf(&synthetic_serial, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(mid)) < 0)
288 return log_oom();
289 w = synthetic_serial;
290 }
291 }
292 }
293 if (w) {
294 _cleanup_free_ char *truncated = strndup(w, 20); /* kernel refuses more than 20 chars (as per nvme spec) */
295 if (!truncated)
296 return log_oom();
297
298 r = write_string_file_at(subsystem_fd, "attr_serial", truncated, WRITE_STRING_FILE_DISABLE_BUFFER);
299 if (r < 0)
300 log_warning_errno(r, "Failed to set serial of subsystem to '%s', ignoring: %m", truncated);
301 }
302
303 return 0;
304}
305
306static int nvme_namespace_write_metadata(int namespace_fd, sd_device *device, const char *node) {
307 sd_id128_t id = SD_ID128_NULL;
308 const char *e;
309 int r;
310
311 assert(namespace_fd >= 0);
312
313 e = secure_getenv("SYSTEMD_NVME_UUID");
314 if (e) {
315 r = sd_id128_from_string(e, &id);
316 if (r < 0)
317 log_warning_errno(r, "Failed to parse $SYSTEMD_NVME_UUID, ignoring: %s", e);
318 }
319
320 if (sd_id128_is_null(id)) {
321 const char *serial = NULL;
322 sd_id128_t mid = SD_ID128_NULL;
323
324 /* We combine machine ID and ID_SERIAL and hash a UUID from it */
325
326 if (device) {
327 (void) sd_device_get_property_value(device, "ID_SERIAL", &serial);
328 if (!serial)
921961c3
YW
329 (void) sd_device_get_devname(device, &serial);
330 }
331 if (!serial)
abc19a6f
LP
332 serial = node;
333
334 r = sd_id128_get_machine(&mid);
335 if (r < 0)
336 log_warning_errno(r, "Failed to get machine ID, ignoring: %m");
337
338 size_t l = sizeof(mid) + strlen_ptr(serial);
339 _cleanup_free_ void *j = malloc(l + 1);
340 if (!j)
341 return log_oom();
342
343 strcpy(mempcpy(j, &mid, sizeof(mid)), strempty(serial));
344
345 id = id128_digest(j, l);
346 }
347
348 r = write_string_file_at(namespace_fd, "device_uuid", SD_ID128_TO_UUID_STRING(id), WRITE_STRING_FILE_DISABLE_BUFFER);
349 if (r < 0)
350 log_warning_errno(r, "Failed to set uuid of namespace to '%s', ignoring: %m", SD_ID128_TO_UUID_STRING(id));
351
352 return 0;
353}
354
355static int nvme_subsystem_add(const char *node, int consumed_fd, sd_device *device, NvmeSubsystem **ret) {
356 _cleanup_(sd_device_unrefp) sd_device *allocated_device = NULL;
1761066b
LP
357 _cleanup_close_ int fd = consumed_fd; /* always take possession of the fd */
358 int r;
359
360 assert(node);
361 assert(ret);
362
363 _cleanup_free_ char *fname = NULL;
364 r = path_extract_filename(node, &fname);
365 if (r < 0)
366 return log_error_errno(r, "Failed to extract file name from path: %s", node);
367
368 _cleanup_free_ char *j = NULL;
369 j = strjoin(arg_nqn, ".", fname);
370 if (!j)
371 return log_oom();
372
373 if (fd < 0) {
374 fd = RET_NERRNO(open(node, O_RDONLY|O_CLOEXEC|O_NONBLOCK));
375 if (fd < 0)
376 return log_error_errno(fd, "Failed to open '%s': %m", node);
377 }
378
379 struct stat st;
380 if (fstat(fd, &st) < 0)
381 return log_error_errno(errno, "Failed to fstat '%s': %m", node);
abc19a6f
LP
382 if (S_ISBLK(st.st_mode)) {
383 if (!device) {
384 r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_dev);
385 if (r < 0)
386 return log_error_errno(r, "Failed to get device information for device '%s': %m", node);
387
388 device = allocated_device;
389 }
390 } else {
1761066b
LP
391 r = stat_verify_regular(&st);
392 if (r < 0)
393 return log_error_errno(r, "Not a block device or regular file, refusing: %s", node);
394 }
395
396 /* Let's lock this device continuously while we are operating on it */
397 r = lock_generic_with_timeout(fd, LOCK_BSD, LOCK_EX, 10 * USEC_PER_SEC);
398 if (r < 0)
399 return log_error_errno(r, "Failed to lock block device: %m");
400
401 _cleanup_close_ int subsystems_fd = -EBADF;
402 subsystems_fd = RET_NERRNO(open("/sys/kernel/config/nvmet/subsystems", O_DIRECTORY|O_CLOEXEC|O_RDONLY));
403 if (subsystems_fd < 0)
404 return log_error_errno(subsystems_fd, "Failed to open /sys/kernel/config/nvmet/subsystems: %m");
405
406 _cleanup_close_ int subsystem_fd = -EBADF;
407 subsystem_fd = open_mkdir_at(subsystems_fd, j, O_EXCL|O_RDONLY|O_CLOEXEC, 0777);
408 if (subsystem_fd < 0)
409 return log_error_errno(subsystem_fd, "Failed to create NVME subsystem '%s': %m", j);
410
411 r = write_string_file_at(subsystem_fd, "attr_allow_any_host", "1", WRITE_STRING_FILE_DISABLE_BUFFER);
412 if (r < 0)
413 return log_error_errno(r, "Failed to set 'attr_allow_any_host' flag: %m");
414
abc19a6f
LP
415 (void) nvme_subsystem_write_metadata(subsystem_fd, device);
416
1761066b
LP
417 _cleanup_close_ int namespace_fd = -EBADF;
418 namespace_fd = open_mkdir_at(subsystem_fd, "namespaces/1", O_EXCL|O_RDONLY|O_CLOEXEC, 0777);
419 if (namespace_fd < 0)
420 return log_error_errno(namespace_fd, "Failed to create NVME namespace '1': %m");
421
abc19a6f
LP
422 (void) nvme_namespace_write_metadata(namespace_fd, device, node);
423
1761066b
LP
424 /* We use /proc/$PID/fd/$FD rather than /proc/self/fd/$FD, because this string is visible to others
425 * via configfs, and by including the PID it's clear to who the stuff belongs. */
426 r = write_string_file_at(namespace_fd, "device_path", FORMAT_PROC_PID_FD_PATH(0, fd), WRITE_STRING_FILE_DISABLE_BUFFER);
427 if (r < 0)
428 return log_error_errno(r, "Failed to write 'device_path' attribute: %m");
429
430 r = write_string_file_at(namespace_fd, "enable", "1", WRITE_STRING_FILE_DISABLE_BUFFER);
431 if (r < 0)
432 return log_error_errno(r, "Failed to write 'enable' attribute: %m");
433
434 _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *subsys = NULL;
435
436 subsys = new(NvmeSubsystem, 1);
437 if (!subsys)
438 return log_oom();
439
440 *subsys = (NvmeSubsystem) {
441 .name = TAKE_PTR(j),
442 .device_fd = TAKE_FD(fd),
443 .nvme_all_subsystems_fd = TAKE_FD(subsystems_fd),
444 .nvme_our_subsystem_fd = TAKE_FD(subsystem_fd),
445 .device_stat = st,
446 };
447
448 subsys->device = strdup(node);
449 if (!subsys->device)
450 return log_oom();
451
452 *ret = TAKE_PTR(subsys);
453 return 0;
454}
455
456typedef struct NvmePort {
457 uint16_t portnr; /* used for both the IP and the NVME port numer */
458
459 int nvme_port_fd;
460 int nvme_ports_fd;
461
462 int ip_family;
463} NvmePort;
464
465static NvmePort *nvme_port_free(NvmePort *p) {
466 if (!p)
467 return NULL;
468
469 safe_close(p->nvme_port_fd);
470 safe_close(p->nvme_ports_fd);
471
472 return mfree(p);
473}
474
475static int nvme_port_unlink(NvmePort *p) {
476 int r, ret = 0;
477
478 assert(p);
479
480 if (p->nvme_port_fd >= 0) {
481 _cleanup_close_ int subsystems_dir_fd = -EBADF;
482
483 subsystems_dir_fd = openat(p->nvme_port_fd, "subsystems", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
484 if (subsystems_dir_fd < 0)
485 log_warning_errno(errno, "Failed to open 'subsystems' dir of port %" PRIu16 ", ignoring: %m", p->portnr);
486 else {
487 _cleanup_free_ DirectoryEntries *de = NULL;
488
489 r = readdir_all(subsystems_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
490 if (r < 0)
491 log_warning_errno(r, "Failed to read 'subsystems' dir of port %" PRIu16 ", ignoring: %m", p->portnr);
492 else
493 FOREACH_ARRAY(ee, de->entries, de->n_entries) {
494 const struct dirent *e = *ee;
495
496 if (unlinkat(subsystems_dir_fd, e->d_name, 0) < 0 && errno != ENOENT)
497 log_warning_errno(errno, "Failed to remove 'subsystems' symlink '%s' of port %" PRIu16 ", ignoring: %m", e->d_name, p->portnr);
498 }
499 }
500
501 p->nvme_port_fd = safe_close(p->nvme_port_fd);
502 }
503
504 if (p->nvme_ports_fd >= 0) {
505 _cleanup_free_ char *fn = NULL;
506 if (asprintf(&fn, "%" PRIu16, p->portnr) < 0)
507 return log_oom();
508
509 if (unlinkat(p->nvme_ports_fd, fn, AT_REMOVEDIR) < 0) {
510 if (errno == ENOENT)
511 ret = 0;
512 else
513 ret = log_warning_errno(errno, "Failed to remove port '%" PRIu16 ", ignoring: %m", p->portnr);
514 } else
515 ret = 1;
516
517 p->nvme_ports_fd = safe_close(p->nvme_ports_fd);
518 }
519
520 return ret;
521}
522
523static NvmePort *nvme_port_destroy(NvmePort *p) {
524 if (!p)
525 return NULL;
526
527 (void) nvme_port_unlink(p);
528
529 return nvme_port_free(p);
530}
531
532DEFINE_TRIVIAL_CLEANUP_FUNC(NvmePort*, nvme_port_destroy);
533
534static int nvme_port_add_portnr(
535 int ports_fd,
536 uint16_t portnr,
537 int ip_family,
538 int *ret_fd) {
539
540 int r;
541
542 assert(ports_fd >= 0);
543 assert(IN_SET(ip_family, AF_INET, AF_INET6));
544 assert(ret_fd);
545
546 _cleanup_free_ char *fname = NULL;
547 if (asprintf(&fname, "%" PRIu16, portnr) < 0)
548 return log_oom();
549
550 _cleanup_close_ int port_fd = -EBADF;
551 port_fd = open_mkdir_at(ports_fd, fname, O_EXCL|O_RDONLY|O_CLOEXEC, 0777);
552 if (port_fd < 0) {
553 if (port_fd != -EEXIST)
554 return log_error_errno(port_fd, "Failed to create port %" PRIu16 ": %m", portnr);
555
556 *ret_fd = -EBADF;
557 return 0;
558 }
559
560 r = write_string_file_at(port_fd, "addr_adrfam", af_to_ipv4_ipv6(ip_family), WRITE_STRING_FILE_DISABLE_BUFFER);
561 if (r < 0)
562 return log_error_errno(r, "Failed to set address family on NVME port %" PRIu16 ": %m", portnr);
563
564 r = write_string_file_at(port_fd, "addr_trtype", "tcp", WRITE_STRING_FILE_DISABLE_BUFFER);
565 if (r < 0)
566 return log_error_errno(r, "Failed to set transport type on NVME port %" PRIu16 ": %m", portnr);
567
568 r = write_string_file_at(port_fd, "addr_trsvcid", fname, WRITE_STRING_FILE_DISABLE_BUFFER);
569 if (r < 0)
570 return log_error_errno(r, "Failed to set IP port on NVME port %" PRIu16 ": %m", portnr);
571
572 r = write_string_file_at(port_fd, "addr_traddr", ip_family == AF_INET6 ? "::" : "0.0.0.0", WRITE_STRING_FILE_DISABLE_BUFFER);
573 if (r < 0)
574 return log_error_errno(r, "Failed to set IP address on NVME port %" PRIu16 ": %m", portnr);
575
576 *ret_fd = TAKE_FD(port_fd);
577 return 1;
578}
579
580static uint16_t calculate_start_port(const char *name, int ip_family) {
581 struct siphash state;
582 uint16_t nr;
583
584 assert(name);
585 assert(IN_SET(ip_family, AF_INET, AF_INET6));
586
587 /* Use some fixed key Lennart pulled from /dev/urandom, so that we are deterministic */
588 siphash24_init(&state, SD_ID128_MAKE(d1,0b,67,b5,e2,b7,4a,91,8d,6b,27,b6,35,c1,9f,d9).bytes);
589 siphash24_compress_string(name, &state);
c01a5c05 590 siphash24_compress_typesafe(ip_family, &state);
1761066b
LP
591
592 nr = 1024U + siphash24_finalize(&state) % (0xFFFFU - 1024U);
593 SET_FLAG(nr, 1, ip_family == AF_INET6); /* Lowest bit reflects family */
594
595 return nr;
596}
597
598static uint16_t calculate_next_port(int ip_family) {
599 uint16_t nr;
600
601 assert(IN_SET(ip_family, AF_INET, AF_INET6));
602
603 nr = 1024U + random_u64_range(0xFFFFU - 1024U);
604 SET_FLAG(nr, 1, ip_family == AF_INET6); /* Lowest bit reflects family */
605
606 return nr;
607}
608
609static int nvme_port_add(const char *name, int ip_family, NvmePort **ret) {
610 int r;
611
612 assert(name);
613 assert(IN_SET(ip_family, AF_INET, AF_INET6));
614 assert(ret);
615
616 _cleanup_close_ int ports_fd = -EBADF;
617 ports_fd = RET_NERRNO(open("/sys/kernel/config/nvmet/ports", O_DIRECTORY|O_RDONLY|O_CLOEXEC));
618 if (ports_fd < 0)
619 return log_error_errno(ports_fd, "Failed to open /sys/kernel/config/nvmet/ports: %m");
620
621 _cleanup_close_ int port_fd = -EBADF;
622 uint16_t portnr = calculate_start_port(name, ip_family);
623 for (unsigned attempt = 0;; attempt++) {
624 r = nvme_port_add_portnr(ports_fd, portnr, ip_family, &port_fd);
625 if (r < 0)
626 return r;
627 if (r > 0)
628 break;
629
630 if (attempt > 16)
631 return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find free NVME port after %u attempts.", attempt);
632
633 log_debug_errno(port_fd, "NVME port %" PRIu16 " exists already, randomizing port.", portnr);
634
635 portnr = calculate_next_port(ip_family);
636 }
637
638 _cleanup_(nvme_port_destroyp) NvmePort *p = new(NvmePort, 1);
639 if (!p)
640 return log_oom();
641
642 *p = (NvmePort) {
643 .portnr = portnr,
644 .nvme_ports_fd = TAKE_FD(ports_fd),
645 .nvme_port_fd = TAKE_FD(port_fd),
646 .ip_family = ip_family,
647 };
648
649 *ret = TAKE_PTR(p);
650 return 0;
651}
652
653static int nvme_port_link_subsystem(NvmePort *port, NvmeSubsystem *subsys) {
654 assert(port);
655 assert(subsys);
656
657 _cleanup_free_ char *target = NULL, *linkname = NULL;
658 target = path_join("/sys/kernel/config/nvmet/subsystems", subsys->name);
659 if (!target)
660 return log_oom();
661
662 linkname = path_join("subsystems", subsys->name);
663 if (!linkname)
664 return log_oom();
665
666 if (symlinkat(target, port->nvme_port_fd, linkname) < 0)
667 return log_error_errno(errno, "Failed to link subsystem '%s' to port %" PRIu16 ": %m", subsys->name, port->portnr);
668
669 return 0;
670}
671
672static int nvme_port_unlink_subsystem(NvmePort *port, NvmeSubsystem *subsys) {
673 assert(port);
674 assert(subsys);
675
676 _cleanup_free_ char *linkname = NULL;
677 linkname = path_join("subsystems", subsys->name);
678 if (!linkname)
679 return log_oom();
680
681 if (unlinkat(port->nvme_port_fd, linkname, 0) < 0 && errno != ENOENT)
682 return log_error_errno(errno, "Failed to unlink subsystem '%s' to port %" PRIu16 ": %m", subsys->name, port->portnr);
683
684 return 0;
685}
686
687static int nvme_subsystem_report(NvmeSubsystem *subsystem, NvmePort *ipv4, NvmePort *ipv6) {
688 assert(subsystem);
689
690 _cleanup_free_ struct local_address *addresses = NULL;
691 int n_addresses;
692 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
693 if (n_addresses < 0)
694 return log_error_errno(n_addresses, "Failed to determine local IP addresses: %m");
695
696 log_notice("NVMe-TCP: %s %s%s%s (%s)",
697 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
698 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "",
699 subsystem->name, subsystem->device);
700
701 FOREACH_ARRAY(a, addresses, n_addresses) {
702 NvmePort *port = a->family == AF_INET ? ipv4 : ipv6;
703
704 if (!port)
705 continue;
706
707 log_info(" %s Try for specific device: nvme connect -t tcp -n '%s' -a %s -s %" PRIu16,
708 special_glyph(a >= addresses + (n_addresses - 1) ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH),
709 subsystem->name,
710 IN_ADDR_TO_STRING(a->family, &a->address),
711 port->portnr);
712 }
713
714 return 0;
715}
716
95d54802
LP
717static int plymouth_send_text(const char *text) {
718 _cleanup_free_ char *plymouth_message = NULL;
719 int c, r;
720
721 assert(text);
722
723 c = asprintf(&plymouth_message,
724 "M\x02%c%s%c"
725 "A%c", /* pause spinner */
726 (int) strlen(text) + 1, text, '\x00',
727 '\x00');
728 if (c < 0)
729 return log_oom();
730
731 r = plymouth_send_raw(plymouth_message, c, SOCK_NONBLOCK);
732 if (r < 0)
733 return log_full_errno(ERRNO_IS_NO_PLYMOUTH(r) ? LOG_DEBUG : LOG_WARNING, r,
734 "Failed to communicate with plymouth, ignoring: %m");
735
736 return 0;
737}
738
739static int plymouth_notify_port(NvmePort *port, struct local_address *a) {
740 _cleanup_free_ char *m = NULL;
741
742 if (!port || !a)
743 return 0;
744
745 if (asprintf(&m, "nvme connect-all -t tcp -a %s -s %" PRIu16, IN_ADDR_TO_STRING(a->family, &a->address), port->portnr) < 0)
746 return log_oom();
747
748 return plymouth_send_text(m);
749}
750
751static int nvme_port_report(NvmePort *port, bool *plymouth_done) {
1761066b
LP
752 if (!port)
753 return 0;
754
755 _cleanup_free_ struct local_address *addresses = NULL;
756 int n_addresses;
757 n_addresses = local_addresses(NULL, 0, port->ip_family, &addresses);
758 if (n_addresses < 0)
759 return log_error_errno(n_addresses, "Failed to determine local IP addresses: %m");
760
761 log_notice("NVMe-TCP: %s %s%sListening on %s (port %" PRIu16 ")",
762 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
763 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_WORLD) : "", emoji_enabled() ? " " : "",
764 af_to_ipv4_ipv6(port->ip_family),
765 port->portnr);
766
767 FOREACH_ARRAY(a, addresses, n_addresses)
768 log_info(" %s Try for all devices: nvme connect-all -t tcp -a %s -s %" PRIu16,
769 special_glyph(a >= addresses + (n_addresses - 1) ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH),
770 IN_ADDR_TO_STRING(a->family, &a->address),
771 port->portnr);
772
95d54802
LP
773 if (plymouth_done && !*plymouth_done) {
774 (void) plymouth_notify_port(port, n_addresses > 0 ? addresses : NULL);
775 *plymouth_done = n_addresses > 0;
776 }
777
1761066b
LP
778 return 0;
779}
780
781typedef struct Context {
782 Hashmap *subsystems;
783 NvmePort *ipv4_port, *ipv6_port;
784
785 bool display_refresh_scheduled;
786} Context;
787
788static void device_hash_func(const struct stat *q, struct siphash *state) {
789 assert(q);
790
791 if (S_ISBLK(q->st_mode) || S_ISCHR(q->st_mode)) {
792 mode_t m = q->st_mode & S_IFMT;
c01a5c05
YW
793 siphash24_compress_typesafe(m, state);
794 siphash24_compress_typesafe(q->st_rdev, state);
1761066b
LP
795 return;
796 }
797
798 return inode_hash_func(q, state);
799}
800
801static int device_compare_func(const struct stat *a, const struct stat *b) {
802 int r;
803
804 assert(a);
805 assert(b);
806
807 r = CMP(a->st_mode & S_IFMT, b->st_mode & S_IFMT);
808 if (r != 0)
809 return r;
810
811 if (S_ISBLK(a->st_mode) || S_ISCHR(a->st_mode)) {
812 r = CMP(major(a->st_rdev), major(b->st_rdev));
813 if (r != 0)
814 return r;
815
816 r = CMP(minor(a->st_rdev), minor(b->st_rdev));
817 if (r != 0)
818 return r;
819
820 return 0;
821 }
822
823 return inode_compare_func(a, b);
824}
825
826DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
827 nvme_subsystem_hash_ops,
828 struct stat,
829 device_hash_func,
830 device_compare_func,
831 NvmeSubsystem,
832 nvme_subsystem_destroy);
833
834static void context_done(Context *c) {
835 assert(c);
836
837 c->ipv4_port = nvme_port_destroy(c->ipv4_port);
838 c->ipv6_port = nvme_port_destroy(c->ipv6_port);
839
840 c->subsystems = hashmap_free(c->subsystems);
841}
842
843static void device_track_back(sd_device *d, sd_device **ret) {
844 int r;
845
846 assert(d);
847 assert(ret);
848
849 const char *devname = NULL;
850 (void) sd_device_get_devname(d, &devname);
851
852 _cleanup_(sd_device_unrefp) sd_device *d_originating = NULL;
853 r = block_device_get_originating(d, &d_originating);
854 if (r < 0)
855 log_device_debug_errno(d, r, "Failed to get originating device for '%s', ignoring: %m", strna(devname));
856
857 sd_device *d_whole = NULL;
858 r = block_device_get_whole_disk(d_originating ?: d, &d_whole); /* does not ref returned device */
859 if (r < 0)
860 log_device_debug_errno(d, r, "Failed to get whole device for '%s', ignoring: %m", strna(devname));
861
862 *ret = d_whole ? sd_device_ref(d_whole) : d_originating ? TAKE_PTR(d_originating) : sd_device_ref(d);
863}
864
865static int device_is_same(sd_device *a, sd_device *b) {
866 dev_t devnum_a, devnum_b;
867 int r;
868
869 assert(a);
870 assert(b);
871
872 r = sd_device_get_devnum(a, &devnum_a);
873 if (r < 0)
874 return r;
875
876 r = sd_device_get_devnum(b, &devnum_b);
877 if (r < 0)
878 return r;
879
880 return devnum_a == devnum_b;
881}
882
883static bool device_is_allowed(sd_device *d) {
884 int r;
885
886 assert(d);
887
888 if (arg_all >= 2) /* If --all is specified twice we allow even the root fs to shared */
889 return true;
890
891 const char *devname;
892 r = sd_device_get_devname(d, &devname);
893 if (r < 0)
894 return log_device_error_errno(d, r, "Failed to get device name: %m");
895
896 dev_t root_devnum;
897 r = get_block_device("/", &root_devnum);
898 if (r < 0) {
899 log_warning_errno(r, "Failed to get backing device of the root file system: %m");
900 return false; /* Better safe */
901 }
902 if (root_devnum == 0) /* Not backed by a block device? */
903 return true;
904
905 _cleanup_(sd_device_unrefp) sd_device *root_device = NULL;
906 r = sd_device_new_from_devnum(&root_device, 'b', root_devnum);
907 if (r < 0) {
908 log_warning_errno(r, "Failed to get root block device, assuming device '%s' is same as root device: %m", devname);
909 return false;
910 }
911
912 _cleanup_(sd_device_unrefp) sd_device *whole_root_device = NULL;
913 device_track_back(root_device, &whole_root_device);
914
915 _cleanup_(sd_device_unrefp) sd_device *whole_d = NULL;
916 device_track_back(d, &whole_d);
917
918 r = device_is_same(whole_root_device, whole_d);
919 if (r < 0) {
920 log_warning_errno(r, "Failed to determine if root device and device '%s' are the same, assuming they are: %m", devname);
921 return false; /* Better safe */
922 }
923
924 return !r;
925}
926
927static int device_added(Context *c, sd_device *device) {
928 _cleanup_close_ int fd = -EBADF;
929 int r;
930
931 assert(c);
932 assert(device);
933
934 const char *sysname;
935 r = sd_device_get_sysname(device, &sysname);
936 if (r < 0)
937 return log_device_error_errno(device, r, "Failed to get device name: %m");
938
939 log_device_debug(device, "new block device '%s'", sysname);
940
941 if (STARTSWITH_SET(sysname, "loop", "zram")) /* Ignore some devices */
942 return 0;
943
944 const char *devname;
945 r = sd_device_get_devname(device, &devname);
946 if (r < 0)
947 return log_device_error_errno(device, r, "Failed to get device node path: %m");
948
949 struct stat lookup_key = {
950 .st_mode = S_IFBLK,
951 };
952
953 r = sd_device_get_devnum(device, &lookup_key.st_rdev);
954 if (r < 0)
955 return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
956
957 if (hashmap_contains(c->subsystems, &lookup_key)) {
958 log_debug("Device '%s' already seen.", devname);
959 return 0;
960 }
961
962 if (!device_is_allowed(device)) {
963 log_device_debug(device, "Not exposing device '%s', as it is backed by root disk.", devname);
964 return 0;
965 }
966
967 fd = sd_device_open(device, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
968 if (fd < 0) {
969 log_device_warning_errno(device, fd, "Failed to open newly acquired device '%s', ignoring device: %m", devname);
970 return 0;
971 }
972
973 _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *s = NULL;
abc19a6f 974 r = nvme_subsystem_add(devname, TAKE_FD(fd), device, &s);
1761066b
LP
975 if (r < 0)
976 return r;
977
978 if (c->ipv4_port) {
979 r = nvme_port_link_subsystem(c->ipv4_port, s);
980 if (r < 0)
981 return r;
982 }
983
984 if (c->ipv6_port) {
985 r = nvme_port_link_subsystem(c->ipv6_port, s);
986 if (r < 0)
987 return r;
988 }
989
990 r = hashmap_ensure_put(&c->subsystems, &nvme_subsystem_hash_ops, &s->device_stat, s);
991 if (r < 0)
992 return log_error_errno(r, "Failed to add subsystem to hash table: %m");
993
994 (void) nvme_subsystem_report(s, c->ipv4_port, c->ipv6_port);
995
996 TAKE_PTR(s);
997 return 1;
998}
999
1000static int device_removed(Context *c, sd_device *device) {
1001 int r;
1002
1003 assert(device);
1004
1005 struct stat lookup_key = {
1006 .st_mode = S_IFBLK,
1007 };
1008
1009 r = sd_device_get_devnum(device, &lookup_key.st_rdev);
1010 if (r < 0)
1011 return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
1012
1013 NvmeSubsystem *s = hashmap_remove(c->subsystems, &lookup_key);
1014 if (!s)
1015 return 0;
1016
1017 log_device_debug(device, "removed block device '%s'", s->name);
1018
1019 if (c->ipv4_port)
1020 (void) nvme_port_unlink_subsystem(c->ipv4_port, s);
1021 if (c->ipv6_port)
1022 (void) nvme_port_unlink_subsystem(c->ipv6_port, s);
1023
1024 s = nvme_subsystem_destroy(s);
1025 return 1;
1026}
1027
1028static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
1029 Context *c = ASSERT_PTR(userdata);
1030
1031 if (device_for_action(device, SD_DEVICE_REMOVE))
1032 device_removed(c, device);
1033 else
1034 device_added(c, device);
1035
1036 return 0;
1037}
1038
1039static int on_display_refresh(sd_event_source *s, uint64_t usec, void *userdata) {
1040 Context *c = ASSERT_PTR(userdata);
1041
1042 assert(s);
1043
1044 c->display_refresh_scheduled = false;
1045
dd9c8da8 1046 if (isatty(STDERR_FILENO))
1761066b
LP
1047 fputs(ANSI_HOME_CLEAR, stderr);
1048
95d54802
LP
1049 /* If we have both IPv4 and IPv6, we display IPv4 info via Plymouth, since it doesn't have much
1050 * space, and IPv4 is simply shorter (and easy to type off screen) */
1051
1052 bool plymouth_done = false;
1053 (void) nvme_port_report(c->ipv4_port, &plymouth_done);
1054 (void) nvme_port_report(c->ipv6_port, &plymouth_done);
1055
1056 if (!plymouth_done)
1057 (void) plymouth_send_text("Network disconnected.");
1761066b
LP
1058
1059 NvmeSubsystem *i;
1060 HASHMAP_FOREACH(i, c->subsystems)
1061 (void) nvme_subsystem_report(i, c->ipv4_port, c->ipv6_port);
1062
1063 return 0;
1064}
1065
1066static int on_address_change(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
1067 Context *c = ASSERT_PTR(userdata);
1068 int r, family;
1069
1070 assert(rtnl);
1071 assert(mm);
1072
1073 r = sd_rtnl_message_addr_get_family(mm, &family);
1074 if (r < 0) {
1075 log_warning_errno(r, "Failed to get address family from netlink address message, ignoring: %m");
1076 return 0;
1077 }
1078
1079 if (!c->display_refresh_scheduled) {
1080 r = sd_event_add_time_relative(
1081 sd_netlink_get_event(rtnl),
1082 /* ret_slot= */ NULL,
1083 CLOCK_MONOTONIC,
1084 750 * USEC_PER_MSEC,
1085 0,
1086 on_display_refresh,
1087 c);
1088 if (r < 0)
1089 log_warning_errno(r, "Failed to schedule display refresh, ignoring: %m");
1090 else
1091 c->display_refresh_scheduled = true;
1092 }
1093
1094 return 0;
1095}
1096
1097static int run(int argc, char* argv[]) {
1098 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
1099 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1100 _cleanup_(context_done) Context context = {};
1101 int r;
1102
1103 log_show_color(true);
1104 log_parse_environment();
1105 log_open();
1106
1107 r = parse_argv(argc, argv);
1108 if (r <= 0)
1109 return r;
1110
1111 r = sd_event_new(&event);
1112 if (r < 0)
1113 return log_error_errno(r, "Failed to allocate event loop: %m");
1114
1115 r = sd_event_set_signal_exit(event, true);
1116 if (r < 0)
1117 return log_error_errno(r, "Failed to install exit signal handlers: %m");
1118
1119 STRV_FOREACH(i, arg_devices) {
1120 _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *subsys = NULL;
1121
abc19a6f 1122 r = nvme_subsystem_add(*i, -EBADF, /* device= */ NULL, &subsys);
1761066b
LP
1123 if (r < 0)
1124 return r;
1125
1126 r = hashmap_ensure_put(&context.subsystems, &nvme_subsystem_hash_ops, &subsys->device_stat, subsys);
1127 if (r == -EEXIST) {
1128 log_warning_errno(r, "Duplicate device '%s' specified, skipping: %m", *i);
1129 continue;
1130 }
1131 if (r < 0)
1132 return log_error_errno(r, "Failed to add subsystem to hash table: %m");
1133
1134 TAKE_PTR(subsys);
1135 }
1136
1137 r = nvme_port_add(arg_nqn, AF_INET, &context.ipv4_port);
1138 if (r < 0)
1139 return r;
1140
95d54802
LP
1141 bool plymouth_done = false;
1142 nvme_port_report(context.ipv4_port, &plymouth_done);
1761066b
LP
1143
1144 if (socket_ipv6_is_enabled()) {
1145 r = nvme_port_add(arg_nqn, AF_INET6, &context.ipv6_port);
1146 if (r < 0)
1147 return r;
1148
95d54802 1149 nvme_port_report(context.ipv6_port, &plymouth_done);
1761066b
LP
1150 }
1151
95d54802
LP
1152 if (!plymouth_done)
1153 (void) plymouth_send_text("Network disconnected.");
1154
1761066b
LP
1155 NvmeSubsystem *i;
1156 HASHMAP_FOREACH(i, context.subsystems) {
1157 if (context.ipv4_port) {
1158 r = nvme_port_link_subsystem(context.ipv4_port, i);
1159 if (r < 0)
1160 return r;
1161 }
1162
1163 if (context.ipv6_port) {
1164 r = nvme_port_link_subsystem(context.ipv6_port, i);
1165 if (r < 0)
1166 return r;
1167 }
1168
1169 (void) nvme_subsystem_report(i, context.ipv4_port, context.ipv6_port);
1170 }
1171
1172 if (arg_all > 0) {
1173 r = sd_device_monitor_new(&monitor);
1174 if (r < 0)
1175 return log_error_errno(r, "Failed to allocate device monitor: %m");
1176
1177 r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "block", "disk");
1178 if (r < 0)
1179 return log_error_errno(r, "Failed to configure device monitor match: %m");
1180
1181 r = sd_device_monitor_attach_event(monitor, event);
1182 if (r < 0)
1183 return log_error_errno(r, "Failed to attach device monitor to event loop: %m");
1184
1185 r = sd_device_monitor_start(monitor, device_monitor_handler, &context);
1186 if (r < 0)
1187 return log_error_errno(r, "Failed to start device monitor: %m");
1188
1189 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL;
1190 r = sd_device_enumerator_new(&enumerator);
1191 if (r < 0)
1192 return log_error_errno(r, "Failed to allocate enumerator: %m");
1193
1194 r = sd_device_enumerator_add_match_subsystem(enumerator, "block", /* match= */ true);
1195 if (r < 0)
1196 return log_error_errno(r, "Failed to match block devices: %m");
1197
1198 r = sd_device_enumerator_add_match_property(enumerator, "DEVTYPE", "disk");
1199 if (r < 0)
1200 return log_error_errno(r, "Failed to match whole block devices: %m");
1201
1202 r = sd_device_enumerator_add_nomatch_sysname(enumerator, "loop*");
1203 if (r < 0)
1204 return log_error_errno(r, "Failed to exclude loop devices: %m");
1205
1206 FOREACH_DEVICE(enumerator, device)
1207 device_added(&context, device);
1208 }
1209
1210 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1211 r = sd_netlink_open(&rtnl);
1212 if (r < 0)
1213 return log_error_errno(r, "Failed to connect to netlink: %m");
1214
1215 r = sd_netlink_attach_event(rtnl, event, SD_EVENT_PRIORITY_NORMAL);
1216 if (r < 0)
1217 return log_error_errno(r, "Failed to attach netlink socket to event loop: %m");
1218
1219 r = sd_netlink_add_match(rtnl, /* ret_slot= */ NULL, RTM_NEWADDR, on_address_change, /* destroy_callback= */ NULL, &context, "storagetm-newaddr");
1220 if (r < 0)
1221 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR events: %m");
1222
1223 r = sd_netlink_add_match(rtnl, /* ret_slot= */ NULL, RTM_DELADDR, on_address_change, /* destroy_callback= */ NULL, &context, "storagetm-deladdr");
1224 if (r < 0)
1225 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR events: %m");
1226
dd9c8da8 1227 if (isatty(STDIN_FILENO))
1761066b
LP
1228 log_info("Hit Ctrl-C to exit target mode.");
1229
1230 _unused_ _cleanup_(notify_on_cleanup) const char *notify_message =
1231 notify_start("READY=1\n"
1232 "STATUS=Exposing disks in target mode...",
1233 NOTIFY_STOPPING);
1234
1235 r = sd_event_loop(event);
1236 if (r < 0)
1237 return log_error_errno(r, "Failed to run event loop: %m");
1238
1239 log_info("Exiting target mode.");
1240 return r;
1241}
1242
1243DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);