settings: shell: improve reading and writing string values

Make reading and writing string values more flexible:
1. Eliminate the intermediate buffer when saving a string
   setting. This needlessly limited the maximum string
   length that could be saved using the shell command.
2. Do not add nor assume that a string saved in the settings
   includes the null-terminator. The settings subsystem uses
   metadata for encoding the value length, so there it is
   redundant to also store the null-terminator in flash.

By the way, also make sure the command handlers only return
-EINVAL and -ENOEXEC error codes as documented in the
handler type description.

Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>
This commit is contained in:
Damian Krolik 2025-02-14 13:46:35 +01:00 committed by Benjamin Cabé
commit fa8eef0fbc

View file

@ -9,6 +9,7 @@
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/toolchain.h> #include <zephyr/toolchain.h>
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
@ -51,6 +52,7 @@ static int cmd_list(const struct shell *shell_ptr, size_t argc, char *argv[])
if (err) { if (err) {
shell_error(shell_ptr, "Failed to load settings: %d", err); shell_error(shell_ptr, "Failed to load settings: %d", err);
err = -ENOEXEC;
} }
return err; return err;
@ -74,6 +76,7 @@ static int settings_read_callback(const char *key,
void *param) void *param)
{ {
uint8_t buffer[SETTINGS_MAX_VAL_LEN]; uint8_t buffer[SETTINGS_MAX_VAL_LEN];
ssize_t i;
ssize_t num_read_bytes = MIN(len, SETTINGS_MAX_VAL_LEN); ssize_t num_read_bytes = MIN(len, SETTINGS_MAX_VAL_LEN);
struct settings_read_callback_params *params = param; struct settings_read_callback_params *params = param;
@ -100,11 +103,13 @@ static int settings_read_callback(const char *key,
shell_hexdump(params->shell_ptr, buffer, num_read_bytes); shell_hexdump(params->shell_ptr, buffer, num_read_bytes);
break; break;
case SETTINGS_VALUE_STRING: case SETTINGS_VALUE_STRING:
if (buffer[num_read_bytes - 1] != '\0') { for (i = 0; i < num_read_bytes; i++) {
shell_error(params->shell_ptr, "Value is not a string"); if (!isprint(buffer[i])) {
return 0; shell_error(params->shell_ptr, "Value is not a string");
return 0;
}
} }
shell_print(params->shell_ptr, "%s", buffer); shell_print(params->shell_ptr, "%.*s", (int)num_read_bytes, buffer);
break; break;
} }
@ -138,7 +143,7 @@ static int cmd_read(const struct shell *shell_ptr, size_t argc, char *argv[])
err = settings_parse_type(argv[1], &value_type); err = settings_parse_type(argv[1], &value_type);
if (err) { if (err) {
shell_error(shell_ptr, "Invalid type: %s", argv[1]); shell_error(shell_ptr, "Invalid type: %s", argv[1]);
return err; return -EINVAL;
} }
} }
@ -152,9 +157,10 @@ static int cmd_read(const struct shell *shell_ptr, size_t argc, char *argv[])
if (err) { if (err) {
shell_error(shell_ptr, "Failed to load setting: %d", err); shell_error(shell_ptr, "Failed to load setting: %d", err);
err = -ENOEXEC;
} else if (!params.value_found) { } else if (!params.value_found) {
err = -ENOENT;
shell_error(shell_ptr, "Setting not found"); shell_error(shell_ptr, "Setting not found");
err = -ENOEXEC;
} }
return err; return err;
@ -164,42 +170,39 @@ static int cmd_write(const struct shell *shell_ptr, size_t argc, char *argv[])
{ {
int err; int err;
uint8_t buffer[CONFIG_SHELL_CMD_BUFF_SIZE / 2]; uint8_t buffer[CONFIG_SHELL_CMD_BUFF_SIZE / 2];
size_t buffer_len = 0; const void *value;
size_t value_len = 0;
enum settings_value_types value_type = SETTINGS_VALUE_HEX; enum settings_value_types value_type = SETTINGS_VALUE_HEX;
if (argc > 3) { if (argc > 3) {
err = settings_parse_type(argv[1], &value_type); err = settings_parse_type(argv[1], &value_type);
if (err) { if (err) {
shell_error(shell_ptr, "Invalid type: %s", argv[1]); shell_error(shell_ptr, "Invalid type: %s", argv[1]);
return err; return -EINVAL;
} }
} }
switch (value_type) { switch (value_type) {
case SETTINGS_VALUE_HEX: case SETTINGS_VALUE_HEX:
buffer_len = hex2bin(argv[argc - 1], strlen(argv[argc - 1]), value = buffer;
buffer, sizeof(buffer)); value_len = hex2bin(argv[argc - 1], strlen(argv[argc - 1]), buffer, sizeof(buffer));
break; break;
case SETTINGS_VALUE_STRING: case SETTINGS_VALUE_STRING:
buffer_len = strlen(argv[argc - 1]) + 1; value = argv[argc - 1];
if (buffer_len > sizeof(buffer)) { value_len = strlen(argv[argc - 1]);
shell_error(shell_ptr, "%s is bigger than shell's buffer", argv[argc - 1]);
return -EINVAL;
}
memcpy(buffer, argv[argc - 1], buffer_len);
break; break;
} }
if (buffer_len == 0) { if (value_len == 0) {
shell_error(shell_ptr, "Failed to parse value"); shell_error(shell_ptr, "Failed to parse value");
return -EINVAL; return -EINVAL;
} }
err = settings_save_one(argv[argc - 2], buffer, buffer_len); err = settings_save_one(argv[argc - 2], value, value_len);
if (err) { if (err) {
shell_error(shell_ptr, "Failed to write setting: %d", err); shell_error(shell_ptr, "Failed to write setting: %d", err);
err = -ENOEXEC;
} }
return err; return err;
@ -213,6 +216,7 @@ static int cmd_delete(const struct shell *shell_ptr, size_t argc, char *argv[])
if (err) { if (err) {
shell_error(shell_ptr, "Failed to delete setting: %d", err); shell_error(shell_ptr, "Failed to delete setting: %d", err);
err = -ENOEXEC;
} }
return err; return err;