Commit 1c94049b authored by schneider's avatar schneider

feat(config): Add epic_config_set_string function

Allows to set the string value of a key. Perists the value to the
configuration file.
parent b48b5506
......@@ -145,6 +145,7 @@ typedef _Bool bool;
#define API_CONFIG_GET_STRING 0x130
#define API_CONFIG_GET_INTEGER 0x131
#define API_CONFIG_GET_BOOLEAN 0x132
#define API_CONFIG_SET_STRING 0x133
/* clang-format on */
typedef uint32_t api_int_id_t;
......@@ -2060,5 +2061,21 @@ API(API_CONFIG_GET_BOOLEAN, int epic_config_get_boolean(const char *key, bool *v
API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf, size_t buf_len));
/**
* Write a string to the configuration file.
*
* :param char* key: Name of the option to write
* :param char* value: The value to write
* :return: `0` on success or a negative value if an error occured. Possivle
* errors:
*
* - ``-EINVAL``: Parameters out of range
* - ``-ENOENT``: Key already exists but can not be read
* - ``-EIO`` : Unspecified I/O error
* - Any fopen/fread/fwrite/fclose related error code
*
* .. versionadded:: 1.16
*/
API(API_CONFIG_SET_STRING, int epic_config_set_string(const char *key, const char *value));
#endif /* _EPICARDIUM_H */
......@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#define MAX_LINE_LENGTH 80
#define KEYS_PER_BLOCK 16
......@@ -381,3 +382,222 @@ bool config_get_boolean_with_default(const char *key, bool default_value)
return value;
}
}
// TODO: don't allow things like "execute_elf" to be set
int epic_config_set_string(const char *key, const char *value)
{
config_slot *slot = find_config_slot(key);
bool present = slot && slot->value_offset;
int ret = 0;
if (snprintf(NULL, 0, "\n%s = %s\n", key, value) > MAX_LINE_LENGTH) {
return -EINVAL;
}
if (!present) {
/* Easy case: We simply add the new option at the
* end of the file. */
char buf[MAX_LINE_LENGTH];
/* Leading new line because I'm lazy */
ret = snprintf(buf, sizeof(buf), "\n%s = %s\n", key, value);
if (ret < 0 || ret >= (int)sizeof(buf)) {
return -EINVAL;
}
int fd = epic_file_open("card10.cfg", "a");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"open for appending failed: %s (%d)",
strerror(-fd),
fd
);
return fd;
}
int write_ret = epic_file_write(fd, buf, strlen(buf));
if (write_ret < 0) {
LOG_DEBUG(
"card10.cfg",
"writing failed: %s (%d)",
strerror(-write_ret),
write_ret
);
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
}
ret = epic_file_close(fd);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"close failed: %s (%d)",
strerror(-ret),
ret
);
}
if (write_ret < 0) {
return write_ret;
}
if (ret < 0) {
return ret;
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
return -EIO;
}
} else {
/* Complex case: The value is already somewhere in the file.
* We do not want to lose existing formatting or comments.
* Solution: Copy parts of the file, insert new value, copy
* rest, rename.
*/
char buf[128];
int fd1 = -1;
int fd2 = -1;
ret = epic_config_get_string(key, buf, sizeof(buf));
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"could not read old value (%d)",
ret
);
goto out;
}
int old_len = strlen(buf);
fd1 = epic_file_open("card10.cfg", "r");
if (fd1 < 0) {
LOG_DEBUG(
"card10.cfg",
"open for read failed: %s (%d)",
strerror(-fd1),
fd1
);
ret = fd1;
goto out;
}
fd2 = epic_file_open("card10.nfg", "w");
if (fd2 < 0) {
LOG_DEBUG(
"card10.nfg",
"open for writing failed: %s (%d)",
strerror(-fd2),
fd2
);
ret = fd2;
goto out;
}
/* Copy over slot->value_offset bytes */
int i = slot->value_offset;
while (i > 0) {
int n = i > (int)sizeof(buf) ? (int)sizeof(buf) : i;
ret = epic_file_read(fd1, buf, n);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto out;
}
i -= ret;
}
/* Insert new value into the new file */
ret = epic_file_write(fd2, value, strlen(value));
if (ret < 0) {
LOG_DEBUG("card10.nfg", "write failed: rc: %d", ret);
goto out;
}
/* Skip the old value inside the old file */
epic_file_seek(fd1, old_len, SEEK_CUR);
/* Copy the rest of the old file to the new file */
while (true) {
int ret = epic_file_read(fd1, buf, sizeof(buf));
if (ret == 0) {
break;
}
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto out;
}
if (ret < (int)sizeof(buf)) {
break;
}
}
out:
if (fd1 >= 0) {
epic_file_close(fd1);
}
if (fd2 >= 0) {
int ret2 = epic_file_close(fd2);
if (ret >= 0) {
ret = ret2;
}
}
if (ret >= 0) {
epic_file_unlink("card10.cfg");
epic_file_rename("card10.nfg", "card10.cfg");
}
}
/* Reload config so the new key or the changed value is available */
load_config();
return ret < 0 ? ret : 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment