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;
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 	ok = zcbor_tstr_put_lit(zse, "rc")		&&
204 	     zcbor_int32_put(zse, MGMT_ERR_EOK)		&&
205 	     zcbor_tstr_put_lit(zse, "stat_list")	&&
206 	     zcbor_list_start_encode(zse, counter);
207 
208 	if (!ok) {
209 		return MGMT_ERR_EMSGSIZE;
210 	}
211 	/* Iterate the list of stat groups, encoding each group's name in the CBOR
212 	 * array.
213 	 */
214 	cur = NULL;
215 	do {
216 		cur = stats_group_get_next(cur);
217 		if (cur != NULL) {
218 			ok = zcbor_tstr_put_term(zse, cur->s_name,
219 						CONFIG_MCUMGR_GRP_STAT_MAX_NAME_LEN);
220 		}
221 	} while (ok && cur != NULL);
222 
223 	if (!ok || !zcbor_list_end_encode(zse, counter)) {
224 		return MGMT_ERR_EMSGSIZE;
225 	}
226 
227 	return 0;
228 }
229 
230 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
231 /*
232  * @brief	Translate stat mgmt group error code into MCUmgr error code
233  *
234  * @param ret	#stat_mgmt_err_code_t error code
235  *
236  * @return	#mcumgr_err_t error code
237  */
stat_mgmt_translate_error_code(uint16_t err)238 static int stat_mgmt_translate_error_code(uint16_t err)
239 {
240 	int rc;
241 
242 	switch (err) {
243 	case STAT_MGMT_ERR_INVALID_GROUP:
244 	case STAT_MGMT_ERR_INVALID_STAT_NAME:
245 		rc = MGMT_ERR_ENOENT;
246 		break;
247 
248 	case STAT_MGMT_ERR_INVALID_STAT_SIZE:
249 		rc = MGMT_ERR_EINVAL;
250 		break;
251 
252 	case STAT_MGMT_ERR_WALK_ABORTED:
253 	default:
254 		rc = MGMT_ERR_EUNKNOWN;
255 	}
256 
257 	return rc;
258 }
259 #endif
260 
261 static const struct mgmt_handler stat_mgmt_handlers[] = {
262 	[STAT_MGMT_ID_SHOW] = { stat_mgmt_show, NULL },
263 	[STAT_MGMT_ID_LIST] = { stat_mgmt_list, NULL },
264 };
265 
266 #define STAT_MGMT_HANDLER_CNT ARRAY_SIZE(stat_mgmt_handlers)
267 
268 static struct mgmt_group stat_mgmt_group = {
269 	.mg_handlers = stat_mgmt_handlers,
270 	.mg_handlers_count = STAT_MGMT_HANDLER_CNT,
271 	.mg_group_id = MGMT_GROUP_ID_STAT,
272 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
273 	.mg_translate_error = stat_mgmt_translate_error_code,
274 #endif
275 };
276 
stat_mgmt_register_group(void)277 static void stat_mgmt_register_group(void)
278 {
279 	mgmt_register_group(&stat_mgmt_group);
280 }
281 
282 MCUMGR_HANDLER_DEFINE(stat_mgmt, stat_mgmt_register_group);
283