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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * System message redirection driver for Sun.
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.
36 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/signal.h>
45 #include <sys/vnode.h>
49 #include <sys/session.h>
50 #include <sys/stream.h>
51 #include <sys/strsubr.h>
53 #include <sys/debug.h>
54 #include <sys/sysmsg_impl.h>
56 #include <sys/termios.h>
57 #include <sys/errno.h>
58 #include <sys/modctl.h>
59 #include <sys/pathname.h>
61 #include <sys/sunddi.h>
62 #include <sys/consdev.h>
63 #include <sys/policy.h>
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
);
80 static dev_info_t
*sysm_dip
; /* private copy of devinfo pointer */
82 static struct cb_ops sysm_cb_ops
= {
85 sysmclose
, /* close */
90 sysmwrite
, /* write */
91 sysmioctl
, /* ioctl */
96 ddi_prop_op
, /* cb_prop_op */
98 D_NEW
| D_MP
, /* Driver compatibility flag */
104 static struct dev_ops sysm_ops
= {
106 DEVO_REV
, /* devo_rev, */
108 sysm_info
, /* info */
109 nulldev
, /* identify */
111 sysm_attach
, /* attach */
112 sysm_detach
, /* detach */
114 &sysm_cb_ops
, /* driver operations */
115 NULL
, /* bus operations */
117 ddi_quiesce_not_needed
, /* quiesce */
122 * Global variables associated with the console device:
125 #define SYS_SYSMIN 0 /* sysmsg minor number */
126 #define SYS_MSGMIN 1 /* msglog minor number */
127 #define SYSPATHLEN 255 /* length of device path */
130 * Private driver state:
140 char dca_name
[SYSPATHLEN
];
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
;
150 /* flags for device cache */
151 #define SYSM_DISABLED 0x0
152 #define SYSM_ENABLED 0x1
155 * Module linkage information for the kernel.
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 */
164 static struct modlinkage modlinkage
= {
173 return (mod_install(&modlinkage
));
179 return (mod_remove(&modlinkage
));
183 _info(struct modinfo
*modinfop
)
185 return (mod_info(&modlinkage
, modinfop
));
192 sysm_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
198 ASSERT(sysm_dip
== NULL
);
200 if (ddi_create_minor_node(devi
, "sysmsg", S_IFCHR
,
201 SYS_SYSMIN
, DDI_PSEUDO
, 0) == DDI_FAILURE
||
202 ddi_create_minor_node(devi
, "msglog", S_IFCHR
,
203 SYS_MSGMIN
, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
204 ddi_remove_minor_node(devi
, NULL
);
205 return (DDI_FAILURE
);
208 for (i
= 0; i
< MAXDEVS
; i
++) {
209 rw_init(&sysmcache
[i
].dca_lock
, NULL
, RW_DRIVER
, NULL
);
213 return (DDI_SUCCESS
);
216 return (DDI_SUCCESS
);
218 return (DDI_FAILURE
);
223 sysm_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
229 ASSERT(sysm_dip
== devi
);
231 for (i
= 0; i
< MAXDEVS
; i
++)
232 rw_destroy(&sysmcache
[i
].dca_lock
);
234 ddi_remove_minor_node(devi
, NULL
);
236 return (DDI_SUCCESS
);
240 return (DDI_SUCCESS
);
242 return (DDI_FAILURE
);
249 sysm_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
251 int rval
= DDI_FAILURE
;
254 instance
= getminor((dev_t
)arg
);
257 case DDI_INFO_DEVT2DEVINFO
:
258 if (sysm_dip
!= NULL
&&
259 (instance
== SYS_SYSMIN
|| instance
== SYS_MSGMIN
)) {
265 case DDI_INFO_DEVT2INSTANCE
:
266 if (instance
== SYS_SYSMIN
|| instance
== SYS_MSGMIN
) {
280 * Parse the contents of the buffer, and bind the named
281 * devices as auxiliary consoles using our own ioctl routine.
283 * Comments begin with '#' and are terminated only by a newline
284 * Device names begin with a '/', and are terminated by a newline,
288 parse_buffer(char *buf
, ssize_t fsize
)
290 char *ebuf
= buf
+ fsize
;
291 char *devname
= NULL
;
314 (void) sysmioctl(NODEV
, CIOCSETCONSOLE
,
315 (intptr_t)devname
, FNATIVE
|FKIOCTL
|FREAD
|FWRITE
,
326 #define CNSADM_BYTES_MAX 2000 /* XXX nasty fixed size */
329 bind_consadm_conf(char *path
)
338 if (vn_open(path
, UIO_SYSSPACE
, FREAD
, 0, &vp
, 0, 0) != 0)
340 vattr
.va_mask
= VATTR_SIZE
;
341 if ((err
= fop_getattr(vp
, &vattr
, 0, kcred
, NULL
)) != 0) {
342 cmn_err(CE_WARN
, "sysmsg: getattr: '%s': error %d",
347 size
= vattr
.va_size
> CNSADM_BYTES_MAX
?
348 CNSADM_BYTES_MAX
: (ssize_t
)vattr
.va_size
;
349 buf
= kmem_alloc(size
, KM_SLEEP
);
351 if ((err
= vn_rdwr(UIO_READ
, vp
, buf
, size
, 0,
352 UIO_SYSSPACE
, 0, 0, kcred
, &resid
)) != 0)
353 cmn_err(CE_WARN
, "sysmsg: vn_rdwr: '%s': error %d",
356 parse_buffer(buf
, size
- resid
);
358 kmem_free(buf
, size
);
360 (void) fop_close(vp
, FREAD
, 1, 0, kcred
, NULL
);
366 sysmopen(dev_t
*dev
, int flag
, int state
, cred_t
*cred
)
371 static boolean_t initialized
;
373 instance
= getminor(*dev
);
375 if (state
!= OTYP_CHR
|| (instance
!= 0 && instance
!= 1))
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
);
385 if (instance
== SYS_SYSMIN
)
386 sysmsg_opened
= B_TRUE
;
388 msglog_opened
= B_TRUE
;
391 bind_consadm_conf("/etc/consadm.conf");
392 initialized
= B_TRUE
;
394 mutex_exit(&dcvp_mutex
);
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
) {
401 * 4196476 - FTRUNC was causing E10K to return EINVAL
404 flag
= flag
& ~FTRUNC
;
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.
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
;
417 rw_exit(&sysmcache
[i
].dca_lock
);
425 sysmclose(dev_t dev
, int flag
, int state
, cred_t
*cred
)
430 ASSERT(dcvp
!= NULL
);
432 if (state
!= OTYP_CHR
)
435 instance
= getminor(dev
);
437 mutex_enter(&dcvp_mutex
);
438 if (instance
== SYS_SYSMIN
)
439 sysmsg_opened
= B_FALSE
;
441 msglog_opened
= B_FALSE
;
443 if (sysmsg_opened
|| msglog_opened
) {
444 mutex_exit(&dcvp_mutex
);
448 (void) fop_close(dcvp
, FWRITE
, 1, 0, kcred
, NULL
);
451 mutex_exit(&dcvp_mutex
);
454 * Close the auxiliary consoles, we're not concerned with
455 * passing up the errors.
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) fop_close(sysmcache
[i
].dca_vp
, flag
,
462 VN_RELE(sysmcache
[i
].dca_vp
);
463 sysmcache
[i
].dca_vp
= NULL
;
465 rw_exit(&sysmcache
[i
].dca_lock
);
471 /* Reads occur only on the default console */
475 sysmread(dev_t dev
, struct uio
*uio
, cred_t
*cred
)
477 ASSERT(dcvp
!= NULL
);
478 return (fop_read(dcvp
, uio
, 0, cred
, NULL
));
483 sysmwrite(dev_t dev
, struct uio
*uio
, cred_t
*cred
)
489 ASSERT(dcvp
!= NULL
);
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
)) {
497 uio_iov
= *(uio
->uio_iov
);
498 tuio
.uio_iov
= &uio_iov
;
499 (void) fop_write(sysmcache
[i
].dca_vp
, &tuio
, 0, cred
,
502 rw_exit(&sysmcache
[i
].dca_lock
);
504 return (fop_write(dcvp
, uio
, 0, cred
, NULL
));
509 sysmioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
, cred_t
*cred
, int *rvalp
)
517 dev_t newdevt
= (dev_t
)NODEV
; /* because 0 == /dev/console */
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;
533 size
= sizeof (sysmcache
[0].dca_name
);
539 extern dev_t rwsconsdev
, rconsdev
, uconsdev
;
542 if (drv_getparm(UPROCP
, &p
) != 0)
547 * If the controlling terminal is the real
548 * or workstation console device, map to what the
549 * user thinks is the console device.
551 if (d
== rwsconsdev
|| d
== rconsdev
)
553 if ((flag
& FMODELS
) != FNATIVE
) {
554 if (!cmpldev(&d32
, d
))
556 if (ddi_copyout(&d32
, (caddr_t
)arg
, sizeof (d32
),
560 if (ddi_copyout(&d
, (caddr_t
)arg
, sizeof (d
), flag
))
566 /* everything else is sent to the console device */
567 return (fop_ioctl(dcvp
, cmd
, arg
, flag
, cred
, rvalp
, NULL
));
570 if ((rval
= secpolicy_console(cred
)) != 0)
573 infop
= kmem_alloc(size
, KM_SLEEP
);
575 error
= copystr((caddr_t
)arg
, infop
, size
, NULL
);
577 error
= copyinstr((caddr_t
)arg
, infop
, size
, NULL
);
583 * If the buffer is null, then return a byte count
594 if (infop
[0] != '\0') {
595 if ((rval
= lookupname(infop
, UIO_SYSSPACE
, FOLLOW
,
596 NULLVPP
, &vp
)) == 0) {
597 if (vp
->v_type
!= VCHR
) {
602 newdevt
= vp
->v_rdev
;
611 * Return the list of device names that are enabled.
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] != '\0')
617 (void) strcat(infop
, " ");
618 (void) strcat(infop
, sysmcache
[i
].dca_name
);
620 rw_exit(&sysmcache
[i
].dca_lock
);
622 if (rval
== 0 && copyoutstr(infop
, (void *)arg
, size
, NULL
))
627 if ((rval
= checkarg(newdevt
)) != 0)
630 * The device does not have to be open or disabled to
631 * perform the set console.
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
);
639 rw_exit(&sysmcache
[i
].dca_lock
);
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
);
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
);
658 rw_exit(&sysmcache
[i
].dca_lock
);
660 if (found
== 0 && rval
== 0)
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
);
674 rw_exit(&sysmcache
[i
].dca_lock
);
685 kmem_free(infop
, size
);
689 /* As with the read, we poll only the default console */
693 sysmpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
694 struct pollhead
**phpp
)
696 return (fop_poll(dcvp
, events
, anyyet
, reventsp
, phpp
, NULL
));
699 /* Sanity check that the device is good */
704 dev_t sysmsg_dev
, msglog_dev
;
705 extern dev_t rwsconsdev
, rconsdev
, uconsdev
;
707 if (devt
== rconsdev
|| devt
== rwsconsdev
|| devt
== uconsdev
) {
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
)