#ifndef _IPXE_SETTINGS_H
#define _IPXE_SETTINGS_H

/** @file
 *
 * Configuration settings
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stdint.h>
#include <ipxe/tables.h>
#include <ipxe/list.h>
#include <ipxe/refcnt.h>

struct settings;
struct in_addr;
struct in6_addr;
union uuid;

/** A setting */
struct setting {
	/** Name
	 *
	 * This is the human-readable name for the setting.
	 */
	const char *name;
	/** Description */
	const char *description;
	/** Setting type
	 *
	 * This identifies the type of setting (e.g. string, IPv4
	 * address, etc.).
	 */
	const struct setting_type *type;
	/** Setting tag, if applicable
	 *
	 * The setting tag is a numerical description of the setting
	 * (such as a DHCP option number, or an SMBIOS structure and
	 * field number).
	 */
	uint64_t tag;
	/** Setting scope (or NULL)
	 *
	 * For historic reasons, a NULL scope with a non-zero tag
	 * indicates a DHCPv4 option setting.
	 */
	const struct settings_scope *scope;
};

/** Configuration setting table */
#define SETTINGS __table ( struct setting, "settings" )

/** Declare a configuration setting */
#define __setting( setting_order, name ) \
	__table_entry ( SETTINGS, setting_order.name )

/** @defgroup setting_order Setting ordering
 * @{
 */

#define SETTING_NETDEV		01 /**< Network device settings */
#define SETTING_NETDEV_EXTRA	02 /**< Network device additional settings */
#define SETTING_IP4		03 /**< IPv4 settings */
#define SETTING_IP4_EXTRA	04 /**< IPv4 additional settings */
#define SETTING_IP6		05 /**< IPv6 settings */
#define SETTING_IP6_EXTRA	06 /**< IPv6 additional settings */
#define SETTING_IP		07 /**< IPv4 settings */
#define SETTING_IP_EXTRA	08 /**< IPv4 additional settings */
#define SETTING_BOOT		09 /**< Generic boot settings */
#define SETTING_BOOT_EXTRA	10 /**< Generic boot additional settings */
#define SETTING_SANBOOT		11 /**< SAN boot settings */
#define SETTING_SANBOOT_EXTRA	12 /**< SAN boot additional settings */
#define SETTING_HOST		13 /**< Host identity settings */
#define SETTING_HOST_EXTRA	14 /**< Host identity additional settings */
#define SETTING_AUTH		15 /**< Authentication settings */
#define SETTING_AUTH_EXTRA	16 /**< Authentication additional settings */
#define SETTING_CRYPTO		17 /**< Cryptography settings */
#define SETTING_MISC		18 /**< Miscellaneous settings */

/** @} */

/** Settings block operations */
struct settings_operations {
	/** Redirect to underlying settings block (if applicable)
	 *
	 * @v settings		Settings block
	 * @ret settings	Underlying settings block
	 */
	struct settings * ( * redirect ) ( struct settings *settings );
	/** Check applicability of setting
	 *
	 * @v settings		Settings block
	 * @v setting		Setting
	 * @ret applies		Setting applies within this settings block
	 */
	int ( * applies ) ( struct settings *settings,
			    const struct setting *setting );
	/** Store value of 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 ( * store ) ( struct settings *settings,
			  const struct setting *setting,
			  const void *data, size_t len );
	/** Fetch value of 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
	 *
	 * The actual length of the setting will be returned even if
	 * the buffer was too small.
	 */
	int ( * fetch ) ( struct settings *settings, struct setting *setting,
			  void *data, size_t len );
	/** Clear settings block
	 *
	 * @v settings		Settings block
	 */
	void ( * clear ) ( struct settings *settings );
};

/** A settings block */
struct settings {
	/** Reference counter */
	struct refcnt *refcnt;
	/** Name */
	const char *name;
	/** Parent settings block */
	struct settings *parent;
	/** Sibling settings blocks */
	struct list_head siblings;
	/** Child settings blocks */
	struct list_head children;
	/** Settings block operations */
	struct settings_operations *op;
	/** Default scope for numerical settings constructed for this block */
	const struct settings_scope *default_scope;
	/** Sibling ordering */
	int order;
};

/**
 * A setting scope
 *
 * Users can construct tags for settings that are not explicitly known
 * to iPXE using the generic syntax for numerical settings.  For
 * example, the setting name "60" will be interpreted as referring to
 * DHCP option 60 (the vendor class identifier).
 *
 * This creates a potential for namespace collisions, since the
 * interpretation of the numerical description will vary according to
 * the settings block.  When a user attempts to fetch a generic
 * numerical setting, we need to ensure that only the intended
 * settings blocks interpret this numerical description.  (For
 * example, we do not want to attempt to retrieve the subnet mask from
 * SMBIOS, or the system UUID from DHCP.)
 *
 * This potential problem is resolved by including a user-invisible
 * "scope" within the definition of each setting.  Settings blocks may
 * use this to determine whether or not the setting is applicable.
 * Any settings constructed from a numerical description
 * (e.g. "smbios/1.4.0") will be assigned the default scope of the
 * settings block specified in the description (e.g. "smbios"); this
 * provides behaviour matching the user's expectations in most
 * circumstances.
 */
struct settings_scope {
	/** Dummy field
	 *
	 * This is included only to ensure that pointers to different
	 * scopes always compare differently.
	 */
	uint8_t dummy;
} __attribute__ (( packed ));

/**
 * A setting type
 *
 * This represents a type of setting (e.g. string, IPv4 address,
 * etc.).
 */
struct setting_type {
	/** Name
	 *
	 * This is the name exposed to the user (e.g. "string").
	 */
	const char *name;
	/** 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 ( * parse ) ( const struct setting_type *type, const char *value,
			  void *buf, size_t len );
	/** 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 ( * format ) ( const struct setting_type *type, const void *raw,
			   size_t raw_len, char *buf, size_t len );
	/** 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 ( * denumerate ) ( const struct setting_type *type,
			       unsigned long value,
			       void *buf, size_t len );
	/** 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
	 */
	int ( * numerate ) ( const struct setting_type *type, const void *raw,
			     size_t raw_len, unsigned long *value );
};

/** Configuration setting type table */
#define SETTING_TYPES __table ( struct setting_type, "setting_types" )

/** Declare a configuration setting type */
#define __setting_type __table_entry ( SETTING_TYPES, 01 )

/**
 * A settings applicator
 *
 */
struct settings_applicator {
	/** Apply updated settings
	 *
	 * @ret rc		Return status code
	 */
	int ( * apply ) ( void );
};

/** Settings applicator table */
#define SETTINGS_APPLICATORS \
	__table ( struct settings_applicator, "settings_applicators" )

/** Declare a settings applicator */
#define __settings_applicator __table_entry ( SETTINGS_APPLICATORS, 01 )

/** A built-in setting */
struct builtin_setting {
	/** Setting */
	const struct setting *setting;
	/** Fetch setting value
	 *
	 * @v data		Buffer to fill with setting data
	 * @v len		Length of buffer
	 * @ret len		Length of setting data, or negative error
	 */
	int ( * fetch ) ( void *data, size_t len );
};

/** Built-in settings table */
#define BUILTIN_SETTINGS __table ( struct builtin_setting, "builtin_settings" )

/** Declare a built-in setting */
#define __builtin_setting __table_entry ( BUILTIN_SETTINGS, 01 )

/** Built-in setting scope */
extern const struct settings_scope builtin_scope;

/** IPv6 setting scope */
extern const struct settings_scope ipv6_settings_scope;

/** DHCPv6 setting scope */
extern const struct settings_scope dhcpv6_scope;

/**
 * A generic settings block
 *
 */
struct generic_settings {
	/** Settings block */
	struct settings settings;
	/** List of generic settings */
	struct list_head list;
};

/** A child settings block locator function */
typedef struct settings * ( *get_child_settings_t ) ( struct settings *settings,
						      const char *name );
extern struct settings_operations generic_settings_operations;
extern int generic_settings_store ( struct settings *settings,
				    const struct setting *setting,
				    const void *data, size_t len );
extern int generic_settings_fetch ( struct settings *settings,
				    struct setting *setting,
				    void *data, size_t len );
extern void generic_settings_clear ( struct settings *settings );

extern int register_settings ( struct settings *settings,
			       struct settings *parent, const char *name );
extern void unregister_settings ( struct settings *settings );

extern struct settings * settings_target ( struct settings *settings );
extern int setting_applies ( struct settings *settings,
			     const struct setting *setting );
extern int store_setting ( struct settings *settings,
			   const struct setting *setting,
			   const void *data, size_t len );
extern int fetch_setting ( struct settings *settings,
			   const struct setting *setting,
			   struct settings **origin, struct setting *fetched,
			   void *data, size_t len );
extern int fetch_setting_copy ( struct settings *settings,
				const struct setting *setting,
				struct settings **origin,
				struct setting *fetched, void **data );
extern int fetch_raw_setting ( struct settings *settings,
			       const struct setting *setting,
			       void *data, size_t len );
extern int fetch_raw_setting_copy ( struct settings *settings,
				    const struct setting *setting,
				    void **data );
extern int fetch_string_setting ( struct settings *settings,
				  const struct setting *setting,
				  char *data, size_t len );
extern int fetch_string_setting_copy ( struct settings *settings,
				       const struct setting *setting,
				       char **data );
extern int fetch_ipv4_array_setting ( struct settings *settings,
				      const struct setting *setting,
				      struct in_addr *inp, unsigned int count );
extern int fetch_ipv4_setting ( struct settings *settings,
				const struct setting *setting,
				struct in_addr *inp );
extern int fetch_ipv6_array_setting ( struct settings *settings,
				      const struct setting *setting,
				      struct in6_addr *inp, unsigned int count);
extern int fetch_ipv6_setting ( struct settings *settings,
				const struct setting *setting,
				struct in6_addr *inp );
extern int fetch_int_setting ( struct settings *settings,
			       const struct setting *setting, long *value );
extern int fetch_uint_setting ( struct settings *settings,
				const struct setting *setting,
				unsigned long *value );
extern long fetch_intz_setting ( struct settings *settings,
				 const struct setting *setting );
extern unsigned long fetch_uintz_setting ( struct settings *settings,
					   const struct setting *setting );
extern int fetch_uuid_setting ( struct settings *settings,
				const struct setting *setting,
				union uuid *uuid );
extern void clear_settings ( struct settings *settings );
extern int setting_cmp ( const struct setting *a, const struct setting *b );

extern struct settings * find_child_settings ( struct settings *parent,
					       const char *name );
extern struct settings * autovivify_child_settings ( struct settings *parent,
						     const char *name );
extern const char * settings_name ( struct settings *settings );
extern struct settings * find_settings ( const char *name );
extern struct setting * find_setting ( const char *name );
extern int parse_setting_name ( char *name, get_child_settings_t get_child,
				struct settings **settings,
				struct setting *setting );
extern int setting_name ( struct settings *settings,
			  const struct setting *setting,
			  char *buf, size_t len );
extern int setting_format ( const struct setting_type *type, const void *raw,
			    size_t raw_len, char *buf, size_t len );
extern int setting_parse ( const struct setting_type *type, const char *value,
			   void *buf, size_t len );
extern int setting_numerate ( const struct setting_type *type, const void *raw,
			      size_t raw_len, unsigned long *value );
extern int setting_denumerate ( const struct setting_type *type,
				unsigned long value, void *buf, size_t len );
extern int fetchf_setting ( struct settings *settings,
			    const struct setting *setting,
			    struct settings **origin, struct setting *fetched,
			    char *buf, size_t len );
extern int fetchf_setting_copy ( struct settings *settings,
				 const struct setting *setting,
				 struct settings **origin,
				 struct setting *fetched, char **value );
extern int storef_setting ( struct settings *settings,
			    const struct setting *setting, const char *value );
extern int fetchn_setting ( struct settings *settings,
			    const struct setting *setting,
			    struct settings **origin, struct setting *fetched,
			    unsigned long *value );
extern int storen_setting ( struct settings *settings,
			    const struct setting *setting,
			    unsigned long value );
extern char * expand_settings ( const char *string );

extern const struct setting_type setting_type_string __setting_type;
extern const struct setting_type setting_type_uristring __setting_type;
extern const struct setting_type setting_type_ipv4 __setting_type;
extern const struct setting_type setting_type_ipv6 __setting_type;
extern const struct setting_type setting_type_int8 __setting_type;
extern const struct setting_type setting_type_int16 __setting_type;
extern const struct setting_type setting_type_int32 __setting_type;
extern const struct setting_type setting_type_uint8 __setting_type;
extern const struct setting_type setting_type_uint16 __setting_type;
extern const struct setting_type setting_type_uint32 __setting_type;
extern const struct setting_type setting_type_hex __setting_type;
extern const struct setting_type setting_type_hexhyp __setting_type;
extern const struct setting_type setting_type_hexraw __setting_type;
extern const struct setting_type setting_type_base64 __setting_type;
extern const struct setting_type setting_type_uuid __setting_type;
extern const struct setting_type setting_type_busdevfn __setting_type;
extern const struct setting_type setting_type_dnssl __setting_type;

extern const struct setting
ip_setting __setting ( SETTING_IP4, ip );
extern const struct setting
netmask_setting __setting ( SETTING_IP4, netmask );
extern const struct setting
gateway_setting __setting ( SETTING_IP4, gateway );
extern const struct setting
dns_setting __setting ( SETTING_IP4_EXTRA, dns );
extern const struct setting
ip6_setting __setting ( SETTING_IP6, ip6 );
extern const struct setting
len6_setting __setting ( SETTING_IP6, len6 );
extern const struct setting
gateway6_setting __setting ( SETTING_IP6, gateway6 );
extern const struct setting
hostname_setting __setting ( SETTING_HOST, hostname );
extern const struct setting
domain_setting __setting ( SETTING_IP_EXTRA, domain );
extern const struct setting
filename_setting __setting ( SETTING_BOOT, filename );
extern const struct setting
root_path_setting __setting ( SETTING_SANBOOT, root-path );
extern const struct setting
san_filename_setting __setting ( SETTING_SANBOOT, san-filename );
extern const struct setting
username_setting __setting ( SETTING_AUTH, username );
extern const struct setting
password_setting __setting ( SETTING_AUTH, password );
extern const struct setting
priority_setting __setting ( SETTING_MISC, priority );
extern const struct setting
uuid_setting __setting ( SETTING_HOST, uuid );
extern const struct setting
next_server_setting __setting ( SETTING_BOOT, next-server );
extern const struct setting
mac_setting __setting ( SETTING_NETDEV, mac );
extern const struct setting
busid_setting __setting ( SETTING_NETDEV, busid );
extern const struct setting
user_class_setting __setting ( SETTING_HOST_EXTRA, user-class );
extern const struct setting
vendor_class_setting __setting ( SETTING_HOST_EXTRA, vendor-class );
extern const struct setting
manufacturer_setting __setting ( SETTING_HOST_EXTRA, manufacturer );
extern const struct setting
product_setting __setting ( SETTING_HOST_EXTRA, product );
extern const struct setting
serial_setting __setting ( SETTING_HOST_EXTRA, serial );
extern const struct setting
asset_setting __setting ( SETTING_HOST_EXTRA, asset );
extern const struct setting
board_serial_setting __setting ( SETTING_HOST_EXTRA, board-serial );
extern const struct setting dhcp_server_setting __setting ( SETTING_MISC,
							    dhcp-server );

/**
 * Initialise a settings block
 *
 * @v settings		Settings block
 * @v op		Settings block operations
 * @v refcnt		Containing object reference counter, or NULL
 * @v default_scope	Default scope
 */
static inline void settings_init ( struct settings *settings,
				   struct settings_operations *op,
				   struct refcnt *refcnt,
				   const struct settings_scope *default_scope ){
	INIT_LIST_HEAD ( &settings->siblings );
	INIT_LIST_HEAD ( &settings->children );
	settings->op = op;
	settings->refcnt = refcnt;
	settings->default_scope = default_scope;
}

/**
 * Initialise a settings block
 *
 * @v generics		Generic settings block
 * @v refcnt		Containing object reference counter, or NULL
 */
static inline void generic_settings_init ( struct generic_settings *generics,
					   struct refcnt *refcnt ) {
	settings_init ( &generics->settings, &generic_settings_operations,
			refcnt, NULL );
	INIT_LIST_HEAD ( &generics->list );
}

/**
 * Delete setting
 *
 * @v settings		Settings block
 * @v setting		Setting to delete
 * @ret rc		Return status code
 */
static inline int delete_setting ( struct settings *settings,
				   const struct setting *setting ) {
	return store_setting ( settings, setting, NULL, 0 );
}

/**
 * Check existence of predefined setting
 *
 * @v settings		Settings block, or NULL to search all blocks
 * @v setting		Setting to fetch
 * @ret exists		Setting exists
 */
static inline int setting_exists ( struct settings *settings,
				   const struct setting *setting ) {
	return ( fetch_setting ( settings, setting, NULL, NULL,
				 NULL, 0 ) >= 0 );
}

#endif /* _IPXE_SETTINGS_H */
