diff options
author | ed <ed@FreeBSD.org> | 2008-08-20 08:31:58 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2008-08-20 08:31:58 +0000 |
commit | cc3116a9380fe32a751b584f3d8083698ccfba15 (patch) | |
tree | bd0c08a66997254385160ce71ea32029b99f99f9 | |
parent | b49301b5cd9ff43a7af0bd9054d9d1a328c0d212 (diff) | |
download | FreeBSD-src-cc3116a9380fe32a751b584f3d8083698ccfba15.zip FreeBSD-src-cc3116a9380fe32a751b584f3d8083698ccfba15.tar.gz |
Integrate the new MPSAFE TTY layer to the FreeBSD operating system.
The last half year I've been working on a replacement TTY layer for the
FreeBSD kernel. The new TTY layer was designed to improve the following:
- Improved driver model:
The old TTY layer has a driver model that is not abstract enough to
make it friendly to use. A good example is the output path, where the
device drivers directly access the output buffers. This means that an
in-kernel PPP implementation must always convert network buffers into
TTY buffers.
If a PPP implementation would be built on top of the new TTY layer
(still needs a hooks layer, though), it would allow the PPP
implementation to directly hand the data to the TTY driver.
- Improved hotplugging:
With the old TTY layer, it isn't entirely safe to destroy TTY's from
the system. This implementation has a two-step destructing design,
where the driver first abandons the TTY. After all threads have left
the TTY, the TTY layer calls a routine in the driver, which can be
used to free resources (unit numbers, etc).
The pts(4) driver also implements this feature, which means
posix_openpt() will now return PTY's that are created on the fly.
- Improved performance:
One of the major improvements is the per-TTY mutex, which is expected
to improve scalability when compared to the old Giant locking.
Another change is the unbuffered copying to userspace, which is both
used on TTY device nodes and PTY masters.
Upgrading should be quite straightforward. Unlike previous versions,
existing kernel configuration files do not need to be changed, except
when they reference device drivers that are listed in UPDATING.
Obtained from: //depot/projects/mpsafetty/...
Approved by: philip (ex-mentor)
Discussed: on the lists, at BSDCan, at the DevSummit
Sponsored by: Snow B.V., the Netherlands
dcons(4) fixed by: kan
110 files changed, 6143 insertions, 6960 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index 18b44c3..60f3c53 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -14,6 +14,9 @@ # The file is partitioned: OLD_FILES first, then OLD_LIBS and OLD_DIRS last. # +# 20080820: MPSAFE TTY layer integrated +OLD_FILES+=usr/include/sys/linedisc.h +OLD_FILES+=usr/share/man/man3/posix_openpt.3.gz # 20080725: sgtty.h removed OLD_FILES+=usr/include/sgtty.h # 20080719: sade(8) removed on all but amd64, i386 and sparc64 @@ -22,6 +22,25 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.x IS SLOW: to maximize performance. (To disable malloc debugging, run ln -s aj /etc/malloc.conf.) +20080820: + The TTY subsystem of the kernel has been replaced by a new + implementation, which provides better scalability and an + improved driver model. Most common drivers have been migrated to + the new TTY subsystem, while others have not. The following + drivers have not yet been ported to the new TTY layer: + + PCI/ISA: + cy, digi, rc, rp, si, sio + + USB: + ubser, ucycom, ufoma + + Line disciplines: + ng_h4, ng_tty, ppp, sl, snp + + Adding these drivers to your kernel configuration file shall + cause compilation to fail. + 20080713: The sio(4) driver has been removed from the i386 and amd64 kernel configuration files. This means uart(4) is now the diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c index 4ca4fcd..6496f4a 100644 --- a/bin/sh/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -342,6 +342,9 @@ static const struct limits limits[] = { #ifdef RLIMIT_SBSIZE { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, #endif +#ifdef RLIMIT_NPTS + { "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' }, +#endif { (char *) 0, (char *)0, 0, 0, '\0' } }; @@ -358,7 +361,7 @@ ulimitcmd(int argc __unused, char **argv __unused) struct rlimit limit; what = 'f'; - while ((optc = nextopt("HSatfdsmcnuvlb")) != '\0') + while ((optc = nextopt("HSatfdsmcnuvlbp")) != '\0') switch (optc) { case 'H': how = HARD; diff --git a/etc/defaults/devfs.rules b/etc/defaults/devfs.rules index 70fbe03..a00b75d 100644 --- a/etc/defaults/devfs.rules +++ b/etc/defaults/devfs.rules @@ -52,8 +52,9 @@ add path 'ttyP*' unhide add path 'ttyQ*' unhide add path 'ttyR*' unhide add path 'ttyS*' unhide +add path ptmx unhide +add path pts unhide add path 'pts/*' unhide -add path 'pty/*' unhide add path fd unhide add path 'fd/*' unhide add path stdin unhide diff --git a/etc/login.conf b/etc/login.conf index 9e8d02c..8d2e16e 100644 --- a/etc/login.conf +++ b/etc/login.conf @@ -40,6 +40,7 @@ default:\ :maxproc=unlimited:\ :sbsize=unlimited:\ :vmemoryuse=unlimited:\ + :pseudoterminals=unlimited:\ :priority=0:\ :ignoretime@:\ :umask=022: diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index f9b8fec..86b3c65 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -6,9 +6,9 @@ MISRCS+=_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c div.c exit.c getenv.c getopt.c getopt_long.c \ - getsubopt.c grantpt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c \ + getsubopt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c malloc.c \ - merge.c qsort.c qsort_r.c radixsort.c rand.c random.c \ + merge.c ptsname.c qsort.c qsort_r.c radixsort.c rand.c random.c \ reallocf.c realpath.c remque.c strfmon.c strtoimax.c \ strtol.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \ strtoumax.c strtouq.c system.c tdelete.c tfind.c tsearch.c twalk.c @@ -21,10 +21,10 @@ SYM_MAPS+= ${.CURDIR}/stdlib/Symbol.map .endif MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 atoi.3 atol.3 bsearch.3 \ - div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 grantpt.3 \ + div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 llabs.3 lldiv.3 \ - lsearch.3 malloc.3 memory.3 posix_memalign.3 qsort.3 radixsort.3 \ - rand.3 random.3 \ + lsearch.3 malloc.3 memory.3 posix_memalign.3 ptsname.3 qsort.3 \ + radixsort.3 rand.3 random.3 \ realpath.3 strfmon.3 strtod.3 strtol.3 strtonum.3 strtoul.3 system.3 \ tsearch.3 @@ -33,10 +33,10 @@ MLINKS+=atol.3 atoll.3 MLINKS+=exit.3 _Exit.3 MLINKS+=getenv.3 putenv.3 getenv.3 setenv.3 getenv.3 unsetenv.3 MLINKS+=getopt_long.3 getopt_long_only.3 -MLINKS+=grantpt.3 posix_openpt.3 grantpt.3 ptsname.3 grantpt.3 unlockpt.3 MLINKS+=hcreate.3 hdestroy.3 hcreate.3 hsearch.3 MLINKS+=insque.3 remque.3 MLINKS+=lsearch.3 lfind.3 +MLINKS+=ptsname.3 grantpt.3 ptsname.3 unlockpt.3 MLINKS+=qsort.3 heapsort.3 qsort.3 mergesort.3 qsort.3 qsort_r.3 MLINKS+=rand.3 rand_r.3 rand.3 srand.3 rand.3 sranddev.3 MLINKS+=random.3 initstate.3 random.3 setstate.3 random.3 srandom.3 \ diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index 23cb391..10dff7e 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -30,7 +30,6 @@ FBSD_1.0 { suboptarg; getsubopt; grantpt; - posix_openpt; ptsname; unlockpt; hcreate; diff --git a/lib/libc/stdlib/grantpt.3 b/lib/libc/stdlib/ptsname.3 index b4ad8c4..b9c7381 100644 --- a/lib/libc/stdlib/grantpt.3 +++ b/lib/libc/stdlib/ptsname.3 @@ -31,14 +31,13 @@ .\" .\" $FreeBSD$ .\" -.Dd December 23, 2002 +.Dd August 20, 2008 .Os -.Dt GRANTPT 3 +.Dt PTSNAME 3 .Sh NAME .Nm grantpt , .Nm ptsname , -.Nm unlockpt , -.Nm posix_openpt +.Nm unlockpt .Nd pseudo-terminal access functions .Sh LIBRARY .Lb libc @@ -50,21 +49,17 @@ .Fn ptsname "int fildes" .Ft int .Fn unlockpt "int fildes" -.In fcntl.h -.Ft int -.Fn posix_openpt "int mode" .Sh DESCRIPTION The .Fn grantpt , .Fn ptsname , -.Fn unlockpt , and -.Fn posix_openpt +.Fn unlockpt functions allow access to pseudo-terminal devices. -The first three functions accept a file descriptor -that references the master half of a pseudo-terminal pair. +These three functions accept a file descriptor that references the +master half of a pseudo-terminal pair. This file descriptor is created with -.Fn posix_openpt . +.Xr posix_openpt 2 . .Pp The .Fn grantpt @@ -77,9 +72,7 @@ of the calling process, and the permissions are set to user readable-writable and group writable. The group owner of the slave device is also set to the group -.Dq Li tty -if it exists on the system; otherwise, it -is left untouched. +.Dq Li tty . .Pp The .Fn ptsname @@ -88,7 +81,7 @@ counterpart to the master device specified with .Fa fildes . This value can be used to subsequently open the appropriate slave after -.Fn posix_openpt +.Xr posix_openpt 2 and .Fn grantpt have been called. @@ -98,22 +91,6 @@ The function clears the lock held on the pseudo-terminal pair for the master device specified with .Fa fildes . -.Pp -The -.Fn posix_openpt -function opens the first available master pseudo-terminal -device and returns a descriptor to it. -The -.Fa mode -argument -specifies the flags used for opening the device: -.Bl -tag -width ".Dv O_NOCTTY" -.It Dv O_RDWR -Open for reading and writing. -.It Dv O_NOCTTY -If set, do not allow the terminal to become -the controlling terminal for the calling process. -.El .Sh RETURN VALUES .Rv -std grantpt unlockpt .Pp @@ -122,27 +99,19 @@ The function returns a pointer to the name of the slave device on success; otherwise a .Dv NULL -pointer is returned and the global variable -.Va errno -is set to indicate the error. -.Pp -The -.Fn posix_openpt -function returns a file descriptor to the first -available master pseudo-terminal device on success; -otherwise \-1 is returned and the global variable -.Va errno -is set to indicate the error. +pointer is returned. .Sh ERRORS The -.Fn grantpt , -.Fn ptsname , +.Fn grantpt and .Fn unlockpt functions may fail and set .Va errno to: .Bl -tag -width Er +.It Bq Er EBADF +.Fa fildes +is not a valid open file descriptor. .It Bq Er EINVAL .Fa fildes is not a master pseudo-terminal device. @@ -157,69 +126,35 @@ to: .It Bq Er EACCES The slave pseudo-terminal device could not be accessed. .El -.Pp -The -.Fn posix_openpt -function may fail and set -.Va errno -to: -.Bl -tag -width Er -.It Bq Er EINVAL -.Fa mode -consists of an invalid mode bit. -.It Bq Er EAGAIN -The system has no available pseudo-terminal devices. -.El -.Pp -The -.Fn grantpt , -.Fn ptsname , -and -.Fn unlockpt -functions may also fail and set -.Va errno -for any of the errors specified for the -.Xr fstat 2 -system call. -.Pp -The -.Fn posix_openpt -function may also fail and set -.Va errno -for any of the errors specified for the -.Xr open 2 -system call. .Sh SEE ALSO -.Xr open 2 , -.Xr pty 4 , +.Xr posix_openpt 2 , +.Xr pts 4 , .Xr tty 4 .Sh STANDARDS The .Fn grantpt , -.Fn ptsname , -.Fn unlockpt , +.Fn ptsname and -.Fn posix_openpt +.Fn unlockpt functions conform to .St -p1003.1-2001 . .Sh HISTORY The .Fn grantpt , -.Fn ptsname , -.Fn unlockpt , +.Fn ptsname and -.Fn posix_openpt +.Fn unlockpt functions appeared in .Fx 5.0 . .Sh NOTES The purpose of the +.Fn grantpt +and .Fn unlockpt -function has no meaning in -.Fx . -.Pp -The flag -.Dv O_NOCTTY -is included for compatibility; in +functions has no meaning in .Fx , -opening a terminal does not cause it to become -a process's controlling terminal. +because pseudo-terminals obtained by +.Xr posix_openpt 2 +are created on demand. +Because these devices are created with proper permissions in place, they +are guaranteed to be unused by unprivileged processes. diff --git a/lib/libc/stdlib/ptsname.c b/lib/libc/stdlib/ptsname.c new file mode 100644 index 0000000..fa606f6 --- /dev/null +++ b/lib/libc/stdlib/ptsname.c @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__FBSDID("$FreeBSD$"); +#endif /* not lint */ + +#include "namespace.h" +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <paths.h> +#include "un-namespace.h" + +/* + * __isptmaster(): return whether the file descriptor refers to a + * pseudo-terminal master device. + */ +static int +__isptmaster(int fildes) +{ + + if (_ioctl(fildes, TIOCPTMASTER) == 0) + return (0); + + if (errno != EBADF) + errno = EINVAL; + + return (-1); +} + +/* + * In our implementation, grantpt() and unlockpt() don't actually have + * any use, because PTY's are created on the fly and already have proper + * permissions upon creation. + * + * Just make sure `fildes' actually points to a real PTY master device. + */ +__strong_reference(__isptmaster, grantpt); +__strong_reference(__isptmaster, unlockpt); + +/* + * ptsname(): return the pathname of the slave pseudo-terminal device + * associated with the specified master. + */ +char * +ptsname(int fildes) +{ + static char pt_slave[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV; + struct fiodgname_arg fgn; + char *ret = NULL; + int sverrno = errno; + + /* Make sure fildes points to a master device. */ + if (__isptmaster(fildes) != 0) + goto done; + + /* Obtain the device name through FIODGNAME. */ + fgn.len = sizeof pt_slave - (sizeof _PATH_DEV - 1); + fgn.buf = pt_slave + (sizeof _PATH_DEV - 1); + if (_ioctl(fildes, FIODGNAME, &fgn) == 0) + ret = pt_slave; + +done: /* Make sure ptsname() does not overwrite errno. */ + errno = sverrno; + return (ret); +} diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 195fb1f..f84ce13 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -79,8 +79,8 @@ MAN+= abort2.2 accept.2 access.2 acct.2 adjtime.2 \ mlockall.2 mmap.2 modfind.2 modnext.2 modstat.2 mount.2 mprotect.2 \ mq_close.2 mq_getattr.2 mq_notify.2 mq_open.2 mq_receive.2 mq_send.2 \ mq_setattr.2 \ - msync.2 munmap.2 nanosleep.2 nfssvc.2 ntp_adjtime.2 \ - open.2 pathconf.2 pipe.2 poll.2 profil.2 ptrace.2 quotactl.2 \ + msync.2 munmap.2 nanosleep.2 nfssvc.2 ntp_adjtime.2 open.2 \ + pathconf.2 pipe.2 poll.2 posix_openpt.2 profil.2 ptrace.2 quotactl.2 \ read.2 readlink.2 reboot.2 recv.2 rename.2 revoke.2 rfork.2 rmdir.2 \ rtprio.2 .if !defined(NO_P1003_1B) diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index d38f287..5217ab0 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -211,6 +211,7 @@ FBSD_1.0 { pathconf; pipe; poll; + posix_openpt; preadv; profil; ptrace; diff --git a/lib/libc/sys/getrlimit.2 b/lib/libc/sys/getrlimit.2 index 93d91f6..d3550d2 100644 --- a/lib/libc/sys/getrlimit.2 +++ b/lib/libc/sys/getrlimit.2 @@ -28,7 +28,7 @@ .\" @(#)getrlimit.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 13, 2004 +.Dd August 20, 2008 .Dt GETRLIMIT 2 .Os .Sh NAME @@ -97,6 +97,8 @@ mbufs, that this user may hold at any time. The maximum size (in bytes) of the stack segment for a process; this defines how far a program's stack segment may be extended. Stack extension is performed automatically by the system. +.It Dv RLIMIT_NPTS +The maximum number of pseudo-terminals created by this user id. .El .Pp A resource limit is specified as a soft limit and a hard limit. diff --git a/lib/libc/sys/posix_openpt.2 b/lib/libc/sys/posix_openpt.2 new file mode 100644 index 0000000..2633847 --- /dev/null +++ b/lib/libc/sys/posix_openpt.2 @@ -0,0 +1,135 @@ +.\" Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Portions of this software were developed under sponsorship from Snow +.\" B.V., the Netherlands. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" Portions of this text are reprinted and reproduced in electronic form +.\" from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- +.\" Portable Operating System Interface (POSIX), The Open Group Base +.\" Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of +.\" Electrical and Electronics Engineers, Inc and The Open Group. In the +.\" event of any discrepancy between this version and the original IEEE and +.\" The Open Group Standard, the original IEEE and The Open Group Standard is +.\" the referee document. The original Standard can be obtained online at +.\" http://www.opengroup.org/unix/online.html. +.\" +.\" $FreeBSD$ +.\" +.Dd August 20, 2008 +.Dt POSIX_OPENPT 2 +.Os +.Sh NAME +.Nm posix_openpt +.Nd "open a pseudo-terminal device" +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In stdlib.h +.In fcntl.h +.Ft int +.Fn posix_openpt "int oflag" +.Sh DESCRIPTION +The +.Fn posix_openpt +function allocates a new pseudo-terminal and establishes a connection +with its master device. +A slave device shall be created in +.Pa /dev/pts . +After the pseudo-terminal has been allocated, the slave device should +have the proper permissions before it can be used (see +.Xr grantpt 3 ) . +The name of the slave device can be determined by calling +.Xr ptsname 3 . +.Pp +The file status flags and file access modes of the open file description +shall be set according to the value of +.Fa oflag . +Values for +.Fa oflag +are constructed by a bitwise-inclusive OR of flags from the following +list, defined in +.In fcntl.h : +.Bl -tag -width ".Dv O_NOCTTY" +.It Dv O_RDWR +Open for reading and writing. +.It Dv O_NOCTTY +If set +.Fn posix_openpt +shall not cause the terminal device to become the controlling terminal +for the process. +.El +.Pp +The +.Fn posix_openpt +function shall fail when +.Fa oflag +contains other values. +.Sh RETURN VALUES +Upon successful completion, the +.Fn posix_openpt +function shall allocate a new pseudo-terminal device and return a +non-negative integer representing a file descriptor, which is connected +to its master device. +Otherwise, -1 shall be returned and errno set to indicate the error. +.Sh ERRORS +The +.Fn posix_openpt +function shall fail if: +.Bl -tag -width Er +.It Bq Er ENFILE +The system file table is full. +.It Bq Er EINVAL +The value of +.Fa oflag +is not valid. +.It Bq Er EAGAIN +Out of pseudo-terminal resources. +.El +.Sh SEE ALSO +.Xr pts 4 , +.Xr ptsname 3 , +.Xr tty 4 +.Sh STANDARDS +The +.Fn posix_openpt +function conforms to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Fn posix_openpt +function appeared in +.Fx 5.0 . +In +.Fx 8.0 , +this function was changed to a system call. +.Sh NOTES +The flag +.Dv O_NOCTTY +is included for compatibility; in +.Fx , +opening a terminal does not cause it to become a process's controlling +terminal. +.Sh AUTHORS +.An Ed Schouten Aq ed@FreeBSD.org diff --git a/lib/libutil/login.conf.5 b/lib/libutil/login.conf.5 index 863aa06..2d1b906 100644 --- a/lib/libutil/login.conf.5 +++ b/lib/libutil/login.conf.5 @@ -19,7 +19,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 9, 2006 +.Dd August 20, 2008 .Dt LOGIN.CONF 5 .Os .Sh NAME @@ -176,7 +176,7 @@ The usual convention to interpolate capability entries using the special .Em tc=value notation may be used. .Sh RESOURCE LIMITS -.Bl -column coredumpsize indent indent +.Bl -column pseudoterminals indent indent .It Sy "Name Type Notes Description .It "coredumpsize size Maximum coredump size limit. .It "cputime time CPU usage limit. @@ -189,6 +189,7 @@ notation may be used. .It "sbsize size Maximum permitted socketbuffer size. .It "vmemoryuse size Maximum permitted total VM usage per process. .It "stacksize size Maximum stack size limit. +.It "pseudoterminals number Maximum number of pseudo-terminals. .El .Pp These resource limit entries actually specify both the maximum diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c index c18d46a..6e81fcf 100644 --- a/lib/libutil/login_class.c +++ b/lib/libutil/login_class.c @@ -50,18 +50,19 @@ static struct login_res { rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); int why; } resources[] = { - { "cputime", login_getcaptime, RLIMIT_CPU }, - { "filesize", login_getcapsize, RLIMIT_FSIZE }, - { "datasize", login_getcapsize, RLIMIT_DATA }, - { "stacksize", login_getcapsize, RLIMIT_STACK }, - { "memoryuse", login_getcapsize, RLIMIT_RSS }, - { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, - { "maxproc", login_getcapnum, RLIMIT_NPROC }, - { "openfiles", login_getcapnum, RLIMIT_NOFILE }, - { "coredumpsize", login_getcapsize, RLIMIT_CORE }, - { "sbsize", login_getcapsize, RLIMIT_SBSIZE }, - { "vmemoryuse", login_getcapsize, RLIMIT_VMEM }, - { NULL, 0, 0 } + { "cputime", login_getcaptime, RLIMIT_CPU }, + { "filesize", login_getcapsize, RLIMIT_FSIZE }, + { "datasize", login_getcapsize, RLIMIT_DATA }, + { "stacksize", login_getcapsize, RLIMIT_STACK }, + { "memoryuse", login_getcapsize, RLIMIT_RSS }, + { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, + { "maxproc", login_getcapnum, RLIMIT_NPROC }, + { "openfiles", login_getcapnum, RLIMIT_NOFILE }, + { "coredumpsize", login_getcapsize, RLIMIT_CORE }, + { "sbsize", login_getcapsize, RLIMIT_SBSIZE }, + { "vmemoryuse", login_getcapsize, RLIMIT_VMEM }, + { "pseudoterminals", login_getcapnum, RLIMIT_NPTS }, + { NULL, 0, 0 } }; diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 1f1e7dd..dd9a8d9 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -279,6 +279,7 @@ MAN= aac.4 \ psm.4 \ pst.4 \ pt.4 \ + pts.4 \ pty.4 \ puc.4 \ ral.4 \ diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 index b7e3a89..69f11ef 100644 --- a/share/man/man4/ddb.4 +++ b/share/man/man4/ddb.4 @@ -60,7 +60,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 21, 2008 +.Dd August 20, 2008 .Dt DDB 4 .Os .Sh NAME @@ -942,6 +942,12 @@ Backtrace. .El .\" .Pp +.It Ic show Cm ttys +Show all TTY's within the system. +Output is similar to +.Xr pstat 8 . +.\" +.Pp .It Ic show Cm turnstile Ar addr Show turnstile .Vt struct turnstile diff --git a/share/man/man4/pts.4 b/share/man/man4/pts.4 new file mode 100644 index 0000000..d614bc2 --- /dev/null +++ b/share/man/man4/pts.4 @@ -0,0 +1,181 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pty.4 8.2 (Berkeley) 11/30/93 +.\" $FreeBSD$ +.\" +.Dd August 20, 2008 +.Dt PTS 4 +.Os +.Sh NAME +.Nm pts +.Nd pseudo-terminal driver +.Sh DESCRIPTION +The +.Nm +driver provides support for a device-pair termed a +.Em pseudo-terminal . +A pseudo-terminal is a pair of character devices, a +.Em master +device and a +.Em slave +device. +The slave device provides to a process an interface identical +to that described in +.Xr tty 4 . +However, whereas all other devices which provide the +interface described in +.Xr tty 4 +have a hardware device of some sort behind them, the slave +device has, instead, another process manipulating +it through the master half of the pseudo-terminal. +That is, anything written on the master device is +given to the slave device as input and anything written +on the slave device is presented as input on the master +device. +.Pp +The following +.Xr ioctl 2 +calls apply only to pseudo-terminals: +.Bl -tag -width TIOCPTMASTER +.It Dv TIOCPKT +Enable/disable +.Em packet +mode. +Packet mode is enabled by specifying (by reference) +a nonzero parameter and disabled by specifying (by reference) +a zero parameter. +When applied to the master side of a pseudo-terminal, each subsequent +.Xr read 2 +from the terminal will return data written on the slave part of +the pseudo-terminal preceded by a zero byte (symbolically +defined as +.Dv TIOCPKT_DATA ) , +or a single byte reflecting control +status information. +In the latter case, the byte is an inclusive-or +of zero or more of the bits: +.Bl -tag -width TIOCPKT_FLUSHWRITE +.It Dv TIOCPKT_FLUSHREAD +whenever the read queue for the terminal is flushed. +.It Dv TIOCPKT_FLUSHWRITE +whenever the write queue for the terminal is flushed. +.It Dv TIOCPKT_STOP +whenever output to the terminal is stopped a la +.Ql ^S . +.It Dv TIOCPKT_START +whenever output to the terminal is restarted. +.It Dv TIOCPKT_DOSTOP +whenever +.Dv VSTOP +is +.Ql ^S +and +.Dv VSTART +is +.Ql ^Q . +.It Dv TIOCPKT_NOSTOP +whenever the start and stop characters are not +.Ql ^S/^Q . +.El +.Pp +While this mode is in use, the presence of control status information +to be read from the master side may be detected by a +.Xr select 2 +for exceptional conditions. +.Pp +This mode is used by +.Xr rlogin 1 +and +.Xr rlogind 8 +to implement a remote-echoed, locally +.Ql ^S/^Q +flow-controlled +remote login with proper back-flushing of output; it can be +used by other similar programs. +.It Dv TIOCGPTN +Obtain device unit number, which can be used to generate the filename of +the pseudo-terminal slave device. This +.Xr ioctl 2 +should not be used directly. Instead, the +.Xr ptsname 3 +function should be used. +.It Dv TIOCPTMASTER +Determine whether the file descriptor is pointing to a pseudo-terminal +master device. +This +.Xr ioctl 2 +should not be used directly. It is used to implement routines like +.Xr grantpt 3 . +.El +.Pp +The maximum number of pseudo-terminals is limited to 1000. +It is not possible to use more than 1000 pseudo-terminals, as all software +which use +.Xr utmp 5 +will not be able to handle pseudo-terminals with number superior to 999. +.Sh FILES +The files used by this +pseudo-terminals implementation are: +.Pp +.Bl -tag -width ".Pa /dev/pts/[num]" +.It Pa /dev/ptmx +Control device, returns a file descriptor to a new master pseudo-terminal +when opened. This device should not be opened directly. It's only +available for binary compatibility. New devices should only be created +though +.Xr posix_openpt 2 . +.It Pa /dev/pts/[num] +Pseudo-terminal slave devices. +.El +.Sh DIAGNOSTICS +None. +.Sh SEE ALSO +.Xr grantpt 3 , +.Xr posix_openpt 2 , +.Xr ptsname 3 , +.Xr pty 4 , +.Xr tty 4 +.Sh HISTORY +A +pseudo-terminal driver appeared in +.Bx 4.2 . +In +.Fx 8.0 , +it was replaced with the +.Nm +driver. +.Sh BUGS +Packet mode has not been properly implemented in this version of +.Fx . +When enabled, it will always prepend +.Dv TIOCPKT_DATA , +even if other events have been triggered. diff --git a/share/man/man4/pty.4 b/share/man/man4/pty.4 index 868c710..515f912 100644 --- a/share/man/man4/pty.4 +++ b/share/man/man4/pty.4 @@ -1,5 +1,8 @@ -.\" Copyright (c) 1983, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Portions of this software were developed under sponsorship from Snow +.\" B.V., the Netherlands. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -9,18 +12,11 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. .\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -29,214 +25,65 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)pty.4 8.2 (Berkeley) 11/30/93 .\" $FreeBSD$ .\" -.Dd November 30, 1993 +.Dd August 20, 2008 .Dt PTY 4 .Os .Sh NAME .Nm pty -.Nd pseudo terminal driver +.Nd BSD-style compatibility pseudo-terminal driver .Sh SYNOPSIS .Cd "device pty" .Sh DESCRIPTION The .Nm -driver provides support for a device-pair termed a -.Em pseudo terminal . -A pseudo terminal is a pair of character devices, a -.Em master -device and a -.Em slave -device. -The slave device provides to a process an interface identical -to that described in -.Xr tty 4 . -However, whereas all other devices which provide the -interface described in -.Xr tty 4 -have a hardware device of some sort behind them, the slave -device has, instead, another process manipulating -it through the master half of the pseudo terminal. -That is, anything written on the master device is -given to the slave device as input and anything written -on the slave device is presented as input on the master -device. -.Pp -The following -.Xr ioctl 2 -calls apply only to pseudo terminals: -.Bl -tag -width TIOCREMOTE -.It Dv TIOCSTOP -Stops output to a terminal (e.g.\& like typing -.Ql ^S ) . -Takes -no parameter. -.It Dv TIOCSTART -Restarts output (stopped by -.Dv TIOCSTOP -or by typing -.Ql ^S ) . -Takes no parameter. -.It Dv TIOCPKT -Enable/disable -.Em packet -mode. -Packet mode is enabled by specifying (by reference) -a nonzero parameter and disabled by specifying (by reference) -a zero parameter. -When applied to the master side of a pseudo terminal, each subsequent -.Xr read 2 -from the terminal will return data written on the slave part of -the pseudo terminal preceded by a zero byte (symbolically -defined as -.Dv TIOCPKT_DATA ) , -or a single byte reflecting control -status information. -In the latter case, the byte is an inclusive-or -of zero or more of the bits: -.Bl -tag -width TIOCPKT_FLUSHWRITE -.It Dv TIOCPKT_FLUSHREAD -whenever the read queue for the terminal is flushed. -.It Dv TIOCPKT_FLUSHWRITE -whenever the write queue for the terminal is flushed. -.It Dv TIOCPKT_STOP -whenever output to the terminal is stopped a la -.Ql ^S . -.It Dv TIOCPKT_START -whenever output to the terminal is restarted. -.It Dv TIOCPKT_DOSTOP -whenever -.Em t_stopc -is -.Ql ^S -and -.Em t_startc -is -.Ql ^Q . -.It Dv TIOCPKT_NOSTOP -whenever the start and stop characters are not -.Ql ^S/^Q . -.El -.Pp -While this mode is in use, the presence of control status information -to be read from the master side may be detected by a -.Xr select 2 -for exceptional conditions. -.Pp -This mode is used by -.Xr rlogin 1 -and -.Xr rlogind 8 -to implement a remote-echoed, locally -.Ql ^S/^Q -flow-controlled -remote login with proper back-flushing of output; it can be -used by other similar programs. -.It Dv TIOCUCNTL -Enable/disable a mode that allows a small number of simple user -.Xr ioctl 2 -commands to be passed through the pseudo-terminal, -using a protocol similar to that of -.Dv TIOCPKT . -The -.Dv TIOCUCNTL -and -.Dv TIOCPKT -modes are mutually exclusive. -This mode is enabled from the master side of a pseudo terminal -by specifying (by reference) -a nonzero parameter and disabled by specifying (by reference) -a zero parameter. -Each subsequent -.Xr read 2 -from the master side will return data written on the slave part of -the pseudo terminal preceded by a zero byte, -or a single byte reflecting a user control operation on the slave side. -A user control command consists of a special -.Xr ioctl 2 -operation with no data; the command is given as -.Dv UIOCCMD Ns (n) , -where -.Ar n -is a number in the range 1-255. -The operation value -.Ar n -will be received as a single byte on the next -.Xr read 2 -from the master side. -The -.Xr ioctl 2 -.Dv UIOCCMD Ns (0) -is a no-op that may be used to probe for -the existence of this facility. -As with -.Dv TIOCPKT -mode, command operations may be detected with a -.Xr select 2 -for exceptional conditions. -.El +driver provides support for the traditional BSD naming scheme that was +used for accessing pseudo-terminals. +When the device +.Pa /dev/ptyXX +is being opened, a new terminal shall be created with the +.Xr pts 4 +driver. +A device node for this terminal shall be created, which has the name +.Pa /dev/ttyXX . .Pp -There is currently two -.Nm -systems available: the original -.Bx Nm , -and a -SysVR4 pts-like implementation. -It is possible to switch between the two implementations by setting the -.Va kern.pts.enable -sysctl. -Setting it to 0 will use the -.Bx Nm , -to non-zero the pts implementation. -It defaults to 0. -It is possible to set the maximum number of ptys -which can be allocated at the same time with the -.Va kern.pts.max -sysctl. -It defaults to 1000. -It is not recommended to use more than 1000 pseudo-terminals, as all software -which use -.Xr utmp 5 -will not be able to handle pseudo-terminals with number superior to 999. -.Pp -The pts implementation also supports the -.Dv TIOCGPTN -.Xr ioctl 2 -call, which takes a pointer to an -.Vt "unsigned int" -as a parameter and provides the -number of the pty. +New code should not try to allocate pseudo-terminals using this +interface. +It is only provided for compatibility with older C libraries +that tried to open such devices when +.Xr posix_openpt 2 +was being called. .Sh FILES -The files used by the -.Bx -pseudo terminals implementation are: -.Pp -.Bl -tag -width ".Pa /dev/tty[p-sP-S][0-9a-v]" -compact -.It Pa /dev/pty[p-sP-S][0-9a-v] -master pseudo terminals -.It Pa /dev/tty[p-sP-S][0-9a-v] -slave pseudo terminals -.El +The BSD-style compatibility pseudo-terminal driver uses the following +device names: .Pp -The files used by the pts implementation are: -.Pp -.Bl -tag -width ".Pa /dev/pts/[num]" -compact -.It Pa /dev/ptmx -control device, returns a file descriptor to a new master pseudo terminal -when opened. -.It Pa /dev/pty[num] -master pseudo terminals -.It Pa /dev/pts/[num] -slave pseudo terminals +.Bl -tag -width ".Pa /dev/pty[l-sL-S][0-9a-v]" +.It Pa /dev/pty[l-sL-S][0-9a-v] +Pseudo-terminal master devices. +.It Pa /dev/tty[l-sL-S][0-9a-v] +Pseudo-terminal slave devices. .El .Sh DIAGNOSTICS None. .Sh SEE ALSO +.Xr posix_openpt 2 , +.Xr pts 4 , .Xr tty 4 .Sh HISTORY +A +pseudo-terminal driver appeared in +.Bx 4.2 . +.Sh BUGS +Unlike previous implementations, the master slave device nodes are +destroyed when the PTY becomes unused. +A call to +.Xr stat 2 +on a nonexistent master device will already cause a new master device +node to be created. +The master device can only be destroyed by opening and closing it. +.Pp The .Nm -driver appeared in -.Bx 4.2 . +driver cannot be unloaded, because it cannot determine if it is being +used. diff --git a/share/man/man4/termios.4 b/share/man/man4/termios.4 index ab17db1..cda1673 100644 --- a/share/man/man4/termios.4 +++ b/share/man/man4/termios.4 @@ -32,7 +32,7 @@ .\" @(#)termios.4 8.4 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd May 13, 2008 +.Dd August 20, 2008 .Dt TERMIOS 4 .Os .Sh NAME @@ -1073,7 +1073,7 @@ Values of the field describe the basic terminal output control, and are composed of the following masks: .Pp -.Bl -tag -width OXTABS -offset indent -compact +.Bl -tag -width ONOEOT -offset indent -compact .It Dv OPOST /* enable following output processing */ .It Dv ONLCR @@ -1082,7 +1082,11 @@ and are composed of the following masks: */ .It Dv OCRNL /* map CR to NL */ -.It Dv OXTABS +.It Dv TABDLY +/* tab delay mask */ +.It Dv TAB0 +/* no tab delay and expansion */ +.It Dv TAB3 /* expand tabs to spaces */ .It Dv ONOEOT /* discard @@ -1108,8 +1112,20 @@ If .Dv OCRNL is set, carriage returns are translated to newlines. .Pp +The +.Dv TABDLY +bits specify the tab delay. +The +.Fa c_oflag +is masked with +.Dv TABDLY +and compared with the +values +.Dv TAB0 +or +.Dv TAB3 . If -.Dv OXTABS +.Dv TAB3 is set, tabs are expanded to the appropriate number of spaces (assuming 8 column tab stops). .Pp diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 3d5e9f0..31e1e9c 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -257,7 +257,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h index 400b79d..c538431 100644 --- a/sys/bsm/audit_kevents.h +++ b/sys/bsm/audit_kevents.h @@ -548,6 +548,7 @@ #define AUE_MKNODAT 43150 /* FreeBSD. */ #define AUE_READLINKAT 43151 /* FreeBSD. */ #define AUE_SYMLINKAT 43152 /* FreeBSD. */ +#define AUE_POSIXOPENPT 43153 /* FreeBSD. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index a2b3102..6eebf04 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -355,4 +355,5 @@ #define FREEBSD32_SYS_renameat 501 #define FREEBSD32_SYS_symlinkat 502 #define FREEBSD32_SYS_unlinkat 503 -#define FREEBSD32_SYS_MAXSYSCALL 504 +#define FREEBSD32_SYS_posix_openpt 504 +#define FREEBSD32_SYS_MAXSYSCALL 505 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index fa21c32..c9638cb 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -511,4 +511,5 @@ const char *freebsd32_syscallnames[] = { "renameat", /* 501 = renameat */ "symlinkat", /* 502 = symlinkat */ "unlinkat", /* 503 = unlinkat */ + "posix_openpt", /* 504 = posix_openpt */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index 0e81268..29c5841 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -542,4 +542,5 @@ struct sysent freebsd32_sysent[] = { { AS(renameat_args), (sy_call_t *)renameat, AUE_RENAMEAT, NULL, 0, 0 }, /* 501 = renameat */ { AS(symlinkat_args), (sy_call_t *)symlinkat, AUE_SYMLINKAT, NULL, 0, 0 }, /* 502 = symlinkat */ { AS(unlinkat_args), (sy_call_t *)unlinkat, AUE_UNLINKAT, NULL, 0, 0 }, /* 503 = unlinkat */ + { AS(posix_openpt_args), (sy_call_t *)posix_openpt, AUE_POSIXOPENPT, NULL, 0, 0 }, /* 504 = posix_openpt */ }; diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 5ab7e42..193c92d 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -846,3 +846,4 @@ char *path2); } 503 AUE_UNLINKAT NOPROTO { int unlinkat(int fd, char *path, \ int flag); } +504 AUE_POSIXOPENPT NOPROTO { int posix_openpt(int flags); } diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c index b166535..083548e 100644 --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -296,6 +296,11 @@ struct linux_winsize { unsigned short ws_xpixel, ws_ypixel; }; +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static struct speedtab sptab[] = { { B0, LINUX_B0 }, { B50, LINUX_B50 }, { B75, LINUX_B75 }, { B110, LINUX_B110 }, @@ -395,7 +400,7 @@ bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios) lios->c_oflag |= LINUX_OPOST; if (bios->c_oflag & ONLCR) lios->c_oflag |= LINUX_ONLCR; - if (bios->c_oflag & OXTABS) + if (bios->c_oflag & TAB3) lios->c_oflag |= LINUX_XTABS; lios->c_cflag = bsd_to_linux_speed(bios->c_ispeed, sptab); @@ -537,7 +542,7 @@ linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) if (lios->c_oflag & LINUX_ONLCR) bios->c_oflag |= ONLCR; if (lios->c_oflag & LINUX_XTABS) - bios->c_oflag |= OXTABS; + bios->c_oflag |= TAB3; bios->c_cflag = (lios->c_cflag & LINUX_CSIZE) << 4; if (lios->c_cflag & LINUX_CSTOPB) diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 7c85fb4..c5f10af 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/stat.h> #include <sys/syscallsubr.h> #include <sys/systm.h> +#include <sys/tty.h> #include <sys/vnode.h> #include <sys/conf.h> #include <sys/fcntl.h> @@ -109,8 +110,17 @@ translate_fd_major_minor(struct thread *td, int fd, struct stat *buf) if (fp->f_vnode != NULL && fp->f_vnode->v_un.vu_cdev != NULL && linux_driver_get_major_minor(fp->f_vnode->v_un.vu_cdev->si_name, - &major, &minor) == 0) + &major, &minor) == 0) { buf->st_rdev = (major << 8 | minor); + } else if (fp->f_type == DTYPE_PTS) { + struct tty *tp = fp->f_data; + + /* Convert the numbers for the slave device. */ + if (linux_driver_get_major_minor(tp->t_dev->si_name, + &major, &minor) == 0) { + buf->st_rdev = (major << 8 | minor); + } + } fdrop(fp, td); } diff --git a/sys/conf/NOTES b/sys/conf/NOTES index aa052ec..a042885 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1275,11 +1275,7 @@ options SES_ENABLE_PASSTHROUGH ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS -# The `pty' device usually turns out to be ``effectively mandatory'', -# as it is required for `telnetd', `rlogind', `screen', `emacs', and -# `xterm', among others. - -device pty #Pseudo ttys +device pty #BSD-style compatibility pseudo ttys device nmdm #back-to-back tty devices device md #Memory/malloc disk device ccd #Concatenated disk driver @@ -1291,9 +1287,6 @@ options LIBICONV # Size of the kernel message buffer. Should be N * pagesize. options MSGBUF_SIZE=40960 -# Maximum size of a tty or pty input buffer. -options TTYHOG=8193 - ##################################################################### # HARDWARE DEVICE CONFIGURATION diff --git a/sys/conf/files b/sys/conf/files index 4e9cfed..7da9135 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1661,12 +1661,14 @@ kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c optional compat_43tty -kern/tty_conf.c standard kern/tty_cons.c standard kern/tty_info.c standard -kern/tty_pts.c optional pty +kern/tty_inq.c standard +kern/tty_outq.c standard +kern/tty_pts.c standard kern/tty_pty.c optional pty kern/tty_tty.c standard +kern/tty_ttydisc.c standard kern/uipc_accf.c optional inet kern/uipc_cow.c optional zero_copy_sockets kern/uipc_debug.c optional ddb @@ -2354,4 +2356,4 @@ xen/xenbus/xenbus_xs.c optional xen dev/xen/console/console.c optional xen dev/xen/console/xencons_ring.c optional xen dev/xen/blkfront/blkfront.c optional xen -dev/xen/netfront/netfront.c optional xen
\ No newline at end of file +dev/xen/netfront/netfront.c optional xen diff --git a/sys/conf/options b/sys/conf/options index 63f827b..a8b3723 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -175,7 +175,6 @@ SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TURNSTILE_PROFILING -TTYHOG opt_tty.h VFS_AIO VERBOSE_SYSINIT opt_global.h WLCACHE opt_wavelan.h @@ -658,6 +657,7 @@ ISAPNP opt_isa.h DEV_BPF opt_bpf.h DEV_MCA opt_mca.h DEV_CARP opt_carp.h +DEV_PTY opt_tty.h DEV_SPLASH opt_splash.h # EISA support diff --git a/sys/dev/dcons/dcons.h b/sys/dev/dcons/dcons.h index 49ef869..e613f5b 100644 --- a/sys/dev/dcons/dcons.h +++ b/sys/dev/dcons/dcons.h @@ -98,7 +98,7 @@ struct dcons_softc { int brk_state; #define DC_GDB 1 int flags; - void *dev; + void *tty; }; int dcons_checkc(struct dcons_softc *); diff --git a/sys/dev/dcons/dcons_os.c b/sys/dev/dcons/dcons_os.c index a43cd28..aad9637 100644 --- a/sys/dev/dcons/dcons_os.c +++ b/sys/dev/dcons/dcons_os.c @@ -1,7 +1,7 @@ /*- * Copyright (C) 2003,2004 * Hidetoshi Shimokawa. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -18,7 +18,7 @@ * 4. Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -30,15 +30,13 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * * $FreeBSD$ */ #include <sys/param.h> -#if __FreeBSD_version >= 502122 #include <sys/kdb.h> #include <gdb/gdb.h> -#endif #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> @@ -54,13 +52,8 @@ #include <machine/bus.h> -#ifdef __DragonFly__ -#include "dcons.h" -#include "dcons_os.h" -#else #include <dev/dcons/dcons.h> #include <dev/dcons/dcons_os.h> -#endif #include <ddb/ddb.h> #include <sys/reboot.h> @@ -90,53 +83,6 @@ #define DCONS_FORCE_CONSOLE 0 /* Mostly for FreeBSD-4/DragonFly */ #endif -#ifndef DCONS_FORCE_GDB -#define DCONS_FORCE_GDB 1 -#endif - -#if __FreeBSD_version >= 500101 -#define CONS_NODEV 1 -#if __FreeBSD_version < 502122 -static struct consdev gdbconsdev; -#endif -#endif - -static d_open_t dcons_open; -static d_close_t dcons_close; -#if defined(__DragonFly__) || __FreeBSD_version < 500104 -static d_ioctl_t dcons_ioctl; -#endif - -static struct cdevsw dcons_cdevsw = { -#ifdef __DragonFly__ -#define CDEV_MAJOR 184 - "dcons", CDEV_MAJOR, D_TTY, NULL, 0, - dcons_open, dcons_close, ttyread, ttywrite, dcons_ioctl, - ttypoll, nommap, nostrategy, nodump, nopsize, -#elif __FreeBSD_version >= 500104 - .d_version = D_VERSION, - .d_open = dcons_open, - .d_close = dcons_close, - .d_name = "dcons", - .d_flags = D_TTY | D_NEEDGIANT, -#else -#define CDEV_MAJOR 184 - /* open */ dcons_open, - /* close */ dcons_close, - /* read */ ttyread, - /* write */ ttywrite, - /* ioctl */ dcons_ioctl, - /* poll */ ttypoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ "dcons", - /* major */ CDEV_MAJOR, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ D_TTY, -#endif -}; - #ifndef KLD_MODULE static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ #endif @@ -156,20 +102,6 @@ static int drv_init = 0; static struct callout dcons_callout; struct dcons_buf *dcons_buf; /* for local dconschat */ -#ifdef __DragonFly__ -#define DEV dev_t -#define THREAD d_thread_t -#elif __FreeBSD_version < 500000 -#define DEV dev_t -#define THREAD struct proc -#else -#define DEV struct cdev * -#define THREAD struct thread -#endif - - -static void dcons_tty_start(struct tty *); -static int dcons_tty_param(struct tty *, struct termios *); static void dcons_timeout(void *); static int dcons_drv_init(int); @@ -181,12 +113,12 @@ static cn_putc_t dcons_cnputc; CONSOLE_DRIVER(dcons); -#if defined(GDB) && (__FreeBSD_version >= 502122) -static gdb_probe_f dcons_dbg_probe; -static gdb_init_f dcons_dbg_init; -static gdb_term_f dcons_dbg_term; -static gdb_getc_f dcons_dbg_getc; -static gdb_putc_f dcons_dbg_putc; +#if defined(GDB) +static gdb_probe_f dcons_dbg_probe; +static gdb_init_f dcons_dbg_init; +static gdb_term_f dcons_dbg_term; +static gdb_getc_f dcons_dbg_getc; +static gdb_putc_f dcons_dbg_putc; GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term, dcons_dbg_getc, dcons_dbg_putc); @@ -194,21 +126,25 @@ GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term, extern struct gdb_dbgport *gdb_cur; #endif +static tsw_outwakeup_t dcons_outwakeup; + +static struct ttydevsw dcons_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_outwakeup = dcons_outwakeup, +}; + #if (defined(GDB) || defined(DDB)) && defined(ALT_BREAK_TO_DEBUGGER) static int dcons_check_break(struct dcons_softc *dc, int c) { -#if __FreeBSD_version >= 502122 int kdb_brk; -#endif + if (c < 0) return (c); -#if __FreeBSD_version >= 502122 if ((kdb_brk = kdb_alt_break(c, &dc->brk_state)) != 0) { switch (kdb_brk) { case KDB_REQ_DEBUGGER: - if ((dc->flags & DC_GDB) != 0) { #ifdef GDB if (gdb_cur == &dcons_gdb_dbgport) { @@ -229,27 +165,6 @@ dcons_check_break(struct dcons_softc *dc, int c) break; } } -#else - switch (dc->brk_state) { - case STATE1: - if (c == KEY_TILDE) - dc->brk_state = STATE2; - else - dc->brk_state = STATE0; - break; - case STATE2: - dc->brk_state = STATE0; - if (c == KEY_CTRLB) { -#if DCONS_FORCE_GDB - if (dc->flags & DC_GDB) - boothowto |= RB_GDB; -#endif - breakpoint(); - } - } - if (c == KEY_CR) - dc->brk_state = STATE1; -#endif return (c); } #else @@ -263,7 +178,7 @@ dcons_os_checkc_nopoll(struct dcons_softc *dc) if (dg.dma_tag != NULL) bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD); - + c = dcons_check_break(dc, dcons_checkc(dc)); if (dg.dma_tag != NULL) @@ -279,18 +194,6 @@ dcons_os_checkc(struct dcons_softc *dc) return (dcons_os_checkc_nopoll(dc)); } -#if defined(GDB) || !defined(CONS_NODEV) -static int -dcons_os_getc(struct dcons_softc *dc) -{ - int c; - - while ((c = dcons_os_checkc(dc)) == -1); - - return (c & 0xff); -} -#endif - static void dcons_os_putc(struct dcons_softc *dc, int c) { @@ -302,122 +205,17 @@ dcons_os_putc(struct dcons_softc *dc, int c) if (dg.dma_tag != NULL) bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE); } -static int -dcons_open(DEV dev, int flag, int mode, THREAD *td) -{ - struct tty *tp; - int unit, error, s; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - tp->t_oproc = dcons_tty_start; - tp->t_param = dcons_tty_param; - tp->t_stop = nottystop; - tp->t_dev = dev; - - error = 0; - - s = spltty(); - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - splx(s); - return (EBUSY); - } - splx(s); - -#if __FreeBSD_version < 502113 - error = (*linesw[tp->t_line].l_open)(dev, tp); -#else - error = ttyld_open(tp, dev); -#endif - - return (error); -} - -static int -dcons_close(DEV dev, int flag, int mode, THREAD *td) -{ - int unit; - struct tty *tp; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - if (tp->t_state & TS_ISOPEN) { -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_close)(tp, flag); - ttyclose(tp); -#else - ttyld_close(tp, flag); - tty_close(tp); -#endif - } - - return (0); -} - -#if defined(__DragonFly__) || __FreeBSD_version < 500104 -static int -dcons_ioctl(DEV dev, u_long cmd, caddr_t data, int flag, THREAD *td) -{ - int unit; - struct tty *tp; - int error; - - unit = minor(dev); - if (unit != 0) - return (ENXIO); - - tp = dev->si_tty; - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); - if (error != ENOIOCTL) - return (error); - - error = ttioctl(tp, cmd, data, flag); - if (error != ENOIOCTL) - return (error); - - return (ENOTTY); -} -#endif - -static int -dcons_tty_param(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; -} static void -dcons_tty_start(struct tty *tp) +dcons_outwakeup(struct tty *tp) { struct dcons_softc *dc; - int s; - - dc = (struct dcons_softc *)tp->t_dev->si_drv1; - s = spltty(); - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - return; - } + char ch; - tp->t_state |= TS_BUSY; - while (tp->t_outq.c_cc != 0) - dcons_os_putc(dc, getc(&tp->t_outq)); - tp->t_state &= ~TS_BUSY; + dc = tty_softc(tp); - ttwwakeup(tp); - splx(s); + while (ttydisc_getc(tp, &ch, sizeof ch) != 0) + dcons_os_putc(dc, ch); } static void @@ -429,14 +227,13 @@ dcons_timeout(void *v) for (i = 0; i < DCONS_NPORT; i ++) { dc = &sc[i]; - tp = ((DEV)dc->dev)->si_tty; + tp = dc->tty; + + tty_lock(tp); while ((c = dcons_os_checkc_nopoll(dc)) != -1) - if (tp->t_state & TS_ISOPEN) -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_rint)(c, tp); -#else - ttyld_rint(tp, c); -#endif + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); } polltime = hz / poll_hz; if (polltime < 1) @@ -447,14 +244,7 @@ dcons_timeout(void *v) static void dcons_cnprobe(struct consdev *cp) { -#ifdef __DragonFly__ - cp->cn_dev = make_dev(&dcons_cdevsw, DCONS_CON, - UID_ROOT, GID_WHEEL, 0600, "dcons"); -#elif __FreeBSD_version >= 501109 sprintf(cp->cn_name, "dcons"); -#else - cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON); -#endif #if DCONS_FORCE_CONSOLE cp->cn_pri = CN_REMOTE; #else @@ -466,12 +256,7 @@ static void dcons_cninit(struct consdev *cp) { dcons_drv_init(0); -#if CONS_NODEV - cp->cn_arg -#else - cp->cn_dev->si_drv1 -#endif - = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ + cp->cn_arg = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ } static void @@ -479,39 +264,19 @@ dcons_cnterm(struct consdev *cp) { } -#if CONS_NODEV static int dcons_cngetc(struct consdev *cp) { struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; return (dcons_os_checkc(dc)); } + static void dcons_cnputc(struct consdev *cp, int c) { struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; dcons_os_putc(dc, c); } -#else -static int -dcons_cngetc(DEV dev) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - return (dcons_os_getc(dc)); -} -static int -dcons_cncheckc(DEV dev) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - return (dcons_os_checkc(dc)); -} -static void -dcons_cnputc(DEV dev, int c) -{ - struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1; - dcons_os_putc(dc, c); -} -#endif static int dcons_drv_init(int stage) @@ -577,24 +342,6 @@ dcons_drv_init(int stage) ok: dcons_buf = dg.buf; -#if __FreeBSD_version < 502122 -#if defined(DDB) && DCONS_FORCE_GDB -#if CONS_NODEV - gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB]; -#if __FreeBSD_version >= 501109 - sprintf(gdbconsdev.cn_name, "dgdb"); -#endif - gdb_arg = &gdbconsdev; -#elif defined(__DragonFly__) - gdbdev = make_dev(&dcons_cdevsw, DCONS_GDB, - UID_ROOT, GID_WHEEL, 0600, "dgdb"); -#else - gdbdev = makedev(CDEV_MAJOR, DCONS_GDB); -#endif - gdb_getc = dcons_cngetc; - gdb_putc = dcons_cnputc; -#endif -#endif drv_init = 1; return 0; @@ -606,23 +353,12 @@ dcons_attach_port(int port, char *name, int flags) { struct dcons_softc *dc; struct tty *tp; - DEV dev; dc = &sc[port]; + tp = tty_alloc(&dcons_ttydevsw, dc, NULL); dc->flags = flags; - dev = make_dev(&dcons_cdevsw, port, - UID_ROOT, GID_WHEEL, 0600, name); - dc->dev = (void *)dev; - tp = ttyalloc(); - - dev->si_drv1 = (void *)dc; - dev->si_tty = tp; - - tp->t_oproc = dcons_tty_start; - tp->t_param = dcons_tty_param; - tp->t_stop = nottystop; - tp->t_dev = dc->dev; - + dc->tty = tp; + tty_makedev(tp, NULL, "%s", name); return(0); } @@ -631,16 +367,9 @@ dcons_attach(void) { int polltime; -#ifdef __DragonFly__ - cdevsw_add(&dcons_cdevsw, -1, 0); -#endif dcons_attach_port(DCONS_CON, "dcons", 0); dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); -#if __FreeBSD_version < 500000 - callout_init(&dcons_callout); -#else callout_init(&dcons_callout, 0); -#endif polltime = hz / poll_hz; if (polltime < 1) polltime = 1; @@ -655,37 +384,14 @@ dcons_detach(int port) struct dcons_softc *dc; dc = &sc[port]; + tp = dc->tty; - tp = ((DEV)dc->dev)->si_tty; - - if (tp->t_state & TS_ISOPEN) { - printf("dcons: still opened\n"); -#if __FreeBSD_version < 502113 - (*linesw[tp->t_line].l_close)(tp, 0); - tp->t_gen++; - ttyclose(tp); - ttwakeup(tp); - ttwwakeup(tp); -#else - ttyld_close(tp, 0); - tty_close(tp); -#endif - } - /* XXX - * must wait until all device are closed. - */ -#ifdef __DragonFly__ - tsleep((void *)dc, 0, "dcodtc", hz/4); -#else - tsleep((void *)dc, PWAIT, "dcodtc", hz/4); -#endif - destroy_dev(dc->dev); + tty_lock(tp); + tty_rel_gone(tp); return(0); } - -/* cnXXX works only for FreeBSD-5 */ static int dcons_modevent(module_t mode, int type, void *data) { @@ -695,29 +401,16 @@ dcons_modevent(module_t mode, int type, void *data) case MOD_LOAD: ret = dcons_drv_init(1); dcons_attach(); -#if __FreeBSD_version >= 500000 if (ret == 0) { dcons_cnprobe(&dcons_consdev); dcons_cninit(&dcons_consdev); cnadd(&dcons_consdev); } -#endif break; case MOD_UNLOAD: printf("dcons: unload\n"); callout_stop(&dcons_callout); -#if __FreeBSD_version < 502122 -#if defined(DDB) && DCONS_FORCE_GDB -#if CONS_NODEV - gdb_arg = NULL; -#else - gdbdev = NULL; -#endif -#endif -#endif -#if __FreeBSD_version >= 500000 cnremove(&dcons_consdev); -#endif dcons_detach(DCONS_CON); dcons_detach(DCONS_GDB); dg.buf->magic = 0; @@ -737,10 +430,20 @@ dcons_modevent(module_t mode, int type, void *data) return(err); } -#if defined(GDB) && (__FreeBSD_version >= 502122) +#if defined(GDB) /* Debugger interface */ static int +dcons_os_getc(struct dcons_softc *dc) +{ + int c; + + while ((c = dcons_os_checkc(dc)) == -1); + + return (c & 0xff); +} + +static int dcons_dbg_probe(void) { int dcons_gdb; diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c index 8536c18..706d416 100644 --- a/sys/dev/nmdm/nmdm.c +++ b/sys/dev/nmdm/nmdm.c @@ -36,9 +36,6 @@ __FBSDID("$FreeBSD$"); * Mighty handy for use with serial console in Vmware */ -#include "opt_compat.h" -#include "opt_tty.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/priv.h> @@ -48,252 +45,176 @@ __FBSDID("$FreeBSD$"); #include <sys/fcntl.h> #include <sys/poll.h> #include <sys/kernel.h> +#include <sys/limits.h> #include <sys/module.h> #include <sys/serial.h> #include <sys/signalvar.h> #include <sys/malloc.h> #include <sys/taskqueue.h> -MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); - -static d_close_t nmdmclose; -static t_modem_t nmdmmodem; -static d_open_t nmdmopen; -static t_oproc_t nmdmoproc; -static t_param_t nmdmparam; -static t_stop_t nmdmstop; - -static struct cdevsw nmdm_cdevsw = { - .d_version = D_VERSION, - .d_open = nmdmopen, - .d_close = nmdmclose, - .d_name = "nmdn", - .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT | D_NEEDMINOR, +MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures"); + +static tsw_inwakeup_t nmdm_outwakeup; +static tsw_outwakeup_t nmdm_inwakeup; +static tsw_param_t nmdm_param; +static tsw_modem_t nmdm_modem; + +static struct ttydevsw nmdm_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_inwakeup = nmdm_inwakeup, + .tsw_outwakeup = nmdm_outwakeup, + .tsw_param = nmdm_param, + .tsw_modem = nmdm_modem, }; -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ -#define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ -#define PF_STOPPED 0x10 /* user told stopped */ -#define BFLAG CLONE_FLAG0 - -struct softpart { - struct tty *nm_tty; - struct cdev *dev; - int nm_dcd; - struct task pt_task; - struct softpart *other; - struct callout co; - u_long quota; - u_long accumulator; - int rate; - int credits; +static void nmdm_task_tty(void *, int); + +struct nmdmsoftc; + +struct nmdmpart { + struct tty *np_tty; + int np_dcd; + struct task np_task; + struct nmdmpart *np_other; + struct nmdmsoftc *np_pair; + struct callout np_callout; + u_long np_quota; + u_long np_accumulator; + int np_rate; + int np_credits; #define QS 8 /* Quota shift */ }; -struct nm_softc { - TAILQ_ENTRY(nm_softc) pt_list; - int pt_flags; - struct softpart part1, part2; - struct prison *pt_prison; +struct nmdmsoftc { + struct nmdmpart ns_part1; + struct nmdmpart ns_part2; + struct mtx ns_mtx; }; -static struct clonedevs *nmdmclones; -static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); +static int nmdm_count = 0; + +static struct nmdmsoftc * +nmdm_alloc(unsigned long unit) +{ + struct nmdmsoftc *ns; + struct tty *tp; + + atomic_add_acq_int(&nmdm_count, 1); + + ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK|M_ZERO); + mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); + + /* Hook the pairs together. */ + ns->ns_part1.np_pair = ns; + ns->ns_part1.np_other = &ns->ns_part2; + TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); + callout_init(&ns->ns_part1.np_callout, 0); + + ns->ns_part2.np_pair = ns; + ns->ns_part2.np_other = &ns->ns_part1; + TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); + callout_init(&ns->ns_part2.np_callout, 0); + + /* Create device nodes. */ + tp = ns->ns_part1.np_tty = tty_alloc(&nmdm_class, &ns->ns_part1, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luA", unit); + + tp = ns->ns_part2.np_tty = tty_alloc(&nmdm_class, &ns->ns_part2, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luB", unit); + + return (ns); +} static void nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, struct cdev **dev) { - int i, unit; - char *p; - struct cdev *d1, *d2; + unsigned long unit; + char *end; + struct nmdmsoftc *ns; if (*dev != NULL) return; - if (strcmp(name, "nmdm") == 0) { - p = NULL; - unit = -1; - } else { - i = dev_stdclone(name, &p, "nmdm", &unit); - if (i == 0) - return; - if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') - return; - else if (p[0] != '\0' && p[1] != '\0') - return; - } - i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); - if (i) { - d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), - 0, 0, 0666, "nmdm%dA", unit); - if (d1 == NULL) - return; - d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, - 0, 0, 0666, "nmdm%dB", unit); - if (d2 == NULL) { - destroy_dev(d1); - return; - } - d2->si_drv2 = d1; - d1->si_drv2 = d2; - dev_depends(d1, d2); - dev_depends(d2, d1); - d1->si_flags |= SI_CHEAPCLONE; - d2->si_flags |= SI_CHEAPCLONE; - } - if (p != NULL && p[0] == 'B') - *dev = d1->si_drv2; + if (strncmp(name, "nmdm", 4) != 0) + return; + + /* Device name must be "nmdm%lu%c", where %c is 'A' or 'B'. */ + name += 4; + unit = strtoul(name, &end, 10); + if (unit == ULONG_MAX || name == end) + return; + if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0') + return; + + /* XXX: pass privileges? */ + ns = nmdm_alloc(unit); + + if (end[0] == 'A') + *dev = ns->ns_part1.np_tty->t_dev; else - *dev = d1; - dev_ref(*dev); + *dev = ns->ns_part2.np_tty->t_dev; } static void nmdm_timeout(void *arg) { - struct softpart *sp; - - sp = arg; + struct nmdmpart *np = arg; - if (sp->rate == 0) + if (np->np_rate == 0) return; /* * Do a simple Floyd-Steinberg dither here to avoid FP math. * Wipe out unused quota from last tick. */ - sp->accumulator += sp->credits; - sp->quota = sp->accumulator >> QS; - sp->accumulator &= ((1 << QS) - 1); + np->np_accumulator += np->np_credits; + np->np_quota = np->np_accumulator >> QS; + np->np_accumulator &= ((1 << QS) - 1); - taskqueue_enqueue(taskqueue_swi_giant, &sp->pt_task); - callout_reset(&sp->co, sp->rate, nmdm_timeout, arg); + taskqueue_enqueue(taskqueue_swi, &np->np_task); + callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); } static void nmdm_task_tty(void *arg, int pending __unused) { struct tty *tp, *otp; - struct softpart *sp; - int c; + struct nmdmpart *np = arg; + char c; - tp = arg; - sp = tp->t_sc; - otp = sp->other->nm_tty; + tp = np->np_tty; + tty_lock(tp); + + otp = np->np_other->np_tty; KASSERT(otp != NULL, ("NULL otp in nmdmstart")); KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); - if (sp->other->nm_dcd) { - if (!(tp->t_state & TS_ISOPEN)) { - sp->other->nm_dcd = 0; - (void)ttyld_modem(otp, 0); + if (np->np_other->np_dcd) { + if (!tty_opened(tp)) { + np->np_other->np_dcd = 0; + ttydisc_modem(otp, 0); } } else { - if (tp->t_state & TS_ISOPEN) { - sp->other->nm_dcd = 1; - (void)ttyld_modem(otp, 1); + if (tty_opened(tp)) { + np->np_other->np_dcd = 1; + ttydisc_modem(otp, 1); } } - if (tp->t_state & TS_TTSTOP) - return; - while (tp->t_outq.c_cc != 0) { - if (sp->rate && !sp->quota) - return; - if (otp->t_state & TS_TBLOCK) - return; - sp->quota--; - c = getc(&tp->t_outq); - if (otp->t_state & TS_ISOPEN) - ttyld_rint(otp, c); - } - if (tp->t_outq.c_cc == 0) - ttwwakeup(tp); -} - -/* - * This function creates and initializes a pair of ttys. - */ -static void -nmdminit(struct cdev *dev1) -{ - struct cdev *dev2; - struct nm_softc *pt; - - dev2 = dev1->si_drv2; - - dev1->si_flags &= ~SI_CHEAPCLONE; - dev2->si_flags &= ~SI_CHEAPCLONE; - - pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); - TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); - - dev1->si_drv1 = dev2->si_drv1 = pt; - - pt->part1.dev = dev1; - pt->part2.dev = dev2; - - pt->part1.nm_tty = ttyalloc(); - pt->part1.nm_tty->t_oproc = nmdmoproc; - pt->part1.nm_tty->t_stop = nmdmstop; - pt->part1.nm_tty->t_modem = nmdmmodem; - pt->part1.nm_tty->t_param = nmdmparam; - pt->part1.nm_tty->t_dev = dev1; - pt->part1.nm_tty->t_sc = &pt->part1; - TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); - callout_init(&pt->part1.co, 0); - - pt->part2.nm_tty = ttyalloc(); - pt->part2.nm_tty->t_oproc = nmdmoproc; - pt->part2.nm_tty->t_stop = nmdmstop; - pt->part2.nm_tty->t_modem = nmdmmodem; - pt->part2.nm_tty->t_param = nmdmparam; - pt->part2.nm_tty->t_dev = dev2; - pt->part2.nm_tty->t_sc = &pt->part2; - TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); - callout_init(&pt->part2.co, 0); - - pt->part1.other = &pt->part2; - pt->part2.other = &pt->part1; - - dev1->si_tty = pt->part1.nm_tty; - dev1->si_drv1 = pt; - - dev2->si_tty = pt->part2.nm_tty; - dev2->si_drv1 = pt; -} - -/* - * Device opened from userland - */ -static int -nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp, *tp2; - int error; - struct nm_softc *pti; - struct softpart *sp; - - if (dev->si_drv1 == NULL) - nmdminit(dev); - pti = dev->si_drv1; - if (pti->pt_prison != td->td_ucred->cr_prison) - return (EBUSY); - - tp = dev->si_tty; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; - - if ((tp->t_state & TS_ISOPEN) == 0) { - ttyinitmode(tp, 0, 0); - ttsetwater(tp); /* XXX ? */ - } else if (tp->t_state & TS_XCLUDE && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); + while (ttydisc_rint_poll(otp) > 0) { + if (np->np_rate && !np->np_quota) + break; + if (ttydisc_getc(tp, &c, 1) != 1) + break; + np->np_quota--; + ttydisc_rint(otp, c, 0); } - error = ttyld_open(tp, dev); - return (error); + ttydisc_rint_done(otp); + + tty_unlock(tp); } static int @@ -317,18 +238,17 @@ bits_per_char(struct termios *t) } static int -nmdmparam(struct tty *tp, struct termios *t) +nmdm_param(struct tty *tp, struct termios *t) { - struct softpart *sp; + struct nmdmpart *np = tty_softc(tp); struct tty *tp2; int bpc, rate, speed, i; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; + tp2 = np->np_other->np_tty; - if (!((t->c_cflag | tp2->t_cflag) & CDSR_OFLOW)) { - sp->rate = 0; - sp->other->rate = 0; + if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -343,10 +263,10 @@ nmdmparam(struct tty *tp, struct termios *t) for (i = 0; i < 2; i++) { /* Use the slower of our receive and their transmit rate */ - speed = imin(tp2->t_ospeed, t->c_ispeed); + speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); if (speed == 0) { - sp->rate = 0; - sp->other->rate = 0; + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -359,73 +279,63 @@ nmdmparam(struct tty *tp, struct termios *t) speed *= rate; speed /= hz; /* [(char/sec)/tick, scaled */ - sp->credits = speed; - sp->rate = rate; - callout_reset(&sp->co, rate, nmdm_timeout, sp); + np->np_credits = speed; + np->np_rate = rate; + callout_reset(&np->np_callout, rate, nmdm_timeout, np); /* * swap pointers for second pass so the other end gets * updated as well. */ - sp = sp->other; + np = np->np_other; t = &tp2->t_termios; tp2 = tp; } + return (0); } static int -nmdmmodem(struct tty *tp, int sigon, int sigoff) +nmdm_modem(struct tty *tp, int sigon, int sigoff) { - struct softpart *sp; - int i; + struct nmdmpart *np = tty_softc(tp); + int i = 0; - sp = tp->t_sc; if (sigon || sigoff) { if (sigon & SER_DTR) - sp->other->nm_dcd = 1; + np->np_other->np_dcd = 1; if (sigoff & SER_DTR) - sp->other->nm_dcd = 0; - ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); + np->np_other->np_dcd = 0; + + ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); + return (0); } else { - i = 0; - if (sp->nm_dcd) + if (np->np_dcd) i |= SER_DCD; - if (sp->other->nm_dcd) + if (np->np_other->np_dcd) i |= SER_DTR; + return (i); } } -static int -nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp = dev->si_tty; - int error; - - error = ttyld_close(tp, flag); - (void) tty_close(dev->si_tty); - - return (error); -} - static void -nmdmoproc(struct tty *tp) +nmdm_inwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can receive again, so wake up the other side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); } static void -nmdmstop(struct tty *tp, int flush) +nmdm_outwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can transmit again, so wake up our side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_task); } /* @@ -435,32 +345,28 @@ static int nmdm_modevent(module_t mod, int type, void *data) { static eventhandler_tag tag; - struct nm_softc *pt, *tpt; - int error = 0; switch(type) { case MOD_LOAD: - clone_setup(&nmdmclones); tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); if (tag == NULL) return (ENOMEM); break; case MOD_SHUTDOWN: - /* FALLTHROUGH */ + break; + case MOD_UNLOAD: + if (nmdm_count != 0) + return (EBUSY); EVENTHANDLER_DEREGISTER(dev_clone, tag); - TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { - destroy_dev(pt->part1.dev); - TAILQ_REMOVE(&nmdmhead, pt, pt_list); - free(pt, M_NLMDM); - } - clone_cleanup(&nmdmclones); break; + default: - error = EOPNOTSUPP; + return (EOPNOTSUPP); } - return (error); + + return (0); } DEV_MODULE(nmdm, nmdm_modevent, NULL); diff --git a/sys/dev/ofw/ofw_console.c b/sys/dev/ofw/ofw_console.c index 156f121..bec52d6 100644 --- a/sys/dev/ofw/ofw_console.c +++ b/sys/dev/ofw/ofw_console.c @@ -49,15 +49,15 @@ __FBSDID("$FreeBSD$"); #endif #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ -static d_open_t ofw_dev_open; -static d_close_t ofw_dev_close; - -static struct cdevsw ofw_cdevsw = { - .d_version = D_VERSION, - .d_open = ofw_dev_open, - .d_close = ofw_dev_close, - .d_name = "ofw", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t ofwtty_open; +static tsw_close_t ofwtty_close; +static tsw_outwakeup_t ofwtty_outwakeup; + +static struct ttydevsw ofw_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_open = ofwtty_open, + .tsw_close = ofwtty_close, + .tsw_outwakeup = ofwtty_outwakeup, }; static struct tty *ofw_tp = NULL; @@ -69,9 +69,6 @@ static struct callout_handle ofw_timeouthandle static int alt_break_state; #endif -static void ofw_tty_start(struct tty *); -static int ofw_tty_param(struct tty *, struct termios *); -static void ofw_tty_stop(struct tty *, int); static void ofw_timeout(void *); static cn_probe_t ofw_cnprobe; @@ -87,7 +84,7 @@ cn_drvinit(void *unused) { phandle_t options; char output[32]; - struct cdev *dev; + struct tty *tp; if (ofw_consdev.cn_pri != CN_DEAD && ofw_consdev.cn_name[0] != '\0') { @@ -99,9 +96,9 @@ cn_drvinit(void *unused) * XXX: This is a hack and it may result in two /dev/ttya * XXX: devices on platforms where the sab driver works. */ - dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", - output); - make_dev_alias(dev, "ofwcons"); + tp = tty_alloc(&ofw_ttydevsw, NULL, NULL); + tty_makedev(tp, NULL, "%s", output); + tty_makealias(tp, "ofwcons"); } } @@ -111,112 +108,36 @@ static int stdin; static int stdout; static int -ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td) +ofwtty_open(struct tty *tp) { - struct tty *tp; - int unit; - int error, setuptimeout; - - error = 0; - setuptimeout = 0; - unit = minor(dev); - - /* - * XXX: BAD, should happen at attach time - */ - if (dev->si_tty == NULL) { - ofw_tp = ttyalloc(); - dev->si_tty = ofw_tp; - ofw_tp->t_dev = dev; - } - tp = dev->si_tty; - - tp->t_oproc = ofw_tty_start; - tp->t_param = ofw_tty_param; - tp->t_stop = ofw_tty_stop; - tp->t_dev = dev; - - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } - - error = ttyld_open(tp, dev); + polltime = hz / OFWCONS_POLL_HZ; + if (polltime < 1) + polltime = 1; - if (error == 0 && setuptimeout) { - polltime = hz / OFWCONS_POLL_HZ; - if (polltime < 1) { - polltime = 1; - } - - ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); - } + ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); - return (error); + return (0); } -static int -ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +ofwtty_close(struct tty *tp) { - int unit; - struct tty *tp; - - unit = minor(dev); - tp = dev->si_tty; - - if (unit != 0) { - return (ENXIO); - } /* XXX Should be replaced with callout_stop(9) */ untimeout(ofw_timeout, tp, ofw_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - - return (0); -} - - -static int -ofw_tty_param(struct tty *tp, struct termios *t) -{ - - return (0); } static void -ofw_tty_start(struct tty *tp) +ofwtty_outwakeup(struct tty *tp) { - struct clist *cl; int len; u_char buf[OFBURSTLEN]; - - if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) - return; - - tp->t_state |= TS_BUSY; - cl = &tp->t_outq; - len = q_to_b(cl, buf, OFBURSTLEN); - OF_write(stdout, buf, len); - tp->t_state &= ~TS_BUSY; - - ttwwakeup(tp); -} - -static void -ofw_tty_stop(struct tty *tp, int flag) -{ - - if (tp->t_state & TS_BUSY) { - if ((tp->t_state & TS_TTSTOP) == 0) { - tp->t_state |= TS_FLUSH; - } + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; + OF_write(stdout, buf, len); } } @@ -228,11 +149,11 @@ ofw_timeout(void *v) tp = (struct tty *)v; - while ((c = ofw_cngetc(NULL)) != -1) { - if (tp->t_state & TS_ISOPEN) { - ttyld_rint(tp, c); - } - } + tty_lock(tp); + while ((c = ofw_cngetc(NULL)) != -1) + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); } diff --git a/sys/dev/syscons/schistory.c b/sys/dev/syscons/schistory.c index 860e8f9..860367b 100644 --- a/sys/dev/syscons/schistory.c +++ b/sys/dev/syscons/schistory.c @@ -291,8 +291,7 @@ sc_hist_down_line(scr_stat *scp) } int -sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) +sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; int error; @@ -300,7 +299,7 @@ sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, switch (cmd) { case CONS_HISTORY: /* set history size */ - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); if (*(int *)data <= 0) return EINVAL; if (scp->status & BUFFER_SAVED) @@ -315,7 +314,7 @@ sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, return error; case CONS_CLRHIST: - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); sc_vtb_clear(scp->history, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); return 0; diff --git a/sys/dev/syscons/scmouse.c b/sys/dev/syscons/scmouse.c index 25a2da0..2d998d4 100644 --- a/sys/dev/syscons/scmouse.c +++ b/sys/dev/syscons/scmouse.c @@ -605,8 +605,7 @@ sc_mouse_paste(scr_stat *scp) #endif /* SC_NO_CUTPASTE */ int -sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) +sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { mouse_info_t *mouse; mouse_info_t buf; @@ -616,7 +615,7 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, int s; int f; - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); switch (cmd) { diff --git a/sys/dev/syscons/scterm-sc.c b/sys/dev/syscons/scterm-sc.c index e89debb..b52bea8 100644 --- a/sys/dev/syscons/scterm-sc.c +++ b/sys/dev/syscons/scterm-sc.c @@ -705,7 +705,7 @@ outloop: static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td) + struct thread *td) { term_stat *tcp = scp->ts; vid_info_t *vi; diff --git a/sys/dev/syscons/scvesactl.c b/sys/dev/syscons/scvesactl.c index c68d46a..9a2c253 100644 --- a/sys/dev/syscons/scvesactl.c +++ b/sys/dev/syscons/scvesactl.c @@ -48,19 +48,15 @@ __FBSDID("$FreeBSD$"); #include <dev/fb/fbreg.h> #include <dev/syscons/syscons.h> -static d_ioctl_t *prev_user_ioctl; +static tsw_ioctl_t *prev_user_ioctl; static int -vesa_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +vesa_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; - struct tty *tp; int mode; - tp = dev->si_tty; - if (!tp) - return ENXIO; - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); switch (cmd) { @@ -123,7 +119,7 @@ vesa_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread * } if (prev_user_ioctl) - return (*prev_user_ioctl)(dev, cmd, data, flag, td); + return (*prev_user_ioctl)(tp, cmd, data, td); else return ENOIOCTL; } diff --git a/sys/dev/syscons/scvidctl.c b/sys/dev/syscons/scvidctl.c index 5e1d910..045f79f 100644 --- a/sys/dev/syscons/scvidctl.c +++ b/sys/dev/syscons/scvidctl.c @@ -241,11 +241,8 @@ sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGWINCH, 1); - PGRP_UNLOCK(tp->t_pgrp); - } + + tty_signal_pgrp(tp, SIGWINCH); } return 0; @@ -308,11 +305,8 @@ sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGWINCH, 1); - PGRP_UNLOCK(tp->t_pgrp); - } + + tty_signal_pgrp(tp, SIGWINCH); } return 0; @@ -475,7 +469,7 @@ sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, vidd_ioctl((a), (c), (caddr_t)(d))) int -sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) +sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { scr_stat *scp; video_adapter_t *adp; @@ -488,7 +482,7 @@ sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread * int ival; #endif - scp = SC_STAT(tp->t_dev); + scp = SC_STAT(tp); if (scp == NULL) /* tp == SC_MOUSE */ return ENOIOCTL; adp = scp->sc->adp; diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 96ca0b7..ed5e459 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/random.h> #include <sys/reboot.h> +#include <sys/serial.h> #include <sys/signalvar.h> #include <sys/sysctl.h> #include <sys/tty.h> @@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <machine/psl.h> #include <machine/frame.h> #endif +#include <machine/stdarg.h> #include <dev/kbd/kbdreg.h> #include <dev/fb/fbreg.h> @@ -105,7 +107,7 @@ static struct tty *sc_console_tty; static struct consdev *sc_consptr; static void *kernel_console_ts; static scr_stat main_console; -static struct cdev *main_devs[MAXCONS]; +static struct tty *main_devs[MAXCONS]; static char init_done = COLD; static char shutdown_in_progress = FALSE; @@ -150,7 +152,7 @@ SYSCTL_INT(_hw_syscons, OID_AUTO, kbd_debug, CTLFLAG_RW|CTLFLAG_SECURE, &enable_ #include "font.h" #endif - d_ioctl_t *sc_user_ioctl; + tsw_ioctl_t *sc_user_ioctl; static bios_values_t bios_value; @@ -161,24 +163,18 @@ SYSCTL_INT(_machdep, OID_AUTO, enable_panic_key, CTLFLAG_RW, &enable_panic_key, #define SC_CONSOLECTL 255 #define VTY_WCHAN(sc, vty) (&SC_DEV(sc, vty)) -#define VIRTUAL_TTY(sc, x) (SC_DEV((sc), (x)) != NULL ? \ - SC_DEV((sc), (x))->si_tty : NULL) -#define ISTTYOPEN(tp) ((tp) && ((tp)->t_state & TS_ISOPEN)) static int debugger; /* prototypes */ static int sc_allocate_keyboard(sc_softc_t *sc, int unit); -static struct tty *sc_alloc_tty(struct cdev *dev); static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scmeminit(void *arg); -static int scdevtounit(struct cdev *dev); +static int scdevtounit(struct tty *tp); static kbd_callback_func_t sckbdevent; -static int scparam(struct tty *tp, struct termios *t); -static void scstart(struct tty *tp); static void scinit(int unit, int flags); -static scr_stat *sc_get_stat(struct cdev *devptr); +static scr_stat *sc_get_stat(struct tty *tp); static void scterm(int unit, int flags); static void scshutdown(void *arg, int howto); static u_int scgetc(sc_softc_t *sc, u_int flags); @@ -219,6 +215,7 @@ static int save_kbd_state(scr_stat *scp); static int update_kbd_state(scr_stat *scp, int state, int mask); static int update_kbd_leds(scr_stat *scp, int which); static timeout_t blink_screen; +static struct tty *sc_alloc_tty(int, const char *, ...) __printflike(2, 3); static cn_probe_t sc_cnprobe; static cn_init_t sc_cninit; @@ -228,21 +225,23 @@ static cn_putc_t sc_cnputc; CONSOLE_DRIVER(sc); -static d_open_t scopen; -static d_close_t scclose; -static d_read_t scread; -static d_ioctl_t scioctl; -static d_mmap_t scmmap; - -static struct cdevsw sc_cdevsw = { - .d_version = D_VERSION, - .d_open = scopen, - .d_close = scclose, - .d_read = scread, - .d_ioctl = scioctl, - .d_mmap = scmmap, - .d_name = "sc", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t sctty_open; +static tsw_close_t sctty_close; +static tsw_outwakeup_t sctty_outwakeup; +static tsw_ioctl_t sctty_ioctl; +static tsw_mmap_t sctty_mmap; + +static struct ttydevsw sc_ttydevsw = { + /* + * XXX: we should use the prefix, but this doesn't work for + * consolectl. + */ + .tsw_flags = TF_NOPREFIX, + .tsw_open = sctty_open, + .tsw_close = sctty_close, + .tsw_outwakeup = sctty_outwakeup, + .tsw_ioctl = sctty_ioctl, + .tsw_mmap = sctty_mmap, }; int @@ -310,17 +309,47 @@ static char return names[i].name[(adp->va_flags & V_ADP_COLOR) ? 0 : 1]; } +static void +sctty_outwakeup(struct tty *tp) +{ + size_t len; + u_char buf[PCBURST]; + scr_stat *scp = sc_get_stat(tp); + + if (scp->status & SLKED || + (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) + return; + + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; + sc_puts(scp, buf, len); + } +} + static struct tty * -sc_alloc_tty(struct cdev *dev) +sc_alloc_tty(int index, const char *fmt, ...) { + va_list ap; + struct sc_ttysoftc *stc; struct tty *tp; + char name[11]; /* "consolectl" */ + + va_start(ap, fmt); + + /* Allocate TTY object and softc to store unit number. */ + stc = malloc(sizeof(struct sc_ttysoftc), M_DEVBUF, M_WAITOK); + stc->st_index = index; + stc->st_stat = NULL; + tp = tty_alloc(&sc_ttydevsw, stc, &Giant); + + /* Create device node. */ + va_start(ap, fmt); + vsnrprintf(name, sizeof name, 32, fmt, ap); + va_end(ap); + tty_makedev(tp, NULL, "%s", name); - tp = dev->si_tty = ttyalloc(); - ttyinitmode(tp, 1, 0); - tp->t_oproc = scstart; - tp->t_param = scparam; - tp->t_stop = nottystop; - tp->t_dev = dev; return (tp); } @@ -333,7 +362,6 @@ sc_attach_unit(int unit, int flags) video_info_t info; #endif int vc; - struct cdev *dev; flags &= ~SC_KERNEL_CONSOLE; @@ -418,9 +446,7 @@ sc_attach_unit(int unit, int flags) for (vc = 0; vc < sc->vtys; vc++) { if (sc->dev[vc] == NULL) { - sc->dev[vc] = make_dev(&sc_cdevsw, vc + unit * MAXCONS, - UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc + unit * MAXCONS); - sc_alloc_tty(sc->dev[vc]); + sc->dev[vc] = sc_alloc_tty(vc, "ttyv%r", vc + unit * MAXCONS); if (vc == 0 && sc->dev == main_devs) SC_STAT(sc->dev[0]) = &main_console; } @@ -431,11 +457,8 @@ sc_attach_unit(int unit, int flags) */ } - dev = make_dev(&sc_cdevsw, SC_CONSOLECTL, - UID_ROOT, GID_WHEEL, 0600, "consolectl"); - sc_console_tty = sc_alloc_tty(dev); - ttyconsolemode(sc_console_tty, 0); - SC_STAT(dev) = sc_console; + sc_console_tty = sc_alloc_tty(0, "consolectl"); + SC_STAT(sc_console_tty) = sc_console; return 0; } @@ -472,9 +495,9 @@ scmeminit(void *arg) SYSINIT(sc_mem, SI_SUB_KMEM, SI_ORDER_ANY, scmeminit, NULL); static int -scdevtounit(struct cdev *dev) +scdevtounit(struct tty *tp) { - int vty = SC_VTY(dev); + int vty = SC_VTY(tp); if (vty == SC_CONSOLECTL) return ((sc_console != NULL) ? sc_console->sc->unit : -1); @@ -485,48 +508,37 @@ scdevtounit(struct cdev *dev) } static int -scopen(struct cdev *dev, int flag, int mode, struct thread *td) +sctty_open(struct tty *tp) { - int unit = scdevtounit(dev); + int unit = scdevtounit(tp); sc_softc_t *sc; - struct tty *tp; scr_stat *scp; #ifndef __sparc64__ keyarg_t key; #endif - int error; DPRINTF(5, ("scopen: dev:%s, unit:%d, vty:%d\n", - devtoname(dev), unit, SC_VTY(dev))); + devtoname(tp->t_dev), unit, SC_VTY(tp))); - tp = dev->si_tty; sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc == NULL) return ENXIO; - if (!ISTTYOPEN(tp)) { - tp->t_termios = tp->t_init_in; + if (!tty_opened(tp)) { /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ #ifndef __sparc64__ if (sc->kbd != NULL) { key.keynum = KEYCODE_BS; kbdd_ioctl(sc->kbd, GIO_KEYMAPENT, (caddr_t)&key); - tp->t_cc[VERASE] = key.key.map[0]; + tp->t_termios.c_cc[VERASE] = key.key.map[0]; } #endif - scparam(tp, &tp->t_termios); - ttyld_modem(tp, 1); } - else - if (tp->t_state & TS_XCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) - return(EBUSY); - error = ttyld_open(tp, dev); - - scp = sc_get_stat(dev); + scp = sc_get_stat(tp); if (scp == NULL) { - scp = SC_STAT(dev) = alloc_scp(sc, SC_VTY(dev)); + scp = SC_STAT(tp) = alloc_scp(sc, SC_VTY(tp)); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, COL, ROW, 16, 8); } @@ -535,18 +547,17 @@ scopen(struct cdev *dev, int flag, int mode, struct thread *td) tp->t_winsize.ws_row = scp->ysize; } - return error; + return (0); } -static int -scclose(struct cdev *dev, int flag, int mode, struct thread *td) +static void +sctty_close(struct tty *tp) { - struct tty *tp = dev->si_tty; scr_stat *scp; int s; - if (SC_VTY(dev) != SC_CONSOLECTL) { - scp = sc_get_stat(tp->t_dev); + if (SC_VTY(tp) != SC_CONSOLECTL) { + scp = sc_get_stat(tp); /* were we in the middle of the VT switching process? */ DPRINTF(5, ("sc%d: scclose(), ", scp->sc->unit)); s = spltty(); @@ -568,7 +579,7 @@ scclose(struct cdev *dev, int flag, int mode, struct thread *td) sc_vtb_destroy(&scp->scr); #endif sc_free_history_buffer(scp, scp->ysize); - SC_STAT(dev) = NULL; + SC_STAT(tp) = NULL; free(scp, M_DEVBUF); } #else @@ -581,13 +592,9 @@ scclose(struct cdev *dev, int flag, int mode, struct thread *td) kbdd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); DPRINTF(5, ("done.\n")); } - spltty(); - ttyld_close(tp, flag); - tty_close(tp); - spl0(); - return(0); } +#if 0 /* XXX mpsafetty: fix screensaver. What about outwakeup? */ static int scread(struct cdev *dev, struct uio *uio, int flag) { @@ -595,19 +602,22 @@ scread(struct cdev *dev, struct uio *uio, int flag) sc_touch_scrn_saver(); return ttyread(dev, uio, flag); } +#endif static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { sc_softc_t *sc; struct tty *cur_tty; - int c; + int c, error = 0; size_t len; u_char *cp; sc = (sc_softc_t *)arg; /* assert(thiskbd == sc->kbd) */ + mtx_lock(&Giant); + switch (event) { case KBDIO_KEYINPUT: break; @@ -615,9 +625,10 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) sc->kbd = NULL; sc->keyboard = -1; kbd_release(thiskbd, (void *)&sc->keyboard); - return 0; + goto done; default: - return EINVAL; + error = EINVAL; + goto done; } /* @@ -627,10 +638,12 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) */ while ((c = scgetc(sc, SCGETC_NONBLOCK)) != NOKEY) { - cur_tty = VIRTUAL_TTY(sc, sc->cur_scp->index); - if (!ISTTYOPEN(cur_tty)) { + cur_tty = SC_DEV(sc, sc->cur_scp->index); + if (!tty_opened(cur_tty)) { cur_tty = sc_console_tty; - if (!ISTTYOPEN(cur_tty)) + if (cur_tty == NULL) + continue; + if (!tty_opened(cur_tty)) continue; } @@ -639,47 +652,45 @@ sckbdevent(keyboard_t *thiskbd, int event, void *arg) switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ - ttyld_rint(cur_tty, KEYCHAR(c)); + ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case FKEY: /* function key, return string */ cp = kbdd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) { - while (len-- > 0) - ttyld_rint(cur_tty, *cp++); + if (ttydisc_can_bypass(cur_tty)) { + ttydisc_rint_bypass(cur_tty, cp, len); + } else { + while (len-- > 0) + ttydisc_rint(cur_tty, *cp++, 0); + } } break; case MKEY: /* meta is active, prepend ESC */ - ttyld_rint(cur_tty, 0x1b); - ttyld_rint(cur_tty, KEYCHAR(c)); + ttydisc_rint(cur_tty, 0x1b, 0); + ttydisc_rint(cur_tty, KEYCHAR(c), 0); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ - ttyld_rint(cur_tty, 0x1b); - ttyld_rint(cur_tty, '['); - ttyld_rint(cur_tty, 'Z'); + ttydisc_rint(cur_tty, 0x1b, 0); + ttydisc_rint(cur_tty, '[', 0); + ttydisc_rint(cur_tty, 'Z', 0); break; } + + ttydisc_rint_done(cur_tty); } sc->cur_scp->status |= MOUSE_HIDDEN; - return 0; +done: + mtx_unlock(&Giant); + return (error); } static int -scparam(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; -} - -static int -scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +sctty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { int error; int i; - struct tty *tp; sc_softc_t *sc; scr_stat *scp; int s; @@ -688,38 +699,36 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) int ival; #endif - tp = dev->si_tty; - /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { - error = (*sc_user_ioctl)(dev, cmd, data, flag, td); + error = (*sc_user_ioctl)(tp, cmd, data, td); if (error != ENOIOCTL) return error; } - error = sc_vid_ioctl(tp, cmd, data, flag, td); + error = sc_vid_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #ifndef SC_NO_HISTORY - error = sc_hist_ioctl(tp, cmd, data, flag, td); + error = sc_hist_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif #ifndef SC_NO_SYSMOUSE - error = sc_mouse_ioctl(tp, cmd, data, flag, td); + error = sc_mouse_ioctl(tp, cmd, data, td); if (error != ENOIOCTL) return error; #endif - scp = sc_get_stat(tp->t_dev); + scp = sc_get_stat(tp); /* assert(scp != NULL) */ /* scp is sc_console, if SC_VTY(dev) == SC_CONSOLECTL. */ sc = scp->sc; if (scp->tsw) { - error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, flag, td); + error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, td); if (error != ENOIOCTL) return error; } @@ -1031,8 +1040,8 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) case VT_OPENQRY: /* return free virtual console */ for (i = sc->first_vty; i < sc->first_vty + sc->vtys; i++) { - tp = VIRTUAL_TTY(sc, i); - if (!ISTTYOPEN(tp)) { + tp = SC_DEV(sc, i); + if (!tty_opened(tp)) { *(int *)data = i + 1; return 0; } @@ -1053,7 +1062,8 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) splx(s); if (error) return error; - return sc_switch_scr(sc, i); + error = sc_switch_scr(sc, i); + return (error); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) @@ -1441,34 +1451,7 @@ scioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) break; } - return (ttyioctl(dev, cmd, data, flag, td)); -} - -static void -scstart(struct tty *tp) -{ - struct clist *rbp; - int s, len; - u_char buf[PCBURST]; - scr_stat *scp = sc_get_stat(tp->t_dev); - - if (scp->status & SLKED || - (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) - return; - s = spltty(); - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; - rbp = &tp->t_outq; - while (rbp->c_cc) { - len = q_to_b(rbp, buf, PCBURST); - splx(s); - sc_puts(scp, buf, len); - s = spltty(); - } - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); - } - splx(s); + return (ENOIOCTL); } static void @@ -1548,9 +1531,11 @@ sc_cnputc(struct consdev *cd, int c) scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } - tp = VIRTUAL_TTY(scp->sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(scp->sc, scp->index); + tty_lock(tp); + if (tty_opened(tp)) + sctty_outwakeup(tp); + tty_unlock(tp); } #endif /* !SC_NO_HISTORY */ @@ -2281,9 +2266,9 @@ sc_switch_scr(sc_softc_t *sc, u_int next_scr) * if the switch mode is VT_AUTO, unless the next vty is the same * as the current or the current vty has been closed (but showing). */ - tp = VIRTUAL_TTY(sc, cur_scp->index); + tp = SC_DEV(sc, cur_scp->index); if ((cur_scp->index != next_scr) - && ISTTYOPEN(tp) + && tty_opened(tp) && (cur_scp->smode.mode == VT_AUTO) && ISGRAPHSC(cur_scp)) { splx(s); @@ -2299,14 +2284,14 @@ sc_switch_scr(sc_softc_t *sc, u_int next_scr) * console even if it is closed. */ if ((sc_console == NULL) || (next_scr != sc_console->index)) { - tp = VIRTUAL_TTY(sc, next_scr); - if (!ISTTYOPEN(tp)) { + tp = SC_DEV(sc, next_scr); + if (!tty_opened(tp)) { splx(s); sc_bell(cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 2, requested vty isn't open!\n")); return EINVAL; } - if ((debugger > 0) && (SC_STAT(tp->t_dev)->smode.mode == VT_PROCESS)) { + if ((debugger > 0) && (SC_STAT(tp)->smode.mode == VT_PROCESS)) { splx(s); DPRINTF(5, ("error 3, requested vty is in the VT_PROCESS mode\n")); return EINVAL; @@ -2609,7 +2594,7 @@ void sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) { sc_softc_t *sc; - struct cdev *dev; + struct tty *tp; int s; int i; @@ -2635,9 +2620,9 @@ sc_change_cursor_shape(scr_stat *scp, int flags, int base, int height) } for (i = sc->first_vty; i < sc->first_vty + sc->vtys; ++i) { - if ((dev = SC_DEV(sc, i)) == NULL) + if ((tp = SC_DEV(sc, i)) == NULL) continue; - if ((scp = sc_get_stat(dev)) == NULL) + if ((scp = sc_get_stat(tp)) == NULL) continue; scp->dflt_curs_attr = sc->curs_attr; change_cursor_shape(scp, CONS_RESET_CURSOR, -1, -1); @@ -2759,10 +2744,9 @@ scinit(int unit, int flags) kernel_default.rev_color); } else { /* assert(sc_malloc) */ - sc->dev = malloc(sizeof(struct cdev *)*sc->vtys, M_DEVBUF, M_WAITOK|M_ZERO); - sc->dev[0] = make_dev(&sc_cdevsw, unit * MAXCONS, - UID_ROOT, GID_WHEEL, 0600, "ttyv%r", unit * MAXCONS); - sc_alloc_tty(sc->dev[0]); + sc->dev = malloc(sizeof(struct tty *)*sc->vtys, M_DEVBUF, + M_WAITOK|M_ZERO); + sc->dev[0] = sc_alloc_tty(0, "ttyv%r", unit * MAXCONS); scp = alloc_scp(sc, sc->first_vty); SC_STAT(sc->dev[0]) = scp; } @@ -3287,9 +3271,9 @@ next_code: scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } - tp = VIRTUAL_TTY(sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(sc, scp->index); + if (tty_opened(tp)) + sctty_outwakeup(tp); #endif } } @@ -3382,8 +3366,8 @@ next_code: for (i = (this_scr - sc->first_vty + 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + 1)%sc->vtys) { - struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); - if (ISTTYOPEN(tp)) { + struct tty *tp = SC_DEV(sc, sc->first_vty + i); + if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } @@ -3395,8 +3379,8 @@ next_code: for (i = (this_scr - sc->first_vty + sc->vtys - 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + sc->vtys - 1)%sc->vtys) { - struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); - if (ISTTYOPEN(tp)) { + struct tty *tp = SC_DEV(sc, sc->first_vty + i); + if (tty_opened(tp)) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } @@ -3425,11 +3409,11 @@ next_code: } static int -scmmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) +sctty_mmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { scr_stat *scp; - scp = sc_get_stat(dev); + scp = sc_get_stat(tp); if (scp != scp->sc->cur_scp) return -1; return vidd_mmap(scp->sc->adp, offset, paddr, nprot); @@ -3586,12 +3570,13 @@ sc_paste(scr_stat *scp, u_char *p, int count) struct tty *tp; u_char *rmap; - tp = VIRTUAL_TTY(scp->sc, scp->sc->cur_scp->index); - if (!ISTTYOPEN(tp)) + tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); + if (!tty_opened(tp)) return; rmap = scp->sc->scr_rmap; for (; count > 0; --count) - ttyld_rint(tp, rmap[*p++]); + ttydisc_rint(tp, rmap[*p++], 0); + ttydisc_rint_done(tp); } void @@ -3626,9 +3611,9 @@ blink_screen(void *arg) if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) { scp->sc->blink_in_progress = 0; mark_all(scp); - tp = VIRTUAL_TTY(scp->sc, scp->index); - if (ISTTYOPEN(tp)) - scstart(tp); + tp = SC_DEV(scp->sc, scp->index); + if (tty_opened(tp)) + sctty_outwakeup(tp); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } @@ -3650,11 +3635,11 @@ blink_screen(void *arg) */ static scr_stat * -sc_get_stat(struct cdev *devptr) +sc_get_stat(struct tty *tp) { - if (devptr == NULL) + if (tp == NULL) return (&main_console); - return (SC_STAT(devptr)); + return (SC_STAT(tp)); } /* diff --git a/sys/dev/syscons/syscons.h b/sys/dev/syscons/syscons.h index f17b294..548cec6 100644 --- a/sys/dev/syscons/syscons.h +++ b/sys/dev/syscons/syscons.h @@ -102,9 +102,9 @@ */ #define SC_DRIVER_NAME "syscons" #endif -#define SC_VTY(dev) minor(dev) +#define SC_VTY(dev) (((sc_ttysoftc *)tty_softc(tp))->st_index) #define SC_DEV(sc, vty) ((sc)->dev[(vty) - (sc)->first_vty]) -#define SC_STAT(dev) (*((scr_stat **)&(dev)->si_drv1)) +#define SC_STAT(tp) (*((scr_stat **)&((sc_ttysoftc *)tty_softc(tp))->st_stat)) /* printable chars */ #ifndef PRINTABLE @@ -220,7 +220,7 @@ typedef struct sc_softc { int first_vty; int vtys; - struct cdev **dev; + struct tty **dev; struct scr_stat *cur_scp; struct scr_stat *new_scp; struct scr_stat *old_scp; @@ -339,6 +339,12 @@ typedef struct scr_stat { #endif } scr_stat; +/* TTY softc. */ +typedef struct sc_ttysoftc { + int st_index; + scr_stat *st_stat; +} sc_ttysoftc; + #ifndef SC_NORM_ATTR #define SC_NORM_ATTR (FG_LIGHTGREY | BG_BLACK) #endif @@ -364,7 +370,7 @@ typedef int sc_term_init_t(scr_stat *scp, void **tcp, int code); typedef int sc_term_term_t(scr_stat *scp, void **tcp); typedef void sc_term_puts_t(scr_stat *scp, u_char *buf, int len); typedef int sc_term_ioctl_t(scr_stat *scp, struct tty *tp, u_long cmd, - caddr_t data, int flag, struct thread *td); + caddr_t data, struct thread *td); typedef int sc_term_reset_t(scr_stat *scp, int code); #define SC_TE_HARD_RESET 0 #define SC_TE_SOFT_RESET 1 @@ -531,8 +537,8 @@ typedef struct { } while(0) /* syscons.c */ -extern int (*sc_user_ioctl)(struct cdev *dev, u_long cmd, caddr_t data, - int flag, struct thread *td); +extern int (*sc_user_ioctl)(struct tty *tp, u_long cmd, caddr_t data, + struct thread *td); int sc_probe_unit(int unit, int flags); int sc_attach_unit(int unit, int flags); @@ -574,7 +580,7 @@ void sc_hist_end(scr_stat *scp); int sc_hist_up_line(scr_stat *scp); int sc_hist_down_line(scr_stat *scp); int sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); + struct thread *td); #endif /* SC_NO_HISTORY */ /* scmouse.c */ @@ -599,7 +605,7 @@ void sc_mouse_paste(scr_stat *scp); #ifndef SC_NO_SYSMOUSE void sc_mouse_move(scr_stat *scp, int x, int y); int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); + struct thread *td); #endif /* SC_NO_SYSMOUSE */ /* scvidctl.c */ @@ -609,7 +615,7 @@ int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode); int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize, int font_width); -int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, +int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td); int sc_render_add(sc_renderer_t *rndr); diff --git a/sys/dev/syscons/sysmouse.c b/sys/dev/syscons/sysmouse.c index 35d761b..9d926b8 100644 --- a/sys/dev/syscons/sysmouse.c +++ b/sys/dev/syscons/sysmouse.c @@ -32,8 +32,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> -#include <sys/conf.h> #include <sys/priv.h> +#include <sys/serial.h> #include <sys/tty.h> #include <sys/kernel.h> #include <sys/consio.h> @@ -45,106 +45,24 @@ __FBSDID("$FreeBSD$"); #define SC_MOUSE 128 /* minor number */ -static d_open_t smopen; -static d_close_t smclose; -static d_ioctl_t smioctl; - -static struct cdevsw sm_cdevsw = { - .d_version = D_VERSION, - .d_open = smopen, - .d_close = smclose, - .d_ioctl = smioctl, - .d_name = "sysmouse", - .d_flags = D_TTY | D_NEEDGIANT, -}; - /* local variables */ static struct tty *sysmouse_tty; static int mouse_level; /* sysmouse protocol level */ static mousestatus_t mouse_status; -static void smstart(struct tty *tp); -static int smparam(struct tty *tp, struct termios *t); - -static int -smopen(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - - DPRINTF(5, ("smopen: dev:%s, vty:%d\n", - devtoname(dev), SC_VTY(dev))); - -#if 0 - if (SC_VTY(dev) != SC_MOUSE) - return ENXIO; -#endif - - tp = dev->si_tty; - if (!(tp->t_state & TS_ISOPEN)) { - ttyinitmode(tp, 0, 0); - smparam(tp, &tp->t_termios); - ttyld_modem(tp, 1); - } else if (tp->t_state & TS_XCLUDE && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return EBUSY; - } - - return ttyld_open(tp, dev); -} - -static int -smclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - int s; - - tp = dev->si_tty; - s = spltty(); - mouse_level = 0; - ttyld_close(tp, flag); - tty_close(tp); - splx(s); - - return 0; -} - static void -smstart(struct tty *tp) -{ - struct clist *rbp; - u_char buf[PCBURST]; - int s; - - s = spltty(); - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; - rbp = &tp->t_outq; - while (rbp->c_cc) - q_to_b(rbp, buf, PCBURST); - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); - } - splx(s); -} - -static int -smparam(struct tty *tp, struct termios *t) +smdev_close(struct tty *tp) { - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - return 0; + mouse_level = 0; } static int -smioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +smdev_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { - struct tty *tp; mousehw_t *hw; mousemode_t *mode; int s; - tp = dev->si_tty; switch (cmd) { case MOUSE_GETHWINFO: /* get device information */ @@ -224,25 +142,35 @@ smioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) return ENODEV; } - return(ttyioctl(dev, cmd, data, flag, td)); + return (ENOIOCTL); } +static int +smdev_param(struct tty *tp, struct termios *t) +{ + + /* + * Set the output baud rate to zero. The mouse device supports + * no output, so we don't want to waste buffers. + */ + t->c_ispeed = TTYDEF_SPEED_PSEUDO; + t->c_ospeed = B0; + + return (0); +} + +static struct ttydevsw smdev_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_close = smdev_close, + .tsw_ioctl = smdev_ioctl, + .tsw_param = smdev_param, +}; + static void sm_attach_mouse(void *unused) { - struct cdev *dev; - struct tty *tp; - - dev = make_dev(&sm_cdevsw, SC_MOUSE, UID_ROOT, GID_WHEEL, 0600, - "sysmouse"); - dev->si_tty = tp = ttyalloc(); - tp->t_oproc = smstart; - tp->t_param = smparam; - tp->t_stop = nottystop; - tp->t_dev = dev; - - sysmouse_tty = tp; - /* sysmouse doesn't have scr_stat */ + sysmouse_tty = tty_alloc(&smdev_ttydevsw, NULL, &Giant); + tty_makedev(sysmouse_tty, NULL, "sysmouse"); } SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sm_attach_mouse, NULL); @@ -293,7 +221,7 @@ sysmouse_event(mouse_info_t *info) if (mouse_status.flags == 0) return 0; - if ((sysmouse_tty == NULL) || !(sysmouse_tty->t_state & TS_ISOPEN)) + if ((sysmouse_tty == NULL) || !tty_opened(sysmouse_tty)) return mouse_status.flags; /* the first five bytes are compatible with MouseSystems' */ @@ -306,7 +234,7 @@ sysmouse_event(mouse_info_t *info) buf[2] = y >> 1; buf[4] = y - buf[2]; for (i = 0; i < MOUSE_MSC_PACKETSIZE; ++i) - ttyld_rint(sysmouse_tty, buf[i]); + ttydisc_rint(sysmouse_tty, buf[i], 0); if (mouse_level >= 1) { /* extended part */ z = imax(imin(z, 127), -128); @@ -315,8 +243,9 @@ sysmouse_event(mouse_info_t *info) /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (i = MOUSE_MSC_PACKETSIZE; i < MOUSE_SYS_PACKETSIZE; ++i) - ttyld_rint(sysmouse_tty, buf[i]); + ttydisc_rint(sysmouse_tty, buf[i], 0); } + ttydisc_rint_done(sysmouse_tty); return mouse_status.flags; } diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index 3b0d64c..932c08a 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <sys/rman.h> #include <sys/termios.h> -#include <sys/tty.h> #include <machine/resource.h> #include <machine/stdarg.h> @@ -466,7 +465,7 @@ uart_bus_attach(device_t dev) sc->sc_polled = 1; } - sc->sc_rxbufsz = IBUFSIZ; + sc->sc_rxbufsz = 384; sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf), M_UART, M_WAITOK); sc->sc_txbuf = malloc(sc->sc_txfifosz * sizeof(*sc->sc_txbuf), diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c index 1971049..9b37bb3 100644 --- a/sys/dev/uart/uart_tty.c +++ b/sys/dev/uart/uart_tty.c @@ -123,11 +123,11 @@ uart_cngetc(struct consdev *cp) } static int -uart_tty_open(struct tty *tp, struct cdev *dev) +uart_tty_open(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return (ENXIO); @@ -141,7 +141,7 @@ uart_tty_close(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving || !sc->sc_opened) return; @@ -158,11 +158,11 @@ uart_tty_close(struct tty *tp) } static void -uart_tty_oproc(struct tty *tp) +uart_tty_outwakeup(struct tty *tp) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return; @@ -173,30 +173,45 @@ uart_tty_oproc(struct tty *tp) * de-assert RTS for us. In that situation we're completely stuffed. * Without hardware support, we need to toggle RTS ourselves. */ - if ((tp->t_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) { - if ((tp->t_state & TS_TBLOCK) && + if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) { +#if 0 + /*if ((tp->t_state & TS_TBLOCK) && (sc->sc_hwsig & SER_RTS)) UART_SETSIG(sc, SER_DRTS); - else if (!(tp->t_state & TS_TBLOCK) && + else */ if (/*!(tp->t_state & TS_TBLOCK) &&*/ !(sc->sc_hwsig & SER_RTS)) UART_SETSIG(sc, SER_DRTS|SER_RTS); +#endif + /* XXX: we should use inwakeup to implement this! */ + if (!(sc->sc_hwsig & SER_RTS)) + UART_SETSIG(sc, SER_DRTS|SER_RTS); } - if (tp->t_state & TS_TTSTOP) + if (sc->sc_txbusy) return; - if ((tp->t_state & TS_BUSY) || sc->sc_txbusy) - return; + sc->sc_txdatasz = ttydisc_getc(tp, sc->sc_txbuf, sc->sc_txfifosz); + if (sc->sc_txdatasz != 0) + UART_TRANSMIT(sc); +} - if (tp->t_outq.c_cc == 0) { - ttwwakeup(tp); - return; - } +static int +uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ + struct uart_softc *sc; + + sc = tty_softc(tp); - sc->sc_txdatasz = q_to_b(&tp->t_outq, sc->sc_txbuf, sc->sc_txfifosz); - tp->t_state |= TS_BUSY; - UART_TRANSMIT(sc); - ttwwakeup(tp); + switch (cmd) { + case TIOCSBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 1); + return (0); + case TIOCCBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 0); + return (0); + default: + return pps_ioctl(cmd, data, &sc->sc_pps); + } } static int @@ -205,7 +220,7 @@ uart_tty_param(struct tty *tp, struct termios *t) struct uart_softc *sc; int databits, parity, stopbits; - sc = tp->t_sc; + sc = tty_softc(tp); if (sc == NULL || sc->sc_leaving) return (ENODEV); if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0) @@ -237,16 +252,16 @@ uart_tty_param(struct tty *tp, struct termios *t) UART_SETSIG(sc, SER_DDTR | SER_DTR); /* Set input flow control state. */ if (!sc->sc_hwiflow) { - if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK)) + /* if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK)) UART_SETSIG(sc, SER_DRTS); - else + else */ UART_SETSIG(sc, SER_DRTS | SER_RTS); } else UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW)); /* Set output flow control state. */ if (sc->sc_hwoflow) UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW)); - ttsetwater(tp); + return (0); } @@ -255,48 +270,18 @@ uart_tty_modem(struct tty *tp, int biton, int bitoff) { struct uart_softc *sc; - sc = tp->t_sc; + sc = tty_softc(tp); if (biton != 0 || bitoff != 0) UART_SETSIG(sc, SER_DELTA(bitoff|biton) | biton); return (sc->sc_hwsig); } -static void -uart_tty_break(struct tty *tp, int state) -{ - struct uart_softc *sc; - - sc = tp->t_sc; - UART_IOCTL(sc, UART_IOCTL_BREAK, state); -} - -static void -uart_tty_stop(struct tty *tp, int rw) -{ - struct uart_softc *sc; - - sc = tp->t_sc; - if (sc == NULL || sc->sc_leaving) - return; - if (rw & FWRITE) { - if (sc->sc_txbusy) { - sc->sc_txbusy = 0; - UART_FLUSH(sc, UART_FLUSH_TRANSMITTER); - } - tp->t_state &= ~TS_BUSY; - } - if (rw & FREAD) { - UART_FLUSH(sc, UART_FLUSH_RECEIVER); - sc->sc_rxget = sc->sc_rxput = 0; - } -} - void uart_tty_intr(void *arg) { struct uart_softc *sc = arg; struct tty *tp; - int c, pend, sig, xc; + int c, err = 0, pend, sig, xc; if (sc->sc_leaving) return; @@ -306,78 +291,72 @@ uart_tty_intr(void *arg) return; tp = sc->sc_u.u_tty.tp; + tty_lock(tp); if (pend & SER_INT_RXREADY) { - while (!uart_rx_empty(sc) && !(tp->t_state & TS_TBLOCK)) { + while (!uart_rx_empty(sc) /* && !(tp->t_state & TS_TBLOCK)*/) { xc = uart_rx_get(sc); c = xc & 0xff; if (xc & UART_STAT_FRAMERR) - c |= TTY_FE; + err |= TRE_FRAMING; if (xc & UART_STAT_OVERRUN) - c |= TTY_OE; + err |= TRE_OVERRUN; if (xc & UART_STAT_PARERR) - c |= TTY_PE; - ttyld_rint(tp, c); + err |= TRE_PARITY; + ttydisc_rint(tp, c, err); } } - if (pend & SER_INT_BREAK) { - if (tp != NULL && !(tp->t_iflag & IGNBRK)) - ttyld_rint(tp, 0); - } + if (pend & SER_INT_BREAK) + ttydisc_rint(tp, 0, TRE_BREAK); if (pend & SER_INT_SIGCHG) { sig = pend & SER_INT_SIGMASK; if (sig & SER_DDCD) - ttyld_modem(tp, sig & SER_DCD); - if ((sig & SER_DCTS) && (tp->t_cflag & CCTS_OFLOW) && + ttydisc_modem(tp, sig & SER_DCD); + if ((sig & SER_DCTS) && (tp->t_termios.c_cflag & CCTS_OFLOW) && !sc->sc_hwoflow) { - if (sig & SER_CTS) { - tp->t_state &= ~TS_TTSTOP; - ttyld_start(tp); - } else - tp->t_state |= TS_TTSTOP; + if (sig & SER_CTS) + uart_tty_outwakeup(tp); } } - if (pend & SER_INT_TXIDLE) { - tp->t_state &= ~TS_BUSY; - ttyld_start(tp); - } + if (pend & SER_INT_TXIDLE) + uart_tty_outwakeup(tp); + ttydisc_rint_done(tp); + tty_unlock(tp); } +static struct ttydevsw uart_tty_class = { + .tsw_flags = TF_INITLOCK|TF_CALLOUT, + .tsw_open = uart_tty_open, + .tsw_close = uart_tty_close, + .tsw_outwakeup = uart_tty_outwakeup, + .tsw_ioctl = uart_tty_ioctl, + .tsw_param = uart_tty_param, + .tsw_modem = uart_tty_modem, +}; + int uart_tty_attach(struct uart_softc *sc) { struct tty *tp; int unit; - tp = ttyalloc(); - sc->sc_u.u_tty.tp = tp; - tp->t_sc = sc; + sc->sc_u.u_tty.tp = tp = tty_alloc(&uart_tty_class, sc, NULL); unit = device_get_unit(sc->sc_dev); - tp->t_oproc = uart_tty_oproc; - tp->t_param = uart_tty_param; - tp->t_stop = uart_tty_stop; - tp->t_modem = uart_tty_modem; - tp->t_break = uart_tty_break; - tp->t_open = uart_tty_open; - tp->t_close = uart_tty_close; - - tp->t_pps = &sc->sc_pps; - if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name, "ttyu%r", unit); - ttyconsolemode(tp, 0); + tty_init_console(tp, 0); } swi_add(&tty_intr_event, uart_driver_name, uart_tty_intr, sc, SWI_TTY, INTR_TYPE_TTY, &sc->sc_softih); - ttycreate(tp, TS_CALLOUT, "u%r", unit); + tty_makedev(tp, NULL, "u%r", unit); return (0); } @@ -387,10 +366,10 @@ int uart_tty_detach(struct uart_softc *sc) struct tty *tp; tp = sc->sc_u.u_tty.tp; - tp->t_pps = NULL; - ttygone(tp); + + tty_lock(tp); swi_remove(sc->sc_softih); - ttyfree(tp); + tty_rel_gone(tp); return (0); } diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c index f99a73a..29c11ec 100644 --- a/sys/dev/usb/ucom.c +++ b/sys/dev/usb/ucom.c @@ -116,22 +116,33 @@ SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, static int ucom_modevent(module_t, int, void *); static void ucom_cleanup(struct ucom_softc *); -static int ucomparam(struct tty *, struct termios *); -static void ucomstart(struct tty *); -static void ucomstop(struct tty *, int); static void ucom_shutdown(struct ucom_softc *); static void ucom_dtr(struct ucom_softc *, int); static void ucom_rts(struct ucom_softc *, int); -static void ucombreak(struct tty *, int); +static void ucombreak(struct ucom_softc *, int); static usbd_status ucomstartread(struct ucom_softc *); static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ucomstopread(struct ucom_softc *); -static t_open_t ucomopen; -static t_close_t ucomclose; -static t_modem_t ucommodem; -static t_ioctl_t ucomioctl; +static tsw_open_t ucomtty_open; +static tsw_close_t ucomtty_close; +static tsw_outwakeup_t ucomtty_outwakeup; +static tsw_ioctl_t ucomtty_ioctl; +static tsw_param_t ucomtty_param; +static tsw_modem_t ucomtty_modem; +static tsw_free_t ucomtty_free; + +static struct ttydevsw ucomtty_class = { + .tsw_flags = TF_INITLOCK|TF_CALLOUT, + .tsw_open = ucomtty_open, + .tsw_close = ucomtty_close, + .tsw_outwakeup = ucomtty_outwakeup, + .tsw_ioctl = ucomtty_ioctl, + .tsw_param = ucomtty_param, + .tsw_modem = ucomtty_modem, + .tsw_free = ucomtty_free, +}; devclass_t ucom_devclass; @@ -160,31 +171,20 @@ ucom_modevent(module_t mod, int type, void *data) return (0); } -int -ucom_attach_tty(struct ucom_softc *sc, int flags, char* fmt, int unit) +void +ucom_attach_tty(struct ucom_softc *sc, char* fmt, int unit) { struct tty *tp; - sc->sc_tty = tp = ttyalloc(); - tp->t_sc = sc; - tp->t_oproc = ucomstart; - tp->t_param = ucomparam; - tp->t_stop = ucomstop; - tp->t_break = ucombreak; - tp->t_open = ucomopen; - tp->t_close = ucomclose; - tp->t_modem = ucommodem; - tp->t_ioctl = ucomioctl; - - return ttycreate(tp, flags, fmt, unit); + sc->sc_tty = tp = tty_alloc(&ucomtty_class, sc, &Giant); + tty_makedev(tp, NULL, fmt, unit); } int ucom_attach(struct ucom_softc *sc) { - ucom_attach_tty(sc, TS_CALLOUT, - "U%d", device_get_unit(sc->sc_dev)); + ucom_attach_tty(sc, "U%d", device_get_unit(sc->sc_dev)); DPRINTF(("ucom_attach: ttycreate: tp = %p, %s\n", sc->sc_tty, sc->sc_tty->t_dev->si_name)); @@ -195,24 +195,17 @@ ucom_attach(struct ucom_softc *sc) int ucom_detach(struct ucom_softc *sc) { - int s; - DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty)); + tty_lock(sc->sc_tty); sc->sc_dying = 1; - ttygone(sc->sc_tty); - if (sc->sc_tty->t_state & TS_ISOPEN) - ucomclose(sc->sc_tty); if (sc->sc_bulkin_pipe != NULL) usbd_abort_pipe(sc->sc_bulkin_pipe); if (sc->sc_bulkout_pipe != NULL) usbd_abort_pipe(sc->sc_bulkout_pipe); - ttyfree(sc->sc_tty); - - s = splusb(); - splx(s); + tty_rel_gone(sc->sc_tty); return (0); } @@ -227,30 +220,30 @@ ucom_shutdown(struct ucom_softc *sc) * Hang up if necessary. Wait a bit, so the other side has time to * notice even if we immediately open the port again. */ - if (ISSET(tp->t_cflag, HUPCL)) { - (void)ucommodem(tp, 0, SER_DTR); + if (tp->t_termios.c_cflag & HUPCL) { + (void)ucomtty_modem(tp, 0, SER_DTR); +#if 0 (void)tsleep(sc, TTIPRI, "ucomsd", hz); +#endif } } static int -ucomopen(struct tty *tp, struct cdev *dev) +ucomtty_open(struct tty *tp) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); usbd_status err; int error; - sc = tp->t_sc; - if (sc->sc_dying) return (ENXIO); - DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp)); + DPRINTF(("%s: ucomtty_open: tp = %p\n", device_get_nameunit(sc->sc_dev), tp)); sc->sc_poll = 0; sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0; - (void)ucommodem(tp, SER_DTR | SER_RTS, 0); + (void)ucomtty_modem(tp, SER_DTR | SER_RTS, 0); /* Device specific open */ if (sc->sc_callback->ucom_open != NULL) { @@ -262,7 +255,7 @@ ucomopen(struct tty *tp, struct cdev *dev) } } - DPRINTF(("ucomopen: open pipes in = %d out = %d\n", + DPRINTF(("ucomtty_open: open pipes in = %d out = %d\n", sc->sc_bulkin_no, sc->sc_bulkout_no)); /* Open the bulk pipes */ @@ -328,13 +321,11 @@ fail: } static void -ucomclose(struct tty *tp) +ucomtty_close(struct tty *tp) { - struct ucom_softc *sc; - - sc = tp->t_sc; + struct ucom_softc *sc = tty_softc(tp); - DPRINTF(("%s: ucomclose \n", device_get_nameunit(sc->sc_dev))); + DPRINTF(("%s: ucomtty_close \n", device_get_nameunit(sc->sc_dev))); ucom_cleanup(sc); @@ -343,35 +334,41 @@ ucomclose(struct tty *tp) } static int -ucomioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *p) +ucomtty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *p) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int error; - sc = tp->t_sc;; if (sc->sc_dying) return (EIO); DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd)); - error = ENOTTY; + switch (cmd) { + case TIOCSBRK: + ucombreak(sc, 1); + return (0); + case TIOCCBRK: + ucombreak(sc, 0); + return (0); + } + + error = ENOIOCTL; if (sc->sc_callback->ucom_ioctl != NULL) error = sc->sc_callback->ucom_ioctl(sc->sc_parent, sc->sc_portno, - cmd, data, flag, p); + cmd, data, p); return (error); } static int -ucommodem(struct tty *tp, int sigon, int sigoff) +ucomtty_modem(struct tty *tp, int sigon, int sigoff) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int mcr; int msr; int onoff; - sc = tp->t_sc; - if (sigon == 0 && sigoff == 0) { mcr = sc->sc_mcr; if (ISSET(mcr, SER_DTR)) @@ -412,12 +409,10 @@ ucommodem(struct tty *tp, int sigon, int sigoff) } static void -ucombreak(struct tty *tp, int onoff) +ucombreak(struct ucom_softc *sc, int onoff) { - struct ucom_softc *sc; - - sc = tp->t_sc; DPRINTF(("ucombreak: onoff = %d\n", onoff)); + if (sc->sc_callback->ucom_set == NULL) return; sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, @@ -467,47 +462,32 @@ ucom_status_change(struct ucom_softc *sc) return; onoff = ISSET(sc->sc_msr, SER_DCD) ? 1 : 0; DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff)); - ttyld_modem(tp, onoff); + ttydisc_modem(tp, onoff); } } static int -ucomparam(struct tty *tp, struct termios *t) +ucomtty_param(struct tty *tp, struct termios *t) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int error; usbd_status uerr; - sc = tp->t_sc; - if (sc->sc_dying) return (EIO); - DPRINTF(("ucomparam: sc = %p\n", sc)); + DPRINTF(("ucomtty_param: sc = %p\n", sc)); /* Check requested parameters. */ if (t->c_ospeed < 0) { - DPRINTF(("ucomparam: negative ospeed\n")); + DPRINTF(("ucomtty_param: negative ospeed\n")); return (EINVAL); } if (t->c_ispeed && t->c_ispeed != t->c_ospeed) { - DPRINTF(("ucomparam: mismatch ispeed and ospeed\n")); + DPRINTF(("ucomtty_param: mismatch ispeed and ospeed\n")); return (EINVAL); } - - /* - * If there were no changes, don't do anything. This avoids dropping - * input and improves performance when all we did was frob things like - * VMIN and VTIME. - */ - if (tp->t_ospeed == t->c_ospeed && - tp->t_cflag == t->c_cflag) - return (0); - - /* And copy to tty. */ - tp->t_ispeed = 0; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; + t->c_ispeed = t->c_ospeed; if (sc->sc_callback->ucom_param == NULL) return (0); @@ -516,20 +496,24 @@ ucomparam(struct tty *tp, struct termios *t) error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t); if (error) { - DPRINTF(("ucomparam: callback: error = %d\n", error)); + DPRINTF(("ucomtty_param: callback: error = %d\n", error)); return (error); } +#if 0 ttsetwater(tp); +#endif if (t->c_cflag & CRTS_IFLOW) { sc->sc_state |= UCS_RTS_IFLOW; } else if (sc->sc_state & UCS_RTS_IFLOW) { sc->sc_state &= ~UCS_RTS_IFLOW; - (void)ucommodem(tp, SER_RTS, 0); + (void)ucomtty_modem(tp, SER_RTS, 0); } +#if 0 ttyldoptim(tp); +#endif uerr = ucomstartread(sc); if (uerr != USBD_NORMAL_COMPLETION) @@ -539,17 +523,23 @@ ucomparam(struct tty *tp, struct termios *t) } static void -ucomstart(struct tty *tp) +ucomtty_free(void *sc) +{ + /* + * Our softc gets deallocated earlier on. + * XXX: we should make sure the TTY device name doesn't get + * recycled before we end up here! + */ +} + +static void +ucomtty_outwakeup(struct tty *tp) { - struct ucom_softc *sc; - struct cblock *cbp; + struct ucom_softc *sc = tty_softc(tp); usbd_status err; - int s; - u_char *data; - int cnt; + size_t cnt; - sc = tp->t_sc; - DPRINTF(("ucomstart: sc = %p\n", sc)); + DPRINTF(("ucomtty_outwakeup: sc = %p\n", sc)); if (sc->sc_dying) return; @@ -564,90 +554,59 @@ ucomstart(struct tty *tp) if (sc->sc_oxfer == NULL) return; - s = spltty(); - + /* XXX: hardware flow control. We should use inwakeup here. */ +#if 0 if (tp->t_state & TS_TBLOCK) { if (ISSET(sc->sc_mcr, SER_RTS) && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { - DPRINTF(("ucomstart: clear RTS\n")); - (void)ucommodem(tp, 0, SER_RTS); + DPRINTF(("ucomtty_outwakeup: clear RTS\n")); + (void)ucomtty_modem(tp, 0, SER_RTS); } } else { if (!ISSET(sc->sc_mcr, SER_RTS) && tp->t_rawq.c_cc <= tp->t_ilowat && ISSET(sc->sc_state, UCS_RTS_IFLOW)) { - DPRINTF(("ucomstart: set RTS\n")); - (void)ucommodem(tp, SER_RTS, 0); + DPRINTF(("ucomtty_outwakeup: set RTS\n")); + (void)ucomtty_modem(tp, SER_RTS, 0); } } +#endif - if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - DPRINTF(("ucomstart: stopped\n")); - goto out; - } - - if (tp->t_outq.c_cc <= tp->t_olowat) { - if (ISSET(tp->t_state, TS_SO_OLOWAT)) { - CLR(tp->t_state, TS_SO_OLOWAT); - wakeup(TSA_OLOWAT(tp)); - } - selwakeuppri(&tp->t_wsel, TTIPRI); - if (tp->t_outq.c_cc == 0) { - if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == - TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { - CLR(tp->t_state, TS_SO_OCOMPLETE); - wakeup(TSA_OCOMPLETE(tp)); - } - goto out; - } - } + if (sc->sc_state & UCS_TXBUSY) + return; - /* Grab the first contiguous region of buffer space. */ - data = tp->t_outq.c_cf; - cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND); - cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc); + sc->sc_state |= UCS_TXBUSY; + if (sc->sc_callback->ucom_write != NULL) + cnt = sc->sc_callback->ucom_write(sc->sc_parent, + sc->sc_portno, tp, sc->sc_obuf, sc->sc_obufsize); + else + cnt = ttydisc_getc(tp, sc->sc_obuf, sc->sc_obufsize); if (cnt == 0) { - DPRINTF(("ucomstart: cnt == 0\n")); - goto out; - } - - SET(tp->t_state, TS_BUSY); - - if (cnt > sc->sc_obufsize) { - DPRINTF(("ucomstart: big buffer %d chars\n", cnt)); - cnt = sc->sc_obufsize; + DPRINTF(("ucomtty_outwakeup: cnt == 0\n")); + sc->sc_state &= ~UCS_TXBUSY; + return; } - if (sc->sc_callback->ucom_write != NULL) - sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno, - sc->sc_obuf, data, &cnt); - else - memcpy(sc->sc_obuf, data, cnt); - DPRINTF(("ucomstart: %d chars\n", cnt)); + DPRINTF(("ucomtty_outwakeup: %zu chars\n", cnt)); usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, (usbd_private_handle)sc, sc->sc_obuf, cnt, USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb); /* What can we do on error? */ err = usbd_transfer(sc->sc_oxfer); - if (err != USBD_IN_PROGRESS) - printf("ucomstart: err=%s\n", usbd_errstr(err)); - - ttwwakeup(tp); - - out: - splx(s); + if (err != USBD_IN_PROGRESS) { + printf("ucomtty_outwakeup: err=%s\n", usbd_errstr(err)); + sc->sc_state &= ~UCS_TXBUSY; + } } +#if 0 static void ucomstop(struct tty *tp, int flag) { - struct ucom_softc *sc; + struct ucom_softc *sc = tty_softc(tp); int s; - sc = tp->t_sc; - DPRINTF(("ucomstop: %d\n", flag)); if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) { @@ -658,19 +617,18 @@ ucomstop(struct tty *tp, int flag) if (flag & FWRITE) { DPRINTF(("ucomstop: write\n")); - s = spltty(); if (ISSET(tp->t_state, TS_BUSY)) { /* XXX do what? */ if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } - splx(s); } - ucomstart(tp); + ucomtty_outwakeup(tp); DPRINTF(("ucomstop: done\n")); } +#endif static void ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) @@ -678,12 +636,11 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) struct ucom_softc *sc = (struct ucom_softc *)p; struct tty *tp = sc->sc_tty; u_int32_t cc; - int s; DPRINTF(("ucomwritecb: status = %d\n", status)); if (status == USBD_CANCELLED || sc->sc_dying) - goto error; + return; if (status != USBD_NORMAL_COMPLETION) { printf("%s: ucomwritecb: %s\n", @@ -691,7 +648,7 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ - goto error; + return; } usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); @@ -699,28 +656,21 @@ ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) if (cc <= sc->sc_opkthdrlen) { printf("%s: sent size too small, cc = %d\n", device_get_nameunit(sc->sc_dev), cc); - goto error; + return; } /* convert from USB bytes to tty bytes */ cc -= sc->sc_opkthdrlen; - s = spltty(); + sc->sc_state &= ~UCS_TXBUSY; +#if 0 CLR(tp->t_state, TS_BUSY); if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, cc); - ttyld_start(tp); - splx(s); - - return; - - error: - s = spltty(); - CLR(tp->t_state, TS_BUSY); - splx(s); - return; +#endif + ucomtty_outwakeup(tp); } static usbd_status @@ -758,8 +708,6 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) usbd_status err; u_int32_t cc; u_char *cp; - int lostcc; - int s; DPRINTF(("ucomreadcb: status = %d\n", status)); @@ -791,42 +739,20 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) } if (cc < 1) goto resubmit; - - s = spltty(); - if (tp->t_state & TS_CAN_BYPASS_L_RINT) { - if (tp->t_rawq.c_cc + cc > tp->t_ihiwat - && (sc->sc_state & UCS_RTS_IFLOW - || tp->t_iflag & IXOFF) - && !(tp->t_state & TS_TBLOCK)) - ttyblock(tp); - lostcc = b_to_q((char *)cp, cc, &tp->t_rawq); - tp->t_rawcc += cc; - ttwakeup(tp); - if (tp->t_state & TS_TTSTOP - && (tp->t_iflag & IXANY - || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { - tp->t_state &= ~TS_TTSTOP; - tp->t_lflag &= ~FLUSHO; - ucomstart(tp); - } - if (lostcc > 0) - printf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev), - lostcc); - } else { - /* Give characters to tty layer. */ - while (cc > 0) { - DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp)); - if (ttyld_rint(tp, *cp) == -1) { - /* XXX what should we do? */ - printf("%s: lost %d chars\n", - device_get_nameunit(sc->sc_dev), cc); - break; - } - cc--; - cp++; + + /* Give characters to tty layer. */ + while (cc > 0) { + DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp)); + if (ttydisc_rint(tp, *cp, 0) == -1) { + /* XXX what should we do? */ + printf("%s: lost %d chars\n", + device_get_nameunit(sc->sc_dev), cc); + break; } + cc--; + cp++; } - splx(s); + ttydisc_rint_done(tp); resubmit: err = ucomstartread(sc); @@ -835,9 +761,11 @@ ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) /* XXX what should we dow now? */ } +#if 0 if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS) && !(tp->t_state & TS_TBLOCK)) - ucommodem(tp, SER_RTS, 0); + ucomtty_modem(tp, SER_RTS, 0); +#endif } static void diff --git a/sys/dev/usb/ucomvar.h b/sys/dev/usb/ucomvar.h index 4a5ddd2..7a35a0a 100644 --- a/sys/dev/usb/ucomvar.h +++ b/sys/dev/usb/ucomvar.h @@ -88,6 +88,7 @@ #define UCOM_UNK_PORTNO -1 /* XXX */ +struct tty; struct ucom_softc; struct ucom_callback { @@ -97,11 +98,11 @@ struct ucom_callback { #define UCOM_SET_RTS 2 #define UCOM_SET_BREAK 3 int (*ucom_param)(void *, int, struct termios *); - int (*ucom_ioctl)(void *, int, u_long, caddr_t, int, struct thread *); + int (*ucom_ioctl)(void *, int, u_long, caddr_t, struct thread *); int (*ucom_open)(void *, int); void (*ucom_close)(void *, int); void (*ucom_read)(void *, int, u_char **, u_int32_t *); - void (*ucom_write)(void *, int, u_char *, u_char *, u_int32_t *); + size_t (*ucom_write)(void *, int, struct tty *, u_char *, u_int32_t); }; /* line status register */ @@ -117,6 +118,7 @@ struct ucom_callback { /* ucom state declarations */ #define UCS_RXSTOP 0x0001 /* Rx stopped */ +#define UCS_TXBUSY 0x0002 /* Tx busy */ #define UCS_RTS_IFLOW 0x0008 /* use RTS input flow control */ struct ucom_softc { @@ -159,7 +161,7 @@ struct ucom_softc { extern devclass_t ucom_devclass; -int ucom_attach_tty(struct ucom_softc *, int, char*, int); +void ucom_attach_tty(struct ucom_softc *, char*, int); int ucom_attach(struct ucom_softc *); int ucom_detach(struct ucom_softc *); void ucom_status_change(struct ucom_softc *); diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c index 22036e4..5a9921b 100644 --- a/sys/dev/usb/uftdi.c +++ b/sys/dev/usb/uftdi.c @@ -118,8 +118,8 @@ static void uftdi_set(void *, int, int, int); static int uftdi_param(void *, int, struct termios *); static int uftdi_open(void *sc, int portno); static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count); -static void uftdi_write(void *sc, int portno, u_char *to, u_char *from, - u_int32_t *count); +static size_t uftdi_write(void *sc, int portno, struct tty *, + u_char *to, u_int32_t count); static void uftdi_break(void *sc, int portno, int onoff); static int uftdi_8u232am_getrate(speed_t speed, int *rate); @@ -486,20 +486,25 @@ uftdi_read(void *vsc, int portno, u_char **ptr, u_int32_t *count) *count -= 2; } -static void -uftdi_write(void *vsc, int portno, u_char *to, u_char *from, u_int32_t *count) +static size_t +uftdi_write(void *vsc, int portno, struct tty *tp, u_char *to, u_int32_t count) { struct uftdi_softc *sc = vsc; + size_t l; - DPRINTFN(10,("uftdi_write: sc=%p, port=%d count=%u data[0]=0x%02x\n", - vsc, portno, *count, from[0])); + DPRINTFN(10,("uftdi_write: sc=%p, port=%d tp=%p, count=%u\n", + vsc, portno, tp, count)); - /* Make length tag and copy data */ - if (sc->sc_hdrlen > 0) - *to = FTDI_OUT_TAG(*count, portno); + /* Leave space for the length tag. */ + l = ttydisc_getc(tp, to + sc->sc_hdrlen, count - sc->sc_hdrlen); + if (l == 0) + return (0); - memcpy(to + sc->sc_hdrlen, from, *count); - *count += sc->sc_hdrlen; + /* Make length tag. */ + if (sc->sc_hdrlen > 0) + *to = FTDI_OUT_TAG(l, portno); + + return (l + sc->sc_hdrlen); } static void diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c index 6424496..89b3e05 100644 --- a/sys/dev/usb/umodem.c +++ b/sys/dev/usb/umodem.c @@ -187,7 +187,7 @@ static void umodem_rts(struct umodem_softc *, int); static void umodem_break(struct umodem_softc *, int); static void umodem_set_line_state(struct umodem_softc *); static int umodem_param(void *, int, struct termios *); -static int umodem_ioctl(void *, int, u_long, caddr_t, int, struct thread *); +static int umodem_ioctl(void *, int, u_long, caddr_t, struct thread *); static int umodem_open(void *, int portno); static void umodem_close(void *, int portno); static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); @@ -611,7 +611,7 @@ umodem_param(void *addr, int portno, struct termios *t) } int -umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, +umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, struct thread *p) { struct umodem_softc *sc = addr; diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 7218119..63ab7d4 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -597,8 +597,6 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc return (0); } - mtx_lock(&Giant); /* XXX TTY */ - vpold = td->td_proc->p_session->s_ttyvp; VREF(vp); SESS_LOCK(td->td_proc->p_session); @@ -610,7 +608,6 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc /* Get rid of reference to old control tty */ if (vpold) vrele(vpold); - mtx_unlock(&Giant); /* XXX TTY */ } return (error); } diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 587fe3e..c70d1a0 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -272,7 +272,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/i386/conf/XBOX b/sys/i386/conf/XBOX index 680c7c4..eec4554 100644 --- a/sys/i386/conf/XBOX +++ b/sys/i386/conf/XBOX @@ -71,7 +71,7 @@ device io # I/O device device random # Entropy device device ether # Ethernet support #device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys #device md # Memory "disks" #device gif # IPv6 and IPv4 tunneling #device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/i386/ibcs2/ibcs2_ioctl.c b/sys/i386/ibcs2/ibcs2_ioctl.c index bbaab39..0275c98 100644 --- a/sys/i386/ibcs2/ibcs2_ioctl.c +++ b/sys/i386/ibcs2/ibcs2_ioctl.c @@ -88,6 +88,11 @@ ibcs2_stty(struct thread *td, struct ibcs2_stty_args *args) * iBCS2 ioctl calls. */ +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static struct speedtab sptab[] = { { 0, 0 }, { 50, 1 }, @@ -128,6 +133,16 @@ static u_long s2btab[] = { 38400, }; +static int +ttspeedtab(int speed, struct speedtab *table) +{ + + for ( ; table->sp_speed != -1; table++) + if (table->sp_speed == speed) + return (table->sp_code); + return (-1); +} + static void stios2btios(st, bt) struct ibcs2_termios *st; @@ -154,7 +169,7 @@ stios2btios(st, bt) l = st->c_oflag; r = 0; if (l & IBCS2_OPOST) r |= OPOST; if (l & IBCS2_ONLCR) r |= ONLCR; - if (l & IBCS2_TAB3) r |= OXTABS; + if (l & IBCS2_TAB3) r |= TAB3; bt->c_oflag = r; l = st->c_cflag; r = 0; @@ -248,7 +263,7 @@ btios2stios(bt, st) l = bt->c_oflag; r = 0; if (l & OPOST) r |= IBCS2_OPOST; if (l & ONLCR) r |= IBCS2_ONLCR; - if (l & OXTABS) r |= IBCS2_TAB3; + if (l & TAB3) r |= IBCS2_TAB3; st->c_oflag = r; l = bt->c_cflag; r = 0; diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index ae94a22..793190d 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -151,7 +151,7 @@ device faith # IPv6-to-IPv4 relaying (translation) device gif # IPv6 and IPv4 tunneling device loop # Network loopback device md # Memory "disks" -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device puc # Multi I/O cards and multi-channel UARTs device random # Entropy device device tun # Packet tunnel. diff --git a/sys/ia64/conf/SKI b/sys/ia64/conf/SKI index 377ee93..001c710 100644 --- a/sys/ia64/conf/SKI +++ b/sys/ia64/conf/SKI @@ -53,7 +53,7 @@ device pci # PCI bus support device ether # Ethernet support device loop # Network loopback device md # Memory "disks" -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device random # Entropy device device tun # Packet tunnel. diff --git a/sys/ia64/ia64/ssc.c b/sys/ia64/ia64/ssc.c index 274cc0d..04144c5 100644 --- a/sys/ia64/ia64/ssc.c +++ b/sys/ia64/ia64/ssc.c @@ -54,26 +54,22 @@ #define SSC_POLL_HZ 50 -static d_open_t ssc_open; -static d_close_t ssc_close; - -static struct cdevsw ssc_cdevsw = { - .d_version = D_VERSION, - .d_open = ssc_open, - .d_close = ssc_close, - .d_name = "ssc", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t ssc_open; +static tsw_outwakeup_t ssc_outwakeup; +static tsw_close_t ssc_close; + +static struct ttydevsw ssc_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_open = ssc_open, + .tsw_outwakeup = ssc_outwakeup, + .tsw_close = ssc_close, }; -static struct tty *ssc_tp = NULL; static int polltime; static struct callout_handle ssc_timeouthandle = CALLOUT_HANDLE_INITIALIZER(&ssc_timeouthandle); -static void ssc_start(struct tty *); static void ssc_timeout(void *); -static int ssc_param(struct tty *, struct termios *); -static void ssc_stop(struct tty *, int); static u_int64_t ssc(u_int64_t in0, u_int64_t in1, u_int64_t in2, u_int64_t in3, int which) @@ -90,7 +86,8 @@ ssc(u_int64_t in0, u_int64_t in1, u_int64_t in2, u_int64_t in3, int which) static void ssc_cnprobe(struct consdev *cp) { - sprintf(cp->cn_name, "ssccons"); + + strcpy(cp->cn_name, "ssccons"); cp->cn_pri = CN_INTERNAL; } @@ -107,8 +104,10 @@ ssc_cnterm(struct consdev *cp) static void ssc_cnattach(void *arg) { - make_dev(&ssc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ssccons"); - ssc_tp = ttyalloc(); + struct tty *tp; + + tp = tty_alloc(&ssc_class, NULL, NULL); + tty_makedev(tp, NULL, "ssccons"); } SYSINIT(ssc_cnattach, SI_SUB_DRIVERS, SI_ORDER_ANY, ssc_cnattach, 0); @@ -130,100 +129,39 @@ ssc_cngetc(struct consdev *cp) } static int -ssc_open(struct cdev *dev, int flag, int mode, struct thread *td) +ssc_open(struct tty *tp) { - struct tty *tp; - int s; - int error = 0, setuptimeout = 0; - - tp = dev->si_tty = ssc_tp; - - s = spltty(); - tp->t_oproc = ssc_start; - tp->t_param = ssc_param; - tp->t_stop = ssc_stop; - tp->t_dev = dev; - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - splx(s); - return EBUSY; - } - - splx(s); - error = ttyld_open(tp, dev); + polltime = hz / SSC_POLL_HZ; + if (polltime < 1) + polltime = 1; + ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); - if (error == 0 && setuptimeout) { - polltime = hz / SSC_POLL_HZ; - if (polltime < 1) - polltime = 1; - ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); - } - return error; + return (0); } -static int -ssc_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +ssc_close(struct tty *tp) { - int unit = minor(dev); - struct tty *tp = ssc_tp; - - if (unit != 0) - return ENXIO; untimeout(ssc_timeout, tp, ssc_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - return 0; -} - -static int -ssc_param(struct tty *tp, struct termios *t) -{ - - return 0; } static void -ssc_start(struct tty *tp) +ssc_outwakeup(struct tty *tp) { - int s; + char buf[128]; + size_t len, c; - s = spltty(); + for (;;) { + len = ttydisc_getc(tp, buf, sizeof buf); + if (len == 0) + break; - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - splx(s); - return; + c = 0; + while (len-- > 0) + ssc_cnputc(NULL, buf[c++]); } - - tp->t_state |= TS_BUSY; - while (tp->t_outq.c_cc != 0) - ssc_cnputc(NULL, getc(&tp->t_outq)); - tp->t_state &= ~TS_BUSY; - - ttwwakeup(tp); - splx(s); -} - -/* - * Stop output on a line. - */ -static void -ssc_stop(struct tty *tp, int flag) -{ - int s; - - s = spltty(); - if (tp->t_state & TS_BUSY) - if ((tp->t_state & TS_TTSTOP) == 0) - tp->t_state |= TS_FLUSH; - splx(s); } static void @@ -232,10 +170,12 @@ ssc_timeout(void *v) struct tty *tp = v; int c; - while ((c = ssc_cngetc(NULL)) != -1) { - if (tp->t_state & TS_ISOPEN) - ttyld_rint(tp, c); - } + tty_lock(tp); + while ((c = ssc_cngetc(NULL)) != -1) + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); + ssc_timeouthandle = timeout(ssc_timeout, tp, polltime); } diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index f726d27..3a51967 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -415,7 +415,7 @@ proc0_init(void *dummy __unused) pgrp0.pg_session = &session0; mtx_init(&session0.s_mtx, "session", NULL, MTX_DEF); - session0.s_count = 1; + refcount_init(&session0.s_count, 1); session0.s_leader = p; p->p_sysent = &null_sysvec; diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 22db8a8..1dcfbab 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #include "opt_compat.h" @@ -532,4 +532,5 @@ struct sysent sysent[] = { { AS(renameat_args), (sy_call_t *)renameat, AUE_RENAMEAT, NULL, 0, 0 }, /* 501 = renameat */ { AS(symlinkat_args), (sy_call_t *)symlinkat, AUE_SYMLINKAT, NULL, 0, 0 }, /* 502 = symlinkat */ { AS(unlinkat_args), (sy_call_t *)unlinkat, AUE_UNLINKAT, NULL, 0, 0 }, /* 503 = unlinkat */ + { AS(posix_openpt_args), (sy_call_t *)posix_openpt, AUE_POSIXOPENPT, NULL, 0, 0 }, /* 504 = posix_openpt */ }; diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c index 76d4c75..e505f62 100644 --- a/sys/kern/kern_acct.c +++ b/sys/kern/kern_acct.c @@ -402,12 +402,12 @@ acct_process(struct thread *td) acct.ac_gid = p->p_ucred->cr_rgid; /* (7) The terminal from which the process was started */ - SESS_LOCK(p->p_session); + sx_slock(&proctree_lock); if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) - acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev); + acct.ac_tty = tty_udev(p->p_pgrp->pg_session->s_ttyp); else acct.ac_tty = NODEV; - SESS_UNLOCK(p->p_session); + sx_sunlock(&proctree_lock); /* (8) The boolean flags that tell how the process terminated, etc. */ acct.ac_flagx = p->p_acflag; diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index 4c10871..409652f 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$"); #include <sys/poll.h> #include <sys/sx.h> #include <sys/ctype.h> -#include <sys/tty.h> #include <sys/ucred.h> #include <sys/taskqueue.h> #include <machine/stdarg.h> @@ -611,14 +610,6 @@ prep_cdevsw(struct cdevsw *devsw) devsw->d_kqfilter = dead_kqfilter; } - if (devsw->d_flags & D_TTY) { - if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl; - if (devsw->d_read == NULL) devsw->d_read = ttyread; - if (devsw->d_write == NULL) devsw->d_write = ttywrite; - if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; - if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; - } - if (devsw->d_flags & D_NEEDGIANT) { if (devsw->d_gianttrick == NULL) { memcpy(dsw2, devsw, sizeof *dsw2); @@ -692,11 +683,9 @@ make_dev_credv(int flags, struct cdevsw *devsw, int minornr, } dev->si_flags |= SI_NAMED; -#ifdef MAC if (cr != NULL) dev->si_cred = crhold(cr); else -#endif dev->si_cred = NULL; dev->si_uid = uid; dev->si_gid = gid; diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index b7a486b..3c0858c 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <sys/syscallsubr.h> #include <sys/sysctl.h> #include <sys/sysproto.h> +#include <sys/tty.h> #include <sys/unistd.h> #include <sys/user.h> #include <sys/vnode.h> @@ -2564,6 +2565,7 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) struct vnode *vp; struct file *fp; struct proc *p; + struct tty *tp; int vfslocked; name = (int *)arg1; @@ -2595,6 +2597,7 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_structsize = sizeof(*kif); vp = NULL; so = NULL; + tp = NULL; kif->kf_fd = i; switch (fp->f_type) { case DTYPE_VNODE: @@ -2637,6 +2640,11 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_type = KF_TYPE_SEM; break; + case DTYPE_PTS: + kif->kf_type = KF_TYPE_PTS; + tp = fp->f_data; + break; + default: kif->kf_type = KF_TYPE_UNKNOWN; break; @@ -2730,6 +2738,10 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif->kf_sock_type = so->so_type; kif->kf_sock_protocol = so->so_proto->pr_protocol; } + if (tp != NULL) { + strlcpy(kif->kf_path, tty_devname(tp), + sizeof(kif->kf_path)); + } error = SYSCTL_OUT(req, kif, sizeof(*kif)); if (error) break; diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 9e53316..f0866ed 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -119,9 +119,8 @@ void exit1(struct thread *td, int rv) { struct proc *p, *nq, *q; - struct tty *tp; - struct vnode *ttyvp; struct vnode *vtmp; + struct vnode *ttyvp = NULL; #ifdef KTRACE struct vnode *tracevp; struct ucred *tracecred; @@ -298,56 +297,48 @@ exit1(struct thread *td, int rv) vmspace_exit(td); - mtx_lock(&Giant); /* XXX TTY */ sx_xlock(&proctree_lock); if (SESS_LEADER(p)) { struct session *sp; sp = p->p_session; - if (sp->s_ttyvp) { + + SESS_LOCK(sp); + ttyvp = sp->s_ttyvp; + sp->s_ttyvp = NULL; + SESS_UNLOCK(sp); + + if (ttyvp != NULL) { /* * Controlling process. - * Signal foreground pgrp, - * drain controlling terminal - * and revoke access to controlling terminal. + * Signal foreground pgrp and revoke access to + * controlling terminal. + * + * There is no need to drain the terminal here, + * because this will be done on revocation. */ - if (sp->s_ttyp && (sp->s_ttyp->t_session == sp)) { - tp = sp->s_ttyp; - if (sp->s_ttyp->t_pgrp) { - PGRP_LOCK(sp->s_ttyp->t_pgrp); - pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1); - PGRP_UNLOCK(sp->s_ttyp->t_pgrp); - } - /* XXX tp should be locked. */ - sx_xunlock(&proctree_lock); - (void) ttywait(tp); - sx_xlock(&proctree_lock); + if (sp->s_ttyp != NULL) { + struct tty *tp = sp->s_ttyp; + + tty_lock(tp); + tty_signal_pgrp(tp, SIGHUP); + tty_unlock(tp); + /* * The tty could have been revoked * if we blocked. */ - if (sp->s_ttyvp) { - ttyvp = sp->s_ttyvp; - SESS_LOCK(p->p_session); - sp->s_ttyvp = NULL; - SESS_UNLOCK(p->p_session); + if (ttyvp->v_type != VBAD) { sx_xunlock(&proctree_lock); VOP_LOCK(ttyvp, LK_EXCLUSIVE); VOP_REVOKE(ttyvp, REVOKEALL); - vput(ttyvp); + VOP_UNLOCK(ttyvp, 0); sx_xlock(&proctree_lock); } } - if (sp->s_ttyvp) { - ttyvp = sp->s_ttyvp; - SESS_LOCK(p->p_session); - sp->s_ttyvp = NULL; - SESS_UNLOCK(p->p_session); - vrele(ttyvp); - } /* - * s_ttyp is not zero'd; we use this to indicate - * that the session once had a controlling terminal. + * s_ttyp is not zero'd; we use this to indicate that + * the session once had a controlling terminal. * (for logging and informational purposes) */ } @@ -358,7 +349,10 @@ exit1(struct thread *td, int rv) fixjobc(p, p->p_pgrp, 0); sx_xunlock(&proctree_lock); (void)acct_process(td); - mtx_unlock(&Giant); + + /* Release the TTY now we've unlocked everything. */ + if (ttyvp != NULL) + vrele(ttyvp); #ifdef KTRACE /* * Disable tracing, then drain any pending records and release diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 34ffaf1..25820ea 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -352,14 +352,13 @@ enterpgrp(p, pgid, pgrp, sess) * new session */ mtx_init(&sess->s_mtx, "session", NULL, MTX_DEF); - mtx_lock(&Giant); /* XXX TTY */ PROC_LOCK(p); p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); PGRP_LOCK(pgrp); sess->s_leader = p; sess->s_sid = p->p_pid; - sess->s_count = 1; + refcount_init(&sess->s_count, 1); sess->s_ttyvp = NULL; sess->s_ttyp = NULL; bcopy(p->p_session->s_login, sess->s_login, @@ -368,11 +367,8 @@ enterpgrp(p, pgid, pgrp, sess) KASSERT(p == curproc, ("enterpgrp: mksession and p != curproc")); } else { - mtx_lock(&Giant); /* XXX TTY */ pgrp->pg_session = p->p_session; - SESS_LOCK(pgrp->pg_session); - pgrp->pg_session->s_count++; - SESS_UNLOCK(pgrp->pg_session); + sess_hold(pgrp->pg_session); PGRP_LOCK(pgrp); } pgrp->pg_id = pgid; @@ -386,7 +382,6 @@ enterpgrp(p, pgid, pgrp, sess) pgrp->pg_jobc = 0; SLIST_INIT(&pgrp->pg_sigiolst); PGRP_UNLOCK(pgrp); - mtx_unlock(&Giant); /* XXX TTY */ doenterpgrp(p, pgrp); @@ -446,7 +441,6 @@ doenterpgrp(p, pgrp) fixjobc(p, pgrp, 1); fixjobc(p, p->p_pgrp, 0); - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(pgrp); PGRP_LOCK(savepgrp); PROC_LOCK(p); @@ -456,7 +450,6 @@ doenterpgrp(p, pgrp) LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); PGRP_UNLOCK(savepgrp); PGRP_UNLOCK(pgrp); - mtx_unlock(&Giant); /* XXX TTY */ if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); } @@ -472,14 +465,12 @@ leavepgrp(p) sx_assert(&proctree_lock, SX_XLOCKED); savepgrp = p->p_pgrp; - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(savepgrp); PROC_LOCK(p); LIST_REMOVE(p, p_pglist); p->p_pgrp = NULL; PROC_UNLOCK(p); PGRP_UNLOCK(savepgrp); - mtx_unlock(&Giant); /* XXX TTY */ if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); return (0); @@ -493,6 +484,7 @@ pgdelete(pgrp) register struct pgrp *pgrp; { struct session *savesess; + struct tty *tp; sx_assert(&proctree_lock, SX_XLOCKED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); @@ -504,18 +496,22 @@ pgdelete(pgrp) */ funsetownlst(&pgrp->pg_sigiolst); - mtx_lock(&Giant); /* XXX TTY */ PGRP_LOCK(pgrp); - if (pgrp->pg_session->s_ttyp != NULL && - pgrp->pg_session->s_ttyp->t_pgrp == pgrp) - pgrp->pg_session->s_ttyp->t_pgrp = NULL; + tp = pgrp->pg_session->s_ttyp; LIST_REMOVE(pgrp, pg_hash); savesess = pgrp->pg_session; - SESSRELE(savesess); PGRP_UNLOCK(pgrp); + + /* Remove the reference to the pgrp before deallocating it. */ + if (tp != NULL) { + tty_lock(tp); + tty_rel_pgrp(tp, pgrp); + tty_unlock(tp); + } + mtx_destroy(&pgrp->pg_mtx); FREE(pgrp, M_PGRP); - mtx_unlock(&Giant); /* XXX TTY */ + sess_release(savesess); } static void @@ -618,16 +614,21 @@ orphanpg(pg) } void -sessrele(struct session *s) +sess_hold(struct session *s) { - int i; - - SESS_LOCK(s); - i = --s->s_count; - SESS_UNLOCK(s); - if (i == 0) { - if (s->s_ttyp != NULL) - ttyrel(s->s_ttyp); + + refcount_acquire(&s->s_count); +} + +void +sess_release(struct session *s) +{ + + if (refcount_release(&s->s_count)) { + if (s->s_ttyp != NULL) { + tty_lock(s->s_ttyp); + tty_rel_sess(s->s_ttyp, s); + } mtx_destroy(&s->s_mtx); FREE(s, M_SESSION); } @@ -779,12 +780,13 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) kp->ki_kiflag |= KI_CTTY; if (SESS_LEADER(p)) kp->ki_kiflag |= KI_SLEADER; + /* XXX proctree_lock */ tp = sp->s_ttyp; SESS_UNLOCK(sp); } } if ((p->p_flag & P_CONTROLT) && tp != NULL) { - kp->ki_tdev = dev2udev(tp->t_dev); + kp->ki_tdev = tty_udev(tp); kp->ki_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; if (tp->t_session) kp->ki_tsid = tp->t_session->s_sid; @@ -1122,9 +1124,10 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) PROC_UNLOCK(p); continue; } + /* XXX proctree_lock */ SESS_LOCK(p->p_session); if (p->p_session->s_ttyp == NULL || - dev2udev(p->p_session->s_ttyp->t_dev) != + tty_udev(p->p_session->s_ttyp) != (dev_t)name[0]) { SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index 8267c29..9a15319 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -1329,3 +1329,28 @@ chgsbsize(uip, hiwat, to, max) *hiwat = to; return (1); } + +/* + * Change the count associated with number of pseudo-terminals + * a given user is using. When 'max' is 0, don't enforce a limit + */ +int +chgptscnt(uip, diff, max) + struct uidinfo *uip; + int diff; + rlim_t max; +{ + + /* Don't allow them to exceed max, but allow subtraction. */ + if (diff > 0 && max != 0) { + if (atomic_fetchadd_long(&uip->ui_ptscnt, (long)diff) + diff > max) { + atomic_subtract_long(&uip->ui_ptscnt, (long)diff); + return (0); + } + } else { + atomic_add_long(&uip->ui_ptscnt, (long)diff); + if (uip->ui_ptscnt < 0) + printf("negative ptscnt for uid = %d\n", uip->ui_uid); + } + return (1); +} diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index a0605ab..dea4034 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -136,7 +136,7 @@ uprintf(const char *fmt, ...) if (td == NULL || TD_IS_IDLETHREAD(td)) return (0); - mtx_lock(&Giant); + sx_slock(&proctree_lock); p = td->td_proc; PROC_LOCK(p); if ((p->p_flag & P_CONTROLT) == 0) { @@ -154,10 +154,12 @@ uprintf(const char *fmt, ...) } pca.flags = TOTTY; va_start(ap, fmt); + tty_lock(pca.tty); retval = kvprintf(fmt, putchar, &pca, 10, ap); + tty_unlock(pca.tty); va_end(ap); out: - mtx_unlock(&Giant); + sx_sunlock(&proctree_lock); return (retval); } @@ -174,19 +176,17 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) struct putchar_arg pca; struct session *sess = NULL; - mtx_lock(&Giant); + sx_slock(&proctree_lock); if (pri != -1) flags |= TOLOG; if (p != NULL) { PROC_LOCK(p); if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { sess = p->p_session; - SESS_LOCK(sess); + sess_hold(sess); PROC_UNLOCK(p); - SESSHOLD(sess); tp = sess->s_ttyp; - SESS_UNLOCK(sess); - if (ttycheckoutq(tp, 0)) + if (tp != NULL && tty_checkoutq(tp)) flags |= TOTTY; else tp = NULL; @@ -197,12 +197,16 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) pca.tty = tp; pca.flags = flags; va_start(ap, fmt); + if (pca.tty != NULL) + tty_lock(pca.tty); kvprintf(fmt, putchar, &pca, 10, ap); + if (pca.tty != NULL) + tty_unlock(pca.tty); va_end(ap); if (sess != NULL) - SESSRELE(sess); + sess_release(sess); msgbuftrigger = 1; - mtx_unlock(&Giant); + sx_sunlock(&proctree_lock); } /* @@ -413,7 +417,7 @@ putchar(int c, void *arg) putcons(c, ap); } else { if ((flags & TOTTY) && tp != NULL) - tputchar(c, tp); + tty_putchar(tp, c); if (flags & TOCONS) { if (constty != NULL) msgbuf_addchar(&consmsgbuf, c); diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 8fb0127..0a057ce 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ const char *syscallnames[] = { @@ -511,4 +511,5 @@ const char *syscallnames[] = { "renameat", /* 501 = renameat */ "symlinkat", /* 502 = symlinkat */ "unlinkat", /* 503 = unlinkat */ + "posix_openpt", /* 504 = posix_openpt */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 4cb55fa..8ed20d4 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -886,5 +886,6 @@ 502 AUE_SYMLINKAT STD { int symlinkat(char *path1, int fd, \ char *path2); } 503 AUE_UNLINKAT STD { int unlinkat(int fd, char *path, int flag); } +504 AUE_POSIXOPENPT STD { int posix_openpt(int flags); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index 98558cc..097bf21 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -3054,6 +3054,13 @@ systrace_args(int sysnum, void *params, u_int64_t *uarg, int *n_args) *n_args = 3; break; } + /* posix_openpt */ + case 504: { + struct posix_openpt_args *p = params; + iarg[0] = p->flags; /* int */ + *n_args = 1; + break; + } default: *n_args = 0; break; @@ -4607,6 +4614,16 @@ systrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* setfib */ + case 175: + switch(ndx) { + case 0: + p = "int"; + break; + default: + break; + }; + break; /* ntp_adjtime */ case 176: switch(ndx) { @@ -8093,6 +8110,16 @@ systrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* posix_openpt */ + case 504: + switch(ndx) { + case 0: + p = "int"; + break; + default: + break; + }; + break; default: break; }; diff --git a/sys/kern/tty.c b/sys/kern/tty.c index e7818b8..af12da3 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1,19 +1,9 @@ /*- - * Copyright (c) 1982, 1986, 1990, 1991, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Copyright (c) 2002 Networks Associates Technologies, Inc. + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> * All rights reserved. * - * Portions of this software were developed for the FreeBSD Project by - * ThinkSec AS and NAI Labs, the Security Research Division of Network - * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 - * ("CBOSS"), as part of the DARPA CHATS research program. + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,14 +13,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -38,798 +25,455 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)tty.c 8.8 (Berkeley) 1/21/94 - */ - -/*- - * TODO: - * o Fix races for sending the start char in ttyflush(). - * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). - * With luck, there will be MIN chars before select() returns(). - * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it. - * o Don't allow input in TS_ZOMBIE case. It would be visible through - * FIONREAD. - * o Do the new sio locking stuff here and use it to avoid special - * case for EXTPROC? - * o Lock PENDIN too? - * o Move EXTPROC and/or PENDIN to t_state? - * o Wrap most of ttioctl in spltty/splx. - * o Implement TIOCNOTTY or remove it from <sys/ioctl.h>. - * o Send STOP if IXOFF is toggled off while TS_TBLOCK is set. - * o Don't allow certain termios flags to affect disciplines other - * than TTYDISC. Cancel their effects before switch disciplines - * and ignore them if they are set while we are in another - * discipline. - * o Now that historical speed conversions are handled here, don't - * do them in drivers. - * o Check for TS_CARR_ON being set while everything is closed and not - * waiting for carrier. TS_CARR_ON isn't cleared if nothing is open, - * so it would live until the next open even if carrier drops. - * o Restore TS_WOPEN since it is useful in pstat. It must be cleared - * only when _all_ openers leave open(). */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include "opt_compat.h" -#include "opt_tty.h" #include <sys/param.h> -#include <sys/systm.h> +#include <sys/conf.h> #include <sys/cons.h> +#include <sys/fcntl.h> #include <sys/filio.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/namei.h> -#include <sys/sx.h> -#if defined(COMPAT_43TTY) +#ifdef COMPAT_43TTY #include <sys/ioctl_compat.h> -#endif +#endif /* COMPAT_43TTY */ +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/poll.h> #include <sys/priv.h> #include <sys/proc.h> -#define TTYDEFCHARS -#include <sys/tty.h> -#undef TTYDEFCHARS -#include <sys/fcntl.h> -#include <sys/conf.h> -#include <sys/poll.h> -#include <sys/kernel.h> -#include <sys/vnode.h> #include <sys/serial.h> -#include <sys/signalvar.h> -#include <sys/malloc.h> -#include <sys/filedesc.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/sx.h> #include <sys/sysctl.h> -#include <sys/timepps.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/ttycom.h> +#define TTYDEFCHARS +#include <sys/ttydefaults.h> +#undef TTYDEFCHARS +#include <sys/ucred.h> +#include <sys/vnode.h> #include <machine/stdarg.h> -MALLOC_DEFINE(M_TTYS, "ttys", "tty data structures"); - -long tk_cancc; -long tk_nin; -long tk_nout; -long tk_rawcc; - -static d_open_t ttysopen; -static d_close_t ttysclose; -static d_read_t ttysrdwr; -static d_ioctl_t ttysioctl; -static d_purge_t ttypurge; - -/* Default cdevsw for common tty devices */ -static struct cdevsw tty_cdevsw = { - .d_version = D_VERSION, - .d_open = ttyopen, - .d_close = ttyclose, - .d_ioctl = ttyioctl, - .d_purge = ttypurge, - .d_name = "ttydrv", - .d_flags = D_TTY | D_NEEDGIANT, -}; +static MALLOC_DEFINE(M_TTY, "tty", "tty device"); -/* Cdevsw for slave tty devices */ -static struct cdevsw ttys_cdevsw = { - .d_version = D_VERSION, - .d_open = ttysopen, - .d_close = ttysclose, - .d_read = ttysrdwr, - .d_write = ttysrdwr, - .d_ioctl = ttysioctl, - .d_name = "TTYS", - .d_flags = D_TTY | D_NEEDGIANT, -}; +static void tty_rel_free(struct tty *tp); -static int ttnread(struct tty *tp); -static void ttyecho(int c, struct tty *tp); -static int ttyoutput(int c, struct tty *tp); -static void ttypend(struct tty *tp); -static void ttyretype(struct tty *tp); -static void ttyrub(int c, struct tty *tp); -static void ttyrubo(struct tty *tp, int cnt); -static void ttyunblock(struct tty *tp); -static int ttywflush(struct tty *tp); -static int filt_ttyread(struct knote *kn, long hint); -static void filt_ttyrdetach(struct knote *kn); -static int filt_ttywrite(struct knote *kn, long hint); -static void filt_ttywdetach(struct knote *kn); +static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); +static struct sx tty_list_sx; +SX_SYSINIT(tty_list, &tty_list_sx, "tty list"); +static unsigned int tty_list_count = 0; /* - * Table with character classes and parity. The 8th bit indicates parity, - * the 7th bit indicates the character is an alphameric or underscore (for - * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits - * are 0 then the character needs no special processing on output; classes - * other than 0 might be translated or (not currently) require delays. + * Flags that are supported and stored by this implementation. */ -#define E 0x00 /* Even parity. */ -#define O 0x80 /* Odd parity. */ -#define PARITY(c) (char_type[c] & O) - -#define ALPHA 0x40 /* Alpha or underscore. */ -#define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA) - -#define CCLASSMASK 0x3f -#define CCLASS(c) (char_type[c] & CCLASSMASK) - -#define BS BACKSPACE -#define CC CONTROL -#define CR RETURN -#define NA ORDINARY | ALPHA -#define NL NEWLINE -#define NO ORDINARY -#define TB TAB -#define VT VTAB - -static u_char const char_type[] = { - E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ - O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ - O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ - E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ - O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ - E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ - E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ - O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ - O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ - E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ - E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ - O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ - E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ - O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ - O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ - E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ - /* - * Meta chars; should be settable per character set; - * for now, treat them all as normal characters. - */ - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, - NA, NA, NA, NA, NA, NA, NA, NA, -}; -#undef BS -#undef CC -#undef CR -#undef NA -#undef NL -#undef NO -#undef TB -#undef VT - -/* Macros to clear/set/test flags. */ -#define SET(t, f) (t) |= (f) -#define CLR(t, f) (t) &= ~(f) -#define ISSET(t, f) ((t) & (f)) - -#undef MAX_INPUT /* XXX wrong in <sys/syslimits.h> */ -#define MAX_INPUT TTYHOG /* XXX limit is usually larger for !ICANON */ +#define TTYSUP_IFLAG (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|\ + INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL) +#define TTYSUP_OFLAG (OPOST|ONLCR|TAB3|ONOEOT|OCRNL|ONOCR|ONLRET) +#define TTYSUP_LFLAG (ECHOKE|ECHOE|ECHOK|ECHO|ECHONL|ECHOPRT|\ + ECHOCTL|ISIG|ICANON|ALTWERASE|IEXTEN|TOSTOP|\ + FLUSHO|NOKERNINFO|NOFLSH) +#define TTYSUP_CFLAG (CIGNORE|CSIZE|CSTOPB|CREAD|PARENB|PARODD|\ + HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\ + CDSR_OFLOW|CCAR_OFLOW) + +#define TTY_CALLOUT(tp,d) ((tp)->t_dev != (d)) /* - * list of struct tty where pstat(8) can pick it up with sysctl - * - * The lock order is to grab the list mutex before the tty mutex. - * Together with additions going on the tail of the list, this allows - * the sysctl to avoid doing retries. + * Set TTY buffer sizes. */ -static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); -static struct mtx tty_list_mutex; -MTX_SYSINIT(tty_list, &tty_list_mutex, "ttylist", MTX_DEF); -static struct unrhdr *tty_unit; +static void +tty_watermarks(struct tty *tp) +{ + speed_t sp; + + /* Provide an input buffer for 0.2 seconds of data. */ + sp = MAX(tp->t_termios.c_ispeed, 0); + ttyinq_setsize(&tp->t_inq, tp, sp / 5); + + /* Set low watermark at 10% (when 90% is available). */ + tp->t_inlow = (ttyinq_getsize(&tp->t_inq) * 9) / 10; -static int drainwait = 5*60; -SYSCTL_INT(_kern, OID_AUTO, drainwait, CTLFLAG_RW, &drainwait, - 0, "Output drain timeout in seconds"); + /* Provide an ouput buffer for 0.2 seconds of data. */ + sp = MAX(tp->t_termios.c_ospeed, 0); + ttyoutq_setsize(&tp->t_outq, tp, sp / 5); -static struct tty * -tty_gettp(struct cdev *dev) + /* Set low watermark at 10% (when 90% is available). */ + tp->t_outlow = (ttyoutq_getsize(&tp->t_outq) * 9) / 10; +} + +static void +tty_freebuffers(struct tty *tp) { - struct tty *tp; - struct cdevsw *csw; - - csw = dev_refthread(dev); - if (csw == NULL) - return (NULL); - KASSERT(csw->d_flags & D_TTY, - ("non D_TTY (%s) in tty code", devtoname(dev))); - tp = dev->si_tty; - dev_relthread(dev); - KASSERT(tp != NULL, - ("no tty pointer on (%s) in tty code", devtoname(dev))); - return (tp); + + /* Destroy input buffers. */ + ttyinq_flush(&tp->t_inq); + ttyinq_setsize(&tp->t_inq, NULL, 0); + MPASS(ttyinq_getsize(&tp->t_inq) == 0); + tp->t_inlow = 0; + + /* Destroy output buffers. */ + ttyoutq_flush(&tp->t_outq); + ttyoutq_setsize(&tp->t_outq, NULL, 0); + MPASS(ttyoutq_getsize(&tp->t_outq) == 0); + tp->t_outlow = 0; } -/* - * Initial open of tty, or (re)entry to standard tty line discipline. - */ -int -tty_open(struct cdev *device, struct tty *tp) +static int +tty_drain(struct tty *tp) { - int s; - - s = spltty(); - tp->t_dev = device; - tp->t_hotchar = 0; - if (!ISSET(tp->t_state, TS_ISOPEN)) { - ttyref(tp); - SET(tp->t_state, TS_ISOPEN); - if (ISSET(tp->t_cflag, CLOCAL)) - SET(tp->t_state, TS_CONNECTED); - bzero(&tp->t_winsize, sizeof(tp->t_winsize)); + int error; + + while (ttyoutq_bytesused(&tp->t_outq) > 0) { + ttydevsw_outwakeup(tp); + /* Could be handled synchronously. */ + if (ttyoutq_bytesused(&tp->t_outq) == 0) + return (0); + + /* Wait for data to be drained. */ + error = tty_wait(tp, &tp->t_outwait); + if (error) + return (error); } - /* XXX don't hang forever on output */ - if (tp->t_timeout < 0) - tp->t_timeout = drainwait*hz; - ttsetwater(tp); - splx(s); + return (0); } /* - * Handle close() on a tty line: flush and set to initial state, - * bumping generation number so that pending read/write calls - * can detect recycling of the tty. - * XXX our caller should have done `spltty(); l_close(); tty_close();' - * and l_close() should have flushed, but we repeat the spltty() and - * the flush in case there are buggy callers. + * Because the revoke() call already calls d_close() without making sure + * all threads are purged from the TTY, we can only destroy the buffers + * and such when the last thread leaves the TTY. ttydev_enter() and + * ttydev_leave() are called from within the cdev functions, to make + * sure we can garbage collect the TTY. */ -int -tty_close(struct tty *tp) + +static __inline int +ttydev_enter(struct tty *tp) { - int ostate, s; + tty_lock(tp); + if (tty_gone(tp) || !tty_opened(tp)) { + /* Device is already gone. */ + tty_unlock(tp); + return (ENXIO); + } + + return (0); +} + +static void +ttydev_leave(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + + if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { + /* Device is still opened somewhere. */ + tty_unlock(tp); + return; + } + + tp->t_flags |= TF_OPENCLOSE; + + /* Stop asynchronous I/O. */ funsetown(&tp->t_sigio); - s = spltty(); + + /* Remove console TTY. */ if (constty == tp) constty_clear(); - ttyflush(tp, FREAD | FWRITE); - clist_free_cblocks(&tp->t_canq); - clist_free_cblocks(&tp->t_outq); - clist_free_cblocks(&tp->t_rawq); - - tp->t_gen++; - tp->t_line = TTYDISC; - tp->t_hotchar = 0; - tp->t_pgrp = NULL; - tp->t_session = NULL; - ostate = tp->t_state; - tp->t_state = 0; - knlist_clear(&tp->t_rsel.si_note, 0); - knlist_clear(&tp->t_wsel.si_note, 0); - /* - * Both final close and revocation close might end up calling - * this method. Only the thread clearing TS_ISOPEN should - * release the reference to the tty. - */ - if (ISSET(ostate, TS_ISOPEN)) - ttyrel(tp); - splx(s); - return (0); -} + /* Drain any output. */ + MPASS((tp->t_flags & TF_STOPPED) == 0); + if (!tty_gone(tp)) + tty_drain(tp); -#define FLUSHQ(q) { \ - if ((q)->c_cc) \ - ndflush(q, (q)->c_cc); \ -} + ttydisc_close(tp); + + /* Destroy associated buffers already. */ + tty_freebuffers(tp); + + knlist_clear(&tp->t_inpoll.si_note, 1); + knlist_clear(&tp->t_outpoll.si_note, 1); -/* Is 'c' a line delimiter ("break" character)? */ -#define TTBREAKC(c, lflag) \ - ((c) == '\n' || (((c) == cc[VEOF] || \ - (c) == cc[VEOL] || ((c) == cc[VEOL2] && lflag & IEXTEN)) && \ - (c) != _POSIX_VDISABLE)) + if (!tty_gone(tp)) + ttydevsw_close(tp); + + tp->t_flags &= ~TF_OPENCLOSE; + tty_rel_free(tp); +} /* - * Process input of a single character received on a tty. + * Operations that are exposed through the character device in /dev. */ -int -ttyinput(int c, struct tty *tp) +static int +ttydev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { - tcflag_t iflag, lflag; - cc_t *cc; - int i, err; + struct tty *tp = dev->si_drv1; + int error; + /* Disallow access when the TTY belongs to a different prison. */ + if (dev->si_cred != NULL && + dev->si_cred->cr_prison != td->td_ucred->cr_prison && + priv_check(td, PRIV_TTY_PRISON)) { + return (EPERM); + } + + tty_lock(tp); + if (tty_gone(tp)) { + /* Device is already gone. */ + tty_unlock(tp); + return (ENXIO); + } /* - * If input is pending take it first. - */ - lflag = tp->t_lflag; - if (ISSET(lflag, PENDIN)) - ttypend(tp); - /* - * Gather stats. + * Prevent the TTY from being opened when being torn down or + * built up by unrelated processes. */ - if (ISSET(lflag, ICANON)) { - ++tk_cancc; - ++tp->t_cancc; - } else { - ++tk_rawcc; - ++tp->t_rawcc; + if (tp->t_flags & TF_OPENCLOSE) { + tty_unlock(tp); + return (EBUSY); } - ++tk_nin; + tp->t_flags |= TF_OPENCLOSE; /* - * Block further input iff: - * current input > threshold AND input is available to user program - * AND input flow control is enabled and not yet invoked. - * The 3 is slop for PARMRK. + * Make sure the "tty" and "cua" device cannot be opened at the + * same time. */ - iflag = tp->t_iflag; - if (tp->t_rawq.c_cc + tp->t_canq.c_cc > tp->t_ihiwat - 3 && - (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) && - (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) && - !ISSET(tp->t_state, TS_TBLOCK)) - ttyblock(tp); - - /* Handle exceptional conditions (break, parity, framing). */ - cc = tp->t_cc; - err = (ISSET(c, TTY_ERRORMASK)); - if (err) { - CLR(c, TTY_ERRORMASK); - if (ISSET(err, TTY_BI)) { - if (ISSET(iflag, IGNBRK)) - return (0); - if (ISSET(iflag, BRKINT)) { - ttyflush(tp, FREAD | FWRITE); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGINT, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - goto endcase; - } - if (ISSET(iflag, PARMRK)) - goto parmrk; - } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK)) - || ISSET(err, TTY_FE)) { - if (ISSET(iflag, IGNPAR)) - return (0); - else if (ISSET(iflag, PARMRK)) { -parmrk: - if (tp->t_rawq.c_cc + tp->t_canq.c_cc > - MAX_INPUT - 3) - goto input_overflow; - (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); - (void)putc(0 | TTY_QUOTE, &tp->t_rawq); - (void)putc(c | TTY_QUOTE, &tp->t_rawq); - goto endcase; - } else - c = 0; + if (TTY_CALLOUT(tp, dev)) { + if (tp->t_flags & TF_OPENED_IN) { + error = EBUSY; + goto done; + } + } else { + if (tp->t_flags & TF_OPENED_OUT) { + error = EBUSY; + goto done; } } - if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP)) - CLR(c, 0x80); - if (!ISSET(lflag, EXTPROC)) { - /* - * Check for literal nexting very first - */ - if (ISSET(tp->t_state, TS_LNCH)) { - SET(c, TTY_QUOTE); - CLR(tp->t_state, TS_LNCH); - } - /* - * Scan for special characters. This code - * is really just a big case statement with - * non-constant cases. The bottom of the - * case statement is labeled ``endcase'', so goto - * it after a case match, or similar. - */ + if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { + error = EBUSY; + goto done; + } - /* - * Control chars which aren't controlled - * by ICANON, ISIG, or IXON. - */ - if (ISSET(lflag, IEXTEN)) { - if (CCEQ(cc[VLNEXT], c)) { - if (ISSET(lflag, ECHO)) { - if (ISSET(lflag, ECHOE)) { - (void)ttyoutput('^', tp); - (void)ttyoutput('\b', tp); - } else - ttyecho(c, tp); - } - SET(tp->t_state, TS_LNCH); - goto endcase; - } - if (CCEQ(cc[VDISCARD], c)) { - if (ISSET(lflag, FLUSHO)) - CLR(tp->t_lflag, FLUSHO); - else { - ttyflush(tp, FWRITE); - ttyecho(c, tp); - if (tp->t_rawq.c_cc + tp->t_canq.c_cc) - ttyretype(tp); - SET(tp->t_lflag, FLUSHO); - } - goto startoutput; - } + if (!tty_opened(tp)) { + /* Set proper termios flags. */ + if (TTY_CALLOUT(tp, dev)) { + tp->t_termios = tp->t_termios_init_out; + } else { + tp->t_termios = tp->t_termios_init_in; } - /* - * Signals. - */ - if (ISSET(lflag, ISIG)) { - if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { - if (!ISSET(lflag, NOFLSH)) - ttyflush(tp, FREAD | FWRITE); - ttyecho(c, tp); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, - CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - goto endcase; - } - if (CCEQ(cc[VSUSP], c)) { - if (!ISSET(lflag, NOFLSH)) - ttyflush(tp, FREAD); - ttyecho(c, tp); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGTSTP, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - goto endcase; - } - } - /* - * Handle start/stop characters. - */ - if (ISSET(iflag, IXON)) { - if (CCEQ(cc[VSTOP], c)) { - if (!ISSET(tp->t_state, TS_TTSTOP)) { - SET(tp->t_state, TS_TTSTOP); - tt_stop(tp, 0); - return (0); - } - if (!CCEQ(cc[VSTART], c)) - return (0); - /* - * if VSTART == VSTOP then toggle - */ - goto endcase; - } - if (CCEQ(cc[VSTART], c)) - goto restartoutput; - } - /* - * IGNCR, ICRNL, & INLCR - */ - if (c == '\r') { - if (ISSET(iflag, IGNCR)) - return (0); - else if (ISSET(iflag, ICRNL)) - c = '\n'; - } else if (c == '\n' && ISSET(iflag, INLCR)) - c = '\r'; + ttydevsw_param(tp, &tp->t_termios); + + ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); + + error = ttydevsw_open(tp); + if (error != 0) + goto done; + + ttydisc_open(tp); + tty_watermarks(tp); } - if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) { - /* - * From here on down canonical mode character - * processing takes place. - */ - /* - * erase or erase2 (^H / ^?) - */ - if (CCEQ(cc[VERASE], c) || CCEQ(cc[VERASE2], c) ) { - if (tp->t_rawq.c_cc) - ttyrub(unputc(&tp->t_rawq), tp); - goto endcase; - } - /* - * kill (^U) - */ - if (CCEQ(cc[VKILL], c)) { - if (ISSET(lflag, ECHOKE) && - tp->t_rawq.c_cc == tp->t_rocount && - !ISSET(lflag, ECHOPRT)) - while (tp->t_rawq.c_cc) - ttyrub(unputc(&tp->t_rawq), tp); - else { - ttyecho(c, tp); - if (ISSET(lflag, ECHOK) || - ISSET(lflag, ECHOKE)) - ttyecho('\n', tp); - FLUSHQ(&tp->t_rawq); - tp->t_rocount = 0; - } - CLR(tp->t_state, TS_LOCAL); - goto endcase; - } - /* - * word erase (^W) - */ - if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) { - int ctype; - /* - * erase whitespace - */ - while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t') - ttyrub(c, tp); - if (c == -1) - goto endcase; - /* - * erase last char of word and remember the - * next chars type (for ALTWERASE) - */ - ttyrub(c, tp); - c = unputc(&tp->t_rawq); - if (c == -1) - goto endcase; - if (c == ' ' || c == '\t') { - (void)putc(c, &tp->t_rawq); - goto endcase; - } - ctype = ISALPHA(c); - /* - * erase rest of word - */ - do { - ttyrub(c, tp); - c = unputc(&tp->t_rawq); - if (c == -1) - goto endcase; - } while (c != ' ' && c != '\t' && - (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype)); - (void)putc(c, &tp->t_rawq); - goto endcase; - } - /* - * reprint line (^R) - */ - if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) { - ttyretype(tp); - goto endcase; - } - /* - * ^T - kernel info and generate SIGINFO - */ - if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) { - if (ISSET(lflag, ISIG) && tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGINFO, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if (!ISSET(lflag, NOKERNINFO)) - ttyinfo(tp); - goto endcase; + /* Wait for Carrier Detect. */ + if (!TTY_CALLOUT(tp, dev) && (oflags & O_NONBLOCK) == 0 && + (tp->t_termios.c_cflag & CLOCAL) == 0) { + while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { + error = tty_wait(tp, &tp->t_dcdwait); + if (error != 0) + goto done; } } - /* - * Check for input buffer overflow - */ - if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) { -input_overflow: - if (ISSET(iflag, IMAXBEL)) { - if (tp->t_outq.c_cc < tp->t_ohiwat) - (void)ttyoutput(CTRL('g'), tp); - } - goto endcase; + + if (TTY_CALLOUT(tp, dev)) { + tp->t_flags |= TF_OPENED_OUT; + } else { + tp->t_flags |= TF_OPENED_IN; } - if ( c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP) - && ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR)) - (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); +done: tp->t_flags &= ~TF_OPENCLOSE; + ttydev_leave(tp); + return (error); +} + +static int +ttydev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct tty *tp = dev->si_drv1; + + tty_lock(tp); /* - * Put data char in q for user and - * wakeup on seeing a line delimiter. + * This can only be called once. The callin and the callout + * devices cannot be opened at the same time. */ - if (putc(c, &tp->t_rawq) >= 0) { - if (!ISSET(lflag, ICANON)) { - ttwakeup(tp); - ttyecho(c, tp); - goto endcase; - } - if (TTBREAKC(c, lflag)) { - tp->t_rocount = 0; - catq(&tp->t_rawq, &tp->t_canq); - ttwakeup(tp); - } else if (tp->t_rocount++ == 0) - tp->t_rocol = tp->t_column; - if (ISSET(tp->t_state, TS_ERASE)) { - /* - * end of prterase \.../ - */ - CLR(tp->t_state, TS_ERASE); - (void)ttyoutput('/', tp); - } - i = tp->t_column; - ttyecho(c, tp); - if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) { - /* - * Place the cursor over the '^' of the ^D. - */ - i = imin(2, tp->t_column - i); - while (i > 0) { - (void)ttyoutput('\b', tp); - i--; - } + MPASS((tp->t_flags & TF_OPENED) != TF_OPENED); + tp->t_flags &= ~(TF_OPENED|TF_EXCLUDE|TF_STOPPED); + + /* Properly wake up threads that are stuck - revoke(). */ + tp->t_revokecnt++; + tty_wakeup(tp, FREAD|FWRITE); + cv_broadcast(&tp->t_bgwait); + + ttydev_leave(tp); + + return (0); +} + +static __inline int +tty_is_ctty(struct tty *tp, struct proc *p) +{ + tty_lock_assert(tp, MA_OWNED); + + return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); +} + +static int +tty_wait_background(struct tty *tp, struct thread *td, int sig) +{ + struct proc *p = td->td_proc; + struct pgrp *pg; + int error; + + MPASS(sig == SIGTTIN || sig == SIGTTOU); + tty_lock_assert(tp, MA_OWNED); + + for (;;) { + PROC_LOCK(p); + /* + * The process should only sleep, when: + * - This terminal is the controling terminal + * - Its process group is not the foreground process + * group + * - The parent process isn't waiting for the child to + * exit + * - the signal to send to the process isn't masked + */ + if (!tty_is_ctty(tp, p) || + p->p_pgrp == tp->t_pgrp || p->p_flag & P_PPWAIT || + SIGISMEMBER(p->p_sigacts->ps_sigignore, sig) || + SIGISMEMBER(td->td_sigmask, sig)) { + /* Allow the action to happen. */ + PROC_UNLOCK(p); + return (0); } + + /* + * Send the signal and sleep until we're the new + * foreground process group. + */ + pg = p->p_pgrp; + PROC_UNLOCK(p); + if (pg->pg_jobc == 0) + return (EIO); + PGRP_LOCK(pg); + pgsignal(pg, sig, 1); + PGRP_UNLOCK(pg); + + error = tty_wait(tp, &tp->t_bgwait); + if (error) + return (error); } -endcase: +} + +static int +ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct tty *tp = dev->si_drv1; + int error; + + error = ttydev_enter(tp); + if (error) + return (0); + + error = tty_wait_background(tp, curthread, SIGTTIN); + if (error) + goto done; + + error = ttydisc_read(tp, uio, ioflag); +done: ttydev_leave(tp); + /* - * IXANY means allow any character to restart output. + * The read() and write() calls should not throw an error when + * the device is ripped offline. */ - if (ISSET(tp->t_state, TS_TTSTOP) && - !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) + if (error == ENXIO) return (0); -restartoutput: - CLR(tp->t_lflag, FLUSHO); - CLR(tp->t_state, TS_TTSTOP); -startoutput: - return (ttstart(tp)); + + return (error); } -/* - * Output a single character on a tty, doing output processing - * as needed (expanding tabs, newline processing, etc.). - * Returns < 0 if succeeds, otherwise returns char to resend. - * Must be recursive. - */ static int -ttyoutput(int c, struct tty *tp) +ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) { - tcflag_t oflag; - int col, s; - - oflag = tp->t_oflag; - if (!ISSET(oflag, OPOST)) { - if (ISSET(tp->t_lflag, FLUSHO)) - return (-1); - if (putc(c, &tp->t_outq)) - return (c); - tk_nout++; - tp->t_outcc++; - return (-1); - } - /* - * Do tab expansion if OXTABS is set. Special case if we external - * processing, we don't do the tab expansion because we'll probably - * get it wrong. If tab expansion needs to be done, let it happen - * externally. - */ - CLR(c, ~TTY_CHARMASK); - if (c == '\t' && - ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) { - c = 8 - (tp->t_column & 7); - if (!ISSET(tp->t_lflag, FLUSHO)) { - s = spltty(); /* Don't interrupt tabs. */ - c -= b_to_q(" ", c, &tp->t_outq); - tk_nout += c; - tp->t_outcc += c; - splx(s); - } - tp->t_column += c; - return (c ? -1 : '\t'); + struct tty *tp = dev->si_drv1; + int error; + + error = ttydev_enter(tp); + if (error) + return (0); + + if (tp->t_termios.c_lflag & TOSTOP) { + error = tty_wait_background(tp, curthread, SIGTTOU); + if (error) + goto done; } - if (c == CEOT && ISSET(oflag, ONOEOT)) - return (-1); + + error = ttydisc_write(tp, uio, ioflag); +done: ttydev_leave(tp); /* - * Newline translation: if ONLCR is set, - * translate newline into "\r\n". + * The read() and write() calls should not throw an error when + * the device is ripped offline. */ - if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) { - tk_nout++; - tp->t_outcc++; - if (!ISSET(tp->t_lflag, FLUSHO) && putc('\r', &tp->t_outq)) - return (c); - } - /* If OCRNL is set, translate "\r" into "\n". */ - else if (c == '\r' && ISSET(tp->t_oflag, OCRNL)) - c = '\n'; - /* If ONOCR is set, don't transmit CRs when on column 0. */ - else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0) - return (-1); - - tk_nout++; - tp->t_outcc++; - if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq)) - return (c); + if (error == ENXIO) + return (0); - col = tp->t_column; - switch (CCLASS(c)) { - case BACKSPACE: - if (col > 0) - --col; - break; - case CONTROL: - break; - case NEWLINE: - if (ISSET(tp->t_oflag, ONLCR | ONLRET)) - col = 0; - break; - case RETURN: - col = 0; - break; - case ORDINARY: - ++col; - break; - case TAB: - col = (col + 8) & ~7; - break; - } - tp->t_column = col; - return (-1); + return (error); } -/* - * Ioctls for all tty devices. Called after line-discipline specific ioctl - * has been called to do discipline-specific functions and/or reject any - * of these ioctl commands. - */ -/* ARGSUSED */ -int -ttioctl(struct tty *tp, u_long cmd, void *data, int flag) +static int +ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, + struct thread *td) { - struct proc *p; - struct thread *td; - struct pgrp *pgrp; - int s, error, bits, sig, sig2; + struct tty *tp = dev->si_drv1; + int error; - td = curthread; /* XXX */ - p = td->td_proc; + error = ttydev_enter(tp); + if (error) + return (error); - /* If the ioctl involves modification, hang if in the background. */ switch (cmd) { - case TIOCCBRK: - case TIOCCONS: - case TIOCDRAIN: - case TIOCEXCL: - case TIOCFLUSH: -#ifdef TIOCHPCL - case TIOCHPCL: + case TIOCCBRK: + case TIOCCONS: + case TIOCDRAIN: + case TIOCEXCL: + case TIOCFLUSH: + case TIOCNXCL: + case TIOCSBRK: + case TIOCSCTTY: + case TIOCSETA: + case TIOCSETAF: + case TIOCSETAW: + case TIOCSPGRP: + case TIOCSTART: + case TIOCSTAT: + case TIOCSTOP: + case TIOCSWINSZ: +#if 0 + case TIOCSDRAINWAIT: + case TIOCSETD: + case TIOCSTI: #endif - case TIOCNXCL: - case TIOCSBRK: - case TIOCSCTTY: - case TIOCSDRAINWAIT: - case TIOCSETA: - case TIOCSETAF: - case TIOCSETAW: - case TIOCSETD: - case TIOCSPGRP: - case TIOCSTART: - case TIOCSTAT: - case TIOCSTI: - case TIOCSTOP: - case TIOCSWINSZ: -#if defined(COMPAT_43TTY) +#ifdef COMPAT_43TTY case TIOCLBIC: case TIOCLBIS: case TIOCLSET: @@ -838,2390 +482,1268 @@ ttioctl(struct tty *tp, u_long cmd, void *data, int flag) case TIOCSETN: case TIOCSETP: case TIOCSLTC: -#endif - sx_slock(&proctree_lock); - PROC_LOCK(p); - while (isbackground(p, tp) && !(p->p_flag & P_PPWAIT) && - !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && - !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { - pgrp = p->p_pgrp; - PROC_UNLOCK(p); - if (pgrp->pg_jobc == 0) { - sx_sunlock(&proctree_lock); - return (EIO); - } - PGRP_LOCK(pgrp); - sx_sunlock(&proctree_lock); - pgsignal(pgrp, SIGTTOU, 1); - PGRP_UNLOCK(pgrp); - error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, "ttybg1", - 0); - if (error) - return (error); - sx_slock(&proctree_lock); - PROC_LOCK(p); - } - PROC_UNLOCK(p); - sx_sunlock(&proctree_lock); - break; - } - - - if (tp->t_modem != NULL) { - switch (cmd) { - case TIOCSDTR: - tt_modem(tp, SER_DTR, 0); - return (0); - case TIOCCDTR: - tt_modem(tp, 0, SER_DTR); - return (0); - case TIOCMSET: - bits = *(int *)data; - sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; - sig2 = ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1; - tt_modem(tp, sig, sig2); - return (0); - case TIOCMBIS: - bits = *(int *)data; - sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; - tt_modem(tp, sig, 0); - return (0); - case TIOCMBIC: - bits = *(int *)data; - sig = (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1; - tt_modem(tp, 0, sig); - return (0); - case TIOCMGET: - sig = tt_modem(tp, 0, 0); - /* See <sys/serial.h. for the "<< 1" stuff */ - bits = TIOCM_LE + (sig << 1); - *(int *)data = bits; - return (0); - default: - break; - } - } - - if (tp->t_pps != NULL) { - error = pps_ioctl(cmd, data, tp->t_pps); - if (error != ENOIOCTL) - return (error); - } - - switch (cmd) { /* Process the ioctl. */ - case FIOASYNC: /* set/clear async i/o */ - s = spltty(); - if (*(int *)data) - SET(tp->t_state, TS_ASYNC); - else - CLR(tp->t_state, TS_ASYNC); - splx(s); - break; - case FIONBIO: /* set/clear non-blocking i/o */ - break; /* XXX: delete. */ - case FIONREAD: /* get # bytes to read */ - s = spltty(); - *(int *)data = ttnread(tp); - splx(s); - break; - - case FIOSETOWN: +#endif /* COMPAT_43TTY */ /* - * Policy -- Don't allow FIOSETOWN on someone else's - * controlling tty + * If the ioctl() causes the TTY to be modified, let it + * wait in the background. */ - if (tp->t_session != NULL && !isctty(p, tp)) - return (ENOTTY); - - error = fsetown(*(int *)data, &tp->t_sigio); + error = tty_wait_background(tp, curthread, SIGTTOU); if (error) - return (error); - break; - case FIOGETOWN: - if (tp->t_session != NULL && !isctty(p, tp)) - return (ENOTTY); - *(int *)data = fgetown(&tp->t_sigio); - break; - - case TIOCEXCL: /* set exclusive use of tty */ - s = spltty(); - SET(tp->t_state, TS_XCLUDE); - splx(s); - break; - case TIOCFLUSH: { /* flush buffers */ - int flags = *(int *)data; - - if (flags == 0) - flags = FREAD | FWRITE; - else - flags &= FREAD | FWRITE; - ttyflush(tp, flags); - break; + goto done; } - case TIOCCONS: /* become virtual console */ - if (*(int *)data) { - struct nameidata nid; - if (constty && constty != tp && - ISSET(constty->t_state, TS_CONNECTED)) - return (EBUSY); - - /* Ensure user can open the real console. */ - NDINIT(&nid, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, - "/dev/console", td); - if ((error = namei(&nid)) != 0) - return (error); - NDFREE(&nid, NDF_ONLY_PNBUF); - error = VOP_ACCESS(nid.ni_vp, VREAD, td->td_ucred, td); - vput(nid.ni_vp); - if (error) - return (error); - - constty_set(tp); - } else if (tp == constty) - constty_clear(); - break; - case TIOCDRAIN: /* wait till output drained */ - error = ttywait(tp); - if (error) - return (error); - break; - case TIOCGETA: { /* get termios struct */ - struct termios *t = (struct termios *)data; + error = tty_ioctl(tp, cmd, data, td); +done: ttydev_leave(tp); - bcopy(&tp->t_termios, t, sizeof(struct termios)); - break; - } - case TIOCGETD: /* get line discipline */ - *(int *)data = tp->t_line; - break; - case TIOCGWINSZ: /* get window size */ - *(struct winsize *)data = tp->t_winsize; - break; - case TIOCGPGRP: /* get pgrp of tty */ - if (!isctty(p, tp)) - return (ENOTTY); - *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; - break; - case TIOCGSID: /* get sid of tty */ - if (!isctty(p, tp)) - return (ENOTTY); - *(int *)data = tp->t_session->s_sid; - break; -#ifdef TIOCHPCL - case TIOCHPCL: /* hang up on last close */ - s = spltty(); - SET(tp->t_cflag, HUPCL); - splx(s); - break; -#endif - case TIOCMGDTRWAIT: - *(int *)data = tp->t_dtr_wait * 100 / hz; - break; - case TIOCMSDTRWAIT: - /* must be root since the wait applies to following logins */ - error = priv_check(td, PRIV_TTY_DTRWAIT); - if (error) - return (error); - tp->t_dtr_wait = *(int *)data * hz / 100; - break; - case TIOCNXCL: /* reset exclusive use of tty */ - s = spltty(); - CLR(tp->t_state, TS_XCLUDE); - splx(s); - break; - case TIOCOUTQ: /* output queue size */ - *(int *)data = tp->t_outq.c_cc; - break; - case TIOCSETA: /* set termios struct */ - case TIOCSETAW: /* drain output, set */ - case TIOCSETAF: { /* drn out, fls in, set */ - struct termios *t = (struct termios *)data; + return (error); +} - if (t->c_ispeed == 0) - t->c_ispeed = t->c_ospeed; - if (t->c_ispeed == 0) - t->c_ispeed = tp->t_ospeed; - if (t->c_ispeed == 0) - return (EINVAL); - s = spltty(); - if (cmd == TIOCSETAW || cmd == TIOCSETAF) { - error = ttywait(tp); - if (error) { - splx(s); - return (error); - } - if (cmd == TIOCSETAF) - ttyflush(tp, FREAD); - } - if (!ISSET(t->c_cflag, CIGNORE)) { - /* - * Set device hardware. - */ - error = tt_param(tp, t); - if (error) { - splx(s); - return (error); - } - if (ISSET(t->c_cflag, CLOCAL) && - !ISSET(tp->t_cflag, CLOCAL)) { - /* - * XXX disconnections would be too hard to - * get rid of without this kludge. The only - * way to get rid of controlling terminals - * is to exit from the session leader. - */ - CLR(tp->t_state, TS_ZOMBIE); - - wakeup(TSA_CARR_ON(tp)); - ttwakeup(tp); - ttwwakeup(tp); - } - if ((ISSET(tp->t_state, TS_CARR_ON) || - ISSET(t->c_cflag, CLOCAL)) && - !ISSET(tp->t_state, TS_ZOMBIE)) - SET(tp->t_state, TS_CONNECTED); - else - CLR(tp->t_state, TS_CONNECTED); - tp->t_cflag = t->c_cflag; - tp->t_ispeed = t->c_ispeed; - if (t->c_ospeed != 0) - tp->t_ospeed = t->c_ospeed; - ttsetwater(tp); - } - if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) && - cmd != TIOCSETAF) { - if (ISSET(t->c_lflag, ICANON)) - SET(tp->t_lflag, PENDIN); - else { - /* - * XXX we really shouldn't allow toggling - * ICANON while we're in a non-termios line - * discipline. Now we have to worry about - * panicing for a null queue. - */ - if (tp->t_canq.c_cbreserved > 0 && - tp->t_rawq.c_cbreserved > 0) { - catq(&tp->t_rawq, &tp->t_canq); - /* - * XXX the queue limits may be - * different, so the old queue - * swapping method no longer works. - */ - catq(&tp->t_canq, &tp->t_rawq); - } - CLR(tp->t_lflag, PENDIN); - } - ttwakeup(tp); - } - tp->t_iflag = t->c_iflag; - tp->t_oflag = t->c_oflag; - /* - * Make the EXTPROC bit read only. - */ - if (ISSET(tp->t_lflag, EXTPROC)) - SET(t->c_lflag, EXTPROC); - else - CLR(t->c_lflag, EXTPROC); - tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN); - if (t->c_cc[VMIN] != tp->t_cc[VMIN] || - t->c_cc[VTIME] != tp->t_cc[VTIME]) - ttwakeup(tp); - bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); - splx(s); - break; +static int +ttydev_poll(struct cdev *dev, int events, struct thread *td) +{ + struct tty *tp = dev->si_drv1; + int error, revents = 0; + + error = ttydev_enter(tp); + if (error) { + /* Don't return the error here, but the event mask. */ + return (events & + (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); } - case TIOCSETD: { /* set line discipline */ - int t = *(int *)data; - if ((u_int)t >= nlinesw) - return (ENXIO); - if (t == tp->t_line) - return (0); - s = spltty(); - ttyld_close(tp, flag); - tp->t_line = t; - /* XXX: we should use the correct cdev here */ - error = ttyld_open(tp, tp->t_dev); - if (error) { - /* - * If we fail to switch line discipline we cannot - * fall back to the previous, because we can not - * trust that ldisc to open successfully either. - * Fall back to the default ldisc which we know - * will allways succeed. - */ - tp->t_line = TTYDISC; - (void)ttyld_open(tp, tp->t_dev); - } - splx(s); - return (error); - break; + if (events & (POLLIN|POLLRDNORM)) { + /* See if we can read something. */ + if (ttydisc_read_poll(tp) > 0) + revents |= events & (POLLIN|POLLRDNORM); } - case TIOCSTART: /* start output, like ^Q */ - s = spltty(); - if (ISSET(tp->t_state, TS_TTSTOP) || - ISSET(tp->t_lflag, FLUSHO)) { - CLR(tp->t_lflag, FLUSHO); - CLR(tp->t_state, TS_TTSTOP); - ttstart(tp); - } - splx(s); - break; - case TIOCSTI: /* simulate terminal input */ - if ((flag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) - return (EPERM); - if (!isctty(p, tp) && priv_check(td, PRIV_TTY_STI)) - return (EACCES); - s = spltty(); - ttyld_rint(tp, *(u_char *)data); - splx(s); - break; - case TIOCSTOP: /* stop output, like ^S */ - s = spltty(); - if (!ISSET(tp->t_state, TS_TTSTOP)) { - SET(tp->t_state, TS_TTSTOP); - tt_stop(tp, 0); - } - splx(s); - break; - case TIOCSCTTY: /* become controlling tty */ - /* Session ctty vnode pointer set in vnode layer. */ - sx_slock(&proctree_lock); - if (!SESS_LEADER(p) || - ((p->p_session->s_ttyvp || tp->t_session) && - (tp->t_session != p->p_session))) { - sx_sunlock(&proctree_lock); - return (EPERM); - } - tp->t_session = p->p_session; - tp->t_pgrp = p->p_pgrp; - SESS_LOCK(p->p_session); - ttyref(tp); /* ttyrel(): kern_proc.c:pgdelete() */ - p->p_session->s_ttyp = tp; - SESS_UNLOCK(p->p_session); - PROC_LOCK(p); - p->p_flag |= P_CONTROLT; - PROC_UNLOCK(p); - sx_sunlock(&proctree_lock); - break; - case TIOCSPGRP: { /* set pgrp of tty */ - sx_slock(&proctree_lock); - pgrp = pgfind(*(int *)data); - if (!isctty(p, tp)) { - if (pgrp != NULL) - PGRP_UNLOCK(pgrp); - sx_sunlock(&proctree_lock); - return (ENOTTY); - } - if (pgrp == NULL) { - sx_sunlock(&proctree_lock); - return (EPERM); - } - PGRP_UNLOCK(pgrp); - if (pgrp->pg_session != p->p_session) { - sx_sunlock(&proctree_lock); - return (EPERM); - } - sx_sunlock(&proctree_lock); - tp->t_pgrp = pgrp; - break; + if (events & (POLLOUT|POLLWRNORM)) { + /* See if we can write something. */ + if (ttydisc_write_poll(tp) > 0) + revents |= events & (POLLOUT|POLLWRNORM); } - case TIOCSTAT: /* simulate control-T */ - s = spltty(); - ttyinfo(tp); - splx(s); - break; - case TIOCSWINSZ: /* set window size */ - if (bcmp((caddr_t)&tp->t_winsize, data, - sizeof (struct winsize))) { - tp->t_winsize = *(struct winsize *)data; - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGWINCH, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - } - break; - case TIOCSDRAINWAIT: - error = priv_check(td, PRIV_TTY_DRAINWAIT); - if (error) - return (error); - tp->t_timeout = *(int *)data * hz; - wakeup(TSA_OCOMPLETE(tp)); - wakeup(TSA_OLOWAT(tp)); - break; - case TIOCGDRAINWAIT: - *(int *)data = tp->t_timeout / hz; - break; - case TIOCSBRK: - return (tt_break(tp, 1)); - case TIOCCBRK: - return (tt_break(tp, 0)); - default: -#if defined(COMPAT_43TTY) - return (ttcompat(tp, cmd, data, flag)); -#else - return (ENOIOCTL); -#endif + if (tp->t_flags & TF_ZOMBIE) + /* Hangup flag on zombie state. */ + revents |= events & POLLHUP; + + if (revents == 0) { + if (events & (POLLIN|POLLRDNORM)) + selrecord(td, &tp->t_inpoll); + if (events & (POLLOUT|POLLWRNORM)) + selrecord(td, &tp->t_outpoll); } - return (0); -} -int -ttypoll(struct cdev *dev, int events, struct thread *td) -{ - int s; - int revents = 0; - struct tty *tp; - - tp = tty_gettp(dev); - - if (tp == NULL) /* XXX used to return ENXIO, but that means true! */ - return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) - | POLLHUP); + ttydev_leave(tp); - s = spltty(); - if (events & (POLLIN | POLLRDNORM)) { - if (ISSET(tp->t_state, TS_ZOMBIE)) - revents |= (events & (POLLIN | POLLRDNORM)) | - POLLHUP; - else if (ttnread(tp) > 0) - revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(td, &tp->t_rsel); - } - if (events & POLLOUT) { - if (ISSET(tp->t_state, TS_ZOMBIE)) - revents |= POLLHUP; - else if (tp->t_outq.c_cc <= tp->t_olowat && - ISSET(tp->t_state, TS_CONNECTED)) - revents |= events & POLLOUT; - else - selrecord(td, &tp->t_wsel); - } - splx(s); return (revents); } -static struct filterops ttyread_filtops = - { 1, NULL, filt_ttyrdetach, filt_ttyread }; -static struct filterops ttywrite_filtops = - { 1, NULL, filt_ttywdetach, filt_ttywrite }; - -int -ttykqfilter(struct cdev *dev, struct knote *kn) +static int +ttydev_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { - struct tty *tp; - struct knlist *klist; - int s; - - tp = tty_gettp(dev); - if (tp == NULL || (tp->t_state & TS_GONE)) - return (ENODEV); - - switch (kn->kn_filter) { - case EVFILT_READ: - klist = &tp->t_rsel.si_note; - kn->kn_fop = &ttyread_filtops; - break; - case EVFILT_WRITE: - klist = &tp->t_wsel.si_note; - kn->kn_fop = &ttywrite_filtops; - break; - default: - return (EINVAL); - } + struct tty *tp = dev->si_drv1; + int error; - kn->kn_hook = (caddr_t)tp; + /* Handle mmap() through the driver. */ - s = spltty(); - knlist_add(klist, kn, 0); - splx(s); + error = ttydev_enter(tp); + if (error) + return (-1); + error = ttydevsw_mmap(tp, offset, paddr, nprot); + ttydev_leave(tp); - return (0); + return (error); } +/* + * kqueue support. + */ + static void -filt_ttyrdetach(struct knote *kn) +tty_kqops_read_detach(struct knote *kn) { - struct tty *tp = (struct tty *)kn->kn_hook; - int s = spltty(); + struct tty *tp = kn->kn_hook; - knlist_remove(&tp->t_rsel.si_note, kn, 0); - splx(s); + knlist_remove(&tp->t_inpoll.si_note, kn, 0); } static int -filt_ttyread(struct knote *kn, long hint) +tty_kqops_read_event(struct knote *kn, long hint) { - struct tty *tp = (struct tty *)kn->kn_hook; + struct tty *tp = kn->kn_hook; + + tty_lock_assert(tp, MA_OWNED); - kn->kn_data = ttnread(tp); - if ((tp->t_state & TS_GONE) || ISSET(tp->t_state, TS_ZOMBIE)) { + if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { kn->kn_flags |= EV_EOF; return (1); + } else { + kn->kn_data = ttydisc_read_poll(tp); + return (kn->kn_data > 0); } - return (kn->kn_data > 0); } static void -filt_ttywdetach(struct knote *kn) +tty_kqops_write_detach(struct knote *kn) { - struct tty *tp = (struct tty *)kn->kn_hook; - int s = spltty(); + struct tty *tp = kn->kn_hook; - knlist_remove(&tp->t_wsel.si_note, kn, 0); - splx(s); + knlist_remove(&tp->t_outpoll.si_note, kn, 0); } static int -filt_ttywrite(struct knote *kn, long hint) +tty_kqops_write_event(struct knote *kn, long hint) { - struct tty *tp = (struct tty *)kn->kn_hook; + struct tty *tp = kn->kn_hook; + + tty_lock_assert(tp, MA_OWNED); - kn->kn_data = tp->t_outq.c_cc; - if ((tp->t_state & TS_GONE) || ISSET(tp->t_state, TS_ZOMBIE)) + if (tty_gone(tp)) { + kn->kn_flags |= EV_EOF; return (1); - return (kn->kn_data <= tp->t_olowat && - ISSET(tp->t_state, TS_CONNECTED)); + } else { + kn->kn_data = ttydisc_write_poll(tp); + return (kn->kn_data > 0); + } } -/* - * Must be called at spltty(). - */ +static struct filterops tty_kqops_read = + { 1, NULL, tty_kqops_read_detach, tty_kqops_read_event }; +static struct filterops tty_kqops_write = + { 1, NULL, tty_kqops_write_detach, tty_kqops_write_event }; + static int -ttnread(struct tty *tp) +ttydev_kqfilter(struct cdev *dev, struct knote *kn) { - int nread; - - if (ISSET(tp->t_lflag, PENDIN)) - ttypend(tp); - nread = tp->t_canq.c_cc; - if (!ISSET(tp->t_lflag, ICANON)) { - nread += tp->t_rawq.c_cc; - if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0) - nread = 0; + struct tty *tp = dev->si_drv1; + int error; + + error = ttydev_enter(tp); + if (error) + return (error); + + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_hook = tp; + kn->kn_fop = &tty_kqops_read; + knlist_add(&tp->t_inpoll.si_note, kn, 1); + break; + case EVFILT_WRITE: + kn->kn_hook = tp; + kn->kn_fop = &tty_kqops_write; + knlist_add(&tp->t_outpoll.si_note, kn, 1); + break; + default: + error = EINVAL; + break; } - return (nread); + + ttydev_leave(tp); + return (error); } +static struct cdevsw ttydev_cdevsw = { + .d_version = D_VERSION, + .d_open = ttydev_open, + .d_close = ttydev_close, + .d_read = ttydev_read, + .d_write = ttydev_write, + .d_ioctl = ttydev_ioctl, + .d_kqfilter = ttydev_kqfilter, + .d_poll = ttydev_poll, + .d_mmap = ttydev_mmap, + .d_name = "ttydev", + .d_flags = D_TTY, +}; + /* - * Wait for output to drain. + * Init/lock-state devices */ -int -ttywait(struct tty *tp) + +static int +ttyil_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { - int error, s; - - error = 0; - s = spltty(); - while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && - ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) { - tt_oproc(tp); - if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && - ISSET(tp->t_state, TS_CONNECTED)) { - SET(tp->t_state, TS_SO_OCOMPLETE); - error = ttysleep(tp, TSA_OCOMPLETE(tp), - TTOPRI | PCATCH, "ttywai", - tp->t_timeout); - if (error) { - if (error == EWOULDBLOCK) - error = EIO; - break; - } - } else - break; - } - if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY))) - error = EIO; - splx(s); + struct tty *tp = dev->si_drv1; + int error = 0; + + tty_lock(tp); + if (tty_gone(tp)) + error = ENODEV; + tty_unlock(tp); + return (error); } -/* - * Flush if successfully wait. - */ static int -ttywflush(struct tty *tp) +ttyil_close(struct cdev *dev, int flag, int mode, struct thread *td) { - int error; + return (0); +} - if ((error = ttywait(tp)) == 0) - ttyflush(tp, FREAD); - return (error); +static int +ttyil_rdwr(struct cdev *dev, struct uio *uio, int ioflag) +{ + return (ENODEV); } -/* - * Flush tty read and/or write queues, notifying anyone waiting. - */ -void -ttyflush(struct tty *tp, int rw) +static int +ttyil_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, + struct thread *td) { - int s; + struct tty *tp = dev->si_drv1; + int error = 0; - s = spltty(); -#if 0 -again: -#endif - if (rw & FWRITE) { - FLUSHQ(&tp->t_outq); - CLR(tp->t_state, TS_TTSTOP); + tty_lock(tp); + if (tty_gone(tp)) { + error = ENODEV; + goto done; } - tt_stop(tp, rw); - if (rw & FREAD) { - FLUSHQ(&tp->t_canq); - FLUSHQ(&tp->t_rawq); - CLR(tp->t_lflag, PENDIN); - tp->t_rocount = 0; - tp->t_rocol = 0; - CLR(tp->t_state, TS_LOCAL); - ttwakeup(tp); - if (ISSET(tp->t_state, TS_TBLOCK)) { - if (rw & FWRITE) - FLUSHQ(&tp->t_outq); - ttyunblock(tp); - /* - * Don't let leave any state that might clobber the - * next line discipline (although we should do more - * to send the START char). Not clearing the state - * may have caused the "putc to a clist with no - * reserved cblocks" panic/printf. - */ - CLR(tp->t_state, TS_TBLOCK); - -#if 0 /* forget it, sleeping isn't always safe and we don't know when it is */ - if (ISSET(tp->t_iflag, IXOFF)) { - /* - * XXX wait a bit in the hope that the stop - * character (if any) will go out. Waiting - * isn't good since it allows races. This - * will be fixed when the stop character is - * put in a special queue. Don't bother with - * the checks in ttywait() since the timeout - * will save us. - */ - SET(tp->t_state, TS_SO_OCOMPLETE); - ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI, - "ttyfls", hz / 10); - /* - * Don't try sending the stop character again. - */ - CLR(tp->t_state, TS_TBLOCK); - goto again; - } -#endif - } - } - if (rw & FWRITE) { - FLUSHQ(&tp->t_outq); - ttwwakeup(tp); + switch (cmd) { + case TIOCGETA: + /* Obtain terminal flags through tcgetattr(). */ + bcopy(dev->si_drv2, data, sizeof(struct termios)); + break; + case TIOCSETA: + /* Set terminal flags through tcsetattr(). */ + error = priv_check(td, PRIV_TTY_SETA); + if (error) + break; + bcopy(data, dev->si_drv2, sizeof(struct termios)); + return (0); + break; + case TIOCGETD: + *(int *)data = TTYDISC; + break; + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + break; + default: + error = ENOTTY; } - splx(s); + +done: tty_unlock(tp); + return (error); } -/* - * Copy in the default termios characters. - */ -void -termioschars(struct termios *t) +static struct cdevsw ttyil_cdevsw = { + .d_version = D_VERSION, + .d_open = ttyil_open, + .d_close = ttyil_close, + .d_read = ttyil_rdwr, + .d_write = ttyil_rdwr, + .d_ioctl = ttyil_ioctl, + .d_name = "ttyil", + .d_flags = D_TTY, +}; + +static void +tty_init_termios(struct tty *tp) { + struct termios *t = &tp->t_termios_init_in; - bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); + t->c_cflag = TTYDEF_CFLAG; + t->c_iflag = TTYDEF_IFLAG; + t->c_lflag = TTYDEF_LFLAG; + t->c_oflag = TTYDEF_OFLAG; + t->c_ispeed = TTYDEF_SPEED; + t->c_ospeed = TTYDEF_SPEED; + bcopy(ttydefchars, &t->c_cc, sizeof ttydefchars); + + tp->t_termios_init_out = *t; } -/* - * Old interface. - */ void -ttychars(struct tty *tp) +tty_init_console(struct tty *tp, speed_t s) { + struct termios *ti = &tp->t_termios_init_in; + struct termios *to = &tp->t_termios_init_out; + + if (s != 0) { + ti->c_ispeed = ti->c_ospeed = s; + to->c_ispeed = to->c_ospeed = s; + } - termioschars(&tp->t_termios); + ti->c_cflag |= CLOCAL; + to->c_cflag |= CLOCAL; } /* - * Handle input high water. Send stop character for the IXOFF case. Turn - * on our input flow control bit and propagate the changes to the driver. - * XXX the stop character should be put in a special high priority queue. + * Standard device routine implementations, mostly meant for + * pseudo-terminal device drivers. When a driver creates a new terminal + * device class, missing routines are patched. */ -void -ttyblock(struct tty *tp) + +static int +ttydevsw_defopen(struct tty *tp) { - SET(tp->t_state, TS_TBLOCK); - if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && - putc(tp->t_cc[VSTOP], &tp->t_outq) != 0) - CLR(tp->t_state, TS_TBLOCK); /* try again later */ - ttstart(tp); + return (0); } -/* - * Handle input low water. Send start character for the IXOFF case. Turn - * off our input flow control bit and propagate the changes to the driver. - * XXX the start character should be put in a special high priority queue. - */ static void -ttyunblock(struct tty *tp) +ttydevsw_defclose(struct tty *tp) { - - CLR(tp->t_state, TS_TBLOCK); - if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE && - putc(tp->t_cc[VSTART], &tp->t_outq) != 0) - SET(tp->t_state, TS_TBLOCK); /* try again later */ - ttstart(tp); } -#ifdef notyet -/* Not used by any current (i386) drivers. */ -/* - * Restart after an inter-char delay. - */ -void -ttrstrt(void *tp_arg) +static void +ttydevsw_defoutwakeup(struct tty *tp) { - struct tty *tp; - int s; - KASSERT(tp_arg != NULL, ("ttrstrt")); + panic("Terminal device has output, while not implemented"); +} - tp = tp_arg; - s = spltty(); +static void +ttydevsw_definwakeup(struct tty *tp) +{ +} - CLR(tp->t_state, TS_TIMEOUT); - ttstart(tp); +static int +ttydevsw_defioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ - splx(s); + return (ENOIOCTL); } -#endif -int -ttstart(struct tty *tp) +static int +ttydevsw_defparam(struct tty *tp, struct termios *t) { - tt_oproc(tp); + /* Use a fake baud rate, we're not a real device. */ + t->c_ispeed = t->c_ospeed = TTYDEF_SPEED_PSEUDO; + return (0); } -/* - * "close" a line discipline - */ -int -ttylclose(struct tty *tp, int flag) +static int +ttydevsw_defmodem(struct tty *tp, int sigon, int sigoff) { - if (flag & FNONBLOCK || ttywflush(tp)) - ttyflush(tp, FREAD | FWRITE); - return (0); + /* Simulate a carrier to make the TTY layer happy. */ + return (SER_DCD); } -/* - * Handle modem control transition on a tty. - * Flag indicates new state of carrier. - * Returns 0 if the line should be turned off, otherwise 1. - */ -int -ttymodem(struct tty *tp, int flag) +static int +ttydevsw_defmmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, + int nprot) { - if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) { - /* - * MDMBUF: do flow control according to carrier flag - * XXX TS_CAR_OFLOW doesn't do anything yet. TS_TTSTOP - * works if IXON and IXANY are clear. - */ - if (flag) { - CLR(tp->t_state, TS_CAR_OFLOW); - CLR(tp->t_state, TS_TTSTOP); - ttstart(tp); - } else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) { - SET(tp->t_state, TS_CAR_OFLOW); - SET(tp->t_state, TS_TTSTOP); - tt_stop(tp, 0); - } - } else if (flag == 0) { - /* - * Lost carrier. - */ - CLR(tp->t_state, TS_CARR_ON); - if (ISSET(tp->t_state, TS_ISOPEN) && - !ISSET(tp->t_cflag, CLOCAL)) { - SET(tp->t_state, TS_ZOMBIE); - CLR(tp->t_state, TS_CONNECTED); - if (tp->t_session) { - sx_slock(&proctree_lock); - if (tp->t_session && tp->t_session->s_leader) { - struct proc *p; - - p = tp->t_session->s_leader; - PROC_LOCK(p); - psignal(p, SIGHUP); - PROC_UNLOCK(p); - } - sx_sunlock(&proctree_lock); - } - ttyflush(tp, FREAD | FWRITE); - return (0); - } - } else { - /* - * Carrier now on. - */ - SET(tp->t_state, TS_CARR_ON); - if (!ISSET(tp->t_state, TS_ZOMBIE)) - SET(tp->t_state, TS_CONNECTED); - wakeup(TSA_CARR_ON(tp)); - ttwakeup(tp); - ttwwakeup(tp); - } - return (1); + return (-1); } -/* - * Reinput pending characters after state switch - * call at spltty(). - */ static void -ttypend(struct tty *tp) +ttydevsw_deffree(void *softc) { - struct clist tq; - int c; - CLR(tp->t_lflag, PENDIN); - SET(tp->t_state, TS_TYPEN); - /* - * XXX this assumes too much about clist internals. It may even - * fail if the cblock slush pool is empty. We can't allocate more - * cblocks here because we are called from an interrupt handler - * and clist_alloc_cblocks() can wait. - */ - tq = tp->t_rawq; - bzero(&tp->t_rawq, sizeof tp->t_rawq); - tp->t_rawq.c_cbmax = tq.c_cbmax; - tp->t_rawq.c_cbreserved = tq.c_cbreserved; - while ((c = getc(&tq)) >= 0) - ttyinput(c, tp); - CLR(tp->t_state, TS_TYPEN); + panic("Terminal device freed without a free-handler"); } /* - * Process a read call on a tty device. + * TTY allocation and deallocation. TTY devices can be deallocated when + * the driver doesn't use it anymore, when the TTY isn't a session's + * controlling TTY and when the device node isn't opened through devfs. */ -int -ttread(struct tty *tp, struct uio *uio, int flag) + +struct tty * +tty_alloc(struct ttydevsw *tsw, void *sc, struct mtx *mutex) { - struct clist *qp; - int c; - tcflag_t lflag; - cc_t *cc = tp->t_cc; - struct thread *td; - struct proc *p; - int s, first, error = 0; - int has_stime = 0, last_cc = 0; - long slp = 0; /* XXX this should be renamed `timo'. */ - struct timeval stime = { 0, 0 }; - struct pgrp *pg; + struct tty *tp; - td = curthread; - p = td->td_proc; -loop: - s = spltty(); - lflag = tp->t_lflag; - /* - * take pending input first - */ - if (ISSET(lflag, PENDIN)) { - ttypend(tp); - splx(s); /* reduce latency */ - s = spltty(); - lflag = tp->t_lflag; /* XXX ttypend() clobbers it */ + /* Make sure the driver defines all routines. */ +#define PATCH_FUNC(x) do { \ + if (tsw->tsw_ ## x == NULL) \ + tsw->tsw_ ## x = ttydevsw_def ## x; \ +} while (0) + PATCH_FUNC(open); + PATCH_FUNC(close); + PATCH_FUNC(outwakeup); + PATCH_FUNC(inwakeup); + PATCH_FUNC(ioctl); + PATCH_FUNC(param); + PATCH_FUNC(modem); + PATCH_FUNC(mmap); + PATCH_FUNC(free); +#undef PATCH_FUNC + + tp = malloc(sizeof(struct tty), M_TTY, M_WAITOK|M_ZERO); + tp->t_devsw = tsw; + tp->t_softc = sc; + tp->t_flags = tsw->tsw_flags; + + tty_init_termios(tp); + + cv_init(&tp->t_inwait, "tty input"); + cv_init(&tp->t_outwait, "tty output"); + cv_init(&tp->t_bgwait, "tty background"); + cv_init(&tp->t_dcdwait, "tty dcd"); + + TAILQ_INIT(&tp->t_inq.ti_list); + STAILQ_INIT(&tp->t_outq.to_list); + + /* Allow drivers to use a custom mutex to lock the TTY. */ + if (mutex != NULL) { + tp->t_mtx = mutex; + } else { + tp->t_mtx = &tp->t_mtxobj; + mtx_init(&tp->t_mtxobj, "tty lock", NULL, MTX_DEF); } - /* - * Hang process if it's in the background. - */ - if (isbackground(p, tp)) { - splx(s); - sx_slock(&proctree_lock); - PROC_LOCK(p); - if (SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTIN) || - SIGISMEMBER(td->td_sigmask, SIGTTIN) || - (p->p_flag & P_PPWAIT) || p->p_pgrp->pg_jobc == 0) { - PROC_UNLOCK(p); - sx_sunlock(&proctree_lock); - return (EIO); - } - pg = p->p_pgrp; - PROC_UNLOCK(p); - PGRP_LOCK(pg); - sx_sunlock(&proctree_lock); - pgsignal(pg, SIGTTIN, 1); - PGRP_UNLOCK(pg); - error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg2", 0); - if (error) - return (error); - goto loop; - } + knlist_init(&tp->t_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL); + knlist_init(&tp->t_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL); - if (ISSET(tp->t_state, TS_ZOMBIE)) { - splx(s); - return (0); /* EOF */ - } + sx_xlock(&tty_list_sx); + TAILQ_INSERT_TAIL(&tty_list, tp, t_list); + tty_list_count++; + sx_xunlock(&tty_list_sx); - /* - * If canonical, use the canonical queue, - * else use the raw queue. - * - * (should get rid of clists...) - */ - qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq; + return (tp); +} - if (flag & IO_NDELAY) { - if (qp->c_cc > 0) - goto read; - if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) { - splx(s); - return (0); - } - splx(s); - return (EWOULDBLOCK); - } - if (!ISSET(lflag, ICANON)) { - int m = cc[VMIN]; - long t = cc[VTIME]; - struct timeval timecopy; +static void +tty_dealloc(void *arg) +{ + struct tty *tp = arg; - /* - * Check each of the four combinations. - * (m > 0 && t == 0) is the normal read case. - * It should be fairly efficient, so we check that and its - * companion case (m == 0 && t == 0) first. - * For the other two cases, we compute the target sleep time - * into slp. - */ - if (t == 0) { - if (qp->c_cc < m) - goto sleep; - if (qp->c_cc > 0) - goto read; - - /* m, t and qp->c_cc are all 0. 0 is enough input. */ - splx(s); - return (0); - } - t *= 100000; /* time in us */ -#define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \ - ((t1).tv_usec - (t2).tv_usec)) - if (m > 0) { - if (qp->c_cc <= 0) - goto sleep; - if (qp->c_cc >= m) - goto read; - getmicrotime(&timecopy); - if (!has_stime) { - /* first character, start timer */ - has_stime = 1; - stime = timecopy; - slp = t; - } else if (qp->c_cc > last_cc) { - /* got a character, restart timer */ - stime = timecopy; - slp = t; - } else { - /* nothing, check expiration */ - slp = t - diff(timecopy, stime); - if (slp <= 0) - goto read; - } - last_cc = qp->c_cc; - } else { /* m == 0 */ - if (qp->c_cc > 0) - goto read; - getmicrotime(&timecopy); - if (!has_stime) { - has_stime = 1; - stime = timecopy; - slp = t; - } else { - slp = t - diff(timecopy, stime); - if (slp <= 0) { - /* Timed out, but 0 is enough input. */ - splx(s); - return (0); - } - } - } -#undef diff - if (slp != 0) { - struct timeval tv; /* XXX style bug. */ + sx_xlock(&tty_list_sx); + TAILQ_REMOVE(&tty_list, tp, t_list); + tty_list_count--; + sx_xunlock(&tty_list_sx); - tv.tv_sec = slp / 1000000; - tv.tv_usec = slp % 1000000; - slp = tvtohz(&tv); - /* - * XXX bad variable names. slp was the timeout in - * usec. Now it is the timeout in ticks. - */ - } - goto sleep; - } - if (qp->c_cc <= 0) { -sleep: - /* - * There is no input, or not enough input and we can block. - */ - error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, - ISSET(tp->t_state, TS_CONNECTED) ? - "ttyin" : "ttyhup", (int)slp); - splx(s); - if (error == EWOULDBLOCK) - error = 0; - else if (error) - return (error); - /* - * XXX what happens if another process eats some input - * while we are asleep (not just here)? It would be - * safest to detect changes and reset our state variables - * (has_stime and last_cc). - */ - slp = 0; - goto loop; - } -read: - splx(s); - /* - * Input present, check for input mapping and processing. - */ - first = 1; - if (ISSET(lflag, ICANON | ISIG)) - goto slowcase; - for (;;) { - char ibuf[IBUFSIZ]; - int icc; - - icc = imin(uio->uio_resid, IBUFSIZ); - icc = q_to_b(qp, ibuf, icc); - if (icc <= 0) { - if (first) - goto loop; - break; - } - error = uiomove(ibuf, icc, uio); - /* - * XXX if there was an error then we should ungetc() the - * unmoved chars and reduce icc here. - */ - if (error) - break; - if (uio->uio_resid == 0) - break; - first = 0; - } - goto out; -slowcase: - for (;;) { - c = getc(qp); - if (c < 0) { - if (first) - goto loop; - break; - } - /* - * delayed suspend (^Y) - */ - if (CCEQ(cc[VDSUSP], c) && - ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) { - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, SIGTSTP, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if (first) { - error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, - "ttybg3", 0); - if (error) - break; - goto loop; - } - break; - } - /* - * Interpret EOF only in canonical mode. - */ - if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON)) - break; - /* - * Give user character. - */ - error = ureadc(c, uio); - if (error) - /* XXX should ungetc(c, qp). */ - break; - if (uio->uio_resid == 0) - break; - /* - * In canonical mode check for a "break character" - * marking the end of a "line of input". - */ - if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag)) - break; - first = 0; - } + knlist_destroy(&tp->t_inpoll.si_note); + knlist_destroy(&tp->t_outpoll.si_note); -out: - /* - * Look to unblock input now that (presumably) - * the input queue has gone down. - */ - s = spltty(); - if (ISSET(tp->t_state, TS_TBLOCK) && - tp->t_rawq.c_cc + tp->t_canq.c_cc <= tp->t_ilowat) - ttyunblock(tp); - splx(s); + cv_destroy(&tp->t_inwait); + cv_destroy(&tp->t_outwait); + cv_destroy(&tp->t_bgwait); + cv_destroy(&tp->t_dcdwait); - return (error); + if (tp->t_mtx == &tp->t_mtxobj) + mtx_destroy(&tp->t_mtxobj); + ttydevsw_free(tp); + free(tp, M_TTY); } -/* - * Check the output queue on tp for space for a kernel message (from uprintf - * or tprintf). Allow some space over the normal hiwater mark so we don't - * lose messages due to normal flow control, but don't let the tty run amok. - * Sleeps here are not interruptible, but we return prematurely if new signals - * arrive. - */ -int -ttycheckoutq(struct tty *tp, int wait) +static void +tty_rel_free(struct tty *tp) { - int hiwat, s; - sigset_t oldmask; - struct thread *td; - struct proc *p; + struct cdev *dev; - td = curthread; - p = td->td_proc; - hiwat = tp->t_ohiwat; - SIGEMPTYSET(oldmask); - s = spltty(); - if (wait) { - PROC_LOCK(p); - oldmask = td->td_siglist; - PROC_UNLOCK(p); - } - if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100) - while (tp->t_outq.c_cc > hiwat) { - ttstart(tp); - if (tp->t_outq.c_cc <= hiwat) - break; - if (!wait) { - splx(s); - return (0); - } - PROC_LOCK(p); - if (!SIGSETEQ(td->td_siglist, oldmask)) { - PROC_UNLOCK(p); - splx(s); - return (0); - } - PROC_UNLOCK(p); - SET(tp->t_state, TS_SO_OLOWAT); - tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz); - } - splx(s); - return (1); -} + tty_lock_assert(tp, MA_OWNED); -/* - * Process a write call on a tty device. - */ -int -ttwrite(struct tty *tp, struct uio *uio, int flag) -{ - char *cp = NULL; - int cc, ce; - struct thread *td; - struct proc *p; - int i, hiwat, cnt, error, s; - char obuf[OBUFSIZ]; - - hiwat = tp->t_ohiwat; - cnt = uio->uio_resid; - error = 0; - cc = 0; - td = curthread; - p = td->td_proc; -loop: - s = spltty(); - if (ISSET(tp->t_state, TS_ZOMBIE)) { - splx(s); - if (uio->uio_resid == cnt) - error = EIO; - goto out; - } - if (!ISSET(tp->t_state, TS_CONNECTED)) { - if (flag & IO_NDELAY) { - splx(s); - error = EWOULDBLOCK; - goto out; - } - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "ttywdcd", 0); - splx(s); - if (error) - goto out; - goto loop; - } - splx(s); - /* - * Hang the process if it's in the background. - */ - sx_slock(&proctree_lock); - PROC_LOCK(p); - if (isbackground(p, tp) && - ISSET(tp->t_lflag, TOSTOP) && !(p->p_flag & P_PPWAIT) && - !SIGISMEMBER(p->p_sigacts->ps_sigignore, SIGTTOU) && - !SIGISMEMBER(td->td_sigmask, SIGTTOU)) { - if (p->p_pgrp->pg_jobc == 0) { - PROC_UNLOCK(p); - sx_sunlock(&proctree_lock); - error = EIO; - goto out; - } - PROC_UNLOCK(p); - PGRP_LOCK(p->p_pgrp); - sx_sunlock(&proctree_lock); - pgsignal(p->p_pgrp, SIGTTOU, 1); - PGRP_UNLOCK(p->p_pgrp); - error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg4", 0); - if (error) - goto out; - goto loop; - } else { - PROC_UNLOCK(p); - sx_sunlock(&proctree_lock); - } - /* - * Process the user's data in at most OBUFSIZ chunks. Perform any - * output translation. Keep track of high water mark, sleep on - * overflow awaiting device aid in acquiring new space. - */ - while (uio->uio_resid > 0 || cc > 0) { - if (ISSET(tp->t_lflag, FLUSHO)) { - uio->uio_resid = 0; - return (0); - } - if (tp->t_outq.c_cc > hiwat) - goto ovhiwat; - /* - * Grab a hunk of data from the user, unless we have some - * leftover from last time. - */ - if (cc == 0) { - cc = imin(uio->uio_resid, OBUFSIZ); - cp = obuf; - error = uiomove(cp, cc, uio); - if (error) { - cc = 0; - break; - } - } - /* - * If nothing fancy need be done, grab those characters we - * can handle without any of ttyoutput's processing and - * just transfer them to the output q. For those chars - * which require special processing (as indicated by the - * bits in char_type), call ttyoutput. After processing - * a hunk of data, look for FLUSHO so ^O's will take effect - * immediately. - */ - while (cc > 0) { - if (!ISSET(tp->t_oflag, OPOST)) - ce = cc; - else { - ce = cc - scanc((u_int)cc, (u_char *)cp, - char_type, CCLASSMASK); - /* - * If ce is zero, then we're processing - * a special character through ttyoutput. - */ - if (ce == 0) { - tp->t_rocount = 0; - if (ttyoutput(*cp, tp) >= 0) { - /* No Clists, wait a bit. */ - ttstart(tp); - if (flag & IO_NDELAY) { - error = EWOULDBLOCK; - goto out; - } - error = ttysleep(tp, &lbolt, - TTOPRI|PCATCH, - "ttybf1", 0); - if (error) - goto out; - goto loop; - } - cp++; - cc--; - if (ISSET(tp->t_lflag, FLUSHO) || - tp->t_outq.c_cc > hiwat) - goto ovhiwat; - continue; - } - } - /* - * A bunch of normal characters have been found. - * Transfer them en masse to the output queue and - * continue processing at the top of the loop. - * If there are any further characters in this - * <= OBUFSIZ chunk, the first should be a character - * requiring special handling by ttyoutput. - */ - tp->t_rocount = 0; - i = b_to_q(cp, ce, &tp->t_outq); - ce -= i; - tp->t_column += ce; - cp += ce, cc -= ce, tk_nout += ce; - tp->t_outcc += ce; - if (i > 0) { - /* No Clists, wait a bit. */ - ttstart(tp); - if (flag & IO_NDELAY) { - error = EWOULDBLOCK; - goto out; - } - error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, - "ttybf2", 0); - if (error) - goto out; - goto loop; - } - if (ISSET(tp->t_lflag, FLUSHO) || - tp->t_outq.c_cc > hiwat) - break; - } - ttstart(tp); + if (tp->t_sessioncnt != 0 || + (tp->t_flags & (TF_GONE|TF_OPENED)) != TF_GONE) { + /* TTY is still in use. */ + tty_unlock(tp); + return; } -out: - /* - * If cc is nonzero, we leave the uio structure inconsistent, as the - * offset and iov pointers have moved forward, but it doesn't matter - * (the call will either return short or restart with a new uio). - */ - uio->uio_resid += cc; - return (error); -ovhiwat: - ttstart(tp); - s = spltty(); - /* - * This can only occur if FLUSHO is set in t_lflag, - * or if ttstart/oproc is synchronous (or very fast). - */ - if (tp->t_outq.c_cc <= hiwat) { - splx(s); - goto loop; - } - if (flag & IO_NDELAY) { - splx(s); - uio->uio_resid += cc; - return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); - } - SET(tp->t_state, TS_SO_OLOWAT); - error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri", - tp->t_timeout); - splx(s); - if (error == EWOULDBLOCK) - error = EIO; - if (error) - goto out; - goto loop; + tty_freebuffers(tp); + + /* TTY can be deallocated. */ + dev = tp->t_dev; + tp->t_dev = NULL; + tty_unlock(tp); + + destroy_dev_sched_cb(dev, tty_dealloc, tp); } -/* - * Rubout one character from the rawq of tp - * as cleanly as possible. - */ -static void -ttyrub(int c, struct tty *tp) +void +tty_rel_pgrp(struct tty *tp, struct pgrp *pg) { - char *cp; - int savecol; - int tabc, s; + tty_lock_assert(tp, MA_OWNED); - if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) - return; - CLR(tp->t_lflag, FLUSHO); - if (ISSET(tp->t_lflag, ECHOE)) { - if (tp->t_rocount == 0) { - /* - * Screwed by ttwrite; retype - */ - ttyretype(tp); - return; - } - if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) - ttyrubo(tp, 2); - else { - CLR(c, ~TTY_CHARMASK); - switch (CCLASS(c)) { - case ORDINARY: - ttyrubo(tp, 1); - break; - case BACKSPACE: - case CONTROL: - case NEWLINE: - case RETURN: - case VTAB: - if (ISSET(tp->t_lflag, ECHOCTL)) - ttyrubo(tp, 2); - break; - case TAB: - if (tp->t_rocount < tp->t_rawq.c_cc) { - ttyretype(tp); - return; - } - s = spltty(); - savecol = tp->t_column; - SET(tp->t_state, TS_CNTTB); - SET(tp->t_lflag, FLUSHO); - tp->t_column = tp->t_rocol; - cp = tp->t_rawq.c_cf; - if (cp) - tabc = *cp; /* XXX FIX NEXTC */ - for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc)) - ttyecho(tabc, tp); - CLR(tp->t_lflag, FLUSHO); - CLR(tp->t_state, TS_CNTTB); - splx(s); - - /* savecol will now be length of the tab. */ - savecol -= tp->t_column; - tp->t_column += savecol; - if (savecol > 8) - savecol = 8; /* overflow screw */ - while (--savecol >= 0) - (void)ttyoutput('\b', tp); - break; - default: /* XXX */ -#define PANICSTR "ttyrub: would panic c = %d, val = %d\n" - (void)printf(PANICSTR, c, CCLASS(c)); -#ifdef notdef - panic(PANICSTR, c, CCLASS(c)); -#endif - } - } - } else if (ISSET(tp->t_lflag, ECHOPRT)) { - if (!ISSET(tp->t_state, TS_ERASE)) { - SET(tp->t_state, TS_ERASE); - (void)ttyoutput('\\', tp); - } - ttyecho(c, tp); - } else { - ttyecho(tp->t_cc[VERASE], tp); - /* - * This code may be executed not only when an ERASE key - * is pressed, but also when ^U (KILL) or ^W (WERASE) are. - * So, I didn't think it was worthwhile to pass the extra - * information (which would need an extra parameter, - * changing every call) needed to distinguish the ERASE2 - * case from the ERASE. - */ - } - --tp->t_rocount; + if (tp->t_pgrp == pg) + tp->t_pgrp = NULL; } -/* - * Back over cnt characters, erasing them. - */ -static void -ttyrubo(struct tty *tp, int cnt) +void +tty_rel_sess(struct tty *tp, struct session *sess) { + MPASS(tp->t_sessioncnt > 0); - while (cnt-- > 0) { - (void)ttyoutput('\b', tp); - (void)ttyoutput(' ', tp); - (void)ttyoutput('\b', tp); + /* Current session has left. */ + if (tp->t_session == sess) { + tp->t_session = NULL; + MPASS(tp->t_pgrp == NULL); } + tp->t_sessioncnt--; + tty_rel_free(tp); } -/* - * ttyretype -- - * Reprint the rawq line. Note, it is assumed that c_cc has already - * been checked. - */ -static void -ttyretype(struct tty *tp) +void +tty_rel_gone(struct tty *tp) { - char *cp; - int s, c; + MPASS(!tty_gone(tp)); - /* Echo the reprint character. */ - if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) - ttyecho(tp->t_cc[VREPRINT], tp); + /* Simulate carrier removal. */ + ttydisc_modem(tp, 0); - (void)ttyoutput('\n', tp); + /* Wake up misc. blocked threads. */ + cv_broadcast(&tp->t_bgwait); + cv_broadcast(&tp->t_dcdwait); - /* - * XXX - * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE - * BIT OF FIRST CHAR. - */ - s = spltty(); - for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0); - cp != NULL; cp = nextc(&tp->t_canq, cp, &c)) - ttyecho(c, tp); - for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0); - cp != NULL; cp = nextc(&tp->t_rawq, cp, &c)) - ttyecho(c, tp); - CLR(tp->t_state, TS_ERASE); - splx(s); - - tp->t_rocount = tp->t_rawq.c_cc; - tp->t_rocol = 0; + tp->t_flags |= TF_GONE; + tty_rel_free(tp); } /* - * Echo a typed character to the terminal. + * Exposing information about current TTY's through sysctl */ + static void -ttyecho(int c, struct tty *tp) +tty_to_xtty(struct tty *tp, struct xtty *xt) { - - if (!ISSET(tp->t_state, TS_CNTTB)) - CLR(tp->t_lflag, FLUSHO); - if ((!ISSET(tp->t_lflag, ECHO) && - (c != '\n' || !ISSET(tp->t_lflag, ECHONL))) || - ISSET(tp->t_lflag, EXTPROC)) - return; - if (ISSET(tp->t_lflag, ECHOCTL) && - ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') || - ISSET(c, TTY_CHARMASK) == 0177)) { - (void)ttyoutput('^', tp); - CLR(c, ~TTY_CHARMASK); - if (c == 0177) - c = '?'; - else - c += 'A' - 1; - } - (void)ttyoutput(c, tp); + tty_lock_assert(tp, MA_OWNED); + + xt->xt_size = sizeof(struct xtty); + xt->xt_insize = ttyinq_getsize(&tp->t_inq); + xt->xt_incc = ttyinq_bytescanonicalized(&tp->t_inq); + xt->xt_inlc = ttyinq_bytesline(&tp->t_inq); + xt->xt_inlow = tp->t_inlow; + xt->xt_outsize = ttyoutq_getsize(&tp->t_outq); + xt->xt_outcc = ttyoutq_bytesused(&tp->t_outq); + xt->xt_outlow = tp->t_outlow; + xt->xt_column = tp->t_column; + xt->xt_pgid = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; + xt->xt_sid = tp->t_session ? tp->t_session->s_sid : 0; + xt->xt_flags = tp->t_flags; + xt->xt_dev = tp->t_dev ? dev2udev(tp->t_dev) : NODEV; } -/* - * Wake up any readers on a tty. - */ -void -ttwakeup(struct tty *tp) +static int +sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { + unsigned long lsize; + struct xtty *xtlist, *xt; + struct tty *tp; + int error; - if (SEL_WAITING(&tp->t_rsel)) - selwakeuppri(&tp->t_rsel, TTIPRI); - if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) - pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); - wakeup(TSA_HUP_OR_INPUT(tp)); - KNOTE_UNLOCKED(&tp->t_rsel.si_note, 0); + sx_slock(&tty_list_sx); + lsize = tty_list_count * sizeof(struct xtty); + if (lsize == 0) { + sx_sunlock(&tty_list_sx); + return (0); + } + + xtlist = xt = malloc(lsize, M_TEMP, M_WAITOK); + + TAILQ_FOREACH(tp, &tty_list, t_list) { + tty_lock(tp); + tty_to_xtty(tp, xt); + tty_unlock(tp); + xt++; + } + sx_sunlock(&tty_list_sx); + + error = SYSCTL_OUT(req, xtlist, lsize); + free(xtlist, M_TEMP); + return (error); } +SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, + 0, 0, sysctl_kern_ttys, "S,xtty", "List of TTYs"); + /* - * Wake up any writers on a tty. + * Device node creation. Device has been set up, now we can expose it to + * the user. */ + void -ttwwakeup(struct tty *tp) +tty_makedev(struct tty *tp, struct ucred *cred, const char *fmt, ...) { + va_list ap; + struct cdev *dev; + const char *prefix = "tty"; + char name[SPECNAMELEN - 3]; /* for "tty" and "cua". */ + uid_t uid; + gid_t gid; + mode_t mode; - if (SEL_WAITING(&tp->t_wsel) && tp->t_outq.c_cc <= tp->t_olowat) - selwakeuppri(&tp->t_wsel, TTOPRI); - if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) - pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); - if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == - TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { - CLR(tp->t_state, TS_SO_OCOMPLETE); - wakeup(TSA_OCOMPLETE(tp)); + /* Remove "tty" prefix from devices like PTY's. */ + if (tp->t_flags & TF_NOPREFIX) + prefix = ""; + + va_start(ap, fmt); + vsnrprintf(name, sizeof name, 32, fmt, ap); + va_end(ap); + + if (cred == NULL) { + /* System device. */ + uid = UID_ROOT; + gid = GID_WHEEL; + mode = S_IRUSR|S_IWUSR; + } else { + /* User device. */ + uid = cred->cr_ruid; + gid = GID_TTY; + mode = S_IRUSR|S_IWUSR|S_IWGRP; } - if (ISSET(tp->t_state, TS_SO_OLOWAT) && - tp->t_outq.c_cc <= tp->t_olowat) { - CLR(tp->t_state, TS_SO_OLOWAT); - wakeup(TSA_OLOWAT(tp)); + + /* Master call-in device. */ + dev = make_dev_cred(&ttydev_cdevsw, 0, cred, + uid, gid, mode, "%s%s", prefix, name); + dev->si_drv1 = tp; + tp->t_dev = dev; + + /* Slave call-in devices. */ + if (tp->t_flags & TF_INITLOCK) { + dev = make_dev_cred(&ttyil_cdevsw, 0, cred, + uid, gid, mode, "%s%s.init", prefix, name); + dev_depends(tp->t_dev, dev); + dev->si_drv1 = tp; + dev->si_drv2 = &tp->t_termios_init_in; + + dev = make_dev_cred(&ttyil_cdevsw, 0, cred, + uid, gid, mode, "%s%s.lock", prefix, name); + dev_depends(tp->t_dev, dev); + dev->si_drv1 = tp; + dev->si_drv2 = &tp->t_termios_lock_in; + } + + /* Call-out devices. */ + if (tp->t_flags & TF_CALLOUT) { + dev = make_dev_cred(&ttydev_cdevsw, 0, cred, + UID_UUCP, GID_DIALER, 0660, "cua%s", name); + dev_depends(tp->t_dev, dev); + dev->si_drv1 = tp; + + /* Slave call-out devices. */ + if (tp->t_flags & TF_INITLOCK) { + dev = make_dev_cred(&ttyil_cdevsw, 0, cred, + UID_UUCP, GID_DIALER, 0660, "cua%s.init", name); + dev_depends(tp->t_dev, dev); + dev->si_drv1 = tp; + dev->si_drv2 = &tp->t_termios_init_out; + + dev = make_dev_cred(&ttyil_cdevsw, 0, cred, + UID_UUCP, GID_DIALER, 0660, "cua%s.lock", name); + dev_depends(tp->t_dev, dev); + dev->si_drv1 = tp; + dev->si_drv2 = &tp->t_termios_lock_out; + } } - KNOTE_UNLOCKED(&tp->t_wsel.si_note, 0); } /* - * Look up a code for a specified speed in a conversion table; - * used by drivers to map software speed values to hardware parameters. + * Signalling processes. */ -int -ttspeedtab(int speed, struct speedtab *table) + +void +tty_signal_sessleader(struct tty *tp, int sig) { + struct proc *p; - for ( ; table->sp_speed != -1; table++) - if (table->sp_speed == speed) - return (table->sp_code); - return (-1); + tty_lock_assert(tp, MA_OWNED); + MPASS(sig >= 1 && sig < NSIG); + + /* Make signals start output again. */ + tp->t_flags &= ~TF_STOPPED; + + if (tp->t_session != NULL && tp->t_session->s_leader != NULL) { + p = tp->t_session->s_leader; + PROC_LOCK(p); + psignal(p, sig); + PROC_UNLOCK(p); + } } -/* - * Set input and output watermarks and buffer sizes. For input, the - * high watermark is about one second's worth of input above empty, the - * low watermark is slightly below high water, and the buffer size is a - * driver-dependent amount above high water. For output, the watermarks - * are near the ends of the buffer, with about 1 second's worth of input - * between them. All this only applies to the standard line discipline. - */ void -ttsetwater(struct tty *tp) +tty_signal_pgrp(struct tty *tp, int sig) { - int cps, ttmaxhiwat, x; - - /* Input. */ - clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512); - switch (tp->t_ispeedwat) { - case (speed_t)-1: - cps = tp->t_ispeed / 10; - break; - case 0: - /* - * This case is for old drivers that don't know about - * t_ispeedwat. Arrange for them to get the old buffer - * sizes and watermarks. - */ - cps = TTYHOG - 2 * 256; - tp->t_ififosize = 2 * 256; - break; - default: - cps = tp->t_ispeedwat / 10; - break; + tty_lock_assert(tp, MA_OWNED); + MPASS(sig >= 1 && sig < NSIG); + + /* Make signals start output again. */ + tp->t_flags &= ~TF_STOPPED; + + if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) + tty_info(tp); + if (tp->t_pgrp != NULL) { + PGRP_LOCK(tp->t_pgrp); + pgsignal(tp->t_pgrp, sig, 1); + PGRP_UNLOCK(tp->t_pgrp); } - tp->t_ihiwat = cps; - tp->t_ilowat = 7 * cps / 8; - x = cps + tp->t_ififosize; - clist_alloc_cblocks(&tp->t_rawq, x, x); - - /* Output. */ - switch (tp->t_ospeedwat) { - case (speed_t)-1: - cps = tp->t_ospeed / 10; - ttmaxhiwat = 2 * TTMAXHIWAT; - break; - case 0: - cps = tp->t_ospeed / 10; - ttmaxhiwat = TTMAXHIWAT; - break; - default: - cps = tp->t_ospeedwat / 10; - ttmaxhiwat = 8 * TTMAXHIWAT; - break; - } -#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) - tp->t_olowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); - x += cps; - x = CLAMP(x, ttmaxhiwat, TTMINHIWAT); /* XXX clamps are too magic */ - tp->t_ohiwat = roundup(x, CBSIZE); /* XXX for compat */ - x = imax(tp->t_ohiwat, TTMAXHIWAT); /* XXX for compat/safety */ - x += OBUFSIZ + 100; - clist_alloc_cblocks(&tp->t_outq, x, x); -#undef CLAMP } -/* - * Output char to tty; console putchar style. - */ -int -tputchar(int c, struct tty *tp) +void +tty_wakeup(struct tty *tp, int flags) { - int s; + if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) + pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); - s = spltty(); - if (!ISSET(tp->t_state, TS_CONNECTED)) { - splx(s); - return (-1); + if (flags & FWRITE) { + cv_broadcast(&tp->t_outwait); + selwakeup(&tp->t_outpoll); + KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); + } + if (flags & FREAD) { + cv_broadcast(&tp->t_inwait); + selwakeup(&tp->t_inpoll); + KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); } - if (c == '\n') - (void)ttyoutput('\r', tp); - (void)ttyoutput(c, tp); - ttstart(tp); - splx(s); - return (0); } -/* - * Sleep on chan, returning ERESTART if tty changed while we napped and - * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep. If - * the tty is revoked, restarting a pending call will redo validation done - * at the start of the call. - */ int -ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo) +tty_wait(struct tty *tp, struct cv *cv) { int error; - int gen; + int revokecnt = tp->t_revokecnt; + +#if 0 + /* XXX: /dev/console also picks up Giant. */ + tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); +#endif + tty_lock_assert(tp, MA_OWNED); + + error = cv_wait_sig(cv, tp->t_mtx); - gen = tp->t_gen; - error = tsleep(chan, pri, wmesg, timo); - if (tp->t_state & TS_GONE) + /* Restart the system call when we may have been revoked. */ + if (tp->t_revokecnt != revokecnt) + return (ERESTART); + + /* Bail out when the device slipped away. */ + if (tty_gone(tp)) return (ENXIO); - if (error) - return (error); - return (tp->t_gen == gen ? 0 : ERESTART); + + return (error); } -/* - * Gain a reference to a TTY - */ int -ttyref(struct tty *tp) +tty_timedwait(struct tty *tp, struct cv *cv, int hz) { - int i; + int error; + int revokecnt = tp->t_revokecnt; + +#if 0 + /* XXX: /dev/console also picks up Giant. */ + tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); +#endif + tty_lock_assert(tp, MA_OWNED); + + error = cv_timedwait_sig(cv, tp->t_mtx, hz); + + /* Restart the system call when we may have been revoked. */ + if (tp->t_revokecnt != revokecnt) + return (ERESTART); - mtx_lock(&tp->t_mtx); - KASSERT(tp->t_refcnt > 0, - ("ttyref(): tty refcnt is %d (%s)", - tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); - i = ++tp->t_refcnt; - mtx_unlock(&tp->t_mtx); - return (i); + /* Bail out when the device slipped away. */ + if (tty_gone(tp)) + return (ENXIO); + + return (error); } -/* - * Drop a reference to a TTY. - * When reference count drops to zero, we free it. - */ -int -ttyrel(struct tty *tp) +void +tty_flush(struct tty *tp, int flags) { - int i; - - mtx_lock(&tty_list_mutex); - mtx_lock(&tp->t_mtx); - KASSERT(tp->t_refcnt > 0, - ("ttyrel(): tty refcnt is %d (%s)", - tp->t_refcnt, tp->t_dev != NULL ? devtoname(tp->t_dev) : "??")); - i = --tp->t_refcnt; - if (i != 0) { - mtx_unlock(&tp->t_mtx); - mtx_unlock(&tty_list_mutex); - return (i); + if (flags & FWRITE) { + tp->t_flags &= ~TF_HIWAT_OUT; + ttyoutq_flush(&tp->t_outq); + tty_wakeup(tp, FWRITE); + } + if (flags & FREAD) { + tty_hiwat_in_unblock(tp); + ttyinq_flush(&tp->t_inq); + ttydevsw_inwakeup(tp); } - TAILQ_REMOVE(&tty_list, tp, t_list); - mtx_unlock(&tp->t_mtx); - mtx_unlock(&tty_list_mutex); - knlist_destroy(&tp->t_rsel.si_note); - knlist_destroy(&tp->t_wsel.si_note); - mtx_destroy(&tp->t_mtx); - free(tp, M_TTYS); - return (i); } -/* - * Allocate a tty struct. Clists in the struct will be allocated by - * tty_open(). - */ -struct tty * -ttyalloc() +static int +tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td) { - struct tty *tp; - - tp = malloc(sizeof *tp, M_TTYS, M_WAITOK | M_ZERO); - mtx_init(&tp->t_mtx, "tty", NULL, MTX_DEF); + int error; + switch (cmd) { /* - * Set up the initial state + * Modem commands. + * The SER_* and TIOCM_* flags are the same, but one bit + * shifted. I don't know why. */ - tp->t_refcnt = 1; - tp->t_timeout = -1; - tp->t_dtr_wait = 3 * hz; - - ttyinitmode(tp, 0, 0); - bcopy(ttydefchars, tp->t_init_in.c_cc, sizeof tp->t_init_in.c_cc); + case TIOCSDTR: + ttydevsw_modem(tp, SER_DTR, 0); + return (0); + case TIOCCDTR: + ttydevsw_modem(tp, 0, SER_DTR); + return (0); + case TIOCMSET: { + int bits = *(int *)data; + ttydevsw_modem(tp, + (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, + ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); + return (0); + } + case TIOCMBIS: { + int bits = *(int *)data; + ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); + return (0); + } + case TIOCMBIC: { + int bits = *(int *)data; + ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); + return (0); + } + case TIOCMGET: + *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); + return (0); - /* Make callout the same as callin */ - tp->t_init_out = tp->t_init_in; + case FIOASYNC: + if (*(int *)data) + tp->t_flags |= TF_ASYNC; + else + tp->t_flags &= ~TF_ASYNC; + return (0); + case FIONBIO: + /* This device supports non-blocking operation. */ + return (0); + case FIONREAD: + *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq); + return (0); + case FIOSETOWN: + if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) + /* Not allowed to set ownership. */ + return (ENOTTY); - mtx_lock(&tty_list_mutex); - TAILQ_INSERT_TAIL(&tty_list, tp, t_list); - mtx_unlock(&tty_list_mutex); - knlist_init(&tp->t_rsel.si_note, &tp->t_mtx, NULL, NULL, NULL); - knlist_init(&tp->t_wsel.si_note, &tp->t_mtx, NULL, NULL, NULL); - return (tp); -} + /* Temporarily unlock the TTY to set ownership. */ + tty_unlock(tp); + error = fsetown(*(int *)data, &tp->t_sigio); + tty_lock(tp); + return (error); + case FIOGETOWN: + if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) + /* Not allowed to set ownership. */ + return (ENOTTY); -static void -ttypurge(struct cdev *dev) -{ + /* Get ownership. */ + *(int *)data = fgetown(&tp->t_sigio); + return (0); + case TIOCGETA: + /* Obtain terminal flags through tcgetattr(). */ + bcopy(&tp->t_termios, data, sizeof(struct termios)); + return (0); + case TIOCSETA: + case TIOCSETAW: + case TIOCSETAF: { + struct termios *t = data; - if (dev->si_tty == NULL) - return; - ttygone(dev->si_tty); -} + /* + * Who makes up these funny rules? According to POSIX, + * input baud rate is set equal to the output baud rate + * when zero. + */ + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; -/* - * ttycreate() - * - * Create the device entries for this tty thereby opening it for business. - * - * The flags argument controls if "cua" units are created. - * - * The t_sc filed is copied to si_drv1 in the created cdevs. This - * is particularly important for ->t_cioctl() users. - * - * XXX: implement the init and lock devices by cloning. - */ + /* Don't allow invalid flags to be set. */ + if ((t->c_iflag & ~TTYSUP_IFLAG) != 0 || + (t->c_oflag & ~TTYSUP_OFLAG) != 0 || + (t->c_lflag & ~TTYSUP_LFLAG) != 0 || + (t->c_cflag & ~TTYSUP_CFLAG) != 0) + return (EINVAL); -int -ttycreate(struct tty *tp, int flags, const char *fmt, ...) -{ - char namebuf[SPECNAMELEN - 3]; /* XXX space for "tty" */ - struct cdevsw *csw = NULL; - int unit = 0; - va_list ap; - struct cdev *cp; - int i, minor, sminor, sunit; + /* Set terminal flags through tcsetattr(). */ + if (cmd == TIOCSETAW || cmd == TIOCSETAF) { + error = tty_drain(tp); + if (error) + return (error); + if (cmd == TIOCSETAF) + tty_flush(tp, FREAD); + } - mtx_assert(&Giant, MA_OWNED); + /* + * Only call param() when the flags really change. + */ + if ((t->c_cflag & CIGNORE) == 0 && + (tp->t_termios.c_cflag != t->c_cflag || + tp->t_termios.c_ispeed != t->c_ispeed || + tp->t_termios.c_ospeed != t->c_ospeed)) { + error = ttydevsw_param(tp, t); + if (error) + return (error); - if (tty_unit == NULL) - tty_unit = new_unrhdr(0, 0xffff, NULL); + /* XXX: CLOCAL? */ + + tp->t_termios.c_cflag = t->c_cflag; + tp->t_termios.c_ispeed = t->c_ispeed; + tp->t_termios.c_ospeed = t->c_ospeed; - sunit = alloc_unr(tty_unit); - tp->t_devunit = sunit; + /* Baud rate has changed - update watermarks. */ + tty_watermarks(tp); + } - if (csw == NULL) { - csw = &tty_cdevsw; - unit = sunit; - } - KASSERT(csw->d_purge == NULL || csw->d_purge == ttypurge, - ("tty should not have d_purge")); + /* Copy new non-device driver parameters. */ + tp->t_termios.c_iflag = t->c_iflag; + tp->t_termios.c_oflag = t->c_oflag; + tp->t_termios.c_lflag = t->c_lflag; + bcopy(t->c_cc, &tp->t_termios.c_cc, sizeof(t->c_cc)); - csw->d_purge = ttypurge; + ttydisc_optimize(tp); - minor = unit2minor(unit); - sminor = unit2minor(sunit); - va_start(ap, fmt); - i = vsnrprintf(namebuf, sizeof namebuf, 32, fmt, ap); - va_end(ap); - KASSERT(i < sizeof namebuf, ("Too long tty name (%s)", namebuf)); - - cp = make_dev(csw, minor, - UID_ROOT, GID_WHEEL, 0600, "tty%s", namebuf); - tp->t_dev = cp; - tp->t_mdev = cp; - cp->si_tty = tp; - cp->si_drv1 = tp->t_sc; - - cp = make_dev(&ttys_cdevsw, sminor | MINOR_INIT, - UID_ROOT, GID_WHEEL, 0600, "tty%s.init", namebuf); - dev_depends(tp->t_dev, cp); - cp->si_drv1 = tp->t_sc; - cp->si_drv2 = &tp->t_init_in; - cp->si_tty = tp; - - cp = make_dev(&ttys_cdevsw, sminor | MINOR_LOCK, - UID_ROOT, GID_WHEEL, 0600, "tty%s.lock", namebuf); - dev_depends(tp->t_dev, cp); - cp->si_drv1 = tp->t_sc; - cp->si_drv2 = &tp->t_lock_in; - cp->si_tty = tp; - - if (flags & TS_CALLOUT) { - cp = make_dev(csw, minor | MINOR_CALLOUT, - UID_UUCP, GID_DIALER, 0660, "cua%s", namebuf); - dev_depends(tp->t_dev, cp); - cp->si_drv1 = tp->t_sc; - cp->si_tty = tp; - - cp = make_dev(&ttys_cdevsw, sminor | MINOR_CALLOUT | MINOR_INIT, - UID_UUCP, GID_DIALER, 0660, "cua%s.init", namebuf); - dev_depends(tp->t_dev, cp); - cp->si_drv1 = tp->t_sc; - cp->si_drv2 = &tp->t_init_out; - cp->si_tty = tp; - - cp = make_dev(&ttys_cdevsw, sminor | MINOR_CALLOUT | MINOR_LOCK, - UID_UUCP, GID_DIALER, 0660, "cua%s.lock", namebuf); - dev_depends(tp->t_dev, cp); - cp->si_drv1 = tp->t_sc; - cp->si_drv2 = &tp->t_lock_out; - cp->si_tty = tp; + if ((t->c_lflag & ICANON) == 0) { + /* + * When in non-canonical mode, wake up all + * readers. Canonicalize any partial input. VMIN + * and VTIME could also be adjusted. + */ + ttyinq_canonicalize(&tp->t_inq); + tty_wakeup(tp, FREAD); + } + return (0); } + case TIOCGETD: + /* For compatibility - we only support TTYDISC. */ + *(int *)data = TTYDISC; + return (0); + case TIOCGPGRP: + if (!tty_is_ctty(tp, td->td_proc)) + return (ENOTTY); - return (0); -} - -/* - * This function is called when the hardware disappears. We set a flag - * and wake up stuff so all sleeping threads will notice. - */ -void -ttygone(struct tty *tp) -{ - - tp->t_state |= TS_GONE; - if (SEL_WAITING(&tp->t_rsel)) - selwakeuppri(&tp->t_rsel, TTIPRI); - if (SEL_WAITING(&tp->t_wsel)) - selwakeuppri(&tp->t_wsel, TTOPRI); - if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) - pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); - wakeup(&tp->t_dtr_wait); - wakeup(TSA_CARR_ON(tp)); - wakeup(TSA_HUP_OR_INPUT(tp)); - wakeup(TSA_OCOMPLETE(tp)); - wakeup(TSA_OLOWAT(tp)); - KNOTE_UNLOCKED(&tp->t_rsel.si_note, 0); - KNOTE_UNLOCKED(&tp->t_wsel.si_note, 0); - tt_purge(tp); -} - -/* - * ttyfree() - * - * Called when the driver is ready to free the tty structure. - * - * XXX: This shall sleep until all threads have left the driver. - */ -void -ttyfree(struct tty *tp) -{ - struct cdev *dev; - u_int unit; - - mtx_assert(&Giant, MA_OWNED); - ttygone(tp); - unit = tp->t_devunit; - dev = tp->t_mdev; - dev->si_tty = NULL; - tp->t_dev = NULL; - destroy_dev(dev); - ttyrel(tp); - free_unr(tty_unit, unit); -} + if (tp->t_pgrp != NULL) + *(int *)data = tp->t_pgrp->pg_id; + else + *(int *)data = NO_PID; + return (0); + case TIOCGSID: + if (!tty_is_ctty(tp, td->td_proc)) + return (ENOTTY); -static int -sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) -{ - struct tty *tp, *tp2; - struct xtty xt; - int error; + MPASS(tp->t_session); + *(int *)data = tp->t_session->s_sid; + return (0); + case TIOCSCTTY: { + struct proc *p = td->td_proc; - error = 0; - mtx_lock(&tty_list_mutex); - tp = TAILQ_FIRST(&tty_list); - if (tp != NULL) - ttyref(tp); - while (tp != NULL) { - if (tp->t_state & TS_GONE) - goto nexttp; - bzero(&xt, sizeof xt); - xt.xt_size = sizeof xt; -#define XT_COPY(field) xt.xt_##field = tp->t_##field - xt.xt_rawcc = tp->t_rawq.c_cc; - xt.xt_cancc = tp->t_canq.c_cc; - xt.xt_outcc = tp->t_outq.c_cc; - XT_COPY(line); + /* XXX: This looks awful. */ + tty_unlock(tp); + sx_xlock(&proctree_lock); + tty_lock(tp); - /* - * XXX: We hold the tty list lock while doing this to - * work around a race with pty/pts tty destruction. - * They set t_dev to NULL and then call ttyrel() to - * free the structure which will block on the list - * lock before they call destroy_dev() on the cdev - * backing t_dev. - * - * XXX: ttyfree() now does the same since it has been - * fixed to not leak ttys. - */ - if (tp->t_dev != NULL) - xt.xt_dev = dev2udev(tp->t_dev); - XT_COPY(state); - XT_COPY(flags); - XT_COPY(timeout); - if (tp->t_pgrp != NULL) - xt.xt_pgid = tp->t_pgrp->pg_id; - if (tp->t_session != NULL) - xt.xt_sid = tp->t_session->s_sid; - XT_COPY(termios); - XT_COPY(winsize); - XT_COPY(column); - XT_COPY(rocount); - XT_COPY(rocol); - XT_COPY(ififosize); - XT_COPY(ihiwat); - XT_COPY(ilowat); - XT_COPY(ispeedwat); - XT_COPY(ohiwat); - XT_COPY(olowat); - XT_COPY(ospeedwat); -#undef XT_COPY - mtx_unlock(&tty_list_mutex); - error = SYSCTL_OUT(req, &xt, sizeof xt); - if (error != 0) { - ttyrel(tp); - return (error); + if (!SESS_LEADER(p)) { + /* Only the session leader may do this. */ + sx_xunlock(&proctree_lock); + return (EPERM); } - mtx_lock(&tty_list_mutex); -nexttp: tp2 = TAILQ_NEXT(tp, t_list); - if (tp2 != NULL) - ttyref(tp2); - mtx_unlock(&tty_list_mutex); - ttyrel(tp); - tp = tp2; - mtx_lock(&tty_list_mutex); - } - mtx_unlock(&tty_list_mutex); - return (0); -} -SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, - 0, 0, sysctl_kern_ttys, "S,xtty", "All ttys"); -SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, - &tk_nin, 0, "Total TTY in characters"); -SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, - &tk_nout, 0, "Total TTY out characters"); + if (tp->t_session != NULL && tp->t_session == p->p_session) { + /* This is already our controlling TTY. */ + sx_xunlock(&proctree_lock); + return (0); + } -void -nottystop(struct tty *tp, int rw) -{ + if (!SESS_LEADER(p) || p->p_session->s_ttyvp != NULL || + (tp->t_session != NULL && tp->t_session->s_ttyvp != NULL)) { + /* + * There is already a relation between a TTY and + * a session, or the caller is not the session + * leader. + * + * Allow the TTY to be stolen when the vnode is + * NULL, but the reference to the TTY is still + * active. + */ + sx_xunlock(&proctree_lock); + return (EPERM); + } - return; -} + /* Connect the session to the TTY. */ + tp->t_session = p->p_session; + tp->t_session->s_ttyp = tp; + tp->t_sessioncnt++; + sx_xunlock(&proctree_lock); -int -ttyopen(struct cdev *dev, int flag, int mode, struct thread *td) -{ - int error; - int s; - struct tty *tp; + /* Assign foreground process group. */ + tp->t_pgrp = p->p_pgrp; + PROC_LOCK(p); + p->p_flag |= P_CONTROLT; + PROC_UNLOCK(p); - tp = dev->si_tty; + return (0); + } + case TIOCSPGRP: { + struct pgrp *pg; - s = spltty(); - /* - * We jump to this label after all non-interrupted sleeps to pick - * up any changes of the device state. - */ -open_top: - if (tp->t_state & TS_GONE) - return (ENXIO); - error = ttydtrwaitsleep(tp); - if (error) - goto out; - if (tp->t_state & TS_ISOPEN) { /* - * The device is open, so everything has been initialized. - * Handle conflicts. + * XXX: Temporarily unlock the TTY to locate the process + * group. This code would be lot nicer if we would ever + * decompose proctree_lock. */ - if (ISCALLOUT(dev) && !tp->t_actout) - return (EBUSY); - if (tp->t_actout && !ISCALLOUT(dev)) { - if (flag & O_NONBLOCK) - return (EBUSY); - error = tsleep(&tp->t_actout, - TTIPRI | PCATCH, "ttybi", 0); - if (error != 0 || (tp->t_state & TS_GONE)) - goto out; - goto open_top; + tty_unlock(tp); + sx_slock(&proctree_lock); + pg = pgfind(*(int *)data); + if (pg != NULL) + PGRP_UNLOCK(pg); + if (pg == NULL || pg->pg_session != td->td_proc->p_session) { + sx_sunlock(&proctree_lock); + tty_lock(tp); + return (EPERM); } - if (tp->t_state & TS_XCLUDE && priv_check(td, - PRIV_TTY_EXCLUSIVE)) - return (EBUSY); - } else { + tty_lock(tp); + /* - * The device isn't open, so there are no conflicts. - * Initialize it. Initialization is done twice in many - * cases: to preempt sleeping callin opens if we are - * callout, and to complete a callin open after DCD rises. + * Determine if this TTY is the controlling TTY after + * relocking the TTY. */ - tp->t_termios = ISCALLOUT(dev) ? tp->t_init_out : tp->t_init_in; - tp->t_cflag = tp->t_termios.c_cflag; - if (tp->t_modem != NULL) - tt_modem(tp, SER_DTR | SER_RTS, 0); - ++tp->t_wopeners; - error = tt_param(tp, &tp->t_termios); - --tp->t_wopeners; - if (error == 0) - error = tt_open(tp, dev); - if (error != 0) - goto out; - if (ISCALLOUT(dev) || (tt_modem(tp, 0, 0) & SER_DCD)) - ttyld_modem(tp, 1); - } - /* - * Wait for DCD if necessary. - */ - if (!(tp->t_state & TS_CARR_ON) && !ISCALLOUT(dev) - && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { - ++tp->t_wopeners; - error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "ttydcd", 0); - --tp->t_wopeners; - if (error != 0 || (tp->t_state & TS_GONE)) - goto out; - goto open_top; - } - error = ttyld_open(tp, dev); - ttyldoptim(tp); - if (tp->t_state & TS_ISOPEN && ISCALLOUT(dev)) - tp->t_actout = TRUE; -out: - splx(s); - if (!(tp->t_state & TS_ISOPEN) && tp->t_wopeners == 0) - tt_close(tp); - return (error); -} + if (!tty_is_ctty(tp, td->td_proc)) { + sx_sunlock(&proctree_lock); + return (ENOTTY); + } + tp->t_pgrp = pg; + sx_sunlock(&proctree_lock); -int -ttyclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; + /* Wake up the background process groups. */ + cv_broadcast(&tp->t_bgwait); + return (0); + } + case TIOCFLUSH: { + int flags = *(int *)data; - tp = dev->si_tty; - ttyld_close(tp, flag); - ttyldoptim(tp); - tt_close(tp); - tp->t_do_timestamp = 0; - if (tp->t_pps != NULL) - tp->t_pps->ppsparam.mode = 0; - tty_close(tp); - return (0); -} + if (flags == 0) + flags = (FREAD|FWRITE); + else + flags &= (FREAD|FWRITE); + tty_flush(tp, flags); + return (0); + } + case TIOCDRAIN: + /* Drain TTY output. */ + return tty_drain(tp); + case TIOCCONS: + /* Set terminal as console TTY. */ + if (*(int *)data) { + struct nameidata nd; + int vfslocked; -int -ttyread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp; + /* + * XXX: TTY won't slip away, but constty would + * really need to be locked! + */ + tty_unlock(tp); - tp = tty_gettp(dev); + if (constty == tp) { + tty_lock(tp); + return (0); + } + if (constty != NULL) { + tty_lock(tp); + return (EBUSY); + } + /* XXX: allow disconnected constty's to be stolen! */ - if (tp == NULL || (tp->t_state & TS_GONE)) - return (ENODEV); - return (ttyld_read(tp, uio, flag)); -} + /* + * Only allow this to work when the user can + * open /dev/console. + */ + NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF|MPSAFE, + UIO_SYSSPACE, "/dev/console", td); + if ((error = namei(&nd)) != 0) { + tty_lock(tp); + return (error); + } + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); -int -ttywrite(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp; + error = VOP_ACCESS(nd.ni_vp, VREAD, td->td_ucred, td); + vput(nd.ni_vp); + VFS_UNLOCK_GIANT(vfslocked); + if (error) { + tty_lock(tp); + return (error); + } - tp = tty_gettp(dev); + constty_set(tp); + tty_lock(tp); + } else if (constty == tp) { + constty_clear(); + } + return (0); + case TIOCGWINSZ: + /* Obtain window size. */ + bcopy(&tp->t_winsize, data, sizeof(struct winsize)); + return (0); + case TIOCSWINSZ: + /* Set window size. */ + if (bcmp(&tp->t_winsize, data, sizeof(struct winsize)) == 0) + return (0); + bcopy(data, &tp->t_winsize, sizeof(struct winsize)); + tty_signal_pgrp(tp, SIGWINCH); + return (0); + case TIOCEXCL: + tp->t_flags |= TF_EXCLUDE; + return (0); + case TIOCNXCL: + tp->t_flags &= ~TF_EXCLUDE; + return (0); + case TIOCOUTQ: + *(unsigned int *)data = ttyoutq_bytesused(&tp->t_outq); + return (0); + case TIOCSTOP: + tp->t_flags |= TF_STOPPED; + return (0); + case TIOCSTART: + tp->t_flags &= ~TF_STOPPED; + ttydevsw_outwakeup(tp); + return (0); + case TIOCSTAT: + tty_info(tp); + return (0); + } - if (tp == NULL || (tp->t_state & TS_GONE)) - return (ENODEV); - return (ttyld_write(tp, uio, flag)); +#ifdef COMPAT_43TTY + return tty_ioctl_compat(tp, cmd, data, td); +#else /* !COMPAT_43TTY */ + return (ENOIOCTL); +#endif /* COMPAT_43TTY */ } int -ttyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +tty_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td) { - struct tty *tp; - int error; - - tp = dev->si_tty; - - if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { - int cc; - struct termios *dt = (struct termios *)data; - struct termios *lt = - ISCALLOUT(dev) ? &tp->t_lock_out : &tp->t_lock_in; - - dt->c_iflag = (tp->t_iflag & lt->c_iflag) - | (dt->c_iflag & ~lt->c_iflag); - dt->c_oflag = (tp->t_oflag & lt->c_oflag) - | (dt->c_oflag & ~lt->c_oflag); - dt->c_cflag = (tp->t_cflag & lt->c_cflag) - | (dt->c_cflag & ~lt->c_cflag); - dt->c_lflag = (tp->t_lflag & lt->c_lflag) - | (dt->c_lflag & ~lt->c_lflag); - for (cc = 0; cc < NCCS; ++cc) - if (lt->c_cc[cc] != 0) - dt->c_cc[cc] = tp->t_cc[cc]; - if (lt->c_ispeed != 0) - dt->c_ispeed = tp->t_ispeed; - if (lt->c_ospeed != 0) - dt->c_ospeed = tp->t_ospeed; - } + int error; - error = ttyld_ioctl(tp, cmd, data, flag, td); + tty_lock_assert(tp, MA_OWNED); + + if (tty_gone(tp)) + return (ENXIO); + + error = ttydevsw_ioctl(tp, cmd, data, td); if (error == ENOIOCTL) - error = ttioctl(tp, cmd, data, flag); - ttyldoptim(tp); - if (error != ENOIOCTL) - return (error); - return (ENOTTY); -} + error = tty_generic_ioctl(tp, cmd, data, td); -void -ttyldoptim(struct tty *tp) -{ - struct termios *t; - - t = &tp->t_termios; - if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) - && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) - && (!(t->c_iflag & PARMRK) - || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) - && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) - && linesw[tp->t_line]->l_rint == ttyinput) - tp->t_state |= TS_CAN_BYPASS_L_RINT; - else - tp->t_state &= ~TS_CAN_BYPASS_L_RINT; + return (error); } -static void -ttydtrwaitwakeup(void *arg) +dev_t +tty_udev(struct tty *tp) { - struct tty *tp; - - tp = arg; - tp->t_state &= ~TS_DTR_WAIT; - wakeup(&tp->t_dtr_wait); + if (tp->t_dev) + return dev2udev(tp->t_dev); + else + return NODEV; } - -void -ttydtrwaitstart(struct tty *tp) +int +tty_checkoutq(struct tty *tp) { - if (tp->t_dtr_wait == 0) - return; - if (tp->t_state & TS_DTR_WAIT) - return; - timeout(ttydtrwaitwakeup, tp, tp->t_dtr_wait); - tp->t_state |= TS_DTR_WAIT; + /* 256 bytes should be enough to print a log message. */ + return (ttyoutq_bytesleft(&tp->t_outq) >= 256); } -int -ttydtrwaitsleep(struct tty *tp) +void +tty_hiwat_in_block(struct tty *tp) { - int error; - error = 0; - while (error == 0) { - if (tp->t_state & TS_GONE) - error = ENXIO; - else if (!(tp->t_state & TS_DTR_WAIT)) - break; - else - error = tsleep(&tp->t_dtr_wait, TTIPRI | PCATCH, - "dtrwait", 0); + if ((tp->t_flags & TF_HIWAT_IN) == 0 && + tp->t_termios.c_iflag & IXOFF && + tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { + /* + * Input flow control. Only enter the high watermark when we + * can successfully store the VSTOP character. + */ + if (ttyoutq_write_nofrag(&tp->t_outq, + &tp->t_termios.c_cc[VSTOP], 1) == 0) + tp->t_flags |= TF_HIWAT_IN; + } else { + /* No input flow control. */ + tp->t_flags |= TF_HIWAT_IN; } - return (error); } -static int -ttysopen(struct cdev *dev, int flag, int mode, struct thread *td) +void +tty_hiwat_in_unblock(struct tty *tp) { - struct tty *tp; - - tp = dev->si_tty; - KASSERT(tp != NULL, - ("ttysopen(): no tty pointer on device (%s)", devtoname(dev))); - if (tp->t_state & TS_GONE) - return (ENODEV); - return (0); -} -static int -ttysclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ + if ((tp->t_flags & TF_HIWAT_IN) == 0 && + tp->t_termios.c_iflag & IXOFF && + tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { + /* + * Input flow control. Only leave the high watermark when we + * can successfully store the VSTART character. + */ + if (ttyoutq_write_nofrag(&tp->t_outq, + &tp->t_termios.c_cc[VSTART], 1) == 0) + tp->t_flags &= ~TF_HIWAT_IN; + } else { + /* No input flow control. */ + tp->t_flags &= ~TF_HIWAT_IN; + } - return (0); + if (!tty_gone(tp)) + ttydevsw_inwakeup(tp); } -static int -ttysrdwr(struct cdev *dev, struct uio *uio, int flag) -{ +#include "opt_ddb.h" +#ifdef DDB +#include <ddb/ddb.h> - return (ENODEV); -} +static struct { + int flag; + char val; +} ttystates[] = { +#if 0 + { TF_NOPREFIX, 'N' }, +#endif + { TF_INITLOCK, 'I' }, + { TF_CALLOUT, 'C' }, + + /* Keep these together -> 'Oi' and 'Oo'. */ + { TF_OPENED, 'O' }, + { TF_OPENED_IN, 'i' }, + { TF_OPENED_OUT,'o' }, + + { TF_GONE, 'G' }, + { TF_OPENCLOSE, 'B' }, + { TF_ASYNC, 'Y' }, + { TF_LITERAL, 'L' }, + + /* Keep these together -> 'Hi' and 'Ho'. */ + { TF_HIWAT, 'H' }, + { TF_HIWAT_IN, 'i' }, + { TF_HIWAT_OUT, 'o' }, + + { TF_STOPPED, 'S' }, + { TF_EXCLUDE, 'X' }, + { TF_BYPASS, 'l' }, + { TF_ZOMBIE, 'Z' }, + + { 0, '\0' }, +}; -static int -ttysioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +/* DDB command to show TTY statistics. */ +DB_SHOW_COMMAND(ttys, db_show_ttys) { - struct tty *tp; - int error; - struct termios *ct; - - tp = dev->si_tty; - KASSERT(tp != NULL, - ("ttysopen(): no tty pointer on device (%s)", devtoname(dev))); - if (tp->t_state & TS_GONE) - return (ENODEV); - ct = dev->si_drv2; - switch (cmd) { - case TIOCSETA: - error = priv_check(td, PRIV_TTY_SETA); - if (error != 0) - return (error); - *ct = *(struct termios *)data; - return (0); - case TIOCGETA: - *(struct termios *)data = *ct; - return (0); - case TIOCGETD: - *(int *)data = TTYDISC; - return (0); - case TIOCGWINSZ: - bzero(data, sizeof(struct winsize)); - return (0); - default: - if (tp->t_cioctl != NULL) - return(tp->t_cioctl(dev, cmd, data, flag, td)); - return (ENOTTY); + struct tty *tp; + size_t isiz, osiz; + int i, j; + + /* Make the output look like `pstat -t'. */ + db_printf(" LINE INQ CAN LIN LOW OUTQ USE LOW " + "COL SESS PGID STATE\n"); + + TAILQ_FOREACH(tp, &tty_list, t_list) { + isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE; + osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE; + + db_printf("%10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d %5d ", + tty_devname(tp), + isiz, + tp->t_inq.ti_linestart - tp->t_inq.ti_begin, + tp->t_inq.ti_end - tp->t_inq.ti_linestart, + isiz - tp->t_inlow, + osiz, + tp->t_outq.to_end - tp->t_outq.to_begin, + osiz - tp->t_outlow, + tp->t_column, + tp->t_session ? tp->t_session->s_sid : 0, + tp->t_pgrp ? tp->t_pgrp->pg_id : 0); + + /* Flag bits. */ + for (i = j = 0; ttystates[i].flag; i++) + if (tp->t_flags & ttystates[i].flag) { + db_printf("%c", ttystates[i].val); + j++; + } + if (j == 0) + db_printf("-"); + db_printf("\n"); } } - -/* - * Initialize a tty to sane modes. - */ -void -ttyinitmode(struct tty *tp, int echo, int speed) -{ - - if (speed == 0) - speed = TTYDEF_SPEED; - tp->t_init_in.c_iflag = TTYDEF_IFLAG; - tp->t_init_in.c_oflag = TTYDEF_OFLAG; - tp->t_init_in.c_cflag = TTYDEF_CFLAG; - if (echo) - tp->t_init_in.c_lflag = TTYDEF_LFLAG_ECHO; - else - tp->t_init_in.c_lflag = TTYDEF_LFLAG_NOECHO; - - tp->t_init_in.c_ispeed = tp->t_init_in.c_ospeed = speed; - termioschars(&tp->t_init_in); - tp->t_init_out = tp->t_init_in; - tp->t_termios = tp->t_init_in; -} - -/* - * Use more "normal" termios paramters for consoles. - */ -void -ttyconsolemode(struct tty *tp, int speed) -{ - - if (speed == 0) - speed = TTYDEF_SPEED; - ttyinitmode(tp, 1, speed); - tp->t_init_in.c_cflag |= CLOCAL; - tp->t_lock_out.c_cflag = tp->t_lock_in.c_cflag = CLOCAL; - tp->t_lock_out.c_ispeed = tp->t_lock_out.c_ospeed = - tp->t_lock_in.c_ispeed = tp->t_lock_in.c_ospeed = speed; - tp->t_init_out = tp->t_init_in; - tp->t_termios = tp->t_init_in; - ttsetwater(tp); -} - -/* - * Record the relationship between the serial ports notion of modem control - * signals and the one used in certain ioctls in a way the compiler can enforce - * XXX: We should define TIOCM_* in terms of SER_ if we can limit the - * XXX: consequences of the #include work that would take. - */ -CTASSERT(SER_DTR == TIOCM_DTR / 2); -CTASSERT(SER_RTS == TIOCM_RTS / 2); -CTASSERT(SER_STX == TIOCM_ST / 2); -CTASSERT(SER_SRX == TIOCM_SR / 2); -CTASSERT(SER_CTS == TIOCM_CTS / 2); -CTASSERT(SER_DCD == TIOCM_DCD / 2); -CTASSERT(SER_RI == TIOCM_RI / 2); -CTASSERT(SER_DSR == TIOCM_DSR / 2); - +#endif /* DDB */ diff --git a/sys/kern/tty_compat.c b/sys/kern/tty_compat.c index 4e2c16f..6736cfb 100644 --- a/sys/kern/tty_compat.c +++ b/sys/kern/tty_compat.c @@ -45,6 +45,11 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/sysctl.h> +struct speedtab { + int sp_speed; /* Speed. */ + int sp_code; /* Code. */ +}; + static int ttcompatgetflags(struct tty *tp); static void ttcompatsetflags(struct tty *tp, struct termios *t); static void ttcompatsetlflags(struct tty *tp, struct termios *t); @@ -102,16 +107,18 @@ ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term) if ((speed = sg->sg_ispeed) > MAX_SPEED || speed < 0) return(EINVAL); - else if (speed != ttcompatspeedtab(tp->t_ispeed, compatspeeds)) + else if (speed != ttcompatspeedtab(tp->t_termios.c_ispeed, + compatspeeds)) term->c_ispeed = compatspcodes[speed]; else - term->c_ispeed = tp->t_ispeed; + term->c_ispeed = tp->t_termios.c_ispeed; if ((speed = sg->sg_ospeed) > MAX_SPEED || speed < 0) return(EINVAL); - else if (speed != ttcompatspeedtab(tp->t_ospeed, compatspeeds)) + else if (speed != ttcompatspeedtab(tp->t_termios.c_ospeed, + compatspeeds)) term->c_ospeed = compatspcodes[speed]; else - term->c_ospeed = tp->t_ospeed; + term->c_ospeed = tp->t_termios.c_ospeed; term->c_cc[VERASE] = sg->sg_erase; term->c_cc[VKILL] = sg->sg_kill; tp->t_flags = (tp->t_flags&0xffff0000) | (sg->sg_flags&0xffff); @@ -171,7 +178,7 @@ ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term) /*ARGSUSED*/ int -ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) +tty_ioctl_compat(struct tty *tp, u_long com, caddr_t data, struct thread *td) { switch (com) { case TIOCSETP: @@ -187,17 +194,19 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) term = tp->t_termios; if ((error = ttsetcompat(tp, &com, data, &term)) != 0) return error; - return ttioctl(tp, com, &term, flag); + return tty_ioctl(tp, com, &term, td); } case TIOCGETP: { struct sgttyb *sg = (struct sgttyb *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; - sg->sg_ospeed = ttcompatspeedtab(tp->t_ospeed, compatspeeds); - if (tp->t_ispeed == 0) + sg->sg_ospeed = ttcompatspeedtab(tp->t_termios.c_ospeed, + compatspeeds); + if (tp->t_termios.c_ispeed == 0) sg->sg_ispeed = sg->sg_ospeed; else - sg->sg_ispeed = ttcompatspeedtab(tp->t_ispeed, compatspeeds); + sg->sg_ispeed = ttcompatspeedtab(tp->t_termios.c_ispeed, + compatspeeds); sg->sg_erase = cc[VERASE]; sg->sg_kill = cc[VKILL]; sg->sg_flags = tp->t_flags = ttcompatgetflags(tp); @@ -205,7 +214,7 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) } case TIOCGETC: { struct tchars *tc = (struct tchars *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; tc->t_intrc = cc[VINTR]; tc->t_quitc = cc[VQUIT]; @@ -217,7 +226,7 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) } case TIOCGLTC: { struct ltchars *ltc = (struct ltchars *)data; - cc_t *cc = tp->t_cc; + cc_t *cc = tp->t_termios.c_cc; ltc->t_suspc = cc[VSUSP]; ltc->t_dsuspc = cc[VDSUSP]; @@ -237,19 +246,19 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) break; case OTIOCGETD: - *(int *)data = tp->t_line ? tp->t_line : 2; + *(int *)data = 2; break; case OTIOCSETD: { int ldisczero = 0; - return (ttioctl(tp, TIOCSETD, - *(int *)data == 2 ? (caddr_t)&ldisczero : data, flag)); + return (tty_ioctl(tp, TIOCSETD, + *(int *)data == 2 ? (caddr_t)&ldisczero : data, td)); } case OTIOCCONS: *(int *)data = 1; - return (ttioctl(tp, TIOCCONS, data, flag)); + return (tty_ioctl(tp, TIOCCONS, data, td)); default: return (ENOIOCTL); @@ -260,10 +269,10 @@ ttcompat(struct tty *tp, u_long com, caddr_t data, int flag) static int ttcompatgetflags(struct tty *tp) { - tcflag_t iflag = tp->t_iflag; - tcflag_t lflag = tp->t_lflag; - tcflag_t oflag = tp->t_oflag; - tcflag_t cflag = tp->t_cflag; + tcflag_t iflag = tp->t_termios.c_iflag; + tcflag_t lflag = tp->t_termios.c_lflag; + tcflag_t oflag = tp->t_termios.c_oflag; + tcflag_t cflag = tp->t_termios.c_cflag; int flags = 0; if (iflag&IXOFF) @@ -299,7 +308,7 @@ ttcompatgetflags(struct tty *tp) flags |= MDMBUF; if ((cflag&HUPCL) == 0) flags |= NOHANG; - if (oflag&OXTABS) + if (oflag&TAB3) flags |= XTABS; if (lflag&ECHOE) flags |= CRTERA|CRTBS; @@ -334,9 +343,9 @@ ttcompatsetflags(struct tty *tp, struct termios *t) iflag |= BRKINT|IXON|IMAXBEL; lflag |= ISIG|IEXTEN|ECHOCTL; /* XXX was echoctl on ? */ if (flags & XTABS) - oflag |= OXTABS; + oflag |= TAB3; else - oflag &= ~OXTABS; + oflag &= ~TAB3; if (flags & CBREAK) lflag &= ~ICANON; else diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c deleted file mode 100644 index 77a9593..0000000 --- a/sys/kern/tty_conf.c +++ /dev/null @@ -1,205 +0,0 @@ -/*- - * Copyright (c) 2004 Poul-Henning Kamp. All rights reserved. - * Copyright (c) 1982, 1986, 1991, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)tty_conf.c 8.4 (Berkeley) 1/21/94 - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "opt_compat.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/tty.h> -#include <sys/conf.h> - -#ifndef MAXLDISC -#define MAXLDISC 9 -#endif - -static l_open_t l_noopen; -static l_close_t l_noclose; -static l_rint_t l_norint; -static l_start_t l_nostart; - -/* - * XXX it probably doesn't matter what the entries other than the l_open - * entry are here. The l_nullioctl and ttymodem entries still look fishy. - * Reconsider the removal of nullmodem anyway. It was too much like - * ttymodem, but a completely null version might be useful. - */ - -static struct linesw nodisc = { - .l_open = l_noopen, - .l_close = l_noclose, - .l_read = l_noread, - .l_write = l_nowrite, - .l_ioctl = l_nullioctl, - .l_rint = l_norint, - .l_start = l_nostart, - .l_modem = ttymodem -}; - -static struct linesw termios_disc = { - .l_open = tty_open, - .l_close = ttylclose, - .l_read = ttread, - .l_write = ttwrite, - .l_ioctl = l_nullioctl, - .l_rint = ttyinput, - .l_start = ttstart, - .l_modem = ttymodem -}; - -#ifdef COMPAT_43 -# define ntty_disc termios_disc -#else -# define ntty_disc nodisc -#endif - -struct linesw *linesw[MAXLDISC] = { - &termios_disc, /* 0 - termios */ - &nodisc, /* 1 - defunct */ - &ntty_disc, /* 2 - NTTYDISC */ - &nodisc, /* 3 - loadable */ - &nodisc, /* 4 - SLIPDISC */ - &nodisc, /* 5 - PPPDISC */ - &nodisc, /* 6 - NETGRAPHDISC */ - &nodisc, /* 7 - loadable */ - &nodisc, /* 8 - loadable */ -}; - -int nlinesw = sizeof (linesw) / sizeof (linesw[0]); - -#define LOADABLE_LDISC 7 - -/* - * ldisc_register: Register a line discipline. - * - * discipline: Index for discipline to load, or LDISC_LOAD for us to choose. - * linesw_p: Pointer to linesw_p. - * - * Returns: Index used or -1 on failure. - */ - -int -ldisc_register(int discipline, struct linesw *linesw_p) -{ - int slot = -1; - - if (discipline == LDISC_LOAD) { - int i; - for (i = LOADABLE_LDISC; i < MAXLDISC; i++) - if (linesw[i] == &nodisc) { - slot = i; - break; - } - } else if (discipline >= 0 && discipline < MAXLDISC) { - slot = discipline; - } - - if (slot != -1 && linesw_p) - linesw[slot] = linesw_p; - - return slot; -} - -/* - * ldisc_deregister: Deregister a line discipline obtained with - * ldisc_register. - * - * discipline: Index for discipline to unload. - */ - -void -ldisc_deregister(int discipline) -{ - - if (discipline < MAXLDISC) - linesw[discipline] = &nodisc; -} - -/* - * "no" and "null" versions of line discipline functions - */ - -static int -l_noopen(struct cdev *dev, struct tty *tp) -{ - - return (ENODEV); -} - -static int -l_noclose(struct tty *tp, int flag) -{ - - return (ENODEV); -} - -int -l_noread(struct tty *tp, struct uio *uio, int flag) -{ - - return (ENODEV); -} - -int -l_nowrite(struct tty *tp, struct uio *uio, int flag) -{ - - return (ENODEV); -} - -static int -l_norint(int c, struct tty *tp) -{ - - return (ENODEV); -} - -static int -l_nostart(struct tty *tp) -{ - - return (ENODEV); -} - -int -l_nullioctl(struct tty *tp, u_long cmd, char *data, int flags, struct thread *td) -{ - - return (ENOIOCTL); -} diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c index ff57d82..81e1708 100644 --- a/sys/kern/tty_cons.c +++ b/sys/kern/tty_cons.c @@ -711,9 +711,18 @@ constty_timeout(void *arg) { int c; - while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { - if (tputchar(c, constty) < 0) - constty = NULL; + if (constty != NULL) { + tty_lock(constty); + while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { + if (tty_putchar(constty, c) < 0) { + tty_unlock(constty); + constty = NULL; + break; + } + } + + if (constty != NULL) + tty_unlock(constty); } if (constty != NULL) { callout_reset(&conscallout, hz / constty_wakeups_per_second, diff --git a/sys/kern/tty_info.c b/sys/kern/tty_info.c index 7da932d..de06775 100644 --- a/sys/kern/tty_info.c +++ b/sys/kern/tty_info.c @@ -211,7 +211,7 @@ proc_compare(struct proc *p1, struct proc *p2) * Report on state of foreground process group. */ void -ttyinfo(struct tty *tp) +tty_info(struct tty *tp) { struct timeval utime, stime; struct proc *p, *pick; @@ -223,32 +223,27 @@ ttyinfo(struct tty *tp) char comm[MAXCOMLEN + 1]; struct rusage ru; - if (ttycheckoutq(tp,0) == 0) + tty_lock_assert(tp, MA_OWNED); + + if (tty_checkoutq(tp) == 0) return; /* Print load average. */ load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", load / 100, load % 100); - /* - * On return following a ttyprintf(), we set tp->t_rocount to 0 so - * that pending input will be retyped on BS. - */ if (tp->t_session == NULL) { ttyprintf(tp, "not a controlling terminal\n"); - tp->t_rocount = 0; return; } if (tp->t_pgrp == NULL) { ttyprintf(tp, "no foreground process group\n"); - tp->t_rocount = 0; return; } PGRP_LOCK(tp->t_pgrp); if (LIST_EMPTY(&tp->t_pgrp->pg_members)) { PGRP_UNLOCK(tp->t_pgrp); ttyprintf(tp, "empty foreground process group\n"); - tp->t_rocount = 0; return; } @@ -313,5 +308,4 @@ ttyinfo(struct tty *tp) (long)utime.tv_sec, utime.tv_usec / 10000, (long)stime.tv_sec, stime.tv_usec / 10000, pctcpu / 100, rss); - tp->t_rocount = 0; } diff --git a/sys/kern/tty_inq.c b/sys/kern/tty_inq.c new file mode 100644 index 0000000..b93dde6 --- /dev/null +++ b/sys/kern/tty_inq.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/uio.h> + +#include <vm/uma.h> + +/* + * TTY input queue buffering. + * + * Unlike the output queue, the input queue has more features that are + * needed to properly implement various features offered by the TTY + * interface: + * + * - Data can be removed from the tail of the queue, which is used to + * implement backspace. + * - Once in a while, input has to be `canonicalized'. When ICANON is + * turned on, this will be done after a CR has been inserted. + * Otherwise, it should be done after any character has been inserted. + * - The input queue can store one bit per byte, called the quoting bit. + * This bit is used by TTYDISC to make backspace work on quoted + * characters. + * + * In most cases, there is probably less input than output, so unlike + * the outq, we'll stick to 128 byte blocks here. + */ + +/* Statistics. */ +static long ttyinq_nfast = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_inq_nfast, CTLFLAG_RD, + &ttyinq_nfast, 0, "Unbuffered reads to userspace on input"); +static long ttyinq_nslow = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_inq_nslow, CTLFLAG_RD, + &ttyinq_nslow, 0, "Buffered reads to userspace on input"); + +#define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE) +#define BMSIZE 32 +#define GETBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE))) +#define SETBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE))) +#define CLRBIT(tib,boff) \ + ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE))) + +struct ttyinq_block { + TAILQ_ENTRY(ttyinq_block) tib_list; + uint32_t tib_quotes[TTYINQ_QUOTESIZE]; + char tib_data[TTYINQ_DATASIZE]; +}; + +static uma_zone_t ttyinq_zone; + +void +ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t size) +{ + unsigned int nblocks; + struct ttyinq_block *tib; + + nblocks = howmany(size, TTYINQ_DATASIZE); + + while (nblocks > ti->ti_nblocks) { + /* + * List is getting bigger. + * Add new blocks to the tail of the list. + * + * We must unlock the TTY temporarily, because we need + * to allocate memory. This won't be a problem, because + * in the worst case, another thread ends up here, which + * may cause us to allocate too many blocks, but this + * will be caught by the loop below. + */ + tty_unlock(tp); + tib = uma_zalloc(ttyinq_zone, M_WAITOK); + tty_lock(tp); + + if (tty_gone(tp)) + return; + + TAILQ_INSERT_TAIL(&ti->ti_list, tib, tib_list); + ti->ti_nblocks++; + } + + while (nblocks < ti->ti_nblocks) { + /* + * List is getting smaller. Remove unused blocks at the + * end. This means we cannot guarantee this routine + * shrinks buffers properly, when we need to reclaim + * more space than there is available. + * + * XXX TODO: Two solutions here: + * - Throw data away + * - Temporarily hit the watermark until enough data has + * been flushed, so we can remove the blocks. + */ + + if (ti->ti_end == 0) + tib = TAILQ_FIRST(&ti->ti_list); + else + tib = TAILQ_NEXT(ti->ti_lastblock, tib_list); + if (tib == NULL) + break; + TAILQ_REMOVE(&ti->ti_list, tib, tib_list); + uma_zfree(ttyinq_zone, tib); + ti->ti_nblocks--; + } +} + +int +ttyinq_read_uio(struct ttyinq *ti, struct tty *tp, struct uio *uio, + size_t rlen, size_t flen) +{ + + MPASS(rlen <= uio->uio_resid); + + while (rlen > 0) { + int error; + struct ttyinq_block *tib; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (ti->ti_begin == ti->ti_linestart) + return (0); + tib = TAILQ_FIRST(&ti->ti_list); + if (tib == NULL) + return (0); + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = ti->ti_begin; + cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), + TTYINQ_DATASIZE); + clen = cend - cbegin; + MPASS(clen >= flen); + rlen -= clen; + + /* + * We can prevent buffering in some cases: + * - We need to read the block until the end. + * - We don't need to read the block until the end, but + * there is no data beyond it, which allows us to move + * the write pointer to a new block. + */ + if (cend == TTYINQ_DATASIZE || cend == ti->ti_end) { + atomic_add_long(&ttyinq_nfast, 1); + + /* + * Fast path: zero copy. Remove the first block, + * so we can unlock the TTY temporarily. + */ + TAILQ_REMOVE(&ti->ti_list, tib, tib_list); + ti->ti_nblocks--; + ti->ti_begin = 0; + + /* + * Because we remove the first block, we must + * fix up the block offsets. + */ +#define CORRECT_BLOCK(t) do { \ + if (t <= TTYINQ_DATASIZE) { \ + t = 0; \ + } else { \ + t -= TTYINQ_DATASIZE; \ + } \ +} while (0) + CORRECT_BLOCK(ti->ti_linestart); + CORRECT_BLOCK(ti->ti_reprint); + CORRECT_BLOCK(ti->ti_end); +#undef CORRECT_BLOCK + + /* + * Temporary unlock and copy the data to + * userspace. We may need to flush trailing + * bytes, like EOF characters. + */ + tty_unlock(tp); + error = uiomove(tib->tib_data + cbegin, + clen - flen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* Something went bad - discard this block. */ + uma_zfree(ttyinq_zone, tib); + return (ENXIO); + } + /* Block can now be readded to the list. */ + /* + * XXX: we could remove the blocks here when the + * queue was shrunk, but still in use. See + * ttyinq_setsize(). + */ + TAILQ_INSERT_TAIL(&ti->ti_list, tib, tib_list); + ti->ti_nblocks++; + if (error != 0) + return (error); + } else { + char ob[TTYINQ_DATASIZE - 1]; + atomic_add_long(&ttyinq_nslow, 1); + + /* + * Slow path: store data in a temporary buffer. + */ + memcpy(ob, tib->tib_data + cbegin, clen - flen); + ti->ti_begin += clen; + MPASS(ti->ti_begin < TTYINQ_DATASIZE); + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(ob, clen - flen, uio); + tty_lock(tp); + + if (error != 0) + return (error); + if (tty_gone(tp)) + return (ENXIO); + } + } + + return (0); +} + +static __inline void +ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset, + size_t length, int value) +{ + + if (value) { + /* Set the bits. */ + for (; length > 0; length--, offset++) + SETBIT(tib, offset); + } else { + /* Unset the bits. */ + for (; length > 0; length--, offset++) + CLRBIT(tib, offset); + } +} + +size_t +ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes, int quote) +{ + const char *cbuf = buf; + struct ttyinq_block *tib; + unsigned int boff; + size_t l; + + while (nbytes > 0) { + tib = ti->ti_lastblock; + boff = ti->ti_end % TTYINQ_DATASIZE; + + if (ti->ti_end == 0) { + /* First time we're being used or drained. */ + MPASS(ti->ti_begin == 0); + tib = ti->ti_lastblock = TAILQ_FIRST(&ti->ti_list); + if (tib == NULL) { + /* Queue has no blocks. */ + break; + } + } else if (boff == 0) { + /* We reached the end of this block on last write. */ + tib = TAILQ_NEXT(tib, tib_list); + if (tib == NULL) { + /* We've reached the watermark. */ + break; + } + ti->ti_lastblock = tib; + } + + /* Don't copy more than was requested. */ + l = MIN(nbytes, TTYINQ_DATASIZE - boff); + MPASS(l > 0); + memcpy(tib->tib_data + boff, cbuf, l); + + /* Set the quoting bits for the proper region. */ + ttyinq_set_quotes(tib, boff, l, quote); + + cbuf += l; + nbytes -= l; + ti->ti_end += l; + } + + return (cbuf - (const char *)buf); +} + +int +ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes, int quote) +{ + size_t ret; + + if (ttyinq_bytesleft(ti) < nbytes) + return (-1); + + /* We should always be able to write it back. */ + ret = ttyinq_write(ti, buf, nbytes, quote); + MPASS(ret == nbytes); + + return (0); +} + +void +ttyinq_canonicalize(struct ttyinq *ti) +{ + + ti->ti_linestart = ti->ti_reprint = ti->ti_end; + ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock; +} + +size_t +ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen, + char *lastc) +{ + struct ttyinq_block *tib = TAILQ_FIRST(&ti->ti_list); + unsigned int boff = ti->ti_begin; + unsigned int bend = MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), + ti->ti_begin + maxlen); + + MPASS(maxlen > 0); + + if (tib == NULL) + return (0); + + while (boff < bend) { + if (index(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff)) { + *lastc = tib->tib_data[boff]; + return (boff - ti->ti_begin + 1); + } + boff++; + } + + /* Not found - just process the entire block. */ + return (bend - ti->ti_begin); +} + +void +ttyinq_flush(struct ttyinq *ti) +{ + + ti->ti_begin = 0; + ti->ti_linestart = 0; + ti->ti_reprint = 0; + ti->ti_end = 0; +} + +#if 0 +void +ttyinq_flush_safe(struct ttyinq *ti) +{ + struct ttyinq_block *tib; + + ttyinq_flush(ti); + + /* Zero all data in the input queue to make it more safe */ + TAILQ_FOREACH(tib, &ti->ti_list, tib_list) { + bzero(&tib->tib_quotes, sizeof tib->tib_quotes); + bzero(&tib->tib_data, sizeof tib->tib_data); + } +} +#endif + +int +ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote) +{ + unsigned int boff; + struct ttyinq_block *tib = ti->ti_lastblock; + + if (ti->ti_linestart == ti->ti_end) + return (-1); + + MPASS(ti->ti_end > 0); + boff = (ti->ti_end - 1) % TTYINQ_DATASIZE; + + *c = tib->tib_data[boff]; + *quote = GETBIT(tib, boff); + + return (0); +} + +void +ttyinq_unputchar(struct ttyinq *ti) +{ + + MPASS(ti->ti_linestart < ti->ti_end); + + if (--ti->ti_end % TTYINQ_DATASIZE == 0) { + /* Roll back to the previous block. */ + ti->ti_lastblock = TAILQ_PREV(ti->ti_lastblock, + ttyinq_bhead, tib_list); + /* + * This can only fail if we are unputchar()'ing the + * first character in the queue. + */ + MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0)); + } +} + +void +ttyinq_reprintpos_set(struct ttyinq *ti) +{ + + ti->ti_reprint = ti->ti_end; + ti->ti_reprintblock = ti->ti_lastblock; +} + +void +ttyinq_reprintpos_reset(struct ttyinq *ti) +{ + + ti->ti_reprint = ti->ti_linestart; + ti->ti_reprintblock = ti->ti_startblock; +} + +static void +ttyinq_line_iterate(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data, + unsigned int offset, struct ttyinq_block *tib) +{ + unsigned int boff; + + /* Use the proper block when we're at the queue head. */ + if (offset == 0) + tib = TAILQ_FIRST(&ti->ti_list); + + /* Iterate all characters and call the iterator function. */ + for (; offset < ti->ti_end; offset++) { + boff = offset % TTYINQ_DATASIZE; + MPASS(tib != NULL); + + /* Call back the iterator function. */ + iterator(data, tib->tib_data[boff], GETBIT(tib, boff)); + + /* Last byte iterated - go to the next block. */ + if (boff == TTYINQ_DATASIZE - 1) + tib = TAILQ_NEXT(tib, tib_list); + MPASS(tib != NULL); + } +} + +void +ttyinq_line_iterate_from_linestart(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data) +{ + + ttyinq_line_iterate(ti, iterator, data, + ti->ti_linestart, ti->ti_startblock); +} + +void +ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti, + ttyinq_line_iterator_t *iterator, void *data) +{ + + ttyinq_line_iterate(ti, iterator, data, + ti->ti_reprint, ti->ti_reprintblock); +} + +static void +ttyinq_startup(void *dummy) +{ + + ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL); diff --git a/sys/kern/tty_outq.c b/sys/kern/tty_outq.c new file mode 100644 index 0000000..e945cca --- /dev/null +++ b/sys/kern/tty_outq.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/uio.h> + +#include <vm/uma.h> + +/* + * TTY output queue buffering. + * + * The previous design of the TTY layer offered the so-called clists. + * These clists were used for both the input queues and the output + * queue. We don't use certain features on the output side, like quoting + * bits for parity marking and such. This mechanism is similar to the + * old clists, but only contains the features we need to buffer the + * output. + */ + +/* Statistics. */ +static long ttyoutq_nfast = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_outq_nfast, CTLFLAG_RD, + &ttyoutq_nfast, 0, "Unbuffered reads to userspace on output"); +static long ttyoutq_nslow = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_outq_nslow, CTLFLAG_RD, + &ttyoutq_nslow, 0, "Buffered reads to userspace on output"); + +struct ttyoutq_block { + STAILQ_ENTRY(ttyoutq_block) tob_list; + char tob_data[TTYOUTQ_DATASIZE]; +}; + +static uma_zone_t ttyoutq_zone; + +void +ttyoutq_flush(struct ttyoutq *to) +{ + + to->to_begin = 0; + to->to_end = 0; +} + +void +ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t size) +{ + unsigned int nblocks; + struct ttyoutq_block *tob; + + nblocks = howmany(size, TTYOUTQ_DATASIZE); + + while (nblocks > to->to_nblocks) { + /* + * List is getting bigger. + * Add new blocks to the tail of the list. + * + * We must unlock the TTY temporarily, because we need + * to allocate memory. This won't be a problem, because + * in the worst case, another thread ends up here, which + * may cause us to allocate too many blocks, but this + * will be caught by the loop below. + */ + tty_unlock(tp); + tob = uma_zalloc(ttyoutq_zone, M_WAITOK); + tty_lock(tp); + + if (tty_gone(tp)) + return; + + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_nblocks++; + } + + while (nblocks < to->to_nblocks) { + /* + * List is getting smaller. Remove unused blocks at the + * end. This means we cannot guarantee this routine + * shrinks buffers properly, when we need to reclaim + * more space than there is available. + * + * XXX TODO: Two solutions here: + * - Throw data away + * - Temporarily hit the watermark until enough data has + * been flushed, so we can remove the blocks. + */ + + if (to->to_end == 0) { + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + break; + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + } else { + tob = STAILQ_NEXT(to->to_lastblock, tob_list); + if (tob == NULL) + break; + STAILQ_REMOVE_NEXT(&to->to_list, to->to_lastblock, tob_list); + } + uma_zfree(ttyoutq_zone, tob); + to->to_nblocks--; + } +} + +size_t +ttyoutq_read(struct ttyoutq *to, void *buf, size_t len) +{ + char *cbuf = buf; + + while (len > 0) { + struct ttyoutq_block *tob; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (to->to_begin == to->to_end) + break; + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + break; + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = to->to_begin; + cend = MIN(MIN(to->to_end, to->to_begin + len), + TTYOUTQ_DATASIZE); + clen = cend - cbegin; + + if (cend == TTYOUTQ_DATASIZE || cend == to->to_end) { + /* Read the block until the end. */ + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_begin = 0; + if (to->to_end <= TTYOUTQ_DATASIZE) { + to->to_end = 0; + } else { + to->to_end -= TTYOUTQ_DATASIZE; + } + } else { + /* Read the block partially. */ + to->to_begin += clen; + } + + /* Copy the data out of the buffers. */ + memcpy(cbuf, tob->tob_data + cbegin, clen); + cbuf += clen; + len -= clen; + } + + return (cbuf - (char *)buf); +} + +/* + * An optimized version of ttyoutq_read() which can be used in pseudo + * TTY drivers to directly copy data from the outq to userspace, instead + * of buffering it. + * + * We can only copy data directly if we need to read the entire block + * back to the user, because we temporarily remove the block from the + * queue. Otherwise we need to copy it to a temporary buffer first, to + * make sure data remains in the correct order. + */ +int +ttyoutq_read_uio(struct ttyoutq *to, struct tty *tp, struct uio *uio) +{ + + while (uio->uio_resid > 0) { + int error; + struct ttyoutq_block *tob; + size_t cbegin, cend, clen; + + /* See if there still is data. */ + if (to->to_begin == to->to_end) + return (0); + tob = STAILQ_FIRST(&to->to_list); + if (tob == NULL) + return (0); + + /* + * The end address should be the lowest of these three: + * - The write pointer + * - The blocksize - we can't read beyond the block + * - The end address if we could perform the full read + */ + cbegin = to->to_begin; + cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid), + TTYOUTQ_DATASIZE); + clen = cend - cbegin; + + /* + * We can prevent buffering in some cases: + * - We need to read the block until the end. + * - We don't need to read the block until the end, but + * there is no data beyond it, which allows us to move + * the write pointer to a new block. + */ + if (cend == TTYOUTQ_DATASIZE || cend == to->to_end) { + atomic_add_long(&ttyoutq_nfast, 1); + + /* + * Fast path: zero copy. Remove the first block, + * so we can unlock the TTY temporarily. + */ + STAILQ_REMOVE_HEAD(&to->to_list, tob_list); + to->to_nblocks--; + to->to_begin = 0; + if (to->to_end <= TTYOUTQ_DATASIZE) { + to->to_end = 0; + } else { + to->to_end -= TTYOUTQ_DATASIZE; + } + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(tob->tob_data + cbegin, clen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* We lost the discipline. */ + uma_zfree(ttyoutq_zone, tob); + return (ENXIO); + } + + /* Block can now be readded to the list. */ + /* + * XXX: we could remove the blocks here when the + * queue was shrunk, but still in use. See + * ttyoutq_setsize(). + */ + STAILQ_INSERT_TAIL(&to->to_list, tob, tob_list); + to->to_nblocks++; + if (error != 0) + return (error); + } else { + char ob[TTYOUTQ_DATASIZE - 1]; + atomic_add_long(&ttyoutq_nslow, 1); + + /* + * Slow path: store data in a temporary buffer. + */ + memcpy(ob, tob->tob_data + cbegin, clen); + to->to_begin += clen; + MPASS(to->to_begin < TTYOUTQ_DATASIZE); + + /* Temporary unlock and copy the data to userspace. */ + tty_unlock(tp); + error = uiomove(ob, clen, uio); + tty_lock(tp); + + if (tty_gone(tp)) { + /* We lost the discipline. */ + return (ENXIO); + } + + if (error != 0) + return (error); + } + } + + return (0); +} + +size_t +ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes) +{ + const char *cbuf = buf; + struct ttyoutq_block *tob; + unsigned int boff; + size_t l; + + while (nbytes > 0) { + /* Offset in current block. */ + tob = to->to_lastblock; + boff = to->to_end % TTYOUTQ_DATASIZE; + + if (to->to_end == 0) { + /* First time we're being used or drained. */ + MPASS(to->to_begin == 0); + tob = to->to_lastblock = STAILQ_FIRST(&to->to_list); + if (tob == NULL) { + /* Queue has no blocks. */ + break; + } + } else if (boff == 0) { + /* We reached the end of this block on last write. */ + tob = STAILQ_NEXT(tob, tob_list); + if (tob == NULL) { + /* We've reached the watermark. */ + break; + } + to->to_lastblock = tob; + } + + /* Don't copy more than was requested. */ + l = MIN(nbytes, TTYOUTQ_DATASIZE - boff); + MPASS(l > 0); + memcpy(tob->tob_data + boff, cbuf, l); + + cbuf += l; + nbytes -= l; + to->to_end += l; + } + + return (cbuf - (const char *)buf); +} + +int +ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes) +{ + size_t ret; + + if (ttyoutq_bytesleft(to) < nbytes) + return (-1); + + /* We should always be able to write it back. */ + ret = ttyoutq_write(to, buf, nbytes); + MPASS(ret == nbytes); + + return (0); +} + +static void +ttyoutq_startup(void *dummy) +{ + + ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL); diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c index ff8838e..8a51448 100644 --- a/sys/kern/tty_pts.c +++ b/sys/kern/tty_pts.c @@ -1,16 +1,9 @@ -/* - * Copyright (c) 2003 Networks Associates Technology, Inc. - * Copyright (c) 2006 Robert N. M. Watson - * Copyright (c) 2006 Olivier Houchard +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> * All rights reserved. * - * This software was developed for the FreeBSD Project in part by Network - * Associates Laboratories, the Security Research Division of Network - * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), - * as part of the DARPA CHATS research program. - * - * Copyright (c) 1982, 1986, 1989, 1993 - * The Regents of the University of California. All rights reserved. + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,14 +13,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -35,889 +25,632 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* - * Pseudo-teletype Driver - * (Actually two drivers, requiring two entries in 'cdevsw') - */ -#include "opt_compat.h" #include "opt_tty.h" + +/* Add compatibility bits for FreeBSD. */ +#define PTS_COMPAT +#ifdef DEV_PTY +/* Add /dev/ptyXX compat bits. */ +#define PTS_EXTERNAL +#endif /* DEV_PTY */ +/* Add bits to make Linux binaries work. */ +#define PTS_LINUX + #include <sys/param.h> -#include <sys/systm.h> #include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#if defined(COMPAT_43TTY) -#include <sys/ioctl_compat.h> -#endif -#include <sys/priv.h> -#include <sys/proc.h> -#include <sys/queue.h> -#include <sys/tty.h> +#include <sys/condvar.h> +#include <sys/conf.h> #include <sys/fcntl.h> -#include <sys/poll.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/filio.h> #include <sys/kernel.h> -#include <sys/vnode.h> -#include <sys/signalvar.h> #include <sys/malloc.h> -#include <sys/conf.h> -#include <sys/sysctl.h> -#include <sys/filio.h> - -static MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); - -static void ptsstart(struct tty *tp); -static void ptsstop(struct tty *tp, int rw); -static void ptcwakeup(struct tty *tp, int flag); - -static d_open_t ptsopen; -static d_close_t ptsclose; -static d_read_t ptsread; -static d_write_t ptswrite; -static d_ioctl_t ptsioctl; -static d_ioctl_t ptcioctl; -static d_open_t ptcopen; -static d_close_t ptcclose; -static d_read_t ptcread; -static d_write_t ptcwrite; -static d_poll_t ptcpoll; - -static struct cdevsw pts_cdevsw = { - .d_version = D_VERSION, - .d_open = ptsopen, - .d_close = ptsclose, - .d_read = ptsread, - .d_write = ptswrite, - .d_ioctl = ptsioctl, - .d_poll = ttypoll, - .d_name = "pts", - .d_flags = D_TTY | D_NEEDGIANT, - .d_kqfilter = ttykqfilter, -}; +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> +#include <sys/serial.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/syscallsubr.h> +#include <sys/sysent.h> +#include <sys/sysproto.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/ttycom.h> -static struct cdevsw ptc_cdevsw = { - .d_version = D_VERSION, - .d_open = ptcopen, - .d_close = ptcclose, - .d_read = ptcread, - .d_write = ptcwrite, - .d_ioctl = ptcioctl, - .d_poll = ptcpoll, - .d_name = "ptc", - .d_flags = D_TTY | D_NEEDGIANT, - .d_kqfilter = ttykqfilter, -}; +#include <machine/stdarg.h> -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ +static struct unrhdr *pts_pool; +#define MAXPTSDEVS 999 -#define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) -#define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) -#define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) +static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device"); -#define NUM_TO_MINOR(c) ((c & 0xff) | ((c & ~0xff) << 16)) -/*- - * Once a tty is allocated, it cannot (currently) be freed. As such, - * we keep a global list of ptys that have been used so we can recycle - * them. Another list is provided for released pts, which are - * not currently allocated, permitting reuse. pt_flags holds state - * associated with a particular session, so isn't overloaded for this. - * When a pty descriptor is unused, its number is set to -1 giving - * more consistent and traditional allocation orders to pty numbers. +/* + * Per-PTS structure. * - * Locking: (p) indicates that the field is locked by the global pt_mtx. - * (c) indicates the value is constant after allocation. Other fields - * await tty locking generally, and are protected by Giant. + * List of locks + * (t) locked by tty_lock() + * (c) const until freeing */ -struct pt_desc { - int pt_num; /* (c) pty number */ - LIST_ENTRY(pt_desc) pt_list; /* (p) global pty list */ - - int pt_flags; - struct selinfo pt_selr, pt_selw; - u_char pt_send; - u_char pt_ucntl; - struct tty *pt_tty; - struct cdev *pt_devs, *pt_devc; - int pt_pts_open, pt_ptc_open; - struct prison *pt_prison; -}; +struct pts_softc { + int pts_unit; /* (c) Device unit number. */ + unsigned int pts_flags; /* (t) Device flags. */ +#define PTS_PKT 0x1 /* Packet mode. */ -static struct mtx pt_mtx; -static LIST_HEAD(,pt_desc) pt_list; -static LIST_HEAD(,pt_desc) pt_free_list; + struct cv pts_inwait; /* (t) Blocking write() on master. */ + struct selinfo pts_inpoll; /* (t) Select queue for write(). */ + struct cv pts_outwait; /* (t) Blocking read() on master. */ + struct selinfo pts_outpoll; /* (t) Select queue for read(). */ -#define PF_PKT 0x008 /* packet mode */ -#define PF_STOPPED 0x010 /* user told stopped */ -#define PF_NOSTOP 0x040 -#define PF_UCNTL 0x080 /* user control mode */ +#ifdef PTS_EXTERNAL + struct cdev *pts_cdev; /* (c) Master device node. */ +#endif /* PTS_EXTERNAL */ -static unsigned int next_avail_nb; - -static int use_pts = 0; + struct uidinfo *pts_uidinfo; /* (c) Resource limit. */ +}; -static unsigned int max_pts = 1000; +/* + * Controller-side file operations. + */ -static unsigned int nb_allocated; +static int +ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred, + int flags, struct thread *td) +{ + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int error, oresid; -TUNABLE_INT("kern.pts.enable", &use_pts); + if (uio->uio_resid == 0) + return (0); + + /* + * Implement packet mode. When packet mode is turned on, the + * first byte contains a bitmask of events that occured (start, + * stop, flush, window size, etc). + */ -SYSCTL_NODE(_kern, OID_AUTO, pts, CTLFLAG_RD, 0, "pts"); + if (psc->pts_flags & PTS_PKT) { + /* XXX: return proper bits. */ + error = ureadc(0, uio); + if (error != 0) + return (error); + if (uio->uio_resid == 0) + return (0); + } -SYSCTL_INT(_kern_pts, OID_AUTO, enable, CTLFLAG_RW, &use_pts, 0, - "enable pts"); + oresid = uio->uio_resid; -SYSCTL_INT(_kern_pts, OID_AUTO, max, CTLFLAG_RW, &max_pts, 0, "max pts"); + tty_lock(tp); + for (;;) { + error = ttydisc_getc_uio(tp, uio); + /* We've got data (or an error). */ + if (error != 0 || uio->uio_resid != oresid) + break; -/* - * If there's a free pty descriptor in the pty descriptor list, retrieve it. - * Otherwise, allocate a new one, initialize it, and hook it up. If there's - * not a tty number, reject. - */ -static struct pt_desc * -pty_new(void) -{ - struct pt_desc *pt; - int nb; + /* Maybe the device isn't used anyway. */ + if (tty_opened(tp) == 0) { + error = ENXIO; + break; + } - mtx_lock(&pt_mtx); - if (nb_allocated >= max_pts || nb_allocated == 0xffffff) { - mtx_unlock(&pt_mtx); - return (NULL); - } - nb_allocated++; - pt = LIST_FIRST(&pt_free_list); - if (pt) { - LIST_REMOVE(pt, pt_list); - } else { - nb = next_avail_nb++; - mtx_unlock(&pt_mtx); - pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); - pt->pt_tty = ttyalloc(); - mtx_lock(&pt_mtx); - pt->pt_num = nb; + /* Wait for more data. */ + if (fp->f_flag & O_NONBLOCK) { + error = EWOULDBLOCK; + break; + } + error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx); + if (error != 0) + break; } - LIST_INSERT_HEAD(&pt_list, pt, pt_list); - mtx_unlock(&pt_mtx); - return (pt); -} - -/* - * Release a pty descriptor back to the pool for reuse. The pty number - * remains allocated. - */ -static void -pty_release(void *v) -{ - struct pt_desc *pt = (struct pt_desc *)v; - - mtx_lock(&pt_mtx); - KASSERT(pt->pt_ptc_open == 0 && pt->pt_pts_open == 0, - ("pty_release: pts/%d freed while open\n", pt->pt_num)); - KASSERT(pt->pt_devs == NULL && pt->pt_devc == NULL, - ("pty_release: pts/%d freed whith non-null struct cdev\n", pt->pt_num)); - nb_allocated--; - LIST_REMOVE(pt, pt_list); - LIST_INSERT_HEAD(&pt_free_list, pt, pt_list); - mtx_unlock(&pt_mtx); -} + tty_unlock(tp); -/* - * Given a pty descriptor, if both endpoints are closed, release all - * resources and destroy the device nodes to flush file system level - * state for the tty (owner, avoid races, etc). - */ -static void -pty_maybecleanup(struct pt_desc *pt) -{ - struct cdev *pt_devs, *pt_devc; - - if (pt->pt_ptc_open || pt->pt_pts_open) - return; - - if (pt->pt_tty->t_refcnt > 1) - return; - - if (bootverbose) - printf("destroying pty %d\n", pt->pt_num); - - pt_devs = pt->pt_devs; - pt_devc = pt->pt_devc; - pt->pt_devs = pt->pt_devc = NULL; - pt->pt_tty->t_dev = NULL; - pt_devc->si_drv1 = NULL; - ttyrel(pt->pt_tty); - pt->pt_tty = NULL; - destroy_dev_sched(pt_devs); - destroy_dev_sched_cb(pt_devc, pty_release, pt); + return (error); } -/*ARGSUSED*/ static int -ptsopen(struct cdev *dev, int flag, int devtype, struct thread *td) +ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred, + int flags, struct thread *td) { - struct tty *tp; - int error; - struct pt_desc *pt; - - pt = dev->si_drv1; - tp = dev->si_tty; - if ((tp->t_state & TS_ISOPEN) == 0) - ttyinitmode(tp, 1, 0); - else if (tp->t_state & TS_XCLUDE && priv_check(td, - PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } else if (pt->pt_prison != td->td_ucred->cr_prison && - priv_check(td, PRIV_TTY_PRISON)) { - return (EBUSY); - } - if (tp->t_oproc) /* Ctrlr still around. */ - ttyld_modem(tp, 1); - while ((tp->t_state & TS_CARR_ON) == 0) { - if (flag & FNONBLOCK) - break; - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "ptsopn", 0); - if (error) - return (error); - } - error = ttyld_open(tp, dev); - if (error == 0) { - ptcwakeup(tp, FREAD|FWRITE); - pt->pt_pts_open = 1; + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + char ib[256], *ibstart; + size_t iblen, rintlen; + int error = 0; + + tty_lock(tp); + + while (uio->uio_resid > 0) { + /* Temporarily unlock to buffer new characters. */ + tty_unlock(tp); + ibstart = ib; + iblen = MIN(uio->uio_resid, sizeof ib); + error = uiomove(ib, iblen, uio); + tty_lock(tp); + if (error != 0) + goto done; + + /* + * When possible, avoid the slow path. rint_bypass() + * copies all input to the input queue at once. + */ + while (iblen > 0) { + if (ttydisc_can_bypass(tp)) { + /* Store data at once. */ + rintlen = ttydisc_rint_bypass(tp, + ibstart, iblen); + ibstart += rintlen; + iblen -= rintlen; + + if (iblen == 0) { + /* All data written. */ + continue; + } + } else { + error = ttydisc_rint(tp, *ibstart, 0); + if (error == 0) { + /* Character stored successfully. */ + ibstart++; + iblen--; + continue; + } + } + + /* Maybe the device isn't used anyway. */ + if (tty_opened(tp) == 0) { + error = ENXIO; + goto done; + } + + /* Wait for more data. */ + if (fp->f_flag & O_NONBLOCK) { + error = EWOULDBLOCK; + goto done; + } + + /* Wake up users on the slave side. */ + ttydisc_rint_done(tp); + error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx); + if (error != 0) + goto done; + } } + +done: ttydisc_rint_done(tp); + tty_unlock(tp); return (error); } static int -ptsclose(struct cdev *dev, int flag, int mode, struct thread *td) +ptsdev_ioctl(struct file *fp, u_long cmd, void *data, + struct ucred *active_cred, struct thread *td) { - struct pt_desc *pt = dev->si_drv1; - struct tty *tp; - int err; - - tp = dev->si_tty; - err = ttyld_close(tp, flag); - ptsstop(tp, FREAD|FWRITE); - (void) tty_close(tp); - pt->pt_pts_open = 0; - pty_maybecleanup(pt); - return (err); -} + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int error = 0, sig; -static int -ptsread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - int error = 0; + switch (cmd) { + case FIONBIO: + /* This device supports non-blocking operation. */ + return (0); + case FIODGNAME: { + struct fiodgname_arg *fgn; + const char *p; + int i; + + /* Reverse device name lookups, for ptsname() and ttyname(). */ + fgn = data; +#ifdef PTS_EXTERNAL + if (psc->pts_cdev != NULL) + p = devtoname(psc->pts_cdev); + else +#endif /* PTS_EXTERNAL */ + p = tty_devname(tp); + i = strlen(p) + 1; + if (i > fgn->len) + return (EINVAL); + return copyout(p, fgn->buf, i); + } + + /* + * We need to implement TIOCGPGRP and TIOCGSID here again. When + * called on the pseudo-terminal master, it should not check if + * the terminal is the foreground terminal of the calling + * process. + * + * TIOCGETA is also implemented here. Various Linux PTY routines + * often call isatty(), which is implemented by tcgetattr(). + */ +#ifdef PTS_LINUX + case TIOCGETA: + /* Obtain terminal flags through tcgetattr(). */ + tty_lock(tp); + bcopy(&tp->t_termios, data, sizeof(struct termios)); + tty_unlock(tp); + return (0); +#endif /* PTS_LINUX */ + case TIOCSETAF: + case TIOCSETAW: + /* + * We must make sure we turn tcsetattr() calls of TCSAFLUSH and + * TCSADRAIN into something different. If an application would + * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may + * deadlock waiting for all data to be read. + */ + cmd = TIOCSETA; + break; +#if defined(PTS_COMPAT) || defined(PTS_LINUX) + case TIOCGPTN: + /* + * Get the device unit number. + */ + if (psc->pts_unit < 0) + return (ENOTTY); + *(unsigned int *)data = psc->pts_unit; + return (0); +#endif /* PTS_COMPAT || PTS_LINUX */ + case TIOCGPGRP: + /* Get the foreground process group ID. */ + tty_lock(tp); + if (tp->t_pgrp != NULL) + *(int *)data = tp->t_pgrp->pg_id; + else + *(int *)data = NO_PID; + tty_unlock(tp); + return (0); + case TIOCGSID: + /* Get the session leader process ID. */ + tty_lock(tp); + if (tp->t_session == NULL) + error = ENOTTY; + else + *(int *)data = tp->t_session->s_sid; + tty_unlock(tp); + return (error); + case TIOCPTMASTER: + /* Yes, we are a pseudo-terminal master. */ + return (0); + case TIOCSIG: + /* Signal the foreground process group. */ + sig = *(int *)data; + if (sig < 1 || sig >= NSIG) + return (EINVAL); + + tty_lock(tp); + tty_signal_pgrp(tp, sig); + tty_unlock(tp); + return (0); + case TIOCPKT: + /* Enable/disable packet mode. */ + tty_lock(tp); + if (*(int *)data) + psc->pts_flags |= PTS_PKT; + else + psc->pts_flags &= ~PTS_PKT; + tty_unlock(tp); + return (0); + } + + /* Just redirect this ioctl to the slave device. */ + tty_lock(tp); + error = tty_ioctl(tp, cmd, data, td); + tty_unlock(tp); - if (tp->t_oproc) - error = ttyld_read(tp, uio, flag); - ptcwakeup(tp, FWRITE); return (error); } -/* - * Write to pseudo-tty. - * Wakeups of controlling tty will happen - * indirectly, when tty driver calls ptsstart. - */ static int -ptswrite(struct cdev *dev, struct uio *uio, int flag) +ptsdev_poll(struct file *fp, int events, struct ucred *active_cred, + struct thread *td) { - struct tty *tp; + struct tty *tp = fp->f_data; + struct pts_softc *psc = tty_softc(tp); + int revents = 0; - tp = dev->si_tty; - if (tp->t_oproc == 0) - return (EIO); - return (ttyld_write(tp, uio, flag)); -} + tty_lock(tp); -/* - * Start output on pseudo-tty. - * Wake up process selecting or sleeping for input from controlling tty. - */ -static void -ptsstart(struct tty *tp) -{ - struct pt_desc *pt = tp->t_dev->si_drv1; + if (tty_opened(tp) == 0) { + /* Slave device is not opened. */ + tty_unlock(tp); + return (events & + (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); + } - if (tp->t_state & TS_TTSTOP) - return; - if (pt->pt_flags & PF_STOPPED) { - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send = TIOCPKT_START; + if (events & (POLLIN|POLLRDNORM)) { + /* See if we can getc something. */ + if (ttydisc_getc_poll(tp)) + revents |= events & (POLLIN|POLLRDNORM); + } + if (events & (POLLOUT|POLLWRNORM)) { + /* See if we can rint something. */ + if (ttydisc_rint_poll(tp)) + revents |= events & (POLLOUT|POLLWRNORM); } - ptcwakeup(tp, FREAD); -} -static void -ptcwakeup(struct tty *tp, int flag) -{ - struct pt_desc *pt = tp->t_dev->si_drv1; + /* + * No need to check for POLLHUP here. This device cannot be used + * as a callout device, which means we always have a carrier, + * because the master is. + */ - if (flag & FREAD) { - selwakeup(&pt->pt_selr); - wakeup(TSA_PTC_READ(tp)); - } - if (flag & FWRITE) { - selwakeup(&pt->pt_selw); - wakeup(TSA_PTC_WRITE(tp)); + if (revents == 0) { + /* + * This code might look misleading, but the naming of + * poll events on this side is the opposite of the slave + * device. + */ + if (events & (POLLIN|POLLRDNORM)) + selrecord(td, &psc->pts_outpoll); + if (events & (POLLOUT|POLLWRNORM)) + selrecord(td, &psc->pts_inpoll); } + + tty_unlock(tp); + + return (revents); } -/* - * ptcopen implementes exclusive access to the master/control device - * as well as creating the slave device based on the credential of the - * process opening the master. By creating the slave here, we avoid - * a race to access the master in terms of having a process with access - * to an incorrectly owned slave, but it does create the possibility - * that a racing process can cause a ptmx user to get EIO if it gets - * there first. Consumers of ptmx must look for EIO and retry if it - * happens. VFS locking may actually prevent this from occurring due - * to the lookup into devfs holding the vnode lock through open, but - * it's better to be careful. - */ static int -ptcopen(struct cdev *dev, int flag, int devtype, struct thread *td) +ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, + struct thread *td) { - struct pt_desc *pt; - struct tty *tp; - struct cdev *devs; + struct tty *tp = fp->f_data; +#ifdef PTS_EXTERNAL + struct pts_softc *psc = tty_softc(tp); +#endif /* PTS_EXTERNAL */ - pt = dev->si_drv1; - if (pt == NULL) - return (EIO); /* - * In case we have destroyed the struct tty at the last connect time, - * we need to recreate it. + * According to POSIX, we must implement an fstat(). This also + * makes this implementation compatible with Linux binaries, + * because Linux calls fstat() on the pseudo-terminal master to + * obtain st_rdev. + * + * XXX: POSIX also mentions we must fill in st_dev, st_atime, + * st_ctime and st_mtime, but how? */ - if (pt->pt_tty == NULL) { - tp = ttyalloc(); - mtx_lock(&pt_mtx); - if (pt->pt_tty == NULL) { - pt->pt_tty = tp; - dev->si_tty = pt->pt_tty; - mtx_unlock(&pt_mtx); - } else { - mtx_unlock(&pt_mtx); - ttyrel(tp); - } - } - tp = dev->si_tty; - if (tp->t_oproc) - return (EIO); - /* - * XXX: Might want to make the ownership/permissions here more - * configurable. - */ - if (pt->pt_devs) - devs = pt->pt_devs; + bzero(sb, sizeof *sb); +#ifdef PTS_EXTERNAL + if (psc->pts_cdev != NULL) + sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev); else - pt->pt_devs = devs = make_dev_cred(&pts_cdevsw, - NUM_TO_MINOR(pt->pt_num), - td->td_ucred, UID_ROOT, GID_WHEEL, 0666, "pts/%d", - pt->pt_num); - devs->si_drv1 = pt; - devs->si_tty = pt->pt_tty; - pt->pt_tty->t_dev = devs; - - tp->t_timeout = -1; - tp->t_oproc = ptsstart; - tp->t_stop = ptsstop; - ttyld_modem(tp, 1); - tp->t_lflag &= ~EXTPROC; - pt = dev->si_drv1; - pt->pt_prison = td->td_ucred->cr_prison; - pt->pt_flags = 0; - pt->pt_send = 0; - pt->pt_ucntl = 0; - pt->pt_ptc_open = 1; +#endif /* PTS_EXTERNAL */ + sb->st_ino = sb->st_rdev = tty_udev(tp); + sb->st_mode = S_IFCHR; + sb->st_uid = tp->t_dev->si_cred->cr_ruid; + sb->st_gid = GID_TTY; + return (0); } static int -ptcclose(struct cdev *dev, int flags, int fmt, struct thread *td) +ptsdev_close(struct file *fp, struct thread *td) { - struct pt_desc *pt = dev->si_drv1; - struct tty *tp; + struct tty *tp = fp->f_data; - tp = dev->si_tty; - ttyld_modem(tp, 0); + /* Deallocate TTY device. */ + tty_lock(tp); + tty_rel_gone(tp); - /* - * XXX MDMBUF makes no sense for ptys but would inhibit the above - * l_modem(). CLOCAL makes sense but isn't supported. Special - * l_modem()s that ignore carrier drop make no sense for ptys but - * may be in use because other parts of the line discipline make - * sense for ptys. Recover by doing everything that a normal - * ttymodem() would have done except for sending a SIGHUP. - */ - if (tp->t_state & TS_ISOPEN) { - tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); - tp->t_state |= TS_ZOMBIE; - ttyflush(tp, FREAD | FWRITE); - } - - tp->t_oproc = 0; /* mark closed */ - pt->pt_ptc_open = 0; - pty_maybecleanup(pt); return (0); } -static int -ptcread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - char buf[BUFSIZ]; - int error = 0, cc; +static struct fileops ptsdev_ops = { + .fo_read = ptsdev_read, + .fo_write = ptsdev_write, + .fo_ioctl = ptsdev_ioctl, + .fo_poll = ptsdev_poll, + .fo_stat = ptsdev_stat, + .fo_close = ptsdev_close, + .fo_flags = DFLAG_PASSABLE, +}; - /* - * We want to block until the slave - * is open, and there's something to read; - * but if we lost the slave or we're NBIO, - * then return the appropriate error instead. - */ - for (;;) { - if (tp->t_state&TS_ISOPEN) { - if (pt->pt_flags&PF_PKT && pt->pt_send) { - error = ureadc((int)pt->pt_send, uio); - if (error) - return (error); - if (pt->pt_send & TIOCPKT_IOCTL) { - cc = min(uio->uio_resid, - sizeof(tp->t_termios)); - uiomove(&tp->t_termios, cc, uio); - } - pt->pt_send = 0; - return (0); - } - if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) { - error = ureadc((int)pt->pt_ucntl, uio); - if (error) - return (error); - pt->pt_ucntl = 0; - return (0); - } - if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) - break; - } - if ((tp->t_state & TS_CONNECTED) == 0) - return (0); /* EOF */ - if (flag & O_NONBLOCK) - return (EWOULDBLOCK); - error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); - if (error) - return (error); - } - if (pt->pt_flags & (PF_PKT|PF_UCNTL)) - error = ureadc(0, uio); - while (uio->uio_resid > 0 && error == 0) { - cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); - if (cc <= 0) - break; - error = uiomove(buf, cc, uio); - } - ttwwakeup(tp); - return (error); -} +/* + * Driver-side hooks. + */ static void -ptsstop(struct tty *tp, int flush) +ptsdrv_outwakeup(struct tty *tp) { - struct pt_desc *pt = tp->t_dev->si_drv1; - int flag; - - /* note: FLUSHREAD and FLUSHWRITE already ok */ - if (flush == 0) { - flush = TIOCPKT_STOP; - pt->pt_flags |= PF_STOPPED; - } else - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send |= flush; - /* change of perspective */ - flag = 0; - if (flush & FREAD) - flag |= FWRITE; - if (flush & FWRITE) - flag |= FREAD; - ptcwakeup(tp, flag); + struct pts_softc *psc = tty_softc(tp); + + cv_broadcast(&psc->pts_outwait); + selwakeup(&psc->pts_outpoll); } -static int -ptcpoll(struct cdev *dev, int events, struct thread *td) +static void +ptsdrv_inwakeup(struct tty *tp) { - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - int revents = 0; - int s; + struct pts_softc *psc = tty_softc(tp); - if ((tp->t_state & TS_CONNECTED) == 0) - return (events & - (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)); + cv_broadcast(&psc->pts_inwait); + selwakeup(&psc->pts_inpoll); +} - /* - * Need to block timeouts (ttrstart). - */ - s = spltty(); +static void +ptsdrv_close(struct tty *tp) +{ - if (events & (POLLIN | POLLRDNORM)) - if ((tp->t_state & TS_ISOPEN) && - ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || - ((pt->pt_flags & PF_PKT) && pt->pt_send) || - ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl))) - revents |= events & (POLLIN | POLLRDNORM); + /* Wake up any blocked readers/writers. */ + ptsdrv_outwakeup(tp); + ptsdrv_inwakeup(tp); +} - if (events & (POLLOUT | POLLWRNORM)) - if (tp->t_state & TS_ISOPEN && - (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || - (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) - revents |= events & (POLLOUT | POLLWRNORM); +static void +ptsdrv_free(void *softc) +{ + struct pts_softc *psc = softc; - if (events & POLLHUP) - if ((tp->t_state & TS_CARR_ON) == 0) - revents |= POLLHUP; + /* Make device number available again. */ + if (psc->pts_unit >= 0) + free_unr(pts_pool, psc->pts_unit); - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(td, &pt->pt_selr); + chgptscnt(psc->pts_uidinfo, -1, 0); + uifree(psc->pts_uidinfo); - if (events & (POLLOUT | POLLWRNORM)) - selrecord(td, &pt->pt_selw); - } - splx(s); +#ifdef PTS_EXTERNAL + /* Destroy master device as well. */ + if (psc->pts_cdev != NULL) + destroy_dev_sched(psc->pts_cdev); +#endif /* PTS_EXTERNAL */ - return (revents); + free(psc, M_PTS); } +static struct ttydevsw pts_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_outwakeup = ptsdrv_outwakeup, + .tsw_inwakeup = ptsdrv_inwakeup, + .tsw_close = ptsdrv_close, + .tsw_free = ptsdrv_free, +}; + static int -ptcwrite(struct cdev *dev, struct uio *uio, int flag) +pts_alloc(int fflags, struct thread *td, struct file *fp) { - struct tty *tp = dev->si_tty; - u_char *cp = 0; - int cc = 0; - u_char locbuf[BUFSIZ]; - int cnt = 0; - int error = 0; - -again: - if ((tp->t_state&TS_ISOPEN) == 0) - goto block; - while (uio->uio_resid > 0 || cc > 0) { - if (cc == 0) { - cc = min(uio->uio_resid, BUFSIZ); - cp = locbuf; - error = uiomove(cp, cc, uio); - if (error) - return (error); - /* check again for safety */ - if ((tp->t_state & TS_ISOPEN) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - } - while (cc > 0) { - if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && - (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) { - wakeup(TSA_HUP_OR_INPUT(tp)); - goto block; - } - ttyld_rint(tp, *cp++); - cnt++; - cc--; - } - cc = 0; + int unit, ok; + struct tty *tp; + struct pts_softc *psc; + struct proc *p = td->td_proc; + struct uidinfo *uid = td->td_ucred->cr_ruidinfo; + + /* Resource limiting. */ + PROC_LOCK(p); + ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); + PROC_UNLOCK(p); + if (!ok) + return (EAGAIN); + + /* Try to allocate a new pts unit number. */ + unit = alloc_unr(pts_pool); + if (unit < 0) { + chgptscnt(uid, -1, 0); + return (EAGAIN); } + + /* Allocate TTY and softc. */ + psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); + cv_init(&psc->pts_inwait, "pts inwait"); + cv_init(&psc->pts_outwait, "pts outwait"); + + psc->pts_unit = unit; + psc->pts_uidinfo = uid; + uihold(uid); + + tp = tty_alloc(&pts_class, psc, NULL); + + /* Expose the slave device as well. */ + tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit); + + finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); + return (0); -block: - /* - * Come here to wait for slave to open, for space - * in outq, or space in rawq, or an empty canq. - */ - if ((tp->t_state & TS_CONNECTED) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - if (flag & IO_NDELAY) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - if (cnt == 0) - return (EWOULDBLOCK); - return (0); - } - error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); - if (error) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (error); - } - goto again; } -static int -ptcioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +#ifdef PTS_EXTERNAL +int +pts_alloc_external(int fflags, struct thread *td, struct file *fp, + struct cdev *dev, const char *name) { - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - int ival; -#endif + int ok; + struct tty *tp; + struct pts_softc *psc; + struct proc *p = td->td_proc; + struct uidinfo *uid = td->td_ucred->cr_ruidinfo; - switch (cmd) { - - case TIOCGPGRP: - /* - * We avoid calling ttioctl on the controller since, - * in that case, tp must be the controlling terminal. - */ - *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; - return (0); - - case TIOCPKT: - if (*(int *)data) { - if (pt->pt_flags & PF_UCNTL) - return (EINVAL); - pt->pt_flags |= PF_PKT; - } else - pt->pt_flags &= ~PF_PKT; - return (0); - - case TIOCUCNTL: - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) - return (EINVAL); - pt->pt_flags |= PF_UCNTL; - } else - pt->pt_flags &= ~PF_UCNTL; - return (0); - case TIOCGPTN: - *(unsigned int *)data = pt->pt_num; - return (0); - } - - /* - * The rest of the ioctls shouldn't be called until - * the slave is open. - */ - if ((tp->t_state & TS_ISOPEN) == 0) { - if (cmd == TIOCGETA) { - /* - * TIOCGETA is used by isatty() to make sure it's - * a tty. Linux openpty() calls isatty() very early, - * before the slave is opened, so don't actually - * fill the struct termios, but just let isatty() - * know it's a tty. - */ - return (0); - } - if (cmd != FIONBIO && cmd != FIOASYNC) - return (EAGAIN); - } - - switch (cmd) { -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: -#endif - case TIOCSETD: - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: - /* - * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. - * ttywflush(tp) will hang if there are characters in - * the outq. - */ - ndflush(&tp->t_outq, tp->t_outq.c_cc); - break; - -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - case _IO('t', 95): - ival = IOCPARM_IVAL(data); - data = (caddr_t)&ival; - /* FALLTHROUGH */ -#endif - case TIOCSIG: - if (*(unsigned int *)data >= NSIG || - *(unsigned int *)data == 0) - return(EINVAL); - if ((tp->t_lflag&NOFLSH) == 0) - ttyflush(tp, FREAD|FWRITE); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if ((*(unsigned int *)data == SIGINFO) && - ((tp->t_lflag&NOKERNINFO) == 0)) - ttyinfo(tp); - return(0); - } - return (ptsioctl(dev, cmd, data, flag, td)); -} -/*ARGSUSED*/ -static int -ptsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct pt_desc *pt = dev->si_drv1; - u_char *cc = tp->t_cc; - int stop, error; + /* Resource limiting. */ + PROC_LOCK(p); + ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); + PROC_UNLOCK(p); + if (!ok) + return (EAGAIN); - if (cmd == TIOCEXT) { - /* - * When the EXTPROC bit is being toggled, we need - * to send an TIOCPKT_IOCTL if the packet driver - * is turned on. - */ - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag |= EXTPROC; - } else { - if ((tp->t_lflag & EXTPROC) && - (pt->pt_flags & PF_PKT)) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag &= ~EXTPROC; - } - return(0); - } - error = ttioctl(tp, cmd, data, flag); - if (error == ENOTTY) { - if (pt->pt_flags & PF_UCNTL && - (cmd & ~0xff) == UIOCCMD(0)) { - if (cmd & 0xff) { - pt->pt_ucntl = (u_char)cmd; - ptcwakeup(tp, FREAD); - } - return (0); - } - error = ENOTTY; - } - /* - * If external processing and packet mode send ioctl packet. - */ - if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) { - switch(cmd) { - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: - case TIOCSETC: - case TIOCSLTC: - case TIOCLBIS: - case TIOCLBIC: - case TIOCLSET: -#endif - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - break; - default: - break; - } - } - stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) - && CCEQ(cc[VSTART], CTRL('q')); - if (pt->pt_flags & PF_NOSTOP) { - if (stop) { - pt->pt_send &= ~TIOCPKT_NOSTOP; - pt->pt_send |= TIOCPKT_DOSTOP; - pt->pt_flags &= ~PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } else { - if (!stop) { - pt->pt_send &= ~TIOCPKT_DOSTOP; - pt->pt_send |= TIOCPKT_NOSTOP; - pt->pt_flags |= PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } - return (error); -} + /* Allocate TTY and softc. */ + psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); + cv_init(&psc->pts_inwait, "pts inwait"); + cv_init(&psc->pts_outwait, "pts outwait"); -/* - * Match lookups on /dev/ptmx, find the next free pty (if any), set up - * the pty descriptor, register it, and return a reference to the master. - * - * pts == /dev/pts/xxx (oldstyle: ttyp...) - * ptc == /dev/pty/xxx (oldstyle: ptyp...) - */ -static void -pty_clone(void *arg, struct ucred *cred, char *name, int namelen, - struct cdev **dev) -{ - struct pt_desc *pt; - struct cdev *devc; + psc->pts_unit = -1; + psc->pts_cdev = dev; + psc->pts_uidinfo = uid; + uihold(uid); - if (!use_pts) - return; + tp = tty_alloc(&pts_class, psc, NULL); - if (*dev != NULL) - return; + /* Expose the slave device as well. */ + tty_makedev(tp, td->td_ucred, "%s", name); - if (strcmp(name, "ptmx") != 0) - return; + finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); - mtx_lock(&Giant); - pt = pty_new(); - if (pt == NULL) { - mtx_unlock(&Giant); - return; - } + return (0); +} +#endif /* PTS_EXTERNAL */ + +int +posix_openpt(struct thread *td, struct posix_openpt_args *uap) +{ + int error, fd; + struct file *fp; /* - * XXX: Lack of locking here considered worrying. We expose the - * pts/pty device nodes before they are fully initialized, although - * Giant likely protects us (unless make_dev blocks...?). - * - * XXX: If a process performs a lookup on /dev/ptmx but never an - * open, we won't GC the device node. We should have a callout - * sometime later that GC's device instances that were never - * opened, or some way to tell devfs that "this had better be for - * an open() or we won't create a device". + * POSIX states it's unspecified when other flags are passed. We + * don't allow this. */ - pt->pt_devc = devc = make_dev_credf(MAKEDEV_REF, &ptc_cdevsw, - NUM_TO_MINOR(pt->pt_num), cred, UID_ROOT, GID_WHEEL, 0666, - "pty/%d", pt->pt_num); + if (uap->flags & ~(O_RDWR|O_NOCTTY)) + return (EINVAL); + + error = falloc(td, &fp, &fd); + if (error) + return (error); + + /* Allocate the actual pseudo-TTY. */ + error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp); + if (error != 0) { + fdclose(td->td_proc->p_fd, fp, fd, td); + return (error); + } + + /* Pass it back to userspace. */ + td->td_retval[0] = fd; + fdrop(fp, td); + + return (0); +} - devc->si_drv1 = pt; - devc->si_tty = pt->pt_tty; - *dev = devc; - mtx_unlock(&Giant); +#if defined(PTS_COMPAT) || defined(PTS_LINUX) +static int +ptmx_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp) +{ + int error; - if (bootverbose) - printf("pty_clone: allocated pty %d to uid %d\n", pt->pt_num, - cred->cr_ruid); + error = pts_alloc(fflags & (FREAD|FWRITE), td, fp); + if (error != 0) + return (error); - return; + return (0); } +static struct cdevsw ptmx_cdevsw = { + .d_version = D_VERSION, + .d_fdopen = ptmx_fdopen, + .d_name = "ptmx", +}; +#endif /* PTS_COMPAT || PTS_LINUX */ + static void -pty_drvinit(void *unused) +pts_init(void *unused) { - mtx_init(&pt_mtx, "pt_mtx", NULL, MTX_DEF); - LIST_INIT(&pt_list); - LIST_INIT(&pt_free_list); - EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000); + pts_pool = new_unrhdr(0, MAXPTSDEVS, NULL); +#if defined(PTS_COMPAT) || defined(PTS_LINUX) + make_dev(&ptmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "ptmx"); +#endif /* PTS_COMPAT || PTS_LINUX */ } -SYSINIT(ptydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,pty_drvinit,NULL); +SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL); diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c index 3827e82..e2edd21 100644 --- a/sys/kern/tty_pty.c +++ b/sys/kern/tty_pty.c @@ -1,6 +1,9 @@ /*- - * Copyright (c) 1982, 1986, 1989, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,14 +13,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -25,793 +25,101 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* - * Pseudo-teletype Driver - * (Actually two drivers, requiring two entries in 'cdevsw') - */ -#include "opt_compat.h" -#include "opt_tty.h" #include <sys/param.h> -#include <sys/systm.h> -#include <sys/libkern.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/sx.h> -#if defined(COMPAT_43TTY) -#include <sys/ioctl_compat.h> -#endif -#include <sys/priv.h> -#include <sys/proc.h> -#include <sys/tty.h> #include <sys/conf.h> +#include <sys/eventhandler.h> #include <sys/fcntl.h> -#include <sys/poll.h> #include <sys/kernel.h> -#include <sys/uio.h> -#include <sys/signalvar.h> -#include <sys/malloc.h> - -static MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); - -static void ptsstart(struct tty *tp); -static void ptsstop(struct tty *tp, int rw); -static void ptcwakeup(struct tty *tp, int flag); -static struct cdev *ptyinit(struct cdev *cdev, struct thread *td); - -static d_open_t ptsopen; -static d_close_t ptsclose; -static d_read_t ptsread; -static d_write_t ptswrite; -static d_ioctl_t ptsioctl; -static d_open_t ptcopen; -static d_close_t ptcclose; -static d_read_t ptcread; -static d_ioctl_t ptcioctl; -static d_write_t ptcwrite; -static d_poll_t ptcpoll; - -static struct cdevsw pts_cdevsw = { - .d_version = D_VERSION, - .d_open = ptsopen, - .d_close = ptsclose, - .d_read = ptsread, - .d_write = ptswrite, - .d_ioctl = ptsioctl, - .d_name = "pts", - .d_flags = D_TTY | D_NEEDGIANT, -}; - -static struct cdevsw ptc_cdevsw = { - .d_version = D_VERSION, - .d_open = ptcopen, - .d_close = ptcclose, - .d_read = ptcread, - .d_write = ptcwrite, - .d_ioctl = ptcioctl, - .d_poll = ptcpoll, - .d_name = "ptc", - .d_flags = D_TTY | D_NEEDGIANT, -}; - -static struct mtx ptyinit_lock; -MTX_SYSINIT(ptyinit_lock, &ptyinit_lock, "ptyinit", MTX_DEF); - -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ - -struct ptsc { - int pt_flags; - struct selinfo pt_selr, pt_selw; - u_char pt_send; - u_char pt_ucntl; - struct tty *pt_tty; - struct cdev *devs, *devc; - int pt_devs_open, pt_devc_open; - struct prison *pt_prison; -}; - -#define PF_PKT 0x08 /* packet mode */ -#define PF_STOPPED 0x10 /* user told stopped */ -#define PF_NOSTOP 0x40 -#define PF_UCNTL 0x80 /* user control mode */ - -#define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) -#define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) -#define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/tty.h> -static const char names[] = "pqrsPQRSlmnoLMNO"; /* - * This function creates and initializes a pts/ptc pair - * - * pts == /dev/tty[pqrsPQRSlmnoLMNO][0123456789abcdefghijklmnopqrstuv] - * ptc == /dev/pty[pqrsPQRSlmnoLMNO][0123456789abcdefghijklmnopqrstuv] + * This driver implements a BSD-style compatibility naming scheme for + * the pts(4) driver. We just call into pts(4) to create the actual PTY. + * To make sure we don't use the same PTY multiple times, we abuse + * si_drv1 inside the cdev to mark whether the PTY is in use. */ -static struct cdev * -ptyinit(struct cdev *devc, struct thread *td) -{ - struct ptsc *pt; - int n; - n = minor2unit(minor(devc)); +static int pty_warningcnt = 10; - /* We only allow for up to 32 ptys per char in "names". */ - if (n >= 32 * (sizeof(names) - 1)) - return (NULL); - - devc->si_flags &= ~SI_CHEAPCLONE; - - /* - * Initially do not create a slave endpoint. - */ - pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); - pt->devc = devc; - - pt->pt_tty = ttyalloc(); - pt->pt_tty->t_sc = pt; - mtx_lock(&ptyinit_lock); - if (devc->si_drv1 == NULL) { - devc->si_drv1 = pt; - devc->si_tty = pt->pt_tty; - mtx_unlock(&ptyinit_lock); - } else { - mtx_unlock(&ptyinit_lock); - ttyrel(pt->pt_tty); - free(pt, M_PTY); - } - return (devc); -} - -static void -pty_create_slave(struct ucred *cred, struct ptsc *pt, int m) +static int +ptydev_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp) { - int n; + int u, error; + char name[] = "ttyXX"; - n = minor2unit(m); - KASSERT(n >= 0 && n / 32 < sizeof(names), - ("pty_create_slave: n %d ptsc %p", n, pt)); - pt->devs = make_dev_cred(&pts_cdevsw, m, cred, UID_ROOT, GID_WHEEL, - 0666, "tty%c%r", names[n / 32], n % 32); - pt->devs->si_drv1 = pt; - pt->devs->si_tty = pt->pt_tty; - pt->pt_tty->t_dev = pt->devs; -} - -static void -pty_destroy_slave(struct ptsc *pt) -{ - - if (pt->pt_tty->t_refcnt > 1) - return; - pt->pt_tty->t_dev = NULL; - ttyrel(pt->pt_tty); - pt->pt_tty = NULL; - destroy_dev(pt->devs); - pt->devs = NULL; -} - -static void -pty_maybe_destroy_slave(struct ptsc *pt) -{ - - /* - * vfs bugs and complications near revoke() make - * it currently impossible to destroy struct cdev - */ - if (0 && pt->pt_devc_open == 0 && pt->pt_devs_open == 0) - pty_destroy_slave(pt); -} - -/*ARGSUSED*/ -static int -ptsopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp; - int error; - struct ptsc *pt; - - if (!dev->si_drv1) - return(ENXIO); - pt = dev->si_drv1; - tp = dev->si_tty; - - if ((tp->t_state & TS_ISOPEN) == 0) { - ttyinitmode(tp, 1, 0); - } else if (tp->t_state & TS_XCLUDE && priv_check(td, - PRIV_TTY_EXCLUSIVE)) - return (EBUSY); - else if (pt->pt_prison != td->td_ucred->cr_prison && - priv_check(td, PRIV_TTY_PRISON)) + if (!atomic_cmpset_ptr((uintptr_t *)&dev->si_drv1, 0, 1)) return (EBUSY); - if (tp->t_oproc) /* Ctrlr still around. */ - (void)ttyld_modem(tp, 1); - while ((tp->t_state & TS_CARR_ON) == 0) { - if (flag&FNONBLOCK) - break; - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "ptsopn", 0); - if (error) - return (error); - } - error = ttyld_open(tp, dev); - if (error == 0) { - ptcwakeup(tp, FREAD|FWRITE); - pt->pt_devs_open = 1; - } else - pty_maybe_destroy_slave(pt); - return (error); -} - -static int -ptsclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct ptsc *pti; - struct tty *tp; - int err; - - tp = dev->si_tty; - pti = dev->si_drv1; - - KASSERT(dev == pti->devs, ("ptsclose: dev != pti->devs")); - - err = ttyld_close(tp, flag); - (void) tty_close(tp); - - pti->pt_devs_open = 0; - pty_maybe_destroy_slave(pti); - - return (err); -} - -static int -ptsread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - int error = 0; - - if (tp->t_oproc) - error = ttyld_read(tp, uio, flag); - ptcwakeup(tp, FWRITE); - return (error); -} - -/* - * Write to pseudo-tty. - * Wakeups of controlling tty will happen - * indirectly, when tty driver calls ptsstart. - */ -static int -ptswrite(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp; - - tp = dev->si_tty; - if (tp->t_oproc == 0) - return (EIO); - return (ttyld_write(tp, uio, flag)); -} - -/* - * Start output on pseudo-tty. - * Wake up process selecting or sleeping for input from controlling tty. - */ -static void -ptsstart(struct tty *tp) -{ - struct ptsc *pt = tp->t_sc; - - if (tp->t_state & TS_TTSTOP) - return; - if (pt->pt_flags & PF_STOPPED) { - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send = TIOCPKT_START; - } - ptcwakeup(tp, FREAD); -} - -static void -ptcwakeup(struct tty *tp, int flag) -{ - struct ptsc *pt = tp->t_sc; - - if (flag & FREAD) { - selwakeuppri(&pt->pt_selr, TTIPRI); - wakeup(TSA_PTC_READ(tp)); - } - if (flag & FWRITE) { - selwakeuppri(&pt->pt_selw, TTOPRI); - wakeup(TSA_PTC_WRITE(tp)); - } -} - -static int -ptcopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp; - struct ptsc *pt; - - if (!dev->si_drv1) - ptyinit(dev, td); - if (!dev->si_drv1) - return(ENXIO); - - pt = dev->si_drv1; - /* - * In case we have destroyed the struct tty at the last connect time, - * we need to recreate it. - */ - if (pt->pt_tty == NULL) { - tp = ttyalloc(); - mtx_lock(&ptyinit_lock); - if (pt->pt_tty == NULL) { - pt->pt_tty = tp; - pt->pt_tty->t_sc = pt; - dev->si_tty = pt->pt_tty; - mtx_unlock(&ptyinit_lock); - } else { - mtx_unlock(&ptyinit_lock); - ttyrel(tp); - } - } - tp = dev->si_tty; - - if (tp->t_oproc) - return (EIO); - tp->t_timeout = -1; - tp->t_oproc = ptsstart; - tp->t_stop = ptsstop; - (void)ttyld_modem(tp, 1); - tp->t_lflag &= ~EXTPROC; - pt->pt_prison = td->td_ucred->cr_prison; - pt->pt_flags = 0; - pt->pt_send = 0; - pt->pt_ucntl = 0; - - if (!pt->devs) - pty_create_slave(td->td_ucred, pt, minor(dev)); - pt->pt_devc_open = 1; - - return (0); -} - -static int -ptcclose(struct cdev *dev, int flags, int fmt, struct thread *td) -{ - struct ptsc *pti = dev->si_drv1; - struct tty *tp; - - tp = dev->si_tty; - (void)ttyld_modem(tp, 0); - - /* - * XXX MDMBUF makes no sense for ptys but would inhibit the above - * l_modem(). CLOCAL makes sense but isn't supported. Special - * l_modem()s that ignore carrier drop make no sense for ptys but - * may be in use because other parts of the line discipline make - * sense for ptys. Recover by doing everything that a normal - * ttymodem() would have done except for sending a SIGHUP. - */ - if (tp->t_state & TS_ISOPEN) { - tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); - tp->t_state |= TS_ZOMBIE; - ttyflush(tp, FREAD | FWRITE); - } - - tp->t_oproc = 0; /* mark closed */ - pti->pt_devc_open = 0; - pty_maybe_destroy_slave(pti); - return (0); -} - -static int -ptcread(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - char buf[BUFSIZ]; - int error = 0, cc; - - /* - * We want to block until the slave - * is open, and there's something to read; - * but if we lost the slave or we're NBIO, - * then return the appropriate error instead. - */ - for (;;) { - if (tp->t_state&TS_ISOPEN) { - if (pt->pt_flags&PF_PKT && pt->pt_send) { - error = ureadc((int)pt->pt_send, uio); - if (error) - return (error); - if (pt->pt_send & TIOCPKT_IOCTL) { - cc = min(uio->uio_resid, - sizeof(tp->t_termios)); - uiomove(&tp->t_termios, cc, uio); - } - pt->pt_send = 0; - return (0); - } - if (pt->pt_flags&PF_UCNTL && pt->pt_ucntl) { - error = ureadc((int)pt->pt_ucntl, uio); - if (error) - return (error); - pt->pt_ucntl = 0; - return (0); - } - if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) - break; - } - if ((tp->t_state & TS_CONNECTED) == 0) - return (0); /* EOF */ - if (flag & O_NONBLOCK) - return (EWOULDBLOCK); - error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); - if (error) - return (error); - } - if (pt->pt_flags & (PF_PKT|PF_UCNTL)) - error = ureadc(0, uio); - while (uio->uio_resid > 0 && error == 0) { - cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); - if (cc <= 0) - break; - error = uiomove(buf, cc, uio); - } - ttwwakeup(tp); - return (error); -} - -static void -ptsstop(struct tty *tp, int flush) -{ - struct ptsc *pt = tp->t_sc; - int flag; - - /* note: FLUSHREAD and FLUSHWRITE already ok */ - if (flush == 0) { - flush = TIOCPKT_STOP; - pt->pt_flags |= PF_STOPPED; - } else - pt->pt_flags &= ~PF_STOPPED; - pt->pt_send |= flush; - /* change of perspective */ - flag = 0; - if (flush & FREAD) - flag |= FWRITE; - if (flush & FWRITE) - flag |= FREAD; - ptcwakeup(tp, flag); -} - -static int -ptcpoll(struct cdev *dev, int events, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - int revents = 0; - int s; - - if ((tp->t_state & TS_CONNECTED) == 0) - return (events & - (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)); - /* - * Need to block timeouts (ttrstart). - */ - s = spltty(); + /* Generate device name and create PTY. */ + u = dev2unit(dev); + name[3] = u >> 8; + name[4] = u; - if (events & (POLLIN | POLLRDNORM)) - if ((tp->t_state & TS_ISOPEN) && - ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || - ((pt->pt_flags & PF_PKT) && pt->pt_send) || - ((pt->pt_flags & PF_UCNTL) && pt->pt_ucntl))) - revents |= events & (POLLIN | POLLRDNORM); - - if (events & (POLLOUT | POLLWRNORM)) - if (tp->t_state & TS_ISOPEN && - (((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || - (tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) - revents |= events & (POLLOUT | POLLWRNORM); - - if (events & POLLHUP) - if ((tp->t_state & TS_CARR_ON) == 0) - revents |= POLLHUP; - - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(td, &pt->pt_selr); - - if (events & (POLLOUT | POLLWRNORM)) - selrecord(td, &pt->pt_selw); - } - splx(s); - - return (revents); -} - -static int -ptcwrite(struct cdev *dev, struct uio *uio, int flag) -{ - struct tty *tp = dev->si_tty; - u_char *cp = 0; - int cc = 0; - u_char locbuf[BUFSIZ]; - int cnt = 0; - int error = 0; - -again: - if ((tp->t_state&TS_ISOPEN) == 0) - goto block; - while (uio->uio_resid > 0 || cc > 0) { - if (cc == 0) { - cc = min(uio->uio_resid, BUFSIZ); - cp = locbuf; - error = uiomove(cp, cc, uio); - if (error) - return (error); - /* check again for safety */ - if ((tp->t_state & TS_ISOPEN) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - } - while (cc > 0) { - if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && - (tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) { - wakeup(TSA_HUP_OR_INPUT(tp)); - goto block; - } - ttyld_rint(tp, *cp++); - cnt++; - cc--; - } - cc = 0; - } - return (0); -block: - /* - * Come here to wait for slave to open, for space - * in outq, or space in rawq, or an empty canq. - */ - if ((tp->t_state & TS_CONNECTED) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - if (flag & O_NONBLOCK) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - if (cnt == 0) - return (EWOULDBLOCK); - return (0); - } - error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); - if (error) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; + error = pts_alloc_external(fflags & (FREAD|FWRITE), td, fp, dev, name); + if (error != 0) { + destroy_dev_sched(dev); return (error); } - goto again; -} - -/*ARGSUSED*/ -static int -ptcioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - int ival; -#endif - - switch (cmd) { - - case TIOCGPGRP: - /* - * We avoid calling ttioctl on the controller since, - * in that case, tp must be the controlling terminal. - */ - *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; - return (0); - - case TIOCPKT: - if (*(int *)data) { - if (pt->pt_flags & PF_UCNTL) - return (EINVAL); - pt->pt_flags |= PF_PKT; - } else - pt->pt_flags &= ~PF_PKT; - return (0); - - case TIOCUCNTL: - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) - return (EINVAL); - pt->pt_flags |= PF_UCNTL; - } else - pt->pt_flags &= ~PF_UCNTL; - return (0); - } - - /* - * The rest of the ioctls shouldn't be called until - * the slave is open. - */ - if ((tp->t_state & TS_ISOPEN) == 0) - return (EAGAIN); - - switch (cmd) { -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: -#endif - case TIOCSETD: - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: - /* - * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. - * ttywflush(tp) will hang if there are characters in - * the outq. - */ - ndflush(&tp->t_outq, tp->t_outq.c_cc); - break; -#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ - defined(COMPAT_FREEBSD4) || defined(COMPAT_43) - case _IO('t', 95): - ival = IOCPARM_IVAL(data); - data = (caddr_t)&ival; - /* FALLTHROUGH */ -#endif - case TIOCSIG: - if (*(unsigned int *)data >= NSIG || - *(unsigned int *)data == 0) - return(EINVAL); - if ((tp->t_lflag&NOFLSH) == 0) - ttyflush(tp, FREAD|FWRITE); - if (tp->t_pgrp != NULL) { - PGRP_LOCK(tp->t_pgrp); - pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); - PGRP_UNLOCK(tp->t_pgrp); - } - if ((*(unsigned int *)data == SIGINFO) && - ((tp->t_lflag&NOKERNINFO) == 0)) - ttyinfo(tp); - return(0); + /* Raise a warning when a legacy PTY has been allocated. */ + if (pty_warningcnt > 0) { + pty_warningcnt--; + printf("pid %d (%s) is using legacy pty devices%s\n", + td->td_proc->p_pid, td->td_name, + pty_warningcnt ? "" : " - not logging anymore"); } - return (ptsioctl(dev, cmd, data, flag, td)); + return (0); } -/*ARGSUSED*/ -static int -ptsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - struct tty *tp = dev->si_tty; - struct ptsc *pt = dev->si_drv1; - u_char *cc = tp->t_cc; - int stop, error; - - if (cmd == TIOCEXT) { - /* - * When the EXTPROC bit is being toggled, we need - * to send an TIOCPKT_IOCTL if the packet driver - * is turned on. - */ - if (*(int *)data) { - if (pt->pt_flags & PF_PKT) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag |= EXTPROC; - } else { - if ((tp->t_lflag & EXTPROC) && - (pt->pt_flags & PF_PKT)) { - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - } - tp->t_lflag &= ~EXTPROC; - } - return(0); - } - error = ttyioctl(dev, cmd, data, flag, td); - if (error == ENOTTY) { - if (pt->pt_flags & PF_UCNTL && - (cmd & ~0xff) == UIOCCMD(0)) { - if (cmd & 0xff) { - pt->pt_ucntl = (u_char)cmd; - ptcwakeup(tp, FREAD); - } - return (0); - } - error = ENOTTY; - } - /* - * If external processing and packet mode send ioctl packet. - */ - if ((tp->t_lflag&EXTPROC) && (pt->pt_flags & PF_PKT)) { - switch(cmd) { - case TIOCSETA: - case TIOCSETAW: - case TIOCSETAF: -#ifdef COMPAT_43TTY - case TIOCSETP: - case TIOCSETN: - case TIOCSETC: - case TIOCSLTC: - case TIOCLBIS: - case TIOCLBIC: - case TIOCLSET: -#endif - pt->pt_send |= TIOCPKT_IOCTL; - ptcwakeup(tp, FREAD); - break; - default: - break; - } - } - stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) - && CCEQ(cc[VSTART], CTRL('q')); - if (pt->pt_flags & PF_NOSTOP) { - if (stop) { - pt->pt_send &= ~TIOCPKT_NOSTOP; - pt->pt_send |= TIOCPKT_DOSTOP; - pt->pt_flags &= ~PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } else { - if (!stop) { - pt->pt_send &= ~TIOCPKT_DOSTOP; - pt->pt_send |= TIOCPKT_NOSTOP; - pt->pt_flags |= PF_NOSTOP; - ptcwakeup(tp, FREAD); - } - } - return (error); -} +static struct cdevsw ptydev_cdevsw = { + .d_version = D_VERSION, + .d_fdopen = ptydev_fdopen, + .d_name = "ptydev", +}; static void pty_clone(void *arg, struct ucred *cr, char *name, int namelen, struct cdev **dev) { - char *cp; int u; + /* Cloning is already satisfied. */ if (*dev != NULL) return; - if (bcmp(name, "pty", 3) != 0) - return; - if (name[5] != '\0' || name[3] == '\0') + + /* Only catch /dev/ptyXX. */ + if (namelen != 5 || bcmp(name, "pty", 3) != 0) return; - cp = index(names, name[3]); - if (cp == NULL) + + /* Only catch /dev/pty[l-sL-S]X. */ + if (!(name[3] >= 'l' && name[3] <= 's') && + !(name[3] >= 'L' && name[3] <= 'S')) return; - u = (cp - names) * 32; - if (name[4] >= '0' && name[4] <= '9') - u += name[4] - '0'; - else if (name[4] >= 'a' && name[4] <= 'v') - u += name[4] - 'a' + 10; - else + + /* Only catch /dev/pty[l-sL-S][0-9a-v]. */ + if (!(name[4] >= '0' && name[4] <= '9') && + !(name[4] >= 'a' && name[4] <= 'v')) return; - *dev = make_dev_credf(MAKEDEV_REF, &ptc_cdevsw, unit2minor(u), cr, - UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32); - (*dev)->si_flags |= SI_CHEAPCLONE; - return; + + /* Create the controller device node. */ + u = (unsigned int)name[3] << 8 | name[4]; + *dev = make_dev_credf(MAKEDEV_REF, &ptydev_cdevsw, u, + NULL, UID_ROOT, GID_WHEEL, 0666, name); } static void -ptc_drvinit(void *unused) +pty_init(void *unused) { EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000); } -SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,ptc_drvinit,NULL); +SYSINIT(pty, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pty_init, NULL); diff --git a/sys/kern/tty_ttydisc.c b/sys/kern/tty_ttydisc.c new file mode 100644 index 0000000..b52fc18 --- /dev/null +++ b/sys/kern/tty_ttydisc.c @@ -0,0 +1,1131 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/filio.h> +#include <sys/kernel.h> +#include <sys/signal.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/tty.h> +#include <sys/ttycom.h> +#include <sys/ttydefaults.h> +#include <sys/uio.h> +#include <sys/vnode.h> + +/* + * Standard TTYDISC `termios' line discipline. + */ + +/* Statistics. */ +static long tty_nin = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, + &tty_nin, 0, "Total amount of bytes received"); +static long tty_nout = 0; +SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, + &tty_nout, 0, "Total amount of bytes transmitted"); + +/* termios comparison macro's. */ +#define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ + tp->t_termios.c_cc[v] == (c)) +#define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) + +/* Characters that cannot be modified through c_cc. */ +#define CTAB '\t' +#define CNL '\n' +#define CCR '\r' + +/* Character is a control character. */ +#define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) +/* Control character should be processed on echo. */ +#define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ + (c) == CNL || (c) == CCR)) +/* Control character should be printed using ^X notation. */ +#define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ + ((q) || ((c) != CTAB && (c) != CNL)))) +/* Character is whitespace. */ +#define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) +/* Character is alphanumeric. */ +#define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) + +void +ttydisc_open(struct tty *tp) +{ + ttydisc_optimize(tp); +} + +void +ttydisc_close(struct tty *tp) +{ + + /* Clean up our flags when leaving the discipline. */ + tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); + + /* POSIX states we should flush when close() is called. */ + ttyinq_flush(&tp->t_inq); + ttyoutq_flush(&tp->t_outq); + + if (!tty_gone(tp)) { + ttydevsw_inwakeup(tp); + ttydevsw_outwakeup(tp); + } +} + +static int +ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) +{ + char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ + int error; + size_t clen, flen = 0, n = 1; + unsigned char lastc = _POSIX_VDISABLE; + +#define BREAK_ADD(c) do { \ + if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ + breakc[n++] = tp->t_termios.c_cc[c]; \ +} while (0) + /* Determine which characters we should trigger on. */ + BREAK_ADD(VEOF); + BREAK_ADD(VEOL); +#undef BREAK_ADD + breakc[n] = '\0'; + + do { + /* + * Quite a tricky case: unlike the old TTY + * implementation, this implementation copies data back + * to userspace in large chunks. Unfortunately, we can't + * calculate the line length on beforehand if it crosses + * ttyinq_block boundaries, because multiple reads could + * then make this code read beyond the newline. + * + * This is why we limit the read to: + * - The size the user has requested + * - The blocksize (done in tty_inq.c) + * - The amount of bytes until the newline + * + * This causes the line length to be recalculated after + * each block has been copied to userspace. This will + * cause the TTY layer to return data in chunks using + * the blocksize (except the first and last blocks). + */ + clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, + &lastc); + + /* No more data. */ + if (clen == 0) { + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + continue; + } + + /* Don't send the EOF char back to userspace. */ + if (CMP_CC(VEOF, lastc)) + flen = 1; + + MPASS(flen <= clen); + + /* Read and throw away the EOF character. */ + error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); + if (error) + return (error); + + } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); + + return (0); +} + +static int +ttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) +{ + size_t vmin = tp->t_termios.c_cc[VMIN]; + int oresid = uio->uio_resid; + int error; + + MPASS(tp->t_termios.c_cc[VTIME] == 0); + + /* + * This routine implements the easy cases of read()s while in + * non-canonical mode, namely case B and D, where we don't have + * any timers at all. + */ + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* We have to wait for more. */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + } +} + +static int +ttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, + int oresid) +{ + size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); + unsigned int vtime = tp->t_termios.c_cc[VTIME]; + struct timeval end, now, left; + int error, hz; + + MPASS(tp->t_termios.c_cc[VTIME] != 0); + + /* Determine when the read should be expired. */ + end.tv_sec = vtime / 10; + end.tv_usec = (vtime % 10) * 100000; + getmicrotime(&now); + timevaladd(&end, &now); + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* Calculate how long we should wait. */ + getmicrotime(&now); + if (timevalcmp(&now, &end, >)) + return (0); + left = end; + timevalsub(&left, &now); + hz = tvtohz(&left); + + /* + * We have to wait for more. If the timer expires, we + * should return a 0-byte read. + */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_timedwait(tp, &tp->t_inwait, hz); + if (error) + return (error == EWOULDBLOCK ? 0 : error); + } + + return (0); +} + +static int +ttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) +{ + size_t vmin = tp->t_termios.c_cc[VMIN]; + int oresid = uio->uio_resid; + int error; + + MPASS(tp->t_termios.c_cc[VMIN] != 0); + MPASS(tp->t_termios.c_cc[VTIME] != 0); + + /* + * When using the interbyte timer, the timer should be started + * after the first byte has been received. We just call into the + * generic read timer code after we've received the first byte. + */ + + for (;;) { + error = ttyinq_read_uio(&tp->t_inq, tp, uio, + uio->uio_resid, 0); + if (error) + return (error); + if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) + return (0); + + /* + * Not enough data, but we did receive some, which means + * we'll now start using the interbyte timer. + */ + if (oresid != uio->uio_resid) + break; + + /* We have to wait for more. */ + if (ioflag & IO_NDELAY) + return (EWOULDBLOCK); + else if (tp->t_flags & TF_ZOMBIE) + return (0); + + error = tty_wait(tp, &tp->t_inwait); + if (error) + return (error); + } + + return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); +} + +int +ttydisc_read(struct tty *tp, struct uio *uio, int ioflag) +{ + int error; + size_t c; + + tty_lock_assert(tp, MA_OWNED); + + if (uio->uio_resid == 0) + return (0); + + if (CMP_FLAG(l, ICANON)) + error = ttydisc_read_canonical(tp, uio, ioflag); + else if (tp->t_termios.c_cc[VTIME] == 0) + error = ttydisc_read_raw_no_timer(tp, uio, ioflag); + else if (tp->t_termios.c_cc[VMIN] == 0) + error = ttydisc_read_raw_read_timer(tp, uio, ioflag, + uio->uio_resid); + else + error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); + + c = ttyinq_bytesleft(&tp->t_inq); + if (c >= tp->t_inlow) { + /* Unset the input watermark when we've got enough space. */ + tty_hiwat_in_unblock(tp); + } + + return (error); +} + +static __inline unsigned int +ttydisc_findchar(const char *obstart, unsigned int oblen) +{ + const char *c = obstart; + + while (oblen--) { + if (CTL_VALID(*c)) + break; + c++; + } + + return (c - obstart); +} + +static int +ttydisc_write_oproc(struct tty *tp, char c) +{ + unsigned int scnt, error; + + MPASS(CMP_FLAG(o, OPOST)); + MPASS(CTL_VALID(c)); + +#define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) + switch (c) { + case CEOF: + /* End-of-text dropping. */ + if (CMP_FLAG(o, ONOEOT)) + return (0); + return PRINT_NORMAL(); + + case CERASE2: + /* Handle backspace to fix tab expansion. */ + if (PRINT_NORMAL() != 0) + return (-1); + if (tp->t_column > 0) + tp->t_column--; + return (0); + + case CTAB: + /* Tab expansion. */ + scnt = 8 - (tp->t_column & 7); + if (CMP_FLAG(o, TAB3)) { + error = ttyoutq_write_nofrag(&tp->t_outq, + " ", scnt); + } else { + error = PRINT_NORMAL(); + } + if (error) + return (-1); + + tp->t_column += scnt; + MPASS((tp->t_column % 8) == 0); + return (0); + + case CNL: + /* Newline conversion. */ + if (CMP_FLAG(o, ONLCR)) { + /* Convert \n to \r\n. */ + error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); + } else { + error = PRINT_NORMAL(); + } + if (error) + return (-1); + + if (CMP_FLAG(o, ONLCR|ONLRET)) { + tp->t_column = tp->t_writepos = 0; + ttyinq_reprintpos_set(&tp->t_inq); + } + return (0); + + case CCR: + /* Carriage return to newline conversion. */ + if (CMP_FLAG(o, OCRNL)) + c = CNL; + /* Omit carriage returns on column 0. */ + if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) + return (0); + if (PRINT_NORMAL() != 0) + return (-1); + + tp->t_column = tp->t_writepos = 0; + ttyinq_reprintpos_set(&tp->t_inq); + return (0); + } + + /* + * Invisible control character. Print it, but don't + * increase the column count. + */ + return PRINT_NORMAL(); +#undef PRINT_NORMAL +} + +/* + * Just like the old TTY implementation, we need to copy data in chunks + * into a temporary buffer. One of the reasons why we need to do this, + * is because output processing (only TAB3 though) may allow the buffer + * to grow eight times. + */ +int +ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) +{ + char ob[256]; + char *obstart; + int error = 0; + unsigned int oblen = 0; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_ZOMBIE) + return (EIO); + + /* + * We don't need to check whether the process is the foreground + * process group or if we have a carrier. This is already done + * in ttydev_write(). + */ + + while (uio->uio_resid > 0) { + unsigned int nlen; + + MPASS(oblen == 0); + + /* Step 1: read data. */ + + tty_unlock(tp); + + obstart = ob; + nlen = MIN(uio->uio_resid, sizeof ob); + error = uiomove(ob, nlen, uio); + if (error != 0) + break; + oblen = nlen; + + tty_lock(tp); + if (tty_gone(tp)) { + error = ENXIO; + break; + } + + MPASS(oblen > 0); + + /* Step 2: process data. */ + do { + unsigned int plen, wlen; + + /* Search for special characters for post processing. */ + if (CMP_FLAG(o, OPOST)) { + plen = ttydisc_findchar(obstart, oblen); + } else { + plen = oblen; + } + + if (plen == 0) { + /* + * We're going to process a character + * that needs processing + */ + if (ttydisc_write_oproc(tp, *obstart) == 0) { + obstart++; + oblen--; + + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + continue; + } + } else { + /* We're going to write regular data. */ + wlen = ttyoutq_write(&tp->t_outq, obstart, plen); + obstart += wlen; + oblen -= wlen; + tp->t_column += wlen; + + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + + if (wlen == plen) + continue; + } + + /* Watermark reached. Try to sleep. */ + tp->t_flags |= TF_HIWAT_OUT; + + if (ioflag & IO_NDELAY) { + error = EWOULDBLOCK; + goto done; + } + + /* + * The driver may write back the data + * synchronously. Be sure to check the high + * water mark before going to sleep. + */ + ttydevsw_outwakeup(tp); + if ((tp->t_flags & TF_HIWAT_OUT) == 0) + continue; + + error = tty_wait(tp, &tp->t_outwait); + if (error) + goto done; + + if (tp->t_flags & TF_ZOMBIE) { + error = EIO; + goto done; + } + } while (oblen > 0); + } + +done: + ttydevsw_outwakeup(tp); + + /* + * Add the amount of bytes that we didn't process back to the + * uio counters. We need to do this to make sure write() doesn't + * count the bytes we didn't store in the queue. + */ + uio->uio_resid += oblen; + return (error); +} + +void +ttydisc_optimize(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + + if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && + (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && + (!CMP_FLAG(i, PARMRK) || + CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && + !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { + tp->t_flags |= TF_BYPASS; + } else { + tp->t_flags &= ~TF_BYPASS; + } +} + +void +ttydisc_modem(struct tty *tp, int open) +{ + + tty_lock_assert(tp, MA_OWNED); + + if (open) + cv_broadcast(&tp->t_dcdwait); + + /* + * Ignore modem status lines when CLOCAL is turned on, but don't + * enter the zombie state when the TTY isn't opened, because + * that would cause the TTY to be in zombie state after being + * opened. + */ + if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) + return; + + if (open == 0) { + /* + * Lost carrier. + */ + tp->t_flags |= TF_ZOMBIE; + + tty_signal_sessleader(tp, SIGHUP); + tty_flush(tp, FREAD|FWRITE); + } else { + /* + * Carrier is back again. + */ + + /* XXX: what should we do here? */ + } +} + +static int +ttydisc_echo_force(struct tty *tp, char c, int quote) +{ + + if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { + /* + * Only perform postprocessing when OPOST is turned on + * and the character is an unquoted BS/TB/NL/CR. + */ + return ttydisc_write_oproc(tp, c); + } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { + /* + * Only use ^X notation when ECHOCTL is turned on and + * we've got an quoted control character. + */ + char ob[2] = { '^', '?' }; + + /* Print ^X notation. */ + if (c != 0x7f) + ob[1] = c + 'A' - 1; + + tp->t_column += 2; + return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); + } else { + /* Can just be printed. */ + tp->t_column++; + return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); + } +} + +static int +ttydisc_echo(struct tty *tp, char c, int quote) +{ + + /* + * Only echo characters when ECHO is turned on, or ECHONL when + * the character is an unquoted newline. + */ + if (!CMP_FLAG(l, ECHO) && + (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) + return (0); + + return ttydisc_echo_force(tp, c, quote); +} + + +static void +ttydisc_reprint_char(void *d, char c, int quote) +{ + struct tty *tp = d; + + ttydisc_echo(tp, c, quote); +} + +static void +ttydisc_reprint(struct tty *tp) +{ + cc_t c; + + /* Print ^R\n, followed by the line. */ + c = tp->t_termios.c_cc[VREPRINT]; + if (c != _POSIX_VDISABLE) + ttydisc_echo(tp, c, 0); + ttydisc_echo(tp, CNL, 0); + ttyinq_reprintpos_reset(&tp->t_inq); + + ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); +} + +struct ttydisc_recalc_length { + struct tty *tp; + unsigned int curlen; +}; + +static void +ttydisc_recalc_charlength(void *d, char c, int quote) +{ + struct ttydisc_recalc_length *data = d; + struct tty *tp = data->tp; + + if (CTL_PRINT(c, quote)) { + if (CMP_FLAG(l, ECHOCTL)) + data->curlen += 2; + } else if (c == CTAB) { + data->curlen += 8 - (data->curlen & 7); + } else { + data->curlen++; + } +} + +static unsigned int +ttydisc_recalc_linelength(struct tty *tp) +{ + struct ttydisc_recalc_length data = { tp, tp->t_writepos }; + + ttyinq_line_iterate_from_reprintpos(&tp->t_inq, + ttydisc_recalc_charlength, &data); + return (data.curlen); +} + +static int +ttydisc_rubchar(struct tty *tp) +{ + char c; + int quote; + unsigned int prevpos, tablen; + + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return (-1); + ttyinq_unputchar(&tp->t_inq); + + if (CMP_FLAG(l, ECHO)) { + /* + * Remove the character from the screen. This is even + * safe for characters that span multiple characters + * (tabs, quoted, etc). + */ + if (tp->t_writepos >= tp->t_column) { + /* Retype the sentence. */ + ttydisc_reprint(tp); + } else if (CMP_FLAG(l, ECHOE)) { + if (CTL_PRINT(c, quote)) { + /* Remove ^X formatted chars. */ + if (CMP_FLAG(l, ECHOCTL)) { + tp->t_column -= 2; + ttyoutq_write_nofrag(&tp->t_outq, + "\b\b \b\b", 6); + } + } else if (c == ' ') { + /* Space character needs no rubbing. */ + tp->t_column -= 1; + ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); + } else if (c == CTAB) { + /* + * Making backspace work with tabs is + * quite hard. Recalculate the length of + * this character and remove it. + * + * Because terminal settings could be + * changed while the line is being + * inserted, the calculations don't have + * to be correct. Make sure we keep the + * tab length within proper bounds. + */ + prevpos = ttydisc_recalc_linelength(tp); + if (prevpos >= tp->t_column) + tablen = 1; + else + tablen = tp->t_column - prevpos; + if (tablen > 8) + tablen = 8; + + tp->t_column = prevpos; + ttyoutq_write_nofrag(&tp->t_outq, + "\b\b\b\b\b\b\b\b", tablen); + return (0); + } else { + /* + * Remove a regular character by + * punching a space over it. + */ + tp->t_column -= 1; + ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); + } + } else { + /* Don't print spaces. */ + ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); + } + } + + return (0); +} + +static void +ttydisc_rubword(struct tty *tp) +{ + char c; + int quote, alnum; + + /* Strip whitespace first. */ + for (;;) { + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return; + if (!CTL_WHITE(c)) + break; + ttydisc_rubchar(tp); + } + + /* + * Record whether the last character from the previous iteration + * was alphanumeric or not. We need this to implement ALTWERASE. + */ + alnum = CTL_ALNUM(c); + for (;;) { + ttydisc_rubchar(tp); + + if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) + return; + if (CTL_WHITE(c)) + return; + if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) + return; + } +} + +int +ttydisc_rint(struct tty *tp, char c, int flags) +{ + int signal, quote = 0; + char ob[3] = { 0xff, 0x00 }; + size_t ol; + + tty_lock_assert(tp, MA_OWNED); + + atomic_add_long(&tty_nin, 1); + + if (tp->t_flags & TF_BYPASS) + goto processed; + + if (flags) { + if (flags & TRE_BREAK) { + if (CMP_FLAG(i, IGNBRK)) { + /* Ignore break characters. */ + return (0); + } else if (CMP_FLAG(i, BRKINT)) { + /* Generate SIGINT on break. */ + tty_flush(tp, FREAD|FWRITE); + tty_signal_pgrp(tp, SIGINT); + return (0); + } else { + /* Just print it. */ + goto parmrk; + } + } else if (flags & TRE_FRAMING || + (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { + if (CMP_FLAG(i, IGNPAR)) { + /* Ignore bad characters. */ + return (0); + } else { + /* Just print it. */ + goto parmrk; + } + } + } + + /* Allow any character to perform a wakeup. */ + if (CMP_FLAG(i, IXANY)) + tp->t_flags &= ~TF_STOPPED; + + /* Remove the top bit. */ + if (CMP_FLAG(i, ISTRIP)) + c &= ~0x80; + + /* Skip input processing when we want to print it literally. */ + if (tp->t_flags & TF_LITERAL) { + tp->t_flags &= ~TF_LITERAL; + quote = 1; + goto processed; + } + + /* Special control characters that are implementation dependent. */ + if (CMP_FLAG(l, IEXTEN)) { + /* Accept the next character as literal. */ + if (CMP_CC(VLNEXT, c)) { + if (CMP_FLAG(l, ECHO)) { + if (CMP_FLAG(l, ECHOE)) + ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); + else + ttydisc_echo(tp, c, 0); + } + tp->t_flags |= TF_LITERAL; + return (0); + } + } + + /* + * Handle signal processing. + */ + if (CMP_FLAG(l, ISIG)) { + if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { + if (CMP_CC(VSTATUS, c)) { + tty_signal_pgrp(tp, SIGINFO); + return (0); + } + } + + /* + * When compared to the old implementation, this + * implementation also flushes the output queue. POSIX + * is really brief about this, but does makes us assume + * we have to do so. + */ + signal = 0; + if (CMP_CC(VINTR, c)) { + signal = SIGINT; + } else if (CMP_CC(VQUIT, c)) { + signal = SIGQUIT; + } else if (CMP_CC(VSUSP, c)) { + signal = SIGTSTP; + } + + if (signal != 0) { + /* + * Echo the character before signalling the + * processes. + */ + if (!CMP_FLAG(l, NOFLSH)) + tty_flush(tp, FREAD|FWRITE); + ttydisc_echo(tp, c, 0); + tty_signal_pgrp(tp, signal); + return (0); + } + } + + /* + * Handle start/stop characters. + */ + if (CMP_FLAG(i, IXON)) { + if (CMP_CC(VSTOP, c)) { + /* Stop it if we aren't stopped yet. */ + if ((tp->t_flags & TF_STOPPED) == 0) { + tp->t_flags |= TF_STOPPED; + return (0); + } + /* + * Fallthrough: + * When VSTART == VSTOP, we should make this key + * toggle it. + */ + if (!CMP_CC(VSTART, c)) + return (0); + } + if (CMP_CC(VSTART, c)) { + tp->t_flags &= ~TF_STOPPED; + return (0); + } + } + + /* Conversion of CR and NL. */ + switch (c) { + case CCR: + if (CMP_FLAG(i, IGNCR)) + return (0); + if (CMP_FLAG(i, ICRNL)) + c = CNL; + break; + case CNL: + if (CMP_FLAG(i, INLCR)) + c = CCR; + break; + } + + /* Canonical line editing. */ + if (CMP_FLAG(l, ICANON)) { + if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { + ttydisc_rubchar(tp); + return (0); + } else if (CMP_CC(VKILL, c)) { + while (ttydisc_rubchar(tp) == 0); + return (0); + } else if (CMP_FLAG(l, IEXTEN)) { + if (CMP_CC(VWERASE, c)) { + ttydisc_rubword(tp); + return (0); + } else if (CMP_CC(VREPRINT, c)) { + ttydisc_reprint(tp); + return (0); + } + } + } + +processed: + if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { + /* Print 0xff 0xff. */ + ob[1] = 0xff; + ol = 2; + quote = 1; + } else { + ob[0] = c; + ol = 1; + } + + goto print; + +parmrk: + if (CMP_FLAG(i, PARMRK)) { + /* Prepend 0xff 0x00 0x.. */ + ob[2] = c; + ol = 3; + quote = 1; + } else { + ob[0] = c; + ol = 1; + } + +print: + /* See if we can store this on the input queue. */ + if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { + /* We cannot. Enable the input watermark. */ + tty_hiwat_in_block(tp); + return (-1); + } + + /* + * In raw mode, we canonicalize after receiving a single + * character. Otherwise, we canonicalize when we receive a + * newline, VEOL or VEOF, but only when it isn't quoted. + */ + if (!CMP_FLAG(l, ICANON) || + (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { + ttyinq_canonicalize(&tp->t_inq); + } + + ttydisc_echo(tp, c, quote); + + return (0); +} + +size_t +ttydisc_rint_bypass(struct tty *tp, char *buf, size_t len) +{ + size_t ret; + + tty_lock_assert(tp, MA_OWNED); + + MPASS(tp->t_flags & TF_BYPASS); + + atomic_add_long(&tty_nin, len); + + ret = ttyinq_write(&tp->t_inq, buf, len, 0); + ttyinq_canonicalize(&tp->t_inq); + + return (ret); +} + +void +ttydisc_rint_done(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + /* Wake up readers. */ + tty_wakeup(tp, FREAD); + /* Wake up driver for echo. */ + ttydevsw_outwakeup(tp); +} + +static void +ttydisc_wakeup_watermark(struct tty *tp) +{ + size_t c; + + c = ttyoutq_bytesleft(&tp->t_outq); + if (tp->t_flags & TF_HIWAT_OUT) { + /* Only allow us to run when we're below the watermark. */ + if (c < tp->t_outlow) + return; + + /* Reset the watermark. */ + tp->t_flags &= ~TF_HIWAT_OUT; + } else { + /* Only run when we have data at all. */ + if (c == 0) + return; + } + tty_wakeup(tp, FWRITE); +} + +size_t +ttydisc_getc(struct tty *tp, void *buf, size_t len) +{ + int ret; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + ret = ttyoutq_read(&tp->t_outq, buf, len); + ttydisc_wakeup_watermark(tp); + + atomic_add_long(&tty_nout, ret); + + return (ret); +} + +int +ttydisc_getc_uio(struct tty *tp, struct uio *uio) +{ + int error; + int obytes = uio->uio_resid; + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + error = ttyoutq_read_uio(&tp->t_outq, tp, uio); + ttydisc_wakeup_watermark(tp); + + atomic_add_long(&tty_nout, obytes - uio->uio_resid); + + return (error); +} + +/* + * XXX: not really related to the TTYDISC, but we'd better put + * tty_putchar() here, because we need to perform proper output + * processing. + */ + +int +tty_putchar(struct tty *tp, char c) +{ + tty_lock_assert(tp, MA_OWNED); + + if (tty_gone(tp)) + return (-1); + + ttydisc_echo_force(tp, c, 0); + tp->t_writepos = tp->t_column; + ttyinq_reprintpos_set(&tp->t_inq); + + ttydevsw_outwakeup(tp); + return (0); +} diff --git a/sys/modules/nmdm/Makefile b/sys/modules/nmdm/Makefile index b788334..25e9470 100644 --- a/sys/modules/nmdm/Makefile +++ b/sys/modules/nmdm/Makefile @@ -4,14 +4,5 @@ KMOD= nmdm SRCS= nmdm.c -SRCS+= opt_compat.h opt_tty.h vnode_if.h - -.if !defined(KERNBUILDDIR) -opt_compat.h: - echo "#define COMPAT_43 1" >opt_compat.h - -opt_tty.h: - echo "#define TTYHOG 8192" >opt_tty.h -.endif .include <bsd.kmod.mk> diff --git a/sys/modules/rc/Makefile b/sys/modules/rc/Makefile index cc8d228..ffa6163 100644 --- a/sys/modules/rc/Makefile +++ b/sys/modules/rc/Makefile @@ -3,11 +3,6 @@ .PATH: ${.CURDIR}/../../dev/rc KMOD= rc -SRCS= rc.c device_if.h bus_if.h isa_if.h opt_tty.h - -.if !defined(KERNBUILDDIR) -opt_tty.h: - echo "#define TTYHOG 8192" >opt_tty.h -.endif +SRCS= rc.c device_if.h bus_if.h isa_if.h .include <bsd.kmod.mk> diff --git a/sys/pc98/cbus/scterm-sck.c b/sys/pc98/cbus/scterm-sck.c index 9b45490..ced4a92 100644 --- a/sys/pc98/cbus/scterm-sck.c +++ b/sys/pc98/cbus/scterm-sck.c @@ -1110,7 +1110,7 @@ ascii_end: static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td) + struct thread *td) { term_stat *tcp = scp->ts; vid_info_t *vi; diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index f55a98c..25576a7 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -154,7 +154,7 @@ device cardbus # CardBus (32-bit) bus # Serial (COM) ports options COM_MULTIPORT #options COM_ESP # ESP98 -device sio # 8250, 16[45]50, 8251 based serial ports +#device sio # 8250, 16[45]50, 8251 based serial ports device mse #device joy @@ -231,7 +231,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/pc98/conf/NOTES b/sys/pc98/conf/NOTES index 4a5786e..efa651e 100644 --- a/sys/pc98/conf/NOTES +++ b/sys/pc98/conf/NOTES @@ -203,7 +203,7 @@ options BPF_JITTER # sio: serial ports (see sio(4)), including support for various # PC Card devices, such as Modem and NICs # -device sio +#device sio hint.sio.0.at="isa" hint.sio.0.port="0x3F8" hint.sio.0.flags="0x10" diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index 86e9687..d1de70e 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -117,7 +117,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device ofwd # Open Firmware disks device gif # IPv6 and IPv4 tunneling diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index 91a95a3..b2a0bdf 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -206,7 +206,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/sun4v/conf/GENERIC b/sys/sun4v/conf/GENERIC index 4e93a06..1a3f695 100644 --- a/sys/sun4v/conf/GENERIC +++ b/sys/sun4v/conf/GENERIC @@ -170,7 +170,7 @@ device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. -device pty # Pseudo-ttys (telnet etc) +device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) diff --git a/sys/sun4v/sun4v/hvcons.c b/sys/sun4v/sun4v/hvcons.c index 0217a85..314d15c 100644 --- a/sys/sun4v/sun4v/hvcons.c +++ b/sys/sun4v/sun4v/hvcons.c @@ -53,16 +53,14 @@ __FBSDID("$FreeBSD$"); #define HVCN_POLL_FREQ 10 - -static d_open_t hvcn_open; -static d_close_t hvcn_close; - -static struct cdevsw hvcn_cdevsw = { - .d_version = D_VERSION, - .d_open = hvcn_open, - .d_close = hvcn_close, - .d_name = "hvcn", - .d_flags = D_TTY | D_NEEDGIANT, +static tsw_open_t hvcn_open; +static tsw_outwakeup_t hvcn_outwakeup; +static tsw_close_t hvcn_close; + +static struct ttydevsw hvcn_class = { + .tsw_open = hvcn_open, + .tsw_outwakeup = hvcn_outwakeup, + .tsw_close = hvcn_close, }; #define PCBURST 16 @@ -81,9 +79,6 @@ static struct callout_handle hvcn_timeouthandle static int alt_break_state; #endif -static void hvcn_tty_start(struct tty *); -static int hvcn_tty_param(struct tty *, struct termios *); -static void hvcn_tty_stop(struct tty *, int); static void hvcn_timeout(void *); static cn_probe_t hvcn_cnprobe; @@ -113,67 +108,27 @@ hv_cnputs(char *p) } static int -hvcn_open(struct cdev *dev, int flag, int mode, struct thread *td) +hvcn_open(struct tty *tp) { - struct tty *tp; - int error, setuptimeout; - - setuptimeout = 0; - - if (dev->si_tty == NULL) { - hvcn_tp = ttyalloc(); - dev->si_tty = hvcn_tp; - hvcn_tp->t_dev = dev; - } - tp = dev->si_tty; - - tp->t_oproc = hvcn_tty_start; - tp->t_param = hvcn_tty_param; - tp->t_stop = hvcn_tty_stop; - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_CARR_ON; - ttyconsolemode(tp, 0); - - setuptimeout = 1; - } else if ((tp->t_state & TS_XCLUDE) && priv_check(td, - PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); - } - - error = ttyld_open(tp, dev); -#if defined(SIMULATOR) || 1 - if (error == 0 && setuptimeout) { - int polltime; + /* + * Set up timeout to trigger fake interrupts to transmit + * trailing data. + */ + polltime = hz / HVCN_POLL_FREQ; + if (polltime < 1) + polltime = 1; + hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime); - polltime = hz / HVCN_POLL_FREQ; - if (polltime < 1) { - polltime = 1; - } + buflen = 0; - hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime); - } -#endif - return (error); + return (0); } -static int -hvcn_close(struct cdev *dev, int flag, int mode, struct thread *td) +static void +hvcn_close(struct tty *tp) { - int unit; - struct tty *tp; - - unit = minor(dev); - tp = dev->si_tty; - - if (unit != 0) - return (ENXIO); - untimeout(hvcn_timeout, tp, hvcn_timeouthandle); - ttyld_close(tp, flag); - tty_close(tp); - - return (0); } static void @@ -210,7 +165,8 @@ done: static void hvcn_cninit(struct consdev *cp) { - sprintf(cp->cn_name, "hvcn"); + + strcpy(cp->cn_name, "hvcn"); } static int @@ -295,64 +251,43 @@ hvcn_cnputc(struct consdev *cp, int c) } while (error == H_EWOULDBLOCK); } -static int -hvcn_tty_param(struct tty *tp, struct termios *t) -{ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = t->c_cflag; - - return (0); -} - static void -hvcn_tty_start(struct tty *tp) +hvcn_outwakeup(struct tty *tp) { - if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { - tp->t_state |= TS_BUSY; + for (;;) { + /* Refill the input buffer. */ + if (buflen == 0) { + buflen = ttydisc_getc(tp, buf, PCBURST); + bufindex = 0; + } - do { - if (buflen == 0) { - buflen = q_to_b(&tp->t_outq, buf, PCBURST); - bufindex = 0; - } - while (buflen) { - if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK) - goto done; - bufindex++; - buflen--; - } - } while (tp->t_outq.c_cc != 0); - done: - tp->t_state &= ~TS_BUSY; - ttwwakeup(tp); + /* Transmit the input buffer. */ + while (buflen) { + if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK) + return; + bufindex++; + buflen--; + } } } static void -hvcn_tty_stop(struct tty *tp, int flag) -{ - if ((tp->t_state & TS_BUSY) && !(tp->t_state & TS_TTSTOP)) - tp->t_state |= TS_FLUSH; - - -} - -static void hvcn_intr(void *v) { - struct tty *tp; + struct tty *tp = v; int c; - tp = (struct tty *)v; + tty_lock(tp); + /* Receive data. */ while ((c = hvcn_cncheckc(NULL)) != -1) - if (tp->t_state & TS_ISOPEN) - ttyld_rint(tp, c); + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); - if (tp->t_outq.c_cc != 0 || buflen != 0) - hvcn_tty_start(tp); + /* Transmit trailing data. */ + hvcn_outwakeup(tp); + tty_unlock(tp); } static void @@ -381,7 +316,7 @@ static int hvcn_dev_attach(device_t dev) { - struct cdev *cdev; + struct tty *tp; int error, rid; /* belongs in attach - but attach is getting called multiple times @@ -392,8 +327,9 @@ hvcn_dev_attach(device_t dev) hvcn_consdev.cn_name[0] == '\0') return (ENXIO); - cdev = make_dev(&hvcn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", 1); - make_dev_alias(cdev, "hvcn"); + tp = tty_alloc(&hvcn_class, NULL, NULL); + tty_makedev(tp, NULL, "v%r", 1); + tty_makealias(tp, "hvcn"); rid = 0; diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 739c3fe..8128fa7 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -46,7 +46,6 @@ #include <sys/queue.h> #endif -struct tty; struct snapdata; struct devfs_dirent; struct cdevsw; @@ -85,13 +84,11 @@ struct cdev { u_long si_usecount; u_long si_threadcount; union { - struct tty *__sit_tty; struct snapdata *__sid_snapdata; } __si_u; char __si_namebuf[SPECNAMELEN + 1]; }; -#define si_tty __si_u.__sit_tty #define si_snapdata __si_u.__sid_snapdata #ifdef _KERNEL diff --git a/sys/sys/file.h b/sys/sys/file.h index 6f7dc00..461b7e8 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -62,6 +62,7 @@ struct socket; #define DTYPE_MQUEUE 7 /* posix message queue */ #define DTYPE_SHM 8 /* swap-backed shared memory */ #define DTYPE_SEM 9 /* posix semaphore */ +#define DTYPE_PTS 10 /* pseudo teletype master device */ #ifdef _KERNEL diff --git a/sys/sys/linedisc.h b/sys/sys/linedisc.h deleted file mode 100644 index 659aa4b..0000000 --- a/sys/sys/linedisc.h +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2004 - * Poul-Henning Kamp. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)conf.h 8.5 (Berkeley) 1/9/95 - * $FreeBSD$ - */ - -#ifndef _SYS_LINEDISC_H_ -#define _SYS_LINEDISC_H_ - -#ifdef _KERNEL - -struct tty; - -typedef int l_open_t(struct cdev *dev, struct tty *tp); -typedef int l_close_t(struct tty *tp, int flag); -typedef int l_read_t(struct tty *tp, struct uio *uio, int flag); -typedef int l_write_t(struct tty *tp, struct uio *uio, int flag); -typedef int l_ioctl_t(struct tty *tp, u_long cmd, caddr_t data, - int flag, struct thread *td); -typedef int l_rint_t(int c, struct tty *tp); -typedef int l_start_t(struct tty *tp); -typedef int l_modem_t(struct tty *tp, int flag); - -/* - * Line discipline switch table - */ -struct linesw { - l_open_t *l_open; - l_close_t *l_close; - l_read_t *l_read; - l_write_t *l_write; - l_ioctl_t *l_ioctl; - l_rint_t *l_rint; - l_start_t *l_start; - l_modem_t *l_modem; -}; - -extern struct linesw *linesw[]; -extern int nlinesw; - -int ldisc_register(int , struct linesw *); -void ldisc_deregister(int); -#define LDISC_LOAD -1 /* Loadable line discipline */ - -l_read_t l_noread; -l_write_t l_nowrite; -l_ioctl_t l_nullioctl; - -static __inline int -ttyld_open(struct tty *tp, struct cdev *dev) -{ - - return ((*linesw[tp->t_line]->l_open)(dev, tp)); -} - -static __inline int -ttyld_close(struct tty *tp, int flag) -{ - - return ((*linesw[tp->t_line]->l_close)(tp, flag)); -} - -static __inline int -ttyld_read(struct tty *tp, struct uio *uio, int flag) -{ - - return ((*linesw[tp->t_line]->l_read)(tp, uio, flag)); -} - -static __inline int -ttyld_write(struct tty *tp, struct uio *uio, int flag) -{ - - return ((*linesw[tp->t_line]->l_write)(tp, uio, flag)); -} - -static __inline int -ttyld_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, - struct thread *td) -{ - - return ((*linesw[tp->t_line]->l_ioctl)(tp, cmd, data, flag, td)); -} - -static __inline int -ttyld_rint(struct tty *tp, int c) -{ - - return ((*linesw[tp->t_line]->l_rint)(c, tp)); -} - -static __inline int -ttyld_start(struct tty *tp) -{ - - return ((*linesw[tp->t_line]->l_start)(tp)); -} - -static __inline int -ttyld_modem(struct tty *tp, int flag) -{ - - return ((*linesw[tp->t_line]->l_modem)(tp, flag)); -} - -#endif /* _KERNEL */ - -#endif /* !_SYS_LINEDISC_H_ */ diff --git a/sys/sys/param.h b/sys/sys/param.h index 773f24f..c0415b0 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -57,7 +57,7 @@ * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 800044 /* Master, propagated to newvers */ +#define __FreeBSD_version 800045 /* Master, propagated to newvers */ #ifndef LOCORE #include <sys/types.h> diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 3779591..0b919b2 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -72,10 +72,10 @@ * (c) const until freeing */ struct session { - int s_count; /* (m) Ref cnt; pgrps in session. */ + u_int s_count; /* Ref cnt; pgrps in session - atomic. */ struct proc *s_leader; /* (m + e) Session leader. */ struct vnode *s_ttyvp; /* (m) Vnode of controlling tty. */ - struct tty *s_ttyp; /* (m) Controlling tty. */ + struct tty *s_ttyp; /* (e) Controlling tty. */ pid_t s_sid; /* (c) Session ID. */ /* (m) Setlogin() name: */ char s_login[roundup(MAXLOGNAME, sizeof(long))]; @@ -644,8 +644,6 @@ MALLOC_DECLARE(M_ZOMBIE); #define NO_PID 100000 #define SESS_LEADER(p) ((p)->p_session->s_leader == (p)) -#define SESSHOLD(s) ((s)->s_count++) -#define SESSRELE(s) sessrele(s) #define STOPEVENT(p, e, v) do { \ @@ -807,7 +805,8 @@ void pstats_fork(struct pstats *src, struct pstats *dst); void pstats_free(struct pstats *ps); int securelevel_ge(struct ucred *cr, int level); int securelevel_gt(struct ucred *cr, int level); -void sessrele(struct session *); +void sess_hold(struct session *); +void sess_release(struct session *); int setrunnable(struct thread *); void setsugid(struct proc *p); int sigonstack(size_t sp); diff --git a/sys/sys/resource.h b/sys/sys/resource.h index d01b0bd..03fbd70 100644 --- a/sys/sys/resource.h +++ b/sys/sys/resource.h @@ -93,8 +93,9 @@ struct rusage { #define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */ #define RLIMIT_VMEM 10 /* virtual process size (inclusive of mmap) */ #define RLIMIT_AS RLIMIT_VMEM /* standard name for RLIMIT_VMEM */ +#define RLIMIT_NPTS 11 /* pseudo-terminals */ -#define RLIM_NLIMITS 11 /* number of resource limits */ +#define RLIM_NLIMITS 12 /* number of resource limits */ #define RLIM_INFINITY ((rlim_t)(((uint64_t)1 << 63) - 1)) /* XXX Missing: RLIM_SAVED_MAX, RLIM_SAVED_CUR */ diff --git a/sys/sys/resourcevar.h b/sys/sys/resourcevar.h index cd80cd4..0a867d1 100644 --- a/sys/sys/resourcevar.h +++ b/sys/sys/resourcevar.h @@ -91,6 +91,7 @@ struct uidinfo { LIST_ENTRY(uidinfo) ui_hash; /* (c) hash chain of uidinfos */ long ui_sbsize; /* (b) socket buffer space consumed */ long ui_proccnt; /* (b) number of processes */ + long ui_ptscnt; /* (b) number of pseudo-terminals */ uid_t ui_uid; /* (a) uid */ u_int ui_ref; /* (b) reference count */ }; @@ -106,6 +107,7 @@ void calcru(struct proc *p, struct timeval *up, struct timeval *sp); int chgproccnt(struct uidinfo *uip, int diff, rlim_t maxval); int chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to, rlim_t maxval); +int chgptscnt(struct uidinfo *uip, int diff, rlim_t maxval); int fuswintr(void *base); struct plimit *lim_alloc(void); diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 3b1b2c1..a92fc9f 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #define SYS_syscall 0 @@ -419,4 +419,5 @@ #define SYS_renameat 501 #define SYS_symlinkat 502 #define SYS_unlinkat 503 -#define SYS_MAXSYSCALL 504 +#define SYS_posix_openpt 504 +#define SYS_MAXSYSCALL 505 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index b0172f4..b09938a 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -1,7 +1,7 @@ # FreeBSD system call names. # DO NOT EDIT-- this file is automatically generated. # $FreeBSD$ -# created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp +# created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian MIASM = \ syscall.o \ exit.o \ @@ -367,4 +367,5 @@ MIASM = \ readlinkat.o \ renameat.o \ symlinkat.o \ - unlinkat.o + unlinkat.o \ + posix_openpt.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index feb582a..fe2a229 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: src/sys/kern/syscalls.master,v 1.242 2008/03/31 12:06:55 kib Exp + * created from FreeBSD: head/sys/kern/syscalls.master 178888 2008-05-09 23:03:00Z julian */ #ifndef _SYS_SYSPROTO_H_ @@ -1630,6 +1630,9 @@ struct unlinkat_args { char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)]; char flag_l_[PADL_(int)]; int flag; char flag_r_[PADR_(int)]; }; +struct posix_openpt_args { + char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)]; +}; int nosys(struct thread *, struct nosys_args *); void sys_exit(struct thread *, struct sys_exit_args *); int fork(struct thread *, struct fork_args *); @@ -1987,6 +1990,7 @@ int readlinkat(struct thread *, struct readlinkat_args *); int renameat(struct thread *, struct renameat_args *); int symlinkat(struct thread *, struct symlinkat_args *); int unlinkat(struct thread *, struct unlinkat_args *); +int posix_openpt(struct thread *, struct posix_openpt_args *); #ifdef COMPAT_43 @@ -2569,6 +2573,7 @@ int freebsd4_sigreturn(struct thread *, struct freebsd4_sigreturn_args *); #define SYS_AUE_renameat AUE_RENAMEAT #define SYS_AUE_symlinkat AUE_SYMLINKAT #define SYS_AUE_unlinkat AUE_UNLINKAT +#define SYS_AUE_posix_openpt AUE_POSIXOPENPT #undef PAD_ #undef PADL_ diff --git a/sys/sys/termios.h b/sys/sys/termios.h index 00259f1..7defbd3 100644 --- a/sys/sys/termios.h +++ b/sys/sys/termios.h @@ -83,10 +83,6 @@ typedef __pid_t pid_t; #define _POSIX_VDISABLE 0xff -#ifndef _POSIX_SOURCE -#define CCEQ(val, c) ((c) == (val) ? (val) != _POSIX_VDISABLE : 0) -#endif - /* * Input flags - software input processing */ @@ -112,7 +108,12 @@ typedef __pid_t pid_t; #define OPOST 0x00000001 /* enable following output processing */ #ifndef _POSIX_SOURCE #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */ -#define OXTABS 0x00000004 /* expand tabs to spaces */ +#define TABDLY 0x00000004 /* tab delay mask */ +#define TAB0 0x00000000 /* no tab delay and expansion */ +#define TAB3 0x00000004 /* expand tabs to spaces */ +#ifndef _KERNEL +#define OXTABS TAB3 +#endif /* !_KERNEL */ #define ONOEOT 0x00000008 /* discard EOT's (^D) on output) */ #define OCRNL 0x00000010 /* map CR to NL on output */ #define ONOCR 0x00000020 /* no CR output at column 0 */ @@ -143,7 +144,9 @@ typedef __pid_t pid_t; #define CDTR_IFLOW 0x00040000 /* DTR flow control of input */ #define CDSR_OFLOW 0x00080000 /* DSR flow control of output */ #define CCAR_OFLOW 0x00100000 /* DCD flow control of output */ -#define MDMBUF 0x00100000 /* old name for CCAR_OFLOW */ +#ifndef _KERNEL +#define MDMBUF CCAR_OFLOW +#endif /* !_KERNEL */ #endif @@ -230,6 +233,10 @@ struct termios { #ifndef _KERNEL +#ifndef _POSIX_SOURCE +#define CCEQ(val, c) ((c) != _POSIX_VDISABLE && (c) == (val)) +#endif + /* * Commands passed to tcsetattr() for setting the termios structure. */ diff --git a/sys/sys/tty.h b/sys/sys/tty.h index e887a2a..8e0e77d 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -1,19 +1,9 @@ /*- - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Copyright (c) 2002 Networks Associates Technologies, Inc. + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> * All rights reserved. * - * Portions of this software were developed for the FreeBSD Project by - * ThinkSec AS and NAI Labs, the Security Research Division of Network - * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 - * ("CBOSS"), as part of the DARPA CHATS research program. + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,14 +13,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) diff --git a/sys/sys/ttycom.h b/sys/sys/ttycom.h index 1d97ab3..60b6145 100644 --- a/sys/sys/ttycom.h +++ b/sys/sys/ttycom.h @@ -73,7 +73,8 @@ struct winsize { /* 23-25 obsolete or unused */ #define TIOCGETD _IOR('t', 26, int) /* get line discipline */ #define TIOCSETD _IOW('t', 27, int) /* set line discipline */ - /* 28-69 free */ +#define TIOCPTMASTER _IO('t', 28) /* pts master validation */ + /* 29-69 free */ /* 80-84 slip */ #define TIOCGDRAINWAIT _IOR('t', 86, int) /* get ttywait timeout */ #define TIOCSDRAINWAIT _IOW('t', 87, int) /* set ttywait timeout */ diff --git a/sys/sys/ttydefaults.h b/sys/sys/ttydefaults.h index 5813bda..d120fb0 100644 --- a/sys/sys/ttydefaults.h +++ b/sys/sys/ttydefaults.h @@ -52,6 +52,7 @@ #define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO #define TTYDEF_CFLAG (CREAD | CS8 | HUPCL) #define TTYDEF_SPEED (B9600) +#define TTYDEF_SPEED_PSEUDO (B38400) /* * Control Character Defaults diff --git a/sys/sys/ttydevsw.h b/sys/sys/ttydevsw.h new file mode 100644 index 0000000..a4f5afb --- /dev/null +++ b/sys/sys/ttydevsw.h @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYDEVSW_H_ +#define _SYS_TTYDEVSW_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +/* + * Driver routines that are called from the line discipline to adjust + * hardware parameters and such. + */ +typedef int tsw_open_t(struct tty *); +typedef void tsw_close_t(struct tty *); +typedef void tsw_outwakeup_t(struct tty *); +typedef void tsw_inwakeup_t(struct tty *); +typedef int tsw_ioctl_t(struct tty *, u_long, caddr_t, struct thread *); +typedef int tsw_param_t(struct tty *, struct termios *); +typedef int tsw_modem_t(struct tty *, int, int); +typedef int tsw_mmap_t(struct tty *, vm_offset_t, vm_paddr_t *, int); +typedef void tsw_free_t(void *); + +struct ttydevsw { + unsigned int tsw_flags; /* Default TTY flags. */ + + tsw_open_t *tsw_open; /* Device opening. */ + tsw_close_t *tsw_close; /* Device closure. */ + + tsw_outwakeup_t *tsw_outwakeup; /* Output available. */ + tsw_inwakeup_t *tsw_inwakeup; /* Input can be stored again. */ + + tsw_ioctl_t *tsw_ioctl; /* ioctl() hooks. */ + tsw_param_t *tsw_param; /* TIOCSETA device parameter setting. */ + tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */ + + tsw_mmap_t *tsw_mmap; /* mmap() hooks. */ + + tsw_free_t *tsw_free; /* Destructor. */ +}; + +static __inline int +ttydevsw_open(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_open(tp); +} + +static __inline void +ttydevsw_close(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + tp->t_devsw->tsw_close(tp); +} + +static __inline void +ttydevsw_outwakeup(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + /* Prevent spurious wakeups. */ + if (tp->t_flags & TF_STOPPED) + return; + if (ttyoutq_bytesused(&tp->t_outq) == 0) + return; + + tp->t_devsw->tsw_outwakeup(tp); +} + +static __inline void +ttydevsw_inwakeup(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + /* Prevent spurious wakeups. */ + if (tp->t_flags & TF_HIWAT_IN) + return; + + tp->t_devsw->tsw_inwakeup(tp); +} + +static __inline int +ttydevsw_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_ioctl(tp, cmd, data, td); +} + +static __inline int +ttydevsw_param(struct tty *tp, struct termios *t) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_param(tp, t); +} + +static __inline int +ttydevsw_modem(struct tty *tp, int sigon, int sigoff) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_modem(tp, sigon, sigoff); +} + +static __inline int +ttydevsw_mmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot) +{ + MPASS(!tty_gone(tp)); + + return tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot); +} + +static __inline void +ttydevsw_free(struct tty *tp) +{ + MPASS(tty_gone(tp)); + + tp->t_devsw->tsw_free(tty_softc(tp)); +} + +#endif /* !_SYS_TTYDEVSW_H_ */ diff --git a/sys/sys/ttydisc.h b/sys/sys/ttydisc.h new file mode 100644 index 0000000..dd170e6 --- /dev/null +++ b/sys/sys/ttydisc.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYDISC_H_ +#define _SYS_TTYDISC_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +struct cv; +struct thread; +struct tty; +struct uio; + +/* Top half routines. */ +void ttydisc_open(struct tty *); +void ttydisc_close(struct tty *); +int ttydisc_read(struct tty *, struct uio *, int); +int ttydisc_write(struct tty *, struct uio *, int); +void ttydisc_optimize(struct tty *); + +/* Bottom half routines. */ +void ttydisc_modem(struct tty *, int); +#define ttydisc_can_bypass(tp) ((tp)->t_flags & TF_BYPASS) +int ttydisc_rint(struct tty *, char, int); +size_t ttydisc_rint_bypass(struct tty *, char *, size_t); +void ttydisc_rint_done(struct tty *); +size_t ttydisc_getc(struct tty *, void *buf, size_t); +int ttydisc_getc_uio(struct tty *, struct uio *); + +/* Error codes for ttydisc_rint(). */ +#define TRE_FRAMING 0x01 +#define TRE_PARITY 0x02 +#define TRE_OVERRUN 0x04 +#define TRE_BREAK 0x08 + +static __inline size_t +ttydisc_read_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyinq_bytescanonicalized(&tp->t_inq); +} + +static __inline size_t +ttydisc_write_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyoutq_bytesleft(&tp->t_outq); +} + +static __inline size_t +ttydisc_rint_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + return ttyinq_bytesleft(&tp->t_inq); +} + +static __inline size_t +ttydisc_getc_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + return ttyoutq_bytesused(&tp->t_outq); +} + +#endif /* !_SYS_TTYDISC_H_ */ diff --git a/sys/sys/ttyqueue.h b/sys/sys/ttyqueue.h new file mode 100644 index 0000000..3e72fef --- /dev/null +++ b/sys/sys/ttyqueue.h @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Portions of this software were developed under sponsorship from Snow + * B.V., the Netherlands. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYQUEUE_H_ +#define _SYS_TTYQUEUE_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +struct tty; +struct ttyinq_block; +struct ttyoutq_block; +struct uio; + +/* Data input queue. */ +struct ttyinq { + TAILQ_HEAD(ttyinq_bhead, ttyinq_block) ti_list; + struct ttyinq_block *ti_startblock; + struct ttyinq_block *ti_reprintblock; + struct ttyinq_block *ti_lastblock; + unsigned int ti_begin; + unsigned int ti_linestart; + unsigned int ti_reprint; + unsigned int ti_end; + unsigned int ti_nblocks; +}; +#define TTYINQ_DATASIZE 128 + +/* Data output queue. */ +struct ttyoutq { + STAILQ_HEAD(, ttyoutq_block) to_list; + struct ttyoutq_block *to_lastblock; + unsigned int to_begin; + unsigned int to_end; + unsigned int to_nblocks; +}; +#define TTYOUTQ_DATASIZE (256 - sizeof(STAILQ_ENTRY(ttyoutq_block))) + +#ifdef _KERNEL +/* Input queue handling routines. */ +void ttyinq_setsize(struct ttyinq *, struct tty *, size_t); +int ttyinq_read_uio(struct ttyinq *, struct tty *, struct uio *, + size_t, size_t); +size_t ttyinq_write(struct ttyinq *, const void *, size_t, int); +int ttyinq_write_nofrag(struct ttyinq *, const void *, size_t, int); +void ttyinq_canonicalize(struct ttyinq *); +size_t ttyinq_findchar(struct ttyinq *, const char *, size_t, char *); +void ttyinq_flush(struct ttyinq *); +int ttyinq_peekchar(struct ttyinq *, char *, int *); +void ttyinq_unputchar(struct ttyinq *); +void ttyinq_reprintpos_set(struct ttyinq *); +void ttyinq_reprintpos_reset(struct ttyinq *); + +static __inline size_t +ttyinq_getsize(struct ttyinq *ti) +{ + return (ti->ti_nblocks * TTYINQ_DATASIZE); +} + +static __inline size_t +ttyinq_bytesleft(struct ttyinq *ti) +{ + size_t len; + + /* Make sure the usage never exceeds the length. */ + len = ti->ti_nblocks * TTYINQ_DATASIZE; + MPASS(len >= ti->ti_end); + + return (len - ti->ti_end); +} + +static __inline size_t +ttyinq_bytescanonicalized(struct ttyinq *ti) +{ + MPASS(ti->ti_begin <= ti->ti_linestart); + + return (ti->ti_linestart - ti->ti_begin); +} + +static __inline size_t +ttyinq_bytesline(struct ttyinq *ti) +{ + MPASS(ti->ti_linestart <= ti->ti_end); + + return (ti->ti_end - ti->ti_linestart); +} + +/* Input buffer iteration. */ +typedef void ttyinq_line_iterator_t(void *, char, int); +void ttyinq_line_iterate_from_linestart(struct ttyinq *, + ttyinq_line_iterator_t *, void *); +void ttyinq_line_iterate_from_reprintpos(struct ttyinq *, + ttyinq_line_iterator_t *, void *); + +/* Output queue handling routines. */ +void ttyoutq_flush(struct ttyoutq *); +void ttyoutq_setsize(struct ttyoutq *, struct tty *, size_t); +size_t ttyoutq_read(struct ttyoutq *, void *, size_t); +int ttyoutq_read_uio(struct ttyoutq *, struct tty *, struct uio *); +size_t ttyoutq_write(struct ttyoutq *, const void *, size_t); +int ttyoutq_write_nofrag(struct ttyoutq *, const void *, size_t); + +static __inline size_t +ttyoutq_getsize(struct ttyoutq *to) +{ + return (to->to_nblocks * TTYOUTQ_DATASIZE); +} + +static __inline size_t +ttyoutq_bytesleft(struct ttyoutq *to) +{ + size_t len; + + /* Make sure the usage never exceeds the length. */ + len = to->to_nblocks * TTYOUTQ_DATASIZE; + MPASS(len >= to->to_end); + + return (len - to->to_end); +} + +static __inline size_t +ttyoutq_bytesused(struct ttyoutq *to) +{ + return (to->to_end - to->to_begin); +} +#endif /* _KERNEL */ + +#endif /* !_SYS_TTYQUEUE_H_ */ diff --git a/sys/sys/user.h b/sys/sys/user.h index e8b6a1f..0af8528 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -250,6 +250,7 @@ struct user { #define KF_TYPE_MQUEUE 7 #define KF_TYPE_SHM 8 #define KF_TYPE_SEM 9 +#define KF_TYPE_PTS 10 #define KF_TYPE_UNKNOWN 255 #define KF_VTYPE_VNON 0 diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c index 886c3bb..6ed7076 100644 --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include <sys/un.h> #include <sys/unpcb.h> #include <sys/sysctl.h> +#include <sys/tty.h> #include <sys/filedesc.h> #include <sys/queue.h> #define _WANT_FILE @@ -154,6 +155,7 @@ int devfs_filestat(struct vnode *vp, struct filestat *fsp); char *getmnton(struct mount *m); void pipetrans(struct pipe *pi, int i, int flag); void socktrans(struct socket *sock, int i); +void ptstrans(struct tty *tp, int i, int flag); void getinetproto(int number); int getfname(const char *filename); void usage(void); @@ -411,6 +413,12 @@ dofiles(struct kinfo_proc *kp) vtrans(file.f_vnode, i, file.f_flag); } #endif +#ifdef DTYPE_PTS + else if (file.f_type == DTYPE_PTS) { + if (checkfile == 0) + ptstrans(file.f_data, i, file.f_flag); + } +#endif else { dprintf(stderr, "unknown file type %d for file %d of pid %d\n", @@ -887,6 +895,50 @@ bad: printf("* error\n"); } +void +ptstrans(struct tty *tp, int i, int flag) +{ + struct tty tty; + char *name; + char rw[3]; + dev_t rdev; + + PREFIX(i); + + /* Obtain struct tty. */ + if (!KVM_READ(tp, &tty, sizeof(struct tty))) { + dprintf(stderr, "can't read tty at %p\n", (void *)tp); + goto bad; + } + + /* Figure out the device name. */ + name = kdevtoname(tty.t_dev); + if (name == NULL) { + dprintf(stderr, "can't determine tty name at %p\n", (void *)tp); + goto bad; + } + + rw[0] = '\0'; + if (flag & FREAD) + strcat(rw, "r"); + if (flag & FWRITE) + strcat(rw, "w"); + + printf("* pseudo-terminal master "); + if (nflg || !name) { + rdev = dev2udev(tty.t_dev); + printf("%10d,%-2d", major(rdev), minor(rdev)); + } else { + printf("%10s", name); + } + printf(" %2s\n", rw); + + free(name); + + return; +bad: + printf("* error\n"); +} /* * Read the cdev structure in the kernel in order to work out the diff --git a/usr.bin/procstat/procstat.1 b/usr.bin/procstat/procstat.1 index daffbbf..43761fd 100644 --- a/usr.bin/procstat/procstat.1 +++ b/usr.bin/procstat/procstat.1 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 19, 2008 +.Dd August 20, 2008 .Dt PROCSTAT 1 .Os .Sh NAME @@ -153,6 +153,8 @@ message queue pipe .It s socket +.It t +pseudo-terminal master .It v vnode .El diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c index 39ce82b..3667913 100644 --- a/usr.bin/procstat/procstat_files.c +++ b/usr.bin/procstat/procstat_files.c @@ -222,6 +222,10 @@ procstat_files(pid_t pid, struct kinfo_proc *kipp) str = "h"; break; + case KF_TYPE_PTS: + str = "t"; + break; + case KF_TYPE_SEM: str = "e"; break; @@ -296,6 +300,7 @@ procstat_files(pid_t pid, struct kinfo_proc *kipp) switch (kif->kf_type) { case KF_TYPE_VNODE: case KF_TYPE_FIFO: + case KF_TYPE_PTS: printf("%-3s ", "-"); printf("%-18s", kif->kf_path); break; diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8 index 3dd67a6..c3f7e60 100644 --- a/usr.sbin/pstat/pstat.8 +++ b/usr.sbin/pstat/pstat.8 @@ -35,7 +35,7 @@ .\" @(#)pstat.8 8.5 (Berkeley) 5/13/94 .\" $FreeBSD$ .\" -.Dd April 1, 2007 +.Dd August 20, 2008 .Dt PSTAT 8 .Os .Sh NAME @@ -153,100 +153,63 @@ with these headings: .Bl -tag -width indent .It LINE Device name. -.It RAW -Number of characters in raw input queue. +.It INQ +Number of characters that can be stored in the input queue. .It CAN -Number of characters in canonicalized input queue. -.It OUT -Number of characters in output queue. -.It IHIWT -High water mark for input. -.It ILOWT +Number of characters in the input queue which can be read. +.It LIN +Number of characters in the input queue which cannot be read yet. +.It LOW +Low water mark for input. +.It OUTQ +Number of characters that can be stored in the output queue. +.It USE +Number of bytes in the output queue. +.It LOW Low water mark for input. -.It OHWT -High water mark for output. -.It LWT -Low water mark for output. .It COL Calculated column position of terminal. +.It SESS +Kernel address of the session structure. +.It PGID +Process group for which this is the controlling terminal. .It STATE Miscellaneous state variables encoded thus: .Pp .Bl -tag -width indent -compact -.It T -delay timeout in progress -.It W -waiting for open to complete -.It O -open -.It F -outq has been flushed during DMA +.It I +init/lock-state device nodes present .It C -carrier is on -.It c -connection open +callout device nodes present +.It O +opened +.It G +gone .It B -busy doing output -.It A -process is waiting for space in output queue -.It a -process is waiting for output to complete -.It X -open for exclusive use -.It S -output stopped (ixon flow control) -.It m -output stopped (carrier flow control) -.It o -output stopped (CTS flow control) -.It d -output stopped (DSR flow control) -.It K -input stopped +busy in +.Xr open 2 .It Y send SIGIO for input events -.It D -state for lowercase -.Ql \e -work -.It E -within a -.Ql \e.../ -for PRTRUB .It L next character is literal -.It P -retyping suspended input (PENDIN) -.It N -counting tab width, ignore FLUSHO +.It H +high watermark reached +.It X +open for exclusive use +.It S +output stopped (ixon flow control) .It l block mode input routine in use -.It s -i/o being snooped .It Z connection lost .El -.It SESS -Kernel address of the session structure. -.It PGID -Process group for which this is the controlling terminal. -.It DISC -Line discipline; -.Ql term -for -TTYDISC -or -.Ql ntty -for -NTTYDISC -or -.Ql slip -for -SLIPDISC -or -.Ql ppp -for -PPPDISC. +.Pp +The +.Ql i +and +.Ql o +characters refer to the previous character, to differentiate between +input and output. .El .It Fl M Extract values associated with the name list from the specified core. diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c index a15292b..51828cb 100644 --- a/usr.sbin/pstat/pstat.c +++ b/usr.sbin/pstat/pstat.c @@ -214,7 +214,7 @@ static const char fhdr64[] = /* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */ static const char hdr[] = -" LINE RAW CAN OUT IHIWT ILOWT OHWT LWT COL STATE SESS PGID DISC\n"; +" LINE INQ CAN LIN LOW OUTQ USE LOW COL SESS PGID STATE\n"; static void ttymode_kvm(void) @@ -232,18 +232,18 @@ ttymode_kvm(void) while (tp != NULL) { if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty) errx(1, "kvm_read(): %s", kvm_geterr(kd)); - xt.xt_rawcc = tty.t_rawq.c_cc; - xt.xt_cancc = tty.t_canq.c_cc; - xt.xt_outcc = tty.t_outq.c_cc; -#define XT_COPY(field) xt.xt_##field = tty.t_##field - XT_COPY(line); - XT_COPY(state); - XT_COPY(column); - XT_COPY(ihiwat); - XT_COPY(ilowat); - XT_COPY(ohiwat); - XT_COPY(olowat); -#undef XT_COPY + xt.xt_insize = tty.t_inq.ti_nblocks * TTYINQ_DATASIZE; + xt.xt_incc = tty.t_inq.ti_linestart - tty.t_inq.ti_begin; + xt.xt_inlc = tty.t_inq.ti_end - tty.t_inq.ti_linestart; + xt.xt_inlow = tty.t_inlow; + xt.xt_outsize = tty.t_outq.to_nblocks * TTYOUTQ_DATASIZE; + xt.xt_outcc = tty.t_outq.to_end - tty.t_outq.to_begin; + xt.xt_outlow = tty.t_outlow; + xt.xt_column = tty.t_column; + /* xt.xt_pgid = ... */ + /* xt.xt_sid = ... */ + xt.xt_flags = tty.t_flags; + xt.xt_dev = NODEV; ttyprt(&xt); tp = TAILQ_NEXT(&tty, t_list); } @@ -287,95 +287,61 @@ static struct { int flag; char val; } ttystates[] = { -#ifdef TS_WOPEN - { TS_WOPEN, 'W'}, -#endif - { TS_ISOPEN, 'O'}, - { TS_CARR_ON, 'C'}, -#ifdef TS_CONNECTED - { TS_CONNECTED, 'c'}, -#endif - { TS_TIMEOUT, 'T'}, - { TS_FLUSH, 'F'}, - { TS_BUSY, 'B'}, -#ifdef TS_ASLEEP - { TS_ASLEEP, 'A'}, -#endif -#ifdef TS_SO_OLOWAT - { TS_SO_OLOWAT, 'A'}, -#endif -#ifdef TS_SO_OCOMPLETE - { TS_SO_OCOMPLETE, 'a'}, -#endif - { TS_XCLUDE, 'X'}, - { TS_TTSTOP, 'S'}, -#ifdef TS_CAR_OFLOW - { TS_CAR_OFLOW, 'm'}, -#endif -#ifdef TS_CTS_OFLOW - { TS_CTS_OFLOW, 'o'}, -#endif -#ifdef TS_DSR_OFLOW - { TS_DSR_OFLOW, 'd'}, -#endif - { TS_TBLOCK, 'K'}, - { TS_ASYNC, 'Y'}, - { TS_BKSL, 'D'}, - { TS_ERASE, 'E'}, - { TS_LNCH, 'L'}, - { TS_TYPEN, 'P'}, - { TS_CNTTB, 'N'}, -#ifdef TS_CAN_BYPASS_L_RINT - { TS_CAN_BYPASS_L_RINT, 'l'}, -#endif -#ifdef TS_SNOOP - { TS_SNOOP, 's'}, -#endif -#ifdef TS_ZOMBIE - { TS_ZOMBIE, 'Z'}, +#if 0 + { TF_NOPREFIX, 'N' }, #endif - { 0, '\0'}, + { TF_INITLOCK, 'I' }, + { TF_CALLOUT, 'C' }, + + /* Keep these together -> 'Oi' and 'Oo'. */ + { TF_OPENED, 'O' }, + { TF_OPENED_IN, 'i' }, + { TF_OPENED_OUT,'o' }, + + { TF_GONE, 'G' }, + { TF_OPENCLOSE, 'B' }, + { TF_ASYNC, 'Y' }, + { TF_LITERAL, 'L' }, + + /* Keep these together -> 'Hi' and 'Ho'. */ + { TF_HIWAT, 'H' }, + { TF_HIWAT_IN, 'i' }, + { TF_HIWAT_OUT, 'o' }, + + { TF_STOPPED, 'S' }, + { TF_EXCLUDE, 'X' }, + { TF_BYPASS, 'l' }, + { TF_ZOMBIE, 'Z' }, + + { 0, '\0' }, }; static void ttyprt(struct xtty *xt) { int i, j; - char *name, state[20]; + char *name; if (xt->xt_size != sizeof *xt) errx(1, "struct xtty size mismatch"); if (usenumflag || xt->xt_dev == 0 || (name = devname(xt->xt_dev, S_IFCHR)) == NULL) - printf(" %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev)); + printf("%5d,%4d ", major(xt->xt_dev), minor(xt->xt_dev)); else - (void)printf("%7s ", name); - (void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc); - (void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc, - xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat, - xt->xt_column); + printf("%10s ", name); + printf("%5zu %4zu %4zu %4zu %5zu %4zu %4zu %5u %5d %5d ", + xt->xt_insize, xt->xt_incc, xt->xt_inlc, + (xt->xt_insize - xt->xt_inlow), xt->xt_outsize, + xt->xt_outcc, (xt->xt_outsize - xt->xt_outlow), + xt->xt_column, xt->xt_sid, xt->xt_pgid); for (i = j = 0; ttystates[i].flag; i++) - if (xt->xt_state & ttystates[i].flag) - state[j++] = ttystates[i].val; + if (xt->xt_flags & ttystates[i].flag) { + putchar(ttystates[i].val); + j++; + } if (j == 0) - state[j++] = '-'; - state[j] = '\0'; - (void)printf("%-6s %8d", state, xt->xt_sid); - (void)printf("%6d ", xt->xt_pgid); - switch (xt->xt_line) { - case TTYDISC: - (void)printf("term\n"); - break; - case SLIPDISC: - (void)printf("slip\n"); - break; - case PPPDISC: - (void)printf("ppp\n"); - break; - default: - (void)printf("%d\n", xt->xt_line); - break; - } + putchar('-'); + putchar('\n'); } static void |