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