#include <string.h>
#include <stdlib.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <talloc/talloc.h>
#include <list/list.h>
#include <log/log.h>
+#include <process/process.h>
#include "pb-config.h"
#include "storage.h"
static const char *partition = "common";
-static const char *prefix = "petitboot,";
struct param {
char *name;
struct list params;
};
+static const char *known_params[] = {
+ "auto-boot?",
+ "petitboot,network",
+ "petitboot,timeout",
+ NULL,
+};
+
#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 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 powerpc_nvram_storage *nv,
char *buf, int len)
{
return -1;
}
- for (pos = buf + i; pos < buf + len; pos += paramlen) {
+ 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);
if (!value)
continue;
- namelen = name - value;
- if (namelen <= strlen(prefix))
+ namelen = value - name;
+ if (namelen == 0)
continue;
- if (strncmp(name, prefix, strlen(prefix)))
+ if (!param_is_known(name, namelen))
continue;
- name += strlen(prefix);
value++;
param = talloc(nv, struct param);
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;
- }
+ struct process *process;
+ const char *argv[5];
+ int rc;
- pid = fork();
+ argv[0] = "nvram";
+ argv[1] = "--print-config";
+ argv[2] = "--partition";
+ argv[3] = partition;
+ argv[4] = NULL;
- if (pid < 0) {
- perror("fork");
- return -1;
- }
+ process = process_create(nv);
+ process->path = "nvram";
+ process->argv = argv;
+ process->keep_stdout = true;
+
+ rc = process_run_sync(process);
- 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);
+ if (rc || !WIFEXITED(process->exit_status)
+ || WEXITSTATUS(process->exit_status)) {
+ fprintf(stderr, "nvram process returned "
+ "non-zero exit status\n");
+ rc = -1;
+ } else {
+ rc = parse_nvram_params(nv, process->stdout_buf,
+ process->stdout_len);
}
- close(pipefds[1]);
+ process_release(process);
+ return rc;
+}
- len = 0;
- buf_len = max_partition_size;
- buf = talloc_array(nv, char, buf_len);
+static int write_nvram(struct powerpc_nvram_storage *nv)
+{
+ struct process *process;
+ struct param *param;
+ const char *argv[6];
+ int rc;
- for (;;) {
- rc = read(pipefds[0], buf + len, buf_len - len);
+ argv[0] = "nvram";
+ argv[1] = "--update-config";
+ argv[2] = NULL;
+ argv[3] = "--partition";
+ argv[4] = partition;
+ argv[5] = NULL;
- if (rc < 0) {
- perror("read");
- break;
- }
+ process = process_create(nv);
+ process->path = "nvram";
+ process->argv = argv;
- if (rc == 0)
- break;
+ list_for_each_entry(&nv->params, param, list) {
+ char *paramstr;
- len += rc;
- }
+ if (!param->modified)
+ continue;
- waitpid(pid, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
- fprintf(stderr, "nvram process returned "
- "non-zero exit status\n");
- return -1;
- }
+ paramstr = talloc_asprintf(nv, "%s=%s",
+ param->name, param->value);
+ argv[2] = paramstr;
- if (rc < 0)
- return rc;
+ rc = process_run_sync(process);
+
+ talloc_free(paramstr);
+
+ if (rc || !WIFEXITED(process->exit_status)
+ || WEXITSTATUS(process->exit_status)) {
+ rc = -1;
+ pb_log("nvram update process returned "
+ "non-zero exit status\n");
+ break;
+ }
+ }
- return parse_nvram_params(nv, buf, len);
+ process_release(process);
+ return rc;
}
static const char *get_param(struct powerpc_nvram_storage *nv,
return NULL;
}
-static int parse_hwaddr(struct network_config *config, char *str)
+static void set_param(struct powerpc_nvram_storage *nv, const char *name,
+ const char *value)
+{
+ struct param *param;
+
+ list_for_each_entry(&nv->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(nv, struct param);
+ param->modified = true;
+ param->name = talloc_strdup(nv, name);
+ param->value = talloc_strdup(nv, value);
+ list_add(&nv->params, ¶m->list);
+}
+
+static int parse_hwaddr(struct interface_config *ifconf, char *str)
{
int i;
if (endp != byte + 2)
return -1;
- config->hwaddr[i] = x & 0xff;
+ ifconf->hwaddr[i] = x & 0xff;
}
return 0;
}
-static int parse_one_network_config(struct network_config *config,
+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))
- return -1;
+ goto out_err;
/* first token should be the mac address */
tok = strtok_r(confstr, ",", &saveptr);
if (!tok)
- return -1;
+ goto out_err;
- if (parse_hwaddr(config, tok))
- return -1;
+ 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")) {
- config->ignore = true;
- return 0;
- }
+ ifconf->ignore = true;
- if (!strcmp(tok, "dhcp")) {
- config->method = CONFIG_METHOD_DHCP;
+ } else if (!strcmp(tok, "dhcp")) {
+ ifconf->method = CONFIG_METHOD_DHCP;
} else if (!strcmp(tok, "static")) {
- config->method = CONFIG_METHOD_STATIC;
+ ifconf->method = CONFIG_METHOD_STATIC;
- /* ip/mask, [optional] gateway, [optional] dns */
+ /* ip/mask, [optional] gateway */
tok = strtok_r(NULL, ",", &saveptr);
if (!tok)
- return -1;
- config->static_config.address =
- talloc_strdup(config, tok);
+ goto out_err;
+ ifconf->static_config.address =
+ talloc_strdup(ifconf, tok);
tok = strtok_r(NULL, ",", &saveptr);
if (tok) {
- config->static_config.gateway =
- talloc_strdup(config, tok);
- tok = strtok_r(NULL, ",", &saveptr);
+ ifconf->static_config.gateway =
+ talloc_strdup(ifconf, tok);
}
- if (tok) {
- config->static_config.dns =
- talloc_strdup(config, tok);
- }
} else {
pb_log("Unknown network configuration method %s\n", tok);
- return -1;
+ 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;
char *val;
int i;
- cval = get_param(nv, "network");
+ cval = get_param(nv, "petitboot,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);
+ if (!strncasecmp(tok, "dns,", strlen("dns,")))
+ parse_one_dns_config(config, tok + strlen("dns,"));
+ else
+ parse_one_interface_config(config, tok);
- config->network_configs[config->n_network_configs - 1] =
- netconf;
}
talloc_free(val);
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(nv, "auto-boot?");
config->autoboot_enabled = !val || strcmp(val, "false");
+ val = get_param(nv, "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(nv, 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_network_config(struct powerpc_nvram_storage *nv,
+ struct config *config)
+{
+ unsigned int i;
+ char *val;
+
+ val = talloc_strdup(nv, "");
+
+ for (i = 0; i < config->network.n_interfaces; i++) {
+ char *iface_str = iface_config_str(nv,
+ 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(nv, config->network.dns_servers,
+ config->network.n_dns_servers);
+ val = talloc_asprintf_append(val, "%s%s",
+ *val == '\0' ? "" : " ", dns_str);
+ talloc_free(dns_str);
+ }
+
+ set_param(nv, "petitboot,network", val);
+
+ talloc_free(val);
+}
+
+static int update_config(struct powerpc_nvram_storage *nv,
+ struct config *config, struct config *defaults)
+{
+ char *val;
+
+ if (config->autoboot_enabled != defaults->autoboot_enabled) {
+ val = config->autoboot_enabled ? "true" : "false";
+ set_param(nv, "auto-boot?", val);
+ }
+
+ if (config->autoboot_timeout_sec != defaults->autoboot_timeout_sec) {
+ val = talloc_asprintf(nv, "%d", config->autoboot_timeout_sec);
+ set_param(nv, "petitboot,timeout", val);
+ talloc_free(val);
+ }
+
+ if (config->network.n_interfaces)
+ update_network_config(nv, config);
+
+ return write_nvram(nv);
+}
+
static int load(struct config_storage *st, struct config *config)
{
struct powerpc_nvram_storage *nv = to_powerpc_nvram_storage(st);
return 0;
}
+static int save(struct config_storage *st, struct config *config)
+{
+ struct powerpc_nvram_storage *nv = to_powerpc_nvram_storage(st);
+ struct config *defaults;
+ int rc;
+
+ defaults = talloc_zero(nv, struct config);
+ config_set_defaults(defaults);
+
+ rc = update_config(nv, config, defaults);
+
+ talloc_free(defaults);
+ return rc;
+}
+
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;
+ nv->storage.save = save;
list_init(&nv->params);
return &nv->storage;