| /* |
| * 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 ) { |
| struct net_device *netdev; |
| |
| subname = remainder; |
| remainder = strchr ( subname, '.' ); |
| if ( remainder ) |
| *(remainder++) = '\0'; |
| |
| /* Special case "netX" root settings block */ |
| if ( ( subname == name_copy ) && ! strcmp ( subname, "netX" ) && |
| ( ( netdev = last_opened_netdev() ) != NULL ) ) |
| settings = get_child ( settings, netdev->name ); |
| else |
| settings = get_child ( settings, subname ); |
| |
| if ( ! settings ) |
| break; |
| } |
| |
| return settings; |
| } |
| |
| /** |
| * Find named settings block |
| * |
| * @v name Name |
| * @ret settings Settings block, or NULL |
| */ |
| struct settings * find_settings ( const char *name ) { |
| |
| return parse_settings_name ( name, find_child_settings ); |
| } |
| |
| /** |
| * Apply all settings |
| * |
| * @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, URI_FRAGMENT ); |
| } |
| } |
| |
| /** 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 = "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, |
| }; |