summaryrefslogtreecommitdiff
path: root/gpxe/src/core/settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpxe/src/core/settings.c')
-rw-r--r--gpxe/src/core/settings.c500
1 files changed, 373 insertions, 127 deletions
diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c
index f34eb664..7d83101c 100644
--- a/gpxe/src/core/settings.c
+++ b/gpxe/src/core/settings.c
@@ -16,6 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -37,85 +39,326 @@
*
*/
-/** Registered settings */
-static struct setting settings[0]
- __table_start ( struct setting, settings );
-static struct setting settings_end[0]
- __table_end ( struct setting, settings );
+/******************************************************************************
+ *
+ * Generic settings blocks
+ *
+ ******************************************************************************
+ */
-/** Registered setting types */
-static struct setting_type setting_types[0]
- __table_start ( struct setting_type, setting_types );
-static struct setting_type setting_types_end[0]
- __table_end ( struct setting_type, setting_types );
+/**
+ * A generic setting
+ *
+ */
+struct generic_setting {
+ /** List of generic settings */
+ struct list_head list;
+ /** Setting */
+ struct setting setting;
+ /** Size of setting name */
+ size_t name_len;
+ /** Size of setting data */
+ size_t data_len;
+};
-/** Registered settings applicators */
-static struct settings_applicator settings_applicators[0]
- __table_start ( struct settings_applicator, settings_applicators );
-static struct settings_applicator settings_applicators_end[0]
- __table_end ( struct settings_applicator, settings_applicators );
+/**
+ * Get generic setting name
+ *
+ * @v generic Generic setting
+ * @ret name Generic setting name
+ */
+static inline void * generic_setting_name ( struct generic_setting *generic ) {
+ return ( ( ( void * ) generic ) + sizeof ( *generic ) );
+}
-/******************************************************************************
+/**
+ * Get generic setting data
*
- * Registered settings blocks
+ * @v generic Generic setting
+ * @ret data Generic setting data
+ */
+static inline void * generic_setting_data ( struct generic_setting *generic ) {
+ return ( ( ( void * ) generic ) + sizeof ( *generic ) +
+ generic->name_len );
+}
+
+/**
+ * Find generic setting
*
- ******************************************************************************
+ * @v generics Generic settings block
+ * @v setting Setting to find
+ * @ret generic Generic setting, or NULL
*/
+static struct generic_setting *
+find_generic_setting ( struct generic_settings *generics,
+ struct setting *setting ) {
+ struct generic_setting *generic;
+
+ list_for_each_entry ( generic, &generics->list, list ) {
+ if ( setting_cmp ( &generic->setting, setting ) == 0 )
+ return generic;
+ }
+ return NULL;
+}
/**
- * Store value of simple setting
+ * Store value of generic setting
*
- * @v options DHCP option block
+ * @v settings Settings block
* @v setting Setting to store
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
-int simple_settings_store ( struct settings *settings, struct setting *setting,
- const void *data, size_t len ) {
- struct simple_settings *simple =
- container_of ( settings, struct simple_settings, settings );
- return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag,
- data, len );
+int generic_settings_store ( struct settings *settings,
+ struct setting *setting,
+ const void *data, size_t len ) {
+ struct generic_settings *generics =
+ container_of ( settings, struct generic_settings, settings );
+ struct generic_setting *old;
+ struct generic_setting *new = NULL;
+ size_t name_len;
+
+ /* Identify existing generic setting, if any */
+ old = find_generic_setting ( generics, setting );
+
+ /* Create new generic setting, if required */
+ if ( len ) {
+ /* Allocate new generic setting */
+ name_len = ( strlen ( setting->name ) + 1 );
+ new = zalloc ( sizeof ( *new ) + name_len + len );
+ if ( ! new )
+ return -ENOMEM;
+
+ /* Populate new generic setting */
+ new->name_len = name_len;
+ new->data_len = len;
+ memcpy ( &new->setting, setting, sizeof ( new->setting ) );
+ new->setting.name = generic_setting_name ( new );
+ memcpy ( generic_setting_name ( new ),
+ setting->name, name_len );
+ memcpy ( generic_setting_data ( new ), data, len );
+ }
+
+ /* Delete existing generic setting, if any */
+ if ( old ) {
+ list_del ( &old->list );
+ free ( old );
+ }
+
+ /* Add new setting to list, if any */
+ if ( new )
+ list_add ( &new->list, &generics->list );
+
+ return 0;
}
/**
- * Fetch value of simple setting
+ * Fetch value of generic setting
*
- * @v options DHCP option block
+ * @v settings Settings block
* @v setting Setting to fetch
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
-int simple_settings_fetch ( struct settings *settings, struct setting *setting,
- void *data, size_t len ) {
- struct simple_settings *simple =
- container_of ( settings, struct simple_settings, settings );
- return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len );
+int generic_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ struct generic_settings *generics =
+ container_of ( settings, struct generic_settings, settings );
+ struct generic_setting *generic;
+
+ /* Find generic setting */
+ generic = find_generic_setting ( generics, setting );
+ if ( ! generic )
+ return -ENOENT;
+
+ /* Copy out generic setting data */
+ if ( len > generic->data_len )
+ len = generic->data_len;
+ memcpy ( data, generic_setting_data ( generic ), len );
+ return generic->data_len;
+}
+
+/**
+ * Clear generic settings block
+ *
+ * @v settings Settings block
+ */
+void generic_settings_clear ( struct settings *settings ) {
+ struct generic_settings *generics =
+ container_of ( settings, struct generic_settings, settings );
+ struct generic_setting *generic;
+ struct generic_setting *tmp;
+
+ list_for_each_entry_safe ( generic, tmp, &generics->list, list ) {
+ list_del ( &generic->list );
+ free ( generic );
+ }
+ assert ( list_empty ( &generics->list ) );
}
-/** Simple settings operations */
-struct settings_operations simple_settings_operations = {
- .store = simple_settings_store,
- .fetch = simple_settings_fetch,
+/** Generic settings operations */
+struct settings_operations generic_settings_operations = {
+ .store = generic_settings_store,
+ .fetch = generic_settings_fetch,
+ .clear = generic_settings_clear,
};
-/** Root simple settings block */
-struct simple_settings simple_settings_root = {
+/******************************************************************************
+ *
+ * Registered settings blocks
+ *
+ ******************************************************************************
+ */
+
+/** Root generic settings block */
+struct generic_settings generic_settings_root = {
.settings = {
.refcnt = NULL,
.name = "",
.siblings =
- LIST_HEAD_INIT ( simple_settings_root.settings.siblings ),
+ LIST_HEAD_INIT ( generic_settings_root.settings.siblings ),
.children =
- LIST_HEAD_INIT ( simple_settings_root.settings.children ),
- .op = &simple_settings_operations,
+ LIST_HEAD_INIT ( generic_settings_root.settings.children ),
+ .op = &generic_settings_operations,
},
+ .list = LIST_HEAD_INIT ( generic_settings_root.list ),
};
/** Root settings block */
-#define settings_root simple_settings_root.settings
+#define settings_root generic_settings_root.settings
+
+/**
+ * Find child named settings block
+ *
+ * @v parent Parent settings block
+ * @v name Name within this parent
+ * @ret settings Settings block, or NULL
+ */
+static struct settings * find_child_settings ( struct settings *parent,
+ const char *name ) {
+ struct settings *settings;
+
+ /* Treat empty name as meaning "this block" */
+ if ( ! *name )
+ return parent;
+
+ /* Look for child with matching name */
+ list_for_each_entry ( settings, &parent->children, siblings ) {
+ if ( strcmp ( settings->name, name ) == 0 )
+ return settings;
+ }
+
+ return NULL;
+}
+
+/**
+ * Find or create child named settings block
+ *
+ * @v parent Parent settings block
+ * @v name Name within this parent
+ * @ret settings Settings block, or NULL
+ */
+static struct settings * autovivify_child_settings ( struct settings *parent,
+ const char *name ) {
+ struct {
+ struct generic_settings generic;
+ char name[ strlen ( name ) + 1 /* NUL */ ];
+ } *new_child;
+ struct settings *settings;
+
+ /* Return existing settings, if existent */
+ if ( ( settings = find_child_settings ( parent, name ) ) != NULL )
+ return settings;
+
+ /* Create new generic settings block */
+ new_child = zalloc ( sizeof ( *new_child ) );
+ if ( ! new_child ) {
+ DBGC ( parent, "Settings %p could not create child %s\n",
+ parent, name );
+ return NULL;
+ }
+ memcpy ( new_child->name, name, sizeof ( new_child->name ) );
+ generic_settings_init ( &new_child->generic, NULL, new_child->name );
+ settings = &new_child->generic.settings;
+ register_settings ( settings, parent );
+ return settings;
+}
+
+/**
+ * Return settings block name (for debug only)
+ *
+ * @v settings Settings block
+ * @ret name Settings block name
+ */
+static const char * settings_name ( struct settings *settings ) {
+ static char buf[64];
+ char tmp[ sizeof ( buf ) ];
+ int count;
+
+ for ( count = 0 ; settings ; settings = settings->parent ) {
+ memcpy ( tmp, buf, sizeof ( tmp ) );
+ snprintf ( buf, sizeof ( buf ), "%s%c%s", settings->name,
+ ( count++ ? '.' : '\0' ), tmp );
+ }
+ return ( buf + 1 );
+}
+
+/**
+ * Parse settings block name
+ *
+ * @v name Name
+ * @v get_child Function to find or create child settings block
+ * @ret settings Settings block, or NULL
+ */
+static struct settings *
+parse_settings_name ( const char *name,
+ struct settings * ( * get_child ) ( struct settings *,
+ const char * ) ) {
+ struct settings *settings = &settings_root;
+ char name_copy[ strlen ( name ) + 1 ];
+ char *subname;
+ char *remainder;
+
+ /* Create modifiable copy of name */
+ memcpy ( name_copy, name, sizeof ( name_copy ) );
+ remainder = name_copy;
+
+ /* Parse each name component in turn */
+ while ( remainder ) {
+ struct net_device *netdev;
+
+ subname = remainder;
+ remainder = strchr ( subname, '.' );
+ if ( remainder )
+ *(remainder++) = '\0';
+
+ /* Special case "netX" root settings block */
+ if ( ( subname == name_copy ) && ! strcmp ( subname, "netX" ) &&
+ ( ( netdev = last_opened_netdev() ) != NULL ) )
+ settings = get_child ( settings, netdev->name );
+ else
+ settings = get_child ( settings, subname );
+
+ if ( ! settings )
+ break;
+ }
+
+ return settings;
+}
+
+/**
+ * Find named settings block
+ *
+ * @v name Name
+ * @ret settings Settings block, or NULL
+ */
+struct settings * find_settings ( const char *name ) {
+
+ return parse_settings_name ( name, find_child_settings );
+}
/**
* Apply all settings
@@ -127,8 +370,7 @@ static int apply_settings ( void ) {
int rc;
/* Call all settings applicators */
- for ( applicator = settings_applicators ;
- applicator < settings_applicators_end ; applicator++ ) {
+ for_each_table_entry ( applicator, SETTINGS_APPLICATORS ) {
if ( ( rc = applicator->apply() ) != 0 ) {
DBG ( "Could not apply settings using applicator "
"%p: %s\n", applicator, strerror ( rc ) );
@@ -199,7 +441,8 @@ int register_settings ( struct settings *settings, struct settings *parent ) {
ref_get ( parent->refcnt );
settings->parent = parent;
list_add_tail ( &settings->siblings, &parent->children );
- DBGC ( settings, "Settings %p registered\n", settings );
+ DBGC ( settings, "Settings %p (\"%s\") registered\n",
+ settings, settings_name ( settings ) );
/* Fix up settings priority */
reprioritise_settings ( settings );
@@ -217,63 +460,19 @@ int register_settings ( struct settings *settings, struct settings *parent ) {
*/
void unregister_settings ( struct settings *settings ) {
+ DBGC ( settings, "Settings %p (\"%s\") unregistered\n",
+ settings, settings_name ( settings ) );
+
/* Remove from list of settings */
ref_put ( settings->refcnt );
ref_put ( settings->parent->refcnt );
settings->parent = NULL;
list_del ( &settings->siblings );
- DBGC ( settings, "Settings %p unregistered\n", settings );
/* Apply potentially-updated settings */
apply_settings();
}
-/**
- * Find child named settings block
- *
- * @v parent Parent settings block
- * @v name Name within this parent
- * @ret settings Settings block, or NULL
- */
-struct settings * find_child_settings ( struct settings *parent,
- const char *name ) {
- struct settings *settings;
- size_t len;
-
- /* NULL parent => add to settings root */
- if ( parent == NULL )
- parent = &settings_root;
-
- /* Look for a child whose name matches the initial component */
- list_for_each_entry ( settings, &parent->children, siblings ) {
- len = strlen ( settings->name );
- if ( strncmp ( name, settings->name, len ) != 0 )
- continue;
- if ( name[len] == 0 )
- return settings;
- if ( name[len] == '.' )
- return find_child_settings ( settings,
- ( name + len + 1 ) );
- }
-
- return NULL;
-}
-
-/**
- * Find named settings block
- *
- * @v name Name
- * @ret settings Settings block, or NULL
- */
-struct settings * find_settings ( const char *name ) {
-
- /* If name is empty, use the root */
- if ( ! *name )
- return &settings_root;
-
- return find_child_settings ( &settings_root, name );
-}
-
/******************************************************************************
*
* Core settings routines
@@ -298,6 +497,10 @@ int store_setting ( struct settings *settings, struct setting *setting,
if ( ! settings )
settings = &settings_root;
+ /* Sanity check */
+ if ( ! settings->op->store )
+ return -ENOTSUP;
+
/* Store setting */
if ( ( rc = settings->op->store ( settings, setting,
data, len ) ) != 0 )
@@ -345,6 +548,10 @@ int fetch_setting ( struct settings *settings, struct setting *setting,
if ( ! settings )
settings = &settings_root;
+ /* Sanity check */
+ if ( ! settings->op->fetch )
+ return -ENOTSUP;
+
/* Try this block first */
if ( ( ret = settings->op->fetch ( settings, setting,
data, len ) ) >= 0 )
@@ -411,7 +618,7 @@ int fetch_string_setting_copy ( struct settings *settings,
struct setting *setting,
char **data ) {
int len;
- int check_len;
+ int check_len = 0;
len = fetch_setting_len ( settings, setting );
if ( len < 0 )
@@ -421,7 +628,8 @@ int fetch_string_setting_copy ( struct settings *settings,
if ( ! *data )
return -ENOMEM;
- fetch_string_setting ( settings, setting, *data, ( len + 1 ) );
+ check_len = fetch_string_setting ( settings, setting, *data,
+ ( len + 1 ) );
assert ( check_len == len );
return len;
}
@@ -504,7 +712,8 @@ int fetch_uint_setting ( struct settings *settings, struct setting *setting,
return len;
/* Mask off sign-extended bits */
- *value = ( svalue & ( -1UL >> ( sizeof ( long ) - len ) ) );
+ assert ( len <= ( int ) sizeof ( long ) );
+ *value = ( svalue & ( -1UL >> ( 8 * ( sizeof ( long ) - len ) ) ) );
return len;
}
@@ -559,6 +768,16 @@ int fetch_uuid_setting ( struct settings *settings, struct setting *setting,
}
/**
+ * Clear settings block
+ *
+ * @v settings Settings block
+ */
+void clear_settings ( struct settings *settings ) {
+ if ( settings->op->clear )
+ settings->op->clear ( settings );
+}
+
+/**
* Compare two settings
*
* @v a Setting to compare
@@ -572,8 +791,12 @@ int setting_cmp ( struct setting *a, struct setting *b ) {
if ( a->tag && ( a->tag == b->tag ) )
return 0;
- /* Otherwise, compare the names */
- return strcmp ( a->name, b->name );
+ /* Otherwise, if the settings have names, compare them */
+ if ( a->name && b->name && a->name[0] )
+ return strcmp ( a->name, b->name );
+
+ /* Otherwise, return a non-match */
+ return ( ! 0 );
}
/******************************************************************************
@@ -614,7 +837,7 @@ int storef_setting ( struct settings *settings, struct setting *setting,
static struct setting * find_setting ( const char *name ) {
struct setting *setting;
- for ( setting = settings ; setting < settings_end ; setting++ ) {
+ for_each_table_entry ( setting, SETTINGS ) {
if ( strcmp ( name, setting->name ) == 0 )
return setting;
}
@@ -622,6 +845,26 @@ static struct setting * find_setting ( const char *name ) {
}
/**
+ * Parse setting name as tag number
+ *
+ * @v name Name
+ * @ret tag Tag number, or 0 if not a valid number
+ */
+static unsigned int parse_setting_tag ( const char *name ) {
+ char *tmp = ( ( char * ) name );
+ unsigned int tag = 0;
+
+ while ( 1 ) {
+ tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) );
+ if ( *tmp == 0 )
+ return tag;
+ if ( *tmp != '.' )
+ return 0;
+ tmp++;
+ }
+}
+
+/**
* Find setting type
*
* @v name Name
@@ -630,7 +873,7 @@ static struct setting * find_setting ( const char *name ) {
static struct setting_type * find_setting_type ( const char *name ) {
struct setting_type *type;
- for ( type = setting_types ; type < setting_types_end ; type++ ) {
+ for_each_table_entry ( type, SETTING_TYPES ) {
if ( strcmp ( name, type->name ) == 0 )
return type;
}
@@ -641,30 +884,38 @@ static struct setting_type * find_setting_type ( const char *name ) {
* Parse setting name
*
* @v name Name of setting
+ * @v get_child Function to find or create child settings block
* @v settings Settings block to fill in
* @v setting Setting to fill in
+ * @v tmp_name Buffer for copy of setting name
* @ret rc Return status code
*
* Interprets a name of the form
* "[settings_name/]tag_name[:type_name]" and fills in the appropriate
* fields.
+ *
+ * The @c tmp_name buffer must be large enough to hold a copy of the
+ * setting name.
*/
-static int parse_setting_name ( const char *name, struct settings **settings,
- struct setting *setting ) {
- char tmp_name[ strlen ( name ) + 1 ];
+static int
+parse_setting_name ( const char *name,
+ struct settings * ( * get_child ) ( struct settings *,
+ const char * ),
+ struct settings **settings, struct setting *setting,
+ char *tmp_name ) {
char *settings_name;
char *setting_name;
char *type_name;
struct setting *named_setting;
- char *tmp;
/* Set defaults */
*settings = &settings_root;
memset ( setting, 0, sizeof ( *setting ) );
- setting->type = &setting_type_hex;
+ setting->name = "";
+ setting->type = &setting_type_string;
/* Split name into "[settings_name/]setting_name[:type_name]" */
- memcpy ( tmp_name, name, sizeof ( tmp_name ) );
+ strcpy ( tmp_name, name );
if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) {
*(setting_name++) = 0;
settings_name = tmp_name;
@@ -677,7 +928,7 @@ static int parse_setting_name ( const char *name, struct settings **settings,
/* Identify settings block, if specified */
if ( settings_name ) {
- *settings = find_settings ( settings_name );
+ *settings = parse_settings_name ( settings_name, get_child );
if ( *settings == NULL ) {
DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n",
settings_name, name );
@@ -685,25 +936,16 @@ static int parse_setting_name ( const char *name, struct settings **settings,
}
}
- /* Identify tag number */
+ /* Identify setting */
if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) {
+ /* Matches a defined named setting; use that setting */
memcpy ( setting, named_setting, sizeof ( *setting ) );
- } else {
- /* Unrecognised name: try to interpret as a tag number */
- tmp = setting_name;
- while ( 1 ) {
- setting->tag = ( ( setting->tag << 8 ) |
- strtoul ( tmp, &tmp, 0 ) );
- if ( *tmp == 0 )
- break;
- if ( *tmp != '.' ) {
- DBG ( "Invalid setting \"%s\" in \"%s\"\n",
- setting_name, name );
- return -ENOENT;
- }
- tmp++;
- }
+ } else if ( ( setting->tag = parse_setting_tag ( setting_name ) ) !=0){
+ /* Is a valid numeric tag; use the tag */
setting->tag |= (*settings)->tag_magic;
+ } else {
+ /* Use the arbitrary name */
+ setting->name = setting_name;
}
/* Identify setting type, if specified */
@@ -729,9 +971,11 @@ static int parse_setting_name ( const char *name, struct settings **settings,
int storef_named_setting ( const char *name, const char *value ) {
struct settings *settings;
struct setting setting;
+ char tmp_name[ strlen ( name ) + 1 ];
int rc;
- if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+ if ( ( rc = parse_setting_name ( name, autovivify_child_settings,
+ &settings, &setting, tmp_name )) != 0)
return rc;
return storef_setting ( settings, &setting, value );
}
@@ -747,9 +991,11 @@ int storef_named_setting ( const char *name, const char *value ) {
int fetchf_named_setting ( const char *name, char *buf, size_t len ) {
struct settings *settings;
struct setting setting;
+ char tmp_name[ strlen ( name ) + 1 ];
int rc;
- if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+ if ( ( rc = parse_setting_name ( name, find_child_settings,
+ &settings, &setting, tmp_name )) != 0)
return rc;
return fetchf_setting ( settings, &setting, buf, len );
}
@@ -839,7 +1085,7 @@ static int fetchf_uristring ( struct settings *settings,
fetch_string_setting ( settings, setting, raw_buf,
sizeof ( raw_buf ) );
- return uri_encode ( raw_buf, buf, len );
+ return uri_encode ( raw_buf, buf, len, URI_FRAGMENT );
}
}
@@ -1180,7 +1426,7 @@ struct setting filename_setting __setting = {
/** Root path setting */
struct setting root_path_setting __setting = {
.name = "root-path",
- .description = "NFS/iSCSI root path",
+ .description = "iSCSI root path",
.tag = DHCP_ROOT_PATH,
.type = &setting_type_string,
};