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
46typedef 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
53nwamd_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
62nwamd_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
71nwamd_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
85nwamd_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
93nwamd_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 */
102nwamd_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
115uu_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 */
122static int
123nwamd_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
141void
142nwamd_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
164void
165nwamd_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
184static nwamd_object_list_t *
185nwamd_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
191static int
192nwamd_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
200static int
201nwamd_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
213static void
214nwamd_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 */
224nwamd_object_t
225nwamd_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 */
276nwamd_object_t
277nwamd_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. */
303static void
304nwamd_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
346static void
347nwamd_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 */
386void
387nwamd_object_release_and_preserve(nwamd_object_t object)
388{
389	(void) pthread_mutex_unlock(&object->nwamd_object_mutex);
390}
391
392void
393nwamd_object_release(nwamd_object_t object)
394{
395	nwamd_object_decref(object, 1);
396}
397
398void
399nwamd_object_release_and_destroy(nwamd_object_t object)
400{
401	nwamd_object_decref(object, 2);
402}
403
404void
405nwamd_object_release_and_destroy_after_preserve(nwamd_object_t object)
406{
407	nwamd_object_decref(object, 3);
408}
409
410void
411nwamd_object_release_after_preserve(nwamd_object_t object)
412{
413	nwamd_object_decref(object, 2);
414}
415
416void
417nwamd_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
430void
431nwamd_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
437nwamd_event_method_t *
438nwamd_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 */
449int
450nwamd_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