5. Network Auto-Magic Data Structures & APIs

Version 2.6, 2008-Sep-29

It is the job of libnwam to store, retrieve and operate on configuration preferences for network (auto)configuration. It is important to stress that it is not libnwam's job to actually apply changes to the system, just to store and retrieve them, and facilitate communication with the NWAM daemon (nwamd). To actually effect changes to datalinks and IP interfaces, or to query their current state (as opposed to their configuration preferences), libdladm and libinetcfg will be used.

5.1 Common Definitions, Data Structures and Functions

Global flags used for functions that access persistent storage:

#define NWAM_FLAG_BLOCKING	0x00000001
#define NWAM_FLAG_CREATE	0x00000002
#define NWAM_FLAG_DO_NOT_FREE	0x00000004

Most libnwam functions return an nwam_error_t; the type is defined as follows.

typedef enum {
        NWAM_SUCCESS,                   /* No error occurred */
        NWAM_LIST_END,                  /* End of list reached */
        NWAM_INVALID_HANDLE,            /* Entity handle is invalid */
        NWAM_HANDLE_UNBOUND,            /* Handle not bound to entity */
        NWAM_INVALID_ARG,               /* Argument is invalid */
        NWAM_PERMISSION_DENIED,         /* Insufficient privileges for action */
        NWAM_NO_MEMORY,                 /* Out of memory */
        NWAM_ENTITY_EXISTS,             /* Entity already exists */
        NWAM_ENTITY_IN_USE,             /* Entity in use */
        NWAM_ENTITY_COMMITTED,          /* Entity already committed */
        NWAM_ENTITY_NOT_FOUND,          /* Entity not found */
        NWAM_ENTITY_TYPE_MISMATCH,      /* Entity type mismatch */
        NWAM_ENTITY_INVALID,            /* Validation of entity failed */
        NWAM_ENTITY_INVALID_MEMBER,     /* Entity member invalid */
        NWAM_ENTITY_INVALID_VALUE,      /* Validation of entity value failed */
        NWAM_ENTITY_MISSING_MEMBER,     /* Required member is missing */
        NWAM_ENTITY_NO_VALUE,           /* No value associated with entity */
        NWAM_ENTITY_MULTIPLE_VALUES,    /* Multiple values for entity */
        NWAM_WALK_HALTED,               /* Callback function returned nonzero */
        NWAM_ERROR_INTERNAL             /* Internal error */
} nwam_error_t;

/* convert an error code to a localized string */
const char *nwam_strerror(nwam_error_t);

NWAM property values are stored in nwam_value_t structures. The following data structures and functions are used to manage these objects.

/* property data types */
typedef enum {
        NWAM_DATA_TYPE_BOOLEAN,
        NWAM_DATA_TYPE_INT64,
        NWAM_DATA_TYPE_STRING,
        NWAM_DATA_TYPE_UNKNOWN
} nwam_data_type_t;

/* generic property value holder */
struct nwam_value;
typedef struct nwam_value *nwam_value_t;

/* functions that manipulate property values */
nwam_error_t nwam_value_create_boolean(boolean_t, nwam_value_t *);
nwam_error_t nwam_value_create_boolean_array(boolean_t *, uint_t, nwam_value_t *);
nwam_error_t nwam_value_create_int64(int64_t, nwam_value_t *);
nwam_error_t nwam_value_create_int64_array(int64_t *, uint_t, nwam_value_t *);
nwam_error_t nwam_value_create_string(char *, nwam_value_t *);
nwam_error_t nwam_value_create_string_array(char **, uint_t, nwam_value_t *);

nwam_error_t nwam_value_get_type(nwam_value_t, nwam_value_type_t *);
nwam_error_t nwam_value_get_numvalues(nwam_value_t, uint_t *);

nwam_error_t nwam_value_get_boolean(nwam_value_t, boolean_t *);
nwam_error_t nwam_value_get_boolean_array(nwam_value_t, boolean_t **, uint_t *);
nwam_error_t nwam_value_get_int64(nwam_value_t, int64_t *);
nwam_error_t nwam_value_get_int64_array(nwam_value_t, int64_t **, uint_t *);
nwam_error_t nwam_value_get_string(nwam_value_t, char **);
nwam_error_t nwam_value_get_string_array(nwam_value_t, char ***, uint_t *);
nwam_error_t nwam_int64_get_value_string(const char *, int64_t, const char **);
nwam_error_t nwam_value_string_get_int64(const char *, const char *, int64_t *);

nwam_error_t nwam_value_copy(nwam_value_t, nwam_value_t *);

void nwam_value_free(nwam_value_t);

NCUs, Locations, and ENMs all have activation_mode properties; though the allowed values for each object type are different, this enumeration includes all possible values and is used by all three objects. The validation function for each object will verify that the value being used is one of the allowed values for that object.

typedef enum {
	NWAM_ACTIVATION_MODE_ALWAYS,
	NWAM_ACTIVATION_MODE_NEVER,
	NWAM_ACTIVATION_MODE_MANUAL,
	NWAM_ACTIVATION_MODE_PRIORITIZED,
	NWAM_ACTIVATION_MODE_CONDITIONAL
} nwam_activation_mode_t;

#define NWAM_ACTIVATION_MODE_ALWAYS_STRING		"always"
#define NWAM_ACTIVATION_MODE_NEVER_STRING		"never"
#define NWAM_ACTIVATION_MODE_MANUAL_STRING		"manual"
#define NWAM_ACTIVATION_MODE_PRIORITIZED_STRING		"prioritized"
#define NWAM_ACTIVATION_MODE_CONDITIONAL_STRING		"conditional"

Miscellaneous global definitions:

#define NWAM_MAX_NAME_LEN		256
#define NWAM_MAX_VALUE_LEN		1024
#define NWAM_MAX_FMRI_LEN		256
#define NWAM_MAX_NUM_VALUES		64

5.2 NCPs and NCUs

There are currently only two possible NCPs, the automatic NCP, made up of automatically created NCUs associated with the hardware discovered in the system, and the user NCP, made up of the user's configured NCU settings. These NCPs have hardwired names which can used to obtain an opaque handle, which is linked to an in-memory representation of the NCP.

#define NWAM_NCP_NAME_AUTOMATIC		"automatic"
#define NWAM_NCP_NAME_USER		"user"

typedef struct nwam_ncp_handle *nwam_ncp_handle_t;

Handles may be obtained either by looking up a specific name, or by walking all NCPs; the walk function will call the user-specified callback once for each NCP, passing in the NCP handle. When finished with the handle, the local memory should be freed.

Note that create and destroy functions are defined for NCPs; however, the use of these beyond the internal nwam implementation is not supported. In the future, when nwam supports multiple NCPs, these functions will be available to external entities as well.

nwam_error_t nwam_ncp_load(const char *name, uint64_t flags, nwam_ncp_handle_t *ncpp);

nwam_error_t nwam_walk_ncps(int (*cb)(nwam_ncp_handle_t ncp, void *arg),
	void *arg, uint64_t flags, int *cb_return);

nwam_error_t nwam_ncp_create(const char *name, uint64_t flags, nwam_ncp_handle_t *ncpp);

nwam_error_t nwam_ncp_destroy(nwam_ncp_handle_t ncp, uint64_t flags);

void nwam_ncp_free(nwam_ncp_handle_t ncp);

Given an NCP handle, information may be obtained about, and actions may be taken on, the NCP.

nwam_error_t nwam_ncp_get_name(nwam_ncp_handle_t ncp, char **name);

nwam_error_t nwam_ncp_activate(nwam_ncp_handle_t ncp);

nwam_error_t nwam_ncp_deactivate(nwam_ncp_handle_t ncp);

As with NCPs, a local, in-memory representation of an NCU is associated with a handle:

struct nwam_ncu_handle;
typedef struct nwam_ncu_handle *nwam_ncu_handle_t;

The first step in operating on an NCU is therefore to load the NCU into memory (or allocate memory for a new NCU) and obtain its handle. There are several ways to do this:

In addition to the global flag values defined in section 5.1, the flags parameter to nwam_ncp_walk_ncus() may also include any of the following filtering values, which control the type and class of NCUs that are returned from the walk. If no filtering flags are specified, or if the _ALL flag is specified, all NCUs will be returned.

Note about the flags parameter: this 64-bit parameter can include both the global flags (defined in section 5.1) and walk-specific flags such as the following list. The global flags defined in section 5.1, and any others that may be added in the future, will occupy the lower 32 bits of the flags parameter, and will be interpreted in the same way by all libnwam functions with a flags parameter. These walking-related flags will occupy the upper 32 bits, and will be interpreted only in the appropriate walkers; i.e. this set is unique only in the nwam_ncp_walk_ncus() function, while a different set will be defined in the same bit-space for the nwam_walk_locs() function.

#define NWAM_FLAG_NCU_TYPE_LINK		0x00000001 << 32
#define NWAM_FLAG_NCU_TYPE_IP		0x00000002 << 32
#define NWAM_FLAG_NCU_TYPE_ALL		(NWAM_FLAG_NCU_TYPE_LINK | \
					NWAM_FLAG_NCU_TYPE_IP)

#define NWAM_FLAG_NCU_CLASS_PHYS	0x00000100 << 32
#define NWAM_FLAG_NCU_CLASS_IPTUN	0x00000200 << 32
#define NWAM_FLAG_NCU_CLASS_IP		0x00010000 << 32
#define NWAM_FLAG_NCU_CLASS_ALL_LINK	(NWAM_FLAG_NCU_CLASS_PHYS | \
					NWAM_FLAG_NCU_CLASS_IPTUN)
#define NWAM_FLAG_NCU_CLASS_ALL_IP	NWAM_FLAG_NCU_CLASS_IP
#define NWAM_FLAG_NCU_CLASS_ALL		(NWAM_FLAG_NCU_CLASS_ALL_LINK | \
					NWAM_FLAG_NCU_CLASS_ALL_IP)
#define NWAM_FLAG_NCU_TYPE_CLASS_ALL	(NWAM_FLAG_NCU_TYPE_ALL | \
					NWAM_FLAG_NCU_CLASS_ALL)

Once an NCU handle has been obtained, various operations are allowed on the NCU.

The following functions may be used to manipulate NCU properties.

The following structures and definitions are used to identify NCU properties. Each property has a string name which maps to an identifier of the form NWAM_NCU_PROP_<foo>. Additionally, for each property whose values are enumerated, there is an nwam_ncu_<foo>_t structure, and additional NWAM_NCU_<FOO>_<BAR>_STRING defines for each possible value.

Individual properties as well as complete NCUs may be validated. When an NCU is validated, the first property with an incorrect value is returned in the propname parameter.

nwam_error_t nwam_ncu_validate_prop(nwam_ncu_handle_t ncu, const char *propname,
	nwam_value_t propval);

nwam_error_t nwam_ncu_validate(nwam_ncu_handle_t ncu, const char **invalid_prop);

When finished with the NCU, the following clean-up options are available.

5.3 Locations

As with NCPs and NCUs, locations are represented by an opaque handle:

struct nwam_loc_handle;
typedef struct nwam_loc_handle *nwam_loc_handle_t;

Locations are also manipulated in much the same way as NCUs. You must begin by loading the location into memory (or allocating memory for a new location) and obtaining its handle. There are several ways to do this:

In addition to the global flag values defined in section 5.1, the flags parameter to nwam_walk_locs() may also include any of the following filtering values, which limit the locations returned based on the activation mode property.

#define NWAM_FLAG_ACTIVATION_MODE_ALWAYS	0x00000001 << 32
#define NWAM_FLAG_ACTIVATION_MODE_NEVER		0x00000002 << 32
#define NWAM_FLAG_ACTIVATION_MODE_MANUAL	0x00000004 << 32
#define NWAM_FLAG_ACTIVATION_MODE_PRIORITIZED	0x00000008 << 32
#define NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL	0x00000010 << 32
#define NWAM_FLAG_ACTIVATION_MODE_ALL  (NWAM_FLAG_ACTIVATION_MODE_ALWAYS | \
				       NWAM_FLAG_ACTIVATION_MODE_NEVER | \
				       NWAM_FLAG_ACTIVATION_MODE_MANUAL | \
				       NWAM_FLAG_ACTIVATION_MODE_PRIORITIZED | \
				       NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL)

Once a location handle has been obtained, various operations are allowed on the location.

The following functions may be used to manipulate location properties.

The following structures and definitions are used to identify location properties. Each property has a string name which maps to an identifier of the form NWAM_LOC_PROP_<foo>. Additionally, for each property whose values are enumerated, there is an nwam_loc_<foo>_t structure, and additional NWAM_LOC_<FOO>_<BAR>_STRING defines for each possible value.

#define NWAM_LOC_PROP_ACTIVATION_MODE		"activation-mode"
#define NWAM_LOC_PROP_CONDITION			"condition"

/* Nameservice location properties */
#define NWAM_LOC_PROP_NAMESERVICE_DISCOVER	"nameservice-discover"
#define NWAM_LOC_PROP_NAMESERVICES		"nameservices"

typedef enum {
	NWAM_NAMESERVICES_DNS,
	NWAM_NAMESERVICES_FILES,
	NWAM_NAMESERVICES_NIS,
	NWAM_NAMESERVICES_NISPLUS,
	NWAM_NAMESERVICES_LDAP
} nwam_nameservices_t;

#define NWAM_NAMESERVICES_DNS_STRING		"dns"
#define NWAM_NAMESERVICES_FILES_STRING		"files"
#define NWAM_NAMESERVICES_NIS_STRING		"nis"
#define NWAM_NAMESERVICES_NISPLUS_STRING	"nisplus"
#define NWAM_NAMESERVICES_LDAP_STRING		"ldap"

#define NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE	"nameservices-config-file"
#define NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS	"dns-nameservice-servers"
#define NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN	"dns-nameservice-domain"
#define NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH	"dns-nameservice-search"
#define NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS	"nis-nameservice-servers"
#define NWAM_LOC_PROP_NIS_NAMESERVICE_DOMAIN	"nis-nameservice-domain"
#define NWAM_LOC_PROP_NISPLUS_NAMESERVICE_SERVERS	\
						"nisplus-nameservice-servers"
#define NWAM_LOC_PROP_NISPLUS_NAMESERVICE_DOMAIN	\
						"nisplus-nameservice-domain"
#define NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS	"ldap-nameservice-servers"
#define NWAM_LOC_PROP_LDAP_NAMESERVICE_DOMAIN	"ldap-nameservice-domain"

/* Path to hosts/ipnodes database */
#define NWAM_LOC_PROP_HOSTS_FILE		"hosts-file"

/* NFSv4 domain */
#define NWAM_LOC_PROP_NFSV4_DOMAIN		"nfsv4-domain"

/* IPFilter configuration */
#define NWAM_LOC_PROP_IPFILTER_CONFIG_FILE	"ipfilter-config-file"
#define NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE	"ipfilter-v6-config-file"
#define NWAM_LOC_PROP_IPNAT_CONFIG_FILE		"ipnat-config-file"
#define NWAM_LOC_PROP_IPPOOL_CONFIG_FILE	"ippool-config-file"

/* IPsec configuration */
#define NWAM_LOC_PROP_IKE_CONFIG_FILE		"ike-config-file"
#define NWAM_LOC_PROP_IPSECKEY_CONFIG_FILE	"ipseckey-config-file"
#define NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE	"ipsecpolicy-config-file"

/* List of SMF services to enable/disable */
#define NWAM_LOC_PROP_SVCS_ENABLE		"svcs-enable"
#define NWAM_LOC_PROP_SVCS_DISABLE		"svcs-disable"

Individual property settings as well as complete locations may be validated. When a location is validated, the first property with an incorrect value is returned in the propname parameter.

nwam_error_t nwam_loc_validate_prop(nwam_loc_handle_t loc, const char *propname,
	nwam_value_t propval);

nwam_error_t nwam_loc_validate(nwam_loc_handle_t loc, const char **invalid_prop);

When finished with the location, the following clean-up options are available.

In addition to the "hardwired" properties defined above, SMF services may identify properties which should be included in an NWAM location. These "extensible" properties may be retrieved from the repository using a template mechanism.

These functions are not specific to a location; after finding out about all the existing templates, how do you associate values for the template properties with a particular location?

A service wishing to include properties in NWAM locations creates a property group which contains descriptive information. That information is available to consumers of libnwam via the nwam_loc_prop_template_t structure:

struct nwam_loc_prop_template;
typedef struct nwam_loc_prop_template *nwam_loc_prop_template_t;

A location property template includes a set of descriptive properties:


#define NWAM_LOC_TEMPLATE_PROP_LOC	"nwam_loc_property"
#define NWAM_LOC_TEMPLATE_PROP_GROUP	"nwam_loc_property_group"

#define NWAM_LOC_TEMPLATE_PROP_NAME	"nwam_loc_property_name"
#define NWAM_LOC_TEMPLATE_PROP_DESC	"nwam_loc_property_description"
#define NWAM_LOC_TEMPLATE_PROP_DEFAULT	"nwam_loc_property_default"

A set of functions is provided to access information about existing template properties.

5.4 External Network Modifiers (ENMs)

As with the preceding object types, ENMs are represented by an opaque handle:

struct nwam_enm_handle;
typedef struct nwam_enm_handle *nwam_enm_handle_t;

ENMs are also manipulated in much the same way as the other object types. You must begin by loading an ENM into memory (or allocating memory for a new ENM) and obtaining its handle. There are several ways to do this:

In addition to the global flag values defined in section 5.1, the flags parameter to nwam_walk_enms() may also include any of the following filtering values, which limit the ENMs returned based on state.

#define NWAM_FLAG_ENM_STATE_CREATED	0x00000001 << 32
#define NWAM_FLAG_ENM_STATE_ENABLED	0x00000002 << 32
#define NWAM_FLAG_ENM_STATE_DISABLED	0x00000004 << 32
#define NWAM_FLAG_ENM_STATE_MAINT	0x00000008 << 32
#define NWAM_FLAG_ENM_STATE_ALL		(NWAM_FLAG_ENM_STATE_CREATED | \
					NWAM_FLAG_ENM_STATE_ENABLED | \
					NWAM_FLAG_ENM_STATE_DISABLED | \
					NWAM_FLAG_ENM_STATE_MAINT)

Once an ENM handle has been obtained, various operations are allowed on the ENM.

The following functions may be used to manipulate ENM properties.

The following structures and definitions are used to identify ENM properties. Each property has a string name which maps to an identifier of the form NWAM_ENM_PROP_<foo>. Additionally, for each property whose values are enumerated, there is an nwam_enm_<foo>_t structure, and additional NWAM_ENM_<FOO>_<BAR>_STRING defines for each possible value.

#define	NWAM_ENM_PROP_ACTIVATION_MODE	"activation-mode"
#define	NWAM_ENM_PROP_CONDITION		"condition"
#define	NWAM_ENM_PROP_STATE		"state"

typedef enum {
	NWAM_ENM_STATE_CREATED,
	NWAM_ENM_STATE_ENABLED,
	NWAM_ENM_STATE_DISABLED,
	NWAM_ENM_STATE_MAINT
} nwam_enm_state_t;

#define	NWAM_ENM_STATE_CREATED_STRING	"created"
#define	NWAM_ENM_STATE_ENABLED_STRING	"enabled"
#define	NWAM_ENM_STATE_DISABLED_STRING	"disabled"
#define	NWAM_ENM_STATE_MAINT_STRING	"maintenance"

/* FMRI associated with the ENM */
#define	NWAM_ENM_PROP_FMRI		"fmri"

/* Start/Stop scripts associated with the ENM */
#define	NWAM_ENM_PROP_START		"start"
#define	NWAM_ENM_PROP_STOP		"stop"

Note that the FMRI and START/STOP properties of an ENM are mutually exclusive; if the FMRI property is defined, that service's start and stop methods will be used to start and stop the ENM; if the FMRI property is not defined, the START and STOP properties must be defined, and will be used to start and stop the ENM.

Individual property settings as well as complete ENMs may be validated. When an ENM is validated, the first property with an invalid value is returned in the propname parameter.

nwam_error_t nwam_enm_validate_prop(nwam_enm_handle_t enm, const char *propname,
	nwam_value_t propval);

nwam_error_t nwam_enm_validate(nwam_enm_handle_t enm, const char **invalid_prop);

When finished with the ENM, the following clean-up options are available.

5.5 Event Notifications

    #define NWAM_EVENTS_NOOP                0
    #define NWAM_EVENTS_SOURCE_DEAD         1
    #define NWAM_EVENTS_SOURCE_BACK         2
    #define NWAM_EVENTS_NO_MAGIC            3
    #define NWAM_EVENTS_INFO                4
    #define NWAM_EVENTS_IF_STATE            5
    #define NWAM_EVENTS_IF_REMOVED          6
    #define NWAM_EVENTS_LINK_STATE          7
    #define NWAM_EVENTS_LINK_REMOVED        8
    #define NWAM_EVENTS_SCAN_REPORT         9

    #define NWAM_EVENTS_STATUS_OK           0
    #define NWAM_EVENTS_NOT_HANDLED         1

    #define NWAM_NETWORK_OBJECT_UNDEFINED   0
    #define NWAM_NETWORK_OBJECT_LINK        1
    #define NWAM_NETWORK_OBJECT_INTERFACE   2

    #define NWAM_REQ_UNDEFINED              0
    #define NWAM_REQ_WLAN                   1
    #define NWAM_REQ_KEY                    2

    typedef struct {
        char essid[DLADM_STRSIZE];
        char bssid[DLADM_STRSIZE];
        char signal_strength[DLADM_STRSIZE];
        uint32_t security_mode; /* a dladm_wlan_secmode_t */
    } nwam_events_wlan_t;

    typedef struct nwam_events_msg nwam_events_msg_t;
    struct nwam_events_msg {
        nwam_events_msg_t *next;
        int32_t type;
        int32_t size;

        union {
            struct {
                int32_t obj_type;       /* NWAM_NETWORK* */
                char name[NWAM_NAMESIZE];
                int32_t req_type;
            } no_magic;

            struct {
                char message[80]; /* can be longer, must allocate structure */
            } info;

            struct {
                /* assumed NWAM_NETWORK_OBJECT_IF */
                char name[NWAM_NAMESIZE];
                uint32_t flags;
                uint32_t index;
                uint32_t addr_valid; /* boolean */
                struct sockaddr addr;
                /* might be longer then sizeof(if_state) for addr */
            } if_state;

            struct {
                /* assumed NWAM_NETWORK_OBJECT_LINK */
                char name[NWAM_NAMESIZE];
                int32_t link_state; /* link_state_t from sys/mac.h */
            } link_state;

            struct {
                /* object referred to by message type has been removed */
                char name[NWAM_NAMESIZE];
            } removed;

            struct {
                /* assumed NWAM_NETWORK_OBJECT_LINK */
                char name[NWAM_NAMESIZE];
                uint16_t num_wlans;
                nwam_events_wlan_t wlans[1];
                /* space is allocated by user here for the number of wlans */
            } wlan_scan;

        } data;
    };
Note: I think we need to distinguish different NO_MAGIC events based on their type. For example, we can have a NO_MAGIC event because we don't have any previously-visited WLANs to connect to in a set of WLANs returned from a scan, and we can have a NO_MAGIC event if we have a secured WLAN to connect to, but no key. More specification of NO_MAGIC event.

5.6 Internal API Framework: Repository Transactions

The libnwam model is transactional - that is data is read from backend storage atomically, modified and then committed atomically. In order to do this, we require an in-memory representation of data associated with an entity, which can be tied to that entity's handle. The suggested approach is to use libnvpair(3LIB) to store in-memory representations, specifically using nvlists for property lists associated with NWAM objects. This solution is attractive, since libnvpair already provides functionality to manipulate name-value pair lists, to duplicate them, traverse them etc.

Concretely, a given object handle will have a nvlist associated with it, which is allocated on object creation. If an object is read in from persistent storage, the nvlist is populated with appropriate name-value pairs, each of which has an associated array of 64 bit integer (signed or unsigned), boolean, or string values (added via nvlist_add_uint64_array() etc). Names of each nvlist element will correspond to property names. When a property is set, it will replace any existing instance of that property in the nvlist (the lists are of NV_UNIQUE_TYPE). At commit time, we walk the list via nvlist_next_nvpair(), translating each property into the persistent storage equivalent.

Note also that the nvlist representation is also amenable to SMF (instances could be represented as an nvlist of property groups, each of which is an nvlist of propertyname-value pairs, so this solution fits with the future direction of libnwam also.

Revision History

Revision Date Changes
2.1 2008-Feb-28 using files rather than smf; library clean-up
2.2 2008-Mar-05 add revision history; fix formatting nits; add some ToDo items
2.3 2008-Mar-12 add section on repository transactions; add some ToDo items
2.4 2008-Apr-03 update intro
2.5 2008-Sep-19 update based on implementation experience
2.6 2008-Sep-29 fix a few more details