1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "blockdev-util.h"
10 #include "daemon-util.h"
11 #include "device-util.h"
14 #include "format-util.h"
16 #include "id128-util.h"
17 #include "local-addresses.h"
18 #include "loop-util.h"
19 #include "main-func.h"
21 #include "parse-argument.h"
22 #include "path-util.h"
23 #include "plymouth-util.h"
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"
32 static char **arg_devices
= NULL
;
33 static char *arg_nqn
= NULL
;
34 static int arg_all
= 0;
36 STATIC_DESTRUCTOR_REGISTER(arg_devices
, strv_freep
);
37 STATIC_DESTRUCTOR_REGISTER(arg_nqn
, freep
);
39 static int help(void) {
40 _cleanup_free_
char *link
= NULL
;
43 r
= terminal_urlify_man("systemd-storagetm", "8", &link
);
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
,
62 static int parse_argv(int argc
, char *argv
[]) {
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' },
82 while ((c
= getopt_long(argc
, argv
, "ha", options
, NULL
)) >= 0)
93 if (!filename_is_valid(optarg
))
94 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "NQN invalid: %s", optarg
);
96 if (free_and_strdup(&arg_nqn
, optarg
) < 0)
109 assert_not_reached();
114 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expects no further arguments if --all/-a is specified.");
117 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expecting device name or --all/-a.");
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
]);
123 arg_devices
= strv_copy(argv
+ optind
);
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
);
131 return log_error_errno(r
, "Failed to get machine ID: %m");
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)
141 typedef struct NvmeSubsystem
{
143 struct stat device_stat
;
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. */
150 static NvmeSubsystem
* nvme_subsystem_free(NvmeSubsystem
*s
) {
155 safe_close(s
->nvme_all_subsystems_fd
);
156 safe_close(s
->nvme_our_subsystem_fd
);
157 safe_close(s
->device_fd
);
163 static int nvme_subsystem_unlink(NvmeSubsystem
*s
) {
168 if (s
->nvme_our_subsystem_fd
>= 0) {
169 _cleanup_close_
int namespaces_fd
= -EBADF
;
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
);
175 _cleanup_free_ DirectoryEntries
*de
= NULL
;
177 r
= readdir_all(namespaces_fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
, &de
);
179 log_warning_errno(r
, "Failed to read 'namespaces' dir of subsystem '%s', ignoring: %m", s
->name
);
181 FOREACH_ARRAY(ee
, de
->entries
, de
->n_entries
) {
182 _cleanup_free_
char *enable_fn
= NULL
;
183 const struct dirent
*e
= *ee
;
185 enable_fn
= path_join(e
->d_name
, "enable");
189 r
= write_string_file_at(namespaces_fd
, enable_fn
, "0", WRITE_STRING_FILE_DISABLE_BUFFER
);
191 log_warning_errno(r
, "Failed to disable namespace '%s' of NVME subsystem '%s', ignoring: %m", e
->d_name
, s
->name
);
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
);
199 s
->nvme_our_subsystem_fd
= safe_close(s
->nvme_our_subsystem_fd
);
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
);
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 */
208 log_info("NVME subsystem '%s' removed.", s
->name
);
214 static NvmeSubsystem
*nvme_subsystem_destroy(NvmeSubsystem
*s
) {
218 (void) nvme_subsystem_unlink(s
);
220 return nvme_subsystem_free(s
);
223 DEFINE_TRIVIAL_CLEANUP_FUNC(NvmeSubsystem
*, nvme_subsystem_destroy
);
225 static 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
;
230 assert(subsystem_fd
>= 0);
232 (void) parse_os_release(
234 "IMAGE_ID", &image_id
,
235 "IMAGE_VERSION", &image_version
,
237 "VERSION_ID", &os_version
);
240 (void) device_get_model_string(device
, &hwmodel
);
241 (void) sd_device_get_property_value(device
, "ID_SERIAL_SHORT", &hwserial
);
244 w
= secure_getenv("SYSTEMD_NVME_MODEL");
246 if (hwmodel
&& (image_id
|| os_id
)) {
247 if (asprintf(&combined_model
, "%s (%s)", hwmodel
, image_id
?: os_id
) < 0)
251 w
= hwmodel
?: image_id
?: os_id
;
254 _cleanup_free_
char *truncated
= strndup(w
, 40); /* kernel refuses more than 40 chars (as per nvme spec) */
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
);
259 log_warning_errno(r
, "Failed to set model of subsystem to '%s', ignoring: %m", w
);
262 w
= secure_getenv("SYSTEMD_NVME_FIRMWARE");
264 w
= image_version
?: os_version
;
266 _cleanup_free_
char *truncated
= strndup(w
, 8); /* kernel refuses more than 8 chars (as per nvme spec) */
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
);
273 log_warning_errno(r
, "Failed to set model of subsystem to '%s', ignoring: %m", truncated
);
276 w
= secure_getenv("SYSTEMD_NVME_SERIAL");
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
);
285 log_warning_errno(r
, "Failed to get machine ID, ignoring: %m");
287 if (asprintf(&synthetic_serial
, SD_ID128_FORMAT_STR
, SD_ID128_FORMAT_VAL(mid
)) < 0)
289 w
= synthetic_serial
;
294 _cleanup_free_
char *truncated
= strndup(w
, 20); /* kernel refuses more than 20 chars (as per nvme spec) */
298 r
= write_string_file_at(subsystem_fd
, "attr_serial", truncated
, WRITE_STRING_FILE_DISABLE_BUFFER
);
300 log_warning_errno(r
, "Failed to set serial of subsystem to '%s', ignoring: %m", truncated
);
306 static int nvme_namespace_write_metadata(int namespace_fd
, sd_device
*device
, const char *node
) {
307 sd_id128_t id
= SD_ID128_NULL
;
311 assert(namespace_fd
>= 0);
313 e
= secure_getenv("SYSTEMD_NVME_UUID");
315 r
= sd_id128_from_string(e
, &id
);
317 log_warning_errno(r
, "Failed to parse $SYSTEMD_NVME_UUID, ignoring: %s", e
);
320 if (sd_id128_is_null(id
)) {
321 const char *serial
= NULL
;
322 sd_id128_t mid
= SD_ID128_NULL
;
324 /* We combine machine ID and ID_SERIAL and hash a UUID from it */
327 (void) sd_device_get_property_value(device
, "ID_SERIAL", &serial
);
329 (void) sd_device_get_devname(device
, &serial
);
334 r
= sd_id128_get_machine(&mid
);
336 log_warning_errno(r
, "Failed to get machine ID, ignoring: %m");
338 size_t l
= sizeof(mid
) + strlen_ptr(serial
);
339 _cleanup_free_
void *j
= malloc(l
+ 1);
343 strcpy(mempcpy(j
, &mid
, sizeof(mid
)), strempty(serial
));
345 id
= id128_digest(j
, l
);
348 r
= write_string_file_at(namespace_fd
, "device_uuid", SD_ID128_TO_UUID_STRING(id
), WRITE_STRING_FILE_DISABLE_BUFFER
);
350 log_warning_errno(r
, "Failed to set uuid of namespace to '%s', ignoring: %m", SD_ID128_TO_UUID_STRING(id
));
355 static 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
;
357 _cleanup_close_
int fd
= consumed_fd
; /* always take possession of the fd */
363 _cleanup_free_
char *fname
= NULL
;
364 r
= path_extract_filename(node
, &fname
);
366 return log_error_errno(r
, "Failed to extract file name from path: %s", node
);
368 _cleanup_free_
char *j
= NULL
;
369 j
= strjoin(arg_nqn
, ".", fname
);
374 fd
= RET_NERRNO(open(node
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
));
376 return log_error_errno(fd
, "Failed to open '%s': %m", node
);
380 if (fstat(fd
, &st
) < 0)
381 return log_error_errno(errno
, "Failed to fstat '%s': %m", node
);
382 if (S_ISBLK(st
.st_mode
)) {
384 r
= sd_device_new_from_devnum(&allocated_device
, 'b', st
.st_rdev
);
386 return log_error_errno(r
, "Failed to get device information for device '%s': %m", node
);
388 device
= allocated_device
;
391 r
= stat_verify_regular(&st
);
393 return log_error_errno(r
, "Not a block device or regular file, refusing: %s", node
);
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
);
399 return log_error_errno(r
, "Failed to lock block device: %m");
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");
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
);
411 r
= write_string_file_at(subsystem_fd
, "attr_allow_any_host", "1", WRITE_STRING_FILE_DISABLE_BUFFER
);
413 return log_error_errno(r
, "Failed to set 'attr_allow_any_host' flag: %m");
415 (void) nvme_subsystem_write_metadata(subsystem_fd
, device
);
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");
422 (void) nvme_namespace_write_metadata(namespace_fd
, device
, node
);
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
);
428 return log_error_errno(r
, "Failed to write 'device_path' attribute: %m");
430 r
= write_string_file_at(namespace_fd
, "enable", "1", WRITE_STRING_FILE_DISABLE_BUFFER
);
432 return log_error_errno(r
, "Failed to write 'enable' attribute: %m");
434 _cleanup_(nvme_subsystem_destroyp
) NvmeSubsystem
*subsys
= NULL
;
436 subsys
= new(NvmeSubsystem
, 1);
440 *subsys
= (NvmeSubsystem
) {
442 .device_fd
= TAKE_FD(fd
),
443 .nvme_all_subsystems_fd
= TAKE_FD(subsystems_fd
),
444 .nvme_our_subsystem_fd
= TAKE_FD(subsystem_fd
),
448 subsys
->device
= strdup(node
);
452 *ret
= TAKE_PTR(subsys
);
456 typedef struct NvmePort
{
457 uint16_t portnr
; /* used for both the IP and the NVME port numer */
465 static NvmePort
*nvme_port_free(NvmePort
*p
) {
469 safe_close(p
->nvme_port_fd
);
470 safe_close(p
->nvme_ports_fd
);
475 static int nvme_port_unlink(NvmePort
*p
) {
480 if (p
->nvme_port_fd
>= 0) {
481 _cleanup_close_
int subsystems_dir_fd
= -EBADF
;
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
);
487 _cleanup_free_ DirectoryEntries
*de
= NULL
;
489 r
= readdir_all(subsystems_dir_fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
, &de
);
491 log_warning_errno(r
, "Failed to read 'subsystems' dir of port %" PRIu16
", ignoring: %m", p
->portnr
);
493 FOREACH_ARRAY(ee
, de
->entries
, de
->n_entries
) {
494 const struct dirent
*e
= *ee
;
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
);
501 p
->nvme_port_fd
= safe_close(p
->nvme_port_fd
);
504 if (p
->nvme_ports_fd
>= 0) {
505 _cleanup_free_
char *fn
= NULL
;
506 if (asprintf(&fn
, "%" PRIu16
, p
->portnr
) < 0)
509 if (unlinkat(p
->nvme_ports_fd
, fn
, AT_REMOVEDIR
) < 0) {
513 ret
= log_warning_errno(errno
, "Failed to remove port '%" PRIu16
", ignoring: %m", p
->portnr
);
517 p
->nvme_ports_fd
= safe_close(p
->nvme_ports_fd
);
523 static NvmePort
*nvme_port_destroy(NvmePort
*p
) {
527 (void) nvme_port_unlink(p
);
529 return nvme_port_free(p
);
532 DEFINE_TRIVIAL_CLEANUP_FUNC(NvmePort
*, nvme_port_destroy
);
534 static int nvme_port_add_portnr(
542 assert(ports_fd
>= 0);
543 assert(IN_SET(ip_family
, AF_INET
, AF_INET6
));
546 _cleanup_free_
char *fname
= NULL
;
547 if (asprintf(&fname
, "%" PRIu16
, portnr
) < 0)
550 _cleanup_close_
int port_fd
= -EBADF
;
551 port_fd
= open_mkdir_at(ports_fd
, fname
, O_EXCL
|O_RDONLY
|O_CLOEXEC
, 0777);
553 if (port_fd
!= -EEXIST
)
554 return log_error_errno(port_fd
, "Failed to create port %" PRIu16
": %m", portnr
);
560 r
= write_string_file_at(port_fd
, "addr_adrfam", af_to_ipv4_ipv6(ip_family
), WRITE_STRING_FILE_DISABLE_BUFFER
);
562 return log_error_errno(r
, "Failed to set address family on NVME port %" PRIu16
": %m", portnr
);
564 r
= write_string_file_at(port_fd
, "addr_trtype", "tcp", WRITE_STRING_FILE_DISABLE_BUFFER
);
566 return log_error_errno(r
, "Failed to set transport type on NVME port %" PRIu16
": %m", portnr
);
568 r
= write_string_file_at(port_fd
, "addr_trsvcid", fname
, WRITE_STRING_FILE_DISABLE_BUFFER
);
570 return log_error_errno(r
, "Failed to set IP port on NVME port %" PRIu16
": %m", portnr
);
572 r
= write_string_file_at(port_fd
, "addr_traddr", ip_family
== AF_INET6
? "::" : "0.0.0.0", WRITE_STRING_FILE_DISABLE_BUFFER
);
574 return log_error_errno(r
, "Failed to set IP address on NVME port %" PRIu16
": %m", portnr
);
576 *ret_fd
= TAKE_FD(port_fd
);
580 static uint16_t calculate_start_port(const char *name
, int ip_family
) {
581 struct siphash state
;
585 assert(IN_SET(ip_family
, AF_INET
, AF_INET6
));
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
);
590 siphash24_compress(&ip_family
, sizeof(ip_family
), &state
);
592 nr
= 1024U + siphash24_finalize(&state
) % (0xFFFFU
- 1024U);
593 SET_FLAG(nr
, 1, ip_family
== AF_INET6
); /* Lowest bit reflects family */
598 static uint16_t calculate_next_port(int ip_family
) {
601 assert(IN_SET(ip_family
, AF_INET
, AF_INET6
));
603 nr
= 1024U + random_u64_range(0xFFFFU
- 1024U);
604 SET_FLAG(nr
, 1, ip_family
== AF_INET6
); /* Lowest bit reflects family */
609 static int nvme_port_add(const char *name
, int ip_family
, NvmePort
**ret
) {
613 assert(IN_SET(ip_family
, AF_INET
, AF_INET6
));
616 _cleanup_close_
int ports_fd
= -EBADF
;
617 ports_fd
= RET_NERRNO(open("/sys/kernel/config/nvmet/ports", O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
));
619 return log_error_errno(ports_fd
, "Failed to open /sys/kernel/config/nvmet/ports: %m");
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
);
631 return log_error_errno(SYNTHETIC_ERRNO(EBUSY
), "Can't find free NVME port after %u attempts.", attempt
);
633 log_debug_errno(port_fd
, "NVME port %" PRIu16
" exists already, randomizing port.", portnr
);
635 portnr
= calculate_next_port(ip_family
);
638 _cleanup_(nvme_port_destroyp
) NvmePort
*p
= new(NvmePort
, 1);
644 .nvme_ports_fd
= TAKE_FD(ports_fd
),
645 .nvme_port_fd
= TAKE_FD(port_fd
),
646 .ip_family
= ip_family
,
653 static int nvme_port_link_subsystem(NvmePort
*port
, NvmeSubsystem
*subsys
) {
657 _cleanup_free_
char *target
= NULL
, *linkname
= NULL
;
658 target
= path_join("/sys/kernel/config/nvmet/subsystems", subsys
->name
);
662 linkname
= path_join("subsystems", subsys
->name
);
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
);
672 static int nvme_port_unlink_subsystem(NvmePort
*port
, NvmeSubsystem
*subsys
) {
676 _cleanup_free_
char *linkname
= NULL
;
677 linkname
= path_join("subsystems", subsys
->name
);
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
);
687 static int nvme_subsystem_report(NvmeSubsystem
*subsystem
, NvmePort
*ipv4
, NvmePort
*ipv6
) {
690 _cleanup_free_
struct local_address
*addresses
= NULL
;
692 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
694 return log_error_errno(n_addresses
, "Failed to determine local IP addresses: %m");
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
);
701 FOREACH_ARRAY(a
, addresses
, n_addresses
) {
702 NvmePort
*port
= a
->family
== AF_INET
? ipv4
: ipv6
;
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
),
710 IN_ADDR_TO_STRING(a
->family
, &a
->address
),
717 static int plymouth_send_text(const char *text
) {
718 _cleanup_free_
char *plymouth_message
= NULL
;
723 c
= asprintf(&plymouth_message
,
725 "A%c", /* pause spinner */
726 (int) strlen(text
) + 1, text
, '\x00',
731 r
= plymouth_send_raw(plymouth_message
, c
, SOCK_NONBLOCK
);
733 return log_full_errno(ERRNO_IS_NO_PLYMOUTH(r
) ? LOG_DEBUG
: LOG_WARNING
, r
,
734 "Failed to communicate with plymouth, ignoring: %m");
739 static int plymouth_notify_port(NvmePort
*port
, struct local_address
*a
) {
740 _cleanup_free_
char *m
= NULL
;
745 if (asprintf(&m
, "nvme connect-all -t tcp -a %s -s %" PRIu16
, IN_ADDR_TO_STRING(a
->family
, &a
->address
), port
->portnr
) < 0)
748 return plymouth_send_text(m
);
751 static int nvme_port_report(NvmePort
*port
, bool *plymouth_done
) {
755 _cleanup_free_
struct local_address
*addresses
= NULL
;
757 n_addresses
= local_addresses(NULL
, 0, port
->ip_family
, &addresses
);
759 return log_error_errno(n_addresses
, "Failed to determine local IP addresses: %m");
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
),
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
),
773 if (plymouth_done
&& !*plymouth_done
) {
774 (void) plymouth_notify_port(port
, n_addresses
> 0 ? addresses
: NULL
);
775 *plymouth_done
= n_addresses
> 0;
781 typedef struct Context
{
783 NvmePort
*ipv4_port
, *ipv6_port
;
785 bool display_refresh_scheduled
;
788 static void device_hash_func(const struct stat
*q
, struct siphash
*state
) {
791 mode_t m
= q
->st_mode
& S_IFMT
;
792 siphash24_compress(&m
, sizeof(m
), state
);
794 if (S_ISBLK(q
->st_mode
) || S_ISCHR(q
->st_mode
)) {
795 siphash24_compress(&q
->st_rdev
, sizeof(q
->st_rdev
), state
);
799 return inode_hash_func(q
, state
);
802 static int device_compare_func(const struct stat
*a
, const struct stat
*b
) {
808 r
= CMP(a
->st_mode
& S_IFMT
, b
->st_mode
& S_IFMT
);
812 if (S_ISBLK(a
->st_mode
) || S_ISCHR(a
->st_mode
)) {
813 r
= CMP(major(a
->st_rdev
), major(b
->st_rdev
));
817 r
= CMP(minor(a
->st_rdev
), minor(b
->st_rdev
));
824 return inode_compare_func(a
, b
);
827 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
828 nvme_subsystem_hash_ops
,
833 nvme_subsystem_destroy
);
835 static void context_done(Context
*c
) {
838 c
->ipv4_port
= nvme_port_destroy(c
->ipv4_port
);
839 c
->ipv6_port
= nvme_port_destroy(c
->ipv6_port
);
841 c
->subsystems
= hashmap_free(c
->subsystems
);
844 static void device_track_back(sd_device
*d
, sd_device
**ret
) {
850 const char *devname
= NULL
;
851 (void) sd_device_get_devname(d
, &devname
);
853 _cleanup_(sd_device_unrefp
) sd_device
*d_originating
= NULL
;
854 r
= block_device_get_originating(d
, &d_originating
);
856 log_device_debug_errno(d
, r
, "Failed to get originating device for '%s', ignoring: %m", strna(devname
));
858 sd_device
*d_whole
= NULL
;
859 r
= block_device_get_whole_disk(d_originating
?: d
, &d_whole
); /* does not ref returned device */
861 log_device_debug_errno(d
, r
, "Failed to get whole device for '%s', ignoring: %m", strna(devname
));
863 *ret
= d_whole
? sd_device_ref(d_whole
) : d_originating
? TAKE_PTR(d_originating
) : sd_device_ref(d
);
866 static int device_is_same(sd_device
*a
, sd_device
*b
) {
867 dev_t devnum_a
, devnum_b
;
873 r
= sd_device_get_devnum(a
, &devnum_a
);
877 r
= sd_device_get_devnum(b
, &devnum_b
);
881 return devnum_a
== devnum_b
;
884 static bool device_is_allowed(sd_device
*d
) {
889 if (arg_all
>= 2) /* If --all is specified twice we allow even the root fs to shared */
893 r
= sd_device_get_devname(d
, &devname
);
895 return log_device_error_errno(d
, r
, "Failed to get device name: %m");
898 r
= get_block_device("/", &root_devnum
);
900 log_warning_errno(r
, "Failed to get backing device of the root file system: %m");
901 return false; /* Better safe */
903 if (root_devnum
== 0) /* Not backed by a block device? */
906 _cleanup_(sd_device_unrefp
) sd_device
*root_device
= NULL
;
907 r
= sd_device_new_from_devnum(&root_device
, 'b', root_devnum
);
909 log_warning_errno(r
, "Failed to get root block device, assuming device '%s' is same as root device: %m", devname
);
913 _cleanup_(sd_device_unrefp
) sd_device
*whole_root_device
= NULL
;
914 device_track_back(root_device
, &whole_root_device
);
916 _cleanup_(sd_device_unrefp
) sd_device
*whole_d
= NULL
;
917 device_track_back(d
, &whole_d
);
919 r
= device_is_same(whole_root_device
, whole_d
);
921 log_warning_errno(r
, "Failed to determine if root device and device '%s' are the same, assuming they are: %m", devname
);
922 return false; /* Better safe */
928 static int device_added(Context
*c
, sd_device
*device
) {
929 _cleanup_close_
int fd
= -EBADF
;
936 r
= sd_device_get_sysname(device
, &sysname
);
938 return log_device_error_errno(device
, r
, "Failed to get device name: %m");
940 log_device_debug(device
, "new block device '%s'", sysname
);
942 if (STARTSWITH_SET(sysname
, "loop", "zram")) /* Ignore some devices */
946 r
= sd_device_get_devname(device
, &devname
);
948 return log_device_error_errno(device
, r
, "Failed to get device node path: %m");
950 struct stat lookup_key
= {
954 r
= sd_device_get_devnum(device
, &lookup_key
.st_rdev
);
956 return log_device_error_errno(device
, r
, "Failed to get major/minor from device: %m");
958 if (hashmap_contains(c
->subsystems
, &lookup_key
)) {
959 log_debug("Device '%s' already seen.", devname
);
963 if (!device_is_allowed(device
)) {
964 log_device_debug(device
, "Not exposing device '%s', as it is backed by root disk.", devname
);
968 fd
= sd_device_open(device
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
);
970 log_device_warning_errno(device
, fd
, "Failed to open newly acquired device '%s', ignoring device: %m", devname
);
974 _cleanup_(nvme_subsystem_destroyp
) NvmeSubsystem
*s
= NULL
;
975 r
= nvme_subsystem_add(devname
, TAKE_FD(fd
), device
, &s
);
980 r
= nvme_port_link_subsystem(c
->ipv4_port
, s
);
986 r
= nvme_port_link_subsystem(c
->ipv6_port
, s
);
991 r
= hashmap_ensure_put(&c
->subsystems
, &nvme_subsystem_hash_ops
, &s
->device_stat
, s
);
993 return log_error_errno(r
, "Failed to add subsystem to hash table: %m");
995 (void) nvme_subsystem_report(s
, c
->ipv4_port
, c
->ipv6_port
);
1001 static int device_removed(Context
*c
, sd_device
*device
) {
1006 struct stat lookup_key
= {
1010 r
= sd_device_get_devnum(device
, &lookup_key
.st_rdev
);
1012 return log_device_error_errno(device
, r
, "Failed to get major/minor from device: %m");
1014 NvmeSubsystem
*s
= hashmap_remove(c
->subsystems
, &lookup_key
);
1018 log_device_debug(device
, "removed block device '%s'", s
->name
);
1021 (void) nvme_port_unlink_subsystem(c
->ipv4_port
, s
);
1023 (void) nvme_port_unlink_subsystem(c
->ipv6_port
, s
);
1025 s
= nvme_subsystem_destroy(s
);
1029 static int device_monitor_handler(sd_device_monitor
*monitor
, sd_device
*device
, void *userdata
) {
1030 Context
*c
= ASSERT_PTR(userdata
);
1032 if (device_for_action(device
, SD_DEVICE_REMOVE
))
1033 device_removed(c
, device
);
1035 device_added(c
, device
);
1040 static int on_display_refresh(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1041 Context
*c
= ASSERT_PTR(userdata
);
1045 c
->display_refresh_scheduled
= false;
1047 if (isatty(STDERR_FILENO
) > 0)
1048 fputs(ANSI_HOME_CLEAR
, stderr
);
1050 /* If we have both IPv4 and IPv6, we display IPv4 info via Plymouth, since it doesn't have much
1051 * space, and IPv4 is simply shorter (and easy to type off screen) */
1053 bool plymouth_done
= false;
1054 (void) nvme_port_report(c
->ipv4_port
, &plymouth_done
);
1055 (void) nvme_port_report(c
->ipv6_port
, &plymouth_done
);
1058 (void) plymouth_send_text("Network disconnected.");
1061 HASHMAP_FOREACH(i
, c
->subsystems
)
1062 (void) nvme_subsystem_report(i
, c
->ipv4_port
, c
->ipv6_port
);
1067 static int on_address_change(sd_netlink
*rtnl
, sd_netlink_message
*mm
, void *userdata
) {
1068 Context
*c
= ASSERT_PTR(userdata
);
1074 r
= sd_rtnl_message_addr_get_family(mm
, &family
);
1076 log_warning_errno(r
, "Failed to get address family from netlink address message, ignoring: %m");
1080 if (!c
->display_refresh_scheduled
) {
1081 r
= sd_event_add_time_relative(
1082 sd_netlink_get_event(rtnl
),
1083 /* ret_slot= */ NULL
,
1085 750 * USEC_PER_MSEC
,
1090 log_warning_errno(r
, "Failed to schedule display refresh, ignoring: %m");
1092 c
->display_refresh_scheduled
= true;
1098 static int run(int argc
, char* argv
[]) {
1099 _cleanup_(sd_device_monitor_unrefp
) sd_device_monitor
*monitor
= NULL
;
1100 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1101 _cleanup_(context_done
) Context context
= {};
1104 log_show_color(true);
1105 log_parse_environment();
1108 r
= parse_argv(argc
, argv
);
1112 r
= sd_event_new(&event
);
1114 return log_error_errno(r
, "Failed to allocate event loop: %m");
1116 r
= sd_event_set_signal_exit(event
, true);
1118 return log_error_errno(r
, "Failed to install exit signal handlers: %m");
1120 STRV_FOREACH(i
, arg_devices
) {
1121 _cleanup_(nvme_subsystem_destroyp
) NvmeSubsystem
*subsys
= NULL
;
1123 r
= nvme_subsystem_add(*i
, -EBADF
, /* device= */ NULL
, &subsys
);
1127 r
= hashmap_ensure_put(&context
.subsystems
, &nvme_subsystem_hash_ops
, &subsys
->device_stat
, subsys
);
1129 log_warning_errno(r
, "Duplicate device '%s' specified, skipping: %m", *i
);
1133 return log_error_errno(r
, "Failed to add subsystem to hash table: %m");
1138 r
= nvme_port_add(arg_nqn
, AF_INET
, &context
.ipv4_port
);
1142 bool plymouth_done
= false;
1143 nvme_port_report(context
.ipv4_port
, &plymouth_done
);
1145 if (socket_ipv6_is_enabled()) {
1146 r
= nvme_port_add(arg_nqn
, AF_INET6
, &context
.ipv6_port
);
1150 nvme_port_report(context
.ipv6_port
, &plymouth_done
);
1154 (void) plymouth_send_text("Network disconnected.");
1157 HASHMAP_FOREACH(i
, context
.subsystems
) {
1158 if (context
.ipv4_port
) {
1159 r
= nvme_port_link_subsystem(context
.ipv4_port
, i
);
1164 if (context
.ipv6_port
) {
1165 r
= nvme_port_link_subsystem(context
.ipv6_port
, i
);
1170 (void) nvme_subsystem_report(i
, context
.ipv4_port
, context
.ipv6_port
);
1174 r
= sd_device_monitor_new(&monitor
);
1176 return log_error_errno(r
, "Failed to allocate device monitor: %m");
1178 r
= sd_device_monitor_filter_add_match_subsystem_devtype(monitor
, "block", "disk");
1180 return log_error_errno(r
, "Failed to configure device monitor match: %m");
1182 r
= sd_device_monitor_attach_event(monitor
, event
);
1184 return log_error_errno(r
, "Failed to attach device monitor to event loop: %m");
1186 r
= sd_device_monitor_start(monitor
, device_monitor_handler
, &context
);
1188 return log_error_errno(r
, "Failed to start device monitor: %m");
1190 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*enumerator
= NULL
;
1191 r
= sd_device_enumerator_new(&enumerator
);
1193 return log_error_errno(r
, "Failed to allocate enumerator: %m");
1195 r
= sd_device_enumerator_add_match_subsystem(enumerator
, "block", /* match= */ true);
1197 return log_error_errno(r
, "Failed to match block devices: %m");
1199 r
= sd_device_enumerator_add_match_property(enumerator
, "DEVTYPE", "disk");
1201 return log_error_errno(r
, "Failed to match whole block devices: %m");
1203 r
= sd_device_enumerator_add_nomatch_sysname(enumerator
, "loop*");
1205 return log_error_errno(r
, "Failed to exclude loop devices: %m");
1207 FOREACH_DEVICE(enumerator
, device
)
1208 device_added(&context
, device
);
1211 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
1212 r
= sd_netlink_open(&rtnl
);
1214 return log_error_errno(r
, "Failed to connect to netlink: %m");
1216 r
= sd_netlink_attach_event(rtnl
, event
, SD_EVENT_PRIORITY_NORMAL
);
1218 return log_error_errno(r
, "Failed to attach netlink socket to event loop: %m");
1220 r
= sd_netlink_add_match(rtnl
, /* ret_slot= */ NULL
, RTM_NEWADDR
, on_address_change
, /* destroy_callback= */ NULL
, &context
, "storagetm-newaddr");
1222 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR events: %m");
1224 r
= sd_netlink_add_match(rtnl
, /* ret_slot= */ NULL
, RTM_DELADDR
, on_address_change
, /* destroy_callback= */ NULL
, &context
, "storagetm-deladdr");
1226 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR events: %m");
1229 log_info("Hit Ctrl-C to exit target mode.");
1231 _unused_
_cleanup_(notify_on_cleanup
) const char *notify_message
=
1232 notify_start("READY=1\n"
1233 "STATUS=Exposing disks in target mode...",
1236 r
= sd_event_loop(event
);
1238 return log_error_errno(r
, "Failed to run event loop: %m");
1240 log_info("Exiting target mode.");
1244 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);