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 <assert.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <pthread.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <syslog.h>
34 #include <libuutil.h>
35 #include <errno.h>
36 
37 #include "events.h"
38 #include "objects.h"
39 #include "util.h"
40 
41 /*
42  * objects.c - contains routines which manipulate object lists of NCUs,
43  * locations, ENMs and known WLANs.
44  */
45 
46 typedef struct nwamd_object_list {
47 	nwam_object_type_t object_type;
48 	uu_list_t *object_list;
49 	nwamd_event_method_t *object_event_methods;
50 	pthread_rwlock_t object_list_lock;
51 } nwamd_object_list_t;
52 
53 nwamd_event_method_t enm_event_methods[] =
54 {
55 	{ NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_enm_handle_init_event },
56 	{ NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_enm_handle_fini_event },
57 	{ NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_enm_handle_action_event },
58 	{ NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_enm_handle_state_event },
59 	{ NWAM_EVENT_TYPE_NOOP, NULL }
60 };
61 
62 nwamd_event_method_t loc_event_methods[] =
63 {
64 	{ NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_loc_handle_init_event },
65 	{ NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_loc_handle_fini_event },
66 	{ NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_loc_handle_action_event },
67 	{ NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_loc_handle_state_event },
68 	{ NWAM_EVENT_TYPE_NOOP, NULL }
69 };
70 
71 nwamd_event_method_t ncu_event_methods[] =
72 {
73 	{ NWAM_EVENT_TYPE_IF_STATE, nwamd_ncu_handle_if_state_event },
74 	{ NWAM_EVENT_TYPE_IF_ACTION, nwamd_ncu_handle_if_action_event },
75 	{ NWAM_EVENT_TYPE_LINK_STATE, nwamd_ncu_handle_link_state_event },
76 	{ NWAM_EVENT_TYPE_LINK_ACTION, nwamd_ncu_handle_link_action_event },
77 	{ NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_ncu_handle_init_event },
78 	{ NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_ncu_handle_fini_event },
79 	{ NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_ncu_handle_action_event },
80 	{ NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_ncu_handle_state_event },
81 	{ NWAM_EVENT_TYPE_PERIODIC_SCAN, nwamd_ncu_handle_periodic_scan_event },
82 	{ NWAM_EVENT_TYPE_NOOP, NULL }
83 };
84 
85 nwamd_event_method_t ncp_event_methods[] =
86 {
87 	{ NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_ncp_handle_action_event },
88 	{ NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_ncp_handle_state_event },
89 	{ NWAM_EVENT_TYPE_UPGRADE, nwamd_handle_upgrade },
90 	{ NWAM_EVENT_TYPE_NOOP, NULL }
91 };
92 
93 nwamd_event_method_t known_wlan_event_methods[] =
94 {
95 	{ NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_known_wlan_handle_init_event },
96 	{ NWAM_EVENT_TYPE_OBJECT_FINI, NULL },
97 	{ NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_known_wlan_handle_action_event },
98 	{ NWAM_EVENT_TYPE_NOOP, NULL }
99 };
100 
101 /* Should be kept in same order as object types */
102 nwamd_object_list_t object_lists[] = {
103 	{ NWAM_OBJECT_TYPE_NCP, NULL, ncp_event_methods,
104 	PTHREAD_RWLOCK_INITIALIZER },
105 	{ NWAM_OBJECT_TYPE_NCU, NULL, ncu_event_methods,
106 	PTHREAD_RWLOCK_INITIALIZER },
107 	{ NWAM_OBJECT_TYPE_LOC, NULL, loc_event_methods,
108 	PTHREAD_RWLOCK_INITIALIZER },
109 	{ NWAM_OBJECT_TYPE_ENM, NULL, enm_event_methods,
110 	PTHREAD_RWLOCK_INITIALIZER },
111 	{ NWAM_OBJECT_TYPE_KNOWN_WLAN, NULL, known_wlan_event_methods,
112 	PTHREAD_RWLOCK_INITIALIZER }
113 };
114 
115 uu_list_pool_t *object_list_pool = NULL;
116 
117 /*
118  * Comparison function for objects, passed in as callback to
119  * uu_list_pool_create().
120  */
121 /* ARGSUSED */
122 static int
nwamd_object_compare(const void * l_arg,const void * r_arg,void * private)123 nwamd_object_compare(const void *l_arg, const void *r_arg, void *private)
124 {
125 	nwamd_object_t l = (nwamd_object_t)l_arg;
126 	nwamd_object_t r = (nwamd_object_t)r_arg;
127 	int rv;
128 
129 	(void) pthread_mutex_lock(&l->nwamd_object_mutex);
130 	if (l != r)
131 		(void) pthread_mutex_lock(&r->nwamd_object_mutex);
132 
133 	rv = strcmp(l->nwamd_object_name, r->nwamd_object_name);
134 	if (l != r)
135 		(void) pthread_mutex_unlock(&r->nwamd_object_mutex);
136 	(void) pthread_mutex_unlock(&l->nwamd_object_mutex);
137 
138 	return (rv);
139 }
140 
141 void
nwamd_object_lists_init(void)142 nwamd_object_lists_init(void)
143 {
144 	int i;
145 
146 	object_list_pool = uu_list_pool_create("object_list_pool",
147 	    sizeof (struct nwamd_object),
148 	    offsetof(struct nwamd_object, nwamd_object_node),
149 	    nwamd_object_compare, UU_LIST_POOL_DEBUG);
150 	if (object_list_pool == NULL)
151 		pfail("uu_list_pool_create failed with error %d", uu_error());
152 
153 	for (i = 0;
154 	    i < sizeof (object_lists) / sizeof (struct nwamd_object_list);
155 	    i++) {
156 		object_lists[i].object_list = uu_list_create(object_list_pool,
157 		    NULL, 0);
158 		if (object_lists[i].object_list == NULL)
159 			pfail("uu_list_create failed with error %d",
160 			    uu_error());
161 	}
162 }
163 
164 void
nwamd_object_lists_fini(void)165 nwamd_object_lists_fini(void)
166 {
167 	int i;
168 	nwamd_object_t object;
169 	void *cookie = NULL;
170 
171 	for (i = 0;
172 	    i < sizeof (object_lists) / sizeof (struct nwamd_object_list);
173 	    i++) {
174 		while ((object = uu_list_teardown(object_lists[i].object_list,
175 		    &cookie)) != NULL) {
176 			free(object);
177 		}
178 		uu_list_destroy(object_lists[i].object_list);
179 	}
180 	if (object_list_pool != NULL)
181 		uu_list_pool_destroy(object_list_pool);
182 }
183 
184 static nwamd_object_list_t *
nwamd_get_object_list(nwam_object_type_t type)185 nwamd_get_object_list(nwam_object_type_t type)
186 {
187 	assert(type < sizeof (object_lists) / sizeof (object_lists[0]));
188 	return (&object_lists[type]);
189 }
190 
191 static int
nwamd_object_list_lock(nwam_object_type_t type)192 nwamd_object_list_lock(nwam_object_type_t type)
193 {
194 	nwamd_object_list_t *object_list = nwamd_get_object_list(type);
195 
196 	(void) pthread_rwlock_wrlock(&object_list->object_list_lock);
197 	return (0);
198 }
199 
200 static int
nwamd_object_list_rlock(nwam_object_type_t type)201 nwamd_object_list_rlock(nwam_object_type_t type)
202 {
203 	nwamd_object_list_t *object_list = nwamd_get_object_list(type);
204 
205 	if (pthread_rwlock_rdlock(&object_list->object_list_lock) == -1) {
206 		nlog(LOG_ERR, "cannot get lock for object list: %s",
207 		    strerror(errno));
208 		return (-1);
209 	}
210 	return (0);
211 }
212 
213 static void
nwamd_object_list_unlock(nwam_object_type_t type)214 nwamd_object_list_unlock(nwam_object_type_t type)
215 {
216 	nwamd_object_list_t *object_list = nwamd_get_object_list(type);
217 
218 	(void) pthread_rwlock_unlock(&object_list->object_list_lock);
219 }
220 
221 /*
222  * Initialize object and return it in locked state.
223  */
224 nwamd_object_t
nwamd_object_init(nwam_object_type_t type,const char * name,void * handle,void * data)225 nwamd_object_init(nwam_object_type_t type, const char *name, void *handle,
226     void *data)
227 {
228 	nwamd_object_t object;
229 	struct nwamd_object_list *object_list = nwamd_get_object_list(type);
230 
231 	object = calloc(1, sizeof (struct nwamd_object));
232 	if (object == NULL)
233 		return (NULL);
234 
235 	(void) strlcpy(object->nwamd_object_name, name, NWAM_MAX_NAME_LEN);
236 
237 	/* 1 for the list and 1 for the returned object */
238 	object->nwamd_object_refcount = 2;
239 	object->nwamd_object_handle = handle;
240 	object->nwamd_object_data = data;
241 	object->nwamd_object_type = type;
242 	object->nwamd_object_state = NWAM_STATE_INITIALIZED;
243 	object->nwamd_object_aux_state = NWAM_AUX_STATE_INITIALIZED;
244 
245 	/* Add object to appropriate object list */
246 	if (nwamd_object_list_lock(type) != 0) {
247 		nlog(LOG_ERR, "nwamd_object_init: could not lock list to init "
248 		    "object %s", name);
249 		free(object);
250 		return (NULL);
251 	}
252 
253 	if (pthread_mutex_init(&object->nwamd_object_mutex, NULL) == -1) {
254 		nlog(LOG_ERR, "pthread_mutex_init failed: %s",
255 		    strerror(errno));
256 		free(object);
257 		nwamd_object_list_unlock(type);
258 		return (NULL);
259 	}
260 	(void) pthread_mutex_lock(&object->nwamd_object_mutex);
261 
262 	uu_list_node_init(object, &object->nwamd_object_node, object_list_pool);
263 	(void) uu_list_insert_after(object_list->object_list,
264 	    uu_list_last(object_list->object_list), object);
265 
266 	nwamd_object_list_unlock(type);
267 
268 	return (object);
269 }
270 
271 /*
272  * Find object in object list, returning it holding a lock and with the
273  * reference count incremented.  The opposite function to this is
274  * nwamd_object_release().
275  */
276 nwamd_object_t
nwamd_object_find(nwam_object_type_t type,const char * name)277 nwamd_object_find(nwam_object_type_t type, const char *name)
278 {
279 	nwamd_object_t object;
280 	struct nwamd_object_list *object_list = nwamd_get_object_list(type);
281 
282 	assert(name != NULL);
283 
284 	if (nwamd_object_list_rlock(type) != 0)
285 		return (NULL);
286 
287 	for (object = uu_list_first(object_list->object_list);
288 	    object != NULL;
289 	    object = uu_list_next(object_list->object_list, object)) {
290 		if (strcmp(object->nwamd_object_name, name) == 0)
291 			break;
292 	}
293 	if (object != NULL) {
294 		(void) pthread_mutex_lock(&object->nwamd_object_mutex);
295 		object->nwamd_object_refcount++;
296 	}
297 	nwamd_object_list_unlock(type);
298 
299 	return (object);
300 }
301 
302 /* Removes object from list, destroy mutex, and free storage. */
303 static void
nwamd_object_fini(nwamd_object_t object,nwam_object_type_t objtype)304 nwamd_object_fini(nwamd_object_t object, nwam_object_type_t objtype)
305 {
306 	nwamd_object_t o;
307 	struct nwamd_object_list *object_list;
308 
309 	assert(object != NULL);
310 
311 	object_list = nwamd_get_object_list(objtype);
312 
313 	for (o = uu_list_first(object_list->object_list);
314 	    o != NULL;
315 	    o = uu_list_next(object_list->object_list, o)) {
316 		if (o == object) {
317 			uu_list_remove(object_list->object_list, object);
318 			(void) pthread_mutex_unlock(
319 			    &object->nwamd_object_mutex);
320 			(void) pthread_mutex_destroy(
321 			    &object->nwamd_object_mutex);
322 			uu_list_node_fini(object, &object->nwamd_object_node,
323 			    object_list_pool);
324 			switch (objtype) {
325 			case NWAM_OBJECT_TYPE_NCU:
326 				nwamd_ncu_free(object->nwamd_object_data);
327 				nwam_ncu_free(object->nwamd_object_handle);
328 				break;
329 			case NWAM_OBJECT_TYPE_LOC:
330 				nwam_loc_free(object->nwamd_object_handle);
331 				break;
332 			case NWAM_OBJECT_TYPE_ENM:
333 				nwam_enm_free(object->nwamd_object_handle);
334 				break;
335 			default:
336 				nlog(LOG_ERR, "nwamd_object_fini: "
337 				    "got unexpected object type %d", objtype);
338 				break;
339 			}
340 			free(object);
341 			break;
342 		}
343 	}
344 }
345 
346 static void
nwamd_object_decref(nwamd_object_t object,int num)347 nwamd_object_decref(nwamd_object_t object, int num)
348 {
349 	nwam_object_type_t objtype;
350 
351 	assert(object->nwamd_object_refcount >= num);
352 	object->nwamd_object_refcount -= num;
353 	if (object->nwamd_object_refcount == 0) {
354 		/*
355 		 * We need to maintain the locking hierarchy of owning the
356 		 * list lock before we get the object lock when we are
357 		 * destroying the object.  If we merely release and then
358 		 * reacquire in the right order we might not find the right
359 		 * object.  Instead we bump the ref count so that it can't
360 		 * be destroyed, we drop the object lock, we acquire the
361 		 * list lock, we acquire the object lock, decrement the ref
362 		 * count, check to make sure we are really destroying it and
363 		 * somebody else hasn't gotten it, and then, if its unref'd,
364 		 * destroying it.
365 		 */
366 		object->nwamd_object_refcount++;
367 		objtype = object->nwamd_object_type;
368 		(void) pthread_mutex_unlock(&object->nwamd_object_mutex);
369 		(void) nwamd_object_list_lock(objtype);
370 		(void) pthread_mutex_lock(&object->nwamd_object_mutex);
371 		if (--object->nwamd_object_refcount != 0)
372 			(void) pthread_mutex_unlock(
373 			    &object->nwamd_object_mutex);
374 		else
375 			nwamd_object_fini(object, objtype);
376 		nwamd_object_list_unlock(objtype);
377 	} else {
378 		(void) pthread_mutex_unlock(&object->nwamd_object_mutex);
379 	}
380 }
381 
382 /*
383  * Drop mutex without decreasing reference count.  Used where we wish to
384  * let go of an object but ensure it will not go away.
385  */
386 void
nwamd_object_release_and_preserve(nwamd_object_t object)387 nwamd_object_release_and_preserve(nwamd_object_t object)
388 {
389 	(void) pthread_mutex_unlock(&object->nwamd_object_mutex);
390 }
391 
392 void
nwamd_object_release(nwamd_object_t object)393 nwamd_object_release(nwamd_object_t object)
394 {
395 	nwamd_object_decref(object, 1);
396 }
397 
398 void
nwamd_object_release_and_destroy(nwamd_object_t object)399 nwamd_object_release_and_destroy(nwamd_object_t object)
400 {
401 	nwamd_object_decref(object, 2);
402 }
403 
404 void
nwamd_object_release_and_destroy_after_preserve(nwamd_object_t object)405 nwamd_object_release_and_destroy_after_preserve(nwamd_object_t object)
406 {
407 	nwamd_object_decref(object, 3);
408 }
409 
410 void
nwamd_object_release_after_preserve(nwamd_object_t object)411 nwamd_object_release_after_preserve(nwamd_object_t object)
412 {
413 	nwamd_object_decref(object, 2);
414 }
415 
416 void
nwamd_object_set_state_timed(nwam_object_type_t type,const char * name,nwam_state_t state,nwam_aux_state_t aux_state,uint32_t when)417 nwamd_object_set_state_timed(nwam_object_type_t type, const char *name,
418     nwam_state_t state, nwam_aux_state_t aux_state, uint32_t when)
419 {
420 	nwamd_event_t event = nwamd_event_init_object_state(type, name,
421 	    state, aux_state);
422 
423 	nlog(LOG_INFO, "nwamd_object_set_state: state event (%s, %s) for %s",
424 	    nwam_state_to_string(state),
425 	    nwam_aux_state_to_string(aux_state), name);
426 	if (event != NULL)
427 		nwamd_event_enqueue_timed(event, when);
428 }
429 
430 void
nwamd_object_set_state(nwam_object_type_t type,const char * name,nwam_state_t state,nwam_aux_state_t aux_state)431 nwamd_object_set_state(nwam_object_type_t type, const char *name,
432     nwam_state_t state, nwam_aux_state_t aux_state)
433 {
434 	nwamd_object_set_state_timed(type, name, state, aux_state, 0);
435 }
436 
437 nwamd_event_method_t *
nwamd_object_event_methods(nwam_object_type_t type)438 nwamd_object_event_methods(nwam_object_type_t type)
439 {
440 	struct nwamd_object_list *object_list = nwamd_get_object_list(type);
441 
442 	return (object_list->object_event_methods);
443 }
444 
445 /*
446  * Walk all objects of specified type calling callback function cb.
447  * Object is locked for duration of callback.
448  */
449 int
nwamd_walk_objects(nwam_object_type_t type,int (* cb)(nwamd_object_t,void *),void * data)450 nwamd_walk_objects(nwam_object_type_t type, int (*cb)(nwamd_object_t, void *),
451     void *data)
452 {
453 	nwamd_object_t object;
454 	struct nwamd_object_list *object_list = nwamd_get_object_list(type);
455 	int ret = 0;
456 
457 	if (nwamd_object_list_rlock(type) != 0)
458 		return (-1);
459 
460 	for (object = uu_list_first(object_list->object_list);
461 	    object != NULL;
462 	    object = uu_list_next(object_list->object_list, object)) {
463 		(void) pthread_mutex_lock(&object->nwamd_object_mutex);
464 		ret = cb(object, data);
465 		(void) pthread_mutex_unlock(&object->nwamd_object_mutex);
466 		if (ret != 0) {
467 			nwamd_object_list_unlock(type);
468 			return (ret);
469 		}
470 	}
471 	nwamd_object_list_unlock(type);
472 
473 	return (0);
474 }
475