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
system1(const char * s_path,const char * s)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
devfsadmdeliver_event(sysevent_t * ev,int flag)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 *
devfsadmd_deliver_thr(void * arg __unused)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 *
slm_init()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
slm_fini()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