#include "common.h" #include "config.h" #include #include #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); }