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