xref: /spdk/examples/sock/hello_world/hello_sock.c (revision 1fc4165fe9bf8512483356ad8e6d27f793f2e3db)
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 #include "spdk/net.h"
43 
44 #define ACCEPT_TIMEOUT_US 1000
45 #define CLOSE_TIMEOUT_US 1000000
46 #define BUFFER_SIZE 1024
47 #define ADDR_STR_LEN INET6_ADDRSTRLEN
48 
49 static bool g_is_running;
50 
51 static char *g_host;
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 	int port;
64 
65 	bool verbose;
66 	int bytes_in;
67 	int bytes_out;
68 
69 	struct spdk_sock *sock;
70 
71 	struct spdk_sock_group *group;
72 	struct spdk_poller *poller_in;
73 	struct spdk_poller *poller_out;
74 	struct spdk_poller *time_out;
75 
76 	int rc;
77 };
78 
79 /*
80  * Usage function for printing parameters that are specific to this application
81  */
82 static void
83 hello_sock_usage(void)
84 {
85 	printf(" -H host_addr  host address\n");
86 	printf(" -P port       port number\n");
87 	printf(" -S            start in server mode\n");
88 	printf(" -V            print out additional informations");
89 }
90 
91 /*
92  * This function is called to parse the parameters that are specific to this application
93  */
94 static int hello_sock_parse_arg(int ch, char *arg)
95 {
96 	switch (ch) {
97 	case 'H':
98 		g_host = arg;
99 		break;
100 	case 'P':
101 		g_port = spdk_strtol(arg, 10);
102 		if (g_port < 0) {
103 			fprintf(stderr, "Invalid port ID\n");
104 			return g_port;
105 		}
106 		break;
107 	case 'S':
108 		g_is_server = 1;
109 		break;
110 	case 'V':
111 		g_verbose = true;
112 		break;
113 	default:
114 		return -EINVAL;
115 	}
116 	return 0;
117 }
118 
119 static void
120 hello_sock_net_fini_cb(void *cb_arg)
121 {
122 	struct hello_context_t *ctx = cb_arg;
123 	spdk_app_stop(ctx->rc);
124 }
125 
126 static int
127 hello_sock_close_timeout_poll(void *arg)
128 {
129 	struct hello_context_t *ctx = arg;
130 	SPDK_NOTICELOG("Connection closed\n");
131 
132 	spdk_poller_unregister(&ctx->time_out);
133 	spdk_poller_unregister(&ctx->poller_in);
134 	spdk_sock_close(&ctx->sock);
135 	spdk_sock_group_close(&ctx->group);
136 
137 	spdk_net_framework_fini(hello_sock_net_fini_cb, arg);
138 	return 0;
139 }
140 
141 static int
142 hello_sock_quit(struct hello_context_t *ctx, int rc)
143 {
144 	ctx->rc = rc;
145 	spdk_poller_unregister(&ctx->poller_out);
146 	if (!ctx->time_out) {
147 		ctx->time_out = spdk_poller_register(hello_sock_close_timeout_poll, ctx,
148 						     CLOSE_TIMEOUT_US);
149 	}
150 	return 0;
151 }
152 
153 static int
154 hello_sock_recv_poll(void *arg)
155 {
156 	struct hello_context_t *ctx = arg;
157 	int rc;
158 	char buf_in[BUFFER_SIZE];
159 
160 	/*
161 	 * Get response
162 	 */
163 	rc = spdk_sock_recv(ctx->sock, buf_in, sizeof(buf_in) - 1);
164 
165 	if (rc <= 0) {
166 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
167 			return 0;
168 		}
169 
170 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
171 			    errno, spdk_strerror(errno));
172 		return -1;
173 	}
174 
175 	if (rc > 0) {
176 		ctx->bytes_in += rc;
177 		buf_in[rc] = '\0';
178 		printf("%s", buf_in);
179 	}
180 
181 	return 0;
182 }
183 
184 static int
185 hello_sock_writev_poll(void *arg)
186 {
187 	struct hello_context_t *ctx = arg;
188 	int rc = 0;
189 	char buf_out[BUFFER_SIZE];
190 	struct iovec iov;
191 	ssize_t n;
192 
193 	n = read(STDIN_FILENO, buf_out, sizeof(buf_out));
194 	if (n == 0 || !g_is_running) {
195 		/* EOF */
196 		SPDK_NOTICELOG("Closing connection...\n");
197 		hello_sock_quit(ctx, 0);
198 		return 0;
199 	}
200 	if (n > 0) {
201 		/*
202 		 * Send message to the server
203 		 */
204 		iov.iov_base = buf_out;
205 		iov.iov_len = n;
206 		rc = spdk_sock_writev(ctx->sock, &iov, 1);
207 		if (rc > 0) {
208 			ctx->bytes_out += rc;
209 		}
210 	}
211 	return rc;
212 }
213 
214 static int
215 hello_sock_connect(struct hello_context_t *ctx)
216 {
217 	int rc;
218 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
219 	uint16_t cport, sport;
220 
221 	SPDK_NOTICELOG("Connecting to the server on %s:%d\n", ctx->host, ctx->port);
222 
223 	ctx->sock = spdk_sock_connect(ctx->host, ctx->port);
224 	if (ctx->sock == NULL) {
225 		SPDK_ERRLOG("connect error(%d): %s\n", errno, spdk_strerror(errno));
226 		return -1;
227 	}
228 
229 	rc = spdk_sock_getaddr(ctx->sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
230 	if (rc < 0) {
231 		SPDK_ERRLOG("Cannot get connection addresses\n");
232 		spdk_sock_close(&ctx->sock);
233 		return -1;
234 	}
235 
236 	SPDK_NOTICELOG("Connection accepted from (%s, %hu) to (%s, %hu)\n", caddr, cport, saddr, sport);
237 
238 	fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
239 
240 	g_is_running = true;
241 	ctx->poller_in = spdk_poller_register(hello_sock_recv_poll, ctx, 0);
242 	ctx->poller_out = spdk_poller_register(hello_sock_writev_poll, ctx, 0);
243 
244 	return 0;
245 }
246 
247 static void
248 hello_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock)
249 {
250 	ssize_t n;
251 	char buf[BUFFER_SIZE];
252 	struct iovec iov;
253 	struct hello_context_t *ctx = arg;
254 
255 	n = spdk_sock_recv(sock, buf, sizeof(buf));
256 	if (n < 0) {
257 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
258 			SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
259 				    errno, spdk_strerror(errno));
260 			return;
261 		}
262 
263 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
264 			    errno, spdk_strerror(errno));
265 	}
266 
267 	if (n > 0) {
268 		ctx->bytes_in += n;
269 		iov.iov_base = buf;
270 		iov.iov_len = n;
271 		n = spdk_sock_writev(sock, &iov, 1);
272 		if (n > 0) {
273 			ctx->bytes_out += n;
274 		}
275 		return;
276 	}
277 
278 	/* Connection closed */
279 	SPDK_NOTICELOG("Connection closed\n");
280 	spdk_sock_group_remove_sock(group, sock);
281 	spdk_sock_close(&sock);
282 }
283 
284 static int
285 hello_sock_accept_poll(void *arg)
286 {
287 	struct hello_context_t *ctx = arg;
288 	struct spdk_sock *sock;
289 	int rc;
290 	int count = 0;
291 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
292 	uint16_t cport, sport;
293 
294 	if (!g_is_running) {
295 		hello_sock_quit(ctx, 0);
296 		return 0;
297 	}
298 
299 	while (1) {
300 		sock = spdk_sock_accept(ctx->sock);
301 		if (sock != NULL) {
302 			rc = spdk_sock_getaddr(sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
303 			if (rc < 0) {
304 				SPDK_ERRLOG("Cannot get connection addresses\n");
305 				spdk_sock_close(&ctx->sock);
306 				return -1;
307 			}
308 
309 			SPDK_NOTICELOG("Accepting a new connection from (%s, %hu) to (%s, %hu)\n",
310 				       caddr, cport, saddr, sport);
311 
312 			rc = spdk_sock_group_add_sock(ctx->group, sock,
313 						      hello_sock_cb, ctx);
314 
315 			if (rc < 0) {
316 				spdk_sock_close(&sock);
317 				SPDK_ERRLOG("failed\n");
318 				break;
319 			}
320 
321 			count++;
322 		} else {
323 			if (errno != EAGAIN && errno != EWOULDBLOCK) {
324 				SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
325 			}
326 			break;
327 		}
328 	}
329 
330 	return count;
331 }
332 
333 static int
334 hello_sock_group_poll(void *arg)
335 {
336 	struct hello_context_t *ctx = arg;
337 	int rc;
338 
339 	rc = spdk_sock_group_poll(ctx->group);
340 	if (rc < 0) {
341 		SPDK_ERRLOG("Failed to poll sock_group=%p\n", ctx->group);
342 	}
343 
344 	return -1;
345 }
346 
347 static int
348 hello_sock_listen(struct hello_context_t *ctx)
349 {
350 	ctx->sock = spdk_sock_listen(ctx->host, ctx->port);
351 	if (ctx->sock == NULL) {
352 		SPDK_ERRLOG("Cannot create server socket\n");
353 		return -1;
354 	}
355 
356 	SPDK_NOTICELOG("Listening connection on %s:%d\n", ctx->host, ctx->port);
357 
358 	/*
359 	 * Create sock group for server socket
360 	 */
361 	ctx->group = spdk_sock_group_create();
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, int rc)
386 {
387 	struct hello_context_t *ctx = arg1;
388 
389 	if (rc) {
390 		SPDK_ERRLOG("ERROR starting application\n");
391 		spdk_app_stop(-1);
392 		return;
393 	}
394 
395 	SPDK_NOTICELOG("Successfully started the application\n");
396 
397 	if (ctx->is_server) {
398 		rc = hello_sock_listen(ctx);
399 	} else {
400 		rc = hello_sock_connect(ctx);
401 	}
402 
403 	if (rc) {
404 		spdk_app_stop(-1);
405 		return;
406 	}
407 }
408 
409 static void
410 start_net_framework(void *arg1, void *arg2)
411 {
412 	spdk_net_framework_start(hello_start, arg1);
413 }
414 
415 int
416 main(int argc, char **argv)
417 {
418 	struct spdk_app_opts opts = {};
419 	int rc = 0;
420 	struct hello_context_t hello_context = {};
421 
422 	/* Set default values in opts structure. */
423 	spdk_app_opts_init(&opts);
424 	opts.name = "hello_sock";
425 	opts.config_file = "sock.conf";
426 	opts.shutdown_cb = hello_sock_shutdown_cb;
427 
428 	if ((rc = spdk_app_parse_args(argc, argv, &opts, "H:P:SV", NULL, hello_sock_parse_arg,
429 				      hello_sock_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
430 		exit(rc);
431 	}
432 	hello_context.is_server = g_is_server;
433 	hello_context.host = g_host;
434 	hello_context.port = g_port;
435 	hello_context.verbose = g_verbose;
436 
437 	rc = spdk_app_start(&opts, start_net_framework, &hello_context);
438 	if (rc) {
439 		SPDK_ERRLOG("ERROR starting application\n");
440 	}
441 
442 	SPDK_NOTICELOG("Exiting from application\n");
443 
444 	if (hello_context.verbose) {
445 		printf("** %d bytes received, %d bytes sent **\n",
446 		       hello_context.bytes_in, hello_context.bytes_out);
447 	}
448 
449 	/* Gracefully close out all of the SPDK subsystems. */
450 	spdk_app_fini();
451 	return rc;
452 }
453