examples/ip_pipeline: add CLI interface
authorJasvinder Singh <jasvinder.singh@intel.com>
Thu, 29 Mar 2018 18:31:40 +0000 (19:31 +0100)
committerCristian Dumitrescu <cristian.dumitrescu@intel.com>
Thu, 5 Apr 2018 16:23:49 +0000 (18:23 +0200)
CLI interface allowing connectivity with external agent (e.g. telnet,
netcat, Python script, etc) is added to the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
examples/ip_pipeline/Makefile
examples/ip_pipeline/cli.c [new file with mode: 0644]
examples/ip_pipeline/cli.h [new file with mode: 0644]
examples/ip_pipeline/conn.c [new file with mode: 0644]
examples/ip_pipeline/conn.h [new file with mode: 0644]
examples/ip_pipeline/main.c
examples/ip_pipeline/meson.build

index 981c4f7..0c5b6b1 100644 (file)
@@ -5,7 +5,9 @@
 APP = ip_pipeline
 
 # all source are stored in SRCS-y
-SRCS-y := main.c
+SRCS-y := cli.c
+SRCS-y += conn.c
+SRCS-y += main.c
 SRCS-y += parser.c
 #SRCS-y += thread.c
 
diff --git a/examples/ip_pipeline/cli.c b/examples/ip_pipeline/cli.c
new file mode 100644 (file)
index 0000000..34ada83
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2018 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_common.h>
+
+#include "cli.h"
+
+static int
+is_comment(char *in)
+{
+       if ((strlen(in) && index("!#%;", in[0])) ||
+               (strncmp(in, "//", 2) == 0) ||
+               (strncmp(in, "--", 2) == 0))
+               return 1;
+
+       return 0;
+}
+
+void
+cli_process(char *in, char *out __rte_unused, size_t out_size __rte_unused)
+{
+       if (is_comment(in))
+               return;
+
+}
+
+int
+cli_script_process(const char *file_name,
+       size_t msg_in_len_max,
+       size_t msg_out_len_max)
+{
+       char *msg_in = NULL, *msg_out = NULL;
+       FILE *f = NULL;
+
+       /* Check input arguments */
+       if ((file_name == NULL) ||
+               (strlen(file_name) == 0) ||
+               (msg_in_len_max == 0) ||
+               (msg_out_len_max == 0))
+               return -EINVAL;
+
+       msg_in = malloc(msg_in_len_max + 1);
+       msg_out = malloc(msg_out_len_max + 1);
+       if ((msg_in == NULL) ||
+               (msg_out == NULL)) {
+               free(msg_out);
+               free(msg_in);
+               return -ENOMEM;
+       }
+
+       /* Open input file */
+       f = fopen(file_name, "r");
+       if (f == NULL) {
+               free(msg_out);
+               free(msg_in);
+               return -EIO;
+       }
+
+       /* Read file */
+       for ( ; ; ) {
+               if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
+                       break;
+
+               printf("%s", msg_in);
+               msg_out[0] = 0;
+
+               cli_process(msg_in,
+                       msg_out,
+                       msg_out_len_max);
+
+               if (strlen(msg_out))
+                       printf("%s", msg_out);
+       }
+
+       /* Close file */
+       fclose(f);
+       free(msg_out);
+       free(msg_in);
+       return 0;
+}
diff --git a/examples/ip_pipeline/cli.h b/examples/ip_pipeline/cli.h
new file mode 100644 (file)
index 0000000..992e4c3
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2018 Intel Corporation
+ */
+
+#ifndef __INCLUDE_CLI_H__
+#define __INCLUDE_CLI_H__
+
+#include <stddef.h>
+
+void
+cli_process(char *in, char *out, size_t out_size);
+
+int
+cli_script_process(const char *file_name,
+       size_t msg_in_len_max,
+       size_t msg_out_len_max);
+
+#endif
diff --git a/examples/ip_pipeline/conn.c b/examples/ip_pipeline/conn.c
new file mode 100644 (file)
index 0000000..9338942
--- /dev/null
@@ -0,0 +1,326 @@
+/* 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>
+
+#define __USE_GNU
+#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 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;
+};
+
+struct conn *
+conn_init(struct conn_params *p)
+{
+       struct sockaddr_in server_address;
+       struct 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 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)) {
+               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) {
+               conn_free(conn);
+               return NULL;
+       }
+
+       status = bind(fd_server,
+               (struct sockaddr *) &server_address,
+               sizeof(server_address));
+       if (status == -1) {
+               conn_free(conn);
+               return NULL;
+       }
+
+       status = listen(fd_server, 16);
+       if (status == -1) {
+               conn_free(conn);
+               return NULL;
+       }
+
+       /* Client group */
+       fd_client_group = epoll_create(1);
+       if (fd_client_group == -1) {
+               conn_free(conn);
+               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;
+
+       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_poll_for_conn(struct 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 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);
+
+                       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 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
+conn_poll_for_msg(struct 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;
+}
diff --git a/examples/ip_pipeline/conn.h b/examples/ip_pipeline/conn.h
new file mode 100644 (file)
index 0000000..46f9f95
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2018 Intel Corporation
+ */
+
+#ifndef __INCLUDE_CONN_H__
+#define __INCLUDE_CONN_H__
+
+#include <stdint.h>
+
+struct conn;
+
+#ifndef CONN_WELCOME_LEN_MAX
+#define CONN_WELCOME_LEN_MAX                               1024
+#endif
+
+#ifndef CONN_PROMPT_LEN_MAX
+#define CONN_PROMPT_LEN_MAX                                16
+#endif
+
+typedef void (*conn_msg_handle_t)(char *msg_in,
+       char *msg_out,
+       size_t msg_out_len_max);
+
+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;
+};
+
+struct conn *
+conn_init(struct conn_params *p);
+
+void
+conn_free(struct conn *conn);
+
+int
+conn_poll_for_conn(struct conn *conn);
+
+int
+conn_poll_for_msg(struct conn *conn);
+
+#endif
index 1696e36..60936f4 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2010-2015 Intel Corporation
+ * Copyright(c) 2010-2018 Intel Corporation
  */
 
 #include <stdio.h>
 
 #include <rte_eal.h>
 
+#include "cli.h"
+#include "conn.h"
+
+static const char usage[] =
+       "%s EAL_ARGS -- [-h HOST] [-p PORT] [-s SCRIPT]\n";
+
+static const char welcome[] =
+       "\n"
+       "Welcome to IP Pipeline!\n"
+       "\n";
+
+static const char prompt[] = "pipeline> ";
+
+static struct app_params {
+       struct conn_params conn;
+       char *script_name;
+} app = {
+       .conn = {
+               .welcome = welcome,
+               .prompt = prompt,
+               .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,
+       },
+       .script_name = NULL,
+};
+
+static int
+parse_args(int argc, char **argv)
+{
+       char *app_name = argv[0];
+       struct option lgopts[] = {
+               { NULL,  0, 0, 0 }
+       };
+       int opt, option_index;
+       int h_present, p_present, s_present, n_args, i;
+
+       /* Skip EAL input args */
+       n_args = argc;
+       for (i = 0; i < n_args; i++)
+               if (strcmp(argv[i], "--") == 0) {
+                       argc -= i;
+                       argv += i;
+                       break;
+               }
+
+       if (i == n_args)
+               return 0;
+
+       /* Parse args */
+       h_present = 0;
+       p_present = 0;
+       s_present = 0;
+
+       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) atoi(optarg);
+                       break;
+
+               case 's':
+                       if (s_present) {
+                               printf("Error: Multiple -s arguments\n");
+                               return -1;
+                       }
+                       s_present = 1;
+
+                       if (!strlen(optarg)) {
+                               printf("Error: Argument for -s not provided\n");
+                               return -1;
+                       }
+
+                       app.script_name = strdup(optarg);
+                       if (app.script_name == NULL) {
+                               printf("Error: Not enough memory\n");
+                               return -1;
+                       }
+                       break;
+
+               default:
+                       printf(usage, app_name);
+                       return -1;
+               }
+
+       optind = 1; /* reset getopt lib */
+
+       return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+       struct conn *conn;
        int status;
 
+       /* Parse application arguments */
+       status = parse_args(argc, argv);
+       if (status < 0)
+               return status;
+
        /* EAL */
        status = rte_eal_init(argc, argv);
        if (status < 0) {
@@ -22,4 +151,24 @@ main(int argc, char **argv)
                return status;
        };
 
+       /* Connectivity */
+       conn = conn_init(&app.conn);
+       if (conn == NULL) {
+               printf("Error: Connectivity initialization failed (%d)\n",
+                       status);
+               return status;
+       };
+
+       /* Script */
+       if (app.script_name)
+               cli_script_process(app.script_name,
+                       app.conn.msg_in_len_max,
+                       app.conn.msg_out_len_max);
+
+       /* Dispatch loop */
+       for ( ; ; ) {
+               conn_poll_for_conn(conn);
+
+               conn_poll_for_msg(conn);
+       }
 }
index 063865c..a89cb30 100644 (file)
@@ -8,6 +8,8 @@
 
 deps += ['pipeline', 'bus_pci']
 sources = files(
+       'cli.c',
+       'conn.c',
        'main.c',
        'parser.c',
 )