/* * Copyright (C) 2008 Michael Brown . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /** @file * * Configuration settings * */ /****************************************************************************** * * Generic settings blocks * ****************************************************************************** */ /** * A generic setting * */ struct generic_setting { /** List of generic settings */ struct list_head list; /** Setting */ struct setting setting; /** Size of setting name */ size_t name_len; /** Size of setting data */ size_t data_len; }; /** * Get generic setting name * * @v generic Generic setting * @ret name Generic setting name */ static inline void * generic_setting_name ( struct generic_setting *generic ) { return ( ( ( void * ) generic ) + sizeof ( *generic ) ); } /** * Get generic setting data * * @v generic Generic setting * @ret data Generic setting data */ static inline void * generic_setting_data ( struct generic_setting *generic ) { return ( ( ( void * ) generic ) + sizeof ( *generic ) + generic->name_len ); } /** * Find generic setting * * @v generics Generic settings block * @v setting Setting to find * @ret generic Generic setting, or NULL */ static struct generic_setting * find_generic_setting ( struct generic_settings *generics, struct setting *setting ) { struct generic_setting *generic; list_for_each_entry ( generic, &generics->list, list ) { if ( setting_cmp ( &generic->setting, setting ) == 0 ) return generic; } return NULL; } /** * Store value of generic setting * * @v settings Settings block * @v setting Setting to store * @v data Setting data, or NULL to clear setting * @v len Length of setting data * @ret rc Return status code */ int generic_settings_store ( struct settings *settings, struct setting *setting, const void *data, size_t len ) { struct generic_settings *generics = container_of ( settings, struct generic_settings, settings ); struct generic_setting *old; struct generic_setting *new = NULL; size_t name_len; /* Identify existing generic setting, if any */ old = find_generic_setting ( generics, setting ); /* Create new generic setting, if required */ if ( len ) { /* Allocate new generic setting */ name_len = ( strlen ( setting->name ) + 1 ); new = zalloc ( sizeof ( *new ) + name_len + len ); if ( ! new ) return -ENOMEM; /* Populate new generic setting */ new->name_len = name_len; new->data_len = len; memcpy ( &new->setting, setting, sizeof ( new->setting ) ); new->setting.name = generic_setting_name ( new ); memcpy ( generic_setting_name ( new ), setting->name, name_len ); memcpy ( generic_setting_data ( new ), data, len ); } /* Delete existing generic setting, if any */ if ( old ) { list_del ( &old->list ); free ( old ); } /* Add new setting to list, if any */ if ( new ) list_add ( &new->list, &generics->list ); return 0; } /** * Fetch value of generic setting * * @v settings Settings block * @v setting Setting to fetch * @v data Buffer to fill with setting data * @v len Length of buffer * @ret len Length of setting data, or negative error */ int generic_settings_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { struct generic_settings *generics = container_of ( settings, struct generic_settings, settings ); struct generic_setting *generic; /* Find generic setting */ generic = find_generic_setting ( generics, setting ); if ( ! generic ) return -ENOENT; /* Copy out generic setting data */ if ( len > generic->data_len ) len = generic->data_len; memcpy ( data, generic_setting_data ( generic ), len ); return generic->data_len; } /** * Clear generic settings block * * @v settings Settings block */ void generic_settings_clear ( struct settings *settings ) { struct generic_settings *generics = container_of ( settings, struct generic_settings, settings ); struct generic_setting *generic; struct generic_setting *tmp; list_for_each_entry_safe ( generic, tmp, &generics->list, list ) { list_del ( &generic->list ); free ( generic ); } assert ( list_empty ( &generics->list ) ); } /** Generic settings operations */ struct settings_operations generic_settings_operations = { .store = generic_settings_store, .fetch = generic_settings_fetch, .clear = generic_settings_clear, }; /****************************************************************************** * * Registered settings blocks * ****************************************************************************** */ /** Root generic settings block */ struct generic_settings generic_settings_root = { .settings = { .refcnt = NULL, .name = "", .siblings = LIST_HEAD_INIT ( generic_settings_root.settings.siblings ), .children = LIST_HEAD_INIT ( generic_settings_root.settings.children ), .op = &generic_settings_operations, }, .list = LIST_HEAD_INIT ( generic_settings_root.list ), }; /** Root settings block */ #define settings_root generic_settings_root.settings /** * Find child named settings block * * @v parent Parent settings block * @v name Name within this parent * @ret settings Settings block, or NULL */ static struct settings * find_child_settings ( struct settings *parent, const char *name ) { struct settings *settings; /* Treat empty name as meaning "this block" */ if ( ! *name ) return parent; /* Look for child with matching name */ list_for_each_entry ( settings, &parent->children, siblings ) { if ( strcmp ( settings->name, name ) == 0 ) return settings; } return NULL; } /** * Find or create child named settings block * * @v parent Parent settings block * @v name Name within this parent * @ret settings Settings block, or NULL */ static struct settings * autovivify_child_settings ( struct settings *parent, const char *name ) { struct { struct generic_settings generic; char name[ strlen ( name ) + 1 /* NUL */ ]; } *new_child; struct settings *settings; /* Return existing settings, if existent */ if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) return settings; /* Create new generic settings block */ new_child = zalloc ( sizeof ( *new_child ) ); if ( ! new_child ) { DBGC ( parent, "Settings %p could not create child %s\n", parent, name ); return NULL; } memcpy ( new_child->name, name, sizeof ( new_child->name ) ); generic_settings_init ( &new_child->generic, NULL, new_child->name ); settings = &new_child->generic.settings; register_settings ( settings, parent ); return settings; } /** * Return settings block name (for debug only) * * @v settings Settings block * @ret name Settings block name */ static const char * settings_name ( struct settings *settings ) { static char buf[64]; char tmp[ sizeof ( buf ) ]; int count; for ( count = 0 ; settings ; settings = settings->parent ) { memcpy ( tmp, buf, sizeof ( tmp ) ); snprintf ( buf, sizeof ( buf ), "%s%c%s", settings->name, ( count++ ? '.' : '\0' ), tmp ); } return ( buf + 1 ); } /** * Parse settings block name * * @v name Name * @v get_child Function to find or create child settings block * @ret settings Settings block, or NULL */ static struct settings * parse_settings_name ( const char *name, struct settings * ( * get_child ) ( struct settings *, const char * ) ) { struct settings *settings = &settings_root; char name_copy[ strlen ( name ) + 1 ]; char *subname; char *remainder; /* Create modifiable copy of name */ memcpy ( name_copy, name, sizeof ( name_copy ) ); remainder = name_copy; /* Parse each name component in turn */ while ( remainder ) { subname = remainder; remainder = strchr ( subname, '.' ); if ( remainder ) *(remainder++) = '\0'; settings = get_child ( settings, subname ); if ( ! settings ) break; } return settings; } /** * Find named settings block * * @v name Name * @ret settings Settings block, or NULL */ struct settings * find_settings ( const char *name ) { return parse_settings_name ( name, find_child_settings ); } /** * Apply all settings * * @ret rc Return status code */ static int apply_settings ( void ) { struct settings_applicator *applicator; int rc; /* Call all settings applicators */ for_each_table_entry ( applicator, SETTINGS_APPLICATORS ) { if ( ( rc = applicator->apply() ) != 0 ) { DBG ( "Could not apply settings using applicator " "%p: %s\n", applicator, strerror ( rc ) ); return rc; } } return 0; } /** * Reprioritise settings * * @v settings Settings block * * Reorders the settings block amongst its siblings according to its * priority. */ static void reprioritise_settings ( struct settings *settings ) { struct settings *parent = settings->parent; long priority; struct settings *tmp; long tmp_priority; /* Stop when we reach the top of the tree */ if ( ! parent ) return; /* Read priority, if present */ priority = fetch_intz_setting ( settings, &priority_setting ); /* Remove from siblings list */ list_del ( &settings->siblings ); /* Reinsert after any existing blocks which have a higher priority */ list_for_each_entry ( tmp, &parent->children, siblings ) { tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); if ( priority > tmp_priority ) break; } list_add_tail ( &settings->siblings, &tmp->siblings ); /* Recurse up the tree */ reprioritise_settings ( parent ); } /** * Register settings block * * @v settings Settings block * @v parent Parent settings block, or NULL * @ret rc Return status code */ int register_settings ( struct settings *settings, struct settings *parent ) { struct settings *old_settings; /* NULL parent => add to settings root */ assert ( settings != NULL ); if ( parent == NULL ) parent = &settings_root; /* Remove any existing settings with the same name */ if ( ( old_settings = find_child_settings ( parent, settings->name ) )) unregister_settings ( old_settings ); /* Add to list of settings */ ref_get ( settings->refcnt ); ref_get ( parent->refcnt ); settings->parent = parent; list_add_tail ( &settings->siblings, &parent->children ); DBGC ( settings, "Settings %p (\"%s\") registered\n", settings, settings_name ( settings ) ); /* Fix up settings priority */ reprioritise_settings ( settings ); /* Apply potentially-updated settings */ apply_settings(); return 0; } /** * Unregister settings block * * @v settings Settings block */ void unregister_settings ( struct settings *settings ) { DBGC ( settings, "Settings %p (\"%s\") unregistered\n", settings, settings_name ( settings ) ); /* Remove from list of settings */ ref_put ( settings->refcnt ); ref_put ( settings->parent->refcnt ); settings->parent = NULL; list_del ( &settings->siblings ); /* Apply potentially-updated settings */ apply_settings(); } /****************************************************************************** * * Core settings routines * ****************************************************************************** */ /** * Store value of setting * * @v settings Settings block, or NULL * @v setting Setting to store * @v data Setting data, or NULL to clear setting * @v len Length of setting data * @ret rc Return status code */ int store_setting ( struct settings *settings, struct setting *setting, const void *data, size_t len ) { int rc; /* NULL settings implies storing into the global settings root */ if ( ! settings ) settings = &settings_root; /* Sanity check */ if ( ! settings->op->store ) return -ENOTSUP; /* Store setting */ if ( ( rc = settings->op->store ( settings, setting, data, len ) ) != 0 ) return rc; /* Reprioritise settings if necessary */ if ( setting_cmp ( setting, &priority_setting ) == 0 ) reprioritise_settings ( settings ); /* If these settings are registered, apply potentially-updated * settings */ for ( ; settings ; settings = settings->parent ) { if ( settings == &settings_root ) { if ( ( rc = apply_settings() ) != 0 ) return rc; break; } } return 0; } /** * Fetch value of setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v data Buffer to fill with setting data * @v len Length of buffer * @ret len Length of setting data, or negative error * * The actual length of the setting will be returned even if * the buffer was too small. */ int fetch_setting ( struct settings *settings, struct setting *setting, void *data, size_t len ) { struct settings *child; int ret; /* Avoid returning uninitialised data on error */ memset ( data, 0, len ); /* NULL settings implies starting at the global settings root */ if ( ! settings ) settings = &settings_root; /* Sanity check */ if ( ! settings->op->fetch ) return -ENOTSUP; /* Try this block first */ if ( ( ret = settings->op->fetch ( settings, setting, data, len ) ) >= 0 ) return ret; /* Recurse into each child block in turn */ list_for_each_entry ( child, &settings->children, siblings ) { if ( ( ret = fetch_setting ( child, setting, data, len ) ) >= 0 ) return ret; } return -ENOENT; } /** * Fetch length of setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @ret len Length of setting data, or negative error * * This function can also be used as an existence check for the * setting. */ int fetch_setting_len ( struct settings *settings, struct setting *setting ) { return fetch_setting ( settings, setting, NULL, 0 ); } /** * Fetch value of string setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v data Buffer to fill with setting string data * @v len Length of buffer * @ret len Length of string setting, or negative error * * The resulting string is guaranteed to be correctly NUL-terminated. * The returned length will be the length of the underlying setting * data. */ int fetch_string_setting ( struct settings *settings, struct setting *setting, char *data, size_t len ) { memset ( data, 0, len ); return fetch_setting ( settings, setting, data, ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); } /** * Fetch value of string setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v data Buffer to allocate and fill with setting string data * @ret len Length of string setting, or negative error * * The resulting string is guaranteed to be correctly NUL-terminated. * The returned length will be the length of the underlying setting * data. The caller is responsible for eventually freeing the * allocated buffer. */ int fetch_string_setting_copy ( struct settings *settings, struct setting *setting, char **data ) { int len; int check_len = 0; len = fetch_setting_len ( settings, setting ); if ( len < 0 ) return len; *data = malloc ( len + 1 ); if ( ! *data ) return -ENOMEM; check_len = fetch_string_setting ( settings, setting, *data, ( len + 1 ) ); assert ( check_len == len ); return len; } /** * Fetch value of IPv4 address setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v inp IPv4 address to fill in * @ret len Length of setting, or negative error */ int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, struct in_addr *inp ) { int len; len = fetch_setting ( settings, setting, inp, sizeof ( *inp ) ); if ( len < 0 ) return len; if ( len < ( int ) sizeof ( *inp ) ) return -ERANGE; return len; } /** * Fetch value of signed integer setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v value Integer value to fill in * @ret len Length of setting, or negative error */ int fetch_int_setting ( struct settings *settings, struct setting *setting, long *value ) { union { uint8_t u8[ sizeof ( long ) ]; int8_t s8[ sizeof ( long ) ]; } buf; int len; int i; /* Avoid returning uninitialised data on error */ *value = 0; /* Fetch raw (network-ordered, variable-length) setting */ len = fetch_setting ( settings, setting, &buf, sizeof ( buf ) ); if ( len < 0 ) return len; if ( len > ( int ) sizeof ( buf ) ) return -ERANGE; /* Convert to host-ordered signed long */ *value = ( ( buf.s8[0] >= 0 ) ? 0 : -1L ); for ( i = 0 ; i < len ; i++ ) { *value = ( ( *value << 8 ) | buf.u8[i] ); } return len; } /** * Fetch value of unsigned integer setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v value Integer value to fill in * @ret len Length of setting, or negative error */ int fetch_uint_setting ( struct settings *settings, struct setting *setting, unsigned long *value ) { long svalue; int len; /* Avoid returning uninitialised data on error */ *value = 0; /* Fetch as a signed long */ len = fetch_int_setting ( settings, setting, &svalue ); if ( len < 0 ) return len; /* Mask off sign-extended bits */ assert ( len <= ( int ) sizeof ( long ) ); *value = ( svalue & ( -1UL >> ( 8 * ( sizeof ( long ) - len ) ) ) ); return len; } /** * Fetch value of signed integer setting, or zero * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @ret value Setting value, or zero */ long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ long value; fetch_int_setting ( settings, setting, &value ); return value; } /** * Fetch value of unsigned integer setting, or zero * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @ret value Setting value, or zero */ unsigned long fetch_uintz_setting ( struct settings *settings, struct setting *setting ) { unsigned long value; fetch_uint_setting ( settings, setting, &value ); return value; } /** * Fetch value of UUID setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v uuid UUID to fill in * @ret len Length of setting, or negative error */ int fetch_uuid_setting ( struct settings *settings, struct setting *setting, union uuid *uuid ) { int len; len = fetch_setting ( settings, setting, uuid, sizeof ( *uuid ) ); if ( len < 0 ) return len; if ( len != sizeof ( *uuid ) ) return -ERANGE; return len; } /** * Clear settings block * * @v settings Settings block */ void clear_settings ( struct settings *settings ) { if ( settings->op->clear ) settings->op->clear ( settings ); } /** * Compare two settings * * @v a Setting to compare * @v b Setting to compare * @ret 0 Settings are the same * @ret non-zero Settings are not the same */ int setting_cmp ( struct setting *a, struct setting *b ) { /* If the settings have tags, compare them */ if ( a->tag && ( a->tag == b->tag ) ) return 0; /* Otherwise, if the settings have names, compare them */ if ( a->name && b->name && a->name[0] ) return strcmp ( a->name, b->name ); /* Otherwise, return a non-match */ return ( ! 0 ); } /****************************************************************************** * * Formatted setting routines * ****************************************************************************** */ /** * Store value of typed setting * * @v settings Settings block * @v setting Setting to store * @v type Settings type * @v value Formatted setting data, or NULL * @ret rc Return status code */ int storef_setting ( struct settings *settings, struct setting *setting, const char *value ) { /* NULL value implies deletion. Avoid imposing the burden of * checking for NULL values on each typed setting's storef() * method. */ if ( ! value ) return delete_setting ( settings, setting ); return setting->type->storef ( settings, setting, value ); } /** * Find named setting * * @v name Name * @ret setting Named setting, or NULL */ static struct setting * find_setting ( const char *name ) { struct setting *setting; for_each_table_entry ( setting, SETTINGS ) { if ( strcmp ( name, setting->name ) == 0 ) return setting; } return NULL; } /** * Parse setting name as tag number * * @v name Name * @ret tag Tag number, or 0 if not a valid number */ static unsigned int parse_setting_tag ( const char *name ) { char *tmp = ( ( char * ) name ); unsigned int tag = 0; while ( 1 ) { tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); if ( *tmp == 0 ) return tag; if ( *tmp != '.' ) return 0; tmp++; } } /** * Find setting type * * @v name Name * @ret type Setting type, or NULL */ static struct setting_type * find_setting_type ( const char *name ) { struct setting_type *type; for_each_table_entry ( type, SETTING_TYPES ) { if ( strcmp ( name, type->name ) == 0 ) return type; } return NULL; } /** * Parse setting name * * @v name Name of setting * @v get_child Function to find or create child settings block * @v settings Settings block to fill in * @v setting Setting to fill in * @v tmp_name Buffer for copy of setting name * @ret rc Return status code * * Interprets a name of the form * "[settings_name/]tag_name[:type_name]" and fills in the appropriate * fields. * * The @c tmp_name buffer must be large enough to hold a copy of the * setting name. */ static int parse_setting_name ( const char *name, struct settings * ( * get_child ) ( struct settings *, const char * ), struct settings **settings, struct setting *setting, char *tmp_name ) { char *settings_name; char *setting_name; char *type_name; struct setting *named_setting; /* Set defaults */ *settings = &settings_root; memset ( setting, 0, sizeof ( *setting ) ); setting->name = ""; setting->type = &setting_type_string; /* Split name into "[settings_name/]setting_name[:type_name]" */ strcpy ( tmp_name, name ); if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { *(setting_name++) = 0; settings_name = tmp_name; } else { setting_name = tmp_name; settings_name = NULL; } if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) *(type_name++) = 0; /* Identify settings block, if specified */ if ( settings_name ) { *settings = parse_settings_name ( settings_name, get_child ); if ( *settings == NULL ) { DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", settings_name, name ); return -ENODEV; } } /* Identify setting */ if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) { /* Matches a defined named setting; use that setting */ memcpy ( setting, named_setting, sizeof ( *setting ) ); } else if ( ( setting->tag = parse_setting_tag ( setting_name ) ) !=0){ /* Is a valid numeric tag; use the tag */ setting->tag |= (*settings)->tag_magic; } else { /* Use the arbitrary name */ setting->name = setting_name; } /* Identify setting type, if specified */ if ( type_name ) { setting->type = find_setting_type ( type_name ); if ( setting->type == NULL ) { DBG ( "Invalid setting type \"%s\" in \"%s\"\n", type_name, name ); return -ENOTSUP; } } return 0; } /** * Parse and store value of named setting * * @v name Name of setting * @v value Formatted setting data, or NULL * @ret rc Return status code */ int storef_named_setting ( const char *name, const char *value ) { struct settings *settings; struct setting setting; char tmp_name[ strlen ( name ) + 1 ]; int rc; if ( ( rc = parse_setting_name ( name, autovivify_child_settings, &settings, &setting, tmp_name )) != 0) return rc; return storef_setting ( settings, &setting, value ); } /** * Fetch and format value of named setting * * @v name Name of setting * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ int fetchf_named_setting ( const char *name, char *buf, size_t len ) { struct settings *settings; struct setting setting; char tmp_name[ strlen ( name ) + 1 ]; int rc; if ( ( rc = parse_setting_name ( name, find_child_settings, &settings, &setting, tmp_name )) != 0) return rc; return fetchf_setting ( settings, &setting, buf, len ); } /****************************************************************************** * * Setting types * ****************************************************************************** */ /** * Parse and store value of string setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @ret rc Return status code */ static int storef_string ( struct settings *settings, struct setting *setting, const char *value ) { return store_setting ( settings, setting, value, strlen ( value ) ); } /** * Fetch and format value of string setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_string ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { return fetch_string_setting ( settings, setting, buf, len ); } /** A string setting type */ struct setting_type setting_type_string __setting_type = { .name = "string", .storef = storef_string, .fetchf = fetchf_string, }; /** * Parse and store value of URI-encoded string setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @ret rc Return status code */ static int storef_uristring ( struct settings *settings, struct setting *setting, const char *value ) { char buf[ strlen ( value ) + 1 ]; /* Decoding never expands string */ size_t len; len = uri_decode ( value, buf, sizeof ( buf ) ); return store_setting ( settings, setting, buf, len ); } /** * Fetch and format value of URI-encoded string setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_uristring ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { ssize_t raw_len; /* We need to always retrieve the full raw string to know the * length of the encoded string. */ raw_len = fetch_setting ( settings, setting, NULL, 0 ); if ( raw_len < 0 ) return raw_len; { char raw_buf[ raw_len + 1 ]; fetch_string_setting ( settings, setting, raw_buf, sizeof ( raw_buf ) ); return uri_encode ( raw_buf, buf, len ); } } /** A URI-encoded string setting type */ struct setting_type setting_type_uristring __setting_type = { .name = "uristring", .storef = storef_uristring, .fetchf = fetchf_uristring, }; /** * Parse and store value of IPv4 address setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @ret rc Return status code */ static int storef_ipv4 ( struct settings *settings, struct setting *setting, const char *value ) { struct in_addr ipv4; if ( inet_aton ( value, &ipv4 ) == 0 ) return -EINVAL; return store_setting ( settings, setting, &ipv4, sizeof ( ipv4 ) ); } /** * Fetch and format value of IPv4 address setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_ipv4 ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { struct in_addr ipv4; int raw_len; if ( ( raw_len = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0) return raw_len; return snprintf ( buf, len, "%s", inet_ntoa ( ipv4 ) ); } /** An IPv4 address setting type */ struct setting_type setting_type_ipv4 __setting_type = { .name = "ipv4", .storef = storef_ipv4, .fetchf = fetchf_ipv4, }; /** * Parse and store value of integer setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @v size Integer size, in bytes * @ret rc Return status code */ static int storef_int ( struct settings *settings, struct setting *setting, const char *value, unsigned int size ) { union { uint32_t num; uint8_t bytes[4]; } u; char *endp; u.num = htonl ( strtoul ( value, &endp, 0 ) ); if ( *endp ) return -EINVAL; return store_setting ( settings, setting, &u.bytes[ sizeof ( u ) - size ], size ); } /** * Parse and store value of 8-bit integer setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @v size Integer size, in bytes * @ret rc Return status code */ static int storef_int8 ( struct settings *settings, struct setting *setting, const char *value ) { return storef_int ( settings, setting, value, 1 ); } /** * Parse and store value of 16-bit integer setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @v size Integer size, in bytes * @ret rc Return status code */ static int storef_int16 ( struct settings *settings, struct setting *setting, const char *value ) { return storef_int ( settings, setting, value, 2 ); } /** * Parse and store value of 32-bit integer setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @v size Integer size, in bytes * @ret rc Return status code */ static int storef_int32 ( struct settings *settings, struct setting *setting, const char *value ) { return storef_int ( settings, setting, value, 4 ); } /** * Fetch and format value of signed integer setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_int ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { long value; int rc; if ( ( rc = fetch_int_setting ( settings, setting, &value ) ) < 0 ) return rc; return snprintf ( buf, len, "%ld", value ); } /** * Fetch and format value of unsigned integer setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_uint ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { unsigned long value; int rc; if ( ( rc = fetch_uint_setting ( settings, setting, &value ) ) < 0 ) return rc; return snprintf ( buf, len, "%#lx", value ); } /** A signed 8-bit integer setting type */ struct setting_type setting_type_int8 __setting_type = { .name = "int8", .storef = storef_int8, .fetchf = fetchf_int, }; /** A signed 16-bit integer setting type */ struct setting_type setting_type_int16 __setting_type = { .name = "int16", .storef = storef_int16, .fetchf = fetchf_int, }; /** A signed 32-bit integer setting type */ struct setting_type setting_type_int32 __setting_type = { .name = "int32", .storef = storef_int32, .fetchf = fetchf_int, }; /** An unsigned 8-bit integer setting type */ struct setting_type setting_type_uint8 __setting_type = { .name = "uint8", .storef = storef_int8, .fetchf = fetchf_uint, }; /** An unsigned 16-bit integer setting type */ struct setting_type setting_type_uint16 __setting_type = { .name = "uint16", .storef = storef_int16, .fetchf = fetchf_uint, }; /** An unsigned 32-bit integer setting type */ struct setting_type setting_type_uint32 __setting_type = { .name = "uint32", .storef = storef_int32, .fetchf = fetchf_uint, }; /** * Parse and store value of hex string setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @ret rc Return status code */ static int storef_hex ( struct settings *settings, struct setting *setting, const char *value ) { char *ptr = ( char * ) value; uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */ unsigned int len = 0; while ( 1 ) { bytes[len++] = strtoul ( ptr, &ptr, 16 ); switch ( *ptr ) { case '\0' : return store_setting ( settings, setting, bytes, len ); case ':' : ptr++; break; default : return -EINVAL; } } } /** * Fetch and format value of hex string setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_hex ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { int raw_len; int check_len; int used = 0; int i; raw_len = fetch_setting_len ( settings, setting ); if ( raw_len < 0 ) return raw_len; { uint8_t raw[raw_len]; check_len = fetch_setting ( settings, setting, raw, sizeof ( raw ) ); if ( check_len < 0 ) return check_len; assert ( check_len == raw_len ); if ( len ) buf[0] = 0; /* Ensure that a terminating NUL exists */ for ( i = 0 ; i < raw_len ; i++ ) { used += ssnprintf ( ( buf + used ), ( len - used ), "%s%02x", ( used ? ":" : "" ), raw[i] ); } return used; } } /** A hex-string setting */ struct setting_type setting_type_hex __setting_type = { .name = "hex", .storef = storef_hex, .fetchf = fetchf_hex, }; /** * Parse and store value of UUID setting * * @v settings Settings block * @v setting Setting to store * @v value Formatted setting data * @ret rc Return status code */ static int storef_uuid ( struct settings *settings __unused, struct setting *setting __unused, const char *value __unused ) { return -ENOTSUP; } /** * Fetch and format value of UUID setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ static int fetchf_uuid ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { union uuid uuid; int raw_len; if ( ( raw_len = fetch_uuid_setting ( settings, setting, &uuid ) ) < 0) return raw_len; return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); } /** UUID setting type */ struct setting_type setting_type_uuid __setting_type = { .name = "uuid", .storef = storef_uuid, .fetchf = fetchf_uuid, }; /****************************************************************************** * * Settings * ****************************************************************************** */ /** Hostname setting */ struct setting hostname_setting __setting = { .name = "hostname", .description = "Host name", .tag = DHCP_HOST_NAME, .type = &setting_type_string, }; /** Filename setting */ struct setting filename_setting __setting = { .name = "filename", .description = "Boot filename", .tag = DHCP_BOOTFILE_NAME, .type = &setting_type_string, }; /** Root path setting */ struct setting root_path_setting __setting = { .name = "root-path", .description = "NFS/iSCSI root path", .tag = DHCP_ROOT_PATH, .type = &setting_type_string, }; /** Username setting */ struct setting username_setting __setting = { .name = "username", .description = "User name", .tag = DHCP_EB_USERNAME, .type = &setting_type_string, }; /** Password setting */ struct setting password_setting __setting = { .name = "password", .description = "Password", .tag = DHCP_EB_PASSWORD, .type = &setting_type_string, }; /** Priority setting */ struct setting priority_setting __setting = { .name = "priority", .description = "Priority of these settings", .tag = DHCP_EB_PRIORITY, .type = &setting_type_int8, };