/* * state.c - state handling tool * * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <limits.h> #include <stdarg.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <linux/fs.h> #include <mtd/mtd-abi.h> #include <barebox-state/state.h> #include <dt/dt.h> #include <state.h> static int verbose; struct state_variable; static int __state_uint8_set(struct state_variable *var, const char *val); static char *__state_uint8_get(struct state_variable *var); static int __state_uint32_set(struct state_variable *var, const char *val); static char *__state_uint32_get(struct state_variable *var); static int __state_enum32_set(struct state_variable *sv, const char *val); static char *__state_enum32_get(struct state_variable *var); static void __state_enum32_info(struct state_variable *var); static int __state_mac_set(struct state_variable *var, const char *val); static char *__state_mac_get(struct state_variable *var); static int __state_string_set(struct state_variable *var, const char *val); static char *__state_string_get(struct state_variable *var); struct variable_str_type { enum state_variable_type type; char *type_name; char *(*get)(struct state_variable *var); int (*set)(struct state_variable *var, const char *val); void (*info)(struct state_variable *var); }; static struct variable_str_type types[] = { { .type = STATE_TYPE_U8, .type_name = "uint8", .set = __state_uint8_set, .get = __state_uint32_get, }, { .type = STATE_TYPE_U32, .type_name = "uint32", .set = __state_uint32_set, .get = __state_uint32_get, }, { .type = STATE_TYPE_ENUM, .type_name = "enum32", .set = __state_enum32_set, .get = __state_enum32_get, .info = __state_enum32_info, }, { .type = STATE_TYPE_MAC, .type_name = "mac", .set = __state_mac_set, .get = __state_mac_get, }, { .type = STATE_TYPE_STRING, .type_name = "string", .set = __state_string_set, .get = __state_string_get, }, }; static struct variable_str_type *state_find_type(enum state_variable_type type) { int i; for (i = 0; i < ARRAY_SIZE(types); i++) { if (type == types[i].type) { return &types[i]; } } return NULL; } static int __state_uint32_set(struct state_variable *var, const char *val) { struct state_uint32 *su32 = to_state_uint32(var); su32->value = strtoul(val, NULL, 0); return 0; } static int __state_uint8_set(struct state_variable *var, const char *val) { struct state_uint32 *su32 = to_state_uint32(var); unsigned long num; num = strtoul(val, NULL, 0); if (num > UINT8_MAX) return -ERANGE; su32->value = num; return 0; } static char *__state_uint32_get(struct state_variable *var) { struct state_uint32 *su32 = to_state_uint32(var); char *str; int ret; ret = asprintf(&str, "%u", su32->value); if (ret < 0) return ERR_PTR(-ENOMEM); return str; } static int __state_enum32_set(struct state_variable *sv, const char *val) { struct state_enum32 *enum32 = to_state_enum32(sv); int i; for (i = 0; i < enum32->num_names; i++) { if (!strcmp(enum32->names[i], val)) { enum32->value = i; return 0; } } return -EINVAL; } static char *__state_enum32_get(struct state_variable *var) { struct state_enum32 *enum32 = to_state_enum32(var); char *str; int ret; ret = asprintf(&str, "%s", enum32->names[enum32->value]); if (ret < 0) return ERR_PTR(-ENOMEM); return str; } static void __state_enum32_info(struct state_variable *var) { struct state_enum32 *enum32 = to_state_enum32(var); int i; printf(", values=["); for (i = 0; i < enum32->num_names; i++) printf("%s%s", enum32->names[i], i == enum32->num_names - 1 ? "" : ","); printf("]"); } static int string_to_ethaddr(const char *str, uint8_t enetaddr[6]) { int reg; char *e; if (!str || strlen(str) != 17) { memset(enetaddr, 0, 6); return -EINVAL; } if (str[2] != ':' || str[5] != ':' || str[8] != ':' || str[11] != ':' || str[14] != ':') return -EINVAL; for (reg = 0; reg < 6; ++reg) { enetaddr[reg] = strtoul(str, &e, 16); str = e + 1; } return 0; } static int __state_mac_set(struct state_variable *var, const char *val) { struct state_mac *mac = to_state_mac(var); uint8_t mac_save[6]; int ret; ret = string_to_ethaddr(val, mac_save); if (ret) return ret; memcpy(mac->value, mac_save, ARRAY_SIZE(mac_save)); return 0; } static char *__state_mac_get(struct state_variable *var) { struct state_mac *mac = to_state_mac(var); char *str; int ret; ret = asprintf(&str, "%02x:%02x:%02x:%02x:%02x:%02x", mac->value[0], mac->value[1], mac->value[2], mac->value[3], mac->value[4], mac->value[5]); if (ret < 0) return ERR_PTR(-ENOMEM); return str; } static int __state_string_set(struct state_variable *sv, const char *val) { struct state_string *string = to_state_string(sv); int ret; ret = state_string_copy_to_raw(string, val); if (ret) return ret; free(string->value); string->value = xstrdup(val); return 0; } static char *__state_string_get(struct state_variable *var) { struct state_string *string = to_state_string(var); char *str; int ret; if (string->raw[0]) str = strndup(string->raw, string->var.size); else str = strdup(""); if (!str) return ERR_PTR(-ENOMEM); return str; } char *state_get_var(struct state *state, const char *var) { struct state_variable *sv; struct variable_str_type *vtype; sv = state_find_var(state, var); if (IS_ERR(sv)) return NULL; vtype = state_find_type(sv->type); if (!vtype) return NULL; return vtype->get(sv); } static int state_set_var(struct state *state, const char *var, const char *val) { struct state_variable *sv; struct variable_str_type *vtype; int ret; sv = state_find_var(state, var); if (IS_ERR(sv)) return PTR_ERR(sv); vtype = state_find_type(sv->type); if (!vtype) return -ENODEV; if (!vtype->set) return -EPERM; ret = vtype->set(sv, val); if (ret) return ret; state->dirty = 1; return 0; } struct state *state_get(const char *name) { struct device_node *root, *node, *partition_node; char *path; struct state *state; int ret; const char *backend_type = NULL; struct state_variable *v; char *devpath; off_t offset; size_t size; root = of_read_proc_devicetree(); if (IS_ERR(root)) { fprintf(stderr, "Unable to read devicetree. %s\n", strerror(-PTR_ERR(root))); return ERR_CAST(root); } of_set_root_node(root); if (name) { node = of_find_node_by_path_or_alias(root, name); if (!node) { fprintf(stderr, "no such node: %s\n", name); return ERR_PTR(-ENOENT); } } else { node = of_find_node_by_path_or_alias(root, "state"); if (!node) node = of_find_node_by_path_or_alias(root, "/state"); if (!node) { fprintf(stderr, "Neither /aliases/state nor /state found\n"); return ERR_PTR(-ENOENT); } } if (verbose > 1) { printf("found state node %s:\n", node->full_name); of_print_nodes(node, 0); } partition_node = of_parse_phandle(node, "backend", 0); if (!partition_node) { fprintf(stderr, "cannot find backend node in %s\n", node->full_name); exit (1); } ret = of_get_devicepath(partition_node, &devpath, &offset, &size); if (ret) { fprintf(stderr, "Cannot find backend path in %s\n", node->full_name); return ERR_PTR(ret); } state = state_new_from_node(node, devpath, offset, size); if (IS_ERR(state)) { fprintf(stderr, "unable to initlialize state: %s\n", strerror(PTR_ERR(state))); return ERR_CAST(state); } return state; } enum opt { OPT_DUMP_SHELL = UCHAR_MAX + 1, }; static struct option long_options[] = { {"get", required_argument, 0, 'g' }, {"set", required_argument, 0, 's' }, {"name", required_argument, 0, 'n' }, {"dump", no_argument, 0, 'd' }, {"dump-shell", no_argument, 0, OPT_DUMP_SHELL }, {"verbose", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, }; static void usage(char *name) { printf( "Usage: %s [OPTIONS]\n" "\n" "-g, --get <variable> get the value of a variable\n" "-s, --set <variable>=<value> set the value of a variable\n" "-n, --name <name> specify the state to use (default=\"state\")\n" "-d, --dump dump the state\n" "--dump-shell dump the state suitable for shell sourcing\n" "-v, --verbose increase verbosity\n" "--help this help\n", name); } #define state_for_each_var(state, var) \ list_for_each_entry(var, &(state)->variables, list) struct state_set_get { char *arg; int get; struct list_head list; }; int main(int argc, char *argv[]) { struct state *state; struct state_variable *v; int ret, c, option_index; int do_dump = 0, do_dump_shell = 0; struct state_set_get *sg; struct list_head sg_list; char *statename = NULL; int lock_fd; INIT_LIST_HEAD(&sg_list); while (1) { c = getopt_long(argc, argv, "hg:s:dvn:", long_options, &option_index); if (c < 0) break; switch (c) { case 'h': usage(argv[0]); exit(0); case 'g': sg = xzalloc(sizeof(*sg)); sg->get = 1; sg->arg = optarg; list_add_tail(&sg->list, &sg_list); break; case 's': sg = xzalloc(sizeof(*sg)); sg->get = 0; sg->arg = optarg; list_add_tail(&sg->list, &sg_list); break; case 'd': do_dump = 1; break; case OPT_DUMP_SHELL: do_dump_shell = 1; break; case 'v': verbose++; break; case 'n': statename = optarg; break; } } lock_fd = open("/var/lock/barebox-state", O_CREAT | O_RDWR, 0600); if (lock_fd < 0) { fprintf(stderr, "Failed to open lock-file /var/lock/barebox-state\n"); exit(1); } ret = flock(lock_fd, LOCK_EX | LOCK_NB); if (ret < 0) { fprintf(stderr, "Failed to lock. Already locked by other process /var/lock/barebox-state.\n"); close(lock_fd); exit(1); } state = state_get(statename); if (IS_ERR(state)) { ret = 1; goto out_unlock; } if (do_dump) { state_for_each_var(state, v) { struct variable_str_type *vtype; vtype = state_find_type(v->type); if (!vtype) { fprintf(stderr, "no such type: %d\n", v->type); ret = 1; goto out_unlock; } printf("%s=%s", v->name, vtype->get(v)); if (verbose) { printf(", type=%s", vtype->type_name); if (vtype->info) vtype->info(v); } printf("\n"); } } if (do_dump_shell) { state_for_each_var(state, v) { struct variable_str_type *vtype; char *name, *ptr; int i; /* replace "." by "_" to make it var name shell compatible */ name = strdup(v->name); ptr = name; while ((ptr = strchr(ptr, '.'))) *ptr++ = '_'; vtype = state_find_type(v->type); printf("%s_%s=\"%s\"\n", state->name, name, vtype->get(v)); } } list_for_each_entry(sg, &sg_list, list) { if (sg->get) { char *val = state_get_var(state, sg->arg); if (!val) { fprintf(stderr, "no such variable: %s\n", sg->arg); ret = 1; goto out_unlock; } printf("%s\n", val); } else { char *var, *val; var = sg->arg; val = index(sg->arg, '='); if (!val) { fprintf(stderr, "usage: -s var=val\n"); ret = 1; goto out_unlock; } *val++ = '\0'; ret = state_set_var(state, var, val); if (ret) { fprintf(stderr, "Failed to set variable %s to %s: %s\n", var, val, strerror(-ret)); ret = 1; goto out_unlock; } } } if (state->dirty) { ret = state_save(state); if (ret) { fprintf(stderr, "Failed to save state: %s\n", strerror(-ret)); ret = 1; goto out_unlock; } } ret = 0; out_unlock: flock(lock_fd, LOCK_UN); close(lock_fd); return ret; }