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
init_channel()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
deliver_event(sysevent_t * ev,int flag)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 *
subscriber_deliver_thr(void * arg __unused)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 *
slm_init()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
slm_fini()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