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