Skip to content

Commit 3a64e07

Browse files
lmaciejonczykrlubos
authored andcommitted
nrf_rpc: Add functionality for invoking remote shell cmd via rpc
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 <lukasz.maciejonczyk@nordicsemi.no>
1 parent 0809077 commit 3a64e07

File tree

10 files changed

+256
-13
lines changed

10 files changed

+256
-13
lines changed

include/nrf_rpc/nrf_rpc_dev_info.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@
1919
* @retval version of the remote on success.
2020
* @retval NULL on failure.
2121
*/
22-
const char *nrf_rpc_get_ncs_commit_sha(void);
22+
char *nrf_rpc_get_ncs_commit_sha(void);
23+
24+
/** @brief Invoke the remote server shell command.
25+
*
26+
* @param[in] argc Number of arguments.
27+
* @param[in] argv Array of arguments.
28+
*
29+
* @retval Command output on success.
30+
* @retval NULL on failure.
31+
*/
32+
char *nrf_rpc_invoke_remote_shell_cmd(size_t argc, char *argv[]);
2333

2434
/**
2535
* @}

samples/nrf_rpc/protocols_serialization/client/src/dev_info_shell.c

+26-1
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,41 @@
99

1010
static int remote_version_cmd(const struct shell *sh, size_t argc, char *argv[])
1111
{
12-
const char *version = nrf_rpc_get_ncs_commit_sha();
12+
char *version = nrf_rpc_get_ncs_commit_sha();
13+
14+
if (!version) {
15+
shell_error(sh, "Failed to get remote version");
16+
return -ENOEXEC;
17+
}
1318

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

21+
k_free(version);
22+
23+
return 0;
24+
}
25+
26+
static int remote_shell_cmd(const struct shell *sh, size_t argc, char *argv[])
27+
{
28+
char *output = nrf_rpc_invoke_remote_shell_cmd(argc - 1, argv + 1);
29+
30+
if (!output) {
31+
shell_error(sh, "Failed to invoke remote shell command");
32+
return -ENOEXEC;
33+
}
34+
35+
shell_print(sh, "%s", output);
36+
37+
k_free(output);
38+
1639
return 0;
1740
}
1841

1942
SHELL_STATIC_SUBCMD_SET_CREATE(util_cmds,
2043
SHELL_CMD_ARG(remote_version, NULL, "Get server version",
2144
remote_version_cmd, 1, 0),
45+
SHELL_CMD_ARG(remote_shell, NULL, "Call remote shell command",
46+
remote_shell_cmd, 2, 255),
2247
SHELL_SUBCMD_SET_END);
2348

2449
SHELL_CMD_ARG_REGISTER(rpc, &util_cmds, "nRF RPC utility commands", NULL, 1, 0);

samples/nrf_rpc/protocols_serialization/server/Kconfig

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ config ZMS
2727
config NVS
2828
default y if !(SOC_FLASH_NRF_RRAM || SOC_FLASH_NRF_MRAM)
2929

30+
config SHELL_BACKEND_SERIAL
31+
default n if NRF_RPC_REMOTE_SHELL
32+
3033
if LOG
3134

3235
choice LOG_MODE

subsys/nrf_rpc/dev_info/Kconfig

+12
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,16 @@ config DEV_INFO_RPC_INITIALIZE_NRF_RPC
3737
Initialize nRF RPC library during the system startup. Disabling this
3838
option allow user to initialize it in a different way.
3939

40+
config NRF_RPC_REMOTE_SHELL
41+
bool "Remote shell"
42+
depends on NRF_RPC_DEV_INFO_SERVER
43+
select SHELL
44+
select SHELL_BACKEND_DUMMY
45+
default y
46+
help
47+
Enable access to remote shell via NRF_RPC.
48+
49+
config SHELL_BACKEND_DUMMY_BUF_SIZE
50+
default 1024 if NRF_RPC_REMOTE_SHELL
51+
4052
endif # NRF_RPC_DEV_INFO

subsys/nrf_rpc/dev_info/client/dev_info_client.c

+95-10
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,111 @@
1313

1414
NRF_RPC_GROUP_DECLARE(dev_info_group);
1515

16-
static char version[16];
16+
#define MAX_ARGV_SIZE 255
1717

18-
const char *nrf_rpc_get_ncs_commit_sha(void)
18+
static char *allocate_buffer_and_decode_str(struct nrf_rpc_cbor_ctx *ctx,
19+
enum def_info_rpc_cmd_server cmd)
1920
{
20-
struct nrf_rpc_cbor_ctx ctx;
21-
size_t size = ARRAY_SIZE(version);
21+
const void *ptr;
22+
size_t len;
23+
char *output = NULL;
24+
25+
ptr = nrf_rpc_decode_str_ptr_and_len(ctx, &len);
26+
27+
if (ptr) {
28+
output = k_malloc(len + 1);
29+
if (output) {
30+
memcpy(output, ptr, len);
31+
output[len] = '\0';
32+
}
33+
}
34+
35+
if (!nrf_rpc_decoding_done_and_check(&dev_info_group, ctx)) {
36+
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, &dev_info_group, cmd,
37+
NRF_RPC_PACKET_TYPE_RSP);
38+
k_free(output);
39+
return NULL;
40+
}
2241

23-
memset(version, 0, ARRAY_SIZE(version));
42+
return output;
43+
}
44+
45+
char *nrf_rpc_get_ncs_commit_sha(void)
46+
{
47+
struct nrf_rpc_cbor_ctx ctx;
2448

2549
NRF_RPC_CBOR_ALLOC(&dev_info_group, ctx, 0);
2650

2751
nrf_rpc_cbor_cmd_rsp_no_err(&dev_info_group, DEV_INFO_RPC_GET_VERSION, &ctx);
2852

29-
nrf_rpc_decode_str(&ctx, version, size);
53+
return allocate_buffer_and_decode_str(&ctx, DEV_INFO_RPC_GET_VERSION);
54+
}
55+
56+
static size_t get_cmd_len(size_t argc, char *argv[])
57+
{
58+
size_t len = 0;
59+
60+
for (size_t i = 0; i < argc; i++) {
61+
if (argv[i] == NULL) {
62+
return 0;
63+
}
64+
65+
len += strnlen(argv[i], MAX_ARGV_SIZE) + 1;
66+
}
67+
68+
return len;
69+
}
70+
71+
static size_t create_cmd_line(char *buffer, size_t cmd_buffer_size, size_t argc, char *argv[])
72+
{
73+
size_t len = 0;
74+
75+
for (size_t i = 0; i < argc; i++) {
76+
if (argv[i] == NULL) {
77+
return 0;
78+
}
79+
80+
size_t arg_len = strnlen(argv[i], MAX_ARGV_SIZE);
81+
82+
if (len + arg_len + 1 > cmd_buffer_size) {
83+
return 0;
84+
}
85+
memcpy(buffer + len, argv[i], arg_len);
86+
len += arg_len;
87+
buffer[len++] = ' ';
88+
}
89+
90+
buffer[len - 1] = '\0';
3091

31-
if (!nrf_rpc_decoding_done_and_check(&dev_info_group, &ctx)) {
32-
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, &dev_info_group,
33-
DEV_INFO_RPC_GET_VERSION, NRF_RPC_PACKET_TYPE_RSP);
92+
return len;
93+
}
94+
95+
char *nrf_rpc_invoke_remote_shell_cmd(size_t argc, char *argv[])
96+
{
97+
struct nrf_rpc_cbor_ctx ctx;
98+
size_t cmd_buffer_size = get_cmd_len(argc, argv);
99+
char *cmd_buffer = k_malloc(cmd_buffer_size);
100+
101+
if (!cmd_buffer) {
102+
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &dev_info_group,
103+
DEV_INFO_RPC_INVOKE_SHELL_CMD, NRF_RPC_PACKET_TYPE_CMD);
34104
return NULL;
35105
}
36106

37-
return version;
107+
size_t cmd_len = create_cmd_line(cmd_buffer, cmd_buffer_size, argc, argv);
108+
109+
if (!cmd_len) {
110+
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &dev_info_group,
111+
DEV_INFO_RPC_INVOKE_SHELL_CMD, NRF_RPC_PACKET_TYPE_CMD);
112+
k_free(cmd_buffer);
113+
return NULL;
114+
}
115+
116+
NRF_RPC_CBOR_ALLOC(&dev_info_group, ctx, 3 + cmd_len);
117+
118+
nrf_rpc_encode_str(&ctx, cmd_buffer, cmd_len);
119+
120+
nrf_rpc_cbor_cmd_rsp_no_err(&dev_info_group, DEV_INFO_RPC_INVOKE_SHELL_CMD, &ctx);
121+
122+
return allocate_buffer_and_decode_str(&ctx, DEV_INFO_RPC_INVOKE_SHELL_CMD);
38123
}

subsys/nrf_rpc/dev_info/common/dev_info_rpc_ids.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
/** @brief Command IDs accepted by the device information over RPC server.
1111
*/
12-
enum ot_rpc_cmd_server {
12+
enum def_info_rpc_cmd_server {
1313
DEV_INFO_RPC_GET_VERSION = 0,
14+
DEV_INFO_RPC_INVOKE_SHELL_CMD = 1,
1415
};
1516

1617
#endif /* DEV_INFO_RPC_IDS_H_ */

subsys/nrf_rpc/dev_info/server/dev_info_server.c

+69
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include <ncs_commit.h>
1111
#include <dev_info_rpc_ids.h>
1212

13+
#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
14+
#include <zephyr/shell/shell_dummy.h>
15+
#endif
16+
1317
NRF_RPC_GROUP_DECLARE(dev_info_group);
1418

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

3539
NRF_RPC_CBOR_CMD_DECODER(dev_info_group, get_server_version, DEV_INFO_RPC_GET_VERSION,
3640
get_server_version, NULL);
41+
42+
#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
43+
static int shell_exec(const char *line)
44+
{
45+
const struct shell *sh = shell_backend_dummy_get_ptr();
46+
47+
shell_backend_dummy_clear_output(sh);
48+
return shell_execute_cmd(sh, line);
49+
}
50+
51+
const char *shell_get_output(size_t *len)
52+
{
53+
return shell_backend_dummy_get_output(shell_backend_dummy_get_ptr(), len);
54+
}
55+
#endif /* CONFIG_NRF_RPC_REMOTE_SHELL */
56+
57+
static void remote_shell_cmd(const struct nrf_rpc_group *group, struct nrf_rpc_cbor_ctx *ctx,
58+
void *handler_data)
59+
{
60+
struct nrf_rpc_cbor_ctx rsp_ctx;
61+
char *cmd_buffer = NULL;
62+
const void *ptr;
63+
size_t len = 0;
64+
const char *output = NULL;
65+
66+
ptr = nrf_rpc_decode_str_ptr_and_len(ctx, &len);
67+
68+
if (ptr) {
69+
cmd_buffer = k_malloc(len + 1);
70+
if (cmd_buffer) {
71+
memcpy(cmd_buffer, ptr, len);
72+
cmd_buffer[len] = '\0';
73+
}
74+
}
75+
76+
if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
77+
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, group, DEV_INFO_RPC_INVOKE_SHELL_CMD,
78+
NRF_RPC_PACKET_TYPE_CMD);
79+
goto exit;
80+
}
81+
82+
if (!ptr || !cmd_buffer) {
83+
goto exit;
84+
}
85+
86+
#if defined(CONFIG_NRF_RPC_REMOTE_SHELL)
87+
shell_exec(cmd_buffer);
88+
output = shell_get_output(&len);
89+
#else
90+
output = "Remote shell is disabled on the server. "
91+
"Enable CONFIG_NRF_RPC_REMOTE_SHELL to use it.";
92+
len = strnlen(output, 255);
93+
#endif /* CONFIG_NRF_RPC_REMOTE_SHELL */
94+
95+
NRF_RPC_CBOR_ALLOC(group, rsp_ctx, 3 + len);
96+
97+
nrf_rpc_encode_str(&rsp_ctx, output, len);
98+
nrf_rpc_cbor_rsp_no_err(group, &rsp_ctx);
99+
100+
exit:
101+
k_free(cmd_buffer);
102+
}
103+
104+
NRF_RPC_CBOR_CMD_DECODER(dev_info_group, remote_shell_cmd, DEV_INFO_RPC_INVOKE_SHELL_CMD,
105+
remote_shell_cmd, NULL);

tests/subsys/nrf_rpc/dev_info/client/src/dev_info_suite.c

+22
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,26 @@ ZTEST(dev_info_rpc_client, test_get_version)
4040
zexpect_str_equal(commit, version);
4141
}
4242

43+
ZTEST(dev_info_rpc_client, test_invoke_shell_cmd)
44+
{
45+
46+
const size_t argc = 2;
47+
static const char *const argv[] = {"say", "hello", NULL};
48+
const char *hello = "HelloWorld\0";
49+
char *reply = NULL;
50+
51+
mock_nrf_rpc_tr_expect_add(
52+
RPC_CMD(DEV_INFO_RPC_INVOKE_SHELL_CMD, 0x6a, 's', 'a', 'y', ' ', 'h', 'e', 'l', 'l',
53+
'o', '\0'),
54+
RPC_RSP(0x6b, 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'));
55+
reply = nrf_rpc_invoke_remote_shell_cmd(argc, (char **)argv);
56+
mock_nrf_rpc_tr_expect_done();
57+
58+
zexpect_str_equal(hello, reply);
59+
60+
if (reply) {
61+
k_free(reply);
62+
}
63+
}
64+
4365
ZTEST_SUITE(dev_info_rpc_client, NULL, NULL, tc_setup, NULL, NULL);

tests/subsys/nrf_rpc/dev_info/server/prj.conf

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ CONFIG_KERNEL_MEM_POOL=y
1818
CONFIG_HEAP_MEM_POOL_SIZE=4096
1919
CONFIG_NRF_RPC_DEV_INFO=y
2020
CONFIG_NRF_RPC_DEV_INFO_SERVER=y
21+
CONFIG_NRF_RPC_REMOTE_SHELL=n

tests/subsys/nrf_rpc/dev_info/server/src/dev_info_suite.c

+15
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,19 @@ ZTEST(dev_info_rpc_server, test_get_server_version)
3434
mock_nrf_rpc_tr_expect_done();
3535
}
3636

37+
ZTEST(dev_info_rpc_server, test_invoke_shell_cmd)
38+
{
39+
mock_nrf_rpc_tr_expect_add(
40+
RPC_RSP(0x78, 0x55, 'R', 'e', 'm', 'o', 't', 'e', ' ', 's', 'h', 'e', 'l', 'l', ' ',
41+
'i', 's', ' ', 'd', 'i', 's', 'a', 'b', 'l', 'e', 'd', ' ', 'o', 'n', ' ',
42+
't', 'h', 'e', ' ', 's', 'e', 'r', 'v', 'e', 'r', '.', ' ', 'E', 'n', 'a',
43+
'b', 'l', 'e', ' ', 'C', 'O', 'N', 'F', 'I', 'G', '_', 'N', 'R', 'F', '_',
44+
'R', 'P', 'C', '_', 'R', 'E', 'M', 'O', 'T', 'E', '_', 'S', 'H', 'E', 'L',
45+
'L', ' ', 't', 'o', ' ', 'u', 's', 'e', ' ', 'i', 't', '.'),
46+
NO_RSP);
47+
mock_nrf_rpc_tr_receive(RPC_CMD(DEV_INFO_RPC_INVOKE_SHELL_CMD, 0x6a, 's', 'a', 'y', ' ',
48+
'h', 'e', 'l', 'l', 'o', '\0'));
49+
mock_nrf_rpc_tr_expect_done();
50+
}
51+
3752
ZTEST_SUITE(dev_info_rpc_server, NULL, NULL, tc_setup, NULL, NULL);

0 commit comments

Comments
 (0)