Add support for per-variable callbacks to the "hashtable" functions. Signed-off-by: Joe Hershberger <joe.hershberger@ni.com> !!!fix comment in callbackmaster
parent
be11235ab8
commit
170ab11075
@ -0,0 +1,222 @@ |
||||
/*
|
||||
* (C) Copyright 2012 |
||||
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* 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. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <env_attr.h> |
||||
#include <errno.h> |
||||
#include <linux/string.h> |
||||
#include <malloc.h> |
||||
|
||||
/*
|
||||
* Iterate through the whole list calling the callback for each found element. |
||||
* "attr_list" takes the form: |
||||
* attributes = [^,:\s]* |
||||
* entry = name[:attributes] |
||||
* list = entry[,list] |
||||
*/ |
||||
int env_attr_walk(const char *attr_list, |
||||
int (*callback)(const char *name, const char *attributes)) |
||||
{ |
||||
const char *entry, *entry_end; |
||||
char *name, *attributes; |
||||
|
||||
if (!attr_list) |
||||
/* list not found */ |
||||
return 1; |
||||
|
||||
entry = attr_list; |
||||
do { |
||||
char *entry_cpy = NULL; |
||||
|
||||
entry_end = strchr(entry, ENV_ATTR_LIST_DELIM); |
||||
/* check if this is the last entry in the list */ |
||||
if (entry_end == NULL) { |
||||
int entry_len = strlen(entry); |
||||
|
||||
if (entry_len) { |
||||
/*
|
||||
* allocate memory to copy the entry into since |
||||
* we will need to inject '\0' chars and squash |
||||
* white-space before calling the callback |
||||
*/ |
||||
entry_cpy = malloc(entry_len + 1); |
||||
if (entry_cpy) |
||||
/* copy the rest of the list */ |
||||
strcpy(entry_cpy, entry); |
||||
else |
||||
return -ENOMEM; |
||||
} |
||||
} else { |
||||
int entry_len = entry_end - entry; |
||||
|
||||
if (entry_len) { |
||||
/*
|
||||
* allocate memory to copy the entry into since |
||||
* we will need to inject '\0' chars and squash |
||||
* white-space before calling the callback |
||||
*/ |
||||
entry_cpy = malloc(entry_len + 1); |
||||
if (entry_cpy) { |
||||
/* copy just this entry and null term */ |
||||
strncpy(entry_cpy, entry, entry_len); |
||||
entry_cpy[entry_len] = '\0'; |
||||
} else |
||||
return -ENOMEM; |
||||
} |
||||
} |
||||
|
||||
/* check if there is anything to process (e.g. not ",,,") */ |
||||
if (entry_cpy != NULL) { |
||||
attributes = strchr(entry_cpy, ENV_ATTR_SEP); |
||||
/* check if there is a ':' */ |
||||
if (attributes != NULL) { |
||||
/* replace the ':' with '\0' to term name */ |
||||
*attributes++ = '\0'; |
||||
/* remove white-space from attributes */ |
||||
attributes = strim(attributes); |
||||
} |
||||
/* remove white-space from name */ |
||||
name = strim(entry_cpy); |
||||
|
||||
/* only call the callback if there is a name */ |
||||
if (strlen(name) != 0) { |
||||
int retval = 0; |
||||
|
||||
retval = callback(name, attributes); |
||||
if (retval) { |
||||
free(entry_cpy); |
||||
return retval; |
||||
} |
||||
} |
||||
} |
||||
|
||||
free(entry_cpy); |
||||
entry = entry_end + 1; |
||||
} while (entry_end != NULL); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* Search for the last matching string in another string with the option to |
||||
* start looking at a certain point (i.e. ignore anything beyond that point). |
||||
*/ |
||||
static char *reverse_strstr(const char *searched, const char *search_for, |
||||
const char *searched_start) |
||||
{ |
||||
char *result = NULL; |
||||
|
||||
if (*search_for == '\0') |
||||
return (char *)searched; |
||||
|
||||
for (;;) { |
||||
char *match = strstr(searched, search_for); |
||||
|
||||
/*
|
||||
* Stop looking if no new match is found or looking past the |
||||
* searched_start pointer |
||||
*/ |
||||
if (match == NULL || (searched_start != NULL && |
||||
match + strlen(search_for) > searched_start)) |
||||
break; |
||||
|
||||
result = match; |
||||
searched = match + 1; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/*
|
||||
* Retrieve the attributes string associated with a single name in the list |
||||
* There is no protection on attributes being too small for the value |
||||
*/ |
||||
int env_attr_lookup(const char *attr_list, const char *name, char *attributes) |
||||
{ |
||||
const char *entry = NULL; |
||||
|
||||
if (!attributes) |
||||
/* bad parameter */ |
||||
return -1; |
||||
if (!attr_list) |
||||
/* list not found */ |
||||
return 1; |
||||
|
||||
entry = reverse_strstr(attr_list, name, NULL); |
||||
while (entry != NULL) { |
||||
const char *prevch = entry - 1; |
||||
const char *nextch = entry + strlen(name); |
||||
|
||||
/* Skip spaces */ |
||||
while (*prevch == ' ') |
||||
prevch--; |
||||
while (*nextch == ' ') |
||||
nextch++; |
||||
|
||||
/* check for an exact match */ |
||||
if ((entry == attr_list || |
||||
*prevch == ENV_ATTR_LIST_DELIM) && |
||||
(*nextch == ENV_ATTR_SEP || |
||||
*nextch == ENV_ATTR_LIST_DELIM || |
||||
*nextch == '\0')) |
||||
break; |
||||
|
||||
entry = reverse_strstr(attr_list, name, entry); |
||||
} |
||||
if (entry != NULL) { |
||||
int len; |
||||
|
||||
/* skip the name */ |
||||
entry += strlen(name); |
||||
/* skip spaces */ |
||||
while (*entry == ' ') |
||||
entry++; |
||||
if (*entry != ENV_ATTR_SEP) |
||||
len = 0; |
||||
else { |
||||
const char *delim; |
||||
static const char delims[] = { |
||||
ENV_ATTR_LIST_DELIM, ' ', '\0'}; |
||||
|
||||
/* skip the attr sep */ |
||||
entry += 1; |
||||
/* skip spaces */ |
||||
while (*entry == ' ') |
||||
entry++; |
||||
|
||||
delim = strpbrk(entry, delims); |
||||
if (delim == NULL) |
||||
len = strlen(entry); |
||||
else |
||||
len = delim - entry; |
||||
memcpy(attributes, entry, len); |
||||
} |
||||
attributes[len] = '\0'; |
||||
|
||||
/* success */ |
||||
return 0; |
||||
} |
||||
|
||||
/* not found in list */ |
||||
return 2; |
||||
} |
@ -0,0 +1,144 @@ |
||||
/*
|
||||
* (C) Copyright 2012 |
||||
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* 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. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <environment.h> |
||||
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
#endif |
||||
|
||||
/*
|
||||
* Look up a callback function pointer by name |
||||
*/ |
||||
struct env_clbk_tbl *find_env_callback(const char *name) |
||||
{ |
||||
struct env_clbk_tbl *clbkp; |
||||
int i; |
||||
int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); |
||||
|
||||
if (name == NULL) |
||||
return NULL; |
||||
|
||||
/* look up the callback in the linker-list */ |
||||
for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); |
||||
i < num_callbacks; |
||||
i++, clbkp++) { |
||||
if (strcmp(name, clbkp->name) == 0) |
||||
return clbkp; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
/*
|
||||
* Look for a possible callback for a newly added variable |
||||
* This is called specifically when the variable did not exist in the hash |
||||
* previously, so the blanket update did not find this variable. |
||||
*/ |
||||
void env_callback_init(ENTRY *var_entry) |
||||
{ |
||||
const char *var_name = var_entry->key; |
||||
const char *callback_list = getenv(ENV_CALLBACK_VAR); |
||||
char callback_name[256] = ""; |
||||
struct env_clbk_tbl *clbkp; |
||||
int ret = 1; |
||||
|
||||
/* look in the ".callbacks" var for a reference to this variable */ |
||||
if (callback_list != NULL) |
||||
ret = env_attr_lookup(callback_list, var_name, callback_name); |
||||
|
||||
/* only if not found there, look in the static list */ |
||||
if (ret) |
||||
ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, |
||||
callback_name); |
||||
|
||||
/* if an association was found, set the callback pointer */ |
||||
if (!ret && strlen(callback_name)) { |
||||
clbkp = find_env_callback(callback_name); |
||||
if (clbkp != NULL) |
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||
var_entry->callback = clbkp->callback + gd->reloc_off; |
||||
#else |
||||
var_entry->callback = clbkp->callback; |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Called on each existing env var prior to the blanket update since removing |
||||
* a callback association should remove its callback. |
||||
*/ |
||||
static int clear_callback(ENTRY *entry) |
||||
{ |
||||
entry->callback = NULL; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* Call for each element in the list that associates variables to callbacks |
||||
*/ |
||||
static int set_callback(const char *name, const char *value) |
||||
{ |
||||
ENTRY e, *ep; |
||||
struct env_clbk_tbl *clbkp; |
||||
|
||||
e.key = name; |
||||
e.data = NULL; |
||||
hsearch_r(e, FIND, &ep, &env_htab, 0); |
||||
|
||||
/* does the env variable actually exist? */ |
||||
if (ep != NULL) { |
||||
/* the assocaition delares no callback, so remove the pointer */ |
||||
if (value == NULL || strlen(value) == 0) |
||||
ep->callback = NULL; |
||||
else { |
||||
/* assign the requested callback */ |
||||
clbkp = find_env_callback(value); |
||||
if (clbkp != NULL) |
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||
ep->callback = clbkp->callback + gd->reloc_off; |
||||
#else |
||||
ep->callback = clbkp->callback; |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int on_callbacks(const char *name, const char *value, enum env_op op, |
||||
int flags) |
||||
{ |
||||
/* remove all callbacks */ |
||||
hwalk_r(&env_htab, clear_callback); |
||||
|
||||
/* configure any static callback bindings */ |
||||
env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback); |
||||
/* configure any dynamic callback bindings */ |
||||
env_attr_walk(value, set_callback); |
||||
|
||||
return 0; |
||||
} |
||||
U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); |
@ -0,0 +1,55 @@ |
||||
/*
|
||||
* (C) Copyright 2012 |
||||
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* 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. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef __ENV_ATTR_H__ |
||||
#define __ENV_ATTR_H__ |
||||
|
||||
#define ENV_ATTR_LIST_DELIM ',' |
||||
#define ENV_ATTR_SEP ':' |
||||
|
||||
/*
|
||||
* env_attr_walk takes as input an "attr_list" that takes the form: |
||||
* attributes = [^,:\s]* |
||||
* entry = name[:attributes] |
||||
* list = entry[,list] |
||||
* It will call the "callback" function with the "name" and attribute as "value" |
||||
* The callback may return a non-0 to abort the list walk. |
||||
* This return value will be passed through to the caller. |
||||
* 0 is returned on success. |
||||
*/ |
||||
extern int env_attr_walk(const char *attr_list, |
||||
int (*callback)(const char *name, const char *value)); |
||||
|
||||
/*
|
||||
* env_attr_lookup takes as input an "attr_list" with the same form as above. |
||||
* It also takes as input a "name" to look for. |
||||
* If the name is found in the list, it's value is copied into "attributes". |
||||
* There is no protection on attributes being too small for the value. |
||||
* It returns -1 if attributes is NULL, 1 if "name" is not found, 2 if |
||||
* "attr_list" is NULL. |
||||
* Returns 0 on success. |
||||
*/ |
||||
extern int env_attr_lookup(const char *attr_list, const char *name, |
||||
char *attributes); |
||||
|
||||
#endif /* __ENV_ATTR_H__ */ |
@ -0,0 +1,62 @@ |
||||
/*
|
||||
* (C) Copyright 2012 |
||||
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* 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. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef __ENV_CALLBACK_H__ |
||||
#define __ENV_CALLBACK_H__ |
||||
|
||||
#include <linker_lists.h> |
||||
#include <search.h> |
||||
|
||||
#define ENV_CALLBACK_VAR ".callbacks" |
||||
|
||||
/* Board configs can define additional static callback bindings */ |
||||
#ifndef CONFIG_ENV_CALLBACK_LIST_STATIC |
||||
#define CONFIG_ENV_CALLBACK_LIST_STATIC |
||||
#endif |
||||
|
||||
/*
|
||||
* This list of callback bindings is static, but may be overridden by defining |
||||
* a new association in the ".callbacks" environment variable. |
||||
*/ |
||||
#define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ |
||||
CONFIG_ENV_CALLBACK_LIST_STATIC |
||||
|
||||
struct env_clbk_tbl { |
||||
const char *name; /* Callback name */ |
||||
int (*callback)(const char *name, const char *value, enum env_op op, |
||||
int flags); |
||||
}; |
||||
|
||||
struct env_clbk_tbl *find_env_callback(const char *); |
||||
void env_callback_init(ENTRY *var_entry); |
||||
|
||||
/*
|
||||
* Define a callback that can be associated with variables. |
||||
* when associated through the ".callbacks" environment variable, the callback |
||||
* will be executed any time the variable is inserted, overwritten, or deleted. |
||||
*/ |
||||
#define U_BOOT_ENV_CALLBACK(name, callback) \ |
||||
ll_entry_declare(struct env_clbk_tbl, name, env_clbk, env_clbk) = \
|
||||
{#name, callback} |
||||
|
||||
#endif /* __ENV_CALLBACK_H__ */ |
Loading…
Reference in new issue