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 } 718