]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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 | ||
11fdf7f2 TL |
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 | } |