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() 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; 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; 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, (void *(*)(void *))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