add chaos_calmer branch
[15.05/openwrt.git] / package / boot / 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), "%s", 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 }