root/usr/src/lib/mpapi/libmpapi/common/mpapi.c
/******************************************************************************
 *
 * Description
 * mpapi.c - Implements Multipath Management API Version 1.0
 *
 * License:
 *  The contents of this file are subject to the SNIA Public License
 *  Version 1.1 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *  http://mp-mgmt-api.sourceforge.net
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 *  the License for the specific language governing rights and limitations
 *  under the License.
 *
 * The Original Code is  SNIA iSCSI Management API and Multipath Management
 *      API header files.
 *
 * The Initial Developer of the Original Code is:
 *      Benjamin F. Kuo Troika Networks, Inc. (benk@troikanetworks.com)
 *      David Dillard   VERITAS Software(david.dillard@veritas.com)
 *      Jeff Ding       Adaptec, Inc. (jding@corp.adaptec.com)
 *      Hyon Kim        Sun Microsystems(hyon.kim@sun.com)
 *
 * Contributor(s):
 *      Paul von Behren Sun Microsystems(paul.vonbehren@sun.com)
 *
 ******************************************************************************
 *
 *   Changes:
 *  1/15/2005   Implemented SNIA MP API specification 1.0
 *  10/11/2005
 *              - License location was specified in the header comment.
 *              - validate_object() routine was updated per the latest
 *                specification.
 *              - is_zero_oid() routine was added.
 *              - MP_GetObjectType() was updated with validate_object().
 *              - pplist argument checking added in MP_GetMultipathLus().
 *              - Corrected typo in MP_GetTaregetPortGroupProperties()
 *              - MP_RegisterForObjectPropertyChanges() was updated with
 *                is_zero_oid() routine.
 *              - MP_DeregisterForObjectPropertyChanges() was updated with
 *                is_zero_oid() routine.
 *              - MP_RegisterForObjectVisibilityChanges() was updated with
 *                is_zero_oid() routine.
 *              - MP_DeregisterForObjectVisibilityChanges() was updated with
 *                is_zero_oid() routine.
 *              - Added stat() check in MP_RegisterPlugin() to validate the
 *                the given plugin file name.
 *              - Made MP_DeregisterPlugin() return MP_STATUS_UNKNOWN_FN
 *                to mach the specification description.
 ******************************************************************************
 */

#include <sys/sem.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include "mpapi.h"
#include "mpapi-sun.h"
#include "mpapi-plugin.h"

#define LIBRARY_SUPPORTED_MP_VERSION    1
#define LIBRARY_IMPLEMENTATION_VERSION  L"1.0.0"
#define LIBRARY_VENDOR                  L"Sun Microsystems Inc."

#define LIBRARY_FILE_NAME               "libMPAPI.so"


MPPLUGININFO_T  plugintable[MP_MAX_NUM_PLUGINS];
pthread_mutex_t mp_lib_mutex = PTHREAD_MUTEX_INITIALIZER;

static int      number_of_plugins = -1;


void InitLibrary();
void ExitLibrary();
static int lock_register(int fd, int cmd, int type, off_t offset, int whence,
            off_t len);
static int search_line(MP_CHAR *buf, size_t buflen, MP_CHAR *srch_id,
            size_t id_len, int *write_offset, int *bytes_left);
static int is_zero_oid(MP_OID);

/**
 ******************************************************************************
 *
 * Validate the oid.
 *
 * - Return MP_STATUS_OBJECT_NOT_FOUND when no plugin is found or the ownerId
 *      of input OID is not found.
 * - Return MP_STATUS_INVALID_OBJECT_TYPE when no plugin is found or
 *      the type of input OID is not one of legitimate types defined SNIA
 *      Multipath Management spec.
 * - Return MP_STATUS_INVALID_PARAMETER when the type of input OID is
 *      legitimate but its object type doesn't match with the object type
 *      argument.
 * - Otherwise return MP_STATUS_SUCCESS.
 *
 ******************************************************************************
 */
MP_STATUS validate_object(MP_OID obj, MP_OBJECT_TYPE objType,
    MP_UINT32 flag)
{

    if ((number_of_plugins == 0) ||
        (obj.ownerId > number_of_plugins || obj.ownerId <= 0)) {
        return (MP_STATUS_OBJECT_NOT_FOUND);
    } else if (obj.objectType < 0 || obj.objectType > MP_OBJECT_TYPE_MAX) {
        return (MP_STATUS_INVALID_OBJECT_TYPE);
    } else if (obj.objectType == MP_OBJECT_TYPE_PLUGIN) {
        if (obj.objectSequenceNumber != 0) {
            return (MP_STATUS_OBJECT_NOT_FOUND);
        }
    }

    if (flag == MP_OBJECT_TYPE_MATCH) {
        if (obj.objectType != objType) {
            return (MP_STATUS_INVALID_PARAMETER);
        }
    }
    return (MP_STATUS_SUCCESS);
}

/**
 ******************************************************************************
 *
 * Check if an oid is ZERO_OID or not.
 *
 * - Return 1 if the input OID is ZERO_OID
 *
 * - Return 0 if not.
 *
 ******************************************************************************
 */
static int is_zero_oid(MP_OID oid)
{

    if ((oid.objectType != MP_OBJECT_TYPE_UNKNOWN) || (oid.ownerId != 0) ||
        (oid.objectSequenceNumber != 0)) {
        return (0);
    }

    return (1);
}

/**
 ******************************************************************************
 *
 * Initialize by loading plugin libraries and calling Initialize routine.
 * Note: The build of libMPAPI.so should include a linker option to make this
 *       routine executed when it is loaded.
 *
 * - This routine bypasses a plugin library if it is not found.
 * - The implementation of this routine is based on configuration file
 *   /etc/mpapi.conf that contains a list of plugin libraries.
 *
 ******************************************************************************
 */
void InitLibrary()
{
        FILE *mpconf;
        int fd_mpconf;
        MP_WCHAR fullline[MAX_LINE_SIZE]; /* line read in from mpapi.conf */
        MP_WCHAR name[MAX_NAME_SIZE];   /* Read in from file mpapi.conf */
        char path[MAX_NAME_SIZE];       /* Read in from file mpapi.conf */
        char systemPath[MAX_NAME_SIZE], mpConfFilePath[MAX_NAME_SIZE];
        MP_WCHAR *charPtr;
        MP_WCHAR *sol;
        struct stat     stat_buf;

        MP_UINT32 i = 0;        /* index for plugin table */

        if(number_of_plugins != -1) {
                return;
        }

        (void) pthread_mutex_lock(&mp_lib_mutex);

        number_of_plugins = 0;

        /* Open configuration file from known location */
        strncpy(mpConfFilePath, "/etc/mpapi.conf", MAX_NAME_SIZE);

        if ((fd_mpconf = open(mpConfFilePath, O_RDONLY)) < 0) {
                (void) pthread_mutex_unlock(&mp_lib_mutex);
                return;
        }

        if (lock_register(fd_mpconf, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0) < 0) {
                close(fd_mpconf);
                (void) pthread_mutex_unlock(&mp_lib_mutex);
                return;
        }

        if ((mpconf = fdopen(fd_mpconf, "r")) == NULL) {
                lock_register(fd_mpconf, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
                close(fd_mpconf);
                (void) pthread_mutex_unlock(&mp_lib_mutex);
                return;
        }

        /* Read in each line and load library */
        while ((mpconf != NULL) &&
            (charPtr = fgetws(fullline, MAX_LINE_SIZE, mpconf))) {
            if ((*charPtr != L'#') && (*charPtr != L'\n')) {
                /* Take out the '\n' */
                if ((charPtr = wcschr(fullline, L'\n')) != NULL)
                    *charPtr = L'\0';

                charPtr = fullline;
                /* remove leading blank or taps. */
                while ((fullline[0] == L' ') || (fullline[0] == L'\t'))
                        charPtr++;

                sol = charPtr;

                /*
                 * look for first tab or space.
                 */
                if ((charPtr = wcschr(fullline, L'\t')) == NULL)
                    charPtr = wcschr(fullline, L' ');

                /* Set Null termination for library name if found */
                if (charPtr != NULL) {
                    *charPtr++ = L'\0';
                    wcsncpy(name, sol, MAX_NAME_SIZE);
                        /* Skip space and tab until the next character found */
                    while ((*charPtr == L' ') || (*charPtr == L'\t'))
                        charPtr++;
                } else {
                    continue;   /* May be invalid entry */
                }

                /* Copy library name and path */
                wcstombs(path, charPtr, MAX_NAME_SIZE);

                /*
                 * Continue to the next line if library name or path is
                 * invalid
                 */
                if ((wcslen(name) == 0) ||
                        (strlen(path) == 0))
                    continue;

                /* Load the plugin now */
                if (stat(path, &stat_buf) != -1) {
                    plugintable[i].hdlPlugin = dlopen(path, RTLD_LAZY);
                } else {
                    continue;
                }

                if (plugintable[i].hdlPlugin != NULL) {
                    InitializeFn PassFunc;

                    wcsncpy(plugintable[i].pluginName,
                        name, MAX_NAME_SIZE);
                    strncpy(plugintable[i].pluginPath,
                        path, MAX_NAME_SIZE);

                    plugintable[i].ownerId = i + 1;

                    PassFunc = (InitializeFn)
                         dlsym(plugintable[i].hdlPlugin, "Initialize");
                    if (PassFunc != NULL) {
                        (void) PassFunc(plugintable[i].ownerId);
                    }

                    i++;
                }
            }
        }

        if (lock_register(fd_mpconf, F_SETLK, F_UNLCK, 0, SEEK_SET, 0) < 0) {
            fclose(mpconf);
            close(fd_mpconf);
            (void) pthread_mutex_unlock(&mp_lib_mutex);
            return;
        }
        fclose(mpconf);
        close(fd_mpconf);

        number_of_plugins = i;
        (void) pthread_mutex_unlock(&mp_lib_mutex);
}

/**
 ******************************************************************************
 *
 * Exit by calling Terminate routine of plugin libraries.
 *
 * Note: The build of libMPAPI.so should include a linker option to make this
 *       routine executed when it is unloaded.
 *
 ******************************************************************************
 */
void ExitLibrary()
{
    MP_UINT32 i, j;

    if(number_of_plugins == -1)
        return;

    (void) pthread_mutex_lock(&mp_lib_mutex);
    for (i = 0; i < number_of_plugins; i++) {
        if (plugintable[i].hdlPlugin != NULL) {
        TerminateFn ExitPassFunc;

        ExitPassFunc = (TerminateFn)
            dlsym(plugintable[i].hdlPlugin, "Terminate");

        if (ExitPassFunc != NULL) {
            ExitPassFunc();
        }

        /* Unload plugin from memory */
        dlclose(plugintable[i].hdlPlugin);
        }
    }

    number_of_plugins = -1;

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    (void) pthread_mutex_destroy(&mp_lib_mutex);
}

/**
 ******************************************************************************
 *
 * Gets the properties of the MP API library that is being used.
 *
 * @param pProps
 *  A pointer to an @ref MP_LIBRARY_PROPERTIES structure allocated by
 *  the caller.  On successful return this structure will contain the
 *  properties of the MP library.
 *
 * @return An MP_STATUS indicating if the operation was successful or
 *  if an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *  Returned if the library properties were successfully returned.
 *
 * @retval MP_STATUS_INVALID_PARAMETER Returned if @a pProps is NULL or
 *  specifies a memory area to which data cannot be written.
 *
 ******************************************************************************
 */
MP_STATUS MP_GetLibraryProperties(
    MP_LIBRARY_PROPERTIES *pProps)
{
    char mpPath[MAX_NAME_SIZE];

    if(pProps == NULL) {
        return MP_STATUS_INVALID_PARAMETER;
    }

    /* Fill in properties */
    if (mbstowcs(pProps->buildTime, BUILD_TIME, 256) !=
        strlen(BUILD_TIME)) {
        return (MP_STATUS_INVALID_PARAMETER);
    }
    pProps->supportedMpVersion = LIBRARY_SUPPORTED_MP_VERSION;

    wcsncpy(pProps->implementationVersion,
        LIBRARY_IMPLEMENTATION_VERSION, MAX_NAME_SIZE);
    wcsncpy(pProps->vendor, LIBRARY_VENDOR, MAX_NAME_SIZE);

    snprintf(pProps->fileName, MAX_NAME_SIZE, "%s",
        LIBRARY_FILE_NAME);

    return MP_STATUS_SUCCESS;
}


/**
 ******************************************************************************
 *
 * Gets a list of the object IDs of all currently loaded plugins.
 *
 * @param ppList A pointer to a pointer to an @ref MP_OID_LIST.  On successful
 *  return this will contain a pointer to an @ref MP_OID_LIST
 *  which contains the object IDs of all of the plugins currently loaded
 *  by the library.
 * @return An MP_STATUS indicating if the operation was successful or if
 * an error
 *              occurred.
 * @retval MP_SUCCESS Returned if the plugin ID list was successfully returned.
 * @retval MP_STATUS_INVALID_PARAMETER Returned if @a ppList is NULL or
 * specifies a memory area to which data cannot be written.
 *
 ******************************************************************************
 */
MP_STATUS MP_GetPluginOidList(
    MP_OID_LIST **ppList)
{
    MP_UINT32  i;

    if (ppList == NULL)
        return (MP_STATUS_INVALID_PARAMETER);

    (void) pthread_mutex_lock(&mp_lib_mutex);

    if (number_of_plugins == 0) {
        *ppList = (MP_OID_LIST*)calloc(1, sizeof(MP_OID_LIST));
    } else {
        *ppList = (MP_OID_LIST*)calloc(1,
        sizeof(MP_OID_LIST) + (number_of_plugins - 1)* sizeof(MP_OID) );
    }

    if ((*ppList) == NULL) {
        (void) pthread_mutex_unlock(&mp_lib_mutex);
        return (MP_STATUS_INSUFFICIENT_MEMORY);
    }

    (*ppList)->oidCount = number_of_plugins;

    if (number_of_plugins != 0) {
        for (i = 0; i < number_of_plugins; i++) {
        (*ppList)->oids[i].objectType = MP_OBJECT_TYPE_PLUGIN;
        (*ppList)->oids[i].ownerId = plugintable[i].ownerId;
        (*ppList)->oids[i].objectSequenceNumber = 0;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return MP_STATUS_SUCCESS;
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified vendor plugin.
 *
 * @param  oid
 *         The ID of the plugin whose properties are being retrieved.
 *
 * @param  pProps
 *         A pointer to an @ref MP_PLUGIN_PROPERTIES structure allocated by
 *         the caller.  On successful return this will contain the properties
 *         of the plugin specified by pluginOid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if an
 *         error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned if the plugin properties were successfully returned.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *         Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *         Returned if oid has an owner that is not currently known to
 *     the system.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *         Returned if 'pProps' is NULL or specifies a memory area to
 *         which data cannot be written.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetPluginProperties(
    MP_OID pluginOid,
    MP_PLUGIN_PROPERTIES *pProps)
{
    MP_GetPluginPropertiesPluginFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if(pProps == NULL)
        return (MP_STATUS_INVALID_PARAMETER);

    if ((status = validate_object(pluginOid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = pluginOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetPluginPropertiesPluginFn)
        dlsym(plugintable[index].hdlPlugin, "MP_GetPluginPropertiesPlugin");

        if (PassFunc != NULL) {
            status = PassFunc(pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return status;
}

/**
 *******************************************************************************
 *
 * Gets the object ID for the plugin associated with the specified object ID.
 *
 * @param  oid
 *         The object ID of an object that has been received from a previous
 *         library call.
 *
 * @param  pPluginOid
 *         A pointer to an MP_OID structure allocated by the caller.  On
 *         successful return this will contain the object ID of the plugin
 *         associated with the object specified by @a objectId.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *          Returned if the associated plugin ID was successfully returned.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid does not specify a plugin that is currently known to
 *     the system.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *          Returned if 'oid' specifies an object not owned by a plugin or
 *     if pPluginOid is NULL or specifies a memory area to which data
 *     cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *         Returned if 'oid' specifies an object with an invalid type.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetAssociatedPluginOid(
    MP_OID objectId,
    MP_OID *pPluginId)
{
    MP_UINT32 i;
    MP_STATUS status;

    if (pPluginId == NULL)
        return (MP_STATUS_INVALID_PARAMETER);

    if ((status = validate_object(objectId, 0, MP_OBJECT_TYPE_ANY)) !=
            MP_STATUS_SUCCESS) {
        return (status);
    }

    pPluginId->objectType = MP_OBJECT_TYPE_PLUGIN;
    pPluginId->ownerId = objectId.ownerId;
    pPluginId->objectSequenceNumber = 0;

    return (MP_STATUS_SUCCESS);
}

/**
 *******************************************************************************
 *
 * Gets the object type of an initialized object ID.
 *
 * @param  oid
 *         The object ID of an object that has been received from a previous
 *         library call.
 *
 * @param  pObjectType
 *         A pointer to an MP_OBJECT_TYPE variable allocated by the caller.
 *         On successful return this will contain the object type of oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or
 *         if an error occurred.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned if oid has an owner that is not currently known to
 *      the system.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetObjectType(
    MP_OID oid,
    MP_OBJECT_TYPE *pObjectType)
{
    MP_STATUS status;

    if (pObjectType == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, 0, MP_OBJECT_TYPE_ANY))
        != MP_STATUS_SUCCESS) {
        return (status);
    }

    *pObjectType = oid.objectType;
    return MP_STATUS_SUCCESS;
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of all the device product properties
 *       associated with this plugin.
 *
 * @param  oid
 *         The object ID of plugin.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the device
 *      product descriptors associated with the specified plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the device product list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *         Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *         Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the API is not supported.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetDeviceProductOidList(
    MP_OID oid,
    MP_OID_LIST **ppList)
{
    MP_GetDeviceProductOidListPluginFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetDeviceProductOidListPluginFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetDeviceProductOidListPlugin");
        if (PassFunc != NULL) {
            status = PassFunc(ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return status;
}

/**
 *******************************************************************************
 *
 * Gets the device product properties of the specified plugin oid.
 *
 * @param  oid
 *         The object ID of the plugin.
 *
 * @param  ppProps
 *      A pointer to a pointer to an MP_DEVICE_PRODUCT_PROPERTIES structure
 *      allocated by the caller. On successful return it will contain
 *      a pointer to an MP_DEVICE_PRODUCT_PROPERTIES structure allocated
 *      by the library.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppProps pointer passed as placeholder for holding
 *      the device product properties is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *         Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *         Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the API is not supported.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetDeviceProductProperties(
        MP_OID oid,
        MP_DEVICE_PRODUCT_PROPERTIES *pProps)
{
    MP_GetDeviceProductPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_DEVICE_PRODUCT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetDeviceProductPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetDeviceProductProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return status;
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of all the initiator ports associated
 * with this plugin.
 *
 * @param  oid
 *         The object ID of plugin.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the initiator
 *      ports associated with the specified plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the initiator port list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the API is not supported.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetInitiatorPortOidList(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetInitiatorPortOidListPluginFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetDeviceProductOidListPluginFn)
        dlsym(plugintable[index].hdlPlugin, "MP_GetInitiatorPortOidListPlugin");

        if (PassFunc != NULL) {
            status = PassFunc(ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified initiator port.
 *
 * @param  oid
 *         The object ID of the initiator port.
 *
 * @param  pProps
 *      A pointer to an MP_INITIATOR_PORT_PROPERTIES structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the port specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetInitiatorPortProperties(
        MP_OID oid,
        MP_INITIATOR_PORT_PROPERTIES *pProps)
{
    MP_GetInitiatorPortPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_INITIATOR_PORT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetInitiatorPortPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetInitiatorPortProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return status;
}

/**
 *******************************************************************************
 *
 * Gets a list of multipath logical units associated to a plugin.
 *
 * @param  oid
 *         The object ID of plugin.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the multipath
 *      logical units associated with the specified plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the multipath logical unit list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the API is not supported.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetMultipathLus(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_DEVICE_PRODUCT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_GetMultipathLusPluginFn PassFunc;
            PassFunc = (MP_GetMultipathLusPluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_GetMultipathLusPlugin");

            if (PassFunc != NULL) {
                status = PassFunc(ppList);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_DEVICE_PRODUCT) {
            MP_GetMultipathLusDevProdFn PassFunc;
            PassFunc = (MP_GetMultipathLusDevProdFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_GetMultipathLusDevProd");

            if (PassFunc != NULL) {
                status = PassFunc(oid, ppList);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}


/**
 *******************************************************************************
 *
 * Gets the properties of the specified logical unit.
 *
 * @param  oid
 *         The object ID of the multipath logical unit.
 *
 * @param  pProps
 *      A pointer to an MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the port specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetMPLogicalUnitProperties(
        MP_OID oid,
        MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES *pProps)
{
    MP_GetMPLogicalUnitPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetMPLogicalUnitPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetMPLogicalUnitProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of all the path logical units associated
 * with the specified multipath logical unit, initiator port, or target port.
 *
 * @param  oid
 *         The object ID of multipath logical unit, initiator port, or
 *     target port.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the mp path
 *      logical units associated with the specified OID.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the device product list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetAssociatedPathOidList(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetAssociatedPathOidListFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_INITIATOR_PORT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_TARGET_PORT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetAssociatedPathOidListFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetAssociatedPathOidList");

        if (PassFunc != NULL) {
            status = PassFunc(oid, ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified path logical unit.
 *
 * @param  oid
 *         The object ID of the path logical unit.
 *
 * @param  pProps
 *      A pointer to an MP_PATH_LOGICAL_UNIT_PROPERTIES structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the port specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetPathLogicalUnitProperties(
        MP_OID oid,
        MP_PATH_LOGICAL_UNIT_PROPERTIES *pProps)
{
    MP_GetPathLogicalUnitPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetPathLogicalUnitPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetPathLogicalUnitProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of all the target port group associated
 * with the specified multipath logical unit.
 *
 * @param  oid
 *         The object ID of the multiple logical unit.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the target
 *      port group associated with the specified multipath logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the target port group list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 *
 *******************************************************************************
 */
MP_STATUS MP_GetAssociatedTPGOidList(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetAssociatedTPGOidListFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetAssociatedTPGOidListFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetAssociatedTPGOidList");

        if (PassFunc != NULL) {
            status = PassFunc(oid, ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified target port group.
 *
 * @param  oid
 *         The object ID of the target port group.
 *
 * @param  pProps
 *      A pointer to an MP_TARGET_PORT_GROUP_PROPERTIES structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the port specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetTargetPortGroupProperties(
        MP_OID oid,
        MP_TARGET_PORT_GROUP_PROPERTIES *pProps)
{
    MP_GetTargetPortGroupPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_TARGET_PORT_GROUP,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetTargetPortGroupPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetTargetPortGroupProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets a list of multipath logical units associated with the specific target
 *  port group.
 *
 * @param  oid
 *         The object ID of the target port group.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the multipath
 *      logical units associated with the specified target port group.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the multipath logical unit list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 *******************************************************************************
 */
MP_STATUS MP_GetMPLuOidListFromTPG(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetMPLuOidListFromTPGFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_TARGET_PORT_GROUP,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetMPLuOidListFromTPGFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetMPLuOidListFromTPG");

        if (PassFunc != NULL) {
            status = PassFunc(oid, ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of all the proprietary load balance
 * algorithms associated with this plugin.
 *
 * @param  oid
 *         The object ID of the plugin.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the proprietary
 *      load balance algorithms associated with the specified plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the proprietary load balance oid list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the API is not supported.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetProprietaryLoadBalanceOidList(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetProprietaryLoadBalanceOidListPluginFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetProprietaryLoadBalanceOidListPluginFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetProprietaryLoadBalanceOidListPlugin");

        if (PassFunc != NULL) {
            status = PassFunc(ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified load balance properties structure.
 *
 * @param  oid
 *         The object ID of the load balance properties structure.
 *
 * @param  pProps
 *      A pointer to an MP_LOAD_BALANCE_PROPRIETARY_TYPE structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the proprietary load balance algorithm
 *      specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetProprietaryLoadBalanceProperties (
        MP_OID oid,
        MP_PROPRIETARY_LOAD_BALANCE_PROPERTIES *pProps)
{
    MP_GetProprietaryLoadBalancePropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PROPRIETARY_LOAD_BALANCE,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetProprietaryLoadBalancePropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetProprietaryLoadBalanceProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets a list of the object IDs of the target ports in the specified target
 * port group.
 *
 * @param  oid
 *         The object ID of the target port group.
 *
 * @param  ppList
 *      A pointer to a pointer to an MP_OID_LIST structure.
 *      On a successful return, this will contain a pointer to
 *      an MP_OID_LIST that contains the object IDs of all the target ports
 *      associated with the specified target port group.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if ppList pointer passed as placeholder for holding
 *      the multipath logical unit list is found to be invalid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the plugin for the specified oid is not found.
 *
 * @retval MP_STATUS_INSUFFICIENT_MEMORY
 *      Returned when memory allocation failure occurs
 *
 *******************************************************************************
 */
MP_STATUS MP_GetTargetPortOidList(
        MP_OID oid,
        MP_OID_LIST **ppList)
{
    MP_GetTargetPortOidListFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (ppList == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_TARGET_PORT_GROUP,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetTargetPortOidListFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetTargetPortOidList");

        if (PassFunc != NULL) {
            status = PassFunc(oid, ppList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Gets the properties of the specified target port.
 *
 * @param  oid
 *         The object ID of the target port.
 *
 * @param  pProps
 *      A pointer to an MP_TARGET_PORT_PROPERTIES structure
 *      allocated by the caller. On successful return, this structure
 *      will contain the properties of the port specified by oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pProps is NULL or specifies a memory area to
 *      which data cannot be written.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_GetTargetPortProperties(
        MP_OID oid,
        MP_TARGET_PORT_PROPERTIES *pProps)
{
    MP_GetTargetPortPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (pProps == NULL)
        return MP_STATUS_INVALID_PARAMETER;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_TARGET_PORT,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_GetTargetPortPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_GetTargetPortProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, pProps);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}


/**
 *******************************************************************************
 *
 * Assign a multipath logical unit to a target port group.
 *
 * @param  tpgOid
 *      An MP_TARGET_PORT_GROUP oid. The target port group currently in
 *      active access state that the administrator would like the LU
 *      assigned to.
 *
 * @param  luOid
 *      An MP_MULTIPATH_LOGICAL_UNIT oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned when luOid is not associated with tpgOid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_AssignLogicalUnitToTPG(
        MP_OID tpgOid,
        MP_OID luOid)
{
    MP_AssignLogicalUnitToTPGFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (luOid.ownerId != tpgOid.ownerId) {
        return (MP_STATUS_INVALID_PARAMETER);
    }

    if ((status = validate_object(tpgOid, MP_OBJECT_TYPE_TARGET_PORT_GROUP,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    if ((status = validate_object(luOid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = tpgOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_AssignLogicalUnitToTPGFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_AssignLogicalUnitToTPG");

        if (PassFunc != NULL) {
            status = PassFunc(tpgOid, luOid);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Manually override the path for a logical unit. The path exclusively used to
 * access the logical unit until cleared.
 *
 * @param  logicalUnitOid
 *      The object ID of the multipath logical unit.
 *
 * @param  pathOid
 *      The object ID of the path logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if the oid of the object is not valid
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_PATH_NONOPERATIONAL
 *          Returned when the driver cannot communicate through selected path.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetOverridePath(
    MP_OID logicalUnitOid,
    MP_OID pathOid)
{
    MP_SetOverridePathFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(logicalUnitOid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }
    if ((status = validate_object(pathOid, MP_OBJECT_TYPE_PATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = pathOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetOverridePathFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetOverridePath");

        if (PassFunc != NULL) {
            status = PassFunc(logicalUnitOid, pathOid);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Cancel a path override and re-enable load balancing.
 *
 * @param  luOid
 *         An MP_MULTIPATH_LOGICAL_UNIT oid.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if MP_MULTIPATH_LOGICAL_UNIT with the luOid is not found.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *          Returned if oid has an owner that is not currently known to
 *      the system.
 *
 *******************************************************************************
 */
MP_STATUS MP_CancelOverridePath(
        MP_OID luOid)
{
    MP_CancelOverridePathFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(luOid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = luOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_CancelOverridePathFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_CancelOverridePath");

        if (PassFunc != NULL) {
            status = PassFunc(luOid);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Enables Auto-failback.
 *
 * @param  oid
 *      The oid of the plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid plugin oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_EnableAutoFailback(
    MP_OID oid)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_EnableAutoFailbackPluginFn PassFunc;
            PassFunc = (MP_EnableAutoFailbackPluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_EnableAutoFailbackPlugin");

            if (PassFunc != NULL) {
                status = PassFunc();
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_EnableAutoFailbackLuFn PassFunc;
            PassFunc = (MP_EnableAutoFailbackLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_EnableAutoFailbackLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Enables Auto-probing.
 *
 * @param  oid
 *      The oid of the plugin or the multipath logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid plugin oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_EnableAutoProbing(
    MP_OID oid)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_EnableAutoProbingPluginFn PassFunc;
            PassFunc = (MP_EnableAutoProbingPluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_EnableAutoProbingPlugin");

            if (PassFunc != NULL) {
                status = PassFunc();
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_EnableAutoProbingLuFn PassFunc;
            PassFunc = (MP_EnableAutoProbingLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_EnableAutoProbingLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Disables Auto-failback.
 *
 * @param  oid
 *      The oid of the plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid plugin oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_DisableAutoFailback(
    MP_OID oid)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_DisableAutoFailbackPluginFn PassFunc;
            PassFunc = (MP_DisableAutoFailbackPluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_DisableAutoFailbackPlugin");

            if (PassFunc != NULL) {
                status = PassFunc();
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_DisableAutoFailbackLuFn PassFunc;
            PassFunc = (MP_DisableAutoFailbackLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_DisableAutoFailbackLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Disables Auto-probing.
 *
 * @param  oid
 *      The oid of the plugin or the multipath logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid plugin oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_DisableAutoProbing(
    MP_OID oid)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_DisableAutoProbingPluginFn PassFunc;
            PassFunc = (MP_DisableAutoProbingPluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_DisableAutoProbingPlugin");

            if (PassFunc != NULL) {
                status = PassFunc();
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_DisableAutoFailbackLuFn PassFunc;
            PassFunc = (MP_DisableAutoProbingLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_DisableAutoProbingLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Enables a path. This API may cause failover in a logical unit with
 * asymmetric access.
 *
 * @param  oid
 *      The oid of the path.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid path oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_EnablePath(
    MP_OID oid)
{
    MP_EnablePathFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_EnablePathFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_EnablePath");

        if (PassFunc != NULL) {
            status = PassFunc(oid);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Disables a path. This API may cause failover in a logical unit with
 * asymmetric access. This API may cause a logical unit to become unavailable.
 *
 * @param  oid
 *      The oid of the path.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if oid is NULL or specifies a memory area that is not
 *      a valid path oid.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_DisablePath(
    MP_OID oid)
{
    MP_DisablePathFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_DisablePathFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_DisablePath");

        if (PassFunc != NULL) {
            status = PassFunc(oid);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the multipath logical unit s load balancing policy.
 *
 * @param  logicalUnitoid
 *      The object ID of the multipath logical unit.
 *
 * @param  loadBanlance
 *      The desired load balance policy for the specified logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if no MP_MULTIPATH_LOGICAL_UNIT associated with
 *      @ref ligicalUnitrOid is found or invalid MP_LOAD_BALANCE_TYPE is
 *      specified.
 *
 * @retval MP_STATUS_FAILED
 *      Returned when the specified loadBalance type cannot be handled
 *      by the plugin.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetLogicalUnitLoadBalanceType(
    MP_OID logicalUnitOid,
    MP_LOAD_BALANCE_TYPE loadBalance)
{
    MP_SetLogicalUnitLoadBalanceTypeFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(logicalUnitOid,
        MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = logicalUnitOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetLogicalUnitLoadBalanceTypeFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetLogicalUnitLoadBalanceType");

        if (PassFunc != NULL) {
            status = PassFunc(logicalUnitOid, loadBalance);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the weight to be assigned to a particular path.
 *
 * @param  pathOid
 *      The object ID of the path logical unit.
 *
 * @param  weight
 *      weight that will be assigned to the path logical unit.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the MP Path specified by the PathOid could not be
 *      found.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the operation failed.
 *
 * @retval MP_STATUS_PATH_NONOPERATIONAL
 *          Returned when the driver cannot communicate through selected path.
 *
 * @retval MP_STATUS_INVALID_WEIGHT
 *          Returned when the weight parameter is greater than the plugin's
 *      maxWeight property.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetPathWeight(
    MP_OID pathOid,
    MP_UINT32 weight)
{
    MP_SetPathWeightFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(pathOid, MP_OBJECT_TYPE_PATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = pathOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetPathWeightFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetPathWeight");

        if (PassFunc != NULL) {
            status = PassFunc(pathOid, weight);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the default load balance policy for the plugin.
 *
 * @param  oid
 *      The object ID of the plugin
 *
 * @param  loadBalance
 *      The desired default load balance policy for the specified plugin.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the the plugin specified by @ref oid could not be
 *      found.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if the oid of the object is not valid.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned when the specified loadBalance type cannot be handled
 *      by the plugin.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetPluginLoadBalanceType(
    MP_OID oid,
    MP_LOAD_BALANCE_TYPE loadBalance)
{
    MP_SetPluginLoadBalanceTypePluginFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetPluginLoadBalanceTypePluginFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetPluginLoadBalanceTypePlugin");

        if (PassFunc != NULL) {
            status = PassFunc(loadBalance);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the failback polling rates. Setting both rates to zero disables polling.
 *
 * @param  pluginOid
 *      The object ID of the plugin or multipath lu.
 *
 * @param  pollingRate
 *      The value to be set in MP_PLUGIN_PROPERTIES currentPollingRate.or
 *      MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES pollingRate.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the the plugin specified by @ref oid could not be
 *      found.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if one of the polling values is outside the range
 *      supported by the driver.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetFailbackPollingRate(
    MP_OID oid,
    MP_UINT32 pollingRate)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_SetFailbackPollingRatePluginFn PassFunc;
            PassFunc = (MP_SetFailbackPollingRatePluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_SetFailbackPollingRatePlugin");

            if (PassFunc != NULL) {
                status = PassFunc(pollingRate);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_SetFailbackPollingRateLuFn PassFunc;
            PassFunc = (MP_SetFailbackPollingRateLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_SetFailbackPollingRateLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid, pollingRate);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the probing polling rates. Setting both rates to zero disables polling.
 *
 * @param  pluginOid
 *      The object ID of either the plugin or a multipath logical unit.
 *
 * @param  pollingRate
 *      The value to be set in MP_PLUGIN_PROPERTIES current pollingRate or
 *      MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES pollingRate.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the the plugin specified by @ref oid could not be
 *      found.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if one of the polling values is outside the range
 *      supported by the driver.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetProbingPollingRate(
    MP_OID    oid,
    MP_UINT32 pollingRate)
{
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        if (oid.objectType == MP_OBJECT_TYPE_PLUGIN) {
            MP_SetProbingPollingRatePluginFn PassFunc;
            PassFunc = (MP_SetProbingPollingRatePluginFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_SetProbingPollingRatePlugin");

            if (PassFunc != NULL) {
                status = PassFunc(pollingRate);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else if (oid.objectType == MP_OBJECT_TYPE_MULTIPATH_LU) {
            MP_SetProbingPollingRateLuFn PassFunc;
            PassFunc = (MP_SetProbingPollingRateLuFn)
            dlsym(plugintable[index].hdlPlugin,
                "MP_SetProbingPollingRateLu");

            if (PassFunc != NULL) {
                status = PassFunc(oid, pollingRate);
            } else {
                status = MP_STATUS_UNSUPPORTED;
            }
        } else {
            status = MP_STATUS_INVALID_PARAMETER;
        }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set proprietary properties in supported object instances.
 *
 * @param  pluginOid
 *      The object ID of MP_LOAD_BALANCE_PROPRIETARY_TYPE, MP_PLUGIN_PROPERTIES
 *      or MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES.
 *
 * @param  count
 *         The number of valid items in pPropertyList.
 *
 * @param  pPropertyList
 *         A pointer to an array of property name/value pairs. This array must
 *         contain the same number of elements as count.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the the plugin specified by @ref oid could not be
 *      found.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if one of the polling values is outside the range
 *      supported by the driver.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_SetProprietaryProperties(
    MP_OID    oid,
    MP_UINT32 count,
    MP_PROPRIETARY_PROPERTY *pPropertyList)
{
    MP_SetProprietaryPropertiesFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if (((status = validate_object(oid, MP_OBJECT_TYPE_PLUGIN,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) &&
        ((status = validate_object(oid, MP_OBJECT_TYPE_PROPRIETARY_LOAD_BALANCE,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS)) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = oid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetProprietaryPropertiesFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetProprietaryProperties");

        if (PassFunc != NULL) {
            status = PassFunc(oid, count, pPropertyList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Set the access state for a list of target port groups. This allows
 * a client to force a failover or failback to a desired set of target port
 * groups.
 *
 * @param  luOid
 *      The object ID of the logical unit where the command is sent.
 *
 * @param  count
 *      The number of valid items in the pTpgStateList.
 *
 * @param  pTpgStateList
 *      A pointer to an array of TPG/access-state values. This array must
 *      contain the same number of elements as @ref count.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_OBJECT_NOT_FOUND
 *      Returned when the MP_MULTIPATH_LOGICAL_UNIT associated with @ref
 *      oid could not be found.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pTpgStateList is null or if one of the TPGs referenced
 *      in the list is not associated with the specified MP logical unit.
 *
 * @retval MP_STATUS_UNSUPPORTED
 *      Returned when the implementation does not support the API
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_ACCESS_STATE_INVALID
 *         Returned if the target device returns a status indicating the caller
 *     is attempting to establish an illegal combination of access states.
 *
 * @retval MP_STATUS_FAILED
 *          Returned if the underlying interface failed the commend for some
 *      reason other than MP_STATUS_ACCESS_STATE_INVALID
 *
 *******************************************************************************
 */
MP_STATUS MP_SetTPGAccess(
    MP_OID luOid,
    MP_UINT32 count,
    MP_TPG_STATE_PAIR *pTpgStateList)
{
    MP_SetTPGAccessFn PassFunc;
    MP_UINT32 index;
    MP_STATUS status;

    if ((status = validate_object(luOid, MP_OBJECT_TYPE_MULTIPATH_LU,
        MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
        return (status);
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    index = luOid.ownerId - 1;
    if (plugintable[index].hdlPlugin != NULL) {
        PassFunc = (MP_SetTPGAccessFn)
        dlsym(plugintable[index].hdlPlugin,
        "MP_SetTPGAccess");

        if (PassFunc != NULL) {
            status = PassFunc(luOid, count, pTpgStateList);
        } else {
            status = MP_STATUS_UNSUPPORTED;
        }
    } else {
        status = MP_STATUS_FAILED;
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Registers a client function that is to be called
 * whenever the property of an an object changes.
 *
 * @param  pClientFn,
 *      A pointer to an MP_OBJECT_PROPERTY_FN function defined by the
 *      client. On successful return this function will be called to
 *      inform the client of objects that have had one or more properties
 *      change.
 *
 * @param  objectType
 *      The type of object the client wishes to deregister for
 *      property change callbacks. If null, then all objects types are
 *      deregistered.
 *
 * @param  pCallerData
 *      A pointer that is passed to the callback routine with each event.
 *      This may be used by the caller to correlate the event to source of
 *      the registration.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pClientFn is NULL or specifies a memory area
 *      that is not executable.
 *
 * @retval MP_STATUS_FN_REPLACED
 *      Returned when an existing client function is replaced with the one
 *      specified in pClientFn.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_RegisterForObjectPropertyChanges(
    MP_OBJECT_PROPERTY_FN pClientFn,
    MP_OBJECT_TYPE objectType,
    void *pCallerData,
    MP_OID pluginOid)
{
    MP_RegisterForObjectPropertyChangesPluginFn PassFunc;
    MP_UINT32 i;
    MP_UINT32 index;
    MP_STATUS status;

    if (pClientFn == NULL) {
        return (MP_STATUS_INVALID_PARAMETER);
    }

    if (objectType > MP_OBJECT_TYPE_MAX) {
        return (MP_STATUS_INVALID_OBJECT_TYPE);
    }

    if (!(is_zero_oid(pluginOid))) {
        if ((status = validate_object(pluginOid, MP_OBJECT_TYPE_PLUGIN,
            MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
            return (status);
        }
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    if (is_zero_oid(pluginOid)) {
        for (i = 0; i < number_of_plugins; i++) {
            if (plugintable[i].hdlPlugin != NULL) {
                PassFunc = (MP_RegisterForObjectPropertyChangesPluginFn)
                dlsym(plugintable[i].hdlPlugin,
                "MP_RegisterForObjectPropertyChangesPlugin");
            }

            if (PassFunc != NULL) {
                status =
                     PassFunc(pClientFn, objectType, pCallerData);
                /* ignore an error and continue */
            }
        }
    } else {
        index = pluginOid.ownerId - 1;
        if (plugintable[index].hdlPlugin != NULL) {
                PassFunc = (MP_RegisterForObjectPropertyChangesPluginFn)
                dlsym(plugintable[index].hdlPlugin,
                "MP_RegisterForObjectPropertyChangesPlugin");
        }

        if (PassFunc != NULL) {
            status = PassFunc(pClientFn, objectType, pCallerData);
        }
    }
    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Deregisters a previously registered client function that is to be invoked
 * whenever an object's property changes.
 *
 * @param  pClientFn,
 *      A pointer to an MP_OBJECT_PROPERTY_FN function defined by the
 *      client that was previously registered using
 *      the MP_RegisterForObjectPropertyChanges API. On successful return
 *      this function will no longer be called to inform the client of
 *      object property changes.
 *
 * @param  objectType
 *      The type of object the client wishes to deregister for
 *      property change callbacks. If null, then all objects types are
 *      deregistered.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pClientFn is NULL or specifies a memory area
 *      that is not executable.
 *
 * @retval MP_STATUS_UNKNOWN_FN
 *      Returned if pClientFn is not the same as the previously registered
 *      function.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if oid does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned if pClientFn deregistration is not possible at this time.
 *
 *******************************************************************************
 */
MP_STATUS MP_DeregisterForObjectPropertyChanges(
    MP_OBJECT_PROPERTY_FN pClientFn,
    MP_OBJECT_TYPE objectType,
    MP_OID pluginOid)
{
    MP_DeregisterForObjectPropertyChangesPluginFn PassFunc;
    MP_UINT32 i;
    MP_UINT32 index;
    MP_STATUS status;

    if (pClientFn == NULL) {
        return (MP_STATUS_INVALID_PARAMETER);
    }

    if (objectType > MP_OBJECT_TYPE_MAX) {
        return (MP_STATUS_INVALID_OBJECT_TYPE);
    }

    if (!(is_zero_oid(pluginOid))) {
        if ((status = validate_object(pluginOid, MP_OBJECT_TYPE_PLUGIN,
            MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
            return (status);
        }
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    if (is_zero_oid(pluginOid)) {
        for (i = 0; i < number_of_plugins; i++) {
            if (plugintable[i].hdlPlugin != NULL) {
                PassFunc = (MP_DeregisterForObjectPropertyChangesPluginFn)
                dlsym(plugintable[i].hdlPlugin,
                "MP_DeregisterForObjectPropertyChangesPlugin");
            }

            if (PassFunc != NULL) {
                status = PassFunc(pClientFn, objectType);
            }
        }
    } else {
        index = pluginOid.ownerId - 1;
        if (plugintable[index].hdlPlugin != NULL) {
                PassFunc = (MP_DeregisterForObjectPropertyChangesPluginFn)
                dlsym(plugintable[index].hdlPlugin,
                "MP_DeregisterForObjectPropertyChangesPlugin");
        }

        if (PassFunc != NULL) {
            status = PassFunc(pClientFn, objectType);
        }
    }
    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Registers a client function that is to be called
 * whenever a high level object appears or disappears.
 *
 * @param  pClientFn,
 *      A pointer to an MP_OBJECT_VISIBILITY_FN function defined by the
 *      client. On successful return this function will be called to
 *      inform the client of objects whose visibility has changed.
 *
 * @param  objectType
 *      The type of object the client wishes to deregister for
 *      property change callbacks. If null, then all objects types are
 *      deregistered.
 *
 * @param  pCallerData
 *      A pointer that is passed to the callback routine with each event.
 *      This may be used by the caller to correlate the event to source of
 *      the registration.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pClientFn is NULL or specifies a memory area
 *      that is not executable.
 *
 * @retval MP_STATUS_FN_REPLACED
 *      Returned when an existing client function is replaced with the one
 *      specified in pClientFn.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if objectType does not specify any valid object type.
 *
 *******************************************************************************
 */
MP_STATUS MP_RegisterForObjectVisibilityChanges(
    MP_OBJECT_VISIBILITY_FN pClientFn,
    MP_OBJECT_TYPE objectType,
    void *pCallerData,
    MP_OID pluginOid)
{
    MP_RegisterForObjectVisibilityChangesPluginFn PassFunc;
    MP_UINT32 i;
    MP_UINT32 index;
    MP_STATUS status;

    if (pClientFn == NULL) {
        return (MP_STATUS_INVALID_PARAMETER);
    }

    if (objectType > MP_OBJECT_TYPE_MAX) {
        return (MP_STATUS_INVALID_OBJECT_TYPE);
    }

    if (!(is_zero_oid(pluginOid))) {
        if ((status = validate_object(pluginOid, MP_OBJECT_TYPE_PLUGIN,
            MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
            return (status);
        }
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    if (is_zero_oid(pluginOid)) {
        for (i = 0; i < number_of_plugins; i++) {
            if (plugintable[i].hdlPlugin != NULL) {
            PassFunc = (MP_RegisterForObjectVisibilityChangesPluginFn)
                dlsym(plugintable[i].hdlPlugin,
                "MP_RegisterForObjectVisibilityChangesPlugin");
            }

            if (PassFunc != NULL) {
                status = PassFunc(pClientFn, objectType, pCallerData);
                /* ignore an error and continue. */
            }
        }
    } else {
            index = pluginOid.ownerId - 1;
            if (plugintable[index].hdlPlugin != NULL) {
            PassFunc = (MP_RegisterForObjectVisibilityChangesPluginFn)
                dlsym(plugintable[index].hdlPlugin,
                "MP_RegisterForObjectVisibilityChangesPlugin");
            }

            if (PassFunc != NULL) {
                status = PassFunc(pClientFn, objectType, pCallerData);
            }
    }
    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);

}

/**
 *******************************************************************************
 *
 * Deregisters a previously registered client function that is to be invoked
 * whenever a high level object appears or disappears.
 *
 * @param  pClientFn,
 *      A pointer to an MP_OBJECT_VISIBILITY_FN function defined by the
 *      client that was previously registered using
 *      the MP_RegisterForObjectVisibilityChanges API. On successful return
 *      this function will no longer be called to inform the client of
 *      object property changes.
 *
 * @param  objectType
 *      The type of object the client wishes to deregister for visibility
 *      change callbacks. If null, then all objects types are
 *      deregistered.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the operation is successful.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pClientFn is NULL or specifies a memory area
 *      that is not executable.
 *
 * @retval MP_STATUS_UNKNOWN_FN
 *      Returned if pClientFn is not the same as the previously registered
 *      function.
 *
 * @retval MP_STATUS_INVALID_OBJECT_TYPE
 *          Returned if objectType does not specify any valid object type.
 *
 * @retval MP_STATUS_FAILED
 *          Returned if pClientFn deregistration is not possible at this time.
 *
 *******************************************************************************
 */
MP_STATUS MP_DeregisterForObjectVisibilityChanges(
    MP_OBJECT_VISIBILITY_FN pClientFn,
    MP_OBJECT_TYPE objectType,
    MP_OID pluginOid)
{
    MP_DeregisterForObjectVisibilityChangesPluginFn PassFunc;
    MP_UINT32 i;
    MP_UINT32 index;
    MP_STATUS status;

    if (pClientFn == NULL) {
        return (MP_STATUS_INVALID_PARAMETER);
    }

    if (objectType > MP_OBJECT_TYPE_MAX) {
        return (MP_STATUS_INVALID_OBJECT_TYPE);
    }

    if (!(is_zero_oid(pluginOid))) {
        if ((status = validate_object(pluginOid, MP_OBJECT_TYPE_PLUGIN,
            MP_OBJECT_TYPE_MATCH)) != MP_STATUS_SUCCESS) {
            return (status);
        }
    }

    (void) pthread_mutex_lock(&mp_lib_mutex);

    if (is_zero_oid(pluginOid)) {
        for (i = 0; i < number_of_plugins; i++) {
            if (plugintable[i].hdlPlugin != NULL) {
                PassFunc = (MP_DeregisterForObjectVisibilityChangesPluginFn)
                    dlsym(plugintable[i].hdlPlugin,
                    "MP_DeregisterForObjectVisibilityChangesPlugin");
                if (PassFunc != NULL) {
                    status = PassFunc(pClientFn, objectType);
                }
            }
        }
    } else  {
            index = pluginOid.ownerId - 1;
            if (plugintable[index].hdlPlugin != NULL) {
                PassFunc = (MP_DeregisterForObjectVisibilityChangesPluginFn)
                    dlsym(plugintable[index].hdlPlugin,
                    "MP_DeregisterForObjectVisibilityChangesPlugin");
                if (PassFunc != NULL) {
                    status = PassFunc(pClientFn, objectType);
                }
            }
    }

    (void) pthread_mutex_unlock(&mp_lib_mutex);
    return (status);
}

/**
 *******************************************************************************
 *
 * Compare two Oids for equality to see whether they refer to the same object.
 *
 * @param  oid1
 *          Oid to compare.
 *
 * @param  oid2
 *          Oid to compare.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when the two Oids do refer to the same object.
 *
 * @retval MP_STATUS_FAILED
 *      Returned if the Oids don't compare.
 *
 *******************************************************************************
 */
MP_STATUS MP_CompareOIDs(
        MP_OID oid1,
    MP_OID oid2)
{
    if ((oid1.objectType == oid2.objectType) && (oid1.ownerId == oid2.ownerId)
        && (oid1.objectSequenceNumber == oid2.objectSequenceNumber)) {
        return (MP_STATUS_SUCCESS);
    } else {
        return (MP_STATUS_FAILED);
    }
}

/**
 *******************************************************************************
 *
 * Frees memory returned by an MP API.
 *
 * @param  pOidList
 *      A pointer to the memory returned by an MP API. On successful
        return, the allocated memory is freed.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when pPluginId is deregistered successfully.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pMemory is NULL or specifies a memory area to which
 *      data cannot be written.
 *
 *******************************************************************************
 */
MP_STATUS MP_FreeOidList(MP_OID_LIST *pOidList)
{
        if (pOidList == NULL) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        free(pOidList);

        return (MP_STATUS_SUCCESS);
}

static MP_CHAR *HDR =
"#\n"
"# This file contains names and references to MP API plugin libraries\n"
"#\n"
"#  Do NOT manually edit this file\n"
"#\n"
"# Format:\n"
"#\n"
"# <library ID>  <library pathname>\n"
"#\n";

#define CLEANUP_N_RET(fd, ret)  \
        if (lock_register(fd, F_SETLK, F_UNLCK, 0, SEEK_SET, 0) < 0) { \
                close(fd); \
                return (MP_STATUS_FAILED); \
        } \
        close(fd); \
        return (ret)

/*
 * This function sets an advisory lock on the file pointed to by the argument
 * fd, which is a file descriptor. The lock is set using fcntl() which uses
 * flock structure.
 */
static int
lock_register(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
    struct flock lock;

    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;

    return (fcntl(fd, cmd, &lock));
}

/*
 * This function searches for "srch_str" (of length "slen") in "buf" (of length
 * "buflen"). If it is not found, "write_offset" has the offset in "buf" where
 * "srch_str" would have to be added in "buf". If "srch_str" is found in "buf",
 * "write_offset" has its offset in "buf"
 *
 * ARGUMENTS :
 * buf          - buffer to search in
 * buflen       - length of buffer
 * srch_id      - id to search
 * id_len       - length of srch_id
 * write_offset - Set in function on exit
 *              - It is the offset in buf where srch_str is or should be
 * bytes_left   - Set in function on exit
 *              - It is the # of bytes left beyond write_offset in buf
 * RETURN VALUES :
 * Zero - "srch_id" found in "buf"... "write_offset" has offset in "buf"
 * != 0 - "srch_str" NOT found in "buf" ... "write_offset" points to the end of
 *          "buf".
 */
static int
search_line(MP_CHAR *buf, size_t buflen, MP_CHAR *srch_id, size_t id_len,
                int *write_offset, int *bytes_left)
{
        int     retval, sizeof_conf_hdr = strlen(HDR);
        MP_CHAR *sol;           /* Pointer to Start-Of-Line */
        MP_CHAR *cur_pos;       /* current position */

        *bytes_left = buflen;
        *write_offset = 0;

        if (buf == NULL || buflen <= 0)
                return (-1);

        if (srch_id == NULL || id_len <= 0)
                return (0);

        sol = cur_pos = buf;

        /*
         * mp conf file should not be edited but takes care of
         * any extra white space when parsing the line.
         *
         * The line should have id + delimiter + name + newline.
         */
        while (*bytes_left >= (id_len + 3)) {
            /* skip leading blank or space. */
            while ((*cur_pos == ' ') || (*cur_pos == '\t')) {
                cur_pos++;
            }

            if (strncmp(cur_pos, srch_id, id_len) == 0) {
                /* id matched. */
                cur_pos += id_len;

                while (*cur_pos != '\n') {
                    cur_pos++;
                }
                *write_offset = (sol - buf);
                *bytes_left = buflen - ((cur_pos + 1) - buf);
                return (0);
            } else {
                /* move to the next line */
                while (*cur_pos != '\n') {
                    cur_pos++;
                }
                *bytes_left = buflen - ((cur_pos + 1) - buf);
            }
            sol = cur_pos = cur_pos + 1;
        }

        /* Given strings are not found. */
        *write_offset = buflen;
        return (-1);
}

/**
 *******************************************************************************
 *
 * Registers a plugin with common library.  The implementation of this routine
 * is based on configuration file /etc/mpapi.conf that contains a list of
 * plugin libraries.
 *
 * @param  pPluginId
 *          A pointer to the key name shall be the reversed domain name of
 *          the vendor followed by followed by the vendor specific name for
 *          the plugin that uniquely identifies the plugin.  Should be NULL
 *          terminated.
 *
 * @param  pFileName
 *          The full path to the plugin library.
 *          Should be NULL terminated.
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when pPluginId is deregistered successfully.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pPluginId is NULL or specifies a memory area that
 *      is not executable.
 *
 * @retval MP_STATUS_FAILED
 *          Returned if pClientFn deregistration is not possible at this time.
 *
 *******************************************************************************
 */
MP_STATUS MP_RegisterPlugin(
        MP_WCHAR *pPluginId,
        char *pFileName)
{
        int mpconf, bytes_left, write_offset;
        MP_CHAR fullline[MAX_LINE_SIZE]; /* Full line to add to mpapi.conf */
        MP_CHAR *mpconf_buf;
        MP_CHAR pluginid[MAX_NAME_SIZE];
        char systemPath[MAX_NAME_SIZE], mpConfFilePath[MAX_NAME_SIZE];
        MP_UINT32   new_file_flag = 0;
        MP_UINT32   sizeof_conf_hdr = strlen(HDR);
        struct stat     stbuf;

        if ((pPluginId == NULL) || (pFileName == NULL)) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        if (stat(pFileName, &stbuf) != 0) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        if (wcstombs(pluginid, pPluginId, MAX_NAME_SIZE) != wcslen(pPluginId)) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        *fullline = '\0';
        strncpy(fullline, pluginid, MAX_NAME_SIZE);
        /* add tab */
        strncat(fullline, "\t", MAX_LINE_SIZE - strlen(pluginid));
        strncat(fullline, pFileName, MAX_LINE_SIZE - strlen(pluginid) - 1);
        /* add a new line. */
        strncat(fullline, "\n",
            MAX_LINE_SIZE - strlen(pluginid) - strlen(pFileName) -1);

        /* Open configuration file from known location */
        strncpy(mpConfFilePath, "/etc/mpapi.conf", MAX_NAME_SIZE);

        if ((chmod(mpConfFilePath, S_IRUSR|S_IRGRP|S_IROTH) == -1) &&
                (errno == ENOENT))  {
            new_file_flag = 1;
        }

        if ((mpconf = open(mpConfFilePath, O_RDWR | O_CREAT)) == -1) {
                return (MP_STATUS_FAILED);
        }

        if (fchmod(mpconf, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
            close(mpconf);
            return (MP_STATUS_FAILED);
        }

        if (lock_register(mpconf, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) {
            close(mpconf);
            return (MP_STATUS_FAILED);
        }

        if (fstat(mpconf, &stbuf) == -1) {
            CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
        }

        if ((new_file_flag) || (stbuf.st_size == 0)) {
            if (write(mpconf, HDR, sizeof_conf_hdr) !=
                sizeof_conf_hdr) {
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            }

            if (pwrite(mpconf, fullline, strlen(fullline),
                sizeof_conf_hdr) !=
                strlen(fullline)) {
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            }
            CLEANUP_N_RET(mpconf, MP_STATUS_SUCCESS);
        }

        if ((mpconf_buf = (MP_CHAR *)mmap(0, stbuf.st_size,
                PROT_READ | PROT_WRITE,
                MAP_SHARED, mpconf, 0)) == MAP_FAILED) {
            CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
        }

        if (search_line(mpconf_buf, stbuf.st_size,
            pluginid, strlen(pluginid), &write_offset, &bytes_left) == 0) {
            /* found a match. */
            munmap((void *)mpconf_buf, stbuf.st_size);
            CLEANUP_N_RET(mpconf, MP_STATUS_SUCCESS);
        } else {
            munmap((void *)mpconf_buf, stbuf.st_size);
            /* append the fullline to the mpconf. */
            if (pwrite(mpconf, fullline, strlen(fullline),
                write_offset) !=
                strlen(fullline)) {
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            } else {
                CLEANUP_N_RET(mpconf, MP_STATUS_SUCCESS);
            }
        }
}

/**
 *******************************************************************************
 *
 * Deregisters a plugin from the common library.  This routine is based on
 * configuration file /etc/mpapi.conf that contains a list of plugin libraries.
 *
 * @param  pPluginId
 *      A pointer to a Plugin ID previously registered using
 *      the MP_RegisterPlugin API..
 *
 * @return An MP_STATUS indicating if the operation was successful or if
 *         an error occurred.
 *
 * @retval MP_STATUS_SUCCESS
 *         Returned when pPluginId is deregistered successfully.
 *
 * @retval MP_STATUS_INVALID_PARAMETER
 *      Returned if pPluginId is NULL or specifies a memory area that
 *      is not executable.
 *
 * @retval MP_STATUS_FAILED
 *          Returned if pClientFn deregistration is not possible at this time.
 *
 *******************************************************************************
 */
MP_STATUS MP_DeregisterPlugin(
    MP_WCHAR *pPluginId)
{
        int mpconf, tmp_mpconf, bytes_left, write_offset;
        char systemPath[MAX_NAME_SIZE], mpConfFilePath[MAX_NAME_SIZE],
            tmp_mpConfFilePath[MAX_NAME_SIZE + sizeof(pid_t)];
        MP_CHAR    pluginid[MAX_NAME_SIZE];
        MP_CHAR    *mpconf_buf;
        MP_UINT32   sizeof_conf_hdr = strlen(HDR);
        struct stat     stbuf;

        if (pPluginId == NULL) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        if (wcstombs(pluginid, pPluginId, MAX_NAME_SIZE) != wcslen(pPluginId)) {
            return (MP_STATUS_INVALID_PARAMETER);
        }

        /* Open configuration file from known location */
        strncpy(mpConfFilePath, "/etc/mpapi.conf", MAX_NAME_SIZE);

        if ((chmod(mpConfFilePath, S_IRUSR|S_IRGRP|S_IROTH) == -1) &&
                (errno == ENOENT))  {
            /* no file found */
            return (MP_STATUS_UNKNOWN_FN);
        }

        if ((mpconf = open(mpConfFilePath, O_RDWR)) == -1) {
                return (MP_STATUS_FAILED);
        }

        if (fchmod(mpconf, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
            close(mpconf);
            return (MP_STATUS_FAILED);
        }

        if (lock_register(mpconf, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) {
            close(mpconf);
            return (MP_STATUS_FAILED);
        }

        if (fstat(mpconf, &stbuf) == -1) {
            CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
        }

        if (stbuf.st_size == 0) {
            CLEANUP_N_RET(mpconf, MP_STATUS_SUCCESS);
        }

        if ((mpconf_buf = (MP_CHAR *)mmap(0, stbuf.st_size,
                PROT_READ | PROT_WRITE,
                MAP_SHARED, mpconf, 0)) == MAP_FAILED) {
            CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
        }

        if (search_line(mpconf_buf, stbuf.st_size, pluginid, strlen(pluginid),
                &write_offset, &bytes_left) != 0) {
            munmap((void *)mpconf_buf, stbuf.st_size);
            CLEANUP_N_RET(mpconf, MP_STATUS_UNKNOWN_FN);
        } else {
            /*
             * found a match.
             * construct temp file name using pid.
             */
            (void) snprintf(tmp_mpConfFilePath, MAX_NAME_SIZE,
                "%s%ld", "/etc/mpapi.conf", getpid());

            if ((tmp_mpconf = open(tmp_mpConfFilePath,
                O_RDWR|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            }

            if (write(tmp_mpconf, mpconf_buf, write_offset) != write_offset) {
                close(tmp_mpconf);
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            }

            if (pwrite(tmp_mpconf, mpconf_buf + (stbuf.st_size - bytes_left),
                bytes_left, write_offset) != bytes_left) {
                close(tmp_mpconf);
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            }

            close(tmp_mpconf);
            munmap((void *)mpconf_buf, stbuf.st_size);

            /* rename temp file to mpConfFile before unlock and close. */
            if (rename(tmp_mpConfFilePath, mpConfFilePath) != 0) {
                CLEANUP_N_RET(mpconf, MP_STATUS_FAILED);
            } else {
                CLEANUP_N_RET(mpconf, MP_STATUS_SUCCESS);
            }
        }
}