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 * 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 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 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 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 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 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