new file mode 100644
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Marvell.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <rte_string_fns.h>
+
+#include "module_api.h"
+
+#define MSG_CMD_TOO_LONG "Command too long."
+
+static int
+data_event_handle(struct conn *conn, int fd_client)
+{
+ ssize_t len, i, rc = 0;
+
+ /* 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 rc;
+
+ /* 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) {
+ rc = write(fd_client, conn->msg_out, n);
+ if (rc == -1)
+ goto exit;
+ }
+
+ 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 {
+ rc = write(fd_client, MSG_CMD_TOO_LONG, strlen(MSG_CMD_TOO_LONG));
+ if (rc == -1)
+ goto exit;
+
+ conn->msg_in_len = 0;
+ }
+ }
+
+ /* Write prompt */
+ rc = write(fd_client, conn->prompt, strlen(conn->prompt));
+ rc = (rc == -1) ? -1 : 0;
+
+exit:
+ return rc;
+}
+
+static int
+control_event_handle(struct conn *conn, int fd_client)
+{
+ int rc;
+
+ rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_DEL, fd_client, NULL);
+ if (rc == -1)
+ goto exit;
+
+ rc = close(fd_client);
+ if (rc == -1)
+ goto exit;
+
+ rc = 0;
+
+exit:
+ return rc;
+}
+
+struct conn *
+conn_init(struct conn_params *p)
+{
+ int fd_server, fd_client_group, rc;
+ struct sockaddr_in server_address;
+ struct conn *conn = NULL;
+ int reuse = 1;
+
+ 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))
+ goto exit;
+
+ rc = inet_aton(p->addr, &server_address.sin_addr);
+ if (rc == 0)
+ goto exit;
+
+ /* Memory allocation */
+ conn = calloc(1, sizeof(struct conn));
+ if (conn == NULL)
+ goto exit;
+
+ 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)) {
+ conn_free(conn);
+ conn = NULL;
+ goto exit;
+ }
+
+ /* 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) {
+ conn_free(conn);
+ conn = NULL;
+ goto exit;
+ }
+
+ if (setsockopt(fd_server, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse,
+ sizeof(reuse)) < 0)
+ goto free;
+
+ rc = bind(fd_server, (struct sockaddr *)&server_address, sizeof(server_address));
+ if (rc == -1)
+ goto free;
+
+ rc = listen(fd_server, 16);
+ if (rc == -1)
+ goto free;
+
+ /* Client group */
+ fd_client_group = epoll_create(1);
+ if (fd_client_group == -1)
+ goto free;
+
+ /* Fill in */
+ rte_strscpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
+ rte_strscpy(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;
+
+exit:
+ return conn;
+free:
+ conn_free(conn);
+ close(fd_server);
+ conn = NULL;
+ return conn;
+}
+
+void
+conn_free(struct 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
+conn_req_poll(struct conn *conn)
+{
+ struct sockaddr_in client_address;
+ socklen_t client_address_length;
+ struct epoll_event event;
+ int fd_client, rc;
+
+ /* 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;
+
+ rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_ADD, fd_client, &event);
+ if (rc == -1) {
+ close(fd_client);
+ goto exit;
+ }
+
+ /* Client */
+ rc = write(fd_client, conn->welcome, strlen(conn->welcome));
+ if (rc == -1) {
+ close(fd_client);
+ goto exit;
+ }
+
+ rc = write(fd_client, conn->prompt, strlen(conn->prompt));
+ if (rc == -1) {
+ close(fd_client);
+ goto exit;
+ }
+
+ rc = 0;
+
+exit:
+ return rc;
+}
+
+int
+conn_msg_poll(struct conn *conn)
+{
+ int fd_client, rc, rc_data = 0, rc_control = 0;
+ struct epoll_event event;
+
+ /* Check input arguments */
+ if (conn == NULL)
+ return -1;
+
+ /* Client group */
+ rc = epoll_wait(conn->fd_client_group, &event, 1, 0);
+ if ((rc == -1) || rc == 0)
+ return rc;
+
+ fd_client = event.data.fd;
+
+ /* Data available */
+ if (event.events & EPOLLIN)
+ rc_data = data_event_handle(conn, fd_client);
+
+ /* Control events */
+ if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
+ rc_control = control_event_handle(conn, fd_client);
+
+ if (rc_data || rc_control)
+ return -1;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Marvell.
+ */
+
+#ifndef APP_GRAPH_CONN_H
+#define APP_GRAPH_CONN_H
+
+#define CONN_WELCOME_LEN_MAX 1024
+#define CONN_PROMPT_LEN_MAX 16
+
+typedef void (*conn_msg_handle_t)(char *msg_in, char *msg_out, size_t msg_out_len_max, void *arg);
+
+struct 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;
+ conn_msg_handle_t msg_handle;
+ void *msg_handle_arg;
+};
+
+struct conn_params {
+ const char *welcome;
+ const char *prompt;
+ const char *addr;
+ uint16_t port;
+ size_t buf_size;
+ size_t msg_in_len_max;
+ size_t msg_out_len_max;
+ conn_msg_handle_t msg_handle;
+ void *msg_handle_arg;
+};
+
+struct conn *conn_init(struct conn_params *p);
+void conn_free(struct conn *conn);
+int conn_req_poll(struct conn *conn);
+int conn_msg_poll(struct conn *conn);
+
+#endif
@@ -2,6 +2,7 @@
* Copyright(c) 2023 Marvell.
*/
+#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
@@ -11,19 +12,33 @@
#include <sys/select.h>
#include <unistd.h>
+#include <rte_cycles.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include "module_api.h"
volatile bool force_quit;
+struct conn *conn;
-static const char usage[] = "%s EAL_ARGS -- -s SCRIPT "
+static const char usage[] = "%s EAL_ARGS -- -s SCRIPT [-h HOST] [-p PORT] "
"[--help]\n";
static struct app_params {
+ struct conn_params conn;
char *script_name;
} app = {
+ .conn = {
+ .welcome = "\nWelcome!\n\n",
+ .prompt = "graph> ",
+ .addr = "0.0.0.0",
+ .port = 8086,
+ .buf_size = 1024 * 1024,
+ .msg_in_len_max = 1024,
+ .msg_out_len_max = 1024 * 1024,
+ .msg_handle = cli_process,
+ .msg_handle_arg = NULL, /* set later. */
+ },
.script_name = NULL,
};
@@ -42,7 +57,7 @@ app_args_parse(int argc, char **argv)
struct option lgopts[] = {
{"help", 0, 0, 'H'},
};
- int s_present, n_args, i;
+ int h_present, p_present, s_present, n_args, i;
char *app_name = argv[0];
int opt, option_index;
@@ -59,10 +74,46 @@ app_args_parse(int argc, char **argv)
return 0;
/* Parse args */
+ h_present = 0;
+ p_present = 0;
s_present = 0;
- while ((opt = getopt_long(argc, argv, "s:", lgopts, &option_index)) != EOF) {
+ while ((opt = getopt_long(argc, argv, "h:p:s:", lgopts, &option_index)) != EOF) {
switch (opt) {
+ case 'h':
+ if (h_present) {
+ printf("Error: Multiple -h arguments\n");
+ return -1;
+ }
+ h_present = 1;
+
+ if (!strlen(optarg)) {
+ printf("Error: Argument for -h not provided\n");
+ return -1;
+ }
+
+ app.conn.addr = strdup(optarg);
+ if (app.conn.addr == NULL) {
+ printf("Error: Not enough memory\n");
+ return -1;
+ }
+ break;
+
+ case 'p':
+ if (p_present) {
+ printf("Error: Multiple -p arguments\n");
+ return -1;
+ }
+ p_present = 1;
+
+ if (!strlen(optarg)) {
+ printf("Error: Argument for -p not provided\n");
+ return -1;
+ }
+
+ app.conn.port = (uint16_t)strtoul(optarg, NULL, 10);
+ break;
+
case 's':
if (s_present) {
printf("Error: Multiple -s arguments\n");
@@ -93,6 +144,26 @@ app_args_parse(int argc, char **argv)
return 0;
}
+bool
+app_graph_exit(void)
+{
+ struct timeval tv;
+ fd_set fds;
+ int ret;
+ char c;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 100;
+ ret = select(1, &fds, NULL, NULL, &tv);
+ if ((ret < 0 && errno == EINTR) || (ret == 1 && read(0, &c, 1) > 0))
+ return true;
+ else
+ return false;
+
+}
+
int
main(int argc, char **argv)
{
@@ -118,10 +189,32 @@ main(int argc, char **argv)
/* Script */
if (app.script_name) {
- cli_script_process(app.script_name, 0,
- 0, NULL);
+ cli_script_process(app.script_name, app.conn.msg_in_len_max,
+ app.conn.msg_out_len_max, NULL);
+ }
+
+ /* Connectivity */
+ app.conn.msg_handle_arg = NULL;
+ conn = conn_init(&app.conn);
+ if (!conn) {
+ printf("Error: Connectivity initialization failed\n");
+ goto exit;
+ };
+
+ rte_delay_ms(1);
+ printf("Press enter to exit\n");
+
+ /* Dispatch loop */
+ while (!force_quit) {
+ conn_req_poll(conn);
+
+ conn_msg_poll(conn);
+ if (app_graph_exit())
+ force_quit = true;
}
+exit:
+ conn_free(conn);
cli_exit();
rte_eal_cleanup();
return 0;
@@ -11,5 +11,6 @@ endif
deps += ['graph', 'eal', 'lpm', 'ethdev', 'node', 'cmdline']
sources = files(
'cli.c',
+ 'conn.c',
'main.c',
)
@@ -7,10 +7,13 @@
#include <stdint.h>
#include <stdbool.h>
+
#include "cli.h"
+#include "conn.h"
/*
* Externs
*/
extern volatile bool force_quit;
+bool app_graph_exit(void);
#endif
@@ -39,6 +39,16 @@ Application Options
Following are the application command-line options:
+* ``-h``
+
+ Set the host IPv4 address over which telnet session can be opened.
+ It is an optional parameter. Default host address is 0.0.0.0.
+
+* ``-p``
+
+ Set the L4 port number over which telnet session can be opened.
+ It is an optional parameter. Default port is 8086.
+
* ``-s``
Script name with absolute path which specifies the use case. It is
@@ -72,7 +82,35 @@ file to express the requested use case configuration.
Runtime configuration
---------------------
+Application allows some configuration to be modified at runtime using a telnet session.
+Application initiates a telnet server with host address ``0.0.0.0`` and port number ``8086``
+by default.
+
+if user passes ``-h`` and ``-p`` options while running application then corresponding
+IP address and port number will be used for telnet session.
+
+After successful launch of application, client can connect to application using given
+host & port and console will be accessed with prompt ``graph>``.
+
+Command to access a telnet session
+
+.. code-block:: console
+
+ telnet <host> <port>
+
+Example: ``dpdk-graph`` is started with -h 10.28.35.207 and -p 50000 then
+
+.. code-block:: console
+
+ $ telnet 10.28.35.207 50000
+ Trying 10.28.35.207...
+ Connected to 10.28.35.207.
+ Escape character is '^]'.
+
+ Welcome!
+ graph>
+ graph>
Created graph for use case
--------------------------