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
usage(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
handle_sig(int sig __unused)52 handle_sig(int sig __unused)
53 {
54 quit = true;
55 }
56
57 static void
register_listen_socket(int kqfd,int s,void * udata)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
create_passive_sockets(int kqfd,const char * port,bool discovery)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
handle_connections(int kqfd)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
main(int ac,char ** av)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