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