/*
* 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
* 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
*/
/*
*/
#include <atomic.h>
#include <errno.h>
#include <execinfo.h>
#include <libuutil.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <unistd.h>
#include "conditions.h"
#include "events.h"
#include "objects.h"
#include "util.h"
/*
* handle the event queue and process events from that queue.
*/
/* Add new event sources here. */
struct nwamd_event_source {
char *name;
void (*events_init)(void);
void (*events_fini)(void);
} event_sources[] = {
{ "routing_events",
{ "sysevent_events",
};
/* Counter for event ids */
static int nwamd_event_compare(const void *, const void *, void *);
static const char *
{
if (event_type <= NWAM_EVENT_MAX)
return (nwam_event_type_to_string(event_type));
switch (event_type) {
return ("OBJECT_INIT");
return ("OBJECT_FINI");
return ("TIMED_CHECK_CONDITIONS");
return ("TRIGGERED_CHECK_CONDITIONS");
return ("NCU_CHECK");
case NWAM_EVENT_TYPE_TIMER:
return ("TIMER");
case NWAM_EVENT_TYPE_UPGRADE:
return ("UPGRADE");
return ("PERIODIC_SCAN");
return ("QUEUE_QUIET");
default:
return ("N/A");
}
}
void
nwamd_event_sources_init(void)
{
int i;
/*
* Now we can safely initialize event sources.
*/
for (i = 0;
i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
i++) {
event_sources[i].events_init();
}
}
void
nwamd_event_sources_fini(void)
{
int i;
for (i = 0;
i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
i++) {
event_sources[i].events_fini();
}
}
/*
* Comparison function for events, passed in as callback to
* uu_list_pool_create(). Compare by time, so that timer
* event queue can be sorted by nearest time to present.
*/
/* ARGSUSED */
static int
{
int rv;
if (rv == 0)
return (rv);
}
void
nwamd_event_queue_init(void)
{
sizeof (struct nwamd_event),
if (event_pool == NULL)
if (event_queue == NULL)
}
void
nwamd_event_queue_fini(void)
{
if (event_pool != NULL)
}
{
return (NULL);
}
/* Is this an externally-visible event? */
if (type <= NWAM_EVENT_MAX) {
"nwamd_event_init: could not create %s event",
return (NULL);
}
} else {
}
if (object_name != NULL) {
} else {
}
/* Set event id */
return (event);
}
void
{
}
void
{
}
}
const char *object_name, const char *parent_name,
{
object_type, 0, object_name);
return (NULL);
if (parent_name == NULL) {
'\0';
return (event);
}
(void) strlcpy
return (event);
}
{
object_type, 0, object_name);
return (NULL);
return (event);
}
{
NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
return (NULL);
return (event);
}
{
char *object_name;
&object_name)) != NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name: %s",
nwam_strerror(err));
return (NULL);
}
return (NULL);
name,
return (event);
}
{
char *object_name;
&object_name)) != NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name: %s",
nwam_strerror(err));
return (NULL);
}
return (NULL);
return (event);
}
{
char *object_name;
/* linkname does not contain the lifnum */
"nwam_ncu_name_to_typed_name: %s",
nwam_strerror(err));
return (NULL);
}
return (NULL);
sizeof (struct sockaddr_in6));
}
sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6));
}
return (event);
}
{
char *object_name;
switch (type) {
break;
break;
default:
return (NULL);
}
&object_name)) != NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name: %s",
nwam_strerror(err));
return (NULL);
}
return (NULL);
/* copy the wlans */
num_wlans * sizeof (nwam_wlan_t));
return (event);
}
{
NWAM_OBJECT_TYPE_NCP, 0, NULL));
}
nwamd_event_init_init(void)
{
return (nwamd_event_init(NWAM_EVENT_TYPE_INIT,
NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
}
{
return (nwamd_event_init(NWAM_EVENT_TYPE_SHUTDOWN,
NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
}
/*
* Add event to the event list.
*/
void
{
}
/*
* Schedule an event to be added to the event list for future processing.
* The event will be scheduled in delta_seconds seconds mod schedule delay and
* time resolution.
*/
void
{
(void) pthread_mutex_lock(&event_queue_mutex);
/*
* Find appropriate location to insert the event based on time.
*/
(void) pthread_cond_signal(&event_queue_cond);
(void) pthread_mutex_unlock(&event_queue_mutex);
}
/*
* Is the specified event enqueued on the event (or pending event queue)
* for execution in when seconds? An object may be specified also.
*/
const char *object)
{
(void) pthread_mutex_lock(&event_queue_mutex);
continue;
if (object_type != NWAM_OBJECT_TYPE_UNKNOWN &&
continue;
continue;
(void) pthread_mutex_unlock(&event_queue_mutex);
return (B_TRUE);
}
(void) pthread_mutex_unlock(&event_queue_mutex);
return (B_FALSE);
}
/*
* Is the time in the past.
*/
static boolean_t
{
return (B_TRUE);
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
}
/*
* Remove event at head of event list for processing. This takes a number of
* nanoseconds to wait. If the number is 0 then it blocks. If there is
* nothing on the queue then it returns an event which says that the queue
* is quiet.
*/
static nwamd_event_t
{
(void) pthread_mutex_lock(&event_queue_mutex);
do {
(void) pthread_cond_wait(&event_queue_cond,
} else {
if (nsec != 0) {
}
/*
* Keep going as long as the first event hasn't matured and
* we havn't passed our maximum wait time.
*/
/*
* Three cases:
* no maximum waittime - just use the event
* both an event and cap - take the least one
* just a maximum waittime - use it
*/
if (nsec == 0) {
if (diff > 0)
else
} else {
/*
* Note that if the event is NULL then nsec is
* nonzero and waitcap is valid.
*/
}
(void) pthread_cond_timedwait(&event_queue_cond,
}
}
/*
* At this point we've met the guard contition of the while loop.
* The event at the top of the queue might be mature in which case
* we use it. Otherwise we hit our cap and we need to enqueue a
* quiesced queue event.
*/
} else {
NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
}
"dequeueing event %lld of type %d (%s) for object %s",
"none");
(void) pthread_mutex_unlock(&event_queue_mutex);
return (event);
}
void
{
"is shutting down");
return;
}
if (err != NWAM_SUCCESS) {
nwam_strerror(err));
}
}
/*
* Run state machine for object. Method is run if
* - event method is non-null
* - event method is valid for current object state (determined by
* ORing the current state against the set of valid states for the method).
*
* If these criteria are met, the method is run.
*/
static void
{
int i;
/* If we're shutting down, only fini events are accepted for objects */
"event %s for object %s",
return;
}
for (i = 0;
i++) {
if (event_methods[i].event_type ==
event->event_type &&
"(%p) %s: running method for event %s",
/* run method */
return;
}
}
}
/*
* Called when we are checking to see what should be activated. First activate
* all of the manual NCUs. Then see if we can find a valid priority group.
* If we can, activate it. Otherwise try all the priority groups starting
* with the lowest one that makes sense.
*/
static void
nwamd_activate_ncus(void) {
if (selected) {
/*
* Activate chosen priority group and stop anything going on in
* lesser priority groups.
*/
} else {
/*
* Nothing unique could be started so try them all. Once one
* of them gets into a reasonable state then we will prune
* everything below it (see first part of this conditional).
*/
}
}
}
/*
* Event handler thread
*
* The complexity in this code comes about from wanting to delay the decision
* making process until after bursts of events. Keep roughly polling (waiting
* for .1s) until we see the queue quiet event and then block.
*/
void
nwamd_event_handler(void)
{
int queue_quiet_time = 0;
/*
* Dequeue events and process them. In most cases, events have
* an assocated object type, and we use this to retrieve
* the function that will process the event.
*/
while (!got_shutdown_event) {
/* keep pulling events as long as they are close together */
/*
* This is an event with no associated object.
*/
switch (event->event_type) {
case NWAM_EVENT_TYPE_NOOP:
case NWAM_EVENT_TYPE_INIT:
/*
* The only action for an INIT event
* is to relay it to event listeners,
* which is done below.
*/
break;
(void) pthread_mutex_lock(&active_ncp_mutex);
(void) pthread_mutex_unlock(&active_ncp_mutex);
break;
if (!shutting_down) {
}
break;
if (!shutting_down)
break;
if (!shutting_down)
break;
case NWAM_EVENT_TYPE_UPGRADE:
if (!shutting_down) {
/*
* Upgrade events have no associated
* object.
*/
}
break;
case NWAM_EVENT_TYPE_SHUTDOWN:
break;
/*
* We want to delay processing of condition and ncu
* checking until after short bursts of events. So we
* keep track of times we've scheduled checking and
* wait for the queue to quiesce.
*/
queue_quiet_time = 0; /* now we can block */
if (!shutting_down && check_conditions) {
}
if (!shutting_down && ncu_check) {
}
break;
default:
"event %d (%s)had no object associated "
break;
}
} else {
/*
* Event has an associated object - run event method
* for that object type (if any).
*/
}
/*
* Send associated message to listeners if event type is
* externally visible.
*/
if (event->event_send)
}
/* If we get here, we got a shutdown event. */
}