lib: Add pb-config module
authorJeremy Kerr <jk@ozlabs.org>
Wed, 29 May 2013 07:24:42 +0000 (17:24 +1000)
committerJeremy Kerr <jk@ozlabs.org>
Mon, 24 Jun 2013 05:07:22 +0000 (13:07 +0800)
Add a library for (name, value) configuration.

Different storage backends are allowed (although currently hardcoded to
powerpc nvram), and config is read-only at present.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
discover/pb-discover.c
lib/Makefile.am
lib/pb-config/pb-config.c [new file with mode: 0644]
lib/pb-config/pb-config.h [new file with mode: 0644]
lib/pb-config/storage-null.c [new file with mode: 0644]
lib/pb-config/storage-powerpc-nvram.c [new file with mode: 0644]
lib/pb-config/storage-test.c [new file with mode: 0644]
lib/pb-config/storage.h [new file with mode: 0644]

index 6966ba4408f45e4ff734fc2ca2f6cf8028ff77d3..fac1c9deb261301e9927b9dc7c095339a6d83370 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <waiter/waiter.h>
 #include <log/log.h>
+#include <pb-config/pb-config.h>
 
 #include "udev.h"
 #include "user-event.h"
@@ -145,6 +146,8 @@ int main(int argc, char *argv[])
 
        signal(SIGINT, sigint_handler);
 
+       config_init(NULL);
+
        waitset = waitset_create(NULL);
 
        server = discover_server_init(waitset);
@@ -175,6 +178,7 @@ int main(int argc, char *argv[])
        device_handler_destroy(handler);
        waitset_destroy(waitset);
        udev_destroy(udev);
+       config_fini();
 
        pb_log("--- end ---\n");
 
index cce05fe6b3b218ab505840c3afe48a4f21936b13..fb251479209abcbbb58ec2b41c5628cb23cd51fd 100644 (file)
@@ -28,6 +28,12 @@ libpbcore_la_SOURCES = \
        waiter/waiter.h \
        pb-protocol/pb-protocol.c \
        pb-protocol/pb-protocol.h \
+       pb-config/pb-config.c \
+       pb-config/pb-config.h \
+       pb-config/storage.h \
+       pb-config/storage-powerpc-nvram.c \
+       pb-config/storage-null.c \
+       pb-config/storage-test.c \
        types/types.h \
        talloc/talloc.c \
        talloc/talloc.h \
diff --git a/lib/pb-config/pb-config.c b/lib/pb-config/pb-config.c
new file mode 100644 (file)
index 0000000..9f7d240
--- /dev/null
@@ -0,0 +1,81 @@
+
+#include <log/log.h>
+#include <talloc/talloc.h>
+
+#include "pb-config.h"
+
+#include "storage.h"
+
+static struct config           *config;
+static struct config_storage   *storage;
+
+
+static void config_set_defaults(struct config *config)
+{
+       config->autoboot_enabled = true;
+       config->network_configs = NULL;
+       config->n_network_configs = 0;
+}
+
+static void dump_config(struct config *config)
+{
+       int i;
+
+       pb_log("configuration:\n");
+
+       pb_log(" autoboot enabled: %s\n",
+                       config->autoboot_enabled ? "yes" : "no");
+
+       if (config->n_network_configs > 0)
+               pb_log(" network configuration:\n");
+
+       for (i = 0; i < config->n_network_configs; i++) {
+               struct network_config *netconf = config->network_configs[i];
+
+               pb_log("  interface %02x:%02x:%02x:%02x:%02x:%02x\n",
+                               netconf->hwaddr[0], netconf->hwaddr[1],
+                               netconf->hwaddr[2], netconf->hwaddr[3],
+                               netconf->hwaddr[4], netconf->hwaddr[5]);
+
+               if (netconf->ignore) {
+                       pb_log("   ignore\n");
+                       continue;
+               }
+
+               if (netconf->method == CONFIG_METHOD_DHCP) {
+                       pb_log("   dhcp\n");
+
+               } else if (netconf->method == CONFIG_METHOD_STATIC) {
+                       pb_log("   static:\n");
+                       pb_log("    ip:  %s\n", netconf->static_config.address);
+                       pb_log("    gw:  %s\n", netconf->static_config.gateway);
+                       pb_log("    dns: %s\n", netconf->static_config.dns);
+
+               }
+       }
+}
+
+int config_init(void *ctx)
+{
+       config = talloc(ctx, struct config);
+       config_set_defaults(config);
+
+       storage = create_powerpc_nvram_storage(config);
+
+       storage->load(storage, config);
+
+       dump_config(config);
+
+       return 0;
+}
+
+const struct config *config_get(void)
+{
+       return config;
+}
+
+int config_fini(void)
+{
+       talloc_free(config);
+       return 0;
+}
diff --git a/lib/pb-config/pb-config.h b/lib/pb-config/pb-config.h
new file mode 100644 (file)
index 0000000..cfcf025
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define HWADDR_SIZE    6
+
+struct network_config {
+       uint8_t hwaddr[HWADDR_SIZE];
+       bool    ignore;
+       enum {
+               CONFIG_METHOD_DHCP,
+               CONFIG_METHOD_STATIC,
+       } method;
+       union {
+               struct {
+               } dhcp_config;
+               struct {
+                       char *address;
+                       char *gateway;
+                       char *dns;
+               } static_config;
+       };
+};
+
+struct config {
+       bool                    autoboot_enabled;
+       struct network_config   **network_configs;
+       int                     n_network_configs;
+};
+
+
+int config_init(void *ctx);
+const struct config *config_get(void);
+int config_fini(void);
+
+#endif /* CONFIGURATION_H */
+
diff --git a/lib/pb-config/storage-null.c b/lib/pb-config/storage-null.c
new file mode 100644 (file)
index 0000000..b9fe6ed
--- /dev/null
@@ -0,0 +1,19 @@
+
+#include <stdlib.h>
+
+#include "storage.h"
+
+static int load(struct config_storage *st __attribute__((unused)),
+               struct config *config __attribute__((unused)))
+{
+       return 0;
+}
+
+static struct config_storage st = {
+       .load  = load,
+};
+
+struct config_storage *create_null_storage(void *ctx __attribute__((unused)))
+{
+       return &st;
+}
diff --git a/lib/pb-config/storage-powerpc-nvram.c b/lib/pb-config/storage-powerpc-nvram.c
new file mode 100644 (file)
index 0000000..4e4d29a
--- /dev/null
@@ -0,0 +1,323 @@
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <talloc/talloc.h>
+#include <list/list.h>
+#include <log/log.h>
+
+#include "pb-config.h"
+#include "storage.h"
+
+static const char *partition = "common";
+static const char *prefix = "petitboot,";
+
+struct param {
+       char                    *name;
+       char                    *value;
+       bool                    modified;
+       struct list_item        list;
+};
+
+struct powerpc_nvram_storage {
+       struct config_storage   storage;
+       struct list             params;
+};
+
+#define to_powerpc_nvram_storage(s) \
+       container_of(s, struct powerpc_nvram_storage, storage)
+
+/* a partition max a max size of 64k * 16bytes = 1M */
+static const int max_partition_size = 64 * 1024 * 16;
+
+static int parse_nvram_params(struct powerpc_nvram_storage *nv,
+               char *buf, int len)
+{
+       char *pos, *name, *value;
+       unsigned int paramlen;
+       int i, count;
+
+       /* discard 2 header lines:
+        * "common" partiton"
+        * ------------------
+        */
+       pos = buf;
+       count = 0;
+
+       for (i = 0; i < len; i++) {
+               if (pos[i] == '\n')
+                       count++;
+               if (count == 2)
+                       break;
+       }
+
+       if (i == len) {
+               fprintf(stderr, "failure parsing nvram output\n");
+               return -1;
+       }
+
+       for (pos = buf + i; pos < buf + len; pos += paramlen) {
+               unsigned int namelen;
+               struct param *param;
+
+               paramlen = strlen(pos);
+
+               name = pos;
+               value = strchr(pos, '=');
+               if (!value)
+                       continue;
+
+               namelen = name - value;
+               if (namelen <= strlen(prefix))
+                       continue;
+
+               if (strncmp(name, prefix, strlen(prefix)))
+                       continue;
+
+               name += strlen(prefix);
+               value++;
+
+               param = talloc(nv, struct param);
+               param->modified = false;
+               param->name = talloc_strndup(nv, name, namelen);
+               param->value = talloc_strdup(nv, value);
+               list_add(&nv->params, &param->list);
+       }
+
+       return 0;
+}
+
+static int parse_nvram(struct powerpc_nvram_storage *nv)
+{
+       int rc, len, buf_len;
+       int pipefds[2], status;
+       char *buf;
+       pid_t pid;
+
+       rc = pipe(pipefds);
+       if (rc) {
+               perror("pipe");
+               return -1;
+       }
+
+       pid = fork();
+
+       if (pid < 0) {
+               perror("fork");
+               return -1;
+       }
+
+       if (pid == 0) {
+               close(STDIN_FILENO);
+               close(pipefds[0]);
+               dup2(pipefds[1], STDOUT_FILENO);
+               execlp("nvram", "nvram", "--print-config",
+                               "--partition", partition, NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       close(pipefds[1]);
+
+       len = 0;
+       buf_len = max_partition_size;
+       buf = talloc_array(nv, char, buf_len);
+
+       for (;;) {
+               rc = read(pipefds[0], buf + len, buf_len - len);
+
+               if (rc < 0) {
+                       perror("read");
+                       break;
+               }
+
+               if (rc == 0)
+                       break;
+
+               len += rc;
+       }
+
+       waitpid(pid, &status, 0);
+       if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+               fprintf(stderr, "nvram process returned "
+                               "non-zero exit status\n");
+               return -1;
+       }
+
+       if (rc < 0)
+               return rc;
+
+       return parse_nvram_params(nv, buf, len);
+}
+
+static const char *get_param(struct powerpc_nvram_storage *nv,
+               const char *name)
+{
+       struct param *param;
+
+       list_for_each_entry(&nv->params, param, list)
+               if (!strcmp(param->name, name))
+                       return param->value;
+       return NULL;
+}
+
+static int parse_hwaddr(struct network_config *config, char *str)
+{
+       int i;
+
+       if (strlen(str) != strlen("00:00:00:00:00:00"))
+               return -1;
+
+       for (i = 0; i < HWADDR_SIZE; i++) {
+               char byte[3], *endp;
+               unsigned long x;
+
+               byte[0] = str[i * 3 + 0];
+               byte[1] = str[i * 3 + 1];
+               byte[2] = '\0';
+
+               x = strtoul(byte, &endp, 16);
+               if (endp != byte + 2)
+                       return -1;
+
+               config->hwaddr[i] = x & 0xff;
+       }
+
+       return 0;
+}
+
+static int parse_one_network_config(struct network_config *config,
+               char *confstr)
+{
+       char *tok, *saveptr;
+
+       if (!confstr || !strlen(confstr))
+               return -1;
+
+       /* first token should be the mac address */
+       tok = strtok_r(confstr, ",", &saveptr);
+       if (!tok)
+               return -1;
+
+       if (parse_hwaddr(config, tok))
+               return -1;
+
+       /* second token is the method */
+       tok = strtok_r(NULL, ",", &saveptr);
+       if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
+               config->ignore = true;
+               return 0;
+       }
+
+       if (!strcmp(tok, "dhcp")) {
+               config->method = CONFIG_METHOD_DHCP;
+
+       } else if (!strcmp(tok, "static")) {
+               config->method = CONFIG_METHOD_STATIC;
+
+               /* ip/mask, [optional] gateway, [optional] dns */
+               tok = strtok_r(NULL, ",", &saveptr);
+               if (!tok)
+                       return -1;
+               config->static_config.address =
+                       talloc_strdup(config, tok);
+
+               tok = strtok_r(NULL, ",", &saveptr);
+               if (tok) {
+                       config->static_config.gateway =
+                               talloc_strdup(config, tok);
+                       tok = strtok_r(NULL, ",", &saveptr);
+               }
+
+               if (tok) {
+                       config->static_config.dns =
+                               talloc_strdup(config, tok);
+               }
+       } else {
+               pb_log("Unknown network configuration method %s\n", tok);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void populate_network_config(struct powerpc_nvram_storage *nv,
+               struct config *config)
+{
+       const char *cval;
+       char *val;
+       int i;
+
+       cval = get_param(nv, "network");
+       if (!cval || !strlen(cval))
+               return;
+
+       val = talloc_strdup(config, cval);
+
+       for (i = 0; ; i++) {
+               struct network_config *netconf;
+               char *tok, *saveptr;
+               int rc;
+
+               tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
+               if (!tok)
+                       break;
+
+               netconf = talloc(nv, struct network_config);
+
+               rc = parse_one_network_config(netconf, tok);
+               if (rc) {
+                       talloc_free(netconf);
+                       continue;
+               }
+
+               config->network_configs = talloc_realloc(nv,
+                                               config->network_configs,
+                                               struct network_config *,
+                                               ++config->n_network_configs);
+
+               config->network_configs[config->n_network_configs - 1] =
+                                               netconf;
+       }
+
+       talloc_free(val);
+}
+
+static void populate_config(struct powerpc_nvram_storage *nv,
+               struct config *config)
+{
+       const char *val;
+
+       /* if the "auto-boot?' property is present and "false", disable auto
+        * boot */
+       val = get_param(nv, "auto-boot?");
+       config->autoboot_enabled = !val || strcmp(val, "false");
+
+       populate_network_config(nv, config);
+}
+
+static int load(struct config_storage *st, struct config *config)
+{
+       struct powerpc_nvram_storage *nv = to_powerpc_nvram_storage(st);
+       int rc;
+
+       rc = parse_nvram(nv);
+       if (rc)
+               return rc;
+
+       populate_config(nv, config);
+
+       return 0;
+}
+
+struct config_storage *create_powerpc_nvram_storage(void *ctx)
+{
+       struct powerpc_nvram_storage *nv;
+
+       nv = talloc(ctx, struct powerpc_nvram_storage);
+       nv->storage.load = load;
+       list_init(&nv->params);
+
+       return &nv->storage;
+}
diff --git a/lib/pb-config/storage-test.c b/lib/pb-config/storage-test.c
new file mode 100644 (file)
index 0000000..ba4e952
--- /dev/null
@@ -0,0 +1,56 @@
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "pb-config.h"
+#include "storage.h"
+
+struct network_config net1 = {
+       .hwaddr = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
+       .method = CONFIG_METHOD_DHCP,
+};
+
+struct network_config net2 = {
+       .hwaddr = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x56 },
+       .method = CONFIG_METHOD_STATIC,
+       .static_config = {
+               .address = "192.168.0.2/24",
+               .gateway = "192.168.0.1",
+       },
+};
+
+struct network_config *network_configs[] = { &net1, &net2 };
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+struct config test_config = {
+       .autoboot_enabled = true,
+       .network_configs = network_configs,
+       .n_network_configs = ARRAY_SIZE(network_configs),
+};
+
+struct test_storage {
+       struct config_storage   storage;
+       struct param            *params;
+       int                     n_params;
+};
+
+static int load(struct config_storage *st __attribute__((unused)),
+               struct config *config)
+{
+       memcpy(config, &test_config, sizeof(test_config));
+       return 0;
+}
+
+static struct test_storage st = {
+       .storage = {
+               .load  = load,
+       },
+};
+
+struct config_storage *create_test_storage(void *ctx __attribute__((unused)))
+{
+       return &st.storage;
+}
diff --git a/lib/pb-config/storage.h b/lib/pb-config/storage.h
new file mode 100644 (file)
index 0000000..a1d5269
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef STORAGE_H
+#define STORAGE_H
+
+#include <stdbool.h>
+
+struct config;
+
+struct config_storage {
+       int     (*load)(struct config_storage *st, struct config *config);
+};
+
+struct config_storage *create_powerpc_nvram_storage(void *ctx);
+struct config_storage *create_test_storage(void *ctx);
+struct config_storage *create_null_storage(void *ctx);
+
+#endif /* STORAGE_H */
+