xref: /illumos-gate/usr/src/cmd/syseventd/modules/devfsadmd_mod/devfsadmd_mod.c (revision fa4a3e77edc40df6d92e8da6fc4961d275e9896d)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <limits.h>
30 #include <thread.h>
31 #include <wait.h>
32 #include <synch.h>
33 #include <syslog.h>
34 #include <libintl.h>
35 #include <sys/stat.h>
36 #include <sys/sunddi.h>
37 
38 #include <libsysevent.h>
39 
40 #include "sysevent_signal.h"
41 #include "../devfsadm/devfsadm.h"
42 
43 /*
44  * SLM for devfsadmd device configuration daemon
45  */
46 
47 extern char *root_dir;
48 extern void syseventd_print();
49 
50 sysevent_handle_t *sysevent_hp;
51 
52 /* Alternate root declarations during install */
53 static int use_alt_root = 0;
54 
55 static int devfsadmdeliver_event(sysevent_t *ev, int flag);
56 
57 static struct slm_mod_ops devfsadm_mod_ops = {
58 	SE_MAJOR_VERSION, SE_MINOR_VERSION, 10, devfsadmdeliver_event};
59 
60 typedef struct ev_queue {
61 	struct ev_queue *evq_next;
62 	sysevent_t	*evq_ev;
63 } ev_queue_t;
64 
65 static mutex_t evq_lock;
66 static cond_t evq_cv;
67 static ev_queue_t *eventq_head;
68 static ev_queue_t *eventq_tail;
69 
70 #define	DELIVERY_FAILED	\
71 	gettext("devfsadmd not responding, /dev may not be current")
72 
73 #define	DELIVERY_RESUMED \
74 	gettext("devfsadmd now responding again")
75 
76 /*
77  * Retry error recovery when attempting to send an event to devfsadmd
78  */
79 #define	RETRY_DAEMON_RESTART	0
80 #define	RETRY_MSG_THRESHOLD	60
81 #define	RETRY_DAEMON_INTERVAL	60
82 
83 static int
84 system1(const char *s_path, const char *s)
85 {
86 	struct sigaction cbuf, ibuf, qbuf, ignore, dfl;
87 	sigset_t mask, savemask;
88 	struct stat st;
89 	pid_t pid;
90 	int status, w;
91 
92 	/* Check the requested command */
93 	if (s == NULL) {
94 		errno = EINVAL;
95 		return (-1);
96 	}
97 
98 	/* Check the ability to execute devfsadmd from this process */
99 	if (stat(s_path, &st) < 0) {
100 		return (-1);
101 	}
102 	if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) ||
103 	    ((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) ||
104 	    ((st.st_mode & S_IXOTH) == 0)) {
105 		errno = EPERM;
106 		return (-1);
107 	}
108 
109 	/*
110 	 * Block SIGCHLD and set up a default handler for the duration of the
111 	 * system1 call.
112 	 */
113 	(void) sigemptyset(&mask);
114 	(void) sigaddset(&mask, SIGCHLD);
115 	(void) sigprocmask(SIG_BLOCK, &mask, &savemask);
116 	(void) memset(&dfl, 0, sizeof (dfl));
117 	dfl.sa_handler = SIG_DFL;
118 	(void) sigaction(SIGCHLD, &dfl, &cbuf);
119 
120 	/* Fork off the child process (using fork1(), because it's MT-safe) */
121 	switch (pid = fork1()) {
122 		case -1:
123 			/* Error */
124 			(void) sigaction(SIGCHLD, &cbuf, NULL);
125 			(void) sigprocmask(SIG_SETMASK, &savemask, NULL);
126 			return (-1);
127 		case 0:
128 			/* Set-up an initial signal mask for the child */
129 			(void) sigemptyset(&mask);
130 			(void) sigprocmask(SIG_SETMASK, &mask, NULL);
131 			closefrom(3);
132 			(void) execl(s_path, s, (char *)0);
133 			_exit(-1);
134 			break;
135 		default:
136 			/* Parent */
137 			break;
138 	}
139 
140 	(void) memset(&ignore, 0, sizeof (ignore));
141 	ignore.sa_handler = SIG_IGN;
142 	(void) sigaction(SIGINT, &ignore, &ibuf);
143 	(void) sigaction(SIGQUIT, &ignore, &qbuf);
144 
145 	do {
146 		w = waitpid(pid, &status, 0);
147 	} while (w == -1 && errno == EINTR);
148 
149 	(void) sigaction(SIGINT, &ibuf, NULL);
150 	(void) sigaction(SIGQUIT, &qbuf, NULL);
151 	(void) sigaction(SIGCHLD, &cbuf, NULL);
152 	(void) sigprocmask(SIG_SETMASK, &savemask, NULL);
153 
154 	return ((w == -1)? w: status);
155 }
156 
157 /*
158  * devfsadmdeliver_event - called by syseventd to deliver an event buffer.
159  *			The event buffer is subsequently delivered to
160  *			devfsadmd.  If devfsadmd, is not responding to the
161  *			delivery attempt, we will try to startup the
162  *			daemon.  MT protection is provided by syseventd
163  *			and the client lock.  This insures sequential
164  *			event delivery and protection from re-entrance.
165  */
166 /*ARGSUSED*/
167 static int
168 devfsadmdeliver_event(sysevent_t *ev, int flag)
169 {
170 	int ev_size;
171 	ev_queue_t *new_evq;
172 
173 	/* Not initialized */
174 	if (sysevent_hp == NULL) {
175 		return (0);
176 	}
177 
178 	/* Quick return for uninteresting events */
179 	if (strcmp(sysevent_get_class_name(ev), EC_DEVFS) != 0) {
180 		return (0);
181 	}
182 
183 	/* Queue event for delivery to devfsadmd */
184 	new_evq = (ev_queue_t *)calloc(1, sizeof (ev_queue_t));
185 	if (new_evq == NULL) {
186 		return (EAGAIN);
187 	}
188 
189 	ev_size = sysevent_get_size(ev);
190 	new_evq->evq_ev = (sysevent_t *)malloc(ev_size);
191 	if (new_evq->evq_ev == NULL) {
192 		free(new_evq);
193 		return (EAGAIN);
194 	}
195 	bcopy(ev, new_evq->evq_ev, ev_size);
196 
197 	(void) mutex_lock(&evq_lock);
198 	if (eventq_head == NULL) {
199 		eventq_head = new_evq;
200 	} else {
201 		eventq_tail->evq_next = new_evq;
202 	}
203 	eventq_tail = new_evq;
204 
205 	(void) cond_signal(&evq_cv);
206 	(void) mutex_unlock(&evq_lock);
207 
208 	return (0);
209 }
210 
211 static int cleanup;
212 thread_t deliver_thr_id;
213 
214 void *
215 devfsadmd_deliver_thr(void *arg __unused)
216 {
217 	int retry = 0;
218 	int msg_emitted = 0;
219 	ev_queue_t *evqp;
220 
221 	(void) mutex_lock(&evq_lock);
222 	for (;;) {
223 		while (eventq_head == NULL) {
224 			(void) cond_wait(&evq_cv, &evq_lock);
225 			if (cleanup && eventq_head == NULL) {
226 				(void) cond_signal(&evq_cv);
227 				(void) mutex_unlock(&evq_lock);
228 				return (NULL);
229 			}
230 		}
231 
232 		/* Send events on to devfsadmd */
233 		evqp = eventq_head;
234 		while (evqp) {
235 			(void) mutex_unlock(&evq_lock);
236 			retry = 0;
237 			while (sysevent_send_event(sysevent_hp,
238 			    evqp->evq_ev) != 0) {
239 				/*
240 				 * Invoke devfsadm to handle node creation
241 				 * but not for an alternate root.
242 				 */
243 				if (use_alt_root != 0)
244 					break;
245 				/*
246 				 * daemon unresponsive -
247 				 * restart daemon and retry once more
248 				 */
249 				if ((errno == EBADF || errno == ENOENT) &&
250 				    (retry == RETRY_DAEMON_RESTART) ||
251 				    ((retry % RETRY_DAEMON_INTERVAL) == 0)) {
252 					(void) system1(
253 					    DEVFSADMD_START_PATH,
254 					    DEVFSADMD_START);
255 				}
256 				if (retry == RETRY_MSG_THRESHOLD) {
257 					syslog(LOG_ERR, DELIVERY_FAILED);
258 					msg_emitted = 1;
259 				}
260 				(void) sleep(1);
261 				++retry;
262 				continue;
263 			}
264 
265 			/*
266 			 * Event delivered: remove from queue
267 			 * and reset delivery retry state.
268 			 */
269 			if (msg_emitted) {
270 				syslog(LOG_ERR, DELIVERY_RESUMED);
271 				msg_emitted = 0;
272 			}
273 			retry = 0;
274 			(void) mutex_lock(&evq_lock);
275 			if (eventq_head != NULL) {
276 				eventq_head = eventq_head->evq_next;
277 				if (eventq_head == NULL)
278 					eventq_tail = NULL;
279 			}
280 			free(evqp->evq_ev);
281 			free(evqp);
282 			evqp = eventq_head;
283 		}
284 		if (cleanup) {
285 			(void) cond_signal(&evq_cv);
286 			(void) mutex_unlock(&evq_lock);
287 			return (NULL);
288 		}
289 	}
290 
291 	/* NOTREACHED */
292 }
293 
294 struct slm_mod_ops *
295 slm_init()
296 {
297 	char alt_door[MAXPATHLEN];
298 
299 	if (strcmp(root_dir, "") == 0) {
300 		/* Initialize the private sysevent handle */
301 		sysevent_hp = sysevent_open_channel_alt(DEVFSADM_SERVICE_DOOR);
302 	} else {
303 
304 		/* Try alternate door during install time */
305 		if (snprintf(alt_door, MAXPATHLEN, "%s%s", "/tmp",
306 		    DEVFSADM_SERVICE_DOOR) >= MAXPATHLEN)
307 			return (NULL);
308 
309 		sysevent_hp = sysevent_open_channel_alt(alt_door);
310 		use_alt_root = 1;
311 	}
312 	if (sysevent_hp == NULL) {
313 		syseventd_print(0, "Unable to allocate sysevent handle"
314 		    " for devfsadm module\n");
315 		return (NULL);
316 	}
317 
318 	if (sysevent_bind_publisher(sysevent_hp) != 0) {
319 		if (errno == EBUSY) {
320 			sysevent_cleanup_publishers(sysevent_hp);
321 			if (sysevent_bind_publisher(sysevent_hp) != 0) {
322 				(void) sysevent_close_channel(sysevent_hp);
323 				return (NULL);
324 			}
325 		}
326 	}
327 
328 	sysevent_cleanup_subscribers(sysevent_hp);
329 	cleanup = 0;
330 	eventq_head = NULL;
331 	eventq_tail = NULL;
332 
333 	(void) mutex_init(&evq_lock, USYNC_THREAD, NULL);
334 	(void) cond_init(&evq_cv, USYNC_THREAD, NULL);
335 
336 	if (thr_create(NULL, 0, devfsadmd_deliver_thr,
337 	    NULL, THR_BOUND, &deliver_thr_id) != 0) {
338 		(void) mutex_destroy(&evq_lock);
339 		(void) cond_destroy(&evq_cv);
340 		sysevent_close_channel(sysevent_hp);
341 		return (NULL);
342 	}
343 
344 	return (&devfsadm_mod_ops);
345 }
346 
347 void
348 slm_fini()
349 {
350 	/* Wait for all events to be flushed out to devfsadmd */
351 	(void) mutex_lock(&evq_lock);
352 	cleanup = 1;
353 	(void) cond_signal(&evq_cv);
354 	(void) cond_wait(&evq_cv, &evq_lock);
355 	(void) mutex_unlock(&evq_lock);
356 
357 	/* Wait for delivery thread to exit */
358 	(void) thr_join(deliver_thr_id, NULL, NULL);
359 
360 	(void) mutex_destroy(&evq_lock);
361 	(void) cond_destroy(&evq_cv);
362 
363 	sysevent_close_channel(sysevent_hp);
364 	sysevent_hp = NULL;
365 }
366