diff options
Diffstat (limited to 'debian/grub-extras/disabled/gpxe/src/core/settings.c')
-rw-r--r-- | debian/grub-extras/disabled/gpxe/src/core/settings.c | 1447 |
1 files changed, 1447 insertions, 0 deletions
diff --git a/debian/grub-extras/disabled/gpxe/src/core/settings.c b/debian/grub-extras/disabled/gpxe/src/core/settings.c new file mode 100644 index 0000000..87d84a0 --- /dev/null +++ b/debian/grub-extras/disabled/gpxe/src/core/settings.c @@ -0,0 +1,1447 @@ +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <byteswap.h> +#include <errno.h> +#include <assert.h> +#include <gpxe/in.h> +#include <gpxe/vsprintf.h> +#include <gpxe/dhcp.h> +#include <gpxe/uuid.h> +#include <gpxe/uri.h> +#include <gpxe/settings.h> + +/** @file + * + * Configuration settings + * + */ + +/****************************************************************************** + * + * Generic settings blocks + * + ****************************************************************************** + */ + +/** + * 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 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 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 generic setting + * + * @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 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 ) ); +} + +/** Generic settings operations */ +struct settings_operations generic_settings_operations = { + .store = generic_settings_store, + .fetch = generic_settings_fetch, + .clear = generic_settings_clear, +}; + +/****************************************************************************** + * + * Registered settings blocks + * + ****************************************************************************** + */ + +/** Root generic settings block */ +struct generic_settings generic_settings_root = { + .settings = { + .refcnt = NULL, + .name = "", + .siblings = + LIST_HEAD_INIT ( generic_settings_root.settings.siblings ), + .children = + 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 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 ) { + subname = remainder; + remainder = strchr ( subname, '.' ); + if ( remainder ) + *(remainder++) = '\0'; + 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 + * + * @ret rc Return status code + */ +static int apply_settings ( void ) { + struct settings_applicator *applicator; + int rc; + + /* Call all settings applicators */ + 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 ) ); + return rc; + } + } + + return 0; +} + +/** + * Reprioritise settings + * + * @v settings Settings block + * + * Reorders the settings block amongst its siblings according to its + * priority. + */ +static void reprioritise_settings ( struct settings *settings ) { + struct settings *parent = settings->parent; + long priority; + struct settings *tmp; + long tmp_priority; + + /* Stop when we reach the top of the tree */ + if ( ! parent ) + return; + + /* Read priority, if present */ + priority = fetch_intz_setting ( settings, &priority_setting ); + + /* Remove from siblings list */ + list_del ( &settings->siblings ); + + /* Reinsert after any existing blocks which have a higher priority */ + list_for_each_entry ( tmp, &parent->children, siblings ) { + tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); + if ( priority > tmp_priority ) + break; + } + list_add_tail ( &settings->siblings, &tmp->siblings ); + + /* Recurse up the tree */ + reprioritise_settings ( parent ); +} + +/** + * Register settings block + * + * @v settings Settings block + * @v parent Parent settings block, or NULL + * @ret rc Return status code + */ +int register_settings ( struct settings *settings, struct settings *parent ) { + struct settings *old_settings; + + /* NULL parent => add to settings root */ + assert ( settings != NULL ); + if ( parent == NULL ) + parent = &settings_root; + + /* Remove any existing settings with the same name */ + if ( ( old_settings = find_child_settings ( parent, settings->name ) )) + unregister_settings ( old_settings ); + + /* Add to list of settings */ + ref_get ( settings->refcnt ); + ref_get ( parent->refcnt ); + settings->parent = parent; + list_add_tail ( &settings->siblings, &parent->children ); + DBGC ( settings, "Settings %p (\"%s\") registered\n", + settings, settings_name ( settings ) ); + + /* Fix up settings priority */ + reprioritise_settings ( settings ); + + /* Apply potentially-updated settings */ + apply_settings(); + + return 0; +} + +/** + * Unregister settings block + * + * @v settings Settings block + */ +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 ); + + /* Apply potentially-updated settings */ + apply_settings(); +} + +/****************************************************************************** + * + * Core settings routines + * + ****************************************************************************** + */ + +/** + * Store value of setting + * + * @v settings Settings block, or NULL + * @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 store_setting ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + int rc; + + /* NULL settings implies storing into the global settings root */ + 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 ) + return rc; + + /* Reprioritise settings if necessary */ + if ( setting_cmp ( setting, &priority_setting ) == 0 ) + reprioritise_settings ( settings ); + + /* If these settings are registered, apply potentially-updated + * settings + */ + for ( ; settings ; settings = settings->parent ) { + if ( settings == &settings_root ) { + if ( ( rc = apply_settings() ) != 0 ) + return rc; + break; + } + } + + return 0; +} + +/** + * Fetch value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @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 + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ +int fetch_setting ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct settings *child; + int ret; + + /* Avoid returning uninitialised data on error */ + memset ( data, 0, len ); + + /* NULL settings implies starting at the global settings root */ + 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 ) + return ret; + + /* Recurse into each child block in turn */ + list_for_each_entry ( child, &settings->children, siblings ) { + if ( ( ret = fetch_setting ( child, setting, + data, len ) ) >= 0 ) + return ret; + } + + return -ENOENT; +} + +/** + * Fetch length of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret len Length of setting data, or negative error + * + * This function can also be used as an existence check for the + * setting. + */ +int fetch_setting_len ( struct settings *settings, struct setting *setting ) { + return fetch_setting ( settings, setting, NULL, 0 ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting string data + * @v len Length of buffer + * @ret len Length of string setting, or negative error + * + * The resulting string is guaranteed to be correctly NUL-terminated. + * The returned length will be the length of the underlying setting + * data. + */ +int fetch_string_setting ( struct settings *settings, struct setting *setting, + char *data, size_t len ) { + memset ( data, 0, len ); + return fetch_setting ( settings, setting, data, + ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to allocate and fill with setting string data + * @ret len Length of string setting, or negative error + * + * The resulting string is guaranteed to be correctly NUL-terminated. + * The returned length will be the length of the underlying setting + * data. The caller is responsible for eventually freeing the + * allocated buffer. + */ +int fetch_string_setting_copy ( struct settings *settings, + struct setting *setting, + char **data ) { + int len; + int check_len = 0; + + len = fetch_setting_len ( settings, setting ); + if ( len < 0 ) + return len; + + *data = malloc ( len + 1 ); + if ( ! *data ) + return -ENOMEM; + + check_len = fetch_string_setting ( settings, setting, *data, + ( len + 1 ) ); + assert ( check_len == len ); + return len; +} + +/** + * Fetch value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv4 address to fill in + * @ret len Length of setting, or negative error + */ +int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, + struct in_addr *inp ) { + int len; + + len = fetch_setting ( settings, setting, inp, sizeof ( *inp ) ); + if ( len < 0 ) + return len; + if ( len < ( int ) sizeof ( *inp ) ) + return -ERANGE; + return len; +} + +/** + * Fetch value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_int_setting ( struct settings *settings, struct setting *setting, + long *value ) { + union { + uint8_t u8[ sizeof ( long ) ]; + int8_t s8[ sizeof ( long ) ]; + } buf; + int len; + int i; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch raw (network-ordered, variable-length) setting */ + len = fetch_setting ( settings, setting, &buf, sizeof ( buf ) ); + if ( len < 0 ) + return len; + if ( len > ( int ) sizeof ( buf ) ) + return -ERANGE; + + /* Convert to host-ordered signed long */ + *value = ( ( buf.s8[0] >= 0 ) ? 0 : -1L ); + for ( i = 0 ; i < len ; i++ ) { + *value = ( ( *value << 8 ) | buf.u8[i] ); + } + + return len; +} + +/** + * Fetch value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uint_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ) { + long svalue; + int len; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch as a signed long */ + len = fetch_int_setting ( settings, setting, &svalue ); + if ( len < 0 ) + return len; + + /* Mask off sign-extended bits */ + assert ( len <= ( int ) sizeof ( long ) ); + *value = ( svalue & ( -1UL >> ( 8 * ( sizeof ( long ) - len ) ) ) ); + + return len; +} + +/** + * Fetch value of signed integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ + long value; + + fetch_int_setting ( settings, setting, &value ); + return value; +} + +/** + * Fetch value of unsigned integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +unsigned long fetch_uintz_setting ( struct settings *settings, + struct setting *setting ) { + unsigned long value; + + fetch_uint_setting ( settings, setting, &value ); + return value; +} + +/** + * Fetch value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v uuid UUID to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uuid_setting ( struct settings *settings, struct setting *setting, + union uuid *uuid ) { + int len; + + len = fetch_setting ( settings, setting, uuid, sizeof ( *uuid ) ); + if ( len < 0 ) + return len; + if ( len != sizeof ( *uuid ) ) + return -ERANGE; + return len; +} + +/** + * 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 + * @v b Setting to compare + * @ret 0 Settings are the same + * @ret non-zero Settings are not the same + */ +int setting_cmp ( struct setting *a, struct setting *b ) { + + /* If the settings have tags, compare them */ + if ( a->tag && ( a->tag == b->tag ) ) + return 0; + + /* 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 ); +} + +/****************************************************************************** + * + * Formatted setting routines + * + ****************************************************************************** + */ + +/** + * Store value of typed setting + * + * @v settings Settings block + * @v setting Setting to store + * @v type Settings type + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_setting ( struct settings *settings, struct setting *setting, + const char *value ) { + + /* NULL value implies deletion. Avoid imposing the burden of + * checking for NULL values on each typed setting's storef() + * method. + */ + if ( ! value ) + return delete_setting ( settings, setting ); + + return setting->type->storef ( settings, setting, value ); +} + +/** + * Find named setting + * + * @v name Name + * @ret setting Named setting, or NULL + */ +static struct setting * find_setting ( const char *name ) { + struct setting *setting; + + for_each_table_entry ( setting, SETTINGS ) { + if ( strcmp ( name, setting->name ) == 0 ) + return setting; + } + return NULL; +} + +/** + * 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 + * @ret type Setting type, or NULL + */ +static struct setting_type * find_setting_type ( const char *name ) { + struct setting_type *type; + + for_each_table_entry ( type, SETTING_TYPES ) { + if ( strcmp ( name, type->name ) == 0 ) + return type; + } + return NULL; +} + +/** + * 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 * ( * 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; + + /* Set defaults */ + *settings = &settings_root; + memset ( setting, 0, sizeof ( *setting ) ); + setting->name = ""; + setting->type = &setting_type_string; + + /* Split name into "[settings_name/]setting_name[:type_name]" */ + strcpy ( tmp_name, name ); + if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { + *(setting_name++) = 0; + settings_name = tmp_name; + } else { + setting_name = tmp_name; + settings_name = NULL; + } + if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) + *(type_name++) = 0; + + /* Identify settings block, if specified */ + if ( settings_name ) { + *settings = parse_settings_name ( settings_name, get_child ); + if ( *settings == NULL ) { + DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", + settings_name, name ); + return -ENODEV; + } + } + + /* 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 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 */ + if ( type_name ) { + setting->type = find_setting_type ( type_name ); + if ( setting->type == NULL ) { + DBG ( "Invalid setting type \"%s\" in \"%s\"\n", + type_name, name ); + return -ENOTSUP; + } + } + + return 0; +} + +/** + * Parse and store value of named setting + * + * @v name Name of setting + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +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, tmp_name )) != 0) + return rc; + return storef_setting ( settings, &setting, value ); +} + +/** + * Fetch and format value of named setting + * + * @v name Name of setting + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +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, tmp_name )) != 0) + return rc; + return fetchf_setting ( settings, &setting, buf, len ); +} + +/****************************************************************************** + * + * Setting types + * + ****************************************************************************** + */ + +/** + * Parse and store value of string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_string ( struct settings *settings, struct setting *setting, + const char *value ) { + return store_setting ( settings, setting, value, strlen ( value ) ); +} + +/** + * Fetch and format value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_string ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + return fetch_string_setting ( settings, setting, buf, len ); +} + +/** A string setting type */ +struct setting_type setting_type_string __setting_type = { + .name = "string", + .storef = storef_string, + .fetchf = fetchf_string, +}; + +/** + * Parse and store value of URI-encoded string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_uristring ( struct settings *settings, + struct setting *setting, + const char *value ) { + char buf[ strlen ( value ) + 1 ]; /* Decoding never expands string */ + size_t len; + + len = uri_decode ( value, buf, sizeof ( buf ) ); + return store_setting ( settings, setting, buf, len ); +} + +/** + * Fetch and format value of URI-encoded string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uristring ( struct settings *settings, + struct setting *setting, + char *buf, size_t len ) { + ssize_t raw_len; + + /* We need to always retrieve the full raw string to know the + * length of the encoded string. + */ + raw_len = fetch_setting ( settings, setting, NULL, 0 ); + if ( raw_len < 0 ) + return raw_len; + + { + char raw_buf[ raw_len + 1 ]; + + fetch_string_setting ( settings, setting, raw_buf, + sizeof ( raw_buf ) ); + return uri_encode ( raw_buf, buf, len ); + } +} + +/** A URI-encoded string setting type */ +struct setting_type setting_type_uristring __setting_type = { + .name = "uristring", + .storef = storef_uristring, + .fetchf = fetchf_uristring, +}; + +/** + * Parse and store value of IPv4 address setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_ipv4 ( struct settings *settings, struct setting *setting, + const char *value ) { + struct in_addr ipv4; + + if ( inet_aton ( value, &ipv4 ) == 0 ) + return -EINVAL; + return store_setting ( settings, setting, &ipv4, sizeof ( ipv4 ) ); +} + +/** + * Fetch and format value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_ipv4 ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + struct in_addr ipv4; + int raw_len; + + if ( ( raw_len = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", inet_ntoa ( ipv4 ) ); +} + +/** An IPv4 address setting type */ +struct setting_type setting_type_ipv4 __setting_type = { + .name = "ipv4", + .storef = storef_ipv4, + .fetchf = fetchf_ipv4, +}; + +/** + * Parse and store value of integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int ( struct settings *settings, struct setting *setting, + const char *value, unsigned int size ) { + union { + uint32_t num; + uint8_t bytes[4]; + } u; + char *endp; + + u.num = htonl ( strtoul ( value, &endp, 0 ) ); + if ( *endp ) + return -EINVAL; + return store_setting ( settings, setting, + &u.bytes[ sizeof ( u ) - size ], size ); +} + +/** + * Parse and store value of 8-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int8 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 1 ); +} + +/** + * Parse and store value of 16-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int16 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 2 ); +} + +/** + * Parse and store value of 32-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int32 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 4 ); +} + +/** + * Fetch and format value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_int ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + long value; + int rc; + + if ( ( rc = fetch_int_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%ld", value ); +} + +/** + * Fetch and format value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uint ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + unsigned long value; + int rc; + + if ( ( rc = fetch_uint_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%#lx", value ); +} + +/** A signed 8-bit integer setting type */ +struct setting_type setting_type_int8 __setting_type = { + .name = "int8", + .storef = storef_int8, + .fetchf = fetchf_int, +}; + +/** A signed 16-bit integer setting type */ +struct setting_type setting_type_int16 __setting_type = { + .name = "int16", + .storef = storef_int16, + .fetchf = fetchf_int, +}; + +/** A signed 32-bit integer setting type */ +struct setting_type setting_type_int32 __setting_type = { + .name = "int32", + .storef = storef_int32, + .fetchf = fetchf_int, +}; + +/** An unsigned 8-bit integer setting type */ +struct setting_type setting_type_uint8 __setting_type = { + .name = "uint8", + .storef = storef_int8, + .fetchf = fetchf_uint, +}; + +/** An unsigned 16-bit integer setting type */ +struct setting_type setting_type_uint16 __setting_type = { + .name = "uint16", + .storef = storef_int16, + .fetchf = fetchf_uint, +}; + +/** An unsigned 32-bit integer setting type */ +struct setting_type setting_type_uint32 __setting_type = { + .name = "uint32", + .storef = storef_int32, + .fetchf = fetchf_uint, +}; + +/** + * Parse and store value of hex string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_hex ( struct settings *settings, struct setting *setting, + const char *value ) { + char *ptr = ( char * ) value; + uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */ + unsigned int len = 0; + + while ( 1 ) { + bytes[len++] = strtoul ( ptr, &ptr, 16 ); + switch ( *ptr ) { + case '\0' : + return store_setting ( settings, setting, bytes, len ); + case ':' : + ptr++; + break; + default : + return -EINVAL; + } + } +} + +/** + * Fetch and format value of hex string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_hex ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + int raw_len; + int check_len; + int used = 0; + int i; + + raw_len = fetch_setting_len ( settings, setting ); + if ( raw_len < 0 ) + return raw_len; + + { + uint8_t raw[raw_len]; + + check_len = fetch_setting ( settings, setting, raw, + sizeof ( raw ) ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == raw_len ); + + if ( len ) + buf[0] = 0; /* Ensure that a terminating NUL exists */ + for ( i = 0 ; i < raw_len ; i++ ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s%02x", ( used ? ":" : "" ), + raw[i] ); + } + return used; + } +} + +/** A hex-string setting */ +struct setting_type setting_type_hex __setting_type = { + .name = "hex", + .storef = storef_hex, + .fetchf = fetchf_hex, +}; + +/** + * Parse and store value of UUID setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_uuid ( struct settings *settings __unused, + struct setting *setting __unused, + const char *value __unused ) { + return -ENOTSUP; +} + +/** + * Fetch and format value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uuid ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + union uuid uuid; + int raw_len; + + if ( ( raw_len = fetch_uuid_setting ( settings, setting, &uuid ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); +} + +/** UUID setting type */ +struct setting_type setting_type_uuid __setting_type = { + .name = "uuid", + .storef = storef_uuid, + .fetchf = fetchf_uuid, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** Hostname setting */ +struct setting hostname_setting __setting = { + .name = "hostname", + .description = "Host name", + .tag = DHCP_HOST_NAME, + .type = &setting_type_string, +}; + +/** Filename setting */ +struct setting filename_setting __setting = { + .name = "filename", + .description = "Boot filename", + .tag = DHCP_BOOTFILE_NAME, + .type = &setting_type_string, +}; + +/** Root path setting */ +struct setting root_path_setting __setting = { + .name = "root-path", + .description = "NFS/iSCSI root path", + .tag = DHCP_ROOT_PATH, + .type = &setting_type_string, +}; + +/** Username setting */ +struct setting username_setting __setting = { + .name = "username", + .description = "User name", + .tag = DHCP_EB_USERNAME, + .type = &setting_type_string, +}; + +/** Password setting */ +struct setting password_setting __setting = { + .name = "password", + .description = "Password", + .tag = DHCP_EB_PASSWORD, + .type = &setting_type_string, +}; + +/** Priority setting */ +struct setting priority_setting __setting = { + .name = "priority", + .description = "Priority of these settings", + .tag = DHCP_EB_PRIORITY, + .type = &setting_type_int8, +}; |