1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <atomic.h>
28 #include <errno.h>
29 #include <execinfo.h>
30 #include <libuutil.h>
31 #include <pthread.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <syslog.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 
40 #include "conditions.h"
41 #include "events.h"
42 #include "objects.h"
43 #include "util.h"
44 
45 /*
46  * events.c - contains routines which create/destroy event sources,
47  * handle the event queue and process events from that queue.
48  */
49 
50 /* Add new event sources here. */
51 struct nwamd_event_source {
52 	char *name;
53 	void (*events_init)(void);
54 	void (*events_fini)(void);
55 } event_sources[] = {
56 	{ "routing_events",
57 	nwamd_routing_events_init, nwamd_routing_events_fini },
58 	{ "sysevent_events",
59 	nwamd_sysevent_events_init, nwamd_sysevent_events_fini },
60 };
61 
62 /* Counter for event ids */
63 static uint64_t event_id_counter = 0;
64 
65 static uu_list_pool_t *event_pool = NULL;
66 static uu_list_t *event_queue = NULL;
67 static pthread_mutex_t event_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
68 static pthread_cond_t event_queue_cond = PTHREAD_COND_INITIALIZER;
69 
70 static int nwamd_event_compare(const void *, const void *, void *);
71 
72 static const char *
73 nwamd_event_name(int event_type)
74 {
75 	if (event_type <= NWAM_EVENT_MAX)
76 		return (nwam_event_type_to_string(event_type));
77 
78 	switch (event_type) {
79 	case NWAM_EVENT_TYPE_OBJECT_INIT:
80 		return ("OBJECT_INIT");
81 	case NWAM_EVENT_TYPE_OBJECT_FINI:
82 		return ("OBJECT_FINI");
83 	case NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS:
84 		return ("TIMED_CHECK_CONDITIONS");
85 	case NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS:
86 		return ("TRIGGERED_CHECK_CONDITIONS");
87 	case NWAM_EVENT_TYPE_NCU_CHECK:
88 		return ("NCU_CHECK");
89 	case NWAM_EVENT_TYPE_TIMER:
90 		return ("TIMER");
91 	case NWAM_EVENT_TYPE_UPGRADE:
92 		return ("UPGRADE");
93 	case NWAM_EVENT_TYPE_PERIODIC_SCAN:
94 		return ("PERIODIC_SCAN");
95 	case NWAM_EVENT_TYPE_QUEUE_QUIET:
96 		return ("QUEUE_QUIET");
97 	default:
98 		return ("N/A");
99 	}
100 }
101 
102 void
103 nwamd_event_sources_init(void)
104 {
105 	int i;
106 
107 	/*
108 	 * Now we can safely initialize event sources.
109 	 */
110 	for (i = 0;
111 	    i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
112 	    i++) {
113 		if (event_sources[i].events_init != NULL)
114 			event_sources[i].events_init();
115 	}
116 }
117 
118 void
119 nwamd_event_sources_fini(void)
120 {
121 	int i;
122 
123 	for (i = 0;
124 	    i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
125 	    i++) {
126 		if (event_sources[i].events_init != NULL)
127 			event_sources[i].events_fini();
128 	}
129 }
130 
131 /*
132  * Comparison function for events, passed in as callback to
133  * uu_list_pool_create(). Compare by time, so that timer
134  * event queue can be sorted by nearest time to present.
135  */
136 /* ARGSUSED */
137 static int
138 nwamd_event_compare(const void *l_arg, const void *r_arg, void *private)
139 {
140 	nwamd_event_t l = (nwamd_event_t)l_arg;
141 	nwamd_event_t r = (nwamd_event_t)r_arg;
142 	int rv;
143 
144 	rv = l->event_time.tv_sec - r->event_time.tv_sec;
145 	if (rv == 0)
146 		rv = l->event_time.tv_nsec - r->event_time.tv_nsec;
147 
148 	return (rv);
149 }
150 
151 void
152 nwamd_event_queue_init(void)
153 {
154 	event_pool = uu_list_pool_create("event_queue_pool",
155 	    sizeof (struct nwamd_event),
156 	    offsetof(struct nwamd_event, event_node),
157 	    nwamd_event_compare, UU_LIST_POOL_DEBUG);
158 	if (event_pool == NULL)
159 		pfail("uu_list_pool_create failed with error %d", uu_error());
160 	event_queue = uu_list_create(event_pool, NULL, UU_LIST_SORTED);
161 	if (event_queue == NULL)
162 		pfail("uu_list_create failed with error %d", uu_error());
163 }
164 
165 void
166 nwamd_event_queue_fini(void)
167 {
168 	void *cookie = NULL;
169 	nwamd_event_t event;
170 
171 	while ((event = uu_list_teardown(event_queue, &cookie)) != NULL)
172 		nwamd_event_fini(event);
173 	uu_list_destroy(event_queue);
174 	if (event_pool != NULL)
175 		uu_list_pool_destroy(event_pool);
176 }
177 
178 nwamd_event_t
179 nwamd_event_init(int32_t type, nwam_object_type_t object_type,
180     size_t size, const char *object_name)
181 {
182 	nwamd_event_t event;
183 
184 	event = calloc(1, sizeof (struct nwamd_event));
185 	if (event == NULL) {
186 		nlog(LOG_ERR, "nwamd_event_init: could not create %s event for "
187 		    "object %s", nwamd_event_name(type),
188 		    object_name != NULL ? object_name : "<no object>");
189 		return (NULL);
190 	}
191 
192 	/* Is this an externally-visible event? */
193 	if (type <= NWAM_EVENT_MAX) {
194 		event->event_send = B_TRUE;
195 		event->event_msg = calloc(1, sizeof (struct nwam_event) + size);
196 		if (event->event_msg == NULL) {
197 			nlog(LOG_ERR,
198 			    "nwamd_event_init: could not create %s event",
199 			    nwamd_event_name(type));
200 			free(event);
201 			return (NULL);
202 		}
203 		event->event_msg->nwe_type = type;
204 		event->event_msg->nwe_size = sizeof (struct nwam_event) + size;
205 	} else {
206 		event->event_send = B_FALSE;
207 		event->event_msg = NULL;
208 	}
209 
210 	event->event_type = type;
211 
212 	if (object_name != NULL) {
213 		(void) strlcpy(event->event_object, object_name,
214 		    NWAM_MAX_NAME_LEN);
215 		event->event_object_type = object_type;
216 	} else {
217 		event->event_object[0] = '\0';
218 	}
219 
220 	/* Set event id */
221 	event->event_id = atomic_add_64_nv(&event_id_counter, 1);
222 	(void) clock_gettime(CLOCK_REALTIME, &event->event_time);
223 
224 	return (event);
225 }
226 
227 void
228 nwamd_event_do_not_send(nwamd_event_t event)
229 {
230 	nlog(LOG_DEBUG, "nwamd_event_do_not_send: cancelling delivery of "
231 	    "event %s for object %s", nwamd_event_name(event->event_type),
232 	    event->event_object[0] != '\0' ?
233 	    event->event_object : "<no object>");
234 	event->event_send = B_FALSE;
235 }
236 
237 void
238 nwamd_event_fini(nwamd_event_t event)
239 {
240 	if (event != NULL) {
241 		free(event->event_msg);
242 		free(event);
243 	}
244 }
245 
246 nwamd_event_t
247 nwamd_event_init_object_action(nwam_object_type_t object_type,
248     const char *object_name, const char *parent_name,
249     nwam_action_t object_action)
250 {
251 	nwamd_event_t event;
252 
253 	event = nwamd_event_init(NWAM_EVENT_TYPE_OBJECT_ACTION,
254 	    object_type, 0, object_name);
255 	if (event == NULL)
256 		return (NULL);
257 
258 	event->event_msg->nwe_data.nwe_object_action.nwe_action = object_action;
259 	event->event_msg->nwe_data.nwe_object_action.nwe_object_type =
260 	    object_type;
261 	(void) strlcpy(event->event_msg->nwe_data.nwe_object_action.nwe_name,
262 	    object_name,
263 	    sizeof (event->event_msg->nwe_data.nwe_object_action.nwe_name));
264 	if (parent_name == NULL) {
265 		event->event_msg->nwe_data.nwe_object_action.nwe_parent[0] =
266 		    '\0';
267 		return (event);
268 	}
269 	(void) strlcpy
270 	    (event->event_msg->nwe_data.nwe_object_action.nwe_parent,
271 	    parent_name,
272 	    sizeof (event->event_msg->nwe_data.nwe_object_action.nwe_parent));
273 	return (event);
274 }
275 
276 nwamd_event_t
277 nwamd_event_init_object_state(nwam_object_type_t object_type,
278     const char *object_name, nwam_state_t state, nwam_aux_state_t aux_state)
279 {
280 	nwamd_event_t event;
281 
282 	event = nwamd_event_init(NWAM_EVENT_TYPE_OBJECT_STATE,
283 	    object_type, 0, object_name);
284 	if (event == NULL)
285 		return (NULL);
286 
287 	event->event_msg->nwe_data.nwe_object_state.nwe_state = state;
288 	event->event_msg->nwe_data.nwe_object_state.nwe_aux_state = aux_state;
289 	event->event_msg->nwe_data.nwe_object_state.nwe_object_type =
290 	    object_type;
291 	(void) strlcpy(event->event_msg->nwe_data.nwe_object_state.nwe_name,
292 	    object_name,
293 	    sizeof (event->event_msg->nwe_data.nwe_object_state.nwe_name));
294 
295 	return (event);
296 }
297 
298 nwamd_event_t
299 nwamd_event_init_priority_group_change(int64_t priority)
300 {
301 	nwamd_event_t event;
302 
303 	event = nwamd_event_init(NWAM_EVENT_TYPE_PRIORITY_GROUP,
304 	    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
305 	if (event == NULL)
306 		return (NULL);
307 
308 	event->event_msg->nwe_data.nwe_priority_group_info.nwe_priority =
309 	    priority;
310 
311 	return (event);
312 }
313 
314 nwamd_event_t
315 nwamd_event_init_link_action(const char *name, nwam_action_t link_action)
316 {
317 	nwamd_event_t event;
318 	nwam_error_t err;
319 	char *object_name;
320 
321 	if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
322 	    &object_name)) != NWAM_SUCCESS) {
323 		nlog(LOG_ERR, "nwamd_event_init_link_action: "
324 		    "nwam_ncu_name_to_typed_name: %s",
325 		    nwam_strerror(err));
326 		return (NULL);
327 	}
328 	event = nwamd_event_init(NWAM_EVENT_TYPE_LINK_ACTION,
329 	    NWAM_OBJECT_TYPE_NCU, 0, object_name);
330 	free(object_name);
331 	if (event == NULL)
332 		return (NULL);
333 
334 	(void) strlcpy(event->event_msg->nwe_data.nwe_link_action.nwe_name,
335 	    name,
336 	    sizeof (event->event_msg->nwe_data.nwe_link_action.nwe_name));
337 	event->event_msg->nwe_data.nwe_link_action.nwe_action = link_action;
338 
339 	return (event);
340 }
341 
342 nwamd_event_t
343 nwamd_event_init_link_state(const char *name, boolean_t up)
344 {
345 	nwamd_event_t event;
346 	nwam_error_t err;
347 	char *object_name;
348 
349 	if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
350 	    &object_name)) != NWAM_SUCCESS) {
351 		nlog(LOG_ERR, "nwamd_event_init_link_state: "
352 		    "nwam_ncu_name_to_typed_name: %s",
353 		    nwam_strerror(err));
354 		return (NULL);
355 	}
356 
357 	event = nwamd_event_init(NWAM_EVENT_TYPE_LINK_STATE,
358 	    NWAM_OBJECT_TYPE_NCU, 0, object_name);
359 	free(object_name);
360 	if (event == NULL)
361 		return (NULL);
362 
363 	(void) strlcpy(event->event_msg->nwe_data.nwe_link_state.nwe_name, name,
364 	    sizeof (event->event_msg->nwe_data.nwe_link_state.nwe_name));
365 	event->event_msg->nwe_data.nwe_link_state.nwe_link_up = up;
366 
367 	return (event);
368 }
369 
370 nwamd_event_t
371 nwamd_event_init_if_state(const char *linkname, uint32_t flags,
372     uint32_t addr_added, uint32_t index, struct sockaddr *addr)
373 {
374 	nwamd_event_t event;
375 	nwam_error_t err;
376 	char *object_name;
377 
378 	/* linkname does not contain the lifnum */
379 	if ((err = nwam_ncu_name_to_typed_name(linkname,
380 	    NWAM_NCU_TYPE_INTERFACE, &object_name)) != NWAM_SUCCESS) {
381 		nlog(LOG_ERR, "nwamd_event_init_if_state: "
382 		    "nwam_ncu_name_to_typed_name: %s",
383 		    nwam_strerror(err));
384 		return (NULL);
385 	}
386 
387 	event = nwamd_event_init(NWAM_EVENT_TYPE_IF_STATE,
388 	    NWAM_OBJECT_TYPE_NCU, 0, object_name);
389 	free(object_name);
390 	if (event == NULL)
391 		return (NULL);
392 
393 	(void) strlcpy(event->event_msg->nwe_data.nwe_if_state.nwe_name,
394 	    linkname,
395 	    sizeof (event->event_msg->nwe_data.nwe_if_state.nwe_name));
396 	event->event_msg->nwe_data.nwe_if_state.nwe_flags = flags;
397 	event->event_msg->nwe_data.nwe_if_state.nwe_index = index;
398 	event->event_msg->nwe_data.nwe_if_state.nwe_addr_added = addr_added;
399 	event->event_msg->nwe_data.nwe_if_state.nwe_addr_valid = (addr != NULL);
400 
401 	if (addr != NULL) {
402 		bcopy(addr, &(event->event_msg->nwe_data.nwe_if_state.nwe_addr),
403 		    addr->sa_family == AF_INET ? sizeof (struct sockaddr_in) :
404 		    sizeof (struct sockaddr_in6));
405 	}
406 
407 	return (event);
408 }
409 
410 nwamd_event_t
411 nwamd_event_init_wlan(const char *name, int32_t type, boolean_t connected,
412     nwam_wlan_t *wlans, uint_t num_wlans)
413 {
414 	size_t size = 0;
415 	char *object_name;
416 	nwamd_event_t event;
417 	nwam_error_t err;
418 
419 	switch (type) {
420 	case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
421 	case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
422 		size = sizeof (nwam_wlan_t) * (num_wlans - 1);
423 		break;
424 	case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
425 	case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
426 		break;
427 	default:
428 		nlog(LOG_ERR, "nwamd_event_init_wlan: unexpected "
429 		    "event type %s (%d)", nwamd_event_name(type), type);
430 		return (NULL);
431 	}
432 	if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
433 	    &object_name)) != NWAM_SUCCESS) {
434 		nlog(LOG_ERR, "nwamd_event_init_wlan: "
435 		    "nwam_ncu_name_to_typed_name: %s",
436 		    nwam_strerror(err));
437 		return (NULL);
438 	}
439 
440 	event = nwamd_event_init(type, NWAM_OBJECT_TYPE_NCU, size, object_name);
441 	free(object_name);
442 	if (event == NULL)
443 		return (NULL);
444 
445 	(void) strlcpy(event->event_msg->nwe_data.nwe_wlan_info.nwe_name, name,
446 	    sizeof (event->event_msg->nwe_data.nwe_wlan_info.nwe_name));
447 	event->event_msg->nwe_data.nwe_wlan_info.nwe_connected = connected;
448 	event->event_msg->nwe_data.nwe_wlan_info.nwe_num_wlans = num_wlans;
449 
450 	/* copy the wlans */
451 	(void) memcpy(event->event_msg->nwe_data.nwe_wlan_info.nwe_wlans, wlans,
452 	    num_wlans * sizeof (nwam_wlan_t));
453 
454 	return (event);
455 }
456 
457 nwamd_event_t
458 nwamd_event_init_ncu_check(void)
459 {
460 	return (nwamd_event_init(NWAM_EVENT_TYPE_NCU_CHECK,
461 	    NWAM_OBJECT_TYPE_NCP, 0, NULL));
462 }
463 
464 nwamd_event_t
465 nwamd_event_init_init(void)
466 {
467 	return (nwamd_event_init(NWAM_EVENT_TYPE_INIT,
468 	    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
469 }
470 
471 nwamd_event_t
472 nwamd_event_init_shutdown(void)
473 {
474 	return (nwamd_event_init(NWAM_EVENT_TYPE_SHUTDOWN,
475 	    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
476 }
477 
478 /*
479  * Add event to the event list.
480  */
481 void
482 nwamd_event_enqueue(nwamd_event_t event)
483 {
484 	nwamd_event_enqueue_timed(event, 0);
485 }
486 
487 /*
488  * Schedule an event to be added to the event list for future processing.
489  * The event will be scheduled in delta_seconds seconds mod schedule delay and
490  * time resolution.
491  */
492 void
493 nwamd_event_enqueue_timed(nwamd_event_t event, int delta_seconds)
494 {
495 	uu_list_index_t idx;
496 
497 	nlog(LOG_DEBUG, "enqueueing event %lld %d (%s) for object %s in %ds",
498 	    event->event_id, event->event_type,
499 	    nwamd_event_name(event->event_type),
500 	    event->event_object[0] != 0 ?  event->event_object : "none",
501 	    delta_seconds);
502 
503 	(void) clock_gettime(CLOCK_REALTIME, &event->event_time);
504 	event->event_time.tv_sec += delta_seconds;
505 
506 	uu_list_node_init(event, &event->event_node, event_pool);
507 
508 	(void) pthread_mutex_lock(&event_queue_mutex);
509 
510 	/*
511 	 * Find appropriate location to insert the event based on time.
512 	 */
513 	(void) uu_list_find(event_queue, event, NULL, &idx);
514 	(void) uu_list_insert(event_queue, event, idx);
515 
516 	(void) pthread_cond_signal(&event_queue_cond);
517 	(void) pthread_mutex_unlock(&event_queue_mutex);
518 }
519 
520 /*
521  * Is the specified event enqueued on the event (or pending event queue)
522  * for execution in when seconds? An object may be specified also.
523  */
524 boolean_t
525 nwamd_event_enqueued(int32_t event_type, nwam_object_type_t object_type,
526     const char *object)
527 {
528 	nwamd_event_t event;
529 
530 	(void) pthread_mutex_lock(&event_queue_mutex);
531 	for (event = uu_list_first(event_queue);
532 	    event != NULL;
533 	    event = uu_list_next(event_queue, event)) {
534 		if (event->event_type != event_type)
535 			continue;
536 		if (object_type != NWAM_OBJECT_TYPE_UNKNOWN &&
537 		    event->event_object_type != object_type)
538 			continue;
539 		if (object != NULL && strcmp(object, event->event_object) != 0)
540 			continue;
541 		(void) pthread_mutex_unlock(&event_queue_mutex);
542 		return (B_TRUE);
543 	}
544 	(void) pthread_mutex_unlock(&event_queue_mutex);
545 
546 	return (B_FALSE);
547 }
548 
549 /*
550  * Is the time in the past.
551  */
552 static boolean_t
553 in_past(struct timespec t)
554 {
555 	struct timespec now;
556 
557 	(void) clock_gettime(CLOCK_REALTIME, &now);
558 	if (t.tv_sec < now.tv_sec)
559 		return (B_TRUE);
560 	if (t.tv_sec > now.tv_sec)
561 		return (B_FALSE);
562 	if (t.tv_nsec < now.tv_nsec)
563 		return (B_TRUE);
564 	return (B_FALSE);
565 }
566 
567 /*
568  * Remove event at head of event list for processing.  This takes a number of
569  * nanoseconds to wait.  If the number is 0 then it blocks.  If there is
570  * nothing on the queue then it returns an event which says that the queue
571  * is quiet.
572  */
573 static nwamd_event_t
574 nwamd_event_dequeue(long nsec)
575 {
576 	nwamd_event_t event;
577 
578 	(void) pthread_mutex_lock(&event_queue_mutex);
579 	event = uu_list_first(event_queue);
580 	if (event == NULL && nsec == 0) {
581 		do {
582 			(void) pthread_cond_wait(&event_queue_cond,
583 			    &event_queue_mutex);
584 		} while ((event = uu_list_first(event_queue)) == NULL);
585 	} else {
586 		struct timespec waitcap;
587 
588 		if (nsec != 0) {
589 			(void) clock_gettime(CLOCK_REALTIME, &waitcap);
590 			waitcap.tv_nsec += nsec;
591 			waitcap.tv_sec += NSEC_TO_SEC(waitcap.tv_nsec);
592 			waitcap.tv_nsec = NSEC_TO_FRACNSEC(waitcap.tv_nsec);
593 		}
594 
595 		/*
596 		 * Keep going as long as the first event hasn't matured and
597 		 * we havn't passed our maximum wait time.
598 		 */
599 		while ((event == NULL || !in_past(event->event_time)) &&
600 		    (nsec == 0 || !in_past(waitcap)))  {
601 			struct timespec eventwait;
602 
603 			/*
604 			 * Three cases:
605 			 *	no maximum waittime - just use the event
606 			 *	both an event and cap - take the least one
607 			 *	just a maximum waittime - use it
608 			 */
609 			if (nsec == 0) {
610 				eventwait = event->event_time;
611 			} else if (event != NULL) {
612 				uint64_t diff;
613 				diff = SEC_TO_NSEC(event->event_time.tv_sec -
614 				    waitcap.tv_sec) +
615 				    event->event_time.tv_nsec - waitcap.tv_nsec;
616 
617 				if (diff > 0)
618 					eventwait = waitcap;
619 				else
620 					eventwait = event->event_time;
621 			} else {
622 				/*
623 				 * Note that if the event is NULL then nsec is
624 				 * nonzero and waitcap is valid.
625 				 */
626 				eventwait = waitcap;
627 			}
628 
629 			(void) pthread_cond_timedwait(&event_queue_cond,
630 			    &event_queue_mutex, &eventwait);
631 			event = uu_list_first(event_queue);
632 		}
633 	}
634 
635 	/*
636 	 * At this point we've met the guard contition of the while loop.
637 	 * The event at the top of the queue might be mature in which case
638 	 * we use it.  Otherwise we hit our cap and we need to enqueue a
639 	 * quiesced queue event.
640 	 */
641 	if (event != NULL && in_past(event->event_time)) {
642 		uu_list_remove(event_queue, event);
643 		uu_list_node_fini(event, &event->event_node, event_pool);
644 	} else {
645 		event = nwamd_event_init(NWAM_EVENT_TYPE_QUEUE_QUIET,
646 		    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
647 	}
648 
649 	if (event != NULL)
650 		nlog(LOG_DEBUG,
651 		    "dequeueing event %lld of type %d (%s) for object %s",
652 		    event->event_id, event->event_type,
653 		    nwamd_event_name(event->event_type),
654 		    event->event_object[0] != 0 ?  event->event_object :
655 		    "none");
656 
657 	(void) pthread_mutex_unlock(&event_queue_mutex);
658 
659 	return (event);
660 }
661 
662 void
663 nwamd_event_send(nwam_event_t event_msg)
664 {
665 	nwam_error_t err;
666 
667 	if (shutting_down && event_msg->nwe_type != NWAM_EVENT_TYPE_SHUTDOWN) {
668 		nlog(LOG_DEBUG, "nwamd_event_send: tossing event as nwamd "
669 		    "is shutting down");
670 		return;
671 	}
672 
673 	err = nwam_event_send(event_msg);
674 
675 	if (err != NWAM_SUCCESS) {
676 		nlog(LOG_ERR, "nwamd_event_send: nwam_event_send: %s",
677 		    nwam_strerror(err));
678 	}
679 }
680 
681 /*
682  * Run state machine for object. Method is run if
683  * - event method is non-null
684  * - event method is valid for current object state (determined by
685  * ORing the current state against the set of valid states for the method).
686  *
687  * If these criteria are met, the method is run.
688  */
689 static void
690 nwamd_event_run_method(nwamd_event_t event)
691 {
692 	nwamd_event_method_t *event_methods;
693 	int i;
694 
695 	event_methods = nwamd_object_event_methods(event->event_object_type);
696 
697 	/* If we're shutting down, only fini events are accepted for objects */
698 	if (shutting_down && event->event_type != NWAM_EVENT_TYPE_OBJECT_FINI) {
699 		nlog(LOG_DEBUG, "nwamd_event_run_method: tossing non-fini "
700 		    "event %s for object %s",
701 		    nwamd_event_name(event->event_type), event->event_object);
702 		return;
703 	}
704 
705 	for (i = 0;
706 	    event_methods[i].event_type != NWAM_EVENT_TYPE_NOOP;
707 	    i++) {
708 		if (event_methods[i].event_type ==
709 		    event->event_type &&
710 		    event_methods[i].event_method != NULL) {
711 			nlog(LOG_DEBUG,
712 			    "(%p) %s: running method for event %s",
713 			    (void *)event, event->event_object,
714 			    nwamd_event_name(event->event_type));
715 			/* run method */
716 			event_methods[i].event_method(event);
717 			return;
718 		}
719 	}
720 	nlog(LOG_DEBUG, "(%p) %s: no matching method for event %d (%s)",
721 	    (void *)event, event->event_object, event->event_type,
722 	    nwamd_event_name(event->event_type));
723 }
724 
725 /*
726  * Called when we are checking to see what should be activated.  First activate
727  * all of the manual NCUs.  Then see if we can find a valid priority group.
728  * If we can, activate it.  Otherwise try all the priority groups starting
729  * with the lowest one that makes sense.
730  */
731 static void
732 nwamd_activate_ncus(void) {
733 	int64_t prio = INVALID_PRIORITY_GROUP;
734 	boolean_t selected;
735 
736 	nwamd_ncp_activate_manual_ncus();
737 	selected = nwamd_ncp_check_priority_group(&prio);
738 	if (selected) {
739 		/*
740 		 * Activate chosen priority group and stop anything going on in
741 		 * lesser priority groups.
742 		 */
743 		nwamd_ncp_activate_priority_group(prio);
744 		nwamd_ncp_deactivate_priority_group_all(prio + 1);
745 	} else {
746 		/*
747 		 * Nothing unique could be started so try them all.  Once one
748 		 * of them gets into a reasonable state then we will prune
749 		 * everything below it (see first part of this conditional).
750 		 */
751 		int64_t oldprio = INVALID_PRIORITY_GROUP;
752 		while (nwamd_ncp_find_next_priority_group(++oldprio, &prio)) {
753 			nwamd_ncp_activate_priority_group(prio);
754 			oldprio = prio;
755 		}
756 	}
757 }
758 
759 /*
760  * Event handler thread
761  *
762  * The complexity in this code comes about from wanting to delay the decision
763  * making process until after bursts of events.  Keep roughly polling (waiting
764  * for .1s) until we see the queue quiet event and then block.
765  */
766 void
767 nwamd_event_handler(void)
768 {
769 	boolean_t got_shutdown_event = B_FALSE;
770 	boolean_t check_conditions = B_FALSE;
771 	boolean_t ncu_check = B_FALSE;
772 	int queue_quiet_time = 0;
773 	nwamd_event_t event;
774 
775 	/*
776 	 * Dequeue events and process them.  In most cases, events have
777 	 * an assocated object type, and we use this to retrieve
778 	 * the function that will process the event.
779 	 */
780 	while (!got_shutdown_event) {
781 		event = nwamd_event_dequeue(queue_quiet_time);
782 		/* keep pulling events as long as they are close together */
783 		queue_quiet_time = SEC_TO_NSEC(1)/10;
784 
785 		/*
786 		 * This is an event with no associated object.
787 		 */
788 		if (event->event_object[0] == '\0') {
789 			switch (event->event_type) {
790 			case NWAM_EVENT_TYPE_NOOP:
791 			case NWAM_EVENT_TYPE_INIT:
792 				/*
793 				 * The only action for an INIT event
794 				 * is to relay it to event listeners,
795 				 * which is done below.
796 				 */
797 				break;
798 			case NWAM_EVENT_TYPE_PRIORITY_GROUP:
799 				(void) pthread_mutex_lock(&active_ncp_mutex);
800 				current_ncu_priority_group =
801 				    event->event_msg->nwe_data.
802 				    nwe_priority_group_info.nwe_priority;
803 				(void) pthread_mutex_unlock(&active_ncp_mutex);
804 				break;
805 			case NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS:
806 				if (!shutting_down) {
807 					nwamd_set_timed_check_all_conditions();
808 					check_conditions = B_TRUE;
809 				}
810 				break;
811 			case NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS:
812 				if (!shutting_down)
813 					check_conditions = B_TRUE;
814 				break;
815 			case NWAM_EVENT_TYPE_NCU_CHECK:
816 				if (!shutting_down)
817 					ncu_check = B_TRUE;
818 				break;
819 			case NWAM_EVENT_TYPE_UPGRADE:
820 				if (!shutting_down) {
821 					/*
822 					 * Upgrade events have no associated
823 					 * object.
824 					 */
825 					nwamd_event_run_method(event);
826 				}
827 				break;
828 			case NWAM_EVENT_TYPE_SHUTDOWN:
829 				got_shutdown_event = B_TRUE;
830 				break;
831 
832 			/*
833 			 * We want to delay processing of condition and ncu
834 			 * checking until after short bursts of events.  So we
835 			 * keep track of times we've scheduled checking and
836 			 * wait for the queue to quiesce.
837 			 */
838 			case NWAM_EVENT_TYPE_QUEUE_QUIET:
839 				queue_quiet_time = 0; /* now we can block */
840 				if (!shutting_down && check_conditions) {
841 					nwamd_check_all_conditions();
842 					check_conditions = B_FALSE;
843 				}
844 
845 				if (!shutting_down && ncu_check) {
846 					nwamd_activate_ncus();
847 					ncu_check = B_FALSE;
848 				}
849 				break;
850 
851 			default:
852 				nlog(LOG_ERR,
853 				    "event %d (%s)had no object associated "
854 				    "with it", event->event_type,
855 				    nwamd_event_name(event->event_type));
856 				break;
857 			}
858 		} else {
859 			/*
860 			 * Event has an associated object - run event method
861 			 * for that object type (if any).
862 			 */
863 			nwamd_event_run_method(event);
864 		}
865 		/*
866 		 * Send associated message to listeners if event type is
867 		 * externally visible.
868 		 */
869 		if (event->event_send)
870 			nwamd_event_send(event->event_msg);
871 
872 		nwamd_event_fini(event);
873 	}
874 	/* If we get here, we got a shutdown event. */
875 	nwamd_event_queue_fini();
876 	nwamd_object_lists_fini();
877 }
878