meta-digi: add 'ubootenv' tool

https://jira.digi.com/browse/DEL-286

Signed-off-by: Javier Viguera <javier.viguera@digi.com>
This commit is contained in:
Javier Viguera 2013-03-12 16:47:43 +01:00
parent df80921748
commit 26d98d13ff
7 changed files with 833 additions and 0 deletions

View File

@ -0,0 +1,32 @@
SUMMARY = "Digi's ubootenv tool"
SECTION = "base"
LICENSE = "GPL-2.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
PR = "r0"
DEPENDS = "libdigi nvram"
SRC_URI = " \
file://env_funcs.c \
file://env_funcs.h \
file://environment.h \
file://main_env.c \
"
S = "${WORKDIR}"
GIT_SHA1 = "$(cd ${THISDIR} && git rev-parse --short HEAD)"
CFLAGS += "-Wall -DLINUX -DGIT_SHA1=\"${GIT_SHA1}\" -I${STAGING_INCDIR}/libdigi"
do_compile() {
${CC} ${CFLAGS} -o ubootenv main_env.c env_funcs.c -lnvram -ldigi
}
do_install() {
mkdir -p ${D}${base_sbindir}
install -m 0755 ubootenv ${D}${base_sbindir}/
}
PACKAGE_ARCH = "${MACHINE_ARCH}"

View File

@ -0,0 +1,183 @@
/*
* env_funcs.c
*
* Copyright (C) 2006-2013 by Digi International Inc.
* All rights reserved.
*
* 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.
*
* Description: Function prototypes for all the flavors of the NVRAM tool
*
*/
#include <stdio.h>
#include <string.h>
#include "env_funcs.h"
/*
* Function: get_var_value
* Return: NULL on failure/ pointer where var value starts on succes
* Description: Checks if the variable name is contained on the string and
* returns a pointer where the variable value starts.
*/
char *get_var_value(const char *from, const char *var_name, char sep)
{
char *separator;
char *start;
if (((separator = strchr(from, sep)) != NULL) &&
((start = strstr(from, var_name)) != NULL)) {
if (start < separator)
return (++separator);
}
return NULL;
}
/*
* Function: get_var_name
* Return: 1 on success, 0 otherwise
* Description: Retrieves the var name from a var string
*/
int get_var_name(const char *from, char *var_name, char sep)
{
char *separator;
if ((separator = strchr(from, sep)) != NULL) {
while (from < separator)
*var_name++ = *from++;
*var_name = 0;
return 1;
}
return 0;
}
/*
* Function: get_next_env_string
* Return: the string to the next environment variable in UBOOT or
* NULL on failure
* Description: returns the addr of the next string in a data structure
* <string>\0<string>\0\0
* It does an offset calculation to check for overflow.
*/
char *get_next_env_string(char *from, char *till)
{
if (*from == 0)
return NULL;
while (from < till && *from)
from++;
if (from == till && *from)
return NULL; // Indicate string to long
return (++from);
}
/*
* Function: get_var_addr
* Return: the pointer to the address string or NULL if not found.
* Description: Returns the addr of the variable in a data structure
* <string>\0<string>\0\0
* It does an offset calculation to check for overflow.
*/
char *get_var_addr(char *from, char *till, char *var_name)
{
char *data = from;
char *var_addr;
char var_name_temp[ENV_MAX_VAR_NAME_LEN + 1];
sprintf(var_name_temp, "%s%s", var_name, "=");
do {
if (*data && data < till) {
if ((var_addr = strstr(data, var_name_temp)) != NULL
&& (var_addr == data))
return var_addr < till ? var_addr : NULL;
}
} while ((data = get_next_env_string(data, till)) != NULL);
return NULL;
}
/*
* Function: get_end_mark
* Description:
*/
char *get_end_mark(char *from, char *till)
{
while (from < till) {
while (*from)
from++;
if (from >= till)
return NULL;
if (*(++from) == 0)
return from;
}
return NULL;
}
/*
* Function: remove_var
* Return: 1 on success, 0 otherwise
* Description: Remove environment variables
*/
int remove_var(char *from, char *till, char *var_name)
{
char *var_addr;
char *env_end;
char *var_end;
/* Check if variable already exists */
if ((var_addr = get_var_addr(from, till, var_name)) != NULL) {
if ((env_end = get_end_mark(var_addr, till)) != NULL) {
if ((var_end = get_next_env_string(var_addr, till)) != NULL) {
while (var_end <= env_end)
*var_addr++ = *var_end++;
while (var_addr <= env_end)
*var_addr++ = 0; /* Just to have a clean environment :-) */
return 1;
}
}
}
return 0;
}
/*
* Function: add_var
* Return: 1 on success, 0 otherwise
* Description: Add a new environment variable
*/
int add_var(char *from, char *till, char *var_str)
{
char *var_addr;
char var_name[ENV_MAX_VAR_NAME_LEN];
if (get_var_name(var_str, var_name, '=')) {
/* Check if variable already exists */
if ((var_addr = get_var_addr(from, till, var_name)) != NULL) {
/* @TODO: remove more?? could be that it were there more than once?? */
if (!remove_var(var_addr, till, var_name))
return 0;
}
/* Append the variable to the end */
if ((var_addr = get_end_mark(from, till)) != NULL) {
/* Check if environment is empty. If yes, start from the beginning */
if (var_addr == (from + 1))
var_addr--;
while (var_addr < till && *var_str)
*var_addr++ = *var_str++;
*var_addr++ = 0;
*var_addr = 0;
return 1;
} else {
fprintf(stderr, "Unable to find environment end\n");
}
}
return 0;
}

View File

@ -0,0 +1,29 @@
/*
* env_funcs.h
*
* Copyright (C) 2006-2013 by Digi International Inc.
* All rights reserved.
*
* 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.
*
* Description: Data types and prototypes for parsing the NVRAM environment
*
*/
#ifndef ENV_FUNCS_H
#define ENV_FUNCS_H
#define ENV_MAX_VAR_NAME_LEN 50
#define ENV_MAX_VAR_VAL_LEN 256
char *get_var_value(const char *from, const char *var_name, char sep);
char *get_next_env_string(char *from, char *till);
char *get_var_addr(char *from, char *till, char *var_name);
char *get_end_mark(char *from, char *till);
int get_var_name(const char *from, char *var_name, char sep);
int add_var(char *from, char *till, char *var_str);
int remove_var(char *from, char *till, char *var_name);
#endif

View File

@ -0,0 +1,26 @@
/*
* environment.h
*
* Copyright (C) 2006-2013 by Digi International Inc.
* All rights reserved.
*
* 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.
*
* Description: Data types and definitions for environment in NVRAM
*
*/
#ifndef __ENV_TOOL_H_
#define __ENV_TOOL_H_
#define VAR_SEP '\0'
#define VAR_ASIGN '='
typedef struct {
unsigned long crc;
char data[];
} env_t;
#endif

View File

@ -0,0 +1,561 @@
/*
* main_env.c
*
* Copyright (C) 2006-2013 by Digi International Inc.
* All rights reserved.
*
* 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.
*
* Description: main() and user code to manage u-boot and linux environment
*
*/
#define VERSION "1.8" "-g"GIT_SHA1
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <mtd/mtd-user.h> /* MEMERASE */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h> /* ioctl */
#include <unistd.h>
#include <libdigi/crc32.h>
#include <nvram.h> /* Nv* */
#include "env_funcs.h"
#include "environment.h"
#define APP_UBOOTENV "ubootenv"
#define APP_PRODINFOENV "prodinfoenv"
static char *fileadd = NULL;
static char *printlist = NULL;
static char *setstring = NULL;
static char *eraselist = NULL;
static char *cmdname = NULL;
static int dump = 0, clean = 0;
static nv_os_type_e envType;
static const char *envTypes[] = {
[NVOS_NONE] = "None",
[NVOS_UBOOT] = "U-Boot",
[NVOS_PROD_INFO] = "Product Info",
};
static const size_t envDefaultSizes[] = {
[NVOS_NONE] = 0,
[NVOS_UBOOT] = 8192, /* Default value is CONFIG_ENV_SIZE, normally set by U-Boot */
[NVOS_PROD_INFO] = PROD_INFO_DATA_SIZE, /* Defined in nvram.h but shouldn't be except uboot needs it */
};
static const char* excludeVars[][2] = {
{"ethaddr", "ethaddr1" },
{"wlanaddr", "ethaddr2" },
{"eth1addr", "ethaddr3" },
{"ipaddr", "ip1" },
{"ipaddr_wlan", "ip2" },
{"ipaddr1", "ip3" },
{"netmask", "netmask1" },
{"netmask_wlan","netmask2" },
{"netmask1", "netmask3" },
{"serverip", "server" },
{"gatewayip", "gateway" },
{"dnsip", "dns1" },
{"dnsip2", "dns2" },
{"dhcp", "dhcp1" },
{"dhcp_wlan", "dhcp2" },
{"dhcp1", "dhcp3" },
};
static const char *env_os_type_to_string(void)
{
return envTypes[envType];
}
static int env_onetime_writable(void)
{
if (NVOS_PROD_INFO == envType)
return 1;
return 0;
}
static int env_is_empty(env_t * env, unsigned int envlen)
{
char *from = (char *)&env->data;
char *till = (char *)&env->data + envlen;
char *end;
end = get_end_mark(from, till);
/* Check if environment is empty. If yes, start from the beginning */
if (end == (from + 1))
return 1;
return 0;
}
static int env_have_to_use_nvram(const char *varname)
{
int j;
if (envType == NVOS_UBOOT) {
for (j = 0; j < ARRAY_SIZE(excludeVars); j++) {
if (strcmp(varname, excludeVars[j][0]) == 0)
return j;
}
}
return -1;
}
static void show_usage(void)
{
fprintf(stdout, "Usage: %s [options]\n"
"%s %s Copyright Digi International Inc.\n\n"
"Prints or updates the %s environment\n"
"\n"
" -d, --dump Prints the values of all the environment\n"
" -p, --print 'var_name_list' Prints the value of the list of variables\n"
" The list has to be simple quoted ('')\n"
" -s, --set 'var_name=var_value' Sets var_value in the variable var_name\n"
" The string has to be simple quoted ('') to allow\n"
" spaces\n"
" -e --erase 'var_name_list' Removes the list of variables (simple quoted)\n"
" -c --clean Removes all variables\n"
" -a --fileadd file_name Adds variables from file_name. To init the full\n"
" environment from file use -c -a simultaneously\n"
" -h --help Displays usage information\n\n",
cmdname, cmdname, VERSION, env_os_type_to_string());
if (env_onetime_writable()) {
fprintf(stdout, " WARNING: Variables can only be set the first time\n"
" and become read-only afterwards.\n");
}
}
static void show_usage_and_exit(int exit_code)
{
show_usage();
exit(exit_code);
}
static void process_options(int argc, char *argv[])
{
int opt_index, opt, optcount = 0;
static const char *short_options = "?hdcp:s:e:a:";
static const struct option long_options[] = {
{"dump", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"erase", required_argument, NULL, 'e'},
{"clean", no_argument, NULL, 'c'},
{"fileadd", required_argument, NULL, 'a'},
{"print", required_argument, NULL, 'p'},
{"set", required_argument, NULL, 's'},
{0, 0, 0, 0},
};
for (opt_index = 0;;) {
opt = getopt_long(argc, argv, short_options, long_options, &opt_index);
if (opt == EOF)
break;
switch (opt) {
case 'd':
dump = 1;
break;
case 'p':
printlist = optarg;
break;
case 's':
setstring = optarg;
break;
case 'a':
fileadd = optarg;
break;
case 'e':
eraselist = optarg;
break;
case 'c':
clean = 1;
break;
case 'h':
case '?':
show_usage_and_exit(EXIT_SUCCESS);
break;
}
optcount++;
}
if (optcount == 0)
show_usage_and_exit(EXIT_FAILURE);
/* Check options */
if (dump && (printlist != NULL)) {
fprintf(stderr, "--dump and --print can't be used simultaneously\n");
show_usage_and_exit(EXIT_FAILURE);
}
if (clean && (eraselist != NULL)) {
fprintf(stderr, "--clean and --erase can't be used simultaneously\n");
show_usage_and_exit(EXIT_FAILURE);
}
}
static int env_add_var(const char *varstring, env_t * env, unsigned int envlen)
{
char *nvramCmd[3] = { "set", "network", NULL };
char tmpstr[50];
char *varval;
int j;
if (varstring == NULL)
return -EINVAL;
if (!get_var_name(varstring, tmpstr, VAR_ASIGN))
return -EINVAL;
/* Check if is a special variable */
if ((j = env_have_to_use_nvram(tmpstr)) != -1) {
if ((varval = get_var_value(varstring, excludeVars[j][0], VAR_ASIGN)) != NULL) {
/* Define command to be used by nvram */
sprintf(tmpstr, "%s=%s", excludeVars[j][1], varval);
nvramCmd[2] = tmpstr;
if (!NvCmdLine(3, (const char **)nvramCmd)) {
return -EINVAL;
}
}
} else {
if (!add_var(env->data, env->data + envlen, (char *)varstring)) {
return -EINVAL;
}
}
return 0;
}
static int env_add_vars_from_file(char *filename, env_t * env, unsigned int envlen)
{
FILE *fp;
char line[ENV_MAX_VAR_NAME_LEN + ENV_MAX_VAR_VAL_LEN + 3];
int ret = EXIT_SUCCESS;
if ((fp = fopen(filename, "r")) == NULL)
return -errno;
while (!feof(fp)) {
if (fgets(line, sizeof(line), fp)) {
/* TODO should we remove comments starting with # ?? */
/* Remove '\n' */
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = 0;
if (env_add_var((const char *)line, env, envlen)) {
fprintf(stderr, "Unable to add environment variable %s\n",
line);
ret = -EINVAL;
goto out;
}
}
}
out:
if (fp)
fclose(fp);
return ret;
}
static void env_remove_varlist(const char *varlist, env_t * env, unsigned int envlen)
{
char *var;
var = strtok((char *)varlist, " ");
while (var != NULL) {
if (!remove_var(env->data, env->data + envlen, var))
fprintf(stderr, "Unable to remove environment variable %s\n", var);
var = strtok(NULL, " ");
}
}
static int env_validate(env_t * env, int datalen, int verbose)
{
unsigned long new_crc;
/* Check stored crc with data */
new_crc = crc32(0, (const unsigned char *)env->data, datalen);
if ((unsigned int)env->crc != new_crc) {
if ( verbose ) {
fprintf(stderr, "CRC failure: got 0x%08x expected 0x%08x\n",
(unsigned int)env->crc, (unsigned int)new_crc);
}
return 1;
}
return 0;
}
static void env_printenv_nvram_vars(char *varname)
{
struct nv_critical *crit;
nv_param_ip_t *ip_params;
int index;
int oneloop = 0;
if (NvCriticalGet(&crit)) {
ip_params = &crit->s.p.xIP;
for (index = 3; index < ARRAY_SIZE(excludeVars); index++) {
if (varname != NULL) {
if ((index = env_have_to_use_nvram(varname)) == -1)
break;
oneloop = 1;
}
switch (index) {
case 3:
fprintf(stdout, "ipaddr=%s\n",
NvToStringIP(ip_params->axDevice[0].uiIP));
break;
case 4:
fprintf(stdout, "ipaddr_wlan=%s\n",
NvToStringIP(ip_params->axDevice[1].uiIP));
break;
case 5:
fprintf(stdout, "ipaddr1=%s\n",
NvToStringIP(crit->s.p.eth1dev.uiIP));
break;
case 6:
fprintf(stdout, "netmask=%s\n",
NvToStringIP(ip_params->axDevice[0].uiNetMask));
break;
case 7:
fprintf(stdout, "netmask_wlan=%s\n",
NvToStringIP(ip_params->axDevice[1].uiNetMask));
break;
case 8:
fprintf(stdout, "netmask1=%s\n",
NvToStringIP(crit->s.p.eth1dev.uiNetMask));
break;
case 9:
fprintf(stdout, "serverip=%s\n",
NvToStringIP(ip_params->uiIPServer));
break;
case 10:
fprintf(stdout, "gatewayip=%s\n",
NvToStringIP(ip_params->uiIPGateway));
break;
case 11:
fprintf(stdout, "dnsip=%s\n",
NvToStringIP(ip_params->auiIPDNS[0]));
break;
case 12:
fprintf(stdout, "dnsip2=%s\n",
NvToStringIP(ip_params->auiIPDNS[1]));
break;
case 13:
fprintf(stdout, "dhcp=%s\n",
(ip_params->axDevice[0].flags.bDHCP ? "on" : "off"));
break;
case 14:
fprintf(stdout, "dhcp_wlan=%s\n",
(ip_params->axDevice[1].flags.bDHCP ? "on" : "off"));
break;
case 15:
fprintf(stdout, "dhcp1=%s\n",
(crit->s.p.eth1dev.flags.bDHCP ? "on" : "off"));
break;
}
if (oneloop)
break;
}
}
}
static void env_printenv(int dump, char *varlist, env_t * env, unsigned int envlen)
{
char *var;
char *data = env->data;
char *varinenv;
if (dump) {
/* print the full environment */
while (data != NULL && *data) {
fprintf(stdout, "%s\n", data);
data = get_next_env_string(data, env->data + envlen);
}
if (envType == NVOS_UBOOT)
env_printenv_nvram_vars(NULL);
return;
} else {
var = strtok((char *)varlist, " ");
while (var != NULL) {
if (envType == NVOS_UBOOT)
env_printenv_nvram_vars(var);
if ((varinenv =
get_var_addr(env->data, env->data + envlen, var)) != NULL) {
fprintf(stdout, "%s\n", varinenv);
}
var = strtok(NULL, " ");
}
}
}
int main(int argc, char *argv[])
{
unsigned int envlen = 0;
env_t *env = NULL;
nv_param_os_cfg_t xCfg;
size_t iSize;
int save_env = 0;
int ret = EXIT_SUCCESS;
int found;
char *cmdstart;
cmdname = argv[0];
cmdname = *argv;
if ((cmdstart = strrchr(cmdname, '/')) != NULL) {
cmdname = cmdstart + 1;
}
if (strcmp(cmdname, APP_UBOOTENV) == 0)
envType = NVOS_UBOOT;
else if (strcmp(cmdname, APP_PRODINFOENV) == 0)
envType = NVOS_PROD_INFO;
else {
fprintf(stderr, "This application can be invoked as:\n"
"%s\n%s\n\n", APP_UBOOTENV, APP_PRODINFOENV);
return EXIT_FAILURE;
}
/* read and process command line */
process_options(argc, argv);
if (!NvInit(NVR_AUTO)) {
fprintf(stderr, "Unable to initialize nvram\n");
return EXIT_FAILURE;
}
found = NvOSCfgFind(&xCfg, envType);
if (!found && clean) {
/* Add missing section to nvram if --clean specified */
if (envDefaultSizes[envType] != 0 ) {
if (!NvOSCfgAdd(envType, envDefaultSizes[envType])) {
fprintf(stderr, "Unable to add missing %s environment to flash\n",
env_os_type_to_string());
return EXIT_FAILURE;
}
/* Try again to find our section */
found = NvOSCfgFind(&xCfg, envType);
}
}
if (!found) {
fprintf(stderr, "Unable to detect %s environment in flash\n",
env_os_type_to_string());
return EXIT_FAILURE;
}
env = malloc(xCfg.uiSize);
if (NULL == env) {
perror("malloc");
return EXIT_FAILURE;
}
memset(env, 0, xCfg.uiSize);
/* Adjust envlen for just for data, crc32 value not included */
envlen = xCfg.uiSize - sizeof(unsigned long);
if (!NvOSCfgGet(envType, (void *)env, xCfg.uiSize, &iSize)) {
/* This can't fail; we either found it, created it, or exited. */
/* But just to be safe... */
fprintf(stderr, "Unable to get %s environment from flash\n",
env_os_type_to_string());
ret = EXIT_FAILURE;
goto free_and_ret;
}
/* Check if env is one-time writable, is valid, and was already written */
if (env_onetime_writable() && !env_validate(env, envlen, 0) &&
!env_is_empty(env, envlen)) {
if ( clean || fileadd || eraselist || setstring ) {
fprintf(stderr, "Environment is one-time writable only\n");
ret = EXIT_FAILURE;
goto free_and_ret;
}
}
if (clean) {
memset(env, 0, xCfg.uiSize);
save_env = 1;
} else {
if (env_validate(env, envlen, 1)) {
fprintf(stderr, "Invalid %s environment found\n",
env_os_type_to_string());
ret = EXIT_FAILURE;
goto free_and_ret;
}
}
if (fileadd != NULL) {
if (env_add_vars_from_file(fileadd, env, envlen) < 0) {
fprintf(stderr, "Unable to add %s environment from file %s\n",
env_os_type_to_string(), fileadd);
ret = EXIT_FAILURE;
goto free_and_ret;
}
save_env = 1;
}
if (eraselist != NULL) {
env_remove_varlist(eraselist, env, envlen);
save_env = 1;
}
if (setstring != NULL) {
if (env_add_var(setstring, env, envlen)) {
fprintf(stderr, "Unable to add environment variable %s\n", setstring);
ret = EXIT_FAILURE;
goto free_and_ret;
}
save_env = 1;
}
if (dump) {
env_printenv(1, NULL, env, envlen);
} else if (printlist != NULL) {
env_printenv(0, printlist, env, envlen);
}
if (save_env) {
/* Compute new crc32 value just in case we are going to update the value in flash */
env->crc = crc32(0, (const unsigned char *)env->data, envlen);
if (!NvOSCfgSet(envType, env, xCfg.uiSize)) {
fprintf(stderr, "Unable to set %s environment to store in flash\n",
env_os_type_to_string());
ret = EXIT_FAILURE;
}
if (!NvSave()) {
fprintf(stderr, "Unable to save %s environment in flash\n",
env_os_type_to_string());
ret = EXIT_FAILURE;
}
}
free_and_ret:
free(env);
return ret;
}

View File

@ -39,6 +39,7 @@ RDEPENDS_${PN} = "\
${VIRTUAL-RUNTIME_login_manager} \ ${VIRTUAL-RUNTIME_login_manager} \
${VIRTUAL-RUNTIME_init_manager} \ ${VIRTUAL-RUNTIME_init_manager} \
${VIRTUAL-RUNTIME_update-alternatives} \ ${VIRTUAL-RUNTIME_update-alternatives} \
ubootenv \
update-flash \ update-flash \
usbutils \ usbutils \
${MACHINE_ESSENTIAL_EXTRA_RDEPENDS}" ${MACHINE_ESSENTIAL_EXTRA_RDEPENDS}"

View File

@ -39,6 +39,7 @@ RDEPENDS_${PN} = "\
${VIRTUAL-RUNTIME_login_manager} \ ${VIRTUAL-RUNTIME_login_manager} \
${VIRTUAL-RUNTIME_init_manager} \ ${VIRTUAL-RUNTIME_init_manager} \
${VIRTUAL-RUNTIME_update-alternatives} \ ${VIRTUAL-RUNTIME_update-alternatives} \
ubootenv \
update-flash \ update-flash \
usbutils \ usbutils \
${MACHINE_ESSENTIAL_EXTRA_RDEPENDS}" ${MACHINE_ESSENTIAL_EXTRA_RDEPENDS}"