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