From c14b12980885edd035322cd3bc87efff444c39b1 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 29 Jan 2014 13:28:06 +0800 Subject: pb-config: Move config storage modules to "platform" modules in discover code There's no need to include the config storage code in lib/ as only the discover server should be using it. This change moves the config-storage code to discover/, with the platform-specific parts moved to a 'struct platform'. Each platform has a probe function, which is called during init. The first probe function to return a platform is used. At present we only have the one platform, but it's now non-intrusive to add others. We keep an array of platform pointers in a separate ("platforms") section, to allow the test module to drop-in its own test "platform". Signed-off-by: Jeremy Kerr --- discover/Makefile.am | 3 + discover/device-handler.c | 2 +- discover/discover-server.c | 1 + discover/network.c | 2 +- discover/pb-discover.c | 6 +- discover/platform-powerpc.c | 553 ++++++++++++++++++++++++++++++++++++++++++++ discover/platform.c | 157 +++++++++++++ discover/platform.h | 34 +++ 8 files changed, 753 insertions(+), 5 deletions(-) create mode 100644 discover/platform-powerpc.c create mode 100644 discover/platform.c create mode 100644 discover/platform.h (limited to 'discover') diff --git a/discover/Makefile.am b/discover/Makefile.am index 84c0436..5d5f5b4 100644 --- a/discover/Makefile.am +++ b/discover/Makefile.am @@ -54,6 +54,9 @@ pb_discover_SOURCES = \ paths.h \ pb-discover.c \ pb-discover.h \ + platform.c \ + platform.h \ + platform-powerpc.c \ resource.c \ resource.h \ sysinfo.c \ diff --git a/discover/device-handler.c b/discover/device-handler.c index d3256fa..c57b7b6 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -19,6 +18,7 @@ #include "device-handler.h" #include "discover-server.h" +#include "platform.h" #include "event.h" #include "parser.h" #include "resource.h" diff --git a/discover/discover-server.c b/discover/discover-server.c index 3cf9fac..1765074 100644 --- a/discover/discover-server.c +++ b/discover/discover-server.c @@ -20,6 +20,7 @@ #include "device-handler.h" #include "discover-server.h" +#include "platform.h" #include "sysinfo.h" struct discover_server { diff --git a/discover/network.c b/discover/network.c index cf340da..9da0f7a 100644 --- a/discover/network.c +++ b/discover/network.c @@ -14,13 +14,13 @@ #include #include #include -#include #include #include #include "file.h" #include "network.h" #include "sysinfo.h" +#include "platform.h" #include "device-handler.h" #define HWADDR_SIZE 6 diff --git a/discover/pb-discover.c b/discover/pb-discover.c index d8708a3..bb590a9 100644 --- a/discover/pb-discover.c +++ b/discover/pb-discover.c @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -21,6 +20,7 @@ #include "device-handler.h" #include "network.h" #include "sysinfo.h" +#include "platform.h" static void print_version(void) { @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) if (!procset) return EXIT_FAILURE; - config_init(NULL); + platform_init(NULL); if (opts.no_autoboot == opt_yes) config_set_autoboot(false); @@ -205,7 +205,7 @@ int main(int argc, char *argv[]) user_event_destroy(uev); udev_destroy(udev); discover_server_destroy(server); - config_fini(); + platform_fini(); talloc_free(waitset); pb_log("--- end ---\n"); diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c new file mode 100644 index 0000000..e2a8631 --- /dev/null +++ b/discover/platform-powerpc.c @@ -0,0 +1,553 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "platform.h" + +static const char *partition = "common"; + +struct param { + char *name; + char *value; + bool modified; + struct list_item list; +}; + +struct platform_powerpc { + struct list params; +}; + +static const char *known_params[] = { + "auto-boot?", + "petitboot,network", + "petitboot,timeout", + NULL, +}; + +#define to_platform_powerpc(p) \ + (struct platform_powerpc *)(p->platform_data) + +/* a partition max a max size of 64k * 16bytes = 1M */ +static const int max_partition_size = 64 * 1024 * 16; + +static bool param_is_known(const char *param, unsigned int len) +{ + const char *known_param; + unsigned int i; + + for (i = 0; known_params[i]; i++) { + known_param = known_params[i]; + if (len == strlen(known_param) && + !strncmp(param, known_param, len)) + return true; + } + + return false; +} + +static int parse_nvram_params(struct platform_powerpc *platform, + 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 + 1) { + unsigned int namelen; + struct param *param; + char *newline; + + newline = strchr(pos, '\n'); + if (!newline) + break; + + *newline = '\0'; + + paramlen = strlen(pos); + + name = pos; + value = strchr(pos, '='); + if (!value) + continue; + + namelen = value - name; + if (namelen == 0) + continue; + + if (!param_is_known(name, namelen)) + continue; + + value++; + + param = talloc(platform, struct param); + param->modified = false; + param->name = talloc_strndup(platform, name, namelen); + param->value = talloc_strdup(platform, value); + list_add(&platform->params, ¶m->list); + } + + return 0; +} + +static int parse_nvram(struct platform_powerpc *platform) +{ + struct process *process; + const char *argv[5]; + int rc; + + argv[0] = "nvram"; + argv[1] = "--print-config"; + argv[2] = "--partition"; + argv[3] = partition; + argv[4] = NULL; + + process = process_create(platform); + process->path = "nvram"; + process->argv = argv; + process->keep_stdout = true; + + rc = process_run_sync(process); + + if (rc || !process_exit_ok(process)) { + fprintf(stderr, "nvram process returned " + "non-zero exit status\n"); + rc = -1; + } else { + rc = parse_nvram_params(platform, process->stdout_buf, + process->stdout_len); + } + + process_release(process); + return rc; +} + +static int write_nvram(struct platform_powerpc *platform) +{ + struct process *process; + struct param *param; + const char *argv[6]; + int rc; + + argv[0] = "nvram"; + argv[1] = "--update-config"; + argv[2] = NULL; + argv[3] = "--partition"; + argv[4] = partition; + argv[5] = NULL; + + process = process_create(platform); + process->path = "nvram"; + process->argv = argv; + + list_for_each_entry(&platform->params, param, list) { + char *paramstr; + + if (!param->modified) + continue; + + paramstr = talloc_asprintf(platform, "%s=%s", + param->name, param->value); + argv[2] = paramstr; + + rc = process_run_sync(process); + + talloc_free(paramstr); + + if (rc || !process_exit_ok(process)) { + rc = -1; + pb_log("nvram update process returned " + "non-zero exit status\n"); + break; + } + } + + process_release(process); + return rc; +} + +static const char *get_param(struct platform_powerpc *platform, + const char *name) +{ + struct param *param; + + list_for_each_entry(&platform->params, param, list) + if (!strcmp(param->name, name)) + return param->value; + return NULL; +} + +static void set_param(struct platform_powerpc *platform, const char *name, + const char *value) +{ + struct param *param; + + list_for_each_entry(&platform->params, param, list) { + if (strcmp(param->name, name)) + continue; + + if (!strcmp(param->value, value)) + return; + + talloc_free(param->value); + param->value = talloc_strdup(param, value); + param->modified = true; + return; + } + + + param = talloc(platform, struct param); + param->modified = true; + param->name = talloc_strdup(platform, name); + param->value = talloc_strdup(platform, value); + list_add(&platform->params, ¶m->list); +} + +static int parse_hwaddr(struct interface_config *ifconf, 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; + + ifconf->hwaddr[i] = x & 0xff; + } + + return 0; +} + +static int parse_one_interface_config(struct config *config, + char *confstr) +{ + struct interface_config *ifconf; + char *tok, *saveptr; + + ifconf = talloc_zero(config, struct interface_config); + + if (!confstr || !strlen(confstr)) + goto out_err; + + /* first token should be the mac address */ + tok = strtok_r(confstr, ",", &saveptr); + if (!tok) + goto out_err; + + if (parse_hwaddr(ifconf, tok)) + goto out_err; + + /* second token is the method */ + tok = strtok_r(NULL, ",", &saveptr); + if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) { + ifconf->ignore = true; + + } else if (!strcmp(tok, "dhcp")) { + ifconf->method = CONFIG_METHOD_DHCP; + + } else if (!strcmp(tok, "static")) { + ifconf->method = CONFIG_METHOD_STATIC; + + /* ip/mask, [optional] gateway */ + tok = strtok_r(NULL, ",", &saveptr); + if (!tok) + goto out_err; + ifconf->static_config.address = + talloc_strdup(ifconf, tok); + + tok = strtok_r(NULL, ",", &saveptr); + if (tok) { + ifconf->static_config.gateway = + talloc_strdup(ifconf, tok); + } + + } else { + pb_log("Unknown network configuration method %s\n", tok); + goto out_err; + } + + config->network.interfaces = talloc_realloc(config, + config->network.interfaces, + struct interface_config *, + ++config->network.n_interfaces); + + config->network.interfaces[config->network.n_interfaces - 1] = ifconf; + + return 0; +out_err: + talloc_free(ifconf); + return -1; +} + +static int parse_one_dns_config(struct config *config, + char *confstr) +{ + char *tok, *saveptr; + + for (tok = strtok_r(confstr, ",", &saveptr); tok; + tok = strtok_r(NULL, ",", &saveptr)) { + + char *server = talloc_strdup(config, tok); + + config->network.dns_servers = talloc_realloc(config, + config->network.dns_servers, const char *, + ++config->network.n_dns_servers); + + config->network.dns_servers[config->network.n_dns_servers - 1] + = server; + } + + return 0; +} + +static void populate_network_config(struct platform_powerpc *platform, + struct config *config) +{ + const char *cval; + char *val; + int i; + + cval = get_param(platform, "petitboot,network"); + if (!cval || !strlen(cval)) + return; + + val = talloc_strdup(config, cval); + + for (i = 0; ; i++) { + char *tok, *saveptr; + + tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr); + if (!tok) + break; + + if (!strncasecmp(tok, "dns,", strlen("dns,"))) + parse_one_dns_config(config, tok + strlen("dns,")); + else + parse_one_interface_config(config, tok); + + } + + talloc_free(val); +} + +static void populate_config(struct platform_powerpc *platform, + struct config *config) +{ + const char *val; + char *end; + unsigned long timeout; + + /* if the "auto-boot?' property is present and "false", disable auto + * boot */ + val = get_param(platform, "auto-boot?"); + config->autoboot_enabled = !val || strcmp(val, "false"); + + val = get_param(platform, "petitboot,timeout"); + if (val) { + timeout = strtoul(val, &end, 10); + if (end != val) { + if (timeout >= INT_MAX) + timeout = INT_MAX; + config->autoboot_timeout_sec = (int)timeout; + } + } + + populate_network_config(platform, config); +} + +static char *iface_config_str(void *ctx, struct interface_config *config) +{ + char *str; + + /* todo: HWADDR size is hardcoded as 6, but we may need to handle + * different hardware address formats */ + str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,", + config->hwaddr[0], config->hwaddr[1], + config->hwaddr[2], config->hwaddr[3], + config->hwaddr[4], config->hwaddr[5]); + + if (config->ignore) { + str = talloc_asprintf_append(str, "ignore"); + + } else if (config->method == CONFIG_METHOD_DHCP) { + str = talloc_asprintf_append(str, "dhcp"); + + } else if (config->method == CONFIG_METHOD_STATIC) { + str = talloc_asprintf_append(str, "static,%s%s%s", + config->static_config.address, + config->static_config.gateway ? "," : "", + config->static_config.gateway ?: ""); + } + return str; +} + +static char *dns_config_str(void *ctx, const char **dns_servers, int n) +{ + char *str; + int i; + + str = talloc_strdup(ctx, "dns,"); + for (i = 0; i < n; i++) { + str = talloc_asprintf_append(str, "%s%s", + i == 0 ? "" : ",", + dns_servers[i]); + } + + return str; +} + +static void update_string_config(struct platform_powerpc *platform, + const char *name, const char *value) +{ + const char *cur; + + cur = get_param(platform, name); + + /* don't set an empty parameter if it doesn't already exist */ + if (!cur && !strlen(value)) + return; + + set_param(platform, name, value); +} + +static void update_network_config(struct platform_powerpc *platform, + struct config *config) +{ + unsigned int i; + char *val; + + val = talloc_strdup(platform, ""); + + for (i = 0; i < config->network.n_interfaces; i++) { + char *iface_str = iface_config_str(platform, + config->network.interfaces[i]); + val = talloc_asprintf_append(val, "%s%s", + *val == '\0' ? "" : " ", iface_str); + talloc_free(iface_str); + } + + if (config->network.n_dns_servers) { + char *dns_str = dns_config_str(platform, + config->network.dns_servers, + config->network.n_dns_servers); + val = talloc_asprintf_append(val, "%s%s", + *val == '\0' ? "" : " ", dns_str); + talloc_free(dns_str); + } + + update_string_config(platform, "petitboot,network", val); + + talloc_free(val); +} + +static int update_config(struct platform_powerpc *platform, + struct config *config, struct config *defaults) +{ + char *tmp = NULL; + const char *val; + + if (config->autoboot_enabled == defaults->autoboot_enabled) + val = ""; + else + val = config->autoboot_enabled ? "true" : "false"; + update_string_config(platform, "auto-boot?", val); + + if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec) + val = ""; + else + val = tmp = talloc_asprintf(platform, "%d", + config->autoboot_timeout_sec); + + update_string_config(platform, "petitboot,timeout", val); + if (tmp) + talloc_free(tmp); + + update_network_config(platform, config); + + return write_nvram(platform); +} + +static int load_config(struct platform *p, struct config *config) +{ + struct platform_powerpc *platform = to_platform_powerpc(p); + int rc; + + rc = parse_nvram(platform); + if (rc) + return rc; + + populate_config(platform, config); + + return 0; +} + +static int save_config(struct platform *p, struct config *config) +{ + struct platform_powerpc *platform = to_platform_powerpc(p); + struct config *defaults; + int rc; + + defaults = talloc_zero(platform, struct config); + config_set_defaults(defaults); + + rc = update_config(platform, config, defaults); + + talloc_free(defaults); + return rc; +} + +static bool probe(struct platform *p, void *ctx) +{ + struct platform_powerpc *platform; + + platform = talloc(ctx, struct platform_powerpc); + list_init(&platform->params); + + p->platform_data = platform; + return true; +} + +static struct platform platform_powerpc = { + .name = "powerpc", + .probe = probe, + .load_config = load_config, + .save_config = save_config, +}; + +register_platform(platform_powerpc); diff --git a/discover/platform.c b/discover/platform.c new file mode 100644 index 0000000..66e776e --- /dev/null +++ b/discover/platform.c @@ -0,0 +1,157 @@ + +#include + +#include +#include +#include + +#include "platform.h" + +void *platform_ctx; +static struct platform *platform; +static struct config *config; + +static void dump_config(struct config *config) +{ + unsigned int i; + + pb_log("configuration:\n"); + + if (config->autoboot_enabled) + pb_log(" autoboot: enabled, %d sec\n", + config->autoboot_timeout_sec); + else + pb_log(" autoboot: disabled\n"); + + if (config->network.n_interfaces || config->network.n_dns_servers) + pb_log(" network configuration:\n"); + + for (i = 0; i < config->network.n_interfaces; i++) { + struct interface_config *ifconf = + config->network.interfaces[i]; + + pb_log(" interface %02x:%02x:%02x:%02x:%02x:%02x\n", + ifconf->hwaddr[0], ifconf->hwaddr[1], + ifconf->hwaddr[2], ifconf->hwaddr[3], + ifconf->hwaddr[4], ifconf->hwaddr[5]); + + if (ifconf->ignore) { + pb_log(" ignore\n"); + continue; + } + + if (ifconf->method == CONFIG_METHOD_DHCP) { + pb_log(" dhcp\n"); + + } else if (ifconf->method == CONFIG_METHOD_STATIC) { + pb_log(" static:\n"); + pb_log(" ip: %s\n", ifconf->static_config.address); + pb_log(" gw: %s\n", ifconf->static_config.gateway); + + } + } + for (i = 0; i < config->network.n_dns_servers; i++) + pb_log(" dns server %s\n", config->network.dns_servers[i]); +} + +void config_set_defaults(struct config *config) +{ + config->autoboot_enabled = true; + config->autoboot_timeout_sec = 10; + config->network.interfaces = NULL; + config->network.n_interfaces = 0; + config->network.dns_servers = NULL; + config->network.n_dns_servers = 0; + + config->n_boot_priorities = 2; + config->boot_priorities = talloc_array(config, struct boot_priority, + config->n_boot_priorities); + config->boot_priorities[0].type = DEVICE_TYPE_NETWORK; + config->boot_priorities[1].type = DEVICE_TYPE_DISK; + +} + +int platform_init(void *ctx) +{ + extern struct platform *__start_platforms, *__stop_platforms; + struct platform **p; + + platform_ctx = talloc_new(ctx); + + for (p = &__start_platforms; p < &__stop_platforms; p++) { + if (!(*p)->probe(*p, platform_ctx)) + continue; + platform = *p; + break; + } + + config = talloc(platform_ctx, struct config); + config_set_defaults(config); + + if (platform) { + pb_log("Detected platform type: %s\n", platform->name); + if (platform->load_config) + platform->load_config(platform, config); + } else { + pb_log("No platform type detected, some platform-specific " + "functionality will be disabled\n"); + } + + dump_config(config); + + return 0; +} + +int config_set(struct config *newconfig) +{ + int rc; + + if (!platform || !platform->save_config) + return -1; + + if (newconfig == config) + return 0; + + pb_log("new configuration data received\n"); + dump_config(newconfig); + + rc = platform->save_config(platform, newconfig); + + if (!rc) + config = talloc_steal(platform_ctx, newconfig); + else + pb_log("error saving new configuration; changes lost\n"); + + return rc; +} + +/* A non-exported function to allow the test infrastructure to initialise + * (and change) the configuration variables */ +struct parser_test; +struct config __attribute__((unused)) *test_config_init( + struct parser_test *test); +struct config *test_config_init(struct parser_test *test) +{ + config = talloc(test, struct config); + config_set_defaults(config); + return config; +} + +const struct config *config_get(void) +{ + return config; +} + +void config_set_autoboot(bool autoboot_enabled) +{ + config->autoboot_enabled = autoboot_enabled; + + pb_log("set autoboot: %s\n", + config->autoboot_enabled ? "enabled" : "disabled"); +} + +int platform_fini(void) +{ + talloc_free(platform_ctx); + return 0; +} diff --git a/discover/platform.h b/discover/platform.h new file mode 100644 index 0000000..44315fa --- /dev/null +++ b/discover/platform.h @@ -0,0 +1,34 @@ +#ifndef PLATFORM_H +#define PLATFORM_H + +#include + +struct platform { + const char *name; + bool (*probe)(struct platform *, void *); + int (*load_config)(struct platform *, struct config *); + int (*save_config)(struct platform *, struct config *); + void *platform_data; +}; + +int platform_init(void *ctx); +int platform_fini(void); + +/* configuration interface */ +const struct config *config_get(void); +int config_set(struct config *config); +void config_set_autoboot(bool autoboot_enabled); + +/* for use by the platform-specific storage code */ +void config_set_defaults(struct config *config); + +#define __platform_ptrname(_n) __platform_ ## _n +#define _platform_ptrname(_n) __platform_ptrname(_n) + +#define register_platform(p) \ + static __attribute__((section("platforms"))) \ + __attribute__((used)) \ + struct platform * _platform_ptrname(__COUNTER__) = &p; + +#endif /* PLATFORM_H */ + -- cgit v1.2.1