broadcom-wl: fix memory corruption and leakage on remove (patch from #9576 by George...
[openwrt.git] / package / rbcfg / src / main.c
1 /*
2 * RouterBOOT configuration utility
3 *
4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 *
10 */
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <linux/limits.h>
21
22 #include "rbcfg.h"
23 #include "cyg_crc.h"
24
25 #define RBCFG_TMP_FILE "/tmp/.rbcfg"
26 #define RBCFG_MTD_NAME "soft_config"
27
28 #define RB_ERR_NOTFOUND 1
29 #define RB_ERR_INVALID 2
30 #define RB_ERR_NOMEM 3
31 #define RB_ERR_IO 4
32
33 #define ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0]))
34
35 struct rbcfg_ctx {
36 char *mtd_device;
37 char *tmp_file;
38 char *buf;
39 unsigned buflen;
40 };
41
42 struct rbcfg_value {
43 const char *name;
44 const char *desc;
45 union {
46 uint32_t u32;
47 const char *raw;
48 } val;
49 };
50
51 #define RBCFG_ENV_TYPE_U32 0
52
53 struct rbcfg_env {
54 const char *name;
55 int type;
56 uint16_t id;
57 const struct rbcfg_value *values;
58 int num_values;
59 };
60
61 #define CMD_FLAG_USES_CFG 0x01
62
63 struct rbcfg_command {
64 const char *command;
65 const char *usage;
66 int flags;
67 int (*exec)(int argc, const char *argv[]);
68 };
69
70 static void usage(void);
71
72 /* Globals */
73
74 static struct rbcfg_ctx *rbcfg_ctx;
75 static char *rbcfg_name;
76
77 #define CFG_U32(_name, _desc, _val) { \
78 .name = (_name), \
79 .desc = (_desc), \
80 .val.u32 = (_val), \
81 }
82
83 static const struct rbcfg_value rbcfg_boot_delay[] = {
84 CFG_U32("1", "1 second", RB_BOOT_DELAY_1SEC),
85 CFG_U32("2", "2 seconds", RB_BOOT_DELAY_2SEC),
86 CFG_U32("3", "3 seconds", RB_BOOT_DELAY_3SEC),
87 CFG_U32("4", "4 seconds", RB_BOOT_DELAY_4SEC),
88 CFG_U32("5", "5 seconds", RB_BOOT_DELAY_5SEC),
89 CFG_U32("6", "6 seconds", RB_BOOT_DELAY_6SEC),
90 CFG_U32("7", "7 seconds", RB_BOOT_DELAY_7SEC),
91 CFG_U32("8", "8 seconds", RB_BOOT_DELAY_8SEC),
92 CFG_U32("9", "9 seconds", RB_BOOT_DELAY_9SEC),
93 };
94
95 static const struct rbcfg_value rbcfg_boot_device[] = {
96 CFG_U32("eth", "boot over Ethernet",
97 RB_BOOT_DEVICE_ETHER),
98 CFG_U32("nandeth", "boot from NAND, if fail then Ethernet",
99 RB_BOOT_DEVICE_NANDETH),
100 CFG_U32("ethnand", "boot Ethernet once, then NAND",
101 RB_BOOT_DEVICE_ETHONCE),
102 CFG_U32("nand", "boot from NAND only",
103 RB_BOOT_DEVICE_NANDONLY),
104 };
105
106 static const struct rbcfg_value rbcfg_boot_key[] = {
107 CFG_U32("any", "any key", RB_BOOT_KEY_ANY),
108 CFG_U32("del", "<Delete> key only", RB_BOOT_KEY_DEL),
109 };
110
111 static const struct rbcfg_value rbcfg_boot_protocol[] = {
112 CFG_U32("bootp", "BOOTP protocol", RB_BOOT_PROTOCOL_BOOTP),
113 CFG_U32("dhcp", "DHCP protocol", RB_BOOT_PROTOCOL_DHCP),
114 };
115
116 static const struct rbcfg_value rbcfg_uart_speed[] = {
117 CFG_U32("115200", "", RB_UART_SPEED_115200),
118 CFG_U32("57600", "", RB_UART_SPEED_57600),
119 CFG_U32("38400", "", RB_UART_SPEED_38400),
120 CFG_U32("19200", "", RB_UART_SPEED_19200),
121 CFG_U32("9600", "", RB_UART_SPEED_9600),
122 CFG_U32("4800", "", RB_UART_SPEED_4800),
123 CFG_U32("2400", "", RB_UART_SPEED_2400),
124 CFG_U32("1200", "", RB_UART_SPEED_1200),
125 CFG_U32("off", "disable console output", RB_UART_SPEED_OFF),
126 };
127
128 static const struct rbcfg_value rbcfg_cpu_mode[] = {
129 CFG_U32("powersave", "power save", RB_CPU_MODE_POWERSAVE),
130 CFG_U32("regular", "regular (better for -0c environment)",
131 RB_CPU_MODE_REGULAR),
132 };
133
134 static const struct rbcfg_value rbcfg_booter[] = {
135 CFG_U32("regular", "load regular booter", RB_BOOTER_REGULAR),
136 CFG_U32("backup", "force backup-booter loading", RB_BOOTER_BACKUP),
137 };
138
139 static const struct rbcfg_env rbcfg_envs[] = {
140 {
141 .name = "boot_delay",
142 .id = RB_ID_BOOT_DELAY,
143 .type = RBCFG_ENV_TYPE_U32,
144 .values = rbcfg_boot_delay,
145 .num_values = ARRAY_SIZE(rbcfg_boot_delay),
146 }, {
147 .name = "boot_device",
148 .id = RB_ID_BOOT_DEVICE,
149 .type = RBCFG_ENV_TYPE_U32,
150 .values = rbcfg_boot_device,
151 .num_values = ARRAY_SIZE(rbcfg_boot_device),
152 }, {
153 .name = "boot_key",
154 .id = RB_ID_BOOT_KEY,
155 .type = RBCFG_ENV_TYPE_U32,
156 .values = rbcfg_boot_key,
157 .num_values = ARRAY_SIZE(rbcfg_boot_key),
158 }, {
159 .name = "boot_protocol",
160 .id = RB_ID_BOOT_PROTOCOL,
161 .type = RBCFG_ENV_TYPE_U32,
162 .values = rbcfg_boot_protocol,
163 .num_values = ARRAY_SIZE(rbcfg_boot_protocol),
164 }, {
165 .name = "booter",
166 .id = RB_ID_BOOTER,
167 .type = RBCFG_ENV_TYPE_U32,
168 .values = rbcfg_booter,
169 .num_values = ARRAY_SIZE(rbcfg_booter),
170 }, {
171 .name = "cpu_mode",
172 .id = RB_ID_CPU_MODE,
173 .type = RBCFG_ENV_TYPE_U32,
174 .values = rbcfg_cpu_mode,
175 .num_values = ARRAY_SIZE(rbcfg_cpu_mode),
176 }, {
177 .name = "uart_speed",
178 .id = RB_ID_UART_SPEED,
179 .type = RBCFG_ENV_TYPE_U32,
180 .values = rbcfg_uart_speed,
181 .num_values = ARRAY_SIZE(rbcfg_uart_speed),
182 }
183 };
184
185 static inline uint16_t
186 get_u16(const void *buf)
187 {
188 const uint8_t *p = buf;
189
190 return ((uint16_t) p[1] + ((uint16_t) p[0] << 8));
191 }
192
193 static inline uint32_t
194 get_u32(const void *buf)
195 {
196 const uint8_t *p = buf;
197
198 return ((uint32_t) p[3] + ((uint32_t) p[2] << 8) +
199 ((uint32_t) p[1] << 16) + ((uint32_t) p[0] << 24));
200 }
201
202 static inline void
203 put_u32(void *buf, uint32_t val)
204 {
205 uint8_t *p = buf;
206
207 p[3] = val & 0xff;
208 p[2] = (val >> 8) & 0xff;
209 p[1] = (val >> 16) & 0xff;
210 p[0] = (val >> 24) & 0xff;
211 }
212
213 static int
214 rbcfg_find_tag(struct rbcfg_ctx *ctx, uint16_t tag_id, uint16_t *tag_len,
215 void **tag_data)
216 {
217 uint16_t id;
218 uint16_t len;
219 char *buf = ctx->buf;
220 unsigned int buflen = ctx->buflen;
221 int ret = RB_ERR_NOTFOUND;
222
223 /* skip magic and CRC value */
224 buf += 8;
225 buflen -= 8;
226
227 while (buflen > 2) {
228 len = get_u16(buf);
229 buf += 2;
230 buflen -= 2;
231
232 if (buflen < 2)
233 break;
234
235 id = get_u16(buf);
236 buf += 2;
237 buflen -= 2;
238
239 if (id == RB_ID_TERMINATOR)
240 break;
241
242 if (buflen < len)
243 break;
244
245 if (id == tag_id) {
246 *tag_len = len;
247 *tag_data = buf;
248 ret = 0;
249 break;
250 }
251
252 buf += len;
253 buflen -= len;
254 }
255
256 if (ret)
257 fprintf(stderr, "no tag found with id=%u\n", tag_id);
258
259 return ret;
260 }
261
262 static int
263 rbcfg_get_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t *val)
264 {
265 void *tag_data;
266 uint16_t tag_len;
267 int err;
268
269 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data);
270 if (err)
271 return err;
272
273 *val = get_u32(tag_data);
274 return 0;
275 }
276
277 static int
278 rbcfg_set_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t val)
279 {
280 void *tag_data;
281 uint16_t tag_len;
282 int err;
283
284 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data);
285 if (err)
286 return err;
287
288 put_u32(tag_data, val);
289 return 0;
290 }
291
292 char *rbcfg_find_mtd(const char *name, int *erase_size)
293 {
294 FILE *f;
295 int mtd_num;
296 char dev[PATH_MAX];
297 char *ret = NULL;
298 struct stat s;
299 int err;
300
301 f = fopen("/proc/mtd", "r");
302 if (!f)
303 return NULL;
304
305 while (1) {
306 char *p;
307 p = fgets(dev, sizeof(dev), f);
308 if (!p)
309 break;
310
311 if (!strstr(dev, name))
312 continue;
313
314 err = sscanf(dev, "mtd%d: %08x", &mtd_num, erase_size);
315 if (err != 2)
316 break;
317
318 sprintf(dev, "/dev/mtdblock%d", mtd_num);
319 err = stat(dev, &s);
320 if (err < 0)
321 break;
322
323 if ((s.st_mode & S_IFBLK) == 0)
324 break;
325
326 ret = malloc(strlen(dev) + 1);
327 if (ret == NULL)
328 break;
329
330 strncpy(ret, dev, strlen(dev) + 1);
331 break;
332 }
333
334 fclose(f);
335 return ret;
336 }
337
338 static int
339 rbcfg_check_tmp(struct rbcfg_ctx *ctx)
340 {
341 struct stat s;
342 int err;
343
344 err = stat(ctx->tmp_file, &s);
345 if (err < 0)
346 return 0;
347
348 if ((s.st_mode & S_IFREG) == 0)
349 return 0;
350
351 if (s.st_size != ctx->buflen)
352 return 0;
353
354 return 1;
355 }
356
357 static int
358 rbcfg_load(struct rbcfg_ctx *ctx)
359 {
360 uint32_t magic;
361 uint32_t crc_orig, crc;
362 char *name;
363 int tmp;
364 int fd;
365 int err;
366
367 tmp = rbcfg_check_tmp(ctx);
368 name = (tmp) ? ctx->tmp_file : ctx->mtd_device;
369
370 fd = open(name, O_RDONLY);
371 if (fd < 0) {
372 fprintf(stderr, "unable to open %s\n", name);
373 err = RB_ERR_IO;
374 goto err;
375 }
376
377 err = read(fd, ctx->buf, ctx->buflen);
378 if (err != ctx->buflen) {
379 fprintf(stderr, "unable to read from %s\n", name);
380 err = RB_ERR_IO;
381 goto err_close;
382 }
383
384 magic = get_u32(ctx->buf);
385 if (magic != RB_MAGIC_SOFT) {
386 fprintf(stderr, "invalid configuration\n");
387 err = RB_ERR_INVALID;
388 goto err_close;
389 }
390
391 crc_orig = get_u32(ctx->buf + 4);
392 put_u32(ctx->buf + 4, 0);
393 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen);
394 if (crc != crc_orig) {
395 fprintf(stderr, "configuration has CRC error\n");
396 err = RB_ERR_INVALID;
397 goto err_close;
398 }
399
400 err = 0;
401
402 err_close:
403 close(fd);
404 err:
405 return err;
406 }
407
408 static int
409 rbcfg_open()
410 {
411 char *mtd_device;
412 struct rbcfg_ctx *ctx;
413 int buflen;
414 int err;
415
416 mtd_device = rbcfg_find_mtd(RBCFG_MTD_NAME, &buflen);
417 if (!mtd_device) {
418 fprintf(stderr, "unable to find configuration\n");
419 return RB_ERR_NOTFOUND;
420 }
421
422 ctx = malloc(sizeof(struct rbcfg_ctx) + buflen);
423 if (ctx == NULL) {
424 err = RB_ERR_NOMEM;
425 goto err_free_mtd;
426 }
427
428 ctx->mtd_device = mtd_device;
429 ctx->tmp_file = RBCFG_TMP_FILE;
430 ctx->buflen = buflen;
431 ctx->buf = (char *) &ctx[1];
432
433 err = rbcfg_load(ctx);
434 if (err)
435 goto err_free_ctx;
436
437 rbcfg_ctx = ctx;
438 return 0;
439
440 err_free_ctx:
441 free(ctx);
442 err_free_mtd:
443 free(mtd_device);
444 return err;
445 }
446
447 static int
448 rbcfg_update(int tmp)
449 {
450 struct rbcfg_ctx *ctx = rbcfg_ctx;
451 char *name;
452 uint32_t crc;
453 int fd;
454 int err;
455
456 put_u32(ctx->buf, RB_MAGIC_SOFT);
457 put_u32(ctx->buf + 4, 0);
458 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen);
459 put_u32(ctx->buf + 4, crc);
460
461 name = (tmp) ? ctx->tmp_file : ctx->mtd_device;
462 fd = open(name, O_WRONLY | O_CREAT);
463 if (fd < 0) {
464 fprintf(stderr, "unable to open %s for writing\n", name);
465 err = RB_ERR_IO;
466 goto out;
467 }
468
469 err = write(fd, ctx->buf, ctx->buflen);
470 if (err != ctx->buflen) {
471 err = RB_ERR_IO;
472 goto out_close;
473 }
474
475 fsync(fd);
476 err = 0;
477
478 out_close:
479 close(fd);
480 out:
481 return err;
482 }
483
484 static void
485 rbcfg_close(void)
486 {
487 struct rbcfg_ctx *ctx;
488
489 ctx = rbcfg_ctx;
490 free(ctx->mtd_device);
491 free(ctx);
492 }
493
494 static const struct rbcfg_value *
495 rbcfg_env_find(const struct rbcfg_env *env, const char *name)
496 {
497 unsigned i;
498
499 for (i = 0; i < env->num_values; i++) {
500 const struct rbcfg_value *v = &env->values[i];
501
502 if (strcmp(v->name, name) == 0)
503 return v;
504 }
505
506 return NULL;
507 }
508
509 static const struct rbcfg_value *
510 rbcfg_env_find_u32(const struct rbcfg_env *env, uint32_t val)
511 {
512 unsigned i;
513
514 for (i = 0; i < env->num_values; i++) {
515 const struct rbcfg_value *v = &env->values[i];
516
517 if (v->val.u32 == val)
518 return v;
519 }
520
521 return NULL;
522 }
523
524 static const char *
525 rbcfg_env_get_u32(const struct rbcfg_env *env)
526 {
527 const struct rbcfg_value *v;
528 uint32_t val;
529 int err;
530
531 err = rbcfg_get_u32(rbcfg_ctx, env->id, &val);
532 if (err)
533 return NULL;
534
535 v = rbcfg_env_find_u32(env, val);
536 if (v == NULL) {
537 fprintf(stderr, "unknown value %08x found for %s\n",
538 val, env->name);
539 return NULL;
540 }
541
542 return v->name;
543 }
544
545 static int
546 rbcfg_env_set_u32(const struct rbcfg_env *env, const char *data)
547 {
548 const struct rbcfg_value *v;
549 int err;
550
551 v = rbcfg_env_find(env, data);
552 if (v == NULL) {
553 fprintf(stderr, "invalid value '%s'\n", data);
554 return RB_ERR_INVALID;
555 }
556
557 err = rbcfg_set_u32(rbcfg_ctx, env->id, v->val.u32);
558 return err;
559 }
560
561 static const char *
562 rbcfg_env_get(const struct rbcfg_env *env)
563 {
564 const char *ret = NULL;
565
566 switch (env->type) {
567 case RBCFG_ENV_TYPE_U32:
568 ret = rbcfg_env_get_u32(env);
569 break;
570 }
571
572 return ret;
573 }
574
575 static int
576 rbcfg_env_set(const struct rbcfg_env *env, const char *data)
577 {
578 int ret = 0;
579
580 switch (env->type) {
581 case RBCFG_ENV_TYPE_U32:
582 ret = rbcfg_env_set_u32(env, data);
583 break;
584 }
585
586 return ret;
587 }
588
589 static int
590 rbcfg_cmd_apply(int argc, const char *argv[])
591 {
592 return rbcfg_update(0);
593 }
594
595 static int
596 rbcfg_cmd_help(int argc, const char *argv[])
597 {
598 usage();
599 return 0;
600 }
601
602 static int
603 rbcfg_cmd_get(int argc, const char *argv[])
604 {
605 int err = RB_ERR_NOTFOUND;
606 int i;
607
608 if (argc != 1) {
609 usage();
610 return RB_ERR_INVALID;
611 }
612
613 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
614 const struct rbcfg_env *env = &rbcfg_envs[i];
615 const char *value;
616
617 if (strcmp(env->name, argv[0]))
618 continue;
619
620 value = rbcfg_env_get(env);
621 if (value) {
622 fprintf(stdout, "%s\n", value);
623 err = 0;
624 }
625 break;
626 }
627
628 return err;
629 }
630
631 static int
632 rbcfg_cmd_set(int argc, const char *argv[])
633 {
634 int err = RB_ERR_INVALID;
635 int i;
636
637 if (argc != 2) {
638 /* not enough parameters */
639 usage();
640 return RB_ERR_INVALID;
641 }
642
643 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
644 const struct rbcfg_env *env = &rbcfg_envs[i];
645
646 if (strcmp(env->name, argv[0]))
647 continue;
648
649 err = rbcfg_env_set(env, argv[1]);
650 if (err == 0)
651 err = rbcfg_update(1);
652 break;
653 }
654
655 return err;
656 }
657
658 static int
659 rbcfg_cmd_show(int argc, const char *argv[])
660 {
661 int i;
662
663 if (argc != 0) {
664 usage();
665 return RB_ERR_INVALID;
666 }
667
668 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
669 const struct rbcfg_env *env = &rbcfg_envs[i];
670 const char *value;
671
672 value = rbcfg_env_get(env);
673 if (value)
674 fprintf(stdout, "%s=%s\n", env->name, value);
675 }
676
677 return 0;
678 }
679
680 static const struct rbcfg_command rbcfg_commands[] = {
681 {
682 .command = "apply",
683 .usage = "apply\n"
684 "\t- write configuration to the mtd device",
685 .flags = CMD_FLAG_USES_CFG,
686 .exec = rbcfg_cmd_apply,
687 }, {
688 .command = "help",
689 .usage = "help\n"
690 "\t- show this screen",
691 .exec = rbcfg_cmd_help,
692 }, {
693 .command = "get",
694 .usage = "get <name>\n"
695 "\t- get value of the configuration option <name>",
696 .flags = CMD_FLAG_USES_CFG,
697 .exec = rbcfg_cmd_get,
698 }, {
699 .command = "set",
700 .usage = "set <name> <value>\n"
701 "\t- set value of the configuration option <name> to <value>",
702 .flags = CMD_FLAG_USES_CFG,
703 .exec = rbcfg_cmd_set,
704 }, {
705 .command = "show",
706 .usage = "show\n"
707 "\t- show value of all configuration options",
708 .flags = CMD_FLAG_USES_CFG,
709 .exec = rbcfg_cmd_show,
710 }
711 };
712
713 static void
714 usage(void)
715 {
716 char buf[255];
717 int len;
718 int i;
719
720 fprintf(stderr, "Usage: %s <command>\n", rbcfg_name);
721
722 fprintf(stderr, "\nCommands:\n");
723 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) {
724 const struct rbcfg_command *cmd;
725 cmd = &rbcfg_commands[i];
726
727 len = snprintf(buf, sizeof(buf), cmd->usage);
728 buf[len] = '\0';
729 fprintf(stderr, "%s\n", buf);
730 }
731
732 fprintf(stderr, "\nConfiguration options:\n");
733 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) {
734 const struct rbcfg_env *env;
735 int j;
736
737 env = &rbcfg_envs[i];
738 fprintf(stderr, "\n%s:\n", env->name);
739 for (j = 0; j < env->num_values; j++) {
740 const struct rbcfg_value *v = &env->values[j];
741 fprintf(stderr, "\t%-12s %s\n", v->name, v->desc);
742 }
743 }
744 fprintf(stderr, "\n");
745 }
746
747 int main(int argc, const char *argv[])
748 {
749 const struct rbcfg_command *cmd = NULL;
750 int ret;
751 int i;
752
753 rbcfg_name = (char *) argv[0];
754
755 if (argc < 2) {
756 usage();
757 return EXIT_FAILURE;
758 }
759
760 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) {
761 if (strcmp(rbcfg_commands[i].command, argv[1]) == 0) {
762 cmd = &rbcfg_commands[i];
763 break;
764 }
765 }
766
767 if (cmd == NULL) {
768 fprintf(stderr, "unknown command '%s'\n", argv[1]);
769 usage();
770 return EXIT_FAILURE;
771 }
772
773 argc -= 2;
774 argv += 2;
775
776 if (cmd->flags & CMD_FLAG_USES_CFG) {
777 ret = rbcfg_open();
778 if (ret)
779 return EXIT_FAILURE;
780 }
781
782 ret = cmd->exec(argc, argv);
783
784 if (cmd->flags & CMD_FLAG_USES_CFG)
785 rbcfg_close();
786
787 if (ret)
788 return EXIT_FAILURE;
789
790 return EXIT_SUCCESS;
791 }
This page took 0.083494 seconds and 5 git commands to generate.