diff options
author | Michael Brown | 2009-05-26 12:05:58 +0200 |
---|---|---|
committer | Michael Brown | 2009-05-26 12:05:58 +0200 |
commit | 3c06277bbb6ea135e6a1daf22463a347fc7898c7 (patch) | |
tree | 48469dfe123ab00d0e417535ac7ae7879e84ff24 /src/core | |
parent | [multiboot] Include argv[0] as part of "command line" (diff) | |
download | ipxe-3c06277bbb6ea135e6a1daf22463a347fc7898c7.tar.gz ipxe-3c06277bbb6ea135e6a1daf22463a347fc7898c7.tar.xz ipxe-3c06277bbb6ea135e6a1daf22463a347fc7898c7.zip |
[settings] Allow for arbitrarily-named settings
This provides a mechanism for using arbitrarily-named variables within
gPXE, using the existing syntax for settings.
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/settings.c | 296 |
1 files changed, 241 insertions, 55 deletions
diff --git a/src/core/settings.c b/src/core/settings.c index 43b463a5..7a02985b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -41,65 +41,194 @@ FILE_LICENCE ( GPL2_OR_LATER ); /****************************************************************************** * - * Registered settings blocks + * Generic settings blocks * ****************************************************************************** */ /** - * Store value of simple setting + * 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; +}; + +/** + * 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 + * + * @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 options DHCP option block + * @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 generic setting + * + * @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 @@ -135,7 +264,7 @@ static struct settings * find_child_settings ( struct settings *parent, static struct settings * autovivify_child_settings ( struct settings *parent, const char *name ) { struct { - struct simple_settings simple; + struct generic_settings generic; char name[ strlen ( name ) + 1 /* NUL */ ]; } *new_child; struct settings *settings; @@ -144,7 +273,7 @@ static struct settings * autovivify_child_settings ( struct settings *parent, if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) return settings; - /* Create new simple settings block */ + /* Create new generic settings block */ new_child = zalloc ( sizeof ( *new_child ) ); if ( ! new_child ) { DBGC ( parent, "Settings %p could not create child %s\n", @@ -152,13 +281,32 @@ static struct settings * autovivify_child_settings ( struct settings *parent, return NULL; } memcpy ( new_child->name, name, sizeof ( new_child->name ) ); - simple_settings_init ( &new_child->simple, NULL, new_child->name ); - settings = &new_child->simple.settings; + 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 @@ -284,7 +432,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 ); @@ -302,12 +451,14 @@ 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(); @@ -337,6 +488,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 ) @@ -384,6 +539,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 ) @@ -600,6 +759,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 @@ -663,6 +832,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 @@ -685,31 +874,35 @@ static struct setting_type * find_setting_type ( const char *name ) { * @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 * ( * get_child ) ( struct settings *, const char * ), - struct settings **settings, struct setting *setting ) { - char tmp_name[ strlen ( name ) + 1 ]; + 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; @@ -730,25 +923,16 @@ parse_setting_name ( const char *name, } } - /* 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 */ @@ -774,10 +958,11 @@ parse_setting_name ( const char *name, 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, autovivify_child_settings, - &settings, &setting ) ) != 0 ) + &settings, &setting, tmp_name )) != 0) return rc; return storef_setting ( settings, &setting, value ); } @@ -793,10 +978,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, find_child_settings, - &settings, &setting ) ) != 0 ) + &settings, &setting, tmp_name )) != 0) return rc; return fetchf_setting ( settings, &setting, buf, len ); } |