xref: /illumos-gate/usr/src/cmd/syseventd/modules/sysevent_reg_mod/sysevent_reg_mod.c (revision 90221f9148b67fdc90178b67f9600b7bd4e3bc7c)
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 }
261