config.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #include "common.h"
  2. #include "config.h"
  3. #include <esp_spiffs.h>
  4. #include <ctype.h>
  5. #define CONFIG_FILE "/spiffs/config.txt"
  6. struct env_var {
  7. const char *var, *val;
  8. };
  9. static const struct env_var default_config[] = {
  10. {"LANG","sv"},
  11. {"TZ", "CET-1CEST,M3.5.0,M10.5.0/3"}, /* Sweden */
  12. {"tzname", "Europe/Stockholm"},
  13. {"hostname", "max80"},
  14. {"sntp.enabled", "1"},
  15. {"sntp.server","time.max80.abc80.org"},
  16. {"abc.hosttype","auto"}
  17. };
  18. static int save_config(void);
  19. static bool config_changed;
  20. int setenv_config(const char *name, const char *value)
  21. {
  22. config_changed = true;
  23. if (name[0] == '-') {
  24. name++;
  25. value = NULL;
  26. }
  27. return setenv_cond(name, value);
  28. }
  29. int setenv_cond(const char *name, const char *value)
  30. {
  31. if (value) {
  32. printf("env: %s=%s\n", name, value);
  33. return setenv(name, value, 1);
  34. } else {
  35. printf("env: %s undefined\n", name);
  36. return unsetenv(name);
  37. }
  38. }
  39. static int reset_config(void)
  40. {
  41. while (1) {
  42. char **envp;
  43. for (envp = environ; *envp; envp++) {
  44. if (!strncmp("status.", *envp, 7))
  45. continue;
  46. else
  47. break;
  48. }
  49. if (!*envp)
  50. break;
  51. const char *eq = strchr(*envp, '=');
  52. if (!eq)
  53. continue; /* This should never happen... */
  54. char ename[eq - *envp + 1];
  55. memcpy(ename, *envp, eq - *envp);
  56. ename[eq - *envp] = '\0';
  57. setenv_cond(ename, NULL);
  58. }
  59. size_t i;
  60. for (i = 0; i < ARRAY_SIZE(default_config); i++)
  61. setenv_cond(default_config[i].var, default_config[i].val);
  62. config_changed = true;
  63. }
  64. static bool is_end_of_string(int c)
  65. {
  66. return c <= 0 || c == '\n' || c == '\r';
  67. }
  68. /*
  69. * Note: the string must be mutable; use strdup() if necessary.
  70. * The "separator" allows the string to be separated into multiple
  71. * arguments; no other decoding is done.
  72. */
  73. static int set_config_string(char *str, unsigned int separator)
  74. {
  75. char *p, *q;
  76. unsigned char c;
  77. p = str;
  78. do {
  79. const char *var = p;
  80. do {
  81. c = *p++;
  82. } while (isalnum(c) || c == '.' || c == '-');
  83. if (c != '=')
  84. return -EINVAL; /* Invalid config line (blank, comment...) */
  85. p[-1] = '\0';
  86. q = p;
  87. do {
  88. c = *q++;
  89. } while (!is_end_of_string(c) && c != separator);
  90. /* Overlong line */
  91. if (q >= str + MAX_CONFIG_LINE)
  92. return -EOVERFLOW;
  93. q[-1] = '\0';
  94. setenv_config(var, p);
  95. p = q;
  96. } while (c == separator);
  97. return 0;
  98. }
  99. static void finish_config_update(bool save)
  100. {
  101. if (config_changed) {
  102. if (save)
  103. save_config();
  104. tzset();
  105. config_changed = false;
  106. }
  107. }
  108. /*
  109. * At the moment "url" just allows values to be separated by semicolons;
  110. * no other decoding is done, and '&' is not supported.
  111. */
  112. int set_config_url_string(const char *str)
  113. {
  114. char *wstr = strdup(str);
  115. if (!wstr)
  116. return -ENOMEM;
  117. int err = set_config_string(wstr, ';');
  118. free(wstr);
  119. finish_config_update(true);
  120. return err;
  121. }
  122. static void skip_rest_of_line(FILE *f)
  123. {
  124. int c;
  125. do {
  126. c = getc(f);
  127. } while (!is_end_of_string(c));
  128. }
  129. /* Calling with with f == NULL just finalizes the update */
  130. int read_config(FILE *f, bool save)
  131. {
  132. char *linebuf = NULL;
  133. int err = -1;
  134. if (f) {
  135. linebuf = malloc(MAX_CONFIG_LINE);
  136. if (!linebuf) {
  137. err = -ENOMEM;
  138. goto exit;
  139. }
  140. while (fgets(linebuf, MAX_CONFIG_LINE, f)) {
  141. if (set_config_string(linebuf, -1) == -EOVERFLOW)
  142. skip_rest_of_line(f);
  143. }
  144. }
  145. err = 0;
  146. if (linebuf)
  147. free(linebuf);
  148. exit:
  149. finish_config_update(save);
  150. return err;
  151. };
  152. int write_env(FILE *f, bool status)
  153. {
  154. size_t skip = status ? 7 : 0;
  155. for (char **var = environ; *var; var++) {
  156. if (!strncmp(*var, "status.", 7) == status) {
  157. fputs(*var + skip, f);
  158. putc('\n', f);
  159. }
  160. }
  161. return ferror(f) ? -1 : 0;
  162. }
  163. static int save_config(void)
  164. {
  165. int err = -ENOENT;
  166. FILE *f = fopen(CONFIG_FILE, "w");
  167. if (f) {
  168. err = write_env(f, false);
  169. fclose(f);
  170. }
  171. if (err)
  172. printf("[CONF] Failed to save configuration (error %d)\n", err);
  173. return err;
  174. }
  175. static const esp_vfs_spiffs_conf_t spiffs_conf = {
  176. .base_path = "/spiffs",
  177. .partition_label = NULL,
  178. .max_files = 4,
  179. .format_if_mount_failed = true
  180. };
  181. void init_config(void)
  182. {
  183. if (!esp_spiffs_mounted(spiffs_conf.partition_label)) {
  184. esp_err_t err;
  185. err = esp_vfs_spiffs_register(&spiffs_conf);
  186. if (err)
  187. printf("[CONF] Failed to mount %s (error 0x%x)\n",
  188. spiffs_conf.base_path, err);
  189. }
  190. reset_config();
  191. FILE *f = fopen(CONFIG_FILE, "r");
  192. if (!f)
  193. printf("[CONF] No configuration file found, using defaults\n");
  194. read_config(f, false);
  195. if (f)
  196. fclose(f);
  197. }
  198. const char *getenv_def(const char *var, const char *def)
  199. {
  200. const char *val = getenv(var);
  201. return val ? val : def;
  202. }
  203. long getenv_l(const char *var, long def)
  204. {
  205. const char *ep;
  206. var = getenv(var);
  207. if (!var || !*var)
  208. return def;
  209. long val = strtol(var, (char **)&ep, 0);
  210. return *ep ? def : val;
  211. }
  212. void setenv_l(const char *var, long val)
  213. {
  214. char vbuf[2+3*sizeof val];
  215. snprintf(vbuf, sizeof vbuf, "%ld", val);
  216. setenv_cond(var, vbuf);
  217. }
  218. unsigned long getenv_ul(const char *var, unsigned long def)
  219. {
  220. const char *ep;
  221. var = getenv(var);
  222. if (!var || !*var)
  223. return def;
  224. unsigned long val = strtol(var, (char **)&ep, 0);
  225. return *ep ? def : val;
  226. }
  227. void setenv_ul(const char *var, unsigned long val)
  228. {
  229. char vbuf[2+3*sizeof val];
  230. snprintf(vbuf, sizeof vbuf, "%lu", val);
  231. setenv_cond(var, vbuf);
  232. }
  233. bool getenv_bool(const char *var)
  234. {
  235. var = getenv(var);
  236. if (!var)
  237. return false;
  238. unsigned char c = *var;
  239. unsigned char cl = c | 0x20;
  240. return !(!c || c == '0' || cl == 'f' || cl == 'n' || cl == 'd' ||
  241. (cl == 'o' && (var[1] | 0x20) == 'f'));
  242. }
  243. void setenv_bool(const char *var, bool val)
  244. {
  245. return setenv_ul(var, val);
  246. }