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. */
50 struct 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 */
62 static uint64_t event_id_counter = 0;
63 
64 static uu_list_pool_t *event_pool = NULL;
65 static uu_list_t *event_queue = NULL;
66 static pthread_mutex_t event_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
67 static pthread_cond_t event_queue_cond = PTHREAD_COND_INITIALIZER;
68 
69 static int nwamd_event_compare(const void *, const void *, void *);
70 
71 static const char *
nwamd_event_name(int event_type)72 nwamd_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 
101 void
nwamd_event_sources_init(void)102 nwamd_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 
117 void
nwamd_event_sources_fini(void)118 nwamd_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 */
136 static int
nwamd_event_compare(const void * l_arg,const void * r_arg,void * private)137 nwamd_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 
150 void
nwamd_event_queue_init(void)151 nwamd_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 
164 void
nwamd_event_queue_fini(void)165 nwamd_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 
177 nwamd_event_t
nwamd_event_init(int32_t type,nwam_object_type_t object_type,size_t size,const char * object_name)178 nwamd_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 
226 void
nwamd_event_do_not_send(nwamd_event_t event)227 nwamd_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 
236 void
nwamd_event_fini(nwamd_event_t event)237 nwamd_event_fini(nwamd_event_t event)
238 {
239 	if (event != NULL) {
240 		free(event->event_msg);
241 		free(event);
242 	}
243 }
244 
245 nwamd_event_t
nwamd_event_init_object_action(nwam_object_type_t object_type,const char * object_name,const char * parent_name,nwam_action_t object_action)246 nwamd_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 
275 nwamd_event_t
nwamd_event_init_object_state(nwam_object_type_t object_type,const char * object_name,nwam_state_t state,nwam_aux_state_t aux_state)276 nwamd_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 
297 nwamd_event_t
nwamd_event_init_priority_group_change(int64_t priority)298 nwamd_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 
313 nwamd_event_t
nwamd_event_init_link_action(const char * name,nwam_action_t link_action)314 nwamd_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 
341 nwamd_event_t
nwamd_event_init_link_state(const char * name,boolean_t up)342 nwamd_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 
369 nwamd_event_t
nwamd_event_init_if_state(const char * linkname,uint32_t flags,uint32_t addr_added,struct sockaddr * addr,struct sockaddr * netmask)370 nwamd_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 
414 nwamd_event_t
nwamd_event_init_wlan(const char * name,int32_t type,boolean_t connected,nwam_wlan_t * wlans,uint_t num_wlans)415 nwamd_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 
461 nwamd_event_t
nwamd_event_init_ncu_check(void)462 nwamd_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 
468 nwamd_event_t
nwamd_event_init_init(void)469 nwamd_event_init_init(void)
470 {
471 	return (nwamd_event_init(NWAM_EVENT_TYPE_INIT,
472 	    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
473 }
474 
475 nwamd_event_t
nwamd_event_init_shutdown(void)476 nwamd_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  */
485 void
nwamd_event_enqueue(nwamd_event_t event)486 nwamd_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  */
496 void
nwamd_event_enqueue_timed(nwamd_event_t event,int delta_seconds)497 nwamd_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  */
528 boolean_t
nwamd_event_enqueued(int32_t event_type,nwam_object_type_t object_type,const char * object)529 nwamd_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  */
556 static boolean_t
in_past(struct timespec t)557 in_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  */
577 static nwamd_event_t
nwamd_event_dequeue(long nsec)578 nwamd_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 
666 void
nwamd_event_send(nwam_event_t event_msg)667 nwamd_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  */
693 static void
nwamd_event_run_method(nwamd_event_t event)694 nwamd_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  */
735 static void
nwamd_activate_ncus(void)736 nwamd_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  */
770 void
nwamd_event_handler(void)771 nwamd_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