main.c 15 KB

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