xref: /dpdk/examples/pipeline/conn.c (revision 5f657a7fbe8697b3c36fb2987824e803f1a6cb02)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 
11 #include <sys/socket.h>
12 
13 #include <sys/epoll.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17 
18 #include "conn.h"
19 
20 #define MSG_CMD_TOO_LONG "Command too long."
21 
22 struct conn {
23 	char *welcome;
24 	char *prompt;
25 	char *buf;
26 	char *msg_in;
27 	char *msg_out;
28 	size_t buf_size;
29 	size_t msg_in_len_max;
30 	size_t msg_out_len_max;
31 	size_t msg_in_len;
32 	int fd_server;
33 	int fd_client_group;
34 	conn_msg_handle_t msg_handle;
35 	void *msg_handle_arg;
36 };
37 
38 struct conn *
conn_init(struct conn_params * p)39 conn_init(struct conn_params *p)
40 {
41 	struct sockaddr_in server_address;
42 	struct conn *conn;
43 	int fd_server, fd_client_group, status;
44 
45 	memset(&server_address, 0, sizeof(server_address));
46 
47 	/* Check input arguments */
48 	if ((p == NULL) ||
49 		(p->welcome == NULL) ||
50 		(p->prompt == NULL) ||
51 		(p->addr == NULL) ||
52 		(p->buf_size == 0) ||
53 		(p->msg_in_len_max == 0) ||
54 		(p->msg_out_len_max == 0) ||
55 		(p->msg_handle == NULL))
56 		return NULL;
57 
58 	status = inet_aton(p->addr, &server_address.sin_addr);
59 	if (status == 0)
60 		return NULL;
61 
62 	/* Memory allocation */
63 	conn = calloc(1, sizeof(struct conn));
64 	if (conn == NULL)
65 		return NULL;
66 
67 	conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
68 	conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
69 	conn->buf = calloc(1, p->buf_size);
70 	conn->msg_in = calloc(1, p->msg_in_len_max + 1);
71 	conn->msg_out = calloc(1, p->msg_out_len_max + 1);
72 
73 	if ((conn->welcome == NULL) ||
74 		(conn->prompt == NULL) ||
75 		(conn->buf == NULL) ||
76 		(conn->msg_in == NULL) ||
77 		(conn->msg_out == NULL)) {
78 		conn_free(conn);
79 		return NULL;
80 	}
81 
82 	/* Server socket */
83 	server_address.sin_family = AF_INET;
84 	server_address.sin_port = htons(p->port);
85 
86 	fd_server = socket(AF_INET,
87 		SOCK_STREAM | SOCK_NONBLOCK,
88 		0);
89 	if (fd_server == -1) {
90 		conn_free(conn);
91 		return NULL;
92 	}
93 
94 	status = bind(fd_server,
95 		(struct sockaddr *) &server_address,
96 		sizeof(server_address));
97 	if (status == -1) {
98 		conn_free(conn);
99 		close(fd_server);
100 		return NULL;
101 	}
102 
103 	status = listen(fd_server, 16);
104 	if (status == -1) {
105 		conn_free(conn);
106 		close(fd_server);
107 		return NULL;
108 	}
109 
110 	/* Client group */
111 	fd_client_group = epoll_create(1);
112 	if (fd_client_group == -1) {
113 		conn_free(conn);
114 		close(fd_server);
115 		return NULL;
116 	}
117 
118 	/* Fill in */
119 	strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
120 	strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
121 	conn->buf_size = p->buf_size;
122 	conn->msg_in_len_max = p->msg_in_len_max;
123 	conn->msg_out_len_max = p->msg_out_len_max;
124 	conn->msg_in_len = 0;
125 	conn->fd_server = fd_server;
126 	conn->fd_client_group = fd_client_group;
127 	conn->msg_handle = p->msg_handle;
128 	conn->msg_handle_arg = p->msg_handle_arg;
129 
130 	return conn;
131 }
132 
133 void
conn_free(struct conn * conn)134 conn_free(struct conn *conn)
135 {
136 	if (conn == NULL)
137 		return;
138 
139 	if (conn->fd_client_group)
140 		close(conn->fd_client_group);
141 
142 	if (conn->fd_server)
143 		close(conn->fd_server);
144 
145 	free(conn->msg_out);
146 	free(conn->msg_in);
147 	free(conn->prompt);
148 	free(conn->welcome);
149 	free(conn);
150 }
151 
152 int
conn_poll_for_conn(struct conn * conn)153 conn_poll_for_conn(struct conn *conn)
154 {
155 	struct sockaddr_in client_address;
156 	struct epoll_event event;
157 	socklen_t client_address_length;
158 	int fd_client, status;
159 
160 	/* Check input arguments */
161 	if (conn == NULL)
162 		return -1;
163 
164 	/* Server socket */
165 	client_address_length = sizeof(client_address);
166 	fd_client = accept4(conn->fd_server,
167 		(struct sockaddr *) &client_address,
168 		&client_address_length,
169 		SOCK_NONBLOCK);
170 	if (fd_client == -1) {
171 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
172 			return 0;
173 
174 		return -1;
175 	}
176 
177 	/* Client group */
178 	event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
179 	event.data.fd = fd_client;
180 
181 	status = epoll_ctl(conn->fd_client_group,
182 		EPOLL_CTL_ADD,
183 		fd_client,
184 		&event);
185 	if (status == -1) {
186 		close(fd_client);
187 		return -1;
188 	}
189 
190 	/* Client */
191 	status = write(fd_client,
192 		conn->welcome,
193 		strlen(conn->welcome));
194 	if (status == -1) {
195 		close(fd_client);
196 		return -1;
197 	}
198 
199 	status = write(fd_client,
200 		conn->prompt,
201 		strlen(conn->prompt));
202 	if (status == -1) {
203 		close(fd_client);
204 		return -1;
205 	}
206 
207 	return 0;
208 }
209 
210 static int
data_event_handle(struct conn * conn,int fd_client)211 data_event_handle(struct conn *conn,
212 	int fd_client)
213 {
214 	ssize_t len, i, status;
215 
216 	/* Read input message */
217 
218 	len = read(fd_client,
219 		conn->buf,
220 		conn->buf_size);
221 	if (len == -1) {
222 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
223 			return 0;
224 
225 		return -1;
226 	}
227 	if (len == 0)
228 		return 0;
229 
230 	/* Handle input messages */
231 	for (i = 0; i < len; i++) {
232 		if (conn->buf[i] == '\n') {
233 			size_t n;
234 
235 			conn->msg_in[conn->msg_in_len] = 0;
236 			conn->msg_out[0] = 0;
237 
238 			conn->msg_handle(conn->msg_in,
239 				conn->msg_out,
240 				conn->msg_out_len_max,
241 				conn->msg_handle_arg);
242 
243 			n = strlen(conn->msg_out);
244 			if (n) {
245 				status = write(fd_client,
246 					conn->msg_out,
247 					n);
248 				if (status == -1)
249 					return status;
250 			}
251 
252 			conn->msg_in_len = 0;
253 		} else if (conn->msg_in_len < conn->msg_in_len_max) {
254 			conn->msg_in[conn->msg_in_len] = conn->buf[i];
255 			conn->msg_in_len++;
256 		} else {
257 			status = write(fd_client,
258 				MSG_CMD_TOO_LONG,
259 				strlen(MSG_CMD_TOO_LONG));
260 			if (status == -1)
261 				return status;
262 
263 			conn->msg_in_len = 0;
264 		}
265 	}
266 
267 	/* Write prompt */
268 	status = write(fd_client,
269 		conn->prompt,
270 		strlen(conn->prompt));
271 	if (status == -1)
272 		return status;
273 
274 	return 0;
275 }
276 
277 static int
control_event_handle(struct conn * conn,int fd_client)278 control_event_handle(struct conn *conn,
279 	int fd_client)
280 {
281 	int status;
282 
283 	status = epoll_ctl(conn->fd_client_group,
284 		EPOLL_CTL_DEL,
285 		fd_client,
286 		NULL);
287 	if (status == -1)
288 		return -1;
289 
290 	status = close(fd_client);
291 	if (status == -1)
292 		return -1;
293 
294 	return 0;
295 }
296 
297 int
conn_poll_for_msg(struct conn * conn)298 conn_poll_for_msg(struct conn *conn)
299 {
300 	struct epoll_event event;
301 	int fd_client, status, status_data = 0, status_control = 0;
302 
303 	/* Check input arguments */
304 	if (conn == NULL)
305 		return -1;
306 
307 	/* Client group */
308 	status = epoll_wait(conn->fd_client_group,
309 		&event,
310 		1,
311 		0);
312 	if (status == -1)
313 		return -1;
314 	if (status == 0)
315 		return 0;
316 
317 	fd_client = event.data.fd;
318 
319 	/* Data available */
320 	if (event.events & EPOLLIN)
321 		status_data = data_event_handle(conn, fd_client);
322 
323 	/* Control events */
324 	if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
325 		status_control = control_event_handle(conn, fd_client);
326 
327 	if (status_data || status_control)
328 		return -1;
329 
330 	return 0;
331 }
332