xref: /freebsd/usr.sbin/nvmfd/nvmfd.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <sys/param.h>
9 #include <sys/event.h>
10 #include <sys/linker.h>
11 #include <sys/module.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <assert.h>
15 #include <err.h>
16 #include <errno.h>
17 #include <libnvmf.h>
18 #include <libutil.h>
19 #include <netdb.h>
20 #include <signal.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "internal.h"
28 
29 bool data_digests = false;
30 bool header_digests = false;
31 bool flow_control_disable = false;
32 bool kernel_io = false;
33 uint32_t maxh2cdata = 256 * 1024;
34 
35 static const char *subnqn;
36 static volatile bool quit = false;
37 
38 static void
39 usage(void)
40 {
41 	fprintf(stderr, "nvmfd -K [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n"
42 	    "nvmfd [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n"
43 	    "\tdevice [device [...]]\n"
44 	    "\n"
45 	    "Devices use one of the following syntaxes:\n"
46 	    "\tpathame      - file or disk device\n"
47 	    "\tramdisk:size - memory disk of given size\n");
48 	exit(1);
49 }
50 
51 static void
52 handle_sig(int sig __unused)
53 {
54 	quit = true;
55 }
56 
57 static void
58 register_listen_socket(int kqfd, int s, void *udata)
59 {
60 	struct kevent kev;
61 
62 	if (listen(s, -1) != 0)
63 		err(1, "listen");
64 
65 	EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, udata);
66 	if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == -1)
67 		err(1, "kevent: failed to add listen socket");
68 }
69 
70 static void
71 create_passive_sockets(int kqfd, const char *port, bool discovery)
72 {
73 	struct addrinfo hints, *ai, *list;
74 	bool created;
75 	int error, s;
76 
77 	memset(&hints, 0, sizeof(hints));
78 	hints.ai_flags = AI_PASSIVE;
79 	hints.ai_family = AF_UNSPEC;
80 	hints.ai_protocol = IPPROTO_TCP;
81 	error = getaddrinfo(NULL, port, &hints, &list);
82 	if (error != 0)
83 		errx(1, "%s", gai_strerror(error));
84 	created = false;
85 
86 	for (ai = list; ai != NULL; ai = ai->ai_next) {
87 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
88 		if (s == -1)
89 			continue;
90 
91 		if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
92 			close(s);
93 			continue;
94 		}
95 
96 		if (discovery) {
97 			register_listen_socket(kqfd, s, (void *)1);
98 		} else {
99 			register_listen_socket(kqfd, s, (void *)2);
100 			discovery_add_io_controller(s, subnqn);
101 		}
102 		created = true;
103 	}
104 
105 	freeaddrinfo(list);
106 	if (!created)
107 		err(1, "Failed to create any listen sockets");
108 }
109 
110 static void
111 handle_connections(int kqfd)
112 {
113 	struct kevent ev;
114 	int s;
115 
116 	signal(SIGHUP, handle_sig);
117 	signal(SIGINT, handle_sig);
118 	signal(SIGQUIT, handle_sig);
119 	signal(SIGTERM, handle_sig);
120 
121 	while (!quit) {
122 		if (kevent(kqfd, NULL, 0, &ev, 1, NULL) == -1) {
123 			if (errno == EINTR)
124 				continue;
125 			err(1, "kevent");
126 		}
127 
128 		assert(ev.filter == EVFILT_READ);
129 
130 		s = accept(ev.ident, NULL, NULL);
131 		if (s == -1) {
132 			warn("accept");
133 			continue;
134 		}
135 
136 		switch ((uintptr_t)ev.udata) {
137 		case 1:
138 			handle_discovery_socket(s);
139 			break;
140 		case 2:
141 			handle_io_socket(s);
142 			break;
143 		default:
144 			__builtin_unreachable();
145 		}
146 	}
147 }
148 
149 int
150 main(int ac, char **av)
151 {
152 	struct pidfh *pfh;
153 	const char *dport, *ioport, *transport;
154 	pid_t pid;
155 	uint64_t value;
156 	int ch, error, kqfd;
157 	bool daemonize;
158 	static char nqn[NVMF_NQN_MAX_LEN];
159 
160 	/* 7.4.9.3 Default port for discovery */
161 	dport = "8009";
162 
163 	pfh = NULL;
164 	daemonize = true;
165 	ioport = "0";
166 	subnqn = NULL;
167 	transport = "tcp";
168 	while ((ch = getopt(ac, av, "dFgGH:Kn:P:p:t:")) != -1) {
169 		switch (ch) {
170 		case 'd':
171 			daemonize = false;
172 			break;
173 		case 'F':
174 			flow_control_disable = true;
175 			break;
176 		case 'G':
177 			data_digests = true;
178 			break;
179 		case 'g':
180 			header_digests = true;
181 			break;
182 		case 'H':
183 			if (expand_number(optarg, &value) != 0)
184 				errx(1, "Invalid MAXH2CDATA value %s", optarg);
185 			if (value < 4096 || value > UINT32_MAX ||
186 			    value % 4 != 0)
187 				errx(1, "Invalid MAXH2CDATA value %s", optarg);
188 			maxh2cdata = value;
189 			break;
190 		case 'K':
191 			kernel_io = true;
192 			break;
193 		case 'n':
194 			subnqn = optarg;
195 			break;
196 		case 'P':
197 			dport = optarg;
198 			break;
199 		case 'p':
200 			ioport = optarg;
201 			break;
202 		case 't':
203 			transport = optarg;
204 			break;
205 		default:
206 			usage();
207 		}
208 	}
209 
210 	av += optind;
211 	ac -= optind;
212 
213 	if (kernel_io) {
214 		if (ac > 0)
215 			usage();
216 		if (modfind("nvmft") == -1 && kldload("nvmft") == -1)
217 			warn("couldn't load nvmft");
218 	} else {
219 		if (ac < 1)
220 			usage();
221 	}
222 
223 	if (strcasecmp(transport, "tcp") == 0) {
224 	} else
225 		errx(1, "Invalid transport %s", transport);
226 
227 	if (subnqn == NULL) {
228 		error = nvmf_nqn_from_hostuuid(nqn);
229 		if (error != 0)
230 			errc(1, error, "Failed to generate NQN");
231 		subnqn = nqn;
232 	}
233 
234 	if (!kernel_io)
235 		register_devices(ac, av);
236 
237 	init_discovery();
238 	init_io(subnqn);
239 
240 	if (daemonize) {
241 		pfh = pidfile_open(NULL, 0600, &pid);
242 		if (pfh == NULL) {
243 			if (errno == EEXIST)
244 				errx(1, "Daemon already running, pid: %jd",
245 				    (intmax_t)pid);
246 			warn("Cannot open or create pidfile");
247 		}
248 
249 		if (daemon(0, 0) != 0) {
250 			pidfile_remove(pfh);
251 			err(1, "Failed to fork into the background");
252 		}
253 
254 		pidfile_write(pfh);
255 	}
256 
257 	kqfd = kqueue();
258 	if (kqfd == -1) {
259 		pidfile_remove(pfh);
260 		err(1, "kqueue");
261 	}
262 
263 	create_passive_sockets(kqfd, dport, true);
264 	create_passive_sockets(kqfd, ioport, false);
265 
266 	handle_connections(kqfd);
267 	shutdown_io();
268 	if (pfh != NULL)
269 		pidfile_remove(pfh);
270 	return (0);
271 }
272