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