numam-dpdk/drivers/net/softnic/conn.c
Anatoly Burakov 5d7b673d5f mk: build with _GNU_SOURCE defined by default
We use _GNU_SOURCE all over the place, but often times we miss
defining it, resulting in broken builds on musl. Rather than
fixing every library's and driver's and application's makefile,
fix it by simply defining _GNU_SOURCE by default for all
builds.

Remove all usages of _GNU_SOURCE in source files and makefiles,
and also fixup a couple of instances of using __USE_GNU instead
of _GNU_SOURCE.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
2018-10-22 11:28:27 +02:00

332 lines
6.2 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2018 Intel Corporation
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "conn.h"
#define MSG_CMD_TOO_LONG "Command too long."
struct softnic_conn {
char *welcome;
char *prompt;
char *buf;
char *msg_in;
char *msg_out;
size_t buf_size;
size_t msg_in_len_max;
size_t msg_out_len_max;
size_t msg_in_len;
int fd_server;
int fd_client_group;
softnic_conn_msg_handle_t msg_handle;
void *msg_handle_arg;
};
struct softnic_conn *
softnic_conn_init(struct softnic_conn_params *p)
{
struct sockaddr_in server_address;
struct softnic_conn *conn;
int fd_server, fd_client_group, status;
memset(&server_address, 0, sizeof(server_address));
/* Check input arguments */
if (p == NULL ||
p->welcome == NULL ||
p->prompt == NULL ||
p->addr == NULL ||
p->buf_size == 0 ||
p->msg_in_len_max == 0 ||
p->msg_out_len_max == 0 ||
p->msg_handle == NULL)
return NULL;
status = inet_aton(p->addr, &server_address.sin_addr);
if (status == 0)
return NULL;
/* Memory allocation */
conn = calloc(1, sizeof(struct softnic_conn));
if (conn == NULL)
return NULL;
conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
conn->buf = calloc(1, p->buf_size);
conn->msg_in = calloc(1, p->msg_in_len_max + 1);
conn->msg_out = calloc(1, p->msg_out_len_max + 1);
if (conn->welcome == NULL ||
conn->prompt == NULL ||
conn->buf == NULL ||
conn->msg_in == NULL ||
conn->msg_out == NULL) {
softnic_conn_free(conn);
return NULL;
}
/* Server socket */
server_address.sin_family = AF_INET;
server_address.sin_port = htons(p->port);
fd_server = socket(AF_INET,
SOCK_STREAM | SOCK_NONBLOCK,
0);
if (fd_server == -1) {
softnic_conn_free(conn);
return NULL;
}
status = bind(fd_server,
(struct sockaddr *)&server_address,
sizeof(server_address));
if (status == -1) {
softnic_conn_free(conn);
close(fd_server);
return NULL;
}
status = listen(fd_server, 16);
if (status == -1) {
softnic_conn_free(conn);
close(fd_server);
return NULL;
}
/* Client group */
fd_client_group = epoll_create(1);
if (fd_client_group == -1) {
softnic_conn_free(conn);
close(fd_server);
return NULL;
}
/* Fill in */
strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
conn->buf_size = p->buf_size;
conn->msg_in_len_max = p->msg_in_len_max;
conn->msg_out_len_max = p->msg_out_len_max;
conn->msg_in_len = 0;
conn->fd_server = fd_server;
conn->fd_client_group = fd_client_group;
conn->msg_handle = p->msg_handle;
conn->msg_handle_arg = p->msg_handle_arg;
return conn;
}
void
softnic_conn_free(struct softnic_conn *conn)
{
if (conn == NULL)
return;
if (conn->fd_client_group)
close(conn->fd_client_group);
if (conn->fd_server)
close(conn->fd_server);
free(conn->msg_out);
free(conn->msg_in);
free(conn->prompt);
free(conn->welcome);
free(conn);
}
int
softnic_conn_poll_for_conn(struct softnic_conn *conn)
{
struct sockaddr_in client_address;
struct epoll_event event;
socklen_t client_address_length;
int fd_client, status;
/* Check input arguments */
if (conn == NULL)
return -1;
/* Server socket */
client_address_length = sizeof(client_address);
fd_client = accept4(conn->fd_server,
(struct sockaddr *)&client_address,
&client_address_length,
SOCK_NONBLOCK);
if (fd_client == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return 0;
return -1;
}
/* Client group */
event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
event.data.fd = fd_client;
status = epoll_ctl(conn->fd_client_group,
EPOLL_CTL_ADD,
fd_client,
&event);
if (status == -1) {
close(fd_client);
return -1;
}
/* Client */
status = write(fd_client,
conn->welcome,
strlen(conn->welcome));
if (status == -1) {
close(fd_client);
return -1;
}
status = write(fd_client,
conn->prompt,
strlen(conn->prompt));
if (status == -1) {
close(fd_client);
return -1;
}
return 0;
}
static int
data_event_handle(struct softnic_conn *conn,
int fd_client)
{
ssize_t len, i, status;
/* Read input message */
len = read(fd_client,
conn->buf,
conn->buf_size);
if (len == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return 0;
return -1;
}
if (len == 0)
return 0;
/* Handle input messages */
for (i = 0; i < len; i++) {
if (conn->buf[i] == '\n') {
size_t n;
conn->msg_in[conn->msg_in_len] = 0;
conn->msg_out[0] = 0;
conn->msg_handle(conn->msg_in,
conn->msg_out,
conn->msg_out_len_max,
conn->msg_handle_arg);
n = strlen(conn->msg_out);
if (n) {
status = write(fd_client,
conn->msg_out,
n);
if (status == -1)
return status;
}
conn->msg_in_len = 0;
} else if (conn->msg_in_len < conn->msg_in_len_max) {
conn->msg_in[conn->msg_in_len] = conn->buf[i];
conn->msg_in_len++;
} else {
status = write(fd_client,
MSG_CMD_TOO_LONG,
strlen(MSG_CMD_TOO_LONG));
if (status == -1)
return status;
conn->msg_in_len = 0;
}
}
/* Write prompt */
status = write(fd_client,
conn->prompt,
strlen(conn->prompt));
if (status == -1)
return status;
return 0;
}
static int
control_event_handle(struct softnic_conn *conn,
int fd_client)
{
int status;
status = epoll_ctl(conn->fd_client_group,
EPOLL_CTL_DEL,
fd_client,
NULL);
if (status == -1)
return -1;
status = close(fd_client);
if (status == -1)
return -1;
return 0;
}
int
softnic_conn_poll_for_msg(struct softnic_conn *conn)
{
struct epoll_event event;
int fd_client, status, status_data = 0, status_control = 0;
/* Check input arguments */
if (conn == NULL)
return -1;
/* Client group */
status = epoll_wait(conn->fd_client_group,
&event,
1,
0);
if (status == -1)
return -1;
if (status == 0)
return 0;
fd_client = event.data.fd;
/* Data available */
if (event.events & EPOLLIN)
status_data = data_event_handle(conn, fd_client);
/* Control events */
if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
status_control = control_event_handle(conn, fd_client);
if (status_data || status_control)
return -1;
return 0;
}