mk: build with _GNU_SOURCE defined by default
[dpdk.git] / drivers / net / softnic / 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 softnic_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         softnic_conn_msg_handle_t msg_handle;
35         void *msg_handle_arg;
36 };
37
38 struct softnic_conn *
39 softnic_conn_init(struct softnic_conn_params *p)
40 {
41         struct sockaddr_in server_address;
42         struct softnic_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 softnic_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                 softnic_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                 softnic_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                 softnic_conn_free(conn);
99                 close(fd_server);
100                 return NULL;
101         }
102
103         status = listen(fd_server, 16);
104         if (status == -1) {
105                 softnic_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                 softnic_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
134 softnic_conn_free(struct softnic_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
153 softnic_conn_poll_for_conn(struct softnic_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
211 data_event_handle(struct softnic_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
278 control_event_handle(struct softnic_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
298 softnic_conn_poll_for_msg(struct softnic_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 }