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/stats/stats.h>
9 #include <zephyr/logging/log.h>
10 #include <string.h>
11 #include <stdio.h>
12 
13 #include <zcbor_common.h>
14 #include <zcbor_decode.h>
15 #include <zcbor_encode.h>
16 
17 #include <mgmt/mcumgr/util/zcbor_bulk.h>
18 
19 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
20 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
21 #include <zephyr/mgmt/mcumgr/smp/smp.h>
22 #include <zephyr/mgmt/mcumgr/grp/stat_mgmt/stat_mgmt.h>
23 
24 LOG_MODULE_REGISTER(mcumgr_stat_grp, CONFIG_MCUMGR_GRP_STAT_LOG_LEVEL);
25 
26 static const struct mgmt_handler stat_mgmt_handlers[];
27 
28 typedef int stat_mgmt_foreach_entry_fn(zcbor_state_t *zse, struct stat_mgmt_entry *entry);
29 
30 static int
stats_mgmt_count_plus_one(struct stats_hdr * hdr,void * arg,const char * name,uint16_t off)31 stats_mgmt_count_plus_one(struct stats_hdr *hdr, void *arg, const char *name, uint16_t off)
32 {
33 	size_t *counter = arg;
34 
35 	(*counter)++;
36 
37 	return 0;
38 }
39 
40 static int
stat_mgmt_count(const char * group_name,size_t * counter)41 stat_mgmt_count(const char *group_name, size_t *counter)
42 {
43 	struct stats_hdr *hdr = stats_group_find(group_name);
44 
45 	if (hdr == NULL) {
46 		return MGMT_ERR_ENOENT;
47 	}
48 
49 	*counter = 0;
50 
51 	return stats_walk(hdr, stats_mgmt_count_plus_one, &counter);
52 }
53 
54 static int
55 stat_mgmt_walk_cb(struct stats_hdr *hdr, void *arg, const char *name, uint16_t off);
56 
57 struct stat_mgmt_walk_arg {
58 	stat_mgmt_foreach_entry_fn *cb;
59 	zcbor_state_t *zse;
60 };
61 
62 static int
stat_mgmt_walk_cb(struct stats_hdr * hdr,void * arg,const char * name,uint16_t off)63 stat_mgmt_walk_cb(struct stats_hdr *hdr, void *arg, const char *name, uint16_t off)
64 {
65 	struct stat_mgmt_walk_arg *walk_arg;
66 	struct stat_mgmt_entry entry;
67 	void *stat_val;
68 
69 	walk_arg = arg;
70 
71 	stat_val = (uint8_t *)hdr + off;
72 	switch (hdr->s_size) {
73 	case sizeof(uint16_t):
74 		entry.value = *(uint16_t *) stat_val;
75 		break;
76 	case sizeof(uint32_t):
77 		entry.value = *(uint32_t *) stat_val;
78 		break;
79 	case sizeof(uint64_t):
80 		entry.value = *(uint64_t *) stat_val;
81 		break;
82 	default:
83 		return STAT_MGMT_ERR_INVALID_STAT_SIZE;
84 	}
85 
86 	entry.name = name;
87 
88 	return walk_arg->cb(walk_arg->zse, &entry);
89 }
90 
91 static int
stat_mgmt_foreach_entry(zcbor_state_t * zse,const char * group_name,stat_mgmt_foreach_entry_fn * cb)92 stat_mgmt_foreach_entry(zcbor_state_t *zse, const char *group_name, stat_mgmt_foreach_entry_fn *cb)
93 {
94 	struct stat_mgmt_walk_arg walk_arg;
95 	struct stats_hdr *hdr;
96 
97 	hdr = stats_group_find(group_name);
98 	if (hdr == NULL) {
99 		return STAT_MGMT_ERR_INVALID_GROUP;
100 	}
101 
102 	walk_arg = (struct stat_mgmt_walk_arg) {
103 		.cb = cb,
104 		.zse = zse
105 	};
106 
107 	return stats_walk(hdr, stat_mgmt_walk_cb, &walk_arg);
108 }
109 
110 static int
stat_mgmt_cb_encode(zcbor_state_t * zse,struct stat_mgmt_entry * entry)111 stat_mgmt_cb_encode(zcbor_state_t *zse, struct stat_mgmt_entry *entry)
112 {
113 	bool ok = zcbor_tstr_put_term(zse, entry->name, CONFIG_MCUMGR_GRP_STAT_MAX_NAME_LEN) &&
114 		  zcbor_uint32_put(zse, entry->value);
115 
116 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
117 }
118 
119 /**
120  * Command handler: stat show
121  */
122 static int
stat_mgmt_show(struct smp_streamer * ctxt)123 stat_mgmt_show(struct smp_streamer *ctxt)
124 {
125 	zcbor_state_t *zse = ctxt->writer->zs;
126 	zcbor_state_t *zsd = ctxt->reader->zs;
127 	char stat_name[CONFIG_MCUMGR_GRP_STAT_MAX_NAME_LEN];
128 	bool ok;
129 	size_t counter = 0;
130 	size_t decoded;
131 	struct zcbor_string name = { 0 };
132 
133 	struct zcbor_map_decode_key_val stat_decode[] = {
134 		ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &name),
135 	};
136 
137 	ok = zcbor_map_decode_bulk(zsd, stat_decode, ARRAY_SIZE(stat_decode), &decoded) == 0;
138 
139 	if (!ok || name.len == 0 || name.len >= ARRAY_SIZE(stat_name)) {
140 		return MGMT_ERR_EINVAL;
141 	}
142 
143 	memcpy(stat_name, name.value, name.len);
144 	stat_name[name.len] = '\0';
145 
146 	if (stat_mgmt_count(stat_name, &counter) != 0) {
147 		LOG_ERR("Invalid stat name: %s", stat_name);
148 		ok = smp_add_cmd_err(zse, ZEPHYR_MGMT_GRP_BASIC,
149 				     STAT_MGMT_ERR_INVALID_STAT_NAME);
150 		goto end;
151 	}
152 
153 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
154 		ok = zcbor_tstr_put_lit(zse, "rc")		&&
155 		     zcbor_int32_put(zse, MGMT_ERR_EOK);
156 	}
157 
158 	if (ok) {
159 		ok = zcbor_tstr_put_lit(zse, "name")		&&
160 		     zcbor_tstr_encode(zse, &name)		&&
161 		     zcbor_tstr_put_lit(zse, "fields")		&&
162 		     zcbor_map_start_encode(zse, counter);
163 	}
164 
165 	if (ok) {
166 		int rc = stat_mgmt_foreach_entry(zse, stat_name,
167 						 stat_mgmt_cb_encode);
168 
169 		if (rc != STAT_MGMT_ERR_OK) {
170 			if (rc != STAT_MGMT_ERR_INVALID_GROUP &&
171 			    rc != STAT_MGMT_ERR_INVALID_STAT_SIZE) {
172 				rc = STAT_MGMT_ERR_WALK_ABORTED;
173 			}
174 
175 			ok = smp_add_cmd_err(zse, ZEPHYR_MGMT_GRP_BASIC, rc);
176 		}
177 	}
178 
179 	ok = ok && zcbor_map_end_encode(zse, counter);
180 
181 end:
182 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
183 }
184 
185 /**
186  * Command handler: stat list
187  */
188 static int
stat_mgmt_list(struct smp_streamer * ctxt)189 stat_mgmt_list(struct smp_streamer *ctxt)
190 {
191 	const struct stats_hdr *cur = NULL;
192 	zcbor_state_t *zse = ctxt->writer->zs;
193 	bool ok = true;
194 	size_t counter = 0;
195 
196 	do {
197 		cur = stats_group_get_next(cur);
198 		if (cur != NULL) {
199 			counter++;
200 		}
201 	} while (cur != NULL);
202 
203 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
204 		ok = zcbor_tstr_put_lit(zse, "rc") &&
205 		zcbor_int32_put(zse, MGMT_ERR_EOK);
206 	}
207 
208 	if (ok) {
209 		ok = zcbor_tstr_put_lit(zse, "stat_list") &&
210 		zcbor_list_start_encode(zse, counter);
211 	}
212 
213 	if (!ok) {
214 		return MGMT_ERR_EMSGSIZE;
215 	}
216 	/* Iterate the list of stat groups, encoding each group's name in the CBOR
217 	 * array.
218 	 */
219 	cur = NULL;
220 	do {
221 		cur = stats_group_get_next(cur);
222 		if (cur != NULL) {
223 			ok = zcbor_tstr_put_term(zse, cur->s_name,
224 						CONFIG_MCUMGR_GRP_STAT_MAX_NAME_LEN);
225 		}
226 	} while (ok && cur != NULL);
227 
228 	if (!ok || !zcbor_list_end_encode(zse, counter)) {
229 		return MGMT_ERR_EMSGSIZE;
230 	}
231 
232 	return MGMT_ERR_EOK;
233 }
234 
235 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
236 /*
237  * @brief	Translate stat mgmt group error code into MCUmgr error code
238  *
239  * @param ret	#stat_mgmt_err_code_t error code
240  *
241  * @return	#mcumgr_err_t error code
242  */
stat_mgmt_translate_error_code(uint16_t err)243 static int stat_mgmt_translate_error_code(uint16_t err)
244 {
245 	int rc;
246 
247 	switch (err) {
248 	case STAT_MGMT_ERR_INVALID_GROUP:
249 	case STAT_MGMT_ERR_INVALID_STAT_NAME:
250 		rc = MGMT_ERR_ENOENT;
251 		break;
252 
253 	case STAT_MGMT_ERR_INVALID_STAT_SIZE:
254 		rc = MGMT_ERR_EINVAL;
255 		break;
256 
257 	case STAT_MGMT_ERR_WALK_ABORTED:
258 	default:
259 		rc = MGMT_ERR_EUNKNOWN;
260 	}
261 
262 	return rc;
263 }
264 #endif
265 
266 static const struct mgmt_handler stat_mgmt_handlers[] = {
267 	[STAT_MGMT_ID_SHOW] = { stat_mgmt_show, NULL },
268 	[STAT_MGMT_ID_LIST] = { stat_mgmt_list, NULL },
269 };
270 
271 #define STAT_MGMT_HANDLER_CNT ARRAY_SIZE(stat_mgmt_handlers)
272 
273 static struct mgmt_group stat_mgmt_group = {
274 	.mg_handlers = stat_mgmt_handlers,
275 	.mg_handlers_count = STAT_MGMT_HANDLER_CNT,
276 	.mg_group_id = MGMT_GROUP_ID_STAT,
277 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
278 	.mg_translate_error = stat_mgmt_translate_error_code,
279 #endif
280 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
281 	.mg_group_name = "stat mgmt",
282 #endif
283 };
284 
stat_mgmt_register_group(void)285 static void stat_mgmt_register_group(void)
286 {
287 	mgmt_register_group(&stat_mgmt_group);
288 }
289 
290 MCUMGR_HANDLER_DEFINE(stat_mgmt, stat_mgmt_register_group);
291