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 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 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 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 * 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 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 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 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 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 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 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 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 387 nwamd_object_release_and_preserve(nwamd_object_t object) 388 { 389 (void) pthread_mutex_unlock(&object->nwamd_object_mutex); 390 } 391 392 void 393 nwamd_object_release(nwamd_object_t object) 394 { 395 nwamd_object_decref(object, 1); 396 } 397 398 void 399 nwamd_object_release_and_destroy(nwamd_object_t object) 400 { 401 nwamd_object_decref(object, 2); 402 } 403 404 void 405 nwamd_object_release_and_destroy_after_preserve(nwamd_object_t object) 406 { 407 nwamd_object_decref(object, 3); 408 } 409 410 void 411 nwamd_object_release_after_preserve(nwamd_object_t object) 412 { 413 nwamd_object_decref(object, 2); 414 } 415 416 void 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 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 * 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 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