Skip to content

Commit

Permalink
nrf_rpc: Add functionality for invoking remote shell cmd via rpc
Browse files Browse the repository at this point in the history
The functionality allows the nrf_rpc client to invoke nrf_rpc server
shell command and obtain the command's output.

This commit adds the new nrf_rpc client shell subcommand: 'remote_shell'

Example of use:

rpc remote_shell device list

devices:
- clock@10e000 (READY)
- gpio@10a000 (READY)
- gpio@d8200 (READY)
- gpio@50400 (READY)
- psa-rng (READY)
- uart@c7000 (READY)
- uart@c6000 (READY)
- spi@4a000 (READY)
- rram-controller@5004b000 (READY)
- mx25r6435f@0 (READY)
- ieee802154 (READY)
- temp@d7000 (READY)

Signed-off-by: Lukasz Maciejonczyk <[email protected]>
  • Loading branch information
lmaciejonczyk committed Jan 24, 2025
1 parent 886f691 commit 913980c
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 13 deletions.
12 changes: 11 additions & 1 deletion include/nrf_rpc/nrf_rpc_dev_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@
* @retval version of the remote on success.
* @retval NULL on failure.
*/
const char *nrf_rpc_get_ncs_commit_sha(void);
char *nrf_rpc_get_ncs_commit_sha(void);

/** @brief Invoke the remote server shell command.
*
* @param[in] argc Number of arguments.
* @param[in] argv Array of arguments.
*
* @retval Command output on success.
* @retval NULL on failure.
*/
char *nrf_rpc_invoke_remote_shell_cmd(size_t argc, char *argv[]);

/**
* @}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,41 @@

static int remote_version_cmd(const struct shell *sh, size_t argc, char *argv[])
{
const char *version = nrf_rpc_get_ncs_commit_sha();
char *version = nrf_rpc_get_ncs_commit_sha();

if (!version) {
shell_error(sh, "Failed to get remote version");
return -ENOEXEC;
}

shell_print(sh, "Remote version: %s", version);

k_free(version);

return 0;
}

static int remote_shell_cmd(const struct shell *sh, size_t argc, char *argv[])
{
char *output = nrf_rpc_invoke_remote_shell_cmd(argc - 1, argv + 1);

if (!output) {
shell_error(sh, "Failed to invoke remote shell command");
return -ENOEXEC;
}

shell_print(sh, "%s", output);

k_free(output);

return 0;
}

SHELL_STATIC_SUBCMD_SET_CREATE(util_cmds,
SHELL_CMD_ARG(remote_version, NULL, "Get server version",
remote_version_cmd, 1, 0),
SHELL_CMD_ARG(remote_shell, NULL, "Call remote shell command",
remote_shell_cmd, 2, 255),
SHELL_SUBCMD_SET_END);

SHELL_CMD_ARG_REGISTER(rpc, &util_cmds, "nRF RPC utility commands", NULL, 1, 0);
3 changes: 3 additions & 0 deletions samples/nrf_rpc/protocols_serialization/server/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ config LOG_BACKEND_RTT
config LOG_BACKEND_UART
default n

config SHELL_BACKEND_SERIAL
default n if NRF_RPC_REMOTE_SHELL

if SOC_NRF54L15

config BT_CTLR_ECDH
Expand Down
12 changes: 12 additions & 0 deletions subsys/nrf_rpc/dev_info/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,16 @@ config DEV_INFO_RPC_INITIALIZE_NRF_RPC
Initialize nRF RPC library during the system startup. Disabling this
option allow user to initialize it in a different way.

config NRF_RPC_REMOTE_SHELL
bool "Remote shell"
depends on NRF_RPC_DEV_INFO_SERVER
select SHELL
select SHELL_BACKEND_DUMMY
default y
help
Enable access to remote shell via NRF_RPC.

config SHELL_BACKEND_DUMMY_BUF_SIZE
default 1024 if NRF_RPC_REMOTE_SHELL

endif # NRF_RPC_DEV_INFO
95 changes: 85 additions & 10 deletions subsys/nrf_rpc/dev_info/client/dev_info_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,101 @@

NRF_RPC_GROUP_DECLARE(dev_info_group);

static char version[16];
static char *allocate_buffer_and_decode_str(struct nrf_rpc_cbor_ctx *ctx,
enum def_info_rpc_cmd_server cmd)
{
const void *ptr;
size_t len;
char *output = NULL;

ptr = nrf_rpc_decode_str_ptr_and_len(ctx, &len);

if (ptr) {
output = k_malloc(len + 1);
if (output) {
memcpy(output, ptr, len);
output[len] = '\0';
}
}

if (!nrf_rpc_decoding_done_and_check(&dev_info_group, ctx)) {
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, &dev_info_group, cmd,
NRF_RPC_PACKET_TYPE_RSP);
k_free(output);
return NULL;
}

return output;
}

const char *nrf_rpc_get_ncs_commit_sha(void)
char *nrf_rpc_get_ncs_commit_sha(void)
{
struct nrf_rpc_cbor_ctx ctx;
size_t size = ARRAY_SIZE(version);

memset(version, 0, ARRAY_SIZE(version));

NRF_RPC_CBOR_ALLOC(&dev_info_group, ctx, 0);

nrf_rpc_cbor_cmd_rsp_no_err(&dev_info_group, DEV_INFO_RPC_GET_VERSION, &ctx);

nrf_rpc_decode_str(&ctx, version, size);
return allocate_buffer_and_decode_str(&ctx, DEV_INFO_RPC_GET_VERSION);
}

if (!nrf_rpc_decoding_done_and_check(&dev_info_group, &ctx)) {
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, &dev_info_group,
DEV_INFO_RPC_GET_VERSION, NRF_RPC_PACKET_TYPE_RSP);
static size_t get_cmd_len(size_t argc, char *argv[])
{
size_t len = 0;

for (size_t i = 0; i < argc; i++) {
len += strlen(argv[i]) + 1;
}

return len;
}

static size_t create_cmd_line(char *buffer, size_t cmd_buffer_size, size_t argc, char *argv[])
{
size_t len = 0;

for (size_t i = 0; i < argc; i++) {
size_t arg_len = strlen(argv[i]);

if (len + arg_len + 1 > cmd_buffer_size) {
return 0;
}
memcpy(buffer + len, argv[i], arg_len);
len += arg_len;
buffer[len++] = ' ';
}

buffer[len - 1] = '\0';

return len;
}

char *nrf_rpc_invoke_remote_shell_cmd(size_t argc, char *argv[])
{
struct nrf_rpc_cbor_ctx ctx;
size_t cmd_buffer_size = get_cmd_len(argc, argv);
char *cmd_buffer = k_malloc(cmd_buffer_size);

if (!cmd_buffer) {
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &dev_info_group,
DEV_INFO_RPC_INVOKE_SHELL_CMD, NRF_RPC_PACKET_TYPE_CMD);
return NULL;
}

size_t cmd_len = create_cmd_line(cmd_buffer, cmd_buffer_size, argc, argv);

if (!cmd_len) {
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &dev_info_group,
DEV_INFO_RPC_INVOKE_SHELL_CMD, NRF_RPC_PACKET_TYPE_CMD);
k_free(cmd_buffer);
return NULL;
}

return version;
NRF_RPC_CBOR_ALLOC(&dev_info_group, ctx, 3 + cmd_len);

nrf_rpc_encode_str(&ctx, cmd_buffer, cmd_len);

nrf_rpc_cbor_cmd_rsp_no_err(&dev_info_group, DEV_INFO_RPC_INVOKE_SHELL_CMD, &ctx);

return allocate_buffer_and_decode_str(&ctx, DEV_INFO_RPC_INVOKE_SHELL_CMD);
}
3 changes: 2 additions & 1 deletion subsys/nrf_rpc/dev_info/common/dev_info_rpc_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

/** @brief Command IDs accepted by the device information over RPC server.
*/
enum ot_rpc_cmd_server {
enum def_info_rpc_cmd_server {
DEV_INFO_RPC_GET_VERSION = 0,
DEV_INFO_RPC_INVOKE_SHELL_CMD = 1,
};

#endif /* DEV_INFO_RPC_IDS_H_ */
69 changes: 69 additions & 0 deletions subsys/nrf_rpc/dev_info/server/dev_info_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include <ncs_commit.h>
#include <dev_info_rpc_ids.h>

#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
#include <zephyr/shell/shell_dummy.h>
#endif

NRF_RPC_GROUP_DECLARE(dev_info_group);

static void get_server_version(const struct nrf_rpc_group *group, struct nrf_rpc_cbor_ctx *ctx,
Expand All @@ -34,3 +38,68 @@ static void get_server_version(const struct nrf_rpc_group *group, struct nrf_rpc

NRF_RPC_CBOR_CMD_DECODER(dev_info_group, get_server_version, DEV_INFO_RPC_GET_VERSION,
get_server_version, NULL);

#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
static int shell_exec(const char *line)
{
const struct shell *sh = shell_backend_dummy_get_ptr();

shell_backend_dummy_clear_output(sh);
return shell_execute_cmd(sh, line);
}

const char *shell_get_output(size_t *len)
{
return shell_backend_dummy_get_output(shell_backend_dummy_get_ptr(), len);
}
#endif /* CONFIG_NRF_RPC_REMOTE_SHELL */

static void remote_shell_cmd(const struct nrf_rpc_group *group, struct nrf_rpc_cbor_ctx *ctx,
void *handler_data)
{
struct nrf_rpc_cbor_ctx rsp_ctx;
char *cmd_buffer = NULL;
const void *ptr;
size_t len = 0;
const char *output = NULL;

ptr = nrf_rpc_decode_str_ptr_and_len(ctx, &len);

if (ptr) {
cmd_buffer = k_malloc(len + 1);
if (cmd_buffer) {
memcpy(cmd_buffer, ptr, len);
cmd_buffer[len] = '\0';
}
}

if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, group, DEV_INFO_RPC_INVOKE_SHELL_CMD,
NRF_RPC_PACKET_TYPE_CMD);
goto exit;
}

if (!ptr || !cmd_buffer) {
goto exit;
}

#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
shell_exec(cmd_buffer);
output = shell_get_output(&len);
#else
output = "Remote shell is disabled on the server. "
"Enable CONFIG_NRF_RPC_REMOTE_SHELL to use it.";
len = strlen(output);
#endif /* CONFIG_NRF_RPC_REMOTE_SHELL */

NRF_RPC_CBOR_ALLOC(group, rsp_ctx, 3 + len);

nrf_rpc_encode_str(&rsp_ctx, output, len);
nrf_rpc_cbor_rsp_no_err(group, &rsp_ctx);

exit:
k_free(cmd_buffer);
}

NRF_RPC_CBOR_CMD_DECODER(dev_info_group, remote_shell_cmd, DEV_INFO_RPC_INVOKE_SHELL_CMD,
remote_shell_cmd, NULL);

0 comments on commit 913980c

Please sign in to comment.