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