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