Root/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
35struct rbcfg_ctx {
36    char *mtd_device;
37    char *tmp_file;
38    char *buf;
39    unsigned buflen;
40};
41
42struct 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
53struct 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
63struct rbcfg_command {
64    const char *command;
65    const char *usage;
66    int flags;
67    int (*exec)(int argc, const char *argv[]);
68};
69
70static void usage(void);
71
72/* Globals */
73
74static struct rbcfg_ctx *rbcfg_ctx;
75static char *rbcfg_name;
76
77#define CFG_U32(_name, _desc, _val) { \
78    .name = (_name), \
79    .desc = (_desc), \
80    .val.u32 = (_val), \
81}
82
83static 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
95static 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
106static 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
111static 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
116static 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
128static 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
134static 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
139static 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
185static inline uint16_t
186get_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
193static inline uint32_t
194get_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
202static inline void
203put_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
213static int
214rbcfg_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
262static int
263rbcfg_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
277static int
278rbcfg_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
292char *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
338static int
339rbcfg_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
357static int
358rbcfg_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
408static int
409rbcfg_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
447static int
448rbcfg_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
484static void
485rbcfg_close(void)
486{
487    struct rbcfg_ctx *ctx;
488
489    ctx = rbcfg_ctx;
490    free(ctx->mtd_device);
491    free(ctx);
492}
493
494static const struct rbcfg_value *
495rbcfg_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
509static const struct rbcfg_value *
510rbcfg_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
524static const char *
525rbcfg_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
545static int
546rbcfg_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
561static const char *
562rbcfg_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
575static int
576rbcfg_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
589static int
590rbcfg_cmd_apply(int argc, const char *argv[])
591{
592    return rbcfg_update(0);
593}
594
595static int
596rbcfg_cmd_help(int argc, const char *argv[])
597{
598    usage();
599    return 0;
600}
601
602static int
603rbcfg_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
631static int
632rbcfg_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
658static int
659rbcfg_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
680static 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
713static void
714usage(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
747int 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}
792

Archive Download this file



interactive