OpenSolaris_b135/lib/sun_fc/common/FCSyseventBridge.cc

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */



#include "FCSyseventBridge.h"
#include "Exceptions.h"
#include "Trace.h"
#include "AdapterAddEvent.h"
#include "AdapterEvent.h"
#include "AdapterPortEvent.h"
#include "AdapterDeviceEvent.h"
#include "TargetEvent.h"
#include "sun_fc.h"
#include <libnvpair.h>
#include <iostream>

using namespace std;

FCSyseventBridge* FCSyseventBridge::_instance = NULL;

FCSyseventBridge* FCSyseventBridge::getInstance() {
    Trace log("FCSyseventBridge::getInstance");
    if (_instance == NULL) {
	_instance = new FCSyseventBridge();
    }
    return (_instance);

}


void FCSyseventBridge::addListener(AdapterAddEventListener *listener) {
    lock();
    try {
	adapterAddEventListeners.insert(adapterAddEventListeners.begin(),
		listener);
	validateRegistration();
	unlock();
    } catch (...) {
	unlock();
	throw;
    }
}
void FCSyseventBridge::addListener(AdapterEventListener *listener, HBA *hba) {
    lock();
    try {
	adapterEventListeners.insert(adapterEventListeners.begin(), listener);
	validateRegistration();
	unlock();
    } catch (...) {
	unlock();
	throw;
    }
}
void FCSyseventBridge::addListener(AdapterPortEventListener *listener,
	    HBAPort *port) {
    lock();
    try {
	adapterPortEventListeners.insert(adapterPortEventListeners.begin(),
		listener);
	validateRegistration();
	unlock();
    } catch (...) {
	unlock();
	throw;
    }
}
void FCSyseventBridge::addListener(AdapterDeviceEventListener *listener,
    HBAPort *port) {
	lock();
	try {
		adapterDeviceEventListeners.insert(adapterDeviceEventListeners.begin(),
		    listener);
		validateRegistration();
		unlock();
	} catch (...) {
		unlock();
		throw;
	}
}
void FCSyseventBridge::addListener(TargetEventListener *listener,
	    HBAPort *port, uint64_t targetWWN, bool filter) {
    lock();
    try {
	targetEventListeners.insert(targetEventListeners.begin(), listener);
	validateRegistration();
	unlock();
    } catch (...) {
	unlock();
	throw;
    }
}

void FCSyseventBridge::removeListener(AdapterAddEventListener *listener) {
    lock();
    try {
	typedef vector<AdapterAddEventListener *>::iterator Iter;
	for (Iter tmp = adapterAddEventListeners.begin();
		tmp != adapterAddEventListeners.end(); tmp++) {
	    if (*tmp == listener) {
		adapterAddEventListeners.erase(tmp);
		unlock();
		return;
	    }
	}
	throw InvalidHandleException();
    } catch (...) {
	unlock();
	throw;
    }
}

void FCSyseventBridge::removeListener(AdapterEventListener *listener) {
    lock();
    try {
	typedef vector<AdapterEventListener *>::iterator Iter;
	for (Iter tmp = adapterEventListeners.begin();
		tmp != adapterEventListeners.end(); tmp++) {
	    if (*tmp == listener) {
		adapterEventListeners.erase(tmp);
		unlock();
		return;
	    }
	}
	throw InvalidHandleException();
    } catch (...) {
	unlock();
	throw;
    }
}

void FCSyseventBridge::removeListener(AdapterPortEventListener *listener) {
    lock();
    try {
	typedef vector<AdapterPortEventListener *>::iterator Iter;
	for (Iter tmp = adapterPortEventListeners.begin();
		tmp != adapterPortEventListeners.end(); tmp++) {
	    if (*tmp == listener) {
		adapterPortEventListeners.erase(tmp);
		unlock();
		return;
	    }
	}
	throw InvalidHandleException();
    } catch (...) {
	unlock();
	throw;
    }
}

void FCSyseventBridge::removeListener(AdapterDeviceEventListener *listener) {
	lock();
	try {
		typedef vector<AdapterDeviceEventListener *>::iterator Iter;
		for (Iter tmp = adapterDeviceEventListeners.begin();
		    tmp != adapterDeviceEventListeners.end(); tmp++) {
			if (*tmp == listener) {
				adapterDeviceEventListeners.erase(tmp);
				unlock();
				return;
			}
		}
		throw InvalidHandleException();
	} catch (...) {
		unlock();
		throw;
	}
}
 
void FCSyseventBridge::removeListener(TargetEventListener *listener) {
    lock();
    try {
	typedef vector<TargetEventListener *>::iterator Iter;
	for (Iter tmp = targetEventListeners.begin();
		tmp != targetEventListeners.end(); tmp++) {
	    if (*tmp == listener) {
		targetEventListeners.erase(tmp);
		unlock();
		return;
	    }
	}
	throw InvalidHandleException();
    } catch (...) {
	unlock();
	throw;
    }
}

extern "C" void static_dispatch(sysevent_t *ev) {
    Trace log("static_dispatch");
    FCSyseventBridge::getInstance()->dispatch(ev);
}

void FCSyseventBridge::dispatch(sysevent_t *ev) {
    Trace log("FCSyseventBridge::dispatch");
    nvlist_t		    *list = NULL;
    hrtime_t			when;

    if (ev == NULL) {
	log.debug("Null event.");
	return;
    }

    if (sysevent_get_attr_list(ev, &list) || list == NULL) {
	log.debug("Empty event.");
	return;
    }

    string eventVendor = sysevent_get_vendor_name(ev);
    string eventPublisher = sysevent_get_pub_name(ev);
    string eventClass = sysevent_get_class_name(ev);
    string eventSubClass = sysevent_get_subclass_name(ev);

    sysevent_get_time(ev, &when);

    // Now that we know what type of event it is, handle it accordingly
    if (eventClass == "EC_sunfc") {

	// All events of this class type have instance and port-wwn for
	// the HBA port.
	uint32_t	instance;
	if (nvlist_lookup_uint32(list, (char *)"instance",
		&instance)) {
	    log.genericIOError(
		"Improperly formed event: no instance field.");
	    nvlist_free(list);
	    return;
	}
	uchar_t		*rawPortWWN;
	uint32_t	rawPortWWNLength;

	if (nvlist_lookup_byte_array(list, (char *)"port-wwn",
		&rawPortWWN, &rawPortWWNLength)) {
	    log.genericIOError(
		"Improperly formed event: no port-wwn field.");
	    nvlist_free(list);
	    return;
	}

	// Now deal with the specific details of each subclass type
	if (eventSubClass == "ESC_sunfc_port_offline") {

	    // Create event instance
	    AdapterPortEvent event(
		wwnConversion(rawPortWWN),
		AdapterPortEvent::OFFLINE,
		0);

	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<AdapterPortEventListener *>::iterator Iter;
		for (Iter tmp = adapterPortEventListeners.begin();
			tmp != adapterPortEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();

	} else if (eventSubClass == "ESC_sunfc_port_online") {

	    // Create event instance
	    AdapterPortEvent event(
		wwnConversion(rawPortWWN),
		AdapterPortEvent::ONLINE,
		0);

	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<AdapterPortEventListener *>::iterator Iter;
		for (Iter tmp = adapterPortEventListeners.begin();
			tmp != adapterPortEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();

	} else if (eventSubClass == "ESC_sunfc_device_online") {
		AdapterDeviceEvent event(
		    wwnConversion(rawPortWWN),
		    AdapterDeviceEvent::ONLINE,
		    0);
		lock();
		try {
			typedef vector<AdapterDeviceEventListener *>::iterator Iter;
			for (Iter tmp = adapterDeviceEventListeners.begin();
			    tmp != adapterDeviceEventListeners.end(); tmp++) {
				(*tmp)->dispatch(event);
			}
		} catch (...) {
			unlock();
			nvlist_free(list);
			throw;
		}
		unlock();

	} else if (eventSubClass == "ESC_sunfc_device_offline") {
		AdapterDeviceEvent event(
		    wwnConversion(rawPortWWN),
		    AdapterDeviceEvent::OFFLINE,
		    0);
		lock();
		try {
			typedef vector<AdapterDeviceEventListener *>::iterator Iter;
			for (Iter tmp = adapterDeviceEventListeners.begin();
			    tmp != adapterDeviceEventListeners.end(); tmp++) {
				(*tmp)->dispatch(event);
			}
		} catch (...) {
			unlock();
			nvlist_free(list);
			throw;
		}
		unlock();

	} else if (eventSubClass == "ESC_sunfc_port_rscn") {
	    /*
	     * RSCNs are a little tricky.  There can be multiple
	     * affected page properties, each numbered.  To make sure
	     * we get them all, we loop through all properties
	     * in the nvlist and if their name begins with "affected_page_"
	     * then we send an event for them.
	     */
	    uint32_t	affected_page;
	    nvpair_t    *attr = NULL;
	    for (attr = nvlist_next_nvpair(list, NULL);
		    attr != NULL;
		    attr = nvlist_next_nvpair(list, attr)) {
		string name = nvpair_name(attr);
		if (name.find("affected_page_") != name.npos) {

		    if (nvpair_value_uint32(attr, &affected_page)) {
			log.genericIOError(
			    "Improperly formed event: "
			    "corrupt affected_page field");
			continue;
		    }
		    // Create event instance
		    AdapterPortEvent event(
			wwnConversion(rawPortWWN),
			AdapterPortEvent::FABRIC,
			affected_page);

		    // Dispatch to interested parties.
		    lock();
		    typedef vector<AdapterPortEventListener *>::iterator Iter;
		    try {
			for (Iter tmp = adapterPortEventListeners.begin();
				tmp != adapterPortEventListeners.end(); tmp++) {
			    (*tmp)->dispatch(event);
			}
		    } catch (...) {
			unlock();
			nvlist_free(list);
			throw;
		    }
		    unlock();
		}
	    }
	} else if (eventSubClass == "ESC_sunfc_target_add") {
	    uchar_t	*rawTargetPortWWN;
	    uint32_t	rawTargetPortWWNLength;

	    if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn",
		    &rawTargetPortWWN, &rawTargetPortWWNLength)) {
		log.genericIOError(
		    "Improperly formed event: no target-port-wwn field.");
		nvlist_free(list);
		return;
	    }

	    // Create event instance
	    AdapterPortEvent event(
		wwnConversion(rawPortWWN),
		AdapterPortEvent::NEW_TARGETS,
		0);

	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<AdapterPortEventListener *>::iterator Iter;
		for (Iter tmp = adapterPortEventListeners.begin();
			tmp != adapterPortEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();
	} else if (eventSubClass == "ESC_sunfc_target_remove") {
	    uchar_t	*rawTargetPortWWN;
	    uint32_t	rawTargetPortWWNLength;

	    if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn",
		    &rawTargetPortWWN, &rawTargetPortWWNLength)) {
		log.genericIOError(
		    "Improperly formed event: no target-port-wwn field.");
		nvlist_free(list);
		return;
	    }
	    // Create event instance
	    TargetEvent event(
		wwnConversion(rawPortWWN),
		wwnConversion(rawTargetPortWWN),
		TargetEvent::REMOVED);

	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<TargetEventListener *>::iterator Iter;
		for (Iter tmp = targetEventListeners.begin();
			tmp != targetEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();
	} else if (eventSubClass == "ESC_sunfc_port_attach") {
	    // Create event instance
	    AdapterAddEvent event(wwnConversion(rawPortWWN));
	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<AdapterAddEventListener *>::iterator Iter;
		for (Iter tmp = adapterAddEventListeners.begin();
			tmp != adapterAddEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();
	} else if (eventSubClass == "ESC_sunfc_port_detach") {
	    // Technically, we should probably try to coalesce
	    // all detach events for the same multi-ported adapter
	    // and only send one event to the client, but for now,
	    // we'll just blindly send duplicates.

	    // Create event instance
	    AdapterEvent event(
		wwnConversion(rawPortWWN),
		AdapterEvent::REMOVE);

	    // Dispatch to interested parties.
	    lock();
	    try {
		typedef vector<AdapterEventListener *>::iterator Iter;
		for (Iter tmp = adapterEventListeners.begin();
			tmp != adapterEventListeners.end(); tmp++) {
		    (*tmp)->dispatch(event);
		}
	    } catch (...) {
		unlock();
		nvlist_free(list);
		throw;
	    }
	    unlock();

	} else {
	    log.genericIOError(
		    "Unrecognized subclass \"%s\": Ignoring event",
		    eventSubClass.c_str());
	}
    } else {
	// This should not happen, as we only asked for specific classes.
	log.genericIOError(
		"Unrecognized class \"%s\": Ignoring event",
		eventClass.c_str());
    }
    nvlist_free(list);
}

void FCSyseventBridge::validateRegistration() {
    Trace log("FCSyseventBridge::validateRegistration");
    uint64_t count = 0;
    count = adapterAddEventListeners.size() +
	    adapterEventListeners.size() +
	    adapterPortEventListeners.size() +
	    targetEventListeners.size();
    if (count == 1) {
	handle = sysevent_bind_handle(static_dispatch);
	if (handle == NULL) {
	    log.genericIOError(
		"Unable to bind sysevent handle.");
	    return;
	}
	const char *subclass_list[9] = {
		"ESC_sunfc_port_attach",
		"ESC_sunfc_port_detach",
		"ESC_sunfc_port_offline",
		"ESC_sunfc_port_online",
		"ESC_sunfc_port_rscn",
		"ESC_sunfc_target_add",
		"ESC_sunfc_target_remove",
		"ESC_sunfc_device_online",
		"ESC_sunfc_device_offline"
	    };
	if (sysevent_subscribe_event(handle,
		"EC_sunfc", (const char **)subclass_list, 9)) {
	    log.genericIOError(
		"Unable to subscribe to sun_fc events.");
	    sysevent_unbind_handle(handle);
	    handle = NULL;
	}
    } else if (count == 0 && handle != NULL) {
	// Remove subscription
	sysevent_unbind_handle(handle);
	handle == NULL;
    } // Else do nothing
}

int32_t FCSyseventBridge::getMaxListener() {
    return (INT_MAX);
}