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, 0, (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 }