
    libpp_cim - Tiny CIM object manager library
    Ingo van Lil <inva@peppercon.de>, 2005-09-29

Table of contents:
  1. Overview
  2. Data type handling
  2.1 Value maps
  3. Using CIM classes and instances
  3.1. Find instances
  3.2. Instance handling
  4. Adding new classes
  4.1. Class description
  4.2. Property description
  4.3. Method descriptions
  4.4. Provider interface
  5. CLP-specific features
  5.1. CLP namespace
  5.2. CLP-to-CIM mapping


1. Overview

  This is libpp_cim, a small and stripped-down to the bones CIM object manager
  library. It is in no way compatible with any CIM standards: It does not
  include a MOF parser, it does not support any standard provider programming
  interface (NPI, CMPI or whatever) and it is by no means accessible from
  outside the server process itself. It does however help to pretend having a
  CIM-compatible environment while keeping a comparatively low memory
  footprint for small, embedded CLP or WS-Management servers.

  Current feature list (2005-09-29):
    - Class inheritance
    - Reading and setting properties
    - CIM methods
    - Private (invisible) properties and methods
    - Basic data types: boolean, integer (32 bit signed/unsigned),
                        float, string, void
    - One-dimensional arrays
    - Value-mapped properties
    - UFcT (User Friendly Class Tag) support
    - CLP instance tree
    - CLP-to-CIM mapping (Set, Reset, Start and Stop verbs)


2. Data type handling

  libpp_cim currently supports six basic data types (boolean, unsigned int,
  signed int, float, string and void) and can handle one-dimensional arrays of
  these. These possible data types are listed in the pp_cim_datatype_t enum.
  Data values are stored in a pp_cim_data_t structure:

    typedef struct {
        int null;
        union {
            int boolean;
            int signed_int;
            unsigned int unsigned_int;
            double real;
            char *string;
            vector_t *array; // element type: pp_cim_data_t
        } types;
    } pp_cim_data_t;

  The null flag specifies whether the data structure contains a valid value
  (null == 0) or is undefined (null != 0). The types union contains the
  actual data. Only one of its members can hold data at any given time; which
  member to use depends on the data type the structure is supposed to
  represent.

  Strings are stored in the types.string member. It must point to a valid
  null-terminated character array for non-null values and must not be
  initialized otherwise.

  CIM arrays are stored in the types.array member. This field must be
  initialized with vector_new() unless the null flag is set, and it must not
  be initialized otherwise. The vector must not have an element delete
  function.

  The cim_data.c source file contains a set of small functions to help dealing
  with CIM data structures:

  * pp_cim_data_null: Reset a given data structure to NULL, but don't free
        the structure itself. This function may be called for any global,
        static, automatic and dynamically allocated structure as long as
        the null flag is set or the types union is correctly initialized
        according to the type and array arguments.
        If the structure stores an array the vector and all its elements
        will be correctly cleaned and freed.

  * pp_cim_data_free: This function cleans and frees a CIM data structure. It
        may only be called for dynamically allocated structures.

  * pp_cim_data_copy: Copy the src data structure's contents to dst. The dst
        data structure must be empty. If src holds a string or an array these
        will be duplicated as well (including all array elements).

  * pp_cim_data_dup: Dynamically allocate a new data structure and copy the
        given structure's contents into the new one.

  * pp_cim_data_parse: Convert a string to a CIM data structure. This function
        handles non-void basic data types (boolean, integer, float and string)
        only.

  * pp_cim_data_equal: Compares two CIM data structures. Both structures must
        contain a value of the same type. This function handles for non-void
        basic data types only.

  * pp_cim_data_print: Print a CIM data structure into a newly allocated
        string buffer. This works for non-void basic data types only.


2.1 Value maps

  Value maps are the CIM 2.x approach to enumeration types: An integer
  property, method or parameter may have 'ValueMap' and 'Values' qualifiers
  which list a set of allowed values for the integer and the same number of
  symbolic names. libpp_cim defines the pp_cim_valmap_t struct for value map
  handling:

    typedef struct {
        int min;
        int max;
        const char *value;
    } pp_cim_valmap_t;

  The min and max fields specify the integer range associated with the string
  in the value field. The ranges must be non-overlapping and sorted, the array
  must be terminated with a value == NULL entry.

  Value mapping is performed by the pp_cim_map_value function. It accepts a
  pointer to a value map array and an index value and returns a string
  constant that must not be changed or freed.


3. Using CIM classes and instances

3.1. Find instances

  The following steps are required to gain access to a set of CIM instances:

  (1) Class lookup
        Use the pp_cim_class_lookup() to find a CIM class by name and gain
        exclusive access to it. This will prevent other threads from accessing
        the class and all its super- and subclasses, so the lock should be
        released as soon as possible.

  (2) Class update
        Some classes (currently CIM_RecordLog/CIM_LogRecord) have dynamic
        instances and thus need to be updated before accessing the instance
        vector. Use pp_cim_class_update() to do so.

  (3) Get instance vector
        Call pp_cim_get_instances() to get a vector of instances of this
        class, including all subclasses. To get the instances of one
        particular class only access the class->instances field directly.

  (4) Tag wanted instances
        Iterate through the instance vector, find any instances of interest
        and use pp_cim_instance_tag() to increase each one's reference counter
        and thus protect it against deletion.

  (5) Free instance vector
        Call vector_delete() to delete the instance vector, unless accessing
        the class->instances field directly.

  (6) Unlock class
        Use pp_cim_class_release() to release the class lock.

  (7) Finally: Release instances
        If an instance is no longer needed use pp_cim_instance_untag() to
        decrease its reference counter.


3.2. Instance handling

  The thread may keep and use the instance until untagging it. The CIM library
  offers the following actions to perform on an instance:

  * pp_cim_qualifies_as: Check whether the instance belongs or is derived from
        a given CIM class.
  * pp_cim_get_property: Get a property by name. The instance's update
        function is called before reading the property.
  * pp_cim_set_property: Change a property by name. The property is marked to
        be taken into account on the next instance commit call.
  * pp_cim_get_properties: Get multiple properties at once. The update function
        is only called once.
  * pp_cim_set_properties: Set multiple properties at once.
  * pp_cim_get_method: Get method reference by name.
  * pp_cim_commit_properties: Call the instance's commit function.
  * pp_cim_method_call_exec: Execute a CIM method call.


4. Adding new classes

4.1. Class description

  A new CIM class is generated by calling the pp_cim_class_new() function.
  This function requires one parameter, a class description structure
  pp_cim_class_desc_t:

    typedef struct {
        char *cim_name;
        char *ufct;
        char *dispname;
        char *superclass;
        pp_cim_property_t *properties;
        pp_cim_method_t *methods;
    } pp_cim_class_desc_t;

  The cim_name field specifies the CIM class name (e.g. CIM_NumericSensor),
  the ufct is an optional User Friendly Class Tag as specified by the SM ME
  Addressing spec (e.g. nsensor) and dispname is an optional display name for
  Microsoft's WS-Management (e.g. CIM Numeric Sensor). The superclass field
  specifies the parent class's name, properties and methods are pointers to
  arrays containing the property and method descriptions (see below).


4.2. Property description

  The class description's properties pointer references an array of
  pp_cim_property_t structures:

    typedef struct {
        char *name;
        pp_cim_datatype_t type;
        int array;
        pp_cim_valmap_t *valmap;
        int key;
        int priv;
        int required;
        int writable;
        pp_cim_data_t defvalue;
    } pp_cim_property_t;

  The name, type and array fields specifie the properties name and type.
  valmap is an optional pointer to a value map which translates unsigned
  integer values into description strings. The next four fields define the
  property's qualifiers: key (key property), priv (private/hidden), required
  (required by the respective profile) and writable (property may be changed).
  The final defvalue field specifies an optional default value to be assigned
  at instance generation.
  The array is terminated by an element with name == NULL.


4.3. Method descriptions

  The methods field points to an array of pp_cim_method_t structures:

    typedef struct {
        char *name;
        pp_cim_datatype_t result;
        int num_args;
        pp_cim_method_arg_t *args;
        int priv;
        int (*defptr)(pp_cim_instance_t *instance, vector_t *args, pp_cim_data_t *result);
    } pp_cim_method_t;

  The name field specifies the method name, result is the return type,
  num_args is the number of arguments. The args field points to an array of
  pp_cim_method_arg_t structures that describes the methods argument names,
  types and qualifiers. The priv flag makes the method private, the defptr
  contains an optional default function pointer.
  The array is terminated by an element with name == NULL.

    typedef struct {
        char *name;
        pp_cim_datatype_t type;
        int array;
        pp_cim_valmap_t *valmap;
        int in;
        int out;
    } pp_cim_method_arg_t;

  This structure describes a method's argument fingerprint: name is the
  argument name, type and array specify the data type, valmap is an optional
  value map pointer and the in and out flags specify whether the argument is
  an input or output parameter.
  The array does not need to be terminated because the number of arguments
  is specified in the method description.


4.4. Provider interface

  Each CIM instance contains a pointer to a pp_cim_provider_t structure, which
  holds a set of function pointers that are called whenever a certain action
  is applied to the instance:

    struct pp_cim_provider_s {
        void (*deinit)(pp_cim_instance_t *instance);
        void (*update)(pp_cim_instance_t *instance);
        void (*commit)(pp_cim_instance_t *instance);
        pp_cim_propval_t *(*get_property)(pp_cim_instance_t *instance,
                const char *name);
        int (*set_property)(pp_cim_instance_t *instance,
                const char *name, pp_cim_data_t data, int force);
        vector_t *(*get_properties)(pp_cim_instance_t *instance);
        int (*set_properties)(pp_cim_instance_t *instance,
                vector_t *names, vector_t *properties);
        pp_cim_methodptr_t *(*get_method)(pp_cim_instance_t *instance,
                const char *name);
        int (*call_method)(pp_cim_instance_t *instance,
                const char *name, vector_t *args, pp_cim_data_t *result);
        pp_cim_method_call_t *(*clp_map_reset)(pp_cim_instance_t *instance);
        pp_cim_method_call_t *(*clp_map_set)(pp_cim_instance_t *instance,
                pp_clp_property_value_t *prop);
        pp_cim_method_call_t *(*clp_map_start)(pp_cim_instance_t *instance);
        pp_cim_method_call_t *(*clp_map_stop)(pp_cim_instance_t *instance);
    };

  * deinit: This function is called immediately before removing the instance
        from memory.
  * update: This function is called before reading one or more properties from
        the instance. Its job is to get current values for all dynamic
        properties (e.g. get current sensor readings via IPMI).
  * commit: Commit recent changes.
  * get_property: Get instance property by name.
  * set_property: Write instance property. The 'force' flag specifies whether
        to allow changing a read-only property or not.
  * get_properties: Get a list of all non-private properties.
  * set_properties: Change multiple properties at once.
  * get_method: Get CIM method reference by name.
  * call_method: Call a CIM method by name.
  * clp_map_*: Map CLP command to CIM method call, see below.

  Most providers don't implement more than one or two of these functions
  themselves (usually update and/or commit), the generic versions from
  provider_common.c are sufficient for most cases. A provider might e.g.
  overwrite the set_property() method, single out one or more properties to
  handle in a special way and call the generic set_property() function for the
  remaining properties.


5. CLP-specific features

  For reasons of convenience and/or bad design the libpp_cim library performs
  a couple of tasks that would actually better fit into the CLP interpreter,
  libpp_clp: The CLP namespace hierarchy, which according to the spec should
  be generated from association classes by the CLP parser, is hard-coded into
  the pp_cim_instance_t structure, user friendly class tags are stored within
  the class and instance structures and the CLP-to-CIM mapping is part of the
  provider programming interface.


5.1. CLP namespace

  The CLP namespace hierarchy is hard-coded into the parent (parent instance),
  next (next sibling instance) and children (first child instance) of the
  instance structure. All these pointers may be NULL if the instance does not
  belong into the CLP namespace, or the instance is the namespace root or it
  does not have children or subsequent siblings.
  User friendly class tags are assigned on a per-class basis (ufct field), but
  may be overwritten by an instance (alias field). The instance id suffix is
  stored within the instance structure (instance_id).
  libpp_cim offers three functions to find an instance within the CLP
  namespace:

  * pp_cim_lookup_relative traverses the namespace tree starting at a given
        instance, following a vector of UFiT structures resembling a UFiP.
  * pp_cim_lookup_absolute does a UFiP lookup relative to the namespace root.
  * pp_cim_clp_lookup accepts a libpp_clp target structure and a default
        target vector and returns a set of instances addressed by the target.


5.2. CLP-to-CIM mapping

  CLP-to-CIM mapping is part of the CIM provider API in order to keep class-
  specific behavior limited to the CIM server and out of the CLP parser. The
  provider interface offers four different mapping functions, one each for the
  Reset, Set, Start and Stop verbs. Each of these functions returns a CIM
  method call structure. The function pointers may be NULL in case the
  respective verb is not supported by the provider.
  The clp_map_reset, clp_map_start and clp_map_stop mapping functions require
  no arguments other than the obligatory instance pointer. The clp_map_set
  function takes an additional CLP property/value structure specifying which
  property is supposed to be set to what value.
  CIM offers eight functions wrapping access to these provider functions:
  pp_cim_clp_(reset|set|start|stop)_supported to check if a verb is supported
  by a particular target and pp_cim_clp_map_(reest|set|start|stop) to perform
  the actual mapping.



 vim:tw=78:et:ai

