1 /*
2 * Copyright (c) 2018-2021 mcumgr authors
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/sys/util.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/shell/shell_dummy.h>
10 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
11 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
12 #include <zephyr/mgmt/mcumgr/smp/smp.h>
13 #include <zephyr/mgmt/mcumgr/grp/shell_mgmt/shell_mgmt.h>
14 #include <string.h>
15 #include <stdio.h>
16
17 #include <zcbor_common.h>
18 #include <zcbor_encode.h>
19 #include <zcbor_decode.h>
20
21 LOG_MODULE_REGISTER(mcumgr_shell_grp, CONFIG_MCUMGR_GRP_SHELL_LOG_LEVEL);
22
23 static int
shell_exec(const char * line)24 shell_exec(const char *line)
25 {
26 const struct shell *sh = shell_backend_dummy_get_ptr();
27
28 shell_backend_dummy_clear_output(sh);
29 return shell_execute_cmd(sh, line);
30 }
31
32 const char *
shell_get_output(size_t * len)33 shell_get_output(size_t *len)
34 {
35 return shell_backend_dummy_get_output(
36 shell_backend_dummy_get_ptr(),
37 len
38 );
39 }
40
41 /**
42 * Command handler: shell exec
43 */
44 static int
shell_mgmt_exec(struct smp_streamer * ctxt)45 shell_mgmt_exec(struct smp_streamer *ctxt)
46 {
47 int rc;
48 bool ok;
49 char line[CONFIG_SHELL_CMD_BUFF_SIZE + 1];
50 size_t len = 0;
51 struct zcbor_string cmd_out;
52 zcbor_state_t *zsd = ctxt->reader->zs;
53 zcbor_state_t *zse = ctxt->writer->zs;
54
55 if (!zcbor_map_start_decode(zsd)) {
56 return MGMT_ERR_EINVAL;
57 }
58
59 /* Expecting single array named "argv" */
60 do {
61 struct zcbor_string key;
62 static const char argv_keyword[] = "argv";
63
64 ok = zcbor_tstr_decode(zsd, &key);
65
66 if (ok) {
67 if (key.len == (ARRAY_SIZE(argv_keyword) - 1) &&
68 memcmp(key.value, argv_keyword, ARRAY_SIZE(argv_keyword) - 1) == 0) {
69 break;
70 }
71
72 ok = zcbor_any_skip(zsd, NULL);
73 }
74 } while (ok);
75
76 if (!ok || !zcbor_list_start_decode(zsd)) {
77 return MGMT_ERR_EINVAL;
78 }
79
80 /* Compose command line */
81 do {
82 struct zcbor_string value;
83
84 ok = zcbor_tstr_decode(zsd, &value);
85 if (ok) {
86 if ((len + value.len) >= (ARRAY_SIZE(line) - 1)) {
87 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SHELL,
88 SHELL_MGMT_ERR_COMMAND_TOO_LONG);
89 goto end;
90 }
91
92 memcpy(&line[len], value.value, value.len);
93 len += value.len + 1;
94 line[len - 1] = ' ';
95 } else if (len > 0) {
96 line[len - 1] = 0;
97 /* Implicit break by while condition */
98 }
99 } while (ok);
100
101 zcbor_list_end_decode(zsd);
102
103 /* Failed to compose command line? */
104 if (len == 0) {
105 /* We do not bother to close decoder */
106 LOG_ERR("Failed to compose command line");
107 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SHELL, SHELL_MGMT_ERR_EMPTY_COMMAND);
108 goto end;
109 }
110
111 rc = shell_exec(line);
112 cmd_out.value = shell_get_output(&cmd_out.len);
113
114 /* Key="o"; value=<command-output> */
115 /* Key="ret"; value=<status>, or rc if legacy option enabled */
116 ok = zcbor_tstr_put_lit(zse, "o") &&
117 zcbor_tstr_encode(zse, &cmd_out) &&
118 #ifdef CONFIG_MCUMGR_GRP_SHELL_LEGACY_RC_RETURN_CODE
119 zcbor_tstr_put_lit(zse, "rc") &&
120 #else
121 zcbor_tstr_put_lit(zse, "ret") &&
122 #endif
123 zcbor_int32_put(zse, rc);
124
125 zcbor_map_end_decode(zsd);
126
127 end:
128 return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
129 }
130
131 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
132 /*
133 * @brief Translate shell mgmt group error code into MCUmgr error code
134 *
135 * @param ret #shell_mgmt_err_code_t error code
136 *
137 * @return #mcumgr_err_t error code
138 */
shell_mgmt_translate_error_code(uint16_t err)139 static int shell_mgmt_translate_error_code(uint16_t err)
140 {
141 int rc;
142
143 switch (err) {
144 case SHELL_MGMT_ERR_COMMAND_TOO_LONG:
145 case SHELL_MGMT_ERR_EMPTY_COMMAND:
146 rc = MGMT_ERR_EINVAL;
147 break;
148
149 default:
150 rc = MGMT_ERR_EUNKNOWN;
151 }
152
153 return rc;
154 }
155 #endif
156
157 static const struct mgmt_handler shell_mgmt_handlers[] = {
158 [SHELL_MGMT_ID_EXEC] = { NULL, shell_mgmt_exec },
159 };
160
161 #define SHELL_MGMT_HANDLER_CNT ARRAY_SIZE(shell_mgmt_handlers)
162
163 static struct mgmt_group shell_mgmt_group = {
164 .mg_handlers = shell_mgmt_handlers,
165 .mg_handlers_count = SHELL_MGMT_HANDLER_CNT,
166 .mg_group_id = MGMT_GROUP_ID_SHELL,
167 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
168 .mg_translate_error = shell_mgmt_translate_error_code,
169 #endif
170 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
171 .mg_group_name = "shell mgmt",
172 #endif
173 };
174
shell_mgmt_register_group(void)175 static void shell_mgmt_register_group(void)
176 {
177 mgmt_register_group(&shell_mgmt_group);
178 }
179
180 MCUMGR_HANDLER_DEFINE(shell_mgmt, shell_mgmt_register_group);
181