config.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. static int save_config(void);
  7. static bool is_end_of_string(int c)
  8. {
  9. return c <= 0 || c == '\n' || c == '\r';
  10. }
  11. /*
  12. * Note: the string must be mutable; use strdup() if necessary.
  13. * The "separator" allows the string to be separated into multiple
  14. * arguments; no other decoding is done.
  15. */
  16. static int set_config_string(char *str, unsigned int separator)
  17. {
  18. char *p, *q;
  19. unsigned char c;
  20. p = str;
  21. do {
  22. const char *var = p;
  23. do {
  24. c = *p++;
  25. } while (isalnum(c) || c == '.' || c == '-');
  26. if (c != '=')
  27. return -EINVAL; /* Invalid config line (blank, comment...) */
  28. p[-1] = '\0';
  29. q = p;
  30. do {
  31. c = *q++;
  32. } while (!is_end_of_string(c) && c != separator);
  33. /* Overlong line */
  34. if (q >= str + MAX_CONFIG_LINE)
  35. return -EOVERFLOW;
  36. q[-1] = '\0';
  37. size_t nvar = sysvar_find(sysvar_ns_config, var);
  38. if (nvar) {
  39. setvar_fromstr(nvar, p);
  40. }
  41. p = q;
  42. } while (c == separator);
  43. return 0;
  44. }
  45. static void finish_config_update(bool save)
  46. {
  47. if (sysvar_changed < sysvar_ns[sysvar_ns_status].first) {
  48. if (save)
  49. save_config();
  50. /* Can do other things here... */
  51. }
  52. sysvar_changed = sysvar_count;
  53. }
  54. /*
  55. * At the moment "url" just allows values to be separated by semicolons;
  56. * no other decoding is done, and '&' is not supported.
  57. */
  58. int set_config_url_string(const char *str)
  59. {
  60. char *wstr = strdup(str);
  61. if (!wstr)
  62. return -ENOMEM;
  63. int err = set_config_string(wstr, ';');
  64. free(wstr);
  65. finish_config_update(true);
  66. return err;
  67. }
  68. static void skip_rest_of_line(FILE *f)
  69. {
  70. int c;
  71. do {
  72. c = getc(f);
  73. } while (!is_end_of_string(c));
  74. }
  75. /* Calling with with f == NULL just finalizes the update */
  76. int read_config(FILE *f, bool save)
  77. {
  78. char *linebuf = NULL;
  79. int err = -1;
  80. if (f) {
  81. linebuf = malloc(MAX_CONFIG_LINE);
  82. if (!linebuf) {
  83. err = -ENOMEM;
  84. goto exit;
  85. }
  86. while (fgets(linebuf, MAX_CONFIG_LINE, f)) {
  87. if (set_config_string(linebuf, -1) == -EOVERFLOW)
  88. skip_rest_of_line(f);
  89. }
  90. }
  91. err = 0;
  92. if (linebuf)
  93. free(linebuf);
  94. exit:
  95. finish_config_update(save);
  96. return err;
  97. };
  98. int write_sysvars(FILE *f, bool status)
  99. {
  100. size_t ns = status ? sysvar_ns_status : sysvar_ns_config;
  101. const sysvar_ns_t * const nsi = &sysvar_ns[ns];
  102. for (enum sysvar_enum var = nsi->first; var < nsi[1].first; var++) {
  103. fputs(sysvar_name[var], f);
  104. putc('=', f);
  105. fputs(notempty(getvar_tostr(var)), f);
  106. putc('\n', f);
  107. }
  108. return ferror(f) ? -1 : 0;
  109. }
  110. static int save_config(void)
  111. {
  112. int err = -ENOENT;
  113. FILE *f = fopen(CONFIG_FILE, "w");
  114. if (f) {
  115. err = write_sysvars(f, false);
  116. fclose(f);
  117. }
  118. if (err)
  119. printf("[CONF] Failed to save configuration (error %d)\n", err);
  120. return err;
  121. }
  122. static const esp_vfs_spiffs_conf_t spiffs_conf = {
  123. .base_path = "/spiffs",
  124. .partition_label = NULL,
  125. .max_files = 4,
  126. .format_if_mount_failed = true
  127. };
  128. void init_config(void)
  129. {
  130. if (!esp_spiffs_mounted(spiffs_conf.partition_label)) {
  131. esp_err_t err;
  132. err = esp_vfs_spiffs_register(&spiffs_conf);
  133. if (err)
  134. printf("[CONF] Failed to mount %s (error 0x%x)\n",
  135. spiffs_conf.base_path, err);
  136. }
  137. sysvar_reset(sysvar_ns_config);
  138. FILE *f = fopen(CONFIG_FILE, "r");
  139. if (!f)
  140. printf("[CONF] No configuration file found, using defaults\n");
  141. read_config(f, false);
  142. if (f)
  143. fclose(f);
  144. }
  145. void log_config_status(void)
  146. {
  147. const sysvar_ns_t *nsi = &sysvar_ns[0];
  148. for (enum sysvar_enum var = 1; var < sysvar_count; var++) {
  149. if (var >= nsi[1].first)
  150. nsi++;
  151. logmsg(nsi->name, "%s = %s\n", sysvar_name[var],
  152. notempty(getvar_tostr(var)));
  153. }
  154. }