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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <door.h> 34 #include <fcntl.h> 35 #include <syslog.h> 36 #include <sys/sunddi.h> 37 #include <libsysevent.h> 38 #include <picl.h> 39 #include <pthread.h> 40 #include "piclevent.h" 41 #include <sys/sysevent/eventdefs.h> 42 #include <sys/sysevent/dr.h> 43 44 #define PICLSLM_DOOR_FAILED gettext("PICL SLM door create failed\n") 45 46 /* 47 * syseventd event handler 48 */ 49 static int piclslm_debug = 0; 50 static int piclslm_deliver_event(sysevent_t *ev, int flag); 51 static int door_fd = -1; 52 53 typedef struct nvlist_queue { 54 char *nvq_item; /* packed nvlist */ 55 size_t nvq_sz; /* buf size */ 56 struct nvlist_queue *nvq_next; 57 } nvlist_queue_t; 58 59 static nvlist_queue_t *nvq_head; 60 static nvlist_queue_t *nvq_tail; 61 62 static mutex_t nvq_lock; 63 static cond_t nvq_cv; 64 static thread_t piclslm_deliver_thr_id; 65 static int cleanup; 66 67 static struct slm_mod_ops piclslm_mod_ops = { 68 SE_MAJOR_VERSION, SE_MINOR_VERSION, SE_MAX_RETRY_LIMIT, 69 piclslm_deliver_event}; 70 71 72 static void 73 init_queue(void) 74 { 75 nvq_head = NULL; 76 nvq_tail = NULL; 77 } 78 79 static int 80 add_to_queue(char *nvl, size_t sz) 81 { 82 nvlist_queue_t *new_nvq; 83 84 new_nvq = malloc(sizeof (*new_nvq)); 85 if (new_nvq == NULL) 86 return (-1); 87 88 new_nvq->nvq_item = nvl; 89 new_nvq->nvq_sz = sz; 90 new_nvq->nvq_next = NULL; 91 92 if (nvq_head == NULL) 93 nvq_head = new_nvq; 94 else 95 nvq_tail->nvq_next = new_nvq; 96 nvq_tail = new_nvq; 97 98 return (0); 99 } 100 101 static nvlist_queue_t * 102 remove_from_queue(void) 103 { 104 nvlist_queue_t *nvqp; 105 106 if (nvq_head == NULL) 107 return (NULL); 108 109 nvqp = nvq_head; 110 nvq_head = nvq_head->nvq_next; 111 if (nvq_head == NULL) 112 nvq_tail = NULL; 113 return (nvqp); 114 } 115 116 static void 117 free_nvqueue(nvlist_queue_t *nvqp) 118 { 119 free(nvqp->nvq_item); 120 free(nvqp); 121 } 122 123 /* 124 * deliver the event to the plugin if the door exists 125 */ 126 static void 127 post_piclevent(char *pack_buf, size_t nvl_size) 128 { 129 door_arg_t darg; 130 131 darg.data_ptr = pack_buf; 132 darg.data_size = nvl_size; 133 darg.desc_ptr = NULL; 134 darg.desc_num = 0; 135 darg.rbuf = NULL; 136 darg.rsize = 0; 137 138 if (door_fd < 0 || door_call(door_fd, &darg) < 0) { 139 if (door_fd >= 0) { 140 if (errno != EBADF) { 141 return; 142 } 143 144 /* 145 * It's not a valid door file descriptor. 146 * Close and reopen the door and try again 147 * as "picld" may have restarted. 148 */ 149 (void) close(door_fd); 150 } 151 152 door_fd = open(PICLEVENT_DOOR, O_RDONLY); 153 if (piclslm_debug) 154 syslog(LOG_INFO, 155 "picl_slm: opened door %s door_fd: %d\n", 156 PICLEVENT_DOOR, door_fd); 157 if (door_fd < 0 || door_call(door_fd, &darg) < 0) { 158 return; 159 } 160 } 161 if (piclslm_debug) 162 syslog(LOG_INFO, 163 "picl_slm: sent sysevent door:%d pack_buf:%p size:0x%x\n", 164 door_fd, pack_buf, nvl_size); 165 } 166 167 /*ARGSUSED*/ 168 static void * 169 piclslm_deliver_thr(void *args) 170 { 171 nvlist_queue_t *nvqp; 172 173 for (;;) { 174 (void) mutex_lock(&nvq_lock); 175 while (nvq_head == NULL && cleanup == 0) { 176 (void) cond_wait(&nvq_cv, &nvq_lock); 177 } 178 nvqp = remove_from_queue(); 179 (void) mutex_unlock(&nvq_lock); 180 while (nvqp) { 181 post_piclevent(nvqp->nvq_item, nvqp->nvq_sz); 182 free_nvqueue(nvqp); 183 (void) mutex_lock(&nvq_lock); 184 nvqp = remove_from_queue(); 185 (void) mutex_unlock(&nvq_lock); 186 } 187 if (cleanup) 188 return (NULL); 189 } 190 /*NOTREACHED*/ 191 } 192 193 /* 194 * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing 195 * from ev and EAGAIN if nvlist_add_string() fails 196 */ 197 static int 198 piclslm_add_ec_devfs_args(nvlist_t *nvl, sysevent_t *ev) 199 { 200 sysevent_value_t se_val; 201 202 if (sysevent_lookup_attr(ev, DEVFS_PATHNAME, SE_DATA_TYPE_STRING, 203 &se_val) != 0 || se_val.value.sv_string == NULL) { 204 return (EINVAL); 205 } 206 if (nvlist_add_string(nvl, PICLEVENTARG_DEVFS_PATH, 207 se_val.value.sv_string)) { 208 return (EAGAIN); 209 } 210 return (0); 211 } 212 213 /* 214 * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing 215 * from ev and EAGAIN if nvlist_add_string() fails 216 */ 217 static int 218 piclslm_add_ec_dr_args(nvlist_t *nvl, sysevent_t *ev) 219 { 220 sysevent_value_t se_val; 221 222 if (sysevent_lookup_attr(ev, DR_AP_ID, SE_DATA_TYPE_STRING, 223 &se_val) != 0 || se_val.value.sv_string == NULL) { 224 return (EINVAL); 225 } 226 if (nvlist_add_string(nvl, PICLEVENTARG_AP_ID, 227 se_val.value.sv_string)) { 228 return (EAGAIN); 229 } 230 if (sysevent_lookup_attr(ev, DR_HINT, SE_DATA_TYPE_STRING, 231 &se_val) != 0 || se_val.value.sv_string == NULL) { 232 if (nvlist_add_string(nvl, PICLEVENTARG_HINT, "")) 233 return (EAGAIN); 234 } else { 235 if (nvlist_add_string(nvl, PICLEVENTARG_HINT, 236 se_val.value.sv_string)) 237 return (EAGAIN); 238 } 239 return (0); 240 } 241 242 /* 243 * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing 244 * from ev and EAGAIN if nvlist_add_string() fails 245 */ 246 static int 247 piclslm_add_ec_dr_req_args(nvlist_t *nvl, sysevent_t *ev) 248 { 249 nvlist_t *nvlist = NULL; 250 char *ap_id = NULL; 251 char *dr_req = NULL; 252 253 if (sysevent_get_attr_list(ev, &nvlist)) { 254 return (EAGAIN); 255 } 256 257 if (nvlist_lookup_string(nvlist, DR_AP_ID, &ap_id) != 0 || 258 ap_id == NULL) { 259 nvlist_free(nvlist); 260 return (EINVAL); 261 } 262 263 if (nvlist_add_string(nvl, PICLEVENTARG_AP_ID, ap_id)) { 264 nvlist_free(nvlist); 265 return (EAGAIN); 266 } 267 268 dr_req = NULL; 269 if (nvlist_lookup_string(nvlist, DR_REQ_TYPE, &dr_req) != 0) 270 dr_req = ""; 271 272 if (nvlist_add_string(nvl, PICLEVENTARG_DR_REQ_TYPE, 273 dr_req)) { 274 nvlist_free(nvlist); 275 return (EAGAIN); 276 } 277 278 if (piclslm_debug) 279 syslog(LOG_DEBUG, "piclevent: dr_req_type = %s on %s\n", 280 (dr_req ? dr_req : "Investigate"), ap_id); 281 282 nvlist_free(nvlist); 283 return (0); 284 } 285 286 /* 287 * piclslm_deliver_event - called by syseventd to deliver an event buffer. 288 * The event buffer is subsequently delivered to 289 * picld. If picld, is not responding to the 290 * delivery attempt, we will ignore it. 291 */ 292 /*ARGSUSED*/ 293 static int 294 piclslm_deliver_event(sysevent_t *ev, int flag) 295 { 296 sysevent_t *dupev; 297 nvlist_t *nvl; 298 char *ec; 299 char *esc; 300 char *ename; 301 int retval; 302 char *pack_buf; 303 size_t nvl_size; 304 int rval; 305 306 /* 307 * Filter out uninteresting events 308 */ 309 ec = sysevent_get_class_name(ev); 310 esc = sysevent_get_subclass_name(ev); 311 if (piclslm_debug) 312 syslog(LOG_INFO, 313 "picl_slm: got sysevent ev:%p class:%s subclass:%s\n", 314 ev, (ec) ? ec : "NULL", (esc) ? esc : "NULL"); 315 if ((ec == NULL) || (esc == NULL)) { 316 return (0); 317 } else if (strcmp(ec, EC_DEVFS) == 0) { 318 if (strcmp(esc, ESC_DEVFS_DEVI_ADD) == 0) 319 ename = strdup(PICLEVENT_SYSEVENT_DEVICE_ADDED); 320 else if (strcmp(esc, ESC_DEVFS_DEVI_REMOVE) == 0) 321 ename = strdup(PICLEVENT_SYSEVENT_DEVICE_REMOVED); 322 else 323 return (0); 324 } else if (strcmp(ec, EC_DR) == 0) { 325 if (strcmp(esc, ESC_DR_AP_STATE_CHANGE) == 0) 326 ename = strdup(PICLEVENT_DR_AP_STATE_CHANGE); 327 else if (strcmp(esc, ESC_DR_REQ) == 0) 328 ename = strdup(PICLEVENT_DR_REQ); 329 else 330 return (0); 331 } else { 332 return (0); 333 } 334 335 if (ename == NULL) 336 return (EAGAIN); 337 338 /* 339 * Make a copy to expand attribute list 340 */ 341 dupev = sysevent_dup(ev); 342 if (dupev == NULL) { 343 free(ename); 344 return (EAGAIN); 345 } 346 347 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, NULL)) { 348 free(ename); 349 sysevent_free(dupev); 350 return (EAGAIN); 351 } 352 353 if (strcmp(ec, EC_DEVFS) == 0) { 354 rval = piclslm_add_ec_devfs_args(nvl, dupev); 355 } else if (strcmp(ec, EC_DR) == 0) { 356 if (strcmp(esc, ESC_DR_REQ) == 0) { 357 rval = piclslm_add_ec_dr_req_args(nvl, dupev); 358 } else { 359 rval = piclslm_add_ec_dr_args(nvl, dupev); 360 } 361 } 362 363 if (rval != 0) { 364 free(ename); 365 nvlist_free(nvl); 366 sysevent_free(dupev); 367 return ((rval == EAGAIN) ? EAGAIN : 0); 368 } 369 370 pack_buf = NULL; 371 if (nvlist_add_string(nvl, PICLEVENTARG_EVENT_NAME, ename) || 372 nvlist_add_string(nvl, PICLEVENTARG_DATA_TYPE, 373 PICLEVENTARG_PICLEVENT_DATA) || 374 nvlist_pack(nvl, &pack_buf, &nvl_size, NV_ENCODE_NATIVE, NULL)) { 375 free(ename); 376 nvlist_free(nvl); 377 sysevent_free(dupev); 378 return (EAGAIN); 379 } 380 381 /* 382 * Add nvlist_t to queue 383 */ 384 (void) mutex_lock(&nvq_lock); 385 retval = add_to_queue(pack_buf, nvl_size); 386 (void) cond_signal(&nvq_cv); 387 (void) mutex_unlock(&nvq_lock); 388 389 nvlist_free(nvl); 390 sysevent_free(dupev); 391 free(ename); 392 return (retval < 0 ? EAGAIN : 0); 393 } 394 395 struct slm_mod_ops * 396 slm_init(void) 397 { 398 cleanup = 0; 399 400 init_queue(); 401 402 (void) mutex_init(&nvq_lock, USYNC_THREAD, NULL); 403 (void) cond_init(&nvq_cv, USYNC_THREAD, NULL); 404 405 if (thr_create(NULL, NULL, piclslm_deliver_thr, 406 NULL, THR_BOUND, &piclslm_deliver_thr_id) != 0) { 407 (void) mutex_destroy(&nvq_lock); 408 (void) cond_destroy(&nvq_cv); 409 return (NULL); 410 } 411 return (&piclslm_mod_ops); 412 } 413 414 void 415 slm_fini(void) 416 { 417 /* 418 * Wait for all events to be sent 419 */ 420 (void) mutex_lock(&nvq_lock); 421 cleanup = 1; 422 (void) cond_signal(&nvq_cv); 423 (void) mutex_unlock(&nvq_lock); 424 425 /* Wait for delivery thread to exit */ 426 (void) thr_join(piclslm_deliver_thr_id, NULL, NULL); 427 428 (void) mutex_destroy(&nvq_lock); 429 (void) cond_destroy(&nvq_cv); 430 431 if (door_fd >= 0) 432 (void) close(door_fd); 433 door_fd = -1; 434 }