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 }