xref: /spdk/examples/sock/hello_world/hello_sock.c (revision 975852a079578816478a906717d1cf45fc97ddf3)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 #include "spdk/thread.h"
36 #include "spdk/env.h"
37 #include "spdk/event.h"
38 #include "spdk/log.h"
39 #include "spdk/string.h"
40 
41 #include "spdk/sock.h"
42 
43 #define ACCEPT_TIMEOUT_US 1000
44 #define CLOSE_TIMEOUT_US 1000000
45 #define BUFFER_SIZE 1024
46 #define ADDR_STR_LEN INET6_ADDRSTRLEN
47 
48 static bool g_is_running;
49 
50 static char *g_host;
51 static char *g_sock_impl_name;
52 static int g_port;
53 static bool g_is_server;
54 static bool g_verbose;
55 
56 /*
57  * We'll use this struct to gather housekeeping hello_context to pass between
58  * our events and callbacks.
59  */
60 struct hello_context_t {
61 	bool is_server;
62 	char *host;
63 	char *sock_impl_name;
64 	int port;
65 
66 	bool verbose;
67 	int bytes_in;
68 	int bytes_out;
69 
70 	struct spdk_sock *sock;
71 
72 	struct spdk_sock_group *group;
73 	struct spdk_poller *poller_in;
74 	struct spdk_poller *poller_out;
75 	struct spdk_poller *time_out;
76 
77 	int rc;
78 };
79 
80 /*
81  * Usage function for printing parameters that are specific to this application
82  */
83 static void
84 hello_sock_usage(void)
85 {
86 	printf(" -H host_addr  host address\n");
87 	printf(" -P port       port number\n");
88 	printf(" -N sock_impl  socket implementation, e.g., -N posix or -N uring\n");
89 	printf(" -S            start in server mode\n");
90 	printf(" -V            print out additional informations\n");
91 }
92 
93 /*
94  * This function is called to parse the parameters that are specific to this application
95  */
96 static int hello_sock_parse_arg(int ch, char *arg)
97 {
98 	switch (ch) {
99 	case 'H':
100 		g_host = arg;
101 		break;
102 	case 'N':
103 		g_sock_impl_name = arg;
104 		break;
105 	case 'P':
106 		g_port = spdk_strtol(arg, 10);
107 		if (g_port < 0) {
108 			fprintf(stderr, "Invalid port ID\n");
109 			return g_port;
110 		}
111 		break;
112 	case 'S':
113 		g_is_server = 1;
114 		break;
115 	case 'V':
116 		g_verbose = true;
117 		break;
118 	default:
119 		return -EINVAL;
120 	}
121 	return 0;
122 }
123 
124 static int
125 hello_sock_close_timeout_poll(void *arg)
126 {
127 	struct hello_context_t *ctx = arg;
128 	SPDK_NOTICELOG("Connection closed\n");
129 
130 	spdk_poller_unregister(&ctx->time_out);
131 	spdk_poller_unregister(&ctx->poller_in);
132 	spdk_sock_close(&ctx->sock);
133 	spdk_sock_group_close(&ctx->group);
134 
135 	spdk_app_stop(ctx->rc);
136 	return 0;
137 }
138 
139 static int
140 hello_sock_quit(struct hello_context_t *ctx, int rc)
141 {
142 	ctx->rc = rc;
143 	spdk_poller_unregister(&ctx->poller_out);
144 	if (!ctx->time_out) {
145 		ctx->time_out = SPDK_POLLER_REGISTER(hello_sock_close_timeout_poll, ctx,
146 						     CLOSE_TIMEOUT_US);
147 	}
148 	return 0;
149 }
150 
151 static int
152 hello_sock_recv_poll(void *arg)
153 {
154 	struct hello_context_t *ctx = arg;
155 	int rc;
156 	char buf_in[BUFFER_SIZE];
157 
158 	/*
159 	 * Get response
160 	 */
161 	rc = spdk_sock_recv(ctx->sock, buf_in, sizeof(buf_in) - 1);
162 
163 	if (rc <= 0) {
164 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
165 			return 0;
166 		}
167 
168 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
169 			    errno, spdk_strerror(errno));
170 		return -1;
171 	}
172 
173 	if (rc > 0) {
174 		ctx->bytes_in += rc;
175 		buf_in[rc] = '\0';
176 		printf("%s", buf_in);
177 	}
178 
179 	return 0;
180 }
181 
182 static int
183 hello_sock_writev_poll(void *arg)
184 {
185 	struct hello_context_t *ctx = arg;
186 	int rc = 0;
187 	char buf_out[BUFFER_SIZE];
188 	struct iovec iov;
189 	ssize_t n;
190 
191 	n = read(STDIN_FILENO, buf_out, sizeof(buf_out));
192 	if (n == 0 || !g_is_running) {
193 		/* EOF */
194 		SPDK_NOTICELOG("Closing connection...\n");
195 		hello_sock_quit(ctx, 0);
196 		return 0;
197 	}
198 	if (n > 0) {
199 		/*
200 		 * Send message to the server
201 		 */
202 		iov.iov_base = buf_out;
203 		iov.iov_len = n;
204 		rc = spdk_sock_writev(ctx->sock, &iov, 1);
205 		if (rc > 0) {
206 			ctx->bytes_out += rc;
207 		}
208 	}
209 	return rc;
210 }
211 
212 static int
213 hello_sock_connect(struct hello_context_t *ctx)
214 {
215 	int rc;
216 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
217 	uint16_t cport, sport;
218 
219 	SPDK_NOTICELOG("Connecting to the server on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port,
220 		       ctx->sock_impl_name);
221 
222 	ctx->sock = spdk_sock_connect(ctx->host, ctx->port, ctx->sock_impl_name);
223 	if (ctx->sock == NULL) {
224 		SPDK_ERRLOG("connect error(%d): %s\n", errno, spdk_strerror(errno));
225 		return -1;
226 	}
227 
228 	rc = spdk_sock_getaddr(ctx->sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
229 	if (rc < 0) {
230 		SPDK_ERRLOG("Cannot get connection addresses\n");
231 		spdk_sock_close(&ctx->sock);
232 		return -1;
233 	}
234 
235 	SPDK_NOTICELOG("Connection accepted from (%s, %hu) to (%s, %hu)\n", caddr, cport, saddr, sport);
236 
237 	fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
238 
239 	g_is_running = true;
240 	ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_recv_poll, ctx, 0);
241 	ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_writev_poll, ctx, 0);
242 
243 	return 0;
244 }
245 
246 static void
247 hello_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock)
248 {
249 	ssize_t n;
250 	char buf[BUFFER_SIZE];
251 	struct iovec iov;
252 	struct hello_context_t *ctx = arg;
253 
254 	n = spdk_sock_recv(sock, buf, sizeof(buf));
255 	if (n < 0) {
256 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
257 			SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
258 				    errno, spdk_strerror(errno));
259 			return;
260 		}
261 
262 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
263 			    errno, spdk_strerror(errno));
264 	}
265 
266 	if (n > 0) {
267 		ctx->bytes_in += n;
268 		iov.iov_base = buf;
269 		iov.iov_len = n;
270 		n = spdk_sock_writev(sock, &iov, 1);
271 		if (n > 0) {
272 			ctx->bytes_out += n;
273 		}
274 		return;
275 	}
276 
277 	/* Connection closed */
278 	SPDK_NOTICELOG("Connection closed\n");
279 	spdk_sock_group_remove_sock(group, sock);
280 	spdk_sock_close(&sock);
281 }
282 
283 static int
284 hello_sock_accept_poll(void *arg)
285 {
286 	struct hello_context_t *ctx = arg;
287 	struct spdk_sock *sock;
288 	int rc;
289 	int count = 0;
290 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
291 	uint16_t cport, sport;
292 
293 	if (!g_is_running) {
294 		hello_sock_quit(ctx, 0);
295 		return 0;
296 	}
297 
298 	while (1) {
299 		sock = spdk_sock_accept(ctx->sock);
300 		if (sock != NULL) {
301 			rc = spdk_sock_getaddr(sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
302 			if (rc < 0) {
303 				SPDK_ERRLOG("Cannot get connection addresses\n");
304 				spdk_sock_close(&ctx->sock);
305 				return -1;
306 			}
307 
308 			SPDK_NOTICELOG("Accepting a new connection from (%s, %hu) to (%s, %hu)\n",
309 				       caddr, cport, saddr, sport);
310 
311 			rc = spdk_sock_group_add_sock(ctx->group, sock,
312 						      hello_sock_cb, ctx);
313 
314 			if (rc < 0) {
315 				spdk_sock_close(&sock);
316 				SPDK_ERRLOG("failed\n");
317 				break;
318 			}
319 
320 			count++;
321 		} else {
322 			if (errno != EAGAIN && errno != EWOULDBLOCK) {
323 				SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
324 			}
325 			break;
326 		}
327 	}
328 
329 	return count;
330 }
331 
332 static int
333 hello_sock_group_poll(void *arg)
334 {
335 	struct hello_context_t *ctx = arg;
336 	int rc;
337 
338 	rc = spdk_sock_group_poll(ctx->group);
339 	if (rc < 0) {
340 		SPDK_ERRLOG("Failed to poll sock_group=%p\n", ctx->group);
341 	}
342 
343 	return -1;
344 }
345 
346 static int
347 hello_sock_listen(struct hello_context_t *ctx)
348 {
349 	ctx->sock = spdk_sock_listen(ctx->host, ctx->port, ctx->sock_impl_name);
350 	if (ctx->sock == NULL) {
351 		SPDK_ERRLOG("Cannot create server socket\n");
352 		return -1;
353 	}
354 
355 	SPDK_NOTICELOG("Listening connection on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port,
356 		       ctx->sock_impl_name);
357 
358 	/*
359 	 * Create sock group for server socket
360 	 */
361 	ctx->group = spdk_sock_group_create(NULL);
362 
363 	g_is_running = true;
364 
365 	/*
366 	 * Start acceptor and group poller
367 	 */
368 	ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_accept_poll, ctx,
369 					      ACCEPT_TIMEOUT_US);
370 	ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_group_poll, ctx, 0);
371 
372 	return 0;
373 }
374 
375 static void
376 hello_sock_shutdown_cb(void)
377 {
378 	g_is_running = false;
379 }
380 
381 /*
382  * Our initial event that kicks off everything from main().
383  */
384 static void
385 hello_start(void *arg1)
386 {
387 	struct hello_context_t *ctx = arg1;
388 	int rc;
389 
390 	SPDK_NOTICELOG("Successfully started the application\n");
391 
392 	if (ctx->is_server) {
393 		rc = hello_sock_listen(ctx);
394 	} else {
395 		rc = hello_sock_connect(ctx);
396 	}
397 
398 	if (rc) {
399 		spdk_app_stop(-1);
400 		return;
401 	}
402 }
403 
404 int
405 main(int argc, char **argv)
406 {
407 	struct spdk_app_opts opts = {};
408 	int rc = 0;
409 	struct hello_context_t hello_context = {};
410 
411 	/* Set default values in opts structure. */
412 	spdk_app_opts_init(&opts, sizeof(opts));
413 	opts.name = "hello_sock";
414 	opts.shutdown_cb = hello_sock_shutdown_cb;
415 
416 	if ((rc = spdk_app_parse_args(argc, argv, &opts, "H:N:P:SV", NULL, hello_sock_parse_arg,
417 				      hello_sock_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
418 		exit(rc);
419 	}
420 	hello_context.is_server = g_is_server;
421 	hello_context.host = g_host;
422 	hello_context.sock_impl_name = g_sock_impl_name;
423 	hello_context.port = g_port;
424 	hello_context.verbose = g_verbose;
425 
426 	rc = spdk_app_start(&opts, hello_start, &hello_context);
427 	if (rc) {
428 		SPDK_ERRLOG("ERROR starting application\n");
429 	}
430 
431 	SPDK_NOTICELOG("Exiting from application\n");
432 
433 	if (hello_context.verbose) {
434 		printf("** %d bytes received, %d bytes sent **\n",
435 		       hello_context.bytes_in, hello_context.bytes_out);
436 	}
437 
438 	/* Gracefully close out all of the SPDK subsystems. */
439 	spdk_app_fini();
440 	return rc;
441 }
442