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 <string.h>
  32 #include <strings.h>
  33 #include <stdio.h>
  34 #include <limits.h>
  35 #include <thread.h>
  36 #include <wait.h>
  37 #include <synch.h>
  38 #include <errno.h>
  39 #include <locale.h>
  40 #include <sys/stat.h>
  41 #include <sys/mnttab.h>
  42 #include <sys/sunddi.h>
  43 #include <sys/modctl.h>
  44 #include <sys/sysevent.h>
  45 #include <sys/sysevent_impl.h>
  46 
  47 #include <libsysevent.h>
  48 
  49 #include "message_reg_mod.h"
  50 
  51 /*
  52  * SLM for sysevent event subscribers
  53  */
  54 
  55 extern char     *root_dir;
  56 extern void     syseventd_print(int level, char *format, ...);
  57 extern void     syseventd_err_print(char *format, ...);
  58 
  59 sysevent_handle_t *sysevent_hp;
  60 
  61 typedef struct ev_queue {
  62         struct ev_queue *evq_next;
  63         sysevent_t      *evq_ev;
  64 } ev_queue_t;
  65 
  66 static mutex_t  evq_lock;
  67 static cond_t evq_cv;
  68 static ev_queue_t *event_q = NULL;
  69 static int cleanup;
  70 static thread_t deliver_thr_id;
  71 
  72 static int
  73 init_channel()
  74 {
  75         /*
  76          * This functionality is not supported in the mini-root
  77          * environment, ie install.  If root_dir is set, implying
  78          * install, we quietly fail.
  79          */
  80         if (strcmp(root_dir, "") != 0) {
  81                 return (EACCES);
  82         }
  83 
  84         /*
  85          * Initialize the private sysevent handle
  86          */
  87         sysevent_hp = sysevent_open_channel(SYSEVENTD_CHAN);
  88         if (sysevent_hp == NULL) {
  89                 if (errno == EACCES) {
  90                         syseventd_print(3, "sysevent_reg_mod: "
  91                             "sysevent_open_channel failed with %s init "
  92                             "deferred\n", strerror(errno));
  93                         return (errno);
  94                 } else {
  95                         syseventd_err_print(INIT_SUB_OPEN_CHAN_ERR,
  96                             strerror(errno));
  97                         return (errno);
  98                 }
  99         }
 100 
 101         if (sysevent_bind_publisher(sysevent_hp) != 0) {
 102                 /*
 103                  * Only one publisher allowed on the syseventd channel,
 104                  * cleanup previously allocated syseventd channel publishers
 105                  */
 106                 if (errno == EBUSY) {
 107                         sysevent_cleanup_publishers(sysevent_hp);
 108                         if (sysevent_bind_publisher(sysevent_hp) == 0)
 109                                 return (0);
 110                 }
 111 
 112                 syseventd_err_print(INIT_SUB_BIND_PUB_ERR,
 113                     strerror(errno));
 114                 sysevent_close_channel(sysevent_hp);
 115                 sysevent_hp = NULL;
 116                 return (errno);
 117         }
 118 
 119         return (0);
 120 }
 121 
 122 static int
 123 deliver_event(sysevent_t *ev, int flag)
 124 {
 125         int ret, ev_size;
 126         ev_queue_t *new_evq, *tmp_evq;
 127 
 128         /* Not initialized */
 129         if (sysevent_hp == NULL) {
 130 
 131                 ret = init_channel();
 132                 if (ret != 0) {
 133                         if (ret == EBUSY && flag != SE_NO_RETRY) {
 134                                 return (EAGAIN);
 135                         } else if (ret == EACCES) {
 136                                 return (0);
 137                         } else {
 138                                 syseventd_err_print(INIT_SUB_OPEN_CHAN_ERR,
 139                                     strerror(ret));
 140                                 return (0);
 141                         }
 142                 }
 143                 /* Check for stale syseventd subscribers */
 144                 sysevent_cleanup_subscribers(sysevent_hp);
 145                 syseventd_print(3, "sysevent_reg_mod: init successful");
 146         }
 147 
 148         /* Queue event for delivery to all subscribers */
 149         new_evq = (ev_queue_t *)calloc(1, sizeof (ev_queue_t));
 150         if (new_evq == NULL) {
 151                 return (EAGAIN);
 152         }
 153         ev_size = sysevent_get_size(ev);
 154         new_evq->evq_ev = (sysevent_t *)malloc(ev_size);
 155         if (new_evq->evq_ev == NULL) {
 156                 free(new_evq);
 157                 return (EAGAIN);
 158         }
 159         bcopy(ev, new_evq->evq_ev, ev_size);
 160 
 161         (void) mutex_lock(&evq_lock);
 162         if (event_q == NULL) {
 163                 event_q = new_evq;
 164         } else {
 165                 tmp_evq = event_q;
 166                 while (tmp_evq->evq_next != NULL)
 167                         tmp_evq = tmp_evq->evq_next;
 168                 tmp_evq->evq_next = new_evq;
 169         }
 170         syseventd_print(3, "sysevent_reg_mod: queue event 0X%llx\n",
 171             sysevent_get_seq(ev));
 172 
 173         (void) cond_signal(&evq_cv);
 174         (void) mutex_unlock(&evq_lock);
 175 
 176         return (0);
 177 }
 178 
 179 void
 180 subscriber_deliver_thr()
 181 {
 182         ev_queue_t *evqp;
 183 
 184         (void) mutex_lock(&evq_lock);
 185         for (;;) {
 186                 while (event_q == NULL && cleanup == 0) {
 187                         (void) cond_wait(&evq_cv, &evq_lock);
 188                 }
 189 
 190                 /* Send events on to all current subscribers */
 191                 evqp = event_q;
 192                 while (evqp) {
 193                         (void) mutex_unlock(&evq_lock);
 194                         syseventd_print(3, "sysevent_reg_mod: sending event "
 195                             "0X%llx\n", sysevent_get_seq(evqp->evq_ev));
 196                         if (sysevent_send_event(sysevent_hp,
 197                             evqp->evq_ev) != 0) {
 198                                 syseventd_print(3, "sysevent_reg_mod: "
 199                                     "failed to send event\n");
 200                         }
 201                         syseventd_print(3, "sysevent_reg_mod: event sent "
 202                             "0X%llx\n", sysevent_get_seq(evqp->evq_ev));
 203                         (void) mutex_lock(&evq_lock);
 204                         event_q = evqp->evq_next;
 205                         free(evqp->evq_ev);
 206                         free(evqp);
 207                         evqp = event_q;
 208                 }
 209                 if (cleanup) {
 210                         syseventd_print(3, "sysevent_reg_mod: deliver "
 211                             "thread exiting\n");
 212                         (void) mutex_unlock(&evq_lock);
 213                         (void) thr_exit(NULL);
 214                         /* NOTREACHED */
 215                 }
 216         }
 217 
 218         /* NOTREACHED */
 219 }
 220 
 221 static struct slm_mod_ops sysevent_reg_mod_ops = {
 222         SE_MAJOR_VERSION, SE_MINOR_VERSION, SE_MAX_RETRY_LIMIT, deliver_event};
 223 
 224 struct slm_mod_ops *
 225 slm_init()
 226 {
 227         cleanup = 0;
 228         sysevent_hp = NULL;
 229 
 230         (void) init_channel();
 231 
 232         (void) mutex_init(&evq_lock, USYNC_THREAD, NULL);
 233         (void) cond_init(&evq_cv, USYNC_THREAD, NULL);
 234 
 235         if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_deliver_thr,
 236             NULL, 0, &deliver_thr_id) != 0) {
 237                 syseventd_err_print(INIT_SUB_THR_CREATE_ERR, strerror(errno));
 238                 return (NULL);
 239         }
 240 
 241         return (&sysevent_reg_mod_ops);
 242 }
 243 
 244 void
 245 slm_fini()
 246 {
 247         (void) mutex_lock(&evq_lock);
 248         cleanup = 1;
 249         (void) cond_signal(&evq_cv);
 250         (void) mutex_unlock(&evq_lock);
 251 
 252         /* Wait for delivery threads to exit */
 253         (void) thr_join(deliver_thr_id, NULL, NULL);
 254 
 255         (void) mutex_destroy(&evq_lock);
 256         (void) cond_destroy(&evq_cv);
 257 
 258         sysevent_close_channel(sysevent_hp);
 259         sysevent_hp = NULL;
 260 }