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