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 }