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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 
  27 /*
  28  * System message redirection driver for Sun.
  29  *
  30  * Redirects system message output to the device designated as the underlying
  31  * "hardware" console, as given by the value of sysmvp.  The implementation
  32  * assumes that sysmvp denotes a STREAMS device; the assumption is justified
  33  * since consoles must be capable of effecting tty semantics.
  34  */
  35 
  36 #include <sys/types.h>
  37 #include <sys/kmem.h>
  38 #include <sys/open.h>
  39 #include <sys/param.h>
  40 #include <sys/systm.h>
  41 #include <sys/signal.h>
  42 #include <sys/cred.h>
  43 #include <sys/user.h>
  44 #include <sys/proc.h>
  45 #include <sys/vnode.h>
  46 #include <sys/uio.h>
  47 #include <sys/stat.h>
  48 #include <sys/file.h>
  49 #include <sys/session.h>
  50 #include <sys/stream.h>
  51 #include <sys/strsubr.h>
  52 #include <sys/poll.h>
  53 #include <sys/debug.h>
  54 #include <sys/sysmsg_impl.h>
  55 #include <sys/conf.h>
  56 #include <sys/termios.h>
  57 #include <sys/errno.h>
  58 #include <sys/modctl.h>
  59 #include <sys/pathname.h>
  60 #include <sys/ddi.h>
  61 #include <sys/sunddi.h>
  62 #include <sys/consdev.h>
  63 #include <sys/policy.h>
  64 
  65 /*
  66  * internal functions
  67  */
  68 static int sysmopen(dev_t *, int, int, cred_t *);
  69 static int sysmclose(dev_t, int, int, cred_t *);
  70 static int sysmread(dev_t, struct uio *, cred_t *);
  71 static int sysmwrite(dev_t, struct uio *, cred_t *);
  72 static int sysmioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  73 static int sysmpoll(dev_t, short, int, short *, struct pollhead **);
  74 static int sysm_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  75 static int sysm_attach(dev_info_t *, ddi_attach_cmd_t);
  76 static int sysm_detach(dev_info_t *, ddi_detach_cmd_t);
  77 static void bind_consadm_conf(char *);
  78 static int checkarg(dev_t);
  79 
  80 static dev_info_t *sysm_dip;            /* private copy of devinfo pointer */
  81 
  82 static struct cb_ops sysm_cb_ops = {
  83 
  84         sysmopen,               /* open */
  85         sysmclose,              /* close */
  86         nodev,                  /* strategy */
  87         nodev,                  /* print */
  88         nodev,                  /* dump */
  89         sysmread,               /* read */
  90         sysmwrite,              /* write */
  91         sysmioctl,              /* ioctl */
  92         nodev,                  /* devmap */
  93         nodev,                  /* mmap */
  94         nodev,                  /* segmap */
  95         sysmpoll,               /* poll */
  96         ddi_prop_op,            /* cb_prop_op */
  97         NULL,                   /* streamtab  */
  98         D_NEW | D_MP,           /* Driver compatibility flag */
  99         CB_REV,                 /* cb_rev */
 100         nodev,                  /* aread */
 101         nodev                   /* awrite */
 102 };
 103 
 104 static struct dev_ops sysm_ops = {
 105 
 106         DEVO_REV,               /* devo_rev, */
 107         0,                      /* refcnt  */
 108         sysm_info,              /* info */
 109         nulldev,                /* identify */
 110         nulldev,                /* probe */
 111         sysm_attach,            /* attach */
 112         sysm_detach,            /* detach */
 113         nodev,                  /* reset */
 114         &sysm_cb_ops,               /* driver operations */
 115         (struct bus_ops *)0,    /* bus operations */
 116         nulldev,                /* power */
 117         ddi_quiesce_not_needed,         /* quiesce */
 118 
 119 };
 120 
 121 /*
 122  * Global variables associated with the console device:
 123  */
 124 
 125 #define SYS_SYSMIN      0       /* sysmsg minor number */
 126 #define SYS_MSGMIN      1       /* msglog minor number */
 127 #define SYSPATHLEN      255     /* length of device path */
 128 
 129 /*
 130  * Private driver state:
 131  */
 132 
 133 #define MAXDEVS 5
 134 
 135 typedef struct {
 136         dev_t   dca_devt;
 137         int     dca_flags;
 138         vnode_t *dca_vp;
 139         krwlock_t       dca_lock;
 140         char    dca_name[SYSPATHLEN];
 141 } devicecache_t;
 142 
 143 /* list of dyn. + persist. config'ed dev's */
 144 static devicecache_t sysmcache[MAXDEVS];
 145 static kmutex_t dcvp_mutex;
 146 static vnode_t  *dcvp = NULL;
 147 static boolean_t sysmsg_opened;
 148 static boolean_t msglog_opened;
 149 
 150 /* flags for device cache */
 151 #define SYSM_DISABLED   0x0
 152 #define SYSM_ENABLED    0x1
 153 
 154 /*
 155  * Module linkage information for the kernel.
 156  */
 157 
 158 static struct modldrv modldrv = {
 159         &mod_driverops, /* Type of module.  This one is a pseudo driver */
 160         "System message redirection (fanout) driver",
 161         &sysm_ops,  /* driver ops */
 162 };
 163 
 164 static struct modlinkage modlinkage = {
 165         MODREV_1,
 166         &modldrv,
 167         NULL
 168 };
 169 
 170 int
 171 _init(void)
 172 {
 173         return (mod_install(&modlinkage));
 174 }
 175 
 176 int
 177 _fini(void)
 178 {
 179         return (mod_remove(&modlinkage));
 180 }
 181 
 182 int
 183 _info(struct modinfo *modinfop)
 184 {
 185         return (mod_info(&modlinkage, modinfop));
 186 }
 187 
 188 /*
 189  * DDI glue routines
 190  */
 191 static int
 192 sysm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 193 {
 194         int i;
 195 
 196         switch (cmd) {
 197         case DDI_ATTACH:
 198                 ASSERT(sysm_dip == NULL);
 199 
 200                 if (ddi_create_minor_node(devi, "sysmsg", S_IFCHR,
 201                     SYS_SYSMIN, DDI_PSEUDO, NULL) == DDI_FAILURE ||
 202                     ddi_create_minor_node(devi, "msglog", S_IFCHR,
 203                     SYS_MSGMIN, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 204                         ddi_remove_minor_node(devi, NULL);
 205                         return (DDI_FAILURE);
 206                 }
 207 
 208                 for (i = 0; i < MAXDEVS; i++) {
 209                         rw_init(&sysmcache[i].dca_lock, NULL, RW_DRIVER, NULL);
 210                 }
 211 
 212                 sysm_dip = devi;
 213                 return (DDI_SUCCESS);
 214         case DDI_SUSPEND:
 215         case DDI_PM_SUSPEND:
 216                 return (DDI_SUCCESS);
 217         default:
 218                 return (DDI_FAILURE);
 219         }
 220 }
 221 
 222 static int
 223 sysm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 224 {
 225         int i;
 226 
 227         switch (cmd) {
 228         case DDI_DETACH:
 229                 ASSERT(sysm_dip == devi);
 230 
 231                 for (i = 0; i < MAXDEVS; i++)
 232                         rw_destroy(&sysmcache[i].dca_lock);
 233 
 234                 ddi_remove_minor_node(devi, NULL);
 235                 sysm_dip = NULL;
 236                 return (DDI_SUCCESS);
 237 
 238         case DDI_SUSPEND:
 239         case DDI_PM_SUSPEND:
 240                 return (DDI_SUCCESS);
 241         default:
 242                 return (DDI_FAILURE);
 243         }
 244 
 245 }
 246 
 247 /* ARGSUSED */
 248 static int
 249 sysm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 250 {
 251         int rval = DDI_FAILURE;
 252         minor_t instance;
 253 
 254         instance = getminor((dev_t)arg);
 255 
 256         switch (infocmd) {
 257         case DDI_INFO_DEVT2DEVINFO:
 258                 if (sysm_dip != NULL &&
 259                     (instance == SYS_SYSMIN || instance == SYS_MSGMIN)) {
 260                         *result = sysm_dip;
 261                         rval = DDI_SUCCESS;
 262                 }
 263                 break;
 264 
 265         case DDI_INFO_DEVT2INSTANCE:
 266                 if (instance == SYS_SYSMIN || instance == SYS_MSGMIN) {
 267                         *result = NULL;
 268                         rval = DDI_SUCCESS;
 269                 }
 270                 break;
 271 
 272         default:
 273                 break;
 274         }
 275 
 276         return (rval);
 277 }
 278 
 279 /*
 280  * Parse the contents of the buffer, and bind the named
 281  * devices as auxiliary consoles using our own ioctl routine.
 282  *
 283  * Comments begin with '#' and are terminated only by a newline
 284  * Device names begin with a '/', and are terminated by a newline,
 285  * space, '#' or tab.
 286  */
 287 static void
 288 parse_buffer(char *buf, ssize_t fsize)
 289 {
 290         char *ebuf = buf + fsize;
 291         char *devname = NULL;
 292         int eatcomments = 0;
 293 
 294         while (buf < ebuf) {
 295                 if (eatcomments) {
 296                         if (*buf++ == '\n')
 297                                 eatcomments = 0;
 298                         continue;
 299                 }
 300                 switch (*buf) {
 301                 case '/':
 302                         if (devname == NULL)
 303                                 devname = buf;
 304                         break;
 305                 case '#':
 306                         eatcomments = 1;
 307                         /*FALLTHROUGH*/
 308                 case ' ':
 309                 case '\t':
 310                 case '\n':
 311                         *buf = '\0';
 312                         if (devname == NULL)
 313                                 break;
 314                         (void) sysmioctl(NODEV, CIOCSETCONSOLE,
 315                             (intptr_t)devname, FNATIVE|FKIOCTL|FREAD|FWRITE,
 316                             kcred, NULL);
 317                         devname = NULL;
 318                         break;
 319                 default:
 320                         break;
 321                 }
 322                 buf++;
 323         }
 324 }
 325 
 326 #define CNSADM_BYTES_MAX        2000    /* XXX  nasty fixed size */
 327 
 328 static void
 329 bind_consadm_conf(char *path)
 330 {
 331         struct vattr vattr;
 332         vnode_t *vp;
 333         void *buf;
 334         size_t size;
 335         ssize_t resid;
 336         int err = 0;
 337 
 338         if (vn_open(path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0)
 339                 return;
 340         vattr.va_mask = AT_SIZE;
 341         if ((err = VOP_GETATTR(vp, &vattr, 0, kcred, NULL)) != 0) {
 342                 cmn_err(CE_WARN, "sysmsg: getattr: '%s': error %d",
 343                     path, err);
 344                 goto closevp;
 345         }
 346 
 347         size = vattr.va_size > CNSADM_BYTES_MAX ?
 348             CNSADM_BYTES_MAX : (ssize_t)vattr.va_size;
 349         buf = kmem_alloc(size, KM_SLEEP);
 350 
 351         if ((err = vn_rdwr(UIO_READ, vp, buf, size, (offset_t)0,
 352             UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid)) != 0)
 353                 cmn_err(CE_WARN, "sysmsg: vn_rdwr: '%s': error %d",
 354                     path, err);
 355         else
 356                 parse_buffer(buf, size - resid);
 357 
 358         kmem_free(buf, size);
 359 closevp:
 360         (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred, NULL);
 361         VN_RELE(vp);
 362 }
 363 
 364 /* ARGSUSED */
 365 static int
 366 sysmopen(dev_t *dev, int flag, int state, cred_t *cred)
 367 {
 368         int     i;
 369         vnode_t *vp;
 370         minor_t instance;
 371         static boolean_t initialized;
 372 
 373         instance = getminor(*dev);
 374 
 375         if (state != OTYP_CHR || (instance != 0 && instance != 1))
 376                 return (ENXIO);
 377 
 378         mutex_enter(&dcvp_mutex);
 379         if ((dcvp == NULL) && (vn_open("/dev/console",
 380             UIO_SYSSPACE, FWRITE, 0, &dcvp, 0, 0) != 0)) {
 381                 mutex_exit(&dcvp_mutex);
 382                 return (ENXIO);
 383         }
 384 
 385         if (instance == SYS_SYSMIN)
 386                 sysmsg_opened = B_TRUE;
 387         else
 388                 msglog_opened = B_TRUE;
 389 
 390         if (!initialized) {
 391                 bind_consadm_conf("/etc/consadm.conf");
 392                 initialized = B_TRUE;
 393         }
 394         mutex_exit(&dcvp_mutex);
 395 
 396         for (i = 0; i < MAXDEVS; i++) {
 397                 rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
 398                 if ((sysmcache[i].dca_flags & SYSM_ENABLED) &&
 399                     sysmcache[i].dca_vp == NULL) {
 400                         /*
 401                          * 4196476 - FTRUNC was causing E10K to return EINVAL
 402                          * on open
 403                          */
 404                         flag = flag & ~FTRUNC;
 405                         /*
 406                          * Open failures on the auxiliary consoles are
 407                          * not returned because we don't care if some
 408                          * subset get an error. We know the default console
 409                          * is okay, and preserve the semantics of the
 410                          * open for the default console.
 411                          * Set NONBLOCK|NDELAY in case there's no carrier.
 412                          */
 413                         if (vn_open(sysmcache[i].dca_name, UIO_SYSSPACE,
 414                             flag | FNONBLOCK | FNDELAY, 0, &vp, 0, 0) == 0)
 415                                 sysmcache[i].dca_vp = vp;
 416                 }
 417                 rw_exit(&sysmcache[i].dca_lock);
 418         }
 419 
 420         return (0);
 421 }
 422 
 423 /* ARGSUSED */
 424 static int
 425 sysmclose(dev_t dev, int flag, int state, cred_t *cred)
 426 {
 427         int     i;
 428         minor_t instance;
 429 
 430         ASSERT(dcvp != NULL);
 431 
 432         if (state != OTYP_CHR)
 433                 return (ENXIO);
 434 
 435         instance = getminor(dev);
 436 
 437         mutex_enter(&dcvp_mutex);
 438         if (instance == SYS_SYSMIN)
 439                 sysmsg_opened = B_FALSE;
 440         else
 441                 msglog_opened = B_FALSE;
 442 
 443         if (sysmsg_opened || msglog_opened) {
 444                 mutex_exit(&dcvp_mutex);
 445                 return (0);
 446         }
 447 
 448         (void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred, NULL);
 449         VN_RELE(dcvp);
 450         dcvp = NULL;
 451         mutex_exit(&dcvp_mutex);
 452 
 453         /*
 454          * Close the auxiliary consoles, we're not concerned with
 455          * passing up the errors.
 456          */
 457         for (i = 0; i < MAXDEVS; i++) {
 458                 rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
 459                 if (sysmcache[i].dca_vp != NULL) {
 460                         (void) VOP_CLOSE(sysmcache[i].dca_vp, flag,
 461                             1, (offset_t)0, cred, NULL);
 462                         VN_RELE(sysmcache[i].dca_vp);
 463                         sysmcache[i].dca_vp = NULL;
 464                 }
 465                 rw_exit(&sysmcache[i].dca_lock);
 466         }
 467 
 468         return (0);
 469 }
 470 
 471 /* Reads occur only on the default console */
 472 
 473 /* ARGSUSED */
 474 static int
 475 sysmread(dev_t dev, struct uio *uio, cred_t *cred)
 476 {
 477         ASSERT(dcvp != NULL);
 478         return (VOP_READ(dcvp, uio, 0, cred, NULL));
 479 }
 480 
 481 /* ARGSUSED */
 482 static int
 483 sysmwrite(dev_t dev, struct uio *uio, cred_t *cred)
 484 {
 485         int     i = 0;
 486         iovec_t uio_iov;
 487         struct uio      tuio;
 488 
 489         ASSERT(dcvp != NULL);
 490         ASSERT(uio != NULL);
 491 
 492         for (i = 0; i < MAXDEVS; i++) {
 493                 rw_enter(&sysmcache[i].dca_lock, RW_READER);
 494                 if (sysmcache[i].dca_vp != NULL &&
 495                     (sysmcache[i].dca_flags & SYSM_ENABLED)) {
 496                         tuio = *uio;
 497                         uio_iov = *(uio->uio_iov);
 498                         tuio.uio_iov = &uio_iov;
 499                         (void) VOP_WRITE(sysmcache[i].dca_vp, &tuio, 0, cred,
 500                             NULL);
 501                 }
 502                 rw_exit(&sysmcache[i].dca_lock);
 503         }
 504         return (VOP_WRITE(dcvp, uio, 0, cred, NULL));
 505 }
 506 
 507 /* ARGSUSED */
 508 static int
 509 sysmioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred, int *rvalp)
 510 {
 511         int     rval = 0;
 512         int     error = 0;
 513         size_t  size = 0;
 514         int     i;
 515         char    *infop;
 516         char    found = 0;
 517         dev_t   newdevt = (dev_t)NODEV; /* because 0 == /dev/console */
 518         vnode_t *vp;
 519 
 520         switch (cmd) {
 521         case CIOCGETCONSOLE:
 522                 /* Sum over the number of enabled devices */
 523                 for (i = 0; i < MAXDEVS; i++) {
 524                         if (sysmcache[i].dca_flags & SYSM_ENABLED)
 525                                 /* list is space separated, followed by NULL */
 526                                 size += strlen(sysmcache[i].dca_name) + 1;
 527                 }
 528                 if (size == 0)
 529                         return (0);
 530                 break;
 531         case CIOCSETCONSOLE:
 532         case CIOCRMCONSOLE:
 533                 size = sizeof (sysmcache[0].dca_name);
 534                 break;
 535         case CIOCTTYCONSOLE:
 536         {
 537                 dev_t   d;
 538                 dev32_t d32;
 539                 extern dev_t rwsconsdev, rconsdev, uconsdev;
 540                 proc_t  *p;
 541 
 542                 if (drv_getparm(UPROCP, &p) != 0)
 543                         return (ENODEV);
 544                 else
 545                         d = cttydev(p);
 546                 /*
 547                  * If the controlling terminal is the real
 548                  * or workstation console device, map to what the
 549                  * user thinks is the console device.
 550                  */
 551                 if (d == rwsconsdev || d == rconsdev)
 552                         d = uconsdev;
 553                 if ((flag & FMODELS) != FNATIVE) {
 554                         if (!cmpldev(&d32, d))
 555                                 return (EOVERFLOW);
 556                         if (ddi_copyout(&d32, (caddr_t)arg, sizeof (d32),
 557                             flag))
 558                                 return (EFAULT);
 559                 } else {
 560                         if (ddi_copyout(&d, (caddr_t)arg, sizeof (d), flag))
 561                                 return (EFAULT);
 562                 }
 563                 return (0);
 564         }
 565         default:
 566                 /* everything else is sent to the console device */
 567                 return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp, NULL));
 568         }
 569 
 570         if ((rval = secpolicy_console(cred)) != 0)
 571                 return (EPERM);
 572 
 573         infop = kmem_alloc(size, KM_SLEEP);
 574         if (flag & FKIOCTL)
 575                 error = copystr((caddr_t)arg, infop, size, NULL);
 576         else
 577                 error = copyinstr((caddr_t)arg, infop, size, NULL);
 578 
 579         if (error) {
 580                 switch (cmd) {
 581                 case CIOCGETCONSOLE:
 582                         /*
 583                          * If the buffer is null, then return a byte count
 584                          * to user land.
 585                          */
 586                         *rvalp = size;
 587                         goto err_exit;
 588                 default:
 589                         rval = EFAULT;
 590                         goto err_exit;
 591                 }
 592         }
 593 
 594         if (infop[0] != NULL) {
 595                 if ((rval = lookupname(infop, UIO_SYSSPACE, FOLLOW,
 596                     NULLVPP, &vp)) == 0) {
 597                         if (vp->v_type != VCHR) {
 598                                 VN_RELE(vp);
 599                                 rval = EINVAL;
 600                                 goto err_exit;
 601                         }
 602                         newdevt = vp->v_rdev;
 603                         VN_RELE(vp);
 604                 } else
 605                         goto err_exit;
 606         }
 607 
 608         switch (cmd) {
 609         case CIOCGETCONSOLE:
 610                 /*
 611                  * Return the list of device names that are enabled.
 612                  */
 613                 for (i = 0; i < MAXDEVS; i++) {
 614                         rw_enter(&sysmcache[i].dca_lock, RW_READER);
 615                         if (sysmcache[i].dca_flags & SYSM_ENABLED) {
 616                                 if (infop[0] != NULL)
 617                                         (void) strcat(infop, " ");
 618                                 (void) strcat(infop, sysmcache[i].dca_name);
 619                         }
 620                         rw_exit(&sysmcache[i].dca_lock);
 621                 }
 622                 if (rval == 0 && copyoutstr(infop, (void *)arg, size, NULL))
 623                         rval = EFAULT;
 624                 break;
 625 
 626         case CIOCSETCONSOLE:
 627                 if ((rval = checkarg(newdevt)) != 0)
 628                         break;
 629                 /*
 630                  * The device does not have to be open or disabled to
 631                  * perform the set console.
 632                  */
 633                 for (i = 0; i < MAXDEVS; i++) {
 634                         rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
 635                         if (sysmcache[i].dca_devt == newdevt &&
 636                             (sysmcache[i].dca_flags & SYSM_ENABLED)) {
 637                                 (void) strcpy(sysmcache[i].dca_name, infop);
 638                                 rval = EEXIST;
 639                                 rw_exit(&sysmcache[i].dca_lock);
 640                                 break;
 641                         } else if (sysmcache[i].dca_devt == newdevt &&
 642                             sysmcache[i].dca_flags == SYSM_DISABLED) {
 643                                 sysmcache[i].dca_flags |= SYSM_ENABLED;
 644                                 (void) strcpy(sysmcache[i].dca_name, infop);
 645                                 rw_exit(&sysmcache[i].dca_lock);
 646                                 found = 1;
 647                                 break;
 648                         } else if (sysmcache[i].dca_devt == 0) {
 649                                 ASSERT(sysmcache[i].dca_vp == NULL &&
 650                                     sysmcache[i].dca_flags == SYSM_DISABLED);
 651                                 (void) strcpy(sysmcache[i].dca_name, infop);
 652                                 sysmcache[i].dca_flags = SYSM_ENABLED;
 653                                 sysmcache[i].dca_devt = newdevt;
 654                                 rw_exit(&sysmcache[i].dca_lock);
 655                                 found = 1;
 656                                 break;
 657                         }
 658                         rw_exit(&sysmcache[i].dca_lock);
 659                 }
 660                 if (found == 0 && rval == 0)
 661                         rval = ENOENT;
 662                 break;
 663 
 664         case CIOCRMCONSOLE:
 665                 for (i = 0; i < MAXDEVS; i++) {
 666                         rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
 667                         if (sysmcache[i].dca_devt == newdevt) {
 668                                 sysmcache[i].dca_flags = SYSM_DISABLED;
 669                                 sysmcache[i].dca_name[0] = '\0';
 670                                 rw_exit(&sysmcache[i].dca_lock);
 671                                 found = 1;
 672                                 break;
 673                         }
 674                         rw_exit(&sysmcache[i].dca_lock);
 675                 }
 676                 if (found == 0)
 677                         rval = ENOENT;
 678                 break;
 679 
 680         default:
 681                 break;
 682         }
 683 
 684 err_exit:
 685         kmem_free(infop, size);
 686         return (rval);
 687 }
 688 
 689 /* As with the read, we poll only the default console */
 690 
 691 /* ARGSUSED */
 692 static int
 693 sysmpoll(dev_t dev, short events, int anyyet, short *reventsp,
 694         struct pollhead **phpp)
 695 {
 696         return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp, NULL));
 697 }
 698 
 699 /* Sanity check that the device is good */
 700 static int
 701 checkarg(dev_t devt)
 702 {
 703         int rval = 0;
 704         dev_t sysmsg_dev, msglog_dev;
 705         extern dev_t rwsconsdev, rconsdev, uconsdev;
 706 
 707         if (devt == rconsdev || devt == rwsconsdev || devt == uconsdev) {
 708                 rval = EBUSY;
 709         } else {
 710                 sysmsg_dev = makedevice(ddi_driver_major(sysm_dip), SYS_SYSMIN);
 711                 msglog_dev = makedevice(ddi_driver_major(sysm_dip), SYS_MSGMIN);
 712                 if (devt == sysmsg_dev || devt == msglog_dev)
 713                         rval = EINVAL;
 714         }
 715 
 716         return (rval);
 717 }