diff options
author | Jere Leppänen <jere.leppanen@nokia.com> | 2021-04-24 10:20:19 +0300 |
---|---|---|
committer | Petri Savolainen <petri.savolainen@nokia.com> | 2021-06-01 14:56:01 +0300 |
commit | 7f30069dd48d38f7069118cc926ae33526cb201e (patch) | |
tree | 612925c6009202c1cd7005f8f1cec0d4385519fd /helper/cli.c | |
parent | 4df98a11a2059ad818d0e56d162dffa852d12946 (diff) |
helper: cli: add user command functions
Allow user to register CLI commands with a name, function, and an
optional help text.
Signed-off-by: Jere Leppänen <jere.leppanen@nokia.com>
Reviewed-by: Petri Savolainen <petri.savolainen@nokia.com>
Diffstat (limited to 'helper/cli.c')
-rw-r--r-- | helper/cli.c | 322 |
1 files changed, 254 insertions, 68 deletions
diff --git a/helper/cli.c b/helper/cli.c index 72bca48bb..ebc5b01e1 100644 --- a/helper/cli.c +++ b/helper/cli.c @@ -16,6 +16,7 @@ #include <errno.h> #include <poll.h> #include <stdio.h> +#include <strings.h> /* Socketpair socket roles. */ enum { @@ -23,6 +24,15 @@ enum { SP_WRITE = 1, }; +#define MAX_NAME_LEN 20 +#define MAX_HELP_LEN 100 + +typedef struct { + odph_cli_user_cmd_func_t fn; + char name[MAX_NAME_LEN]; + char help[MAX_HELP_LEN]; +} user_cmd_t; + typedef struct { volatile int cli_fd; /* Server thread will exit if this is false. */ @@ -30,9 +40,15 @@ typedef struct { /* Socketpair descriptors. */ int sp[2]; int listen_fd; - /* This lock guards cli_fd and run, which must be accessed atomically. */ + /* Guards cli_fd and run, which must be accessed atomically. */ odp_spinlock_t lock; + odp_spinlock_t api_lock; odph_thread_t thr_server; + odp_instance_t instance; + struct sockaddr_in addr; + uint32_t max_user_commands; + uint32_t num_user_commands; + user_cmd_t user_cmd[0]; } cli_shm_t; static const char *shm_name = "_odp_cli"; @@ -40,6 +56,7 @@ static const char *shm_name = "_odp_cli"; static const odph_cli_param_t param_default = { .address = "127.0.0.1", .port = 55555, + .max_user_commands = 50, }; void odph_cli_param_init(odph_cli_param_t *param) @@ -47,6 +64,116 @@ void odph_cli_param_init(odph_cli_param_t *param) *param = param_default; } +static cli_shm_t *shm_lookup(void) +{ + cli_shm_t *shm = NULL; + odp_shm_t shm_hdl = odp_shm_lookup(shm_name); + + if (shm_hdl != ODP_SHM_INVALID) + shm = (cli_shm_t *)odp_shm_addr(shm_hdl); + + return shm; +} + +int odph_cli_init(odp_instance_t instance, const odph_cli_param_t *param) +{ + if (odp_shm_lookup(shm_name) != ODP_SHM_INVALID) { + ODPH_ERR("Error: shm %s already exists\n", shm_name); + return -1; + } + + cli_shm_t *shm = NULL; + int shm_size = sizeof(cli_shm_t) + + param->max_user_commands * sizeof(user_cmd_t); + odp_shm_t shm_hdl = + odp_shm_reserve(shm_name, shm_size, 64, ODP_SHM_SW_ONLY); + + if (shm_hdl != ODP_SHM_INVALID) + shm = (cli_shm_t *)odp_shm_addr(shm_hdl); + + if (!shm) { + ODPH_ERR("Error: failed to reserve shm %s\n", shm_name); + return -1; + } + + memset(shm, 0, shm_size); + odp_spinlock_init(&shm->lock); + odp_spinlock_init(&shm->api_lock); + shm->sp[SP_READ] = -1; + shm->sp[SP_WRITE] = -1; + shm->listen_fd = -1; + shm->cli_fd = -1; + shm->instance = instance; + + shm->addr.sin_family = AF_INET; + shm->addr.sin_port = htons(param->port); + + switch (inet_pton(AF_INET, param->address, &shm->addr.sin_addr)) { + case -1: + ODPH_ERR("Error: inet_pton(): %s\n", strerror(errno)); + return -1; + case 0: + ODPH_ERR("Error: inet_pton(): illegal address format\n"); + return -1; + default: + break; + } + + shm->max_user_commands = param->max_user_commands; + + return 0; +} + +int odph_cli_register_command(const char *name, odph_cli_user_cmd_func_t func, + const char *help) +{ + cli_shm_t *shm = shm_lookup(); + + if (!shm) { + ODPH_ERR("Error: shm %s not found\n", shm_name); + return -1; + } + + odp_spinlock_lock(&shm->api_lock); + + odp_spinlock_lock(&shm->lock); + if (shm->run) { + odp_spinlock_unlock(&shm->lock); + ODPH_ERR("Error: cannot register commands while cli server is running\n"); + goto error; + } + odp_spinlock_unlock(&shm->lock); + + if (shm->num_user_commands >= shm->max_user_commands) { + ODPH_ERR("Error: maximum number of user commands already registered\n"); + goto error; + } + + user_cmd_t *cmd = &shm->user_cmd[shm->num_user_commands]; + + cmd->fn = func; + + if (strlen(name) >= MAX_NAME_LEN - 1) { + ODPH_ERR("Error: command name too long\n"); + goto error; + } + strcpy(cmd->name, name); + + if (strlen(help) >= MAX_HELP_LEN - 1) { + ODPH_ERR("Error: command help too long\n"); + goto error; + } + strcpy(cmd->help, help); + + shm->num_user_commands++; + odp_spinlock_unlock(&shm->api_lock); + return 0; + +error: + odp_spinlock_unlock(&shm->api_lock); + return -1; +} + /* * Check that number of given arguments matches required number of * arguments. Print error messages if this is not the case. Return 0 @@ -215,7 +342,27 @@ static int cmd_call_odp_shm_print(struct cli_def *cli, return CLI_OK; } -static struct cli_def *create_cli(void) +static int cmd_user_cmd(struct cli_def *cli ODP_UNUSED, const char *command, + char *argv[], int argc) +{ + cli_shm_t *shm = shm_lookup(); + + if (!shm) { + ODPH_ERR("Error: shm %s not found\n", shm_name); + return CLI_ERROR; + } + + for (uint32_t i = 0; i < shm->num_user_commands; i++) { + if (!strcasecmp(command, shm->user_cmd[i].name)) { + shm->user_cmd[i].fn(argc, argv); + break; + } + } + + return CLI_OK; +} + +static struct cli_def *create_cli(cli_shm_t *shm) { struct cli_command *c; struct cli_def *cli; @@ -258,6 +405,12 @@ static struct cli_def *create_cli(void) cmd_call_odp_sys_info_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); + for (uint32_t i = 0; i < shm->num_user_commands; i++) { + cli_register_command(cli, NULL, shm->user_cmd[i].name, + cmd_user_cmd, PRIVILEGE_UNPRIVILEGED, + MODE_EXEC, shm->user_cmd[i].help); + } + return cli; } @@ -265,8 +418,8 @@ static struct cli_def *create_cli(void) static struct cli_def *cli; static char *cli_log_fn_buf; -ODP_PRINTF_FORMAT(2, 3) -static int cli_log_fn(odp_log_level_t level, const char *fmt, ...) +ODP_PRINTF_FORMAT(2, 0) +static int cli_log_va(odp_log_level_t level, const char *fmt, va_list in_args) { (void)level; @@ -285,17 +438,17 @@ static int cli_log_fn(odp_log_level_t level, const char *fmt, ...) * If the string does not end in a newline, we keep the part of the * string after the last newline and use it the next time we're called. */ - va_start(args, fmt); + va_copy(args, in_args); len = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); str = malloc(len); if (!str) { - ODPH_ERR("malloc failed"); - return 0; + ODPH_ERR("malloc failed\n"); + return -1; } - va_start(args, fmt); + va_copy(args, in_args); vsnprintf(str, len, fmt, args); va_end(args); p = str; @@ -319,7 +472,7 @@ static int cli_log_fn(odp_log_level_t level, const char *fmt, ...) malloc(strlen(cli_log_fn_buf) + strlen(p) + 1); if (!buffer_new) { - ODPH_ERR("malloc failed"); + ODPH_ERR("malloc failed\n"); goto out; } @@ -331,7 +484,7 @@ static int cli_log_fn(odp_log_level_t level, const char *fmt, ...) cli_log_fn_buf = malloc(strlen(p) + 1); if (!cli_log_fn_buf) { - ODPH_ERR("malloc failed"); + ODPH_ERR("malloc failed\n"); goto out; } @@ -342,23 +495,47 @@ static int cli_log_fn(odp_log_level_t level, const char *fmt, ...) out: free(str); - return 0; + return len; } -static int cli_server(void *arg ODP_UNUSED) +ODP_PRINTF_FORMAT(2, 3) +static int cli_log(odp_log_level_t level, const char *fmt, ...) { - cli_shm_t *shm = NULL; - odp_shm_t shm_hdl = odp_shm_lookup(shm_name); + (void)level; - if (shm_hdl != ODP_SHM_INVALID) - shm = (cli_shm_t *)odp_shm_addr(shm_hdl); + int r; + va_list args; + + va_start(args, fmt); + r = cli_log_va(level, fmt, args); + va_end(args); + + return r; +} + +ODP_PRINTF_FORMAT(1, 2) +int odph_cli_log(const char *fmt, ...) +{ + int r; + va_list args; + + va_start(args, fmt); + r = cli_log_va(ODP_LOG_PRINT, fmt, args); + va_end(args); + + return r; +} + +static int cli_server(void *arg ODP_UNUSED) +{ + cli_shm_t *shm = shm_lookup(); if (!shm) { - ODPH_ERR("Error: can't start cli server (shm %s not found)\n", shm_name); + ODPH_ERR("Error: shm %s not found\n", shm_name); return -1; } - cli = create_cli(); + cli = create_cli(shm); while (1) { struct pollfd pfd[2] = { @@ -396,11 +573,10 @@ static int cli_server(void *arg ODP_UNUSED) } /* - * The only way to break out of cli_loop() is to close the - * socket, after which cli_loop() gets an error on the next - * select() and then calls close() before returning. This is a - * problem because the fd may be reused before the select() or - * the final close(). + * The only way to stop cli_loop() is to close the socket, after + * which cli_loop() gets an error on the next select() and then + * calls close() before returning. This is a problem because the + * fd may be reused before the select() or the final close(). * * To avoid this problem, switch to a higher fd number * (select() maximum). We will still run into problems if the @@ -430,7 +606,8 @@ static int cli_server(void *arg ODP_UNUSED) } shm->cli_fd = fd; odp_spinlock_unlock(&shm->lock); - odp_log_thread_fn_set(cli_log_fn); + + odp_log_thread_fn_set(cli_log); /* * cli_loop() returns only when client is disconnected. One * possible reason for disconnect is odph_cli_stop(). @@ -458,32 +635,30 @@ static int cli_server(void *arg ODP_UNUSED) return 0; } -int odph_cli_start(const odp_instance_t instance, - const odph_cli_param_t *param_in) +int odph_cli_start(void) { - if (odp_shm_lookup(shm_name) != ODP_SHM_INVALID) { - ODPH_ERR("Error: cli server already running (shm %s exists)\n", shm_name); + cli_shm_t *shm = shm_lookup(); + + if (!shm) { + ODPH_ERR("Error: shm %s not found\n", shm_name); return -1; } - cli_shm_t *shm = NULL; - odp_shm_t shm_hdl = odp_shm_reserve(shm_name, sizeof(cli_shm_t), 64, - ODP_SHM_SW_ONLY); - - if (shm_hdl != ODP_SHM_INVALID) - shm = (cli_shm_t *)odp_shm_addr(shm_hdl); - - if (!shm) { - ODPH_ERR("Error: failed to reserve shm %s\n", shm_name); + odp_spinlock_lock(&shm->api_lock); + odp_spinlock_lock(&shm->lock); + if (shm->run) { + odp_spinlock_unlock(&shm->lock); + odp_spinlock_unlock(&shm->api_lock); + ODPH_ERR("Error: cli server has already been started\n"); return -1; } + shm->run = 1; + shm->cli_fd = -1; + odp_spinlock_unlock(&shm->lock); - memset(shm, 0, sizeof(cli_shm_t)); - odp_spinlock_init(&shm->lock); - shm->sp[SP_READ] = shm->sp[SP_WRITE] = -1; + shm->sp[SP_READ] = -1; + shm->sp[SP_WRITE] = -1; shm->listen_fd = -1; - shm->cli_fd = -1; - shm->run = 1; if (socketpair(PF_LOCAL, SOCK_STREAM, 0, shm->sp)) { ODPH_ERR("Error: socketpair(): %s\n", strerror(errno)); @@ -505,21 +680,8 @@ int odph_cli_start(const odp_instance_t instance, goto error; } - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = htons(param_in->port); - - switch (inet_pton(AF_INET, param_in->address, &addr.sin_addr)) { - case -1: - ODPH_ERR("Error: inet_pton(): %s\n", strerror(errno)); - goto error; - case 0: - ODPH_ERR("Error: inet_pton(): illegal address format\n"); - goto error; - } - - if (bind(shm->listen_fd, (struct sockaddr *)&addr, sizeof(addr))) { + if (bind(shm->listen_fd, (struct sockaddr *)&shm->addr, + sizeof(shm->addr))) { ODPH_ERR("Error: bind(): %s\n", strerror(errno)); goto error; } @@ -541,7 +703,7 @@ int odph_cli_start(const odp_instance_t instance, } memset(&thr_common, 0, sizeof(thr_common)); - thr_common.instance = instance; + thr_common.instance = shm->instance; thr_common.cpumask = &cpumask; memset(&thr_param, 0, sizeof(thr_param)); @@ -555,9 +717,11 @@ int odph_cli_start(const odp_instance_t instance, goto error; } + odp_spinlock_unlock(&shm->api_lock); return 0; error: + shm->run = 0; if (shm->sp[SP_READ] >= 0) close(shm->sp[SP_READ]); if (shm->sp[SP_WRITE] >= 0) @@ -566,25 +730,27 @@ error: close(shm->listen_fd); if (shm->cli_fd >= 0) close(shm->cli_fd); - if (odp_shm_free(shm_hdl)) - ODPH_ERR("Error: odp_shm_free() failed\n"); + odp_spinlock_unlock(&shm->api_lock); return -1; } int odph_cli_stop(void) { - cli_shm_t *shm = NULL; - odp_shm_t shm_hdl = odp_shm_lookup(shm_name); - - if (shm_hdl != ODP_SHM_INVALID) - shm = (cli_shm_t *)odp_shm_addr(shm_hdl); + cli_shm_t *shm = shm_lookup(); if (!shm) { - ODPH_ERR("Error: cli server not running (shm %s not found)\n", shm_name); + ODPH_ERR("Error: shm %s not found\n", shm_name); return -1; } + odp_spinlock_lock(&shm->api_lock); odp_spinlock_lock(&shm->lock); + if (!shm->run) { + odp_spinlock_unlock(&shm->lock); + odp_spinlock_unlock(&shm->api_lock); + ODPH_ERR("Error: cli server has not been started\n"); + return -1; + } shm->run = 0; /* * Close the current cli connection. This stops cli_loop(). If cli @@ -607,17 +773,37 @@ int odph_cli_stop(void) if (sent != sizeof(stop)) { ODPH_ERR("Error: send() = %d: %s\n", sent, strerror(errno)); - return -1; + goto error; } if (odph_thread_join(&shm->thr_server, 1) != 1) { ODPH_ERR("Error: odph_thread_join() failed\n"); - return -1; + goto error; } close(shm->sp[SP_READ]); close(shm->sp[SP_WRITE]); close(shm->listen_fd); + odp_spinlock_unlock(&shm->api_lock); + return 0; + +error: + odp_spinlock_unlock(&shm->api_lock); + return -1; +} + +int odph_cli_term(void) +{ + cli_shm_t *shm = NULL; + odp_shm_t shm_hdl = odp_shm_lookup(shm_name); + + if (shm_hdl != ODP_SHM_INVALID) + shm = (cli_shm_t *)odp_shm_addr(shm_hdl); + + if (!shm) { + ODPH_ERR("Error: shm %s not found\n", shm_name); + return -1; + } if (odp_shm_free(shm_hdl)) { ODPH_ERR("Error: odp_shm_free() failed\n"); |