| /* |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, 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 <ipxe/in.h> |
| #include <ipxe/vsprintf.h> |
| #include <ipxe/dhcp.h> |
| #include <ipxe/uuid.h> |
| #include <ipxe/uri.h> |
| #include <ipxe/base16.h> |
| #include <ipxe/pci.h> |
| #include <ipxe/init.h> |
| #include <ipxe/version.h> |
| #include <ipxe/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, |
| const 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, |
| const 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 ); |
| |
| /* Set setting type, if not yet specified */ |
| if ( ! setting->type ) |
| setting->type = generic->setting.type; |
| |
| 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 |
| |
| /** Autovivified settings block */ |
| struct autovivified_settings { |
| /** Reference count */ |
| struct refcnt refcnt; |
| /** Generic settings block */ |
| struct generic_settings generic; |
| }; |
| |
| /** |
| * Free autovivified settings block |
| * |
| * @v refcnt Reference count |
| */ |
| static void autovivified_settings_free ( struct refcnt *refcnt ) { |
| struct autovivified_settings *autovivified = |
| container_of ( refcnt, struct autovivified_settings, refcnt ); |
| |
| generic_settings_clear ( &autovivified->generic.settings ); |
| free ( autovivified ); |
| } |
| |
| /** |
| * Find child 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; |
| |
| /* Find target parent settings block */ |
| parent = settings_target ( parent ); |
| |
| /* 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_target ( settings ); |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * Find or create child settings block |
| * |
| * @v parent Parent settings block |
| * @v name Name within this parent |
| * @ret settings Settings block, or NULL |
| */ |
| struct settings * autovivify_child_settings ( struct settings *parent, |
| const char *name ) { |
| struct { |
| struct autovivified_settings autovivified; |
| char name[ strlen ( name ) + 1 /* NUL */ ]; |
| } *new_child; |
| struct settings *settings; |
| |
| /* Find target parent settings block */ |
| parent = settings_target ( parent ); |
| |
| /* 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 ) ); |
| ref_init ( &new_child->autovivified.refcnt, |
| autovivified_settings_free ); |
| generic_settings_init ( &new_child->autovivified.generic, |
| &new_child->autovivified.refcnt ); |
| settings = &new_child->autovivified.generic.settings; |
| register_settings ( settings, parent, new_child->name ); |
| return settings; |
| } |
| |
| /** |
| * Return settings block name |
| * |
| * @v settings Settings block |
| * @ret name Settings block name |
| */ |
| const char * settings_name ( struct settings *settings ) { |
| static char buf[16]; |
| char tmp[ sizeof ( buf ) ]; |
| |
| /* Find target settings block */ |
| settings = settings_target ( settings ); |
| |
| /* Construct name */ |
| for ( buf[2] = buf[0] = 0 ; settings ; settings = settings->parent ) { |
| memcpy ( tmp, buf, sizeof ( tmp ) ); |
| snprintf ( buf, sizeof ( buf ), ".%s%s", settings->name, tmp ); |
| } |
| return ( buf + 2 ); |
| } |
| |
| /** |
| * 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, get_child_settings_t get_child ) { |
| 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 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 |
| * @v name Settings block name |
| * @ret rc Return status code |
| */ |
| int register_settings ( struct settings *settings, struct settings *parent, |
| const char *name ) { |
| struct settings *old_settings; |
| |
| /* Sanity check */ |
| assert ( settings != NULL ); |
| |
| /* Find target parent settings block */ |
| parent = settings_target ( parent ); |
| |
| /* Apply settings block name */ |
| settings->name = name; |
| |
| /* 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 ) { |
| struct settings *child; |
| struct settings *tmp; |
| |
| /* Unregister child settings */ |
| list_for_each_entry_safe ( child, tmp, &settings->children, siblings ) { |
| unregister_settings ( child ); |
| } |
| |
| DBGC ( settings, "Settings %p (\"%s\") unregistered\n", |
| settings, settings_name ( settings ) ); |
| |
| /* Remove from list of settings */ |
| ref_put ( settings->parent->refcnt ); |
| settings->parent = NULL; |
| list_del ( &settings->siblings ); |
| ref_put ( settings->refcnt ); |
| |
| /* Apply potentially-updated settings */ |
| apply_settings(); |
| } |
| |
| /****************************************************************************** |
| * |
| * Core settings routines |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Redirect to target settings block |
| * |
| * @v settings Settings block, or NULL |
| * @ret settings Underlying settings block |
| */ |
| struct settings * settings_target ( struct settings *settings ) { |
| |
| /* NULL settings implies the global settings root */ |
| if ( ! settings ) |
| settings = &settings_root; |
| |
| /* Redirect to underlying settings block, if applicable */ |
| if ( settings->op->redirect ) |
| return settings->op->redirect ( settings ); |
| |
| /* Otherwise, return this settings block */ |
| return settings; |
| } |
| |
| /** |
| * Check applicability of setting |
| * |
| * @v settings Settings block |
| * @v setting Setting |
| * @ret applies Setting applies within this settings block |
| */ |
| int setting_applies ( struct settings *settings, |
| const struct setting *setting ) { |
| |
| /* Find target settings block */ |
| settings = settings_target ( settings ); |
| |
| /* Check applicability of setting */ |
| return ( settings->op->applies ? |
| settings->op->applies ( settings, setting ) : 1 ); |
| } |
| |
| /** |
| * 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, const struct setting *setting, |
| const void *data, size_t len ) { |
| int rc; |
| |
| /* Find target settings block */ |
| settings = settings_target ( settings ); |
| |
| /* Fail if tag does not apply to this settings block */ |
| if ( ! setting_applies ( settings, setting ) ) |
| return -ENOTTY; |
| |
| /* 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 setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @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, const struct setting *setting, |
| struct settings **origin, struct setting *fetched, |
| void *data, size_t len ) { |
| struct settings *child; |
| struct setting tmp; |
| int ret; |
| |
| /* Avoid returning uninitialised data on error */ |
| memset ( data, 0, len ); |
| if ( origin ) |
| *origin = NULL; |
| if ( fetched ) |
| memcpy ( fetched, setting, sizeof ( *fetched ) ); |
| |
| /* Find target settings block */ |
| settings = settings_target ( settings ); |
| |
| /* Sanity check */ |
| if ( ! settings->op->fetch ) |
| return -ENOTSUP; |
| |
| /* Try this block first, if applicable */ |
| if ( setting_applies ( settings, setting ) ) { |
| |
| /* Create modifiable copy of setting */ |
| memcpy ( &tmp, setting, sizeof ( tmp ) ); |
| if ( ( ret = settings->op->fetch ( settings, &tmp, |
| data, len ) ) >= 0 ) { |
| |
| /* Default to string type, if not yet specified */ |
| if ( ! tmp.type ) |
| tmp.type = &setting_type_string; |
| |
| /* Record origin, if applicable */ |
| if ( origin ) |
| *origin = settings; |
| |
| /* Record fetched setting, if applicable */ |
| if ( fetched ) |
| memcpy ( fetched, &tmp, sizeof ( *fetched ) ); |
| |
| return ret; |
| } |
| } |
| |
| /* Recurse into each child block in turn */ |
| list_for_each_entry ( child, &settings->children, siblings ) { |
| if ( ( ret = fetch_setting ( child, setting, origin, fetched, |
| data, len ) ) >= 0 ) |
| return ret; |
| } |
| |
| return -ENOENT; |
| } |
| |
| /** |
| * Fetch allocated copy of setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @v data Buffer to allocate and fill with setting data |
| * @v alloc Allocation function |
| * @ret len Length of setting, or negative error |
| * |
| * The caller is responsible for eventually freeing the allocated |
| * buffer. |
| */ |
| static int fetch_setting_alloc ( struct settings *settings, |
| const struct setting *setting, |
| struct settings **origin, |
| struct setting *fetched, |
| void **data, |
| void * ( * alloc ) ( size_t len ) ) { |
| struct settings *tmp_origin; |
| struct setting tmp_fetched; |
| int len; |
| int check_len; |
| |
| /* Use local buffers if necessary */ |
| if ( ! origin ) |
| origin = &tmp_origin; |
| if ( ! fetched ) |
| fetched = &tmp_fetched; |
| |
| /* Avoid returning uninitialised data on error */ |
| *data = NULL; |
| |
| /* Check existence, and fetch setting length */ |
| len = fetch_setting ( settings, setting, origin, fetched, NULL, 0 ); |
| if ( len < 0 ) |
| return len; |
| |
| /* Allocate buffer */ |
| *data = alloc ( len ); |
| if ( ! *data ) |
| return -ENOMEM; |
| |
| /* Fetch setting value */ |
| check_len = fetch_setting ( *origin, fetched, NULL, NULL, *data, len ); |
| assert ( check_len == len ); |
| return len; |
| } |
| |
| /** |
| * Fetch copy of setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @v data Buffer to allocate and fill with setting data |
| * @ret len Length of setting, or negative error |
| * |
| * The caller is responsible for eventually freeing the allocated |
| * buffer. |
| */ |
| int fetch_setting_copy ( struct settings *settings, |
| const struct setting *setting, |
| struct settings **origin, struct setting *fetched, |
| void **data ) { |
| |
| return fetch_setting_alloc ( settings, setting, origin, fetched, |
| data, malloc ); |
| } |
| |
| /** |
| * 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 string data |
| * @v len Length of buffer |
| * @ret len Length of setting, or negative error |
| */ |
| int fetch_raw_setting ( struct settings *settings, |
| const struct setting *setting, |
| void *data, size_t len ) { |
| |
| return fetch_setting ( settings, setting, NULL, NULL, data, len ); |
| } |
| |
| /** |
| * Fetch value of 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 data |
| * @ret len Length of setting, or negative error |
| * |
| * The caller is responsible for eventually freeing the allocated |
| * buffer. |
| */ |
| int fetch_raw_setting_copy ( struct settings *settings, |
| const struct setting *setting, |
| void **data ) { |
| |
| return fetch_setting_copy ( settings, setting, NULL, NULL, data ); |
| } |
| |
| /** |
| * 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, |
| const struct setting *setting, |
| char *data, size_t len ) { |
| |
| memset ( data, 0, len ); |
| return fetch_raw_setting ( settings, setting, data, |
| ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); |
| } |
| |
| /** |
| * Allocate memory for copy of string setting |
| * |
| * @v len Length of setting |
| * @ret ptr Allocated memory |
| */ |
| static void * fetch_string_setting_copy_alloc ( size_t len ) { |
| return zalloc ( len + 1 /* NUL */ ); |
| } |
| |
| /** |
| * 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, |
| const struct setting *setting, char **data ) { |
| |
| return fetch_setting_alloc ( settings, setting, NULL, NULL, |
| ( ( void ** ) data ), |
| fetch_string_setting_copy_alloc ); |
| } |
| |
| /** |
| * Fetch value of IPv4 address setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v inp IPv4 addresses to fill in |
| * @v count Maximum number of IPv4 addresses |
| * @ret len Length of setting, or negative error |
| */ |
| int fetch_ipv4_array_setting ( struct settings *settings, |
| const struct setting *setting, |
| struct in_addr *inp, unsigned int count ) { |
| int len; |
| |
| len = fetch_raw_setting ( settings, setting, inp, |
| ( sizeof ( *inp ) * count ) ); |
| if ( len < 0 ) |
| return len; |
| if ( ( len % sizeof ( *inp ) ) != 0 ) |
| return -ERANGE; |
| 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, |
| const struct setting *setting, |
| struct in_addr *inp ) { |
| |
| return fetch_ipv4_array_setting ( settings, setting, inp, 1 ); |
| } |
| |
| /** |
| * Extract numeric value of setting |
| * |
| * @v is_signed Treat value as a signed integer |
| * @v raw Raw setting data |
| * @v len Length of raw setting data |
| * @ret value Numeric value |
| * @ret len Length of setting, or negative error |
| */ |
| static int numeric_setting_value ( int is_signed, const void *raw, size_t len, |
| unsigned long *value ) { |
| const uint8_t *unsigned_bytes = raw; |
| const int8_t *signed_bytes = raw; |
| int is_negative; |
| unsigned int i; |
| uint8_t pad; |
| uint8_t byte; |
| |
| /* Convert to host-ordered longs */ |
| is_negative = ( len && ( signed_bytes[0] < 0 ) ); |
| *value = ( ( is_signed && is_negative ) ? -1L : 0 ); |
| pad = *value; |
| for ( i = 0 ; i < len ; i++ ) { |
| byte = unsigned_bytes[i]; |
| *value = ( ( *value << 8 ) | byte ); |
| if ( ( ( i + sizeof ( *value ) ) < len ) && ( byte != pad ) ) |
| return -ERANGE; |
| } |
| |
| return len; |
| } |
| |
| /** |
| * Fetch value of numeric 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_numeric_setting ( struct settings *settings, |
| const struct setting *setting, |
| unsigned long *value, int is_signed ) { |
| unsigned long tmp; |
| int len; |
| |
| /* Avoid returning uninitialised data on error */ |
| *value = 0; |
| |
| /* Fetch raw (network-ordered, variable-length) setting */ |
| len = fetch_raw_setting ( settings, setting, &tmp, sizeof ( tmp ) ); |
| if ( len < 0 ) |
| return len; |
| |
| /* Extract numeric value */ |
| return numeric_setting_value ( is_signed, &tmp, len, value ); |
| } |
| |
| /** |
| * 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, |
| const struct setting *setting, |
| long *value ) { |
| |
| return fetch_numeric_setting ( settings, setting, |
| ( ( unsigned long * ) value ), 1 ); |
| } |
| |
| /** |
| * 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, |
| const struct setting *setting, |
| unsigned long *value ) { |
| |
| return fetch_numeric_setting ( settings, setting, value, 0 ); |
| } |
| |
| /** |
| * 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, |
| const struct setting *setting ) { |
| unsigned long value; |
| |
| fetch_numeric_setting ( settings, setting, &value, 1 ); |
| 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, |
| const struct setting *setting ) { |
| unsigned long value; |
| |
| fetch_numeric_setting ( settings, setting, &value, 0 ); |
| 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, |
| const struct setting *setting, |
| union uuid *uuid ) { |
| int len; |
| |
| len = fetch_raw_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 ) { |
| |
| /* Find target settings block */ |
| settings = settings_target ( settings ); |
| |
| /* Clear settings, if applicable */ |
| 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 ( const struct setting *a, const struct setting *b ) { |
| |
| /* If the settings have tags, compare them */ |
| if ( a->tag && ( a->tag == b->tag ) && ( a->scope == b->scope ) ) |
| 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 |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Format setting value as a string |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| int setting_format ( const struct setting_type *type, const void *raw, |
| size_t raw_len, char *buf, size_t len ) { |
| |
| /* Sanity check */ |
| if ( ! type->format ) |
| return -ENOTSUP; |
| |
| return type->format ( type, raw, raw_len, buf, len ); |
| } |
| |
| /** |
| * Parse formatted string to setting value |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| int setting_parse ( const struct setting_type *type, const char *value, |
| void *buf, size_t len ) { |
| |
| /* Sanity check */ |
| if ( ! type->parse ) |
| return -ENOTSUP; |
| |
| return type->parse ( type, value, buf, len ); |
| } |
| |
| /** |
| * Convert setting value to number |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @ret value Numeric value |
| * @ret rc Return status code |
| */ |
| int setting_numerate ( const struct setting_type *type, const void *raw, |
| size_t raw_len, unsigned long *value ) { |
| |
| /* Sanity check */ |
| if ( ! type->numerate ) |
| return -ENOTSUP; |
| |
| return type->numerate ( type, raw, raw_len, value ); |
| } |
| |
| /** |
| * Convert number to setting value |
| * |
| * @v type Setting type |
| * @v value Numeric value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| int setting_denumerate ( const struct setting_type *type, unsigned long value, |
| void *buf, size_t len ) { |
| |
| /* Sanity check */ |
| if ( ! type->denumerate ) |
| return -ENOTSUP; |
| |
| return type->denumerate ( type, value, buf, len ); |
| } |
| |
| /** |
| * Fetch formatted value of setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| int fetchf_setting ( struct settings *settings, const struct setting *setting, |
| struct settings **origin, struct setting *fetched, |
| char *buf, size_t len ) { |
| struct setting tmp_fetched; |
| void *raw; |
| int raw_len; |
| int ret; |
| |
| /* Use local buffers if necessary */ |
| if ( ! fetched ) |
| fetched = &tmp_fetched; |
| |
| /* Fetch raw value */ |
| raw_len = fetch_setting_copy ( settings, setting, origin, fetched, |
| &raw ); |
| if ( raw_len < 0 ) { |
| ret = raw_len; |
| goto err_fetch_copy; |
| } |
| |
| /* Sanity check */ |
| assert ( fetched->type != NULL ); |
| |
| /* Format setting */ |
| if ( ( ret = setting_format ( fetched->type, raw, raw_len, buf, |
| len ) ) < 0 ) |
| goto err_format; |
| |
| err_format: |
| free ( raw ); |
| err_fetch_copy: |
| return ret; |
| } |
| |
| /** |
| * Fetch copy of formatted value of setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @v value Buffer to allocate and fill with formatted value |
| * @ret len Length of formatted value, or negative error |
| * |
| * The caller is responsible for eventually freeing the allocated |
| * buffer. |
| */ |
| int fetchf_setting_copy ( struct settings *settings, |
| const struct setting *setting, |
| struct settings **origin, struct setting *fetched, |
| char **value ) { |
| struct settings *tmp_origin; |
| struct setting tmp_fetched; |
| int len; |
| int check_len; |
| |
| /* Use local buffers if necessary */ |
| if ( ! origin ) |
| origin = &tmp_origin; |
| if ( ! fetched ) |
| fetched = &tmp_fetched; |
| |
| /* Avoid returning uninitialised data on error */ |
| *value = NULL; |
| |
| /* Check existence, and fetch formatted value length */ |
| len = fetchf_setting ( settings, setting, origin, fetched, NULL, 0 ); |
| if ( len < 0 ) |
| return len; |
| |
| /* Allocate buffer */ |
| *value = zalloc ( len + 1 /* NUL */ ); |
| if ( ! *value ) |
| return -ENOMEM; |
| |
| /* Fetch formatted value */ |
| check_len = fetchf_setting ( *origin, fetched, NULL, NULL, *value, |
| ( len + 1 /* NUL */ ) ); |
| assert ( check_len == len ); |
| return len; |
| } |
| |
| /** |
| * Store formatted value of setting |
| * |
| * @v settings Settings block |
| * @v setting Setting to store |
| * @v value Formatted setting data, or NULL |
| * @ret rc Return status code |
| */ |
| int storef_setting ( struct settings *settings, const struct setting *setting, |
| const char *value ) { |
| void *raw; |
| int raw_len; |
| int check_len; |
| int rc; |
| |
| /* NULL value or empty string implies deletion */ |
| if ( ( ! value ) || ( ! value[0] ) ) |
| return delete_setting ( settings, setting ); |
| |
| /* Sanity check */ |
| assert ( setting->type != NULL ); |
| |
| /* Get raw value length */ |
| raw_len = setting_parse ( setting->type, value, NULL, 0 ); |
| if ( raw_len < 0 ) { |
| rc = raw_len; |
| goto err_raw_len; |
| } |
| |
| /* Allocate buffer for raw value */ |
| raw = malloc ( raw_len ); |
| if ( ! raw ) { |
| rc = -ENOMEM; |
| goto err_alloc_raw; |
| } |
| |
| /* Parse formatted value */ |
| check_len = setting_parse ( setting->type, value, raw, raw_len ); |
| assert ( check_len == raw_len ); |
| |
| /* Store raw value */ |
| if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) |
| goto err_store; |
| |
| err_store: |
| free ( raw ); |
| err_alloc_raw: |
| err_raw_len: |
| return rc; |
| } |
| |
| /** |
| * Fetch numeric value of setting |
| * |
| * @v settings Settings block, or NULL to search all blocks |
| * @v setting Setting to fetch |
| * @v origin Origin of setting to fill in, or NULL |
| * @v fetched Fetched setting to fill in, or NULL |
| * @v value Numeric value to fill in |
| * @ret rc Return status code |
| */ |
| int fetchn_setting ( struct settings *settings, const struct setting *setting, |
| struct settings **origin, struct setting *fetched, |
| unsigned long *value ) { |
| struct setting tmp_fetched; |
| void *raw; |
| int raw_len; |
| int rc; |
| |
| /* Use local buffers if necessary */ |
| if ( ! fetched ) |
| fetched = &tmp_fetched; |
| |
| /* Fetch raw value */ |
| raw_len = fetch_setting_copy ( settings, setting, origin, fetched, |
| &raw ); |
| if ( raw_len < 0 ) { |
| rc = raw_len; |
| goto err_fetch_copy; |
| } |
| |
| /* Sanity check */ |
| assert ( fetched->type != NULL ); |
| |
| /* Numerate setting */ |
| if ( ( rc = setting_numerate ( fetched->type, raw, raw_len, |
| value ) ) < 0 ) |
| goto err_numerate; |
| |
| err_numerate: |
| free ( raw ); |
| err_fetch_copy: |
| return rc; |
| } |
| |
| /** |
| * Store numeric value of setting |
| * |
| * @v settings Settings block |
| * @v setting Setting |
| * @v value Numeric value |
| * @ret rc Return status code |
| */ |
| int storen_setting ( struct settings *settings, const struct setting *setting, |
| unsigned long value ) { |
| void *raw; |
| int raw_len; |
| int check_len; |
| int rc; |
| |
| /* Sanity check */ |
| assert ( setting->type != NULL ); |
| |
| /* Get raw value length */ |
| raw_len = setting_denumerate ( setting->type, value, NULL, 0 ); |
| if ( raw_len < 0 ) { |
| rc = raw_len; |
| goto err_raw_len; |
| } |
| |
| /* Allocate buffer for raw value */ |
| raw = malloc ( raw_len ); |
| if ( ! raw ) { |
| rc = -ENOMEM; |
| goto err_alloc_raw; |
| } |
| |
| /* Denumerate value */ |
| check_len = setting_denumerate ( setting->type, value, raw, raw_len ); |
| assert ( check_len == raw_len ); |
| |
| /* Store raw value */ |
| if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) |
| goto err_store; |
| |
| err_store: |
| free ( raw ); |
| err_alloc_raw: |
| err_raw_len: |
| return rc; |
| } |
| |
| /****************************************************************************** |
| * |
| * Named settings |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Find predefined setting |
| * |
| * @v name Name |
| * @ret setting Setting, or NULL |
| */ |
| 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 const struct setting_type * find_setting_type ( const char *name ) { |
| const 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 |
| * @ret rc Return status code |
| * |
| * Interprets a name of the form |
| * "[settings_name/]tag_name[:type_name]" and fills in the appropriate |
| * fields. |
| * |
| * Note that on success, this function will have modified the original |
| * setting @c name. |
| */ |
| int parse_setting_name ( char *name, get_child_settings_t get_child, |
| struct settings **settings, struct setting *setting ) { |
| char *settings_name; |
| char *setting_name; |
| char *type_name; |
| struct setting *predefined; |
| int rc; |
| |
| /* Set defaults */ |
| *settings = &settings_root; |
| memset ( setting, 0, sizeof ( *setting ) ); |
| setting->name = ""; |
| |
| /* Split name into "[settings_name/]setting_name[:type_name]" */ |
| if ( ( setting_name = strchr ( name, '/' ) ) != NULL ) { |
| *(setting_name++) = 0; |
| settings_name = name; |
| } else { |
| setting_name = 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 ); |
| rc = -ENODEV; |
| goto err; |
| } |
| } |
| |
| /* Identify setting */ |
| setting->tag = parse_setting_tag ( setting_name ); |
| setting->scope = (*settings)->default_scope; |
| setting->name = setting_name; |
| for_each_table_entry ( predefined, SETTINGS ) { |
| /* Matches a predefined setting; use that setting */ |
| if ( setting_cmp ( predefined, setting ) == 0 ) { |
| memcpy ( setting, predefined, sizeof ( *setting ) ); |
| break; |
| } |
| } |
| |
| /* 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 ); |
| rc = -ENOTSUP; |
| goto err; |
| } |
| } |
| |
| return 0; |
| |
| err: |
| /* Restore original name */ |
| if ( settings_name ) |
| *( setting_name - 1 ) = '/'; |
| if ( type_name ) |
| *( type_name - 1 ) = ':'; |
| return rc; |
| } |
| |
| /** |
| * Return full setting name |
| * |
| * @v settings Settings block, or NULL |
| * @v setting Setting |
| * @v buf Buffer |
| * @v len Length of buffer |
| * @ret len Length of setting name, or negative error |
| */ |
| int setting_name ( struct settings *settings, const struct setting *setting, |
| char *buf, size_t len ) { |
| const char *name; |
| |
| settings = settings_target ( settings ); |
| name = settings_name ( settings ); |
| return snprintf ( buf, len, "%s%s%s:%s", name, ( name[0] ? "/" : "" ), |
| setting->name, setting->type->name ); |
| } |
| |
| /****************************************************************************** |
| * |
| * Setting types |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Parse string setting value |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_string_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, size_t len ) { |
| size_t raw_len = strlen ( value ); /* Exclude terminating NUL */ |
| |
| /* Copy string to buffer */ |
| if ( len > raw_len ) |
| len = raw_len; |
| memcpy ( buf, value, len ); |
| |
| return raw_len; |
| } |
| |
| /** |
| * Format string setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_string_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, char *buf, |
| size_t len ) { |
| |
| /* Copy string to buffer, and terminate */ |
| memset ( buf, 0, len ); |
| if ( len > raw_len ) |
| len = raw_len; |
| memcpy ( buf, raw, len ); |
| |
| return raw_len; |
| } |
| |
| /** A string setting type */ |
| const struct setting_type setting_type_string __setting_type = { |
| .name = "string", |
| .parse = parse_string_setting, |
| .format = format_string_setting, |
| }; |
| |
| /** |
| * Parse URI-encoded string setting value |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_uristring_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, size_t len ){ |
| char tmp[ len + 1 /* NUL */ ]; |
| size_t raw_len; |
| |
| /* Decode to temporary buffer (including NUL) */ |
| raw_len = uri_decode ( value, tmp, sizeof ( tmp ) ); |
| |
| /* Copy to output buffer (excluding NUL) */ |
| if ( len > raw_len ) |
| len = raw_len; |
| memcpy ( buf, tmp, len ); |
| |
| return raw_len; |
| } |
| |
| /** |
| * Format URI-encoded string setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_uristring_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| char tmp[ raw_len + 1 /* NUL */ ]; |
| |
| /* Copy to temporary buffer and terminate */ |
| memcpy ( tmp, raw, raw_len ); |
| tmp[raw_len] = '\0'; |
| |
| /* Encode directly into output buffer */ |
| return uri_encode ( tmp, buf, len, URI_FRAGMENT ); |
| } |
| |
| /** A URI-encoded string setting type */ |
| const struct setting_type setting_type_uristring __setting_type = { |
| .name = "uristring", |
| .parse = parse_uristring_setting, |
| .format = format_uristring_setting, |
| }; |
| |
| /** |
| * Parse IPv4 address setting value |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_ipv4_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, size_t len ) { |
| struct in_addr ipv4; |
| |
| /* Parse IPv4 address */ |
| if ( inet_aton ( value, &ipv4 ) == 0 ) |
| return -EINVAL; |
| |
| /* Copy to buffer */ |
| if ( len > sizeof ( ipv4 ) ) |
| len = sizeof ( ipv4 ); |
| memcpy ( buf, &ipv4, len ); |
| |
| return ( sizeof ( ipv4 ) ); |
| } |
| |
| /** |
| * Format IPv4 address setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_ipv4_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, char *buf, |
| size_t len ) { |
| const struct in_addr *ipv4 = raw; |
| |
| if ( raw_len < sizeof ( *ipv4 ) ) |
| return -EINVAL; |
| return snprintf ( buf, len, "%s", inet_ntoa ( *ipv4 ) ); |
| } |
| |
| /** An IPv4 address setting type */ |
| const struct setting_type setting_type_ipv4 __setting_type = { |
| .name = "ipv4", |
| .parse = parse_ipv4_setting, |
| .format = format_ipv4_setting, |
| }; |
| |
| /** |
| * Integer setting type indices |
| * |
| * These indexes are defined such that (1<<index) gives the width of |
| * the integer, in bytes. |
| */ |
| enum setting_type_int_index { |
| SETTING_TYPE_INT8 = 0, |
| SETTING_TYPE_INT16 = 1, |
| SETTING_TYPE_INT32 = 2, |
| }; |
| |
| /** |
| * Integer setting type names |
| * |
| * These names exist as a static array in order to allow the type's |
| * integer size and signedness to be determined from the type's name. |
| * Note that there are no separate entries for the signed integer |
| * types: the name pointers simply point to the second character of |
| * the relevant string. |
| */ |
| static const char setting_type_int_name[][8] = { |
| [SETTING_TYPE_INT8] = "uint8", |
| [SETTING_TYPE_INT16] = "uint16", |
| [SETTING_TYPE_INT32] = "uint32", |
| }; |
| |
| /** |
| * Get unsigned integer setting type name |
| * |
| * @v index Integer setting type index |
| * @ret name Setting type name |
| */ |
| #define SETTING_TYPE_UINT_NAME( index ) setting_type_int_name[index] |
| |
| /** |
| * Get signed integer setting type name |
| * |
| * @v index Integer setting type index |
| * @ret name Setting type name |
| */ |
| #define SETTING_TYPE_INT_NAME( index ) ( setting_type_int_name[index] + 1 ) |
| |
| /** |
| * Get integer setting type index |
| * |
| * @v type Setting type |
| * @ret index Integer setting type index |
| */ |
| static unsigned int setting_type_int_index ( const struct setting_type *type ) { |
| |
| return ( ( type->name - setting_type_int_name[0] ) / |
| sizeof ( setting_type_int_name[0] ) ); |
| } |
| |
| /** |
| * Get integer setting type width |
| * |
| * @v type Setting type |
| * @ret index Integer setting type width |
| */ |
| static unsigned int setting_type_int_width ( const struct setting_type *type ) { |
| |
| return ( 1 << setting_type_int_index ( type ) ); |
| } |
| |
| /** |
| * Get integer setting type signedness |
| * |
| * @v type Setting type |
| * @ret is_signed Integer setting type is signed |
| */ |
| static int setting_type_int_is_signed ( const struct setting_type *type ) { |
| return ( ( type->name - setting_type_int_name[0] ) & 1 ); |
| } |
| |
| /** |
| * Convert number to setting value |
| * |
| * @v type Setting type |
| * @v value Numeric value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| static int denumerate_int_setting ( const struct setting_type *type, |
| unsigned long value, void *buf, |
| size_t len ) { |
| unsigned int size = setting_type_int_width ( type ); |
| union { |
| uint32_t num; |
| uint8_t bytes[4]; |
| } u; |
| |
| u.num = htonl ( value ); |
| if ( len > size ) |
| len = size; |
| memcpy ( buf, &u.bytes[ sizeof ( u ) - size ], len ); |
| |
| return size; |
| } |
| |
| /** |
| * Convert setting value to number |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v value Numeric value to fill in |
| * @ret rc Return status code |
| */ |
| static int numerate_int_setting ( const struct setting_type *type, |
| const void *raw, size_t raw_len, |
| unsigned long *value ) { |
| int is_signed = setting_type_int_is_signed ( type ); |
| int check_len; |
| |
| /* Extract numeric value */ |
| check_len = numeric_setting_value ( is_signed, raw, raw_len, value ); |
| if ( check_len < 0 ) |
| return check_len; |
| assert ( check_len == ( int ) raw_len ); |
| |
| return 0; |
| } |
| |
| /** |
| * Parse integer setting value |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_int_setting ( const struct setting_type *type, |
| const char *value, void *buf, size_t len ) { |
| char *endp; |
| unsigned long num_value; |
| |
| /* Parse value */ |
| num_value = strtoul ( value, &endp, 0 ); |
| if ( *endp ) |
| return -EINVAL; |
| |
| return type->denumerate ( type, num_value, buf, len ); |
| } |
| |
| /** |
| * Format signed integer setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_int_setting ( const struct setting_type *type, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| unsigned long value; |
| int ret; |
| |
| /* Extract numeric value */ |
| if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) |
| return ret; |
| |
| /* Format value */ |
| return snprintf ( buf, len, "%ld", value ); |
| } |
| |
| /** |
| * Format unsigned integer setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_uint_setting ( const struct setting_type *type, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| unsigned long value; |
| int ret; |
| |
| /* Extract numeric value */ |
| if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) |
| return ret; |
| |
| /* Format value */ |
| return snprintf ( buf, len, "%#lx", value ); |
| } |
| |
| /** |
| * Define a signed integer setting type |
| * |
| * @v index Integer setting type index |
| * @ret type Setting type |
| */ |
| #define SETTING_TYPE_INT( index ) { \ |
| .name = SETTING_TYPE_INT_NAME ( index ), \ |
| .parse = parse_int_setting, \ |
| .format = format_int_setting, \ |
| .denumerate = denumerate_int_setting, \ |
| .numerate = numerate_int_setting, \ |
| } |
| |
| /** |
| * Define an unsigned integer setting type |
| * |
| * @v index Integer setting type index |
| * @ret type Setting type |
| */ |
| #define SETTING_TYPE_UINT( index ) { \ |
| .name = SETTING_TYPE_UINT_NAME ( index ), \ |
| .parse = parse_int_setting, \ |
| .format = format_uint_setting, \ |
| .denumerate = denumerate_int_setting, \ |
| .numerate = numerate_int_setting, \ |
| } |
| |
| /** A signed 8-bit integer setting type */ |
| const struct setting_type setting_type_int8 __setting_type = |
| SETTING_TYPE_INT ( SETTING_TYPE_INT8 ); |
| |
| /** A signed 16-bit integer setting type */ |
| const struct setting_type setting_type_int16 __setting_type = |
| SETTING_TYPE_INT ( SETTING_TYPE_INT16 ); |
| |
| /** A signed 32-bit integer setting type */ |
| const struct setting_type setting_type_int32 __setting_type = |
| SETTING_TYPE_INT ( SETTING_TYPE_INT32 ); |
| |
| /** An unsigned 8-bit integer setting type */ |
| const struct setting_type setting_type_uint8 __setting_type = |
| SETTING_TYPE_UINT ( SETTING_TYPE_INT8 ); |
| |
| /** An unsigned 16-bit integer setting type */ |
| const struct setting_type setting_type_uint16 __setting_type = |
| SETTING_TYPE_UINT ( SETTING_TYPE_INT16 ); |
| |
| /** An unsigned 32-bit integer setting type */ |
| const struct setting_type setting_type_uint32 __setting_type = |
| SETTING_TYPE_UINT ( SETTING_TYPE_INT32 ); |
| |
| /** |
| * Format hex string setting value |
| * |
| * @v delimiter Byte delimiter |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_hex_setting ( const char *delimiter, const void *raw, |
| size_t raw_len, char *buf, size_t len ) { |
| const uint8_t *bytes = raw; |
| int used = 0; |
| unsigned int i; |
| |
| 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 ? delimiter : "" ), |
| bytes[i] ); |
| } |
| return used; |
| } |
| |
| /** |
| * Parse hex string setting value (using colon delimiter) |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @v size Integer size, in bytes |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_hex_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, size_t len ) { |
| return hex_decode ( value, ':', buf, len ); |
| } |
| |
| /** |
| * Format hex string setting value (using colon delimiter) |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_hex_colon_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| return format_hex_setting ( ":", raw, raw_len, buf, len ); |
| } |
| |
| /** |
| * Parse hex string setting value (using hyphen delimiter) |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @v size Integer size, in bytes |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, |
| size_t len ) { |
| return hex_decode ( value, '-', buf, len ); |
| } |
| |
| /** |
| * Format hex string setting value (using hyphen delimiter) |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_hex_hyphen_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| return format_hex_setting ( "-", raw, raw_len, buf, len ); |
| } |
| |
| /** |
| * Parse hex string setting value (using no delimiter) |
| * |
| * @v type Setting type |
| * @v value Formatted setting value |
| * @v buf Buffer to contain raw value |
| * @v len Length of buffer |
| * @v size Integer size, in bytes |
| * @ret len Length of raw value, or negative error |
| */ |
| static int parse_hex_raw_setting ( const struct setting_type *type __unused, |
| const char *value, void *buf, size_t len ) { |
| return hex_decode ( value, 0, buf, len ); |
| } |
| |
| /** |
| * Format hex string setting value (using no delimiter) |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_hex_raw_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, |
| char *buf, size_t len ) { |
| return format_hex_setting ( "", raw, raw_len, buf, len ); |
| } |
| |
| /** A hex-string setting (colon-delimited) */ |
| const struct setting_type setting_type_hex __setting_type = { |
| .name = "hex", |
| .parse = parse_hex_setting, |
| .format = format_hex_colon_setting, |
| }; |
| |
| /** A hex-string setting (hyphen-delimited) */ |
| const struct setting_type setting_type_hexhyp __setting_type = { |
| .name = "hexhyp", |
| .parse = parse_hex_hyphen_setting, |
| .format = format_hex_hyphen_setting, |
| }; |
| |
| /** A hex-string setting (non-delimited) */ |
| const struct setting_type setting_type_hexraw __setting_type = { |
| .name = "hexraw", |
| .parse = parse_hex_raw_setting, |
| .format = format_hex_raw_setting, |
| }; |
| |
| /** |
| * Format UUID setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_uuid_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, char *buf, |
| size_t len ) { |
| const union uuid *uuid = raw; |
| |
| /* Range check */ |
| if ( raw_len != sizeof ( *uuid ) ) |
| return -ERANGE; |
| |
| /* Format value */ |
| return snprintf ( buf, len, "%s", uuid_ntoa ( uuid ) ); |
| } |
| |
| /** UUID setting type */ |
| const struct setting_type setting_type_uuid __setting_type = { |
| .name = "uuid", |
| .format = format_uuid_setting, |
| }; |
| |
| /** |
| * Format PCI bus:dev.fn setting value |
| * |
| * @v type Setting type |
| * @v raw Raw setting value |
| * @v raw_len Length of raw setting value |
| * @v buf Buffer to contain formatted value |
| * @v len Length of buffer |
| * @ret len Length of formatted value, or negative error |
| */ |
| static int format_busdevfn_setting ( const struct setting_type *type __unused, |
| const void *raw, size_t raw_len, char *buf, |
| size_t len ) { |
| unsigned long busdevfn; |
| int check_len; |
| |
| /* Extract numeric value */ |
| check_len = numeric_setting_value ( 0, raw, raw_len, &busdevfn ); |
| if ( check_len < 0 ) |
| return check_len; |
| assert ( check_len == ( int ) raw_len ); |
| |
| /* Format value */ |
| return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), |
| PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); |
| } |
| |
| /** PCI bus:dev.fn setting type */ |
| const struct setting_type setting_type_busdevfn __setting_type = { |
| .name = "busdevfn", |
| .format = format_busdevfn_setting, |
| }; |
| |
| /****************************************************************************** |
| * |
| * Setting expansion |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Expand variables within string |
| * |
| * @v string String |
| * @ret expstr Expanded string |
| * |
| * The expanded string is allocated with malloc() and the caller must |
| * eventually free() it. |
| */ |
| char * expand_settings ( const char *string ) { |
| struct settings *settings; |
| struct setting setting; |
| char *expstr; |
| char *start; |
| char *end; |
| char *head; |
| char *name; |
| char *tail; |
| char *value; |
| char *tmp; |
| int new_len; |
| int rc; |
| |
| /* Obtain temporary modifiable copy of string */ |
| expstr = strdup ( string ); |
| if ( ! expstr ) |
| return NULL; |
| |
| /* Expand while expansions remain */ |
| while ( 1 ) { |
| |
| head = expstr; |
| |
| /* Locate setting to be expanded */ |
| start = NULL; |
| end = NULL; |
| for ( tmp = expstr ; *tmp ; tmp++ ) { |
| if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) |
| start = tmp; |
| if ( start && ( tmp[0] == '}' ) ) { |
| end = tmp; |
| break; |
| } |
| } |
| if ( ! end ) |
| break; |
| *start = '\0'; |
| name = ( start + 2 ); |
| *end = '\0'; |
| tail = ( end + 1 ); |
| |
| /* Expand setting */ |
| if ( ( rc = parse_setting_name ( name, find_child_settings, |
| &settings, |
| &setting ) ) != 0 ) { |
| /* Treat invalid setting names as empty */ |
| value = NULL; |
| } else { |
| /* Fetch and format setting value. Ignore |
| * errors; treat non-existent settings as empty. |
| */ |
| fetchf_setting_copy ( settings, &setting, NULL, NULL, |
| &value ); |
| } |
| |
| /* Construct expanded string and discard old string */ |
| tmp = expstr; |
| new_len = asprintf ( &expstr, "%s%s%s", |
| head, ( value ? value : "" ), tail ); |
| free ( value ); |
| free ( tmp ); |
| if ( new_len < 0 ) |
| return NULL; |
| } |
| |
| return expstr; |
| } |
| |
| /****************************************************************************** |
| * |
| * Settings |
| * |
| ****************************************************************************** |
| */ |
| |
| /** Hostname setting */ |
| const struct setting hostname_setting __setting ( SETTING_HOST ) = { |
| .name = "hostname", |
| .description = "Host name", |
| .tag = DHCP_HOST_NAME, |
| .type = &setting_type_string, |
| }; |
| |
| /** Domain name setting */ |
| const struct setting domain_setting __setting ( SETTING_IPv4_EXTRA ) = { |
| .name = "domain", |
| .description = "DNS domain", |
| .tag = DHCP_DOMAIN_NAME, |
| .type = &setting_type_string, |
| }; |
| |
| /** TFTP server setting */ |
| const struct setting next_server_setting __setting ( SETTING_BOOT ) = { |
| .name = "next-server", |
| .description = "TFTP server", |
| .tag = DHCP_EB_SIADDR, |
| .type = &setting_type_ipv4, |
| }; |
| |
| /** Filename setting */ |
| const struct setting filename_setting __setting ( SETTING_BOOT ) = { |
| .name = "filename", |
| .description = "Boot filename", |
| .tag = DHCP_BOOTFILE_NAME, |
| .type = &setting_type_string, |
| }; |
| |
| /** Root path setting */ |
| const struct setting root_path_setting __setting ( SETTING_SANBOOT ) = { |
| .name = "root-path", |
| .description = "SAN root path", |
| .tag = DHCP_ROOT_PATH, |
| .type = &setting_type_string, |
| }; |
| |
| /** Username setting */ |
| const struct setting username_setting __setting ( SETTING_AUTH ) = { |
| .name = "username", |
| .description = "User name", |
| .tag = DHCP_EB_USERNAME, |
| .type = &setting_type_string, |
| }; |
| |
| /** Password setting */ |
| const struct setting password_setting __setting ( SETTING_AUTH ) = { |
| .name = "password", |
| .description = "Password", |
| .tag = DHCP_EB_PASSWORD, |
| .type = &setting_type_string, |
| }; |
| |
| /** Priority setting */ |
| const struct setting priority_setting __setting ( SETTING_MISC ) = { |
| .name = "priority", |
| .description = "Settings priority", |
| .tag = DHCP_EB_PRIORITY, |
| .type = &setting_type_int8, |
| }; |
| |
| /** DHCP user class setting */ |
| const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA ) = { |
| .name = "user-class", |
| .description = "DHCP user class", |
| .tag = DHCP_USER_CLASS_ID, |
| .type = &setting_type_string, |
| }; |
| |
| /****************************************************************************** |
| * |
| * Built-in settings block |
| * |
| ****************************************************************************** |
| */ |
| |
| /** Built-in setting scope */ |
| const struct settings_scope builtin_scope; |
| |
| /** |
| * Fetch error number setting |
| * |
| * @v data Buffer to fill with setting data |
| * @v len Length of buffer |
| * @ret len Length of setting data, or negative error |
| */ |
| static int errno_fetch ( void *data, size_t len ) { |
| uint32_t content; |
| |
| /* Return current error */ |
| content = htonl ( errno ); |
| if ( len > sizeof ( content ) ) |
| len = sizeof ( content ); |
| memcpy ( data, &content, len ); |
| return sizeof ( content ); |
| } |
| |
| /** Error number setting */ |
| const struct setting errno_setting __setting ( SETTING_MISC ) = { |
| .name = "errno", |
| .description = "Last error", |
| .type = &setting_type_uint32, |
| .scope = &builtin_scope, |
| }; |
| |
| /** Error number built-in setting */ |
| struct builtin_setting errno_builtin_setting __builtin_setting = { |
| .setting = &errno_setting, |
| .fetch = errno_fetch, |
| }; |
| |
| /** |
| * Fetch build architecture setting |
| * |
| * @v data Buffer to fill with setting data |
| * @v len Length of buffer |
| * @ret len Length of setting data, or negative error |
| */ |
| static int buildarch_fetch ( void *data, size_t len ) { |
| static const char buildarch[] = _S2 ( ARCH ); |
| |
| strncpy ( data, buildarch, len ); |
| return ( sizeof ( buildarch ) - 1 /* NUL */ ); |
| } |
| |
| /** Build architecture setting */ |
| const struct setting buildarch_setting __setting ( SETTING_MISC ) = { |
| .name = "buildarch", |
| .description = "Build architecture", |
| .type = &setting_type_string, |
| .scope = &builtin_scope, |
| }; |
| |
| /** Build architecture built-in setting */ |
| struct builtin_setting buildarch_builtin_setting __builtin_setting = { |
| .setting = &buildarch_setting, |
| .fetch = buildarch_fetch, |
| }; |
| |
| /** |
| * Fetch platform setting |
| * |
| * @v data Buffer to fill with setting data |
| * @v len Length of buffer |
| * @ret len Length of setting data, or negative error |
| */ |
| static int platform_fetch ( void *data, size_t len ) { |
| static const char platform[] = _S2 ( PLATFORM ); |
| |
| strncpy ( data, platform, len ); |
| return ( sizeof ( platform ) - 1 /* NUL */ ); |
| } |
| |
| /** Platform setting */ |
| const struct setting platform_setting __setting ( SETTING_MISC ) = { |
| .name = "platform", |
| .description = "Platform", |
| .type = &setting_type_string, |
| .scope = &builtin_scope, |
| }; |
| |
| /** Platform built-in setting */ |
| struct builtin_setting platform_builtin_setting __builtin_setting = { |
| .setting = &platform_setting, |
| .fetch = platform_fetch, |
| }; |
| |
| /** |
| * Fetch version setting |
| * |
| * @v data Buffer to fill with setting data |
| * @v len Length of buffer |
| * @ret len Length of setting data, or negative error |
| */ |
| static int version_fetch ( void *data, size_t len ) { |
| strncpy ( data, product_version, len ); |
| return ( strlen ( product_version ) ); |
| } |
| |
| /** Version setting */ |
| const struct setting version_setting __setting ( SETTING_MISC ) = { |
| .name = "version", |
| .description = "Version", |
| .type = &setting_type_string, |
| .scope = &builtin_scope, |
| }; |
| |
| /** Version built-in setting */ |
| struct builtin_setting version_builtin_setting __builtin_setting = { |
| .setting = &version_setting, |
| .fetch = version_fetch, |
| }; |
| |
| /** |
| * Fetch built-in 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 |
| */ |
| static int builtin_fetch ( struct settings *settings __unused, |
| struct setting *setting, |
| void *data, size_t len ) { |
| struct builtin_setting *builtin; |
| |
| for_each_table_entry ( builtin, BUILTIN_SETTINGS ) { |
| if ( setting_cmp ( setting, builtin->setting ) == 0 ) |
| return builtin->fetch ( data, len ); |
| } |
| return -ENOENT; |
| } |
| |
| /** |
| * Check applicability of built-in setting |
| * |
| * @v settings Settings block |
| * @v setting Setting |
| * @ret applies Setting applies within this settings block |
| */ |
| static int builtin_applies ( struct settings *settings __unused, |
| const struct setting *setting ) { |
| |
| return ( setting->scope == &builtin_scope ); |
| } |
| |
| /** Built-in settings operations */ |
| static struct settings_operations builtin_settings_operations = { |
| .applies = builtin_applies, |
| .fetch = builtin_fetch, |
| }; |
| |
| /** Built-in settings */ |
| static struct settings builtin_settings = { |
| .refcnt = NULL, |
| .siblings = LIST_HEAD_INIT ( builtin_settings.siblings ), |
| .children = LIST_HEAD_INIT ( builtin_settings.children ), |
| .op = &builtin_settings_operations, |
| }; |
| |
| /** Initialise built-in settings */ |
| static void builtin_init ( void ) { |
| int rc; |
| |
| if ( ( rc = register_settings ( &builtin_settings, NULL, |
| "builtin" ) ) != 0 ) { |
| DBG ( "Could not register built-in settings: %s\n", |
| strerror ( rc ) ); |
| return; |
| } |
| } |
| |
| /** Built-in settings initialiser */ |
| struct init_fn builtin_init_fn __init_fn ( INIT_NORMAL ) = { |
| .initialise = builtin_init, |
| }; |