From: Jeremy Kerr Date: Wed, 29 May 2013 07:24:42 +0000 (+1000) Subject: lib: Add pb-config module X-Git-Tag: v1.0.0~575 X-Git-Url: https://git.ozlabs.org/?p=petitboot;a=commitdiff_plain;h=32fe8024f5af5cd8de23c638ddad8ada67a46cd5 lib: Add pb-config module 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 --- diff --git a/discover/pb-discover.c b/discover/pb-discover.c index 6966ba4..fac1c9d 100644 --- a/discover/pb-discover.c +++ b/discover/pb-discover.c @@ -11,6 +11,7 @@ #include #include +#include #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"); diff --git a/lib/Makefile.am b/lib/Makefile.am index cce05fe..fb25147 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 index 0000000..9f7d240 --- /dev/null +++ b/lib/pb-config/pb-config.c @@ -0,0 +1,81 @@ + +#include +#include + +#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 index 0000000..cfcf025 --- /dev/null +++ b/lib/pb-config/pb-config.h @@ -0,0 +1,39 @@ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include +#include + +#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 index 0000000..b9fe6ed --- /dev/null +++ b/lib/pb-config/storage-null.c @@ -0,0 +1,19 @@ + +#include + +#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 index 0000000..4e4d29a --- /dev/null +++ b/lib/pb-config/storage-powerpc-nvram.c @@ -0,0 +1,323 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#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, ¶m->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 index 0000000..ba4e952 --- /dev/null +++ b/lib/pb-config/storage-test.c @@ -0,0 +1,56 @@ + +#include +#include +#include +#include + +#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 index 0000000..a1d5269 --- /dev/null +++ b/lib/pb-config/storage.h @@ -0,0 +1,17 @@ +#ifndef STORAGE_H +#define STORAGE_H + +#include + +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 */ +