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