123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- #include "common.h"
- #include "config.h"
- #include <esp_spiffs.h>
- #include <ctype.h>
- #define CONFIG_FILE "/spiffs/config.txt"
- struct env_var {
- const char *var, *val;
- };
- static const struct env_var default_config[] = {
- {"LANG","sv"},
- {"TZ", "CET-1CEST,M3.5.0,M10.5.0/3"}, /* Sweden */
- {"tzname", "Europe/Stockholm"},
- {"hostname", "max80"},
- {"sntp.enabled", "1"},
- {"sntp.server","time.max80.abc80.org"},
- {"abc.hosttype","auto"}
- };
- static int save_config(void);
- static bool config_changed;
- int setenv_config(const char *name, const char *value)
- {
- config_changed = true;
- if (name[0] == '-') {
- name++;
- value = NULL;
- }
- return setenv_cond(name, value);
- }
- int setenv_cond(const char *name, const char *value)
- {
- if (value) {
- printf("env: %s=%s\n", name, value);
- return setenv(name, value, 1);
- } else {
- printf("env: %s undefined\n", name);
- return unsetenv(name);
- }
- }
- static int reset_config(void)
- {
- while (1) {
- char **envp;
- for (envp = environ; *envp; envp++) {
- if (!strncmp("status.", *envp, 7))
- continue;
- else
- break;
- }
- if (!*envp)
- break;
- const char *eq = strchr(*envp, '=');
- if (!eq)
- continue; /* This should never happen... */
- char ename[eq - *envp + 1];
- memcpy(ename, *envp, eq - *envp);
- ename[eq - *envp] = '\0';
- setenv_cond(ename, NULL);
- }
- size_t i;
- for (i = 0; i < ARRAY_SIZE(default_config); i++)
- setenv_cond(default_config[i].var, default_config[i].val);
- config_changed = true;
- }
- static bool is_end_of_string(int c)
- {
- return c <= 0 || c == '\n' || c == '\r';
- }
- /*
- * Note: the string must be mutable; use strdup() if necessary.
- * The "separator" allows the string to be separated into multiple
- * arguments; no other decoding is done.
- */
- static int set_config_string(char *str, unsigned int separator)
- {
- char *p, *q;
- unsigned char c;
- p = str;
- do {
- const char *var = p;
- do {
- c = *p++;
- } while (isalnum(c) || c == '.' || c == '-');
- if (c != '=')
- return -EINVAL; /* Invalid config line (blank, comment...) */
- p[-1] = '\0';
- q = p;
- do {
- c = *q++;
- } while (!is_end_of_string(c) && c != separator);
- /* Overlong line */
- if (q >= str + MAX_CONFIG_LINE)
- return -EOVERFLOW;
- q[-1] = '\0';
- setenv_config(var, p);
- p = q;
- } while (c == separator);
- return 0;
- }
- static void finish_config_update(bool save)
- {
- if (config_changed) {
- if (save)
- save_config();
- tzset();
- config_changed = false;
- }
- }
- /*
- * At the moment "url" just allows values to be separated by semicolons;
- * no other decoding is done, and '&' is not supported.
- */
- int set_config_url_string(const char *str)
- {
- char *wstr = strdup(str);
- if (!wstr)
- return -ENOMEM;
- int err = set_config_string(wstr, ';');
- free(wstr);
- finish_config_update(true);
- return err;
- }
- static void skip_rest_of_line(FILE *f)
- {
- int c;
- do {
- c = getc(f);
- } while (!is_end_of_string(c));
- }
- /* Calling with with f == NULL just finalizes the update */
- int read_config(FILE *f, bool save)
- {
- char *linebuf = NULL;
- int err = -1;
- if (f) {
- linebuf = malloc(MAX_CONFIG_LINE);
- if (!linebuf) {
- err = -ENOMEM;
- goto exit;
- }
- while (fgets(linebuf, MAX_CONFIG_LINE, f)) {
- if (set_config_string(linebuf, -1) == -EOVERFLOW)
- skip_rest_of_line(f);
- }
- }
- err = 0;
- if (linebuf)
- free(linebuf);
- exit:
- finish_config_update(save);
- return err;
- };
- int write_env(FILE *f, bool status)
- {
- size_t skip = status ? 7 : 0;
- for (char **var = environ; *var; var++) {
- if (!strncmp(*var, "status.", 7) == status) {
- fputs(*var + skip, f);
- putc('\n', f);
- }
- }
- return ferror(f) ? -1 : 0;
- }
- static int save_config(void)
- {
- int err = -ENOENT;
- FILE *f = fopen(CONFIG_FILE, "w");
- if (f) {
- err = write_env(f, false);
- fclose(f);
- }
- if (err)
- printf("[CONF] Failed to save configuration (error %d)\n", err);
- return err;
- }
- static const esp_vfs_spiffs_conf_t spiffs_conf = {
- .base_path = "/spiffs",
- .partition_label = NULL,
- .max_files = 4,
- .format_if_mount_failed = true
- };
- void init_config(void)
- {
- if (!esp_spiffs_mounted(spiffs_conf.partition_label)) {
- esp_err_t err;
- err = esp_vfs_spiffs_register(&spiffs_conf);
- if (err)
- printf("[CONF] Failed to mount %s (error 0x%x)\n",
- spiffs_conf.base_path, err);
- }
- reset_config();
- FILE *f = fopen(CONFIG_FILE, "r");
- if (!f)
- printf("[CONF] No configuration file found, using defaults\n");
- read_config(f, false);
- if (f)
- fclose(f);
- }
- const char *getenv_def(const char *var, const char *def)
- {
- const char *val = getenv(var);
- return val ? val : def;
- }
- long getenv_l(const char *var, long def)
- {
- const char *ep;
- var = getenv(var);
- if (!var || !*var)
- return def;
- long val = strtol(var, (char **)&ep, 0);
- return *ep ? def : val;
- }
- void setenv_l(const char *var, long val)
- {
- char vbuf[2+3*sizeof val];
- snprintf(vbuf, sizeof vbuf, "%ld", val);
- setenv_cond(var, vbuf);
- }
- unsigned long getenv_ul(const char *var, unsigned long def)
- {
- const char *ep;
- var = getenv(var);
- if (!var || !*var)
- return def;
- unsigned long val = strtol(var, (char **)&ep, 0);
- return *ep ? def : val;
- }
- void setenv_ul(const char *var, unsigned long val)
- {
- char vbuf[2+3*sizeof val];
- snprintf(vbuf, sizeof vbuf, "%lu", val);
- setenv_cond(var, vbuf);
- }
- bool getenv_bool(const char *var)
- {
- var = getenv(var);
- if (!var)
- return false;
- unsigned char c = *var;
- unsigned char cl = c | 0x20;
- return !(!c || c == '0' || cl == 'f' || cl == 'n' || cl == 'd' ||
- (cl == 'o' && (var[1] | 0x20) == 'f'));
- }
- void setenv_bool(const char *var, bool val)
- {
- return setenv_ul(var, val);
- }
|