Tomato 1.24
[tomato.git] / release / src / router / nvram / nvram.c
blob3593c2052fc32226cb5dd11938bf8e3cb2d9cede
1 /*
3 NVRAM Utility
4 Copyright (C) 2006-2009 Jonathan Zarate
6 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <sys/wait.h>
15 #include <bcmdevs.h>
16 #include <bcmnvram.h>
17 #include <utils.h>
18 #include <shutils.h>
19 #include <shared.h>
21 #include "nvram_convert.h"
22 #include "defaults.h"
25 __attribute__ ((noreturn))
26 static void help(void)
28 printf(
29 "NVRAM Utility\n"
30 "Copyright (C) 2006-2009 Jonathan Zarate\n\n"
31 "Usage: nvram set <key=value> | get <key> | unset <key> | "
32 "ren <key> <key> | commit | show [--nosort|--nostat] | "
33 "find <text> | defaults <--yes|--initcheck> | backup <filename> | "
34 "restore <filename> [--test] [--force] [--forceall] [--nocommit] | "
35 "export <--quote|--c|--dump|--dump0|--set|--tab> | "
36 "import [--forceall] | "
37 "setfb64 <key> <filename> | getfb64 <key> <filename>"
38 // "test"
39 "\n");
40 exit(1);
43 static void getall(char *buffer)
45 if (nvram_getall(buffer, NVRAM_SPACE) != 0) {
46 fprintf(stderr, "Error reading NVRAM\n");
47 exit(1);
51 static int set_main(int argc, char **argv)
53 char *b, *p;
55 if ((b = strdup(argv[1])) == NULL) {
56 fprintf(stderr, "Not enough memory");
57 return 1;
59 if ((p = strchr(b, '=')) != NULL) {
60 *p = 0;
61 nvram_set(b, p + 1);
62 return 0;
64 help();
67 static int get_main(int argc, char **argv)
69 char *p;
71 if ((p = nvram_get(argv[1])) != NULL) {
72 puts(p);
73 return 0;
75 return 1;
78 static int unset_main(int argc, char **argv)
80 nvram_unset(argv[1]);
81 return 0;
84 static int ren_main(int argc, char **argv)
86 char *p;
88 if ((p = nvram_get(argv[1])) == NULL) {
89 fprintf(stderr, "Unable to find %s\n", argv[1]);
90 return 1;
92 if (strcmp(argv[1], argv[2]) != 0) {
93 nvram_set(argv[2], p);
94 nvram_unset(argv[1]);
96 return 0;
99 static int show_main(int argc, char **argv)
101 char *p, *q;
102 char buffer[NVRAM_SPACE];
103 int n;
104 int count;
105 int show = 1;
106 int stat = 1;
107 int sort = 1;
109 for (n = 1; n < argc; ++n) {
110 if (strcmp(argv[n], "--nostat") == 0) stat = 0;
111 else if (strcmp(argv[n], "--nosort") == 0) sort = 0;
112 else help();
115 if (sort) {
116 system("nvram show --nostat --nosort | sort"); // smallest and easiest way :)
117 show = 0;
120 getall(buffer);
121 count = 0;
122 for (p = buffer; *p; p += strlen(p) + 1) {
123 q = p;
124 while (*q) {
125 if (!isprint(*q)) *q = ' ';
126 ++q;
128 if (show) puts(p);
129 ++count;
131 if (stat) {
132 n = sizeof(struct nvram_header) + (p - buffer);
133 printf("---\n%d entries, %d bytes used, %d bytes free.\n", count, n, NVRAM_SPACE - n);
135 return 0;
138 static int find_main(int argc, char **argv)
140 char cmd[512];
141 int r;
143 snprintf(cmd, sizeof(cmd), "nvram show --nostat --nosort | sort | grep \"%s\"", argv[1]);
144 r = system(cmd);
145 return (r == -1) ? 1 : WEXITSTATUS(r);
148 static int defaults_main(int argc, char **argv)
150 const defaults_t *t;
151 char *p;
152 char s[256];
153 int i;
154 int force = 0;
155 int commit = 0;
157 if (strcmp(argv[1], "--yes") == 0) {
158 force = 1;
160 else if (strcmp(argv[1], "--initcheck") != 0) {
161 help();
164 if ((!nvram_match("restore_defaults", "0")) || (!nvram_match("os_name", "linux"))) {
165 force = 1;
168 #if 0 // --need to test--
169 // prevent lockout if upgrading from DD-WRT v23 SP2+ w/ encrypted password
170 if (nvram_match("nvram_ver", "2")) {
171 nvram_unset("nvram_ver");
173 // If username is "", root or admin, then nvram_ver was probably a left-over
174 // from an old DD-WRT installation (ex: DD-WRT -> Linksys (reset) ->
175 // Tomato) and we don't need to do anything. Otherwise, reset.
176 if ((p = nvram_get("httpd_username")) != NULL) {
177 if ((*p != 0) && (strcmp(p, "root") != 0) && (strcmp(p, "admin") != 0)) {
178 nvram_unset("httpd_passwd");
179 nvram_unset("httpd_username"); // not used here, but dd will try to re-encrypt this
180 // filled below
184 #else
185 if (force) nvram_unset("nvram_ver"); // prep to prevent problems later
186 #endif
189 for (t = defaults; t->key; t++) {
190 if (((p = nvram_get(t->key)) == NULL) || (force)) {
191 if (t->value == NULL) {
192 if (p != NULL) {
193 nvram_unset(t->key);
194 commit = 1;
197 else {
198 nvram_set(t->key, t->value);
199 commit = 1;
202 else if (strncmp(t->key, "wl_", 3) == 0) {
203 // sync wl_ and wl0_
204 strcpy(s, "wl0_");
205 strcat(s, t->key + 3);
206 if (nvram_get(s) == NULL) nvram_set(s, nvram_safe_get(t->key));
210 // todo: moveme
211 if ((strtoul(nvram_safe_get("boardflags"), NULL, 0) & BFL_ENETVLAN) ||
212 (check_hw_type() == HW_BCM4712)) t = if_vlan;
213 else t = if_generic;
214 for (; t->key; t++) {
215 if (((p = nvram_get(t->key)) == NULL) || (*p == 0) || (force)) {
216 nvram_set(t->key, t->value);
217 commit = 1;
218 // if (!force) cprintf("SET %s=%s\n", t->key, t->value);
222 if (force) {
223 for (i = 0; i < 20; i++) {
224 sprintf(s, "wl0_wds%d", i);
225 nvram_unset(s);
227 for (i = 0; i < LED_COUNT; ++i) {
228 sprintf(s, "led_%s", led_names[i]);
229 nvram_unset(s);
232 // 0 = example
233 for (i = 1; i < 50; i++) {
234 sprintf(s, "rrule%d", i);
235 nvram_unset(s);
239 if (!nvram_match("boot_wait", "on")) {
240 nvram_set("boot_wait", "on");
241 commit = 1;
244 nvram_set("os_name", "linux");
245 nvram_set("os_version", tomato_version);
246 nvram_set("os_date", tomato_buildtime);
248 /* if (nvram_match("dirty", "1")) {
249 nvram_unset("dirty");
250 commit = 1;
253 if ((commit) || (force)) {
254 printf("Saving...\n");
255 nvram_commit();
257 else {
258 printf("No change was necessary.\n");
260 return 0;
263 static int commit_main(int argc, char **argv)
265 int r;
267 printf("Commit... ");
268 fflush(stdout);
269 r = nvram_commit();
270 printf("done.\n");
271 return r ? 1 : 0;
274 #define X_QUOTE 0
275 #define X_SET 1
276 #define X_C 2
277 #define X_TAB 3
279 static int export_main(int argc, char **argv)
281 char *p;
282 char buffer[NVRAM_SPACE];
283 int eq;
284 int mode;
286 // C, set, quote
287 static const char *start[4] = { "\"", "nvram set \"", "{ \"", "" };
288 static const char *stop[4] = { "\"", "\"", "\" },", "" };
291 getall(buffer);
292 p = buffer;
294 if (strcmp(argv[1], "--dump") == 0) {
295 for (p = buffer; *p; p += strlen(p) + 1) {
296 puts(p);
298 return 0;
300 if (strcmp(argv[1], "--dump0") == 0) {
301 for (p = buffer; *p; p += strlen(p) + 1) { }
302 fwrite(buffer, p - buffer, 1, stdout);
303 return 0;
306 if (strcmp(argv[1], "--c") == 0) mode = X_C;
307 else if (strcmp(argv[1], "--set") == 0) mode = X_SET;
308 else if (strcmp(argv[1], "--tab") == 0) mode = X_TAB;
309 else if (strcmp(argv[1], "--quote") == 0) mode = X_QUOTE;
310 else help();
312 while (*p) {
313 eq = 0;
314 printf("%s", start[mode]);
315 do {
316 switch (*p) {
317 case 9:
318 printf("\\t");
319 break;
320 case 13:
321 printf("\\r");
322 break;
323 case 10:
324 printf("\\n");
325 break;
326 case '"':
327 case '\\':
328 printf("\\%c", *p);
329 break;
330 case '=':
331 if ((eq == 0) && (mode > X_SET)) {
332 printf((mode == X_C) ? "\", \"" : "\t");
333 break;
335 eq = 1;
336 default:
337 if (!isprint(*p)) printf("\\x%02x", *p);
338 else putchar(*p);
339 break;
341 ++p;
342 } while (*p);
343 printf("%s\n", stop[mode]);
344 ++p;
346 return 0;
349 static int in_defaults(const char *key)
351 const defaults_t *t;
352 int n;
354 for (t = defaults; t->key; t++) {
355 if (strcmp(t->key, key) == 0) return 1;
358 if ((strncmp(key, "rrule", 5) == 0) && ((n = atoi(key + 5)) > 0) && (n < 50)) return 1;
360 return 0;
363 static int import_main(int argc, char **argv)
365 FILE *f;
366 char s[10240];
367 int n;
368 char *k, *v;
369 char *p, *q;
370 int all;
371 int same, skip, set;
373 all = 0;
374 if (strcmp(argv[1], "--forceall") == 0) {
375 all = 1;
376 ++argv;
379 if ((f = fopen(argv[1], "r")) == NULL) {
380 printf("Error opening file.\n");
381 return 1;
384 same = skip = set = 0;
386 while (fgets(s, sizeof(s), f) != NULL) {
387 n = strlen(s);
388 while ((--n > 0) && (isspace(s[n]))) ;
389 if ((n <= 0) || (s[n] != '"')) continue;
390 s[n] = 0;
392 k = s;
393 while (isspace(*k)) ++k;
394 if (*k != '"') continue;
395 ++k;
397 if ((v = strchr(k, '=')) == NULL) continue;
398 *v++ = 0;
400 p = q = v;
401 while (*p) {
402 if (*p == '\\') {
403 ++p;
404 switch (*p) {
405 case 't':
406 *q++ = '\t';
407 break;
408 case 'r':
409 *q++ = '\n';
410 break;
411 case 'n':
412 *q++ = '\n';
413 break;
414 case '\\':
415 case '"':
416 *q++ = *p;
417 break;
418 default:
419 printf("Error unescaping %s=%s\n", k, v);
420 return 1;
423 else {
424 *q++ = *p;
426 ++p;
428 *q = 0;
430 if ((all) || (in_defaults(k))) {
431 if (nvram_match(k, v)) {
432 ++same;
433 // printf("SAME: %s=%s\n", k, v);
435 else {
436 ++set;
437 printf("%s=%s\n", k, v);
438 nvram_set(k, v);
441 else {
442 ++skip;
443 // printf("SKIP: %s=%s\n", k, v);
447 fclose(f);
449 printf("---\n%d skipped, %d same, %d set\n", skip, same, set);
450 return 0;
453 #define V1 0x31464354L
455 typedef struct {
456 unsigned long sig;
457 unsigned long hwid;
458 char buffer[NVRAM_SPACE];
459 } backup_t;
461 static int backup_main(int argc, char **argv)
463 backup_t data;
464 unsigned int size;
465 char *p;
466 char s[512];
467 char tmp[128];
468 int r;
470 getall(data.buffer);
472 data.sig = V1;
473 data.hwid = check_hw_type();
475 p = data.buffer;
476 while (*p != 0) p += strlen(p) + 1;
478 size = (sizeof(data) - sizeof(data.buffer)) + (p - data.buffer) + 1;
480 strcpy(tmp, "/tmp/nvramXXXXXX");
481 mktemp(tmp);
482 if (f_write(tmp, &data, size, 0, 0) != size) {
483 printf("Error saving file.\n");
484 return 1;
486 sprintf(s, "gzip < %s > %s", tmp, argv[1]);
487 r = system(s);
488 unlink(tmp);
490 if (r != 0) {
491 unlink(argv[1]);
492 printf("Error compressing file.\n");
493 return 1;
496 printf("Saved.\n");
497 return 0;
500 static int restore_main(int argc, char **argv)
502 char *name;
503 int test;
504 int force;
505 int commit;
506 backup_t data;
507 unsigned int size;
508 char s[512];
509 char tmp[128];
510 unsigned long hw;
511 char current[NVRAM_SPACE];
512 char *b, *bk, *bv;
513 char *c, *ck, *cv;
514 int nset;
515 int nunset;
516 int nsame;
517 int cmp;
518 int i;
520 test = 0;
521 force = 0;
522 commit = 1;
523 name = NULL;
524 for (i = 1; i < argc; ++i) {
525 if (argv[i][0] == '-') {
526 if (strcmp(argv[i], "--test") == 0) {
527 test = 1;
529 else if (strcmp(argv[i], "--force") == 0) {
530 force = 1;
532 else if (strcmp(argv[i], "--forceall") == 0) {
533 force = 2;
535 else if (strcmp(argv[i], "--nocommit") == 0) {
536 commit = 0;
538 else {
539 help();
542 else {
543 name = argv[i];
546 if (!name) help();
548 strcpy(tmp, "/tmp/nvramXXXXXX");
549 mktemp(tmp);
550 sprintf(s, "gzip -d < %s > %s", name, tmp);
551 if (system(s) != 0) {
552 unlink(tmp);
553 printf("Error decompressing file.\n");
554 return 1;
557 size = f_size(tmp);
558 if ((size <= (sizeof(data) - sizeof(data.buffer))) || (size > sizeof(data)) ||
559 (f_read(tmp, &data, sizeof(data)) != size)) {
560 unlink(tmp);
561 printf("Invalid data size or read error.\n");
562 return 1;
565 unlink(tmp);
567 if (data.sig != V1) {
568 printf("Invalid signature: %08lX / %08lX\n", data.sig, V1);
569 return 1;
572 hw = check_hw_type();
573 if ((data.hwid != hw) && (!force)) {
574 printf("Invalid hardware type: %08lX / %08lX\n", data.hwid, hw);
575 return 1;
578 // 1 - check data
580 size -= sizeof(data) - sizeof(data.buffer);
581 if ((data.buffer[size - 1] != 0) || (data.buffer[size - 2] != 0)) {
582 CORRUPT:
583 printf("Corrupted data area.\n");
584 return 1;
587 b = data.buffer;
588 while (*b) {
589 bk = b;
590 b += strlen(b) + 1;
591 if ((bv = strchr(bk, '=')) == NULL) {
592 goto CORRUPT;
594 *bv = 0;
595 if (strcmp(bk, "et0macaddr") == 0) {
596 if (!nvram_match(bk, bv + 1)) {
597 if (!force) {
598 printf("Cannot restore on a different router.\n");
599 return 1;
603 *bv = '=';
605 if (((b - data.buffer) + 1) != size) {
606 printf("Extra data found at the end.\n");
607 return 1;
611 // 2 - set
613 if (!test) {
614 if (!wait_action_idle(10)) {
615 printf("System busy.\n");
616 return 1;
618 set_action(ACT_SW_RESTORE);
619 led(LED_DIAG, 1);
622 nset = nunset = nsame = 0;
624 b = data.buffer;
625 while (*b) {
626 bk = b;
627 b += strlen(b) + 1;
628 bv = strchr(bk, '=');
629 *bv++ = 0;
631 if ((force != 1) || (in_defaults(bk))) {
632 if (!nvram_match(bk, bv)) {
633 if (test) printf("nvram set \"%s=%s\"\n", bk, bv);
634 else nvram_set(bk, bv);
635 ++nset;
637 else {
638 ++nsame;
642 *(bv - 1) = '=';
646 // 3 - unset
648 getall(current);
649 c = current;
650 while (*c) {
651 ck = c;
652 c += strlen(c) + 1;
653 if ((cv = strchr(ck, '=')) == NULL) {
654 printf("Invalid data in NVRAM: %s.", ck);
655 continue;
657 *cv++ = 0;
659 if ((force != 1) || (in_defaults(ck))) {
660 cmp = 1;
661 b = data.buffer;
662 while (*b) {
663 bk = b;
664 b += strlen(b) + 1;
665 bv = strchr(bk, '=');
666 *bv++ = 0;
667 cmp = strcmp(bk, ck);
668 *(bv - 1) = '=';
669 if (cmp == 0) break;
671 if (cmp != 0) {
672 ++nunset;
673 if (test) printf("nvram unset \"%s\"\n", ck);
674 else nvram_unset(ck);
680 if ((nset == 0) && (nunset == 0)) commit = 0;
681 printf("\nPerformed %d set and %d unset operations. %d required no changes.\n%s\n",
682 nset, nunset, nsame, commit ? "Committing..." : "Not commiting.");
683 fflush(stdout);
685 if (!test) {
686 set_action(ACT_IDLE);
687 if (commit) nvram_commit();
689 return 0;
692 #if 0
693 static int test_main(int argc, char **argv)
696 static const char *extra[] = {
697 "clkfreq", "pa0b0", "pa0b1", "pa0b2", "pa0itssit", "pa0maxpwr",
698 "sdram_config", "sdram_init", "sdram_ncdl", "vlan0ports", NULL };
699 const char **x;
701 char buffer[NVRAM_SPACE];
702 char *k, *v, *e;
703 const defaults_t *rest;
704 struct nvram_convert *conv;
706 printf("Unknown keys:\n");
708 getall(buffer);
709 k = buffer;
710 while (*k) {
711 e = k + strlen(k) + 1;
712 if ((v = strchr(k, '=')) != NULL) {
713 *v = 0;
714 for (rest = defaults; rest->key; ++rest) {
715 if (strcmp(k, rest->key) == 0) break;
717 if (rest->key == NULL) {
718 for (conv = nvram_converts; conv->name; ++conv) {
719 if ((strcmp(k, conv->name) == 0) || (strcmp(k, conv->wl0_name) == 0)) break;
721 if (conv->name == NULL) {
722 printf("%s=%s\n", k, v + 1);
724 for (x = extra; *x; ++x) {
725 if (strcmp(k, *x) == 0) break;
727 if (*x == NULL) {
728 printf("nvram unset \"%s\"\n", k);
734 else {
735 printf("WARNING: '%s' doesn't have a '=' delimiter\n", k);
737 k = e;
740 return 0;
742 #endif
745 static int setfb64_main(int argc, char **argv)
747 if (!nvram_set_file(argv[1], argv[2], 10240)) {
748 fprintf(stderr, "Unable to set %s or read %s\n", argv[1], argv[2]);
749 return 1;
751 return 0;
754 static int getfb64_main(int argc, char **argv)
756 if (!nvram_get_file(argv[1], argv[2], 10240)) {
757 fprintf(stderr, "Unable to get %s or write %s\n", argv[1], argv[2]);
758 return 1;
760 return 0;
763 // -----------------------------------------------------------------------------
765 typedef struct {
766 const char *name;
767 int args;
768 int (*main)(int argc, char *argv[]);
769 } applets_t;
771 static const applets_t applets[] = {
772 { "set", 3, set_main },
773 { "get", 3, get_main },
774 { "unset", 3, unset_main },
775 { "ren", 4, ren_main },
776 { "show", -2, show_main },
777 { "commit", 2, commit_main },
778 { "find", 3, find_main },
779 { "export", 3, export_main },
780 { "import", -3, import_main },
781 { "defaults", 3, defaults_main },
782 { "backup", 3, backup_main },
783 { "restore", -3, restore_main },
784 { "setfb64", 4, setfb64_main },
785 { "getfb64", 4, getfb64_main },
786 // { "test", 2, test_main },
787 { NULL, 0, NULL }
790 int main(int argc, char **argv)
792 const applets_t *a;
794 if (argc >= 2) {
795 a = applets;
796 while (a->name) {
797 if (strcmp(argv[1], a->name) == 0) {
798 if ((argc != a->args) && ((a->args > 0) || (argc < -(a->args)))) help();
799 return a->main(argc - 1, argv + 1);
801 ++a;
804 help();