562 lines
14 KiB
C
562 lines
14 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|