Commit | Line | Data |
---|---|---|
984263bc | 1 | /*- |
22ff886e AH |
2 | * (MPSAFE) |
3 | * | |
984263bc MD |
4 | * Copyright (c) 1982, 1986, 1990, 1991, 1993 |
5 | * The Regents of the University of California. All rights reserved. | |
6 | * (c) UNIX System Laboratories, Inc. | |
7 | * All or some portions of this file are derived from material licensed | |
8 | * to the University of California by American Telephone and Telegraph | |
9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
10 | * the permission of UNIX System Laboratories, Inc. | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
dc71b7ab | 20 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
21 | * may be used to endorse or promote products derived from this software |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | * @(#)tty.c 8.8 (Berkeley) 1/21/94 | |
37 | * $FreeBSD: src/sys/kern/tty.c,v 1.129.2.5 2002/03/11 01:32:31 dd Exp $ | |
38 | */ | |
39 | ||
22ff886e AH |
40 | /* |
41 | * MPSAFE NOTE: | |
42 | * Almost all functions in this file are acquiring the tty token due to their | |
43 | * access and modifications of the 'tp' (struct tty) objects. | |
44 | */ | |
45 | ||
984263bc MD |
46 | /*- |
47 | * TODO: | |
48 | * o Fix races for sending the start char in ttyflush(). | |
49 | * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). | |
50 | * With luck, there will be MIN chars before select() returns(). | |
51 | * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it. | |
52 | * o Don't allow input in TS_ZOMBIE case. It would be visible through | |
53 | * FIONREAD. | |
54 | * o Do the new sio locking stuff here and use it to avoid special | |
55 | * case for EXTPROC? | |
56 | * o Lock PENDIN too? | |
57 | * o Move EXTPROC and/or PENDIN to t_state? | |
58 | * o Wrap most of ttioctl in spltty/splx. | |
59 | * o Implement TIOCNOTTY or remove it from <sys/ioctl.h>. | |
60 | * o Send STOP if IXOFF is toggled off while TS_TBLOCK is set. | |
61 | * o Don't allow certain termios flags to affect disciplines other | |
62 | * than TTYDISC. Cancel their effects before switch disciplines | |
63 | * and ignore them if they are set while we are in another | |
64 | * discipline. | |
65 | * o Now that historical speed conversions are handled here, don't | |
66 | * do them in drivers. | |
67 | * o Check for TS_CARR_ON being set while everything is closed and not | |
68 | * waiting for carrier. TS_CARR_ON isn't cleared if nothing is open, | |
69 | * so it would live until the next open even if carrier drops. | |
70 | * o Restore TS_WOPEN since it is useful in pstat. It must be cleared | |
71 | * only when _all_ openers leave open(). | |
72 | */ | |
73 | ||
984263bc MD |
74 | #include "opt_uconsole.h" |
75 | ||
76 | #include <sys/param.h> | |
77 | #include <sys/systm.h> | |
13dd34d8 | 78 | #include <sys/uio.h> |
984263bc | 79 | #include <sys/filio.h> |
805c8e8e | 80 | #include <sys/malloc.h> |
984263bc | 81 | #include <sys/proc.h> |
2b3f93ea | 82 | #include <sys/caps.h> |
984263bc | 83 | #include <sys/tty.h> |
937ccd40 SW |
84 | #define TTYDEFCHARS |
85 | #include <sys/ttydefaults.h> /* for ttydefchars, CEOT */ | |
984263bc MD |
86 | #undef TTYDEFCHARS |
87 | #include <sys/fcntl.h> | |
88 | #include <sys/conf.h> | |
89 | #include <sys/dkstat.h> | |
984263bc MD |
90 | #include <sys/kernel.h> |
91 | #include <sys/vnode.h> | |
92 | #include <sys/signalvar.h> | |
b1b4e5a6 | 93 | #include <sys/signal2.h> |
984263bc | 94 | #include <sys/resourcevar.h> |
984263bc MD |
95 | #include <sys/filedesc.h> |
96 | #include <sys/sysctl.h> | |
8dda877a | 97 | #include <sys/thread2.h> |
984263bc MD |
98 | |
99 | #include <vm/vm.h> | |
100 | #include <sys/lock.h> | |
101 | #include <vm/pmap.h> | |
102 | #include <vm/vm_map.h> | |
197b441c | 103 | #include <vm/vm_extern.h> |
984263bc MD |
104 | |
105 | MALLOC_DEFINE(M_TTYS, "ttys", "tty data structures"); | |
106 | ||
402ed7e1 RG |
107 | static int proc_compare (struct proc *p1, struct proc *p2); |
108 | static int ttnread (struct tty *tp); | |
109 | static void ttyecho (int c, struct tty *tp); | |
110 | static int ttyoutput (int c, struct tty *tp); | |
111 | static void ttypend (struct tty *tp); | |
112 | static void ttyretype (struct tty *tp); | |
113 | static void ttyrub (int c, struct tty *tp); | |
114 | static void ttyrubo (struct tty *tp, int cnt); | |
115 | static void ttyunblock (struct tty *tp); | |
116 | static int ttywflush (struct tty *tp); | |
117 | static int filt_ttyread (struct knote *kn, long hint); | |
118 | static void filt_ttyrdetach (struct knote *kn); | |
119 | static int filt_ttywrite (struct knote *kn, long hint); | |
120 | static void filt_ttywdetach (struct knote *kn); | |
984263bc MD |
121 | |
122 | /* | |
123 | * Table with character classes and parity. The 8th bit indicates parity, | |
124 | * the 7th bit indicates the character is an alphameric or underscore (for | |
125 | * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits | |
126 | * are 0 then the character needs no special processing on output; classes | |
127 | * other than 0 might be translated or (not currently) require delays. | |
128 | */ | |
129 | #define E 0x00 /* Even parity. */ | |
130 | #define O 0x80 /* Odd parity. */ | |
131 | #define PARITY(c) (char_type[c] & O) | |
132 | ||
133 | #define ALPHA 0x40 /* Alpha or underscore. */ | |
134 | #define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA) | |
135 | ||
136 | #define CCLASSMASK 0x3f | |
137 | #define CCLASS(c) (char_type[c] & CCLASSMASK) | |
138 | ||
139 | #define BS BACKSPACE | |
140 | #define CC CONTROL | |
141 | #define CR RETURN | |
142 | #define NA ORDINARY | ALPHA | |
143 | #define NL NEWLINE | |
144 | #define NO ORDINARY | |
145 | #define TB TAB | |
146 | #define VT VTAB | |
147 | ||
148 | static u_char const char_type[] = { | |
149 | E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ | |
150 | O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ | |
151 | O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ | |
152 | E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ | |
153 | O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ | |
154 | E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ | |
155 | E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ | |
156 | O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ | |
157 | O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ | |
158 | E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ | |
159 | E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ | |
160 | O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ | |
161 | E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ | |
162 | O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ | |
163 | O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ | |
164 | E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ | |
165 | /* | |
166 | * Meta chars; should be settable per character set; | |
167 | * for now, treat them all as normal characters. | |
168 | */ | |
169 | NA, NA, NA, NA, NA, NA, NA, NA, | |
170 | NA, NA, NA, NA, NA, NA, NA, NA, | |
171 | NA, NA, NA, NA, NA, NA, NA, NA, | |
172 | NA, NA, NA, NA, NA, NA, NA, NA, | |
173 | NA, NA, NA, NA, NA, NA, NA, NA, | |
174 | NA, NA, NA, NA, NA, NA, NA, NA, | |
175 | NA, NA, NA, NA, NA, NA, NA, NA, | |
176 | NA, NA, NA, NA, NA, NA, NA, NA, | |
177 | NA, NA, NA, NA, NA, NA, NA, NA, | |
178 | NA, NA, NA, NA, NA, NA, NA, NA, | |
179 | NA, NA, NA, NA, NA, NA, NA, NA, | |
180 | NA, NA, NA, NA, NA, NA, NA, NA, | |
181 | NA, NA, NA, NA, NA, NA, NA, NA, | |
182 | NA, NA, NA, NA, NA, NA, NA, NA, | |
183 | NA, NA, NA, NA, NA, NA, NA, NA, | |
184 | NA, NA, NA, NA, NA, NA, NA, NA, | |
185 | }; | |
186 | #undef BS | |
187 | #undef CC | |
188 | #undef CR | |
189 | #undef NA | |
190 | #undef NL | |
191 | #undef NO | |
192 | #undef TB | |
193 | #undef VT | |
194 | ||
195 | /* Macros to clear/set/test flags. */ | |
196 | #define SET(t, f) (t) |= (f) | |
197 | #define CLR(t, f) (t) &= ~(f) | |
198 | #define ISSET(t, f) ((t) & (f)) | |
199 | ||
200 | #undef MAX_INPUT /* XXX wrong in <sys/syslimits.h> */ | |
201 | #define MAX_INPUT TTYHOG /* XXX limit is usually larger for !ICANON */ | |
202 | ||
f5d21610 JS |
203 | uint64_t tk_nin; |
204 | SYSCTL_OPAQUE(_kern, OID_AUTO, tk_nin, CTLFLAG_RD, &tk_nin, sizeof(tk_nin), | |
205 | "LU", "TTY input statistic"); | |
206 | uint64_t tk_nout; | |
207 | SYSCTL_OPAQUE(_kern, OID_AUTO, tk_nout, CTLFLAG_RD, &tk_nout, sizeof(tk_nout), | |
208 | "LU", "TTY output statistic"); | |
209 | uint64_t tk_rawcc; | |
210 | ||
984263bc MD |
211 | /* |
212 | * list of struct tty where pstat(8) can pick it up with sysctl | |
213 | */ | |
ddac2002 | 214 | static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); |
984263bc MD |
215 | |
216 | /* | |
217 | * Initial open of tty, or (re)entry to standard tty line discipline. | |
218 | */ | |
219 | int | |
c972a82f | 220 | ttyopen(cdev_t device, struct tty *tp) |
984263bc | 221 | { |
2efb75f3 | 222 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
223 | tp->t_dev = device; |
224 | if (!ISSET(tp->t_state, TS_ISOPEN)) { | |
225 | SET(tp->t_state, TS_ISOPEN); | |
cd29885a | 226 | if (ISSET(tp->t_cflag, CLOCAL)) { |
984263bc | 227 | SET(tp->t_state, TS_CONNECTED); |
cd29885a | 228 | } |
984263bc MD |
229 | bzero(&tp->t_winsize, sizeof(tp->t_winsize)); |
230 | } | |
231 | ttsetwater(tp); | |
2efb75f3 MD |
232 | lwkt_reltoken(&tp->t_token); |
233 | ||
984263bc MD |
234 | return (0); |
235 | } | |
236 | ||
237 | /* | |
238 | * Handle close() on a tty line: flush and set to initial state, | |
239 | * bumping generation number so that pending read/write calls | |
240 | * can detect recycling of the tty. | |
8b90699b | 241 | * |
984263bc MD |
242 | * XXX our caller should have done `spltty(); l_close(); ttyclose();' |
243 | * and l_close() should have flushed, but we repeat the spltty() and | |
244 | * the flush in case there are buggy callers. | |
245 | */ | |
246 | int | |
c972a82f | 247 | ttyclose(struct tty *tp) |
984263bc | 248 | { |
2efb75f3 | 249 | lwkt_gettoken(&tp->t_token); |
58c2553a | 250 | funsetown(&tp->t_sigio); |
984263bc MD |
251 | if (constty == tp) |
252 | constty = NULL; | |
253 | ||
254 | ttyflush(tp, FREAD | FWRITE); | |
255 | clist_free_cblocks(&tp->t_canq); | |
256 | clist_free_cblocks(&tp->t_outq); | |
257 | clist_free_cblocks(&tp->t_rawq); | |
258 | ||
259 | tp->t_gen++; | |
260 | tp->t_line = TTYDISC; | |
8b90699b | 261 | ttyclearsession(tp); |
ddac2002 | 262 | tp->t_state &= TS_REGISTERED; /* clear all bits except */ |
2efb75f3 MD |
263 | lwkt_reltoken(&tp->t_token); |
264 | ||
984263bc MD |
265 | return (0); |
266 | } | |
267 | ||
8b90699b MD |
268 | /* |
269 | * Disassociate the tty from its session. Traditionally this has only been | |
270 | * a half-close, meaning that the session was still allowed to point at the | |
271 | * tty (resulting in the tty in the ps command showing something like 'p0-'), | |
272 | * even though the tty is no longer pointing at the session. | |
273 | * | |
274 | * The half close seems to be useful only for 'ps' output but there is as | |
275 | * yet no reason to remove the feature. The full-close code is currently | |
276 | * #if 0'd out. See also sess_rele() in kern/kern_proc.c. | |
277 | */ | |
278 | void | |
279 | ttyclearsession(struct tty *tp) | |
280 | { | |
281 | struct session *sp; | |
2efb75f3 | 282 | struct procglob *prg; |
58c2553a | 283 | struct pgrp *opgrp; |
8b90699b | 284 | |
2efb75f3 | 285 | lwkt_gettoken(&tp->t_token); |
58c2553a | 286 | opgrp = tp->t_pgrp; |
8b90699b | 287 | tp->t_pgrp = NULL; |
58c2553a MD |
288 | if (opgrp) { |
289 | pgrel(opgrp); | |
290 | opgrp = NULL; | |
291 | } | |
292 | ||
2efb75f3 | 293 | again: |
8b90699b | 294 | if ((sp = tp->t_session) != NULL) { |
2efb75f3 MD |
295 | prg = sp->s_prg; |
296 | lwkt_gettoken(&prg->proc_token); | |
297 | if (sp != tp->t_session) { | |
298 | lwkt_reltoken(&prg->proc_token); | |
299 | goto again; | |
300 | } | |
8b90699b | 301 | tp->t_session = NULL; |
2efb75f3 | 302 | |
8b90699b MD |
303 | #ifdef TTY_DO_FULL_CLOSE |
304 | /* FULL CLOSE (not yet) */ | |
305 | if (sp->s_ttyp == tp) { | |
306 | sp->s_ttyp = NULL; | |
94a6eea8 | 307 | ttyunhold(tp); |
8b90699b | 308 | } else { |
6ea70f76 | 309 | kprintf("ttyclearsession: warning: sp->s_ttyp != tp " |
8b90699b MD |
310 | "%p/%p\n", sp->s_ttyp, tp); |
311 | } | |
312 | #endif | |
2efb75f3 | 313 | lwkt_reltoken(&prg->proc_token); |
8b90699b | 314 | } |
2efb75f3 | 315 | lwkt_reltoken(&tp->t_token); |
8b90699b MD |
316 | } |
317 | ||
5fd012e0 | 318 | /* |
f1aeb0c0 | 319 | * Release the tty vnode association for a session. This is the |
84cca6ec MD |
320 | * 'other half' of the close. Because multiple opens of /dev/tty |
321 | * only generate a single open to the actual tty, the file modes | |
322 | * are locked to FREAD|FWRITE. | |
f1aeb0c0 MD |
323 | * |
324 | * If dorevoke is non-zero, the session is also revoked. We have to | |
325 | * close the vnode if VCTTYISOPEN is set. | |
5fd012e0 MD |
326 | */ |
327 | void | |
f1aeb0c0 | 328 | ttyclosesession(struct session *sp, int dorevoke) |
5fd012e0 MD |
329 | { |
330 | struct vnode *vp; | |
2efb75f3 | 331 | struct procglob *prg; |
5fd012e0 | 332 | |
2efb75f3 MD |
333 | prg = sp->s_prg; |
334 | lwkt_gettoken(&prg->proc_token); | |
f1aeb0c0 MD |
335 | retry: |
336 | /* | |
337 | * There may not be a controlling terminal or it may have been closed | |
338 | * out from under us. | |
339 | */ | |
22ff886e | 340 | if ((vp = sp->s_ttyvp) == NULL) { |
2efb75f3 | 341 | lwkt_reltoken(&prg->proc_token); |
5fd012e0 | 342 | return; |
22ff886e | 343 | } |
f1aeb0c0 MD |
344 | |
345 | /* | |
346 | * We need a lock if we have to close or revoke. | |
347 | */ | |
348 | if ((vp->v_flag & VCTTYISOPEN) || dorevoke) { | |
349 | vhold(vp); | |
b458d1ab | 350 | if (vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_FAILRECLAIM)) { |
f1aeb0c0 MD |
351 | vdrop(vp); |
352 | goto retry; | |
353 | } | |
354 | ||
355 | /* | |
356 | * Retry if the vnode was ripped out from under us | |
357 | */ | |
358 | if (vp != sp->s_ttyvp) { | |
359 | vn_unlock(vp); | |
360 | vdrop(vp); | |
361 | goto retry; | |
362 | } | |
363 | ||
364 | /* | |
365 | * Close and revoke as needed | |
366 | */ | |
367 | sp->s_ttyvp = NULL; | |
368 | if (vp->v_flag & VCTTYISOPEN) { | |
5fd012e0 | 369 | vclrflags(vp, VCTTYISOPEN); |
3596743e | 370 | VOP_CLOSE(vp, FREAD|FWRITE, NULL); |
5fd012e0 | 371 | } |
f1aeb0c0 | 372 | vn_unlock(vp); |
b8477cda MD |
373 | if (dorevoke) |
374 | vrevoke(vp, proc0.p_ucred); | |
f1aeb0c0 MD |
375 | vdrop(vp); |
376 | } else { | |
377 | sp->s_ttyvp = NULL; | |
5fd012e0 | 378 | } |
f1aeb0c0 | 379 | vrele(vp); |
2efb75f3 | 380 | lwkt_reltoken(&prg->proc_token); |
5fd012e0 MD |
381 | } |
382 | ||
984263bc MD |
383 | #define FLUSHQ(q) { \ |
384 | if ((q)->c_cc) \ | |
385 | ndflush(q, (q)->c_cc); \ | |
386 | } | |
387 | ||
388 | /* Is 'c' a line delimiter ("break" character)? */ | |
389 | #define TTBREAKC(c, lflag) \ | |
390 | ((c) == '\n' || (((c) == cc[VEOF] || \ | |
391 | (c) == cc[VEOL] || ((c) == cc[VEOL2] && lflag & IEXTEN)) && \ | |
392 | (c) != _POSIX_VDISABLE)) | |
393 | ||
394 | /* | |
395 | * Process input of a single character received on a tty. | |
396 | */ | |
397 | int | |
c972a82f | 398 | ttyinput(int c, struct tty *tp) |
984263bc | 399 | { |
1fd87d54 RG |
400 | tcflag_t iflag, lflag; |
401 | cc_t *cc; | |
984263bc MD |
402 | int i, err; |
403 | ||
2efb75f3 | 404 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
405 | /* |
406 | * If input is pending take it first. | |
407 | */ | |
408 | lflag = tp->t_lflag; | |
409 | if (ISSET(lflag, PENDIN)) | |
410 | ttypend(tp); | |
411 | /* | |
412 | * Gather stats. | |
413 | */ | |
f5d21610 | 414 | if (ISSET(lflag, ICANON)) |
984263bc | 415 | ++tp->t_cancc; |
f5d21610 | 416 | else |
984263bc | 417 | ++tp->t_rawcc; |
984263bc MD |
418 | ++tk_nin; |
419 | ||
420 | /* | |
421 | * Block further input iff: | |
422 | * current input > threshold AND input is available to user program | |
423 | * AND input flow control is enabled and not yet invoked. | |
424 | * The 3 is slop for PARMRK. | |
425 | */ | |
426 | iflag = tp->t_iflag; | |
427 | if (tp->t_rawq.c_cc + tp->t_canq.c_cc > tp->t_ihiwat - 3 && | |
428 | (!ISSET(lflag, ICANON) || tp->t_canq.c_cc != 0) && | |
429 | (ISSET(tp->t_cflag, CRTS_IFLOW) || ISSET(iflag, IXOFF)) && | |
430 | !ISSET(tp->t_state, TS_TBLOCK)) | |
431 | ttyblock(tp); | |
432 | ||
433 | /* Handle exceptional conditions (break, parity, framing). */ | |
434 | cc = tp->t_cc; | |
435 | err = (ISSET(c, TTY_ERRORMASK)); | |
436 | if (err) { | |
437 | CLR(c, TTY_ERRORMASK); | |
438 | if (ISSET(err, TTY_BI)) { | |
22ff886e | 439 | if (ISSET(iflag, IGNBRK)) { |
2efb75f3 | 440 | lwkt_reltoken(&tp->t_token); |
984263bc | 441 | return (0); |
22ff886e | 442 | } |
984263bc MD |
443 | if (ISSET(iflag, BRKINT)) { |
444 | ttyflush(tp, FREAD | FWRITE); | |
445 | pgsignal(tp->t_pgrp, SIGINT, 1); | |
446 | goto endcase; | |
447 | } | |
448 | if (ISSET(iflag, PARMRK)) | |
449 | goto parmrk; | |
450 | } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK)) | |
451 | || ISSET(err, TTY_FE)) { | |
22ff886e | 452 | if (ISSET(iflag, IGNPAR)) { |
2efb75f3 | 453 | lwkt_reltoken(&tp->t_token); |
984263bc | 454 | return (0); |
22ff886e | 455 | } |
984263bc MD |
456 | else if (ISSET(iflag, PARMRK)) { |
457 | parmrk: | |
458 | if (tp->t_rawq.c_cc + tp->t_canq.c_cc > | |
459 | MAX_INPUT - 3) | |
460 | goto input_overflow; | |
0ced1954 MD |
461 | clist_putc(0377 | TTY_QUOTE, &tp->t_rawq); |
462 | clist_putc(0 | TTY_QUOTE, &tp->t_rawq); | |
463 | clist_putc(c | TTY_QUOTE, &tp->t_rawq); | |
984263bc MD |
464 | goto endcase; |
465 | } else | |
466 | c = 0; | |
467 | } | |
468 | } | |
469 | ||
470 | if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP)) | |
471 | CLR(c, 0x80); | |
472 | if (!ISSET(lflag, EXTPROC)) { | |
473 | /* | |
474 | * Check for literal nexting very first | |
475 | */ | |
476 | if (ISSET(tp->t_state, TS_LNCH)) { | |
477 | SET(c, TTY_QUOTE); | |
478 | CLR(tp->t_state, TS_LNCH); | |
479 | } | |
480 | /* | |
481 | * Scan for special characters. This code | |
482 | * is really just a big case statement with | |
483 | * non-constant cases. The bottom of the | |
484 | * case statement is labeled ``endcase'', so goto | |
485 | * it after a case match, or similar. | |
486 | */ | |
487 | ||
488 | /* | |
489 | * Control chars which aren't controlled | |
490 | * by ICANON, ISIG, or IXON. | |
491 | */ | |
492 | if (ISSET(lflag, IEXTEN)) { | |
493 | if (CCEQ(cc[VLNEXT], c)) { | |
494 | if (ISSET(lflag, ECHO)) { | |
495 | if (ISSET(lflag, ECHOE)) { | |
496 | (void)ttyoutput('^', tp); | |
497 | (void)ttyoutput('\b', tp); | |
498 | } else | |
499 | ttyecho(c, tp); | |
500 | } | |
501 | SET(tp->t_state, TS_LNCH); | |
502 | goto endcase; | |
503 | } | |
504 | if (CCEQ(cc[VDISCARD], c)) { | |
505 | if (ISSET(lflag, FLUSHO)) | |
506 | CLR(tp->t_lflag, FLUSHO); | |
507 | else { | |
508 | ttyflush(tp, FWRITE); | |
509 | ttyecho(c, tp); | |
510 | if (tp->t_rawq.c_cc + tp->t_canq.c_cc) | |
511 | ttyretype(tp); | |
512 | SET(tp->t_lflag, FLUSHO); | |
513 | } | |
514 | goto startoutput; | |
515 | } | |
516 | } | |
517 | /* | |
518 | * Signals. | |
519 | */ | |
520 | if (ISSET(lflag, ISIG)) { | |
521 | if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { | |
522 | if (!ISSET(lflag, NOFLSH)) | |
523 | ttyflush(tp, FREAD | FWRITE); | |
524 | ttyecho(c, tp); | |
525 | pgsignal(tp->t_pgrp, | |
526 | CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); | |
527 | goto endcase; | |
528 | } | |
529 | if (CCEQ(cc[VSUSP], c)) { | |
530 | if (!ISSET(lflag, NOFLSH)) | |
531 | ttyflush(tp, FREAD); | |
532 | ttyecho(c, tp); | |
533 | pgsignal(tp->t_pgrp, SIGTSTP, 1); | |
534 | goto endcase; | |
535 | } | |
536 | } | |
537 | /* | |
538 | * Handle start/stop characters. | |
539 | */ | |
540 | if (ISSET(iflag, IXON)) { | |
541 | if (CCEQ(cc[VSTOP], c)) { | |
542 | if (!ISSET(tp->t_state, TS_TTSTOP)) { | |
543 | SET(tp->t_state, TS_TTSTOP); | |
544 | (*tp->t_stop)(tp, 0); | |
2efb75f3 | 545 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
546 | return (0); |
547 | } | |
22ff886e | 548 | if (!CCEQ(cc[VSTART], c)) { |
2efb75f3 | 549 | lwkt_reltoken(&tp->t_token); |
984263bc | 550 | return (0); |
22ff886e | 551 | } |
984263bc MD |
552 | /* |
553 | * if VSTART == VSTOP then toggle | |
554 | */ | |
555 | goto endcase; | |
556 | } | |
557 | if (CCEQ(cc[VSTART], c)) | |
558 | goto restartoutput; | |
559 | } | |
560 | /* | |
561 | * IGNCR, ICRNL, & INLCR | |
562 | */ | |
563 | if (c == '\r') { | |
22ff886e | 564 | if (ISSET(iflag, IGNCR)) { |
2efb75f3 | 565 | lwkt_reltoken(&tp->t_token); |
984263bc | 566 | return (0); |
22ff886e | 567 | } |
984263bc MD |
568 | else if (ISSET(iflag, ICRNL)) |
569 | c = '\n'; | |
570 | } else if (c == '\n' && ISSET(iflag, INLCR)) | |
571 | c = '\r'; | |
572 | } | |
573 | if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) { | |
574 | /* | |
575 | * From here on down canonical mode character | |
576 | * processing takes place. | |
577 | */ | |
578 | /* | |
579 | * erase or erase2 (^H / ^?) | |
580 | */ | |
581 | if (CCEQ(cc[VERASE], c) || CCEQ(cc[VERASE2], c) ) { | |
582 | if (tp->t_rawq.c_cc) | |
0ced1954 | 583 | ttyrub(clist_unputc(&tp->t_rawq), tp); |
984263bc MD |
584 | goto endcase; |
585 | } | |
586 | /* | |
587 | * kill (^U) | |
588 | */ | |
589 | if (CCEQ(cc[VKILL], c)) { | |
590 | if (ISSET(lflag, ECHOKE) && | |
591 | tp->t_rawq.c_cc == tp->t_rocount && | |
592 | !ISSET(lflag, ECHOPRT)) | |
593 | while (tp->t_rawq.c_cc) | |
0ced1954 | 594 | ttyrub(clist_unputc(&tp->t_rawq), tp); |
984263bc MD |
595 | else { |
596 | ttyecho(c, tp); | |
597 | if (ISSET(lflag, ECHOK) || | |
598 | ISSET(lflag, ECHOKE)) | |
599 | ttyecho('\n', tp); | |
600 | FLUSHQ(&tp->t_rawq); | |
601 | tp->t_rocount = 0; | |
602 | } | |
603 | CLR(tp->t_state, TS_LOCAL); | |
604 | goto endcase; | |
605 | } | |
606 | /* | |
607 | * word erase (^W) | |
608 | */ | |
609 | if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) { | |
610 | int ctype; | |
611 | ||
612 | /* | |
613 | * erase whitespace | |
614 | */ | |
0ced1954 | 615 | while ((c = clist_unputc(&tp->t_rawq)) == ' ' || c == '\t') |
984263bc MD |
616 | ttyrub(c, tp); |
617 | if (c == -1) | |
618 | goto endcase; | |
619 | /* | |
620 | * erase last char of word and remember the | |
621 | * next chars type (for ALTWERASE) | |
622 | */ | |
623 | ttyrub(c, tp); | |
0ced1954 | 624 | c = clist_unputc(&tp->t_rawq); |
984263bc MD |
625 | if (c == -1) |
626 | goto endcase; | |
627 | if (c == ' ' || c == '\t') { | |
0ced1954 | 628 | clist_putc(c, &tp->t_rawq); |
984263bc MD |
629 | goto endcase; |
630 | } | |
631 | ctype = ISALPHA(c); | |
632 | /* | |
633 | * erase rest of word | |
634 | */ | |
635 | do { | |
636 | ttyrub(c, tp); | |
0ced1954 | 637 | c = clist_unputc(&tp->t_rawq); |
984263bc MD |
638 | if (c == -1) |
639 | goto endcase; | |
640 | } while (c != ' ' && c != '\t' && | |
641 | (!ISSET(lflag, ALTWERASE) || ISALPHA(c) == ctype)); | |
0ced1954 | 642 | clist_putc(c, &tp->t_rawq); |
984263bc MD |
643 | goto endcase; |
644 | } | |
645 | /* | |
646 | * reprint line (^R) | |
647 | */ | |
648 | if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) { | |
649 | ttyretype(tp); | |
650 | goto endcase; | |
651 | } | |
652 | /* | |
653 | * ^T - kernel info and generate SIGINFO | |
654 | */ | |
655 | if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) { | |
656 | if (ISSET(lflag, ISIG)) | |
657 | pgsignal(tp->t_pgrp, SIGINFO, 1); | |
658 | if (!ISSET(lflag, NOKERNINFO)) | |
659 | ttyinfo(tp); | |
660 | goto endcase; | |
661 | } | |
dcf5aa01 MD |
662 | if (CCEQ(cc[VCHECKPT], c) && ISSET(lflag, IEXTEN)) { |
663 | if (ISSET(lflag, ISIG)) | |
664 | pgsignal(tp->t_pgrp, SIGCKPT, 1); | |
665 | goto endcase; | |
666 | } | |
984263bc MD |
667 | } |
668 | /* | |
669 | * Check for input buffer overflow | |
670 | */ | |
671 | if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= MAX_INPUT) { | |
672 | input_overflow: | |
673 | if (ISSET(iflag, IMAXBEL)) { | |
674 | if (tp->t_outq.c_cc < tp->t_ohiwat) | |
675 | (void)ttyoutput(CTRL('g'), tp); | |
676 | } | |
677 | goto endcase; | |
678 | } | |
679 | ||
e5117602 MD |
680 | if (c == 0377 && ISSET(iflag, PARMRK) && !ISSET(iflag, ISTRIP) && |
681 | ISSET(iflag, IGNBRK|IGNPAR) != (IGNBRK|IGNPAR)) { | |
0ced1954 | 682 | clist_putc(0377 | TTY_QUOTE, &tp->t_rawq); |
e5117602 | 683 | } |
984263bc MD |
684 | |
685 | /* | |
686 | * Put data char in q for user and | |
687 | * wakeup on seeing a line delimiter. | |
688 | */ | |
0ced1954 | 689 | if (clist_putc(c, &tp->t_rawq) >= 0) { |
984263bc MD |
690 | if (!ISSET(lflag, ICANON)) { |
691 | ttwakeup(tp); | |
692 | ttyecho(c, tp); | |
693 | goto endcase; | |
694 | } | |
695 | if (TTBREAKC(c, lflag)) { | |
696 | tp->t_rocount = 0; | |
4725869b | 697 | clist_catq(&tp->t_rawq, &tp->t_canq); |
984263bc MD |
698 | ttwakeup(tp); |
699 | } else if (tp->t_rocount++ == 0) | |
700 | tp->t_rocol = tp->t_column; | |
701 | if (ISSET(tp->t_state, TS_ERASE)) { | |
702 | /* | |
703 | * end of prterase \.../ | |
704 | */ | |
705 | CLR(tp->t_state, TS_ERASE); | |
706 | (void)ttyoutput('/', tp); | |
707 | } | |
708 | i = tp->t_column; | |
709 | ttyecho(c, tp); | |
710 | if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) { | |
711 | /* | |
712 | * Place the cursor over the '^' of the ^D. | |
713 | */ | |
714 | i = imin(2, tp->t_column - i); | |
715 | while (i > 0) { | |
716 | (void)ttyoutput('\b', tp); | |
717 | i--; | |
718 | } | |
719 | } | |
720 | } | |
721 | endcase: | |
722 | /* | |
723 | * IXANY means allow any character to restart output. | |
724 | */ | |
725 | if (ISSET(tp->t_state, TS_TTSTOP) && | |
22ff886e | 726 | !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) { |
2efb75f3 | 727 | lwkt_reltoken(&tp->t_token); |
984263bc | 728 | return (0); |
22ff886e | 729 | } |
984263bc MD |
730 | restartoutput: |
731 | CLR(tp->t_lflag, FLUSHO); | |
732 | CLR(tp->t_state, TS_TTSTOP); | |
733 | startoutput: | |
2efb75f3 | 734 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
735 | return (ttstart(tp)); |
736 | } | |
737 | ||
738 | /* | |
739 | * Output a single character on a tty, doing output processing | |
740 | * as needed (expanding tabs, newline processing, etc.). | |
741 | * Returns < 0 if succeeds, otherwise returns char to resend. | |
742 | * Must be recursive. | |
743 | */ | |
744 | static int | |
c972a82f | 745 | ttyoutput(int c, struct tty *tp) |
984263bc | 746 | { |
1fd87d54 | 747 | tcflag_t oflag; |
e43a034f | 748 | int col; |
984263bc | 749 | |
2efb75f3 | 750 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
751 | oflag = tp->t_oflag; |
752 | if (!ISSET(oflag, OPOST)) { | |
22ff886e | 753 | if (ISSET(tp->t_lflag, FLUSHO)) { |
2efb75f3 | 754 | lwkt_reltoken(&tp->t_token); |
984263bc | 755 | return (-1); |
22ff886e AH |
756 | } |
757 | if (clist_putc(c, &tp->t_outq)) { | |
2efb75f3 | 758 | lwkt_reltoken(&tp->t_token); |
984263bc | 759 | return (c); |
22ff886e | 760 | } |
984263bc MD |
761 | tk_nout++; |
762 | tp->t_outcc++; | |
2efb75f3 | 763 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
764 | return (-1); |
765 | } | |
766 | /* | |
8edc0aff SW |
767 | * Do tab expansion if OXTABS aka TAB3 is set. Special case if we |
768 | * external processing, we don't do the tab expansion because we'll | |
769 | * probably get it wrong. If tab expansion needs to be done, let | |
770 | * it happen externally. | |
984263bc MD |
771 | */ |
772 | CLR(c, ~TTY_CHARMASK); | |
773 | if (c == '\t' && | |
774 | ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) { | |
775 | c = 8 - (tp->t_column & 7); | |
776 | if (!ISSET(tp->t_lflag, FLUSHO)) { | |
4725869b | 777 | c -= clist_btoq(" ", c, &tp->t_outq); |
984263bc MD |
778 | tk_nout += c; |
779 | tp->t_outcc += c; | |
984263bc MD |
780 | } |
781 | tp->t_column += c; | |
2efb75f3 | 782 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
783 | return (c ? -1 : '\t'); |
784 | } | |
22ff886e | 785 | if (c == CEOT && ISSET(oflag, ONOEOT)) { |
2efb75f3 | 786 | lwkt_reltoken(&tp->t_token); |
984263bc | 787 | return (-1); |
22ff886e | 788 | } |
984263bc MD |
789 | |
790 | /* | |
791 | * Newline translation: if ONLCR is set, | |
792 | * translate newline into "\r\n". | |
793 | */ | |
794 | if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) { | |
795 | tk_nout++; | |
796 | tp->t_outcc++; | |
22ff886e | 797 | if (!ISSET(tp->t_lflag, FLUSHO) && clist_putc('\r', &tp->t_outq)) { |
2efb75f3 | 798 | lwkt_reltoken(&tp->t_token); |
984263bc | 799 | return (c); |
22ff886e | 800 | } |
984263bc MD |
801 | } |
802 | /* If OCRNL is set, translate "\r" into "\n". */ | |
803 | else if (c == '\r' && ISSET(tp->t_oflag, OCRNL)) | |
804 | c = '\n'; | |
805 | /* If ONOCR is set, don't transmit CRs when on column 0. */ | |
22ff886e | 806 | else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0) { |
2efb75f3 | 807 | lwkt_reltoken(&tp->t_token); |
984263bc | 808 | return (-1); |
22ff886e | 809 | } |
984263bc MD |
810 | |
811 | tk_nout++; | |
812 | tp->t_outcc++; | |
22ff886e | 813 | if (!ISSET(tp->t_lflag, FLUSHO) && clist_putc(c, &tp->t_outq)) { |
2efb75f3 | 814 | lwkt_reltoken(&tp->t_token); |
984263bc | 815 | return (c); |
22ff886e | 816 | } |
984263bc MD |
817 | |
818 | col = tp->t_column; | |
819 | switch (CCLASS(c)) { | |
820 | case BACKSPACE: | |
821 | if (col > 0) | |
822 | --col; | |
823 | break; | |
824 | case CONTROL: | |
825 | break; | |
826 | case NEWLINE: | |
827 | if (ISSET(tp->t_oflag, ONLCR | ONLRET)) | |
828 | col = 0; | |
829 | break; | |
830 | case RETURN: | |
831 | col = 0; | |
832 | break; | |
833 | case ORDINARY: | |
834 | ++col; | |
835 | break; | |
836 | case TAB: | |
837 | col = (col + 8) & ~7; | |
838 | break; | |
839 | } | |
840 | tp->t_column = col; | |
2efb75f3 MD |
841 | lwkt_reltoken(&tp->t_token); |
842 | ||
984263bc MD |
843 | return (-1); |
844 | } | |
845 | ||
846 | /* | |
847 | * Ioctls for all tty devices. Called after line-discipline specific ioctl | |
848 | * has been called to do discipline-specific functions and/or reject any | |
849 | * of these ioctl commands. | |
850 | */ | |
851 | /* ARGSUSED */ | |
852 | int | |
dadab5e9 | 853 | ttioctl(struct tty *tp, u_long cmd, void *data, int flag) |
984263bc | 854 | { |
dadab5e9 | 855 | struct thread *td = curthread; |
08f2f1bb | 856 | struct lwp *lp = td->td_lwp; |
dadab5e9 | 857 | struct proc *p = td->td_proc; |
58c2553a | 858 | struct pgrp *opgrp; |
94a6eea8 | 859 | struct tty *otp; |
e43a034f | 860 | int error; |
984263bc | 861 | |
dadab5e9 | 862 | KKASSERT(p); |
2efb75f3 | 863 | lwkt_gettoken(&tp->t_token); |
616516c8 | 864 | lwkt_gettoken(&p->p_token); |
dadab5e9 | 865 | |
984263bc MD |
866 | /* If the ioctl involves modification, hang if in the background. */ |
867 | switch (cmd) { | |
868 | case TIOCCBRK: | |
869 | case TIOCCONS: | |
870 | case TIOCDRAIN: | |
871 | case TIOCEXCL: | |
872 | case TIOCFLUSH: | |
873 | #ifdef TIOCHPCL | |
874 | case TIOCHPCL: | |
875 | #endif | |
876 | case TIOCNXCL: | |
877 | case TIOCSBRK: | |
878 | case TIOCSCTTY: | |
879 | case TIOCSDRAINWAIT: | |
880 | case TIOCSETA: | |
881 | case TIOCSETAF: | |
882 | case TIOCSETAW: | |
883 | case TIOCSETD: | |
884 | case TIOCSPGRP: | |
885 | case TIOCSTART: | |
886 | case TIOCSTAT: | |
887 | case TIOCSTI: | |
888 | case TIOCSTOP: | |
889 | case TIOCSWINSZ: | |
4643740a | 890 | while (isbackground(p, tp) && !(p->p_flags & P_PPWAIT) && |
984263bc | 891 | !SIGISMEMBER(p->p_sigignore, SIGTTOU) && |
08f2f1bb | 892 | !SIGISMEMBER(lp->lwp_sigmask, SIGTTOU)) { |
22ff886e | 893 | if (p->p_pgrp->pg_jobc == 0) { |
616516c8 | 894 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 895 | lwkt_reltoken(&tp->t_token); |
984263bc | 896 | return (EIO); |
22ff886e | 897 | } |
984263bc | 898 | pgsignal(p->p_pgrp, SIGTTOU, 1); |
377d4740 | 899 | error = ttysleep(tp, &lbolt, PCATCH, "ttybg1", |
984263bc | 900 | 0); |
22ff886e | 901 | if (error) { |
616516c8 | 902 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 903 | lwkt_reltoken(&tp->t_token); |
984263bc | 904 | return (error); |
22ff886e | 905 | } |
984263bc MD |
906 | } |
907 | break; | |
908 | } | |
909 | ||
910 | switch (cmd) { /* Process the ioctl. */ | |
911 | case FIOASYNC: /* set/clear async i/o */ | |
984263bc MD |
912 | if (*(int *)data) |
913 | SET(tp->t_state, TS_ASYNC); | |
914 | else | |
915 | CLR(tp->t_state, TS_ASYNC); | |
984263bc | 916 | break; |
984263bc | 917 | case FIONREAD: /* get # bytes to read */ |
984263bc | 918 | *(int *)data = ttnread(tp); |
984263bc MD |
919 | break; |
920 | ||
921 | case FIOSETOWN: | |
922 | /* | |
923 | * Policy -- Don't allow FIOSETOWN on someone else's | |
924 | * controlling tty | |
925 | */ | |
22ff886e | 926 | if (tp->t_session != NULL && !isctty(p, tp)) { |
616516c8 | 927 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 928 | lwkt_reltoken(&tp->t_token); |
984263bc | 929 | return (ENOTTY); |
22ff886e | 930 | } |
984263bc MD |
931 | |
932 | error = fsetown(*(int *)data, &tp->t_sigio); | |
22ff886e | 933 | if (error) { |
616516c8 | 934 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 935 | lwkt_reltoken(&tp->t_token); |
984263bc | 936 | return (error); |
22ff886e | 937 | } |
984263bc MD |
938 | break; |
939 | case FIOGETOWN: | |
22ff886e | 940 | if (tp->t_session != NULL && !isctty(p, tp)) { |
616516c8 | 941 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 942 | lwkt_reltoken(&tp->t_token); |
984263bc | 943 | return (ENOTTY); |
22ff886e | 944 | } |
b5c4d81f | 945 | *(int *)data = fgetown(&tp->t_sigio); |
984263bc MD |
946 | break; |
947 | ||
948 | case TIOCEXCL: /* set exclusive use of tty */ | |
984263bc | 949 | SET(tp->t_state, TS_XCLUDE); |
984263bc MD |
950 | break; |
951 | case TIOCFLUSH: { /* flush buffers */ | |
1fd87d54 | 952 | int flags = *(int *)data; |
984263bc MD |
953 | |
954 | if (flags == 0) | |
955 | flags = FREAD | FWRITE; | |
956 | else | |
957 | flags &= FREAD | FWRITE; | |
958 | ttyflush(tp, flags); | |
959 | break; | |
960 | } | |
961 | case TIOCCONS: /* become virtual console */ | |
962 | if (*(int *)data) { | |
963 | if (constty && constty != tp && | |
22ff886e | 964 | ISSET(constty->t_state, TS_CONNECTED)) { |
616516c8 | 965 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 966 | lwkt_reltoken(&tp->t_token); |
984263bc | 967 | return (EBUSY); |
22ff886e | 968 | } |
984263bc | 969 | #ifndef UCONSOLE |
2b3f93ea MD |
970 | error = caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT); |
971 | if (error) { | |
616516c8 | 972 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 973 | lwkt_reltoken(&tp->t_token); |
984263bc | 974 | return (error); |
22ff886e | 975 | } |
984263bc MD |
976 | #endif |
977 | constty = tp; | |
978 | } else if (tp == constty) | |
979 | constty = NULL; | |
980 | break; | |
981 | case TIOCDRAIN: /* wait till output drained */ | |
982 | error = ttywait(tp); | |
22ff886e | 983 | if (error) { |
616516c8 | 984 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 985 | lwkt_reltoken(&tp->t_token); |
984263bc | 986 | return (error); |
22ff886e | 987 | } |
984263bc MD |
988 | break; |
989 | case TIOCGETA: { /* get termios struct */ | |
990 | struct termios *t = (struct termios *)data; | |
991 | ||
992 | bcopy(&tp->t_termios, t, sizeof(struct termios)); | |
993 | break; | |
994 | } | |
995 | case TIOCGETD: /* get line discipline */ | |
996 | *(int *)data = tp->t_line; | |
997 | break; | |
998 | case TIOCGWINSZ: /* get window size */ | |
999 | *(struct winsize *)data = tp->t_winsize; | |
1000 | break; | |
1001 | case TIOCGPGRP: /* get pgrp of tty */ | |
22ff886e | 1002 | if (!isctty(p, tp)) { |
616516c8 | 1003 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1004 | lwkt_reltoken(&tp->t_token); |
984263bc | 1005 | return (ENOTTY); |
22ff886e | 1006 | } |
984263bc MD |
1007 | *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; |
1008 | break; | |
fa94d286 | 1009 | case TIOCGSID: /* get sid of tty */ |
22ff886e | 1010 | if (!isctty(p, tp)) { |
616516c8 | 1011 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1012 | lwkt_reltoken(&tp->t_token); |
fa94d286 | 1013 | return (ENOTTY); |
22ff886e | 1014 | } |
fa94d286 PA |
1015 | *(int *)data = tp->t_session->s_sid; |
1016 | break; | |
984263bc MD |
1017 | #ifdef TIOCHPCL |
1018 | case TIOCHPCL: /* hang up on last close */ | |
984263bc | 1019 | SET(tp->t_cflag, HUPCL); |
984263bc MD |
1020 | break; |
1021 | #endif | |
1022 | case TIOCNXCL: /* reset exclusive use of tty */ | |
984263bc | 1023 | CLR(tp->t_state, TS_XCLUDE); |
984263bc MD |
1024 | break; |
1025 | case TIOCOUTQ: /* output queue size */ | |
1026 | *(int *)data = tp->t_outq.c_cc; | |
1027 | break; | |
1028 | case TIOCSETA: /* set termios struct */ | |
1029 | case TIOCSETAW: /* drain output, set */ | |
1030 | case TIOCSETAF: { /* drn out, fls in, set */ | |
1fd87d54 | 1031 | struct termios *t = (struct termios *)data; |
984263bc MD |
1032 | |
1033 | if (t->c_ispeed == 0) | |
1034 | t->c_ispeed = t->c_ospeed; | |
1035 | if (t->c_ispeed == 0) | |
1036 | t->c_ispeed = tp->t_ospeed; | |
22ff886e | 1037 | if (t->c_ispeed == 0) { |
616516c8 | 1038 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1039 | lwkt_reltoken(&tp->t_token); |
984263bc | 1040 | return (EINVAL); |
22ff886e | 1041 | } |
984263bc MD |
1042 | if (cmd == TIOCSETAW || cmd == TIOCSETAF) { |
1043 | error = ttywait(tp); | |
1044 | if (error) { | |
616516c8 | 1045 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1046 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1047 | return (error); |
1048 | } | |
1049 | if (cmd == TIOCSETAF) | |
1050 | ttyflush(tp, FREAD); | |
1051 | } | |
1052 | if (!ISSET(t->c_cflag, CIGNORE)) { | |
1053 | /* | |
1054 | * Set device hardware. | |
1055 | */ | |
1056 | if (tp->t_param && (error = (*tp->t_param)(tp, t))) { | |
616516c8 | 1057 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1058 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1059 | return (error); |
1060 | } | |
1061 | if (ISSET(t->c_cflag, CLOCAL) && | |
1062 | !ISSET(tp->t_cflag, CLOCAL)) { | |
1063 | /* | |
1064 | * XXX disconnections would be too hard to | |
1065 | * get rid of without this kludge. The only | |
1066 | * way to get rid of controlling terminals | |
1067 | * is to exit from the session leader. | |
1068 | */ | |
1069 | CLR(tp->t_state, TS_ZOMBIE); | |
1070 | ||
1071 | wakeup(TSA_CARR_ON(tp)); | |
1072 | ttwakeup(tp); | |
1073 | ttwwakeup(tp); | |
1074 | } | |
1075 | if ((ISSET(tp->t_state, TS_CARR_ON) || | |
1076 | ISSET(t->c_cflag, CLOCAL)) && | |
1077 | !ISSET(tp->t_state, TS_ZOMBIE)) | |
1078 | SET(tp->t_state, TS_CONNECTED); | |
1079 | else | |
1080 | CLR(tp->t_state, TS_CONNECTED); | |
1081 | tp->t_cflag = t->c_cflag; | |
1082 | tp->t_ispeed = t->c_ispeed; | |
1083 | if (t->c_ospeed != 0) | |
1084 | tp->t_ospeed = t->c_ospeed; | |
1085 | ttsetwater(tp); | |
1086 | } | |
1087 | if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) && | |
1088 | cmd != TIOCSETAF) { | |
1089 | if (ISSET(t->c_lflag, ICANON)) | |
1090 | SET(tp->t_lflag, PENDIN); | |
1091 | else { | |
1092 | /* | |
1093 | * XXX we really shouldn't allow toggling | |
1094 | * ICANON while we're in a non-termios line | |
1095 | * discipline. Now we have to worry about | |
1096 | * panicing for a null queue. | |
1097 | */ | |
4725869b MD |
1098 | if (tp->t_canq.c_ccmax > 0 && |
1099 | tp->t_rawq.c_ccmax > 0) { | |
1100 | clist_catq(&tp->t_rawq, &tp->t_canq); | |
984263bc MD |
1101 | /* |
1102 | * XXX the queue limits may be | |
1103 | * different, so the old queue | |
1104 | * swapping method no longer works. | |
1105 | */ | |
4725869b | 1106 | clist_catq(&tp->t_canq, &tp->t_rawq); |
984263bc MD |
1107 | } |
1108 | CLR(tp->t_lflag, PENDIN); | |
1109 | } | |
1110 | ttwakeup(tp); | |
1111 | } | |
1112 | tp->t_iflag = t->c_iflag; | |
1113 | tp->t_oflag = t->c_oflag; | |
1114 | /* | |
1115 | * Make the EXTPROC bit read only. | |
1116 | */ | |
1117 | if (ISSET(tp->t_lflag, EXTPROC)) | |
1118 | SET(t->c_lflag, EXTPROC); | |
1119 | else | |
1120 | CLR(t->c_lflag, EXTPROC); | |
1121 | tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN); | |
1122 | if (t->c_cc[VMIN] != tp->t_cc[VMIN] || | |
1123 | t->c_cc[VTIME] != tp->t_cc[VTIME]) | |
1124 | ttwakeup(tp); | |
1125 | bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); | |
984263bc MD |
1126 | break; |
1127 | } | |
1128 | case TIOCSETD: { /* set line discipline */ | |
1fd87d54 | 1129 | int t = *(int *)data; |
b13267a5 | 1130 | cdev_t device = tp->t_dev; |
984263bc | 1131 | |
22ff886e | 1132 | if ((u_int)t >= nlinesw) { |
616516c8 | 1133 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1134 | lwkt_reltoken(&tp->t_token); |
984263bc | 1135 | return (ENXIO); |
22ff886e | 1136 | } |
984263bc | 1137 | if (t != tp->t_line) { |
984263bc MD |
1138 | (*linesw[tp->t_line].l_close)(tp, flag); |
1139 | error = (*linesw[t].l_open)(device, tp); | |
1140 | if (error) { | |
1141 | (void)(*linesw[tp->t_line].l_open)(device, tp); | |
616516c8 | 1142 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1143 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1144 | return (error); |
1145 | } | |
1146 | tp->t_line = t; | |
984263bc MD |
1147 | } |
1148 | break; | |
1149 | } | |
1150 | case TIOCSTART: /* start output, like ^Q */ | |
984263bc MD |
1151 | if (ISSET(tp->t_state, TS_TTSTOP) || |
1152 | ISSET(tp->t_lflag, FLUSHO)) { | |
1153 | CLR(tp->t_lflag, FLUSHO); | |
1154 | CLR(tp->t_state, TS_TTSTOP); | |
1155 | ttstart(tp); | |
1156 | } | |
984263bc MD |
1157 | break; |
1158 | case TIOCSTI: /* simulate terminal input */ | |
2b3f93ea MD |
1159 | if ((flag & FREAD) == 0 && |
1160 | caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT)) | |
1161 | { | |
616516c8 | 1162 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1163 | lwkt_reltoken(&tp->t_token); |
984263bc | 1164 | return (EPERM); |
22ff886e | 1165 | } |
2b3f93ea MD |
1166 | if (!isctty(p, tp) && |
1167 | caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT)) | |
1168 | { | |
616516c8 | 1169 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1170 | lwkt_reltoken(&tp->t_token); |
984263bc | 1171 | return (EACCES); |
22ff886e | 1172 | } |
984263bc | 1173 | (*linesw[tp->t_line].l_rint)(*(u_char *)data, tp); |
984263bc MD |
1174 | break; |
1175 | case TIOCSTOP: /* stop output, like ^S */ | |
984263bc MD |
1176 | if (!ISSET(tp->t_state, TS_TTSTOP)) { |
1177 | SET(tp->t_state, TS_TTSTOP); | |
1178 | (*tp->t_stop)(tp, 0); | |
1179 | } | |
984263bc MD |
1180 | break; |
1181 | case TIOCSCTTY: /* become controlling tty */ | |
1182 | /* Session ctty vnode pointer set in vnode layer. */ | |
1183 | if (!SESS_LEADER(p) || | |
1184 | ((p->p_session->s_ttyvp || tp->t_session) && | |
94a6eea8 | 1185 | (tp->t_session != p->p_session))) { |
616516c8 | 1186 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1187 | lwkt_reltoken(&tp->t_token); |
984263bc | 1188 | return (EPERM); |
94a6eea8 MD |
1189 | } |
1190 | ttyhold(tp); | |
984263bc | 1191 | tp->t_session = p->p_session; |
58c2553a MD |
1192 | opgrp = tp->t_pgrp; |
1193 | pgref(p->p_pgrp); | |
984263bc | 1194 | tp->t_pgrp = p->p_pgrp; |
94a6eea8 | 1195 | otp = p->p_session->s_ttyp; |
984263bc | 1196 | p->p_session->s_ttyp = tp; |
4643740a | 1197 | p->p_flags |= P_CONTROLT; |
94a6eea8 MD |
1198 | if (otp) |
1199 | ttyunhold(otp); | |
58c2553a MD |
1200 | if (opgrp) { |
1201 | pgrel(opgrp); | |
1202 | opgrp = NULL; | |
1203 | } | |
984263bc MD |
1204 | break; |
1205 | case TIOCSPGRP: { /* set pgrp of tty */ | |
3e28fb5c | 1206 | pid_t pgid = *(int *)data; |
984263bc | 1207 | |
22ff886e | 1208 | if (!isctty(p, tp)) { |
616516c8 | 1209 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1210 | lwkt_reltoken(&tp->t_token); |
984263bc | 1211 | return (ENOTTY); |
22ff886e AH |
1212 | } |
1213 | else if (pgid < 1 || pgid > PID_MAX) { | |
616516c8 | 1214 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1215 | lwkt_reltoken(&tp->t_token); |
3e28fb5c | 1216 | return (EINVAL); |
22ff886e | 1217 | } else { |
3e28fb5c | 1218 | struct pgrp *pgrp = pgfind(pgid); |
22ff886e | 1219 | if (pgrp == NULL || pgrp->pg_session != p->p_session) { |
58c2553a MD |
1220 | if (pgrp) |
1221 | pgrel(pgrp); | |
616516c8 | 1222 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1223 | lwkt_reltoken(&tp->t_token); |
3e28fb5c | 1224 | return (EPERM); |
22ff886e | 1225 | } |
58c2553a | 1226 | opgrp = tp->t_pgrp; |
3e28fb5c | 1227 | tp->t_pgrp = pgrp; |
58c2553a MD |
1228 | if (opgrp) { |
1229 | pgrel(opgrp); | |
1230 | opgrp = NULL; | |
1231 | } | |
3e28fb5c | 1232 | } |
984263bc MD |
1233 | break; |
1234 | } | |
1235 | case TIOCSTAT: /* simulate control-T */ | |
984263bc | 1236 | ttyinfo(tp); |
984263bc MD |
1237 | break; |
1238 | case TIOCSWINSZ: /* set window size */ | |
1239 | if (bcmp((caddr_t)&tp->t_winsize, data, | |
1240 | sizeof (struct winsize))) { | |
1241 | tp->t_winsize = *(struct winsize *)data; | |
1242 | pgsignal(tp->t_pgrp, SIGWINCH, 1); | |
1243 | } | |
1244 | break; | |
1245 | case TIOCSDRAINWAIT: | |
2b3f93ea | 1246 | error = caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT); |
22ff886e | 1247 | if (error) { |
616516c8 | 1248 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1249 | lwkt_reltoken(&tp->t_token); |
984263bc | 1250 | return (error); |
22ff886e | 1251 | } |
984263bc MD |
1252 | tp->t_timeout = *(int *)data * hz; |
1253 | wakeup(TSA_OCOMPLETE(tp)); | |
1254 | wakeup(TSA_OLOWAT(tp)); | |
1255 | break; | |
1256 | case TIOCGDRAINWAIT: | |
1257 | *(int *)data = tp->t_timeout / hz; | |
1258 | break; | |
1259 | default: | |
616516c8 | 1260 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1261 | lwkt_reltoken(&tp->t_token); |
984263bc | 1262 | return (ENOIOCTL); |
984263bc | 1263 | } |
616516c8 | 1264 | lwkt_reltoken(&p->p_token); |
2efb75f3 | 1265 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1266 | return (0); |
1267 | } | |
1268 | ||
984263bc | 1269 | static struct filterops ttyread_filtops = |
a081e067 | 1270 | { FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_ttyrdetach, filt_ttyread }; |
984263bc | 1271 | static struct filterops ttywrite_filtops = |
a081e067 | 1272 | { FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_ttywdetach, filt_ttywrite }; |
984263bc MD |
1273 | |
1274 | int | |
fef8985e | 1275 | ttykqfilter(struct dev_kqfilter_args *ap) |
984263bc | 1276 | { |
b13267a5 | 1277 | cdev_t dev = ap->a_head.a_dev; |
fef8985e | 1278 | struct knote *kn = ap->a_kn; |
984263bc MD |
1279 | struct tty *tp = dev->si_tty; |
1280 | struct klist *klist; | |
984263bc | 1281 | |
fef8985e | 1282 | ap->a_result = 0; |
22ff886e | 1283 | |
2efb75f3 | 1284 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1285 | switch (kn->kn_filter) { |
1286 | case EVFILT_READ: | |
5b22f1a7 | 1287 | klist = &tp->t_rkq.ki_note; |
984263bc MD |
1288 | kn->kn_fop = &ttyread_filtops; |
1289 | break; | |
1290 | case EVFILT_WRITE: | |
5b22f1a7 | 1291 | klist = &tp->t_wkq.ki_note; |
984263bc MD |
1292 | kn->kn_fop = &ttywrite_filtops; |
1293 | break; | |
1294 | default: | |
b287d649 | 1295 | ap->a_result = EOPNOTSUPP; |
2efb75f3 | 1296 | lwkt_reltoken(&tp->t_token); |
fef8985e | 1297 | return (0); |
984263bc | 1298 | } |
2efb75f3 | 1299 | lwkt_reltoken(&tp->t_token); |
984263bc | 1300 | kn->kn_hook = (caddr_t)dev; |
5b22f1a7 | 1301 | knote_insert(klist, kn); |
984263bc MD |
1302 | |
1303 | return (0); | |
1304 | } | |
1305 | ||
1306 | static void | |
1307 | filt_ttyrdetach(struct knote *kn) | |
1308 | { | |
b13267a5 | 1309 | struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty; |
984263bc | 1310 | |
2efb75f3 | 1311 | lwkt_gettoken(&tp->t_token); |
5b22f1a7 | 1312 | knote_remove(&tp->t_rkq.ki_note, kn); |
2efb75f3 | 1313 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1314 | } |
1315 | ||
1316 | static int | |
1317 | filt_ttyread(struct knote *kn, long hint) | |
1318 | { | |
b13267a5 | 1319 | struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty; |
984263bc | 1320 | |
2efb75f3 | 1321 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1322 | kn->kn_data = ttnread(tp); |
1323 | if (ISSET(tp->t_state, TS_ZOMBIE)) { | |
3bcb6e5e | 1324 | kn->kn_flags |= (EV_EOF | EV_NODATA); |
2efb75f3 | 1325 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1326 | return (1); |
1327 | } | |
2efb75f3 | 1328 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1329 | return (kn->kn_data > 0); |
1330 | } | |
1331 | ||
1332 | static void | |
1333 | filt_ttywdetach(struct knote *kn) | |
1334 | { | |
b13267a5 | 1335 | struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty; |
984263bc | 1336 | |
2efb75f3 | 1337 | lwkt_gettoken(&tp->t_token); |
5b22f1a7 | 1338 | knote_remove(&tp->t_wkq.ki_note, kn); |
2efb75f3 | 1339 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1340 | } |
1341 | ||
1342 | static int | |
c972a82f | 1343 | filt_ttywrite(struct knote *kn, long hint) |
984263bc | 1344 | { |
b13267a5 | 1345 | struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty; |
22ff886e | 1346 | int ret; |
984263bc | 1347 | |
2efb75f3 | 1348 | lwkt_gettoken(&tp->t_token); |
984263bc | 1349 | kn->kn_data = tp->t_outq.c_cc; |
22ff886e | 1350 | if (ISSET(tp->t_state, TS_ZOMBIE)) { |
2efb75f3 | 1351 | lwkt_reltoken(&tp->t_token); |
984263bc | 1352 | return (1); |
22ff886e AH |
1353 | } |
1354 | ret = (kn->kn_data <= tp->t_olowat && | |
984263bc | 1355 | ISSET(tp->t_state, TS_CONNECTED)); |
2efb75f3 | 1356 | lwkt_reltoken(&tp->t_token); |
22ff886e | 1357 | return ret; |
984263bc MD |
1358 | } |
1359 | ||
1360 | /* | |
2efb75f3 | 1361 | * NOTE: tp->t_token must be held. |
984263bc MD |
1362 | */ |
1363 | static int | |
c972a82f | 1364 | ttnread(struct tty *tp) |
984263bc MD |
1365 | { |
1366 | int nread; | |
1367 | ||
2efb75f3 | 1368 | ASSERT_LWKT_TOKEN_HELD(&tp->t_token); |
984263bc MD |
1369 | if (ISSET(tp->t_lflag, PENDIN)) |
1370 | ttypend(tp); | |
1371 | nread = tp->t_canq.c_cc; | |
1372 | if (!ISSET(tp->t_lflag, ICANON)) { | |
1373 | nread += tp->t_rawq.c_cc; | |
1374 | if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0) | |
1375 | nread = 0; | |
1376 | } | |
1377 | return (nread); | |
1378 | } | |
1379 | ||
1380 | /* | |
1381 | * Wait for output to drain. | |
1382 | */ | |
1383 | int | |
c972a82f | 1384 | ttywait(struct tty *tp) |
984263bc | 1385 | { |
e43a034f | 1386 | int error; |
984263bc MD |
1387 | |
1388 | error = 0; | |
2efb75f3 | 1389 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1390 | while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && |
1391 | ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) { | |
1392 | (*tp->t_oproc)(tp); | |
1393 | if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && | |
1394 | ISSET(tp->t_state, TS_CONNECTED)) { | |
1395 | SET(tp->t_state, TS_SO_OCOMPLETE); | |
1396 | error = ttysleep(tp, TSA_OCOMPLETE(tp), | |
377d4740 | 1397 | PCATCH, "ttywai", |
984263bc MD |
1398 | tp->t_timeout); |
1399 | if (error) { | |
1400 | if (error == EWOULDBLOCK) | |
1401 | error = EIO; | |
1402 | break; | |
1403 | } | |
1404 | } else | |
1405 | break; | |
1406 | } | |
1407 | if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY))) | |
1408 | error = EIO; | |
2efb75f3 MD |
1409 | lwkt_reltoken(&tp->t_token); |
1410 | ||
984263bc MD |
1411 | return (error); |
1412 | } | |
1413 | ||
1414 | /* | |
1415 | * Flush if successfully wait. | |
1416 | */ | |
1417 | static int | |
c972a82f | 1418 | ttywflush(struct tty *tp) |
984263bc MD |
1419 | { |
1420 | int error; | |
1421 | ||
1422 | if ((error = ttywait(tp)) == 0) | |
1423 | ttyflush(tp, FREAD); | |
1424 | return (error); | |
1425 | } | |
1426 | ||
1427 | /* | |
1428 | * Flush tty read and/or write queues, notifying anyone waiting. | |
1429 | */ | |
1430 | void | |
c972a82f | 1431 | ttyflush(struct tty *tp, int rw) |
984263bc | 1432 | { |
2efb75f3 | 1433 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1434 | #if 0 |
1435 | again: | |
1436 | #endif | |
1437 | if (rw & FWRITE) { | |
1438 | FLUSHQ(&tp->t_outq); | |
1439 | CLR(tp->t_state, TS_TTSTOP); | |
1440 | } | |
1441 | (*tp->t_stop)(tp, rw); | |
1442 | if (rw & FREAD) { | |
1443 | FLUSHQ(&tp->t_canq); | |
1444 | FLUSHQ(&tp->t_rawq); | |
1445 | CLR(tp->t_lflag, PENDIN); | |
1446 | tp->t_rocount = 0; | |
1447 | tp->t_rocol = 0; | |
1448 | CLR(tp->t_state, TS_LOCAL); | |
1449 | ttwakeup(tp); | |
1450 | if (ISSET(tp->t_state, TS_TBLOCK)) { | |
1451 | if (rw & FWRITE) | |
1452 | FLUSHQ(&tp->t_outq); | |
1453 | ttyunblock(tp); | |
1454 | ||
1455 | /* | |
1456 | * Don't let leave any state that might clobber the | |
1457 | * next line discipline (although we should do more | |
1458 | * to send the START char). Not clearing the state | |
1459 | * may have caused the "putc to a clist with no | |
6ea70f76 | 1460 | * reserved cblocks" panic/kprintf. |
984263bc MD |
1461 | */ |
1462 | CLR(tp->t_state, TS_TBLOCK); | |
1463 | ||
1464 | #if 0 /* forget it, sleeping isn't always safe and we don't know when it is */ | |
1465 | if (ISSET(tp->t_iflag, IXOFF)) { | |
1466 | /* | |
1467 | * XXX wait a bit in the hope that the stop | |
1468 | * character (if any) will go out. Waiting | |
1469 | * isn't good since it allows races. This | |
1470 | * will be fixed when the stop character is | |
1471 | * put in a special queue. Don't bother with | |
1472 | * the checks in ttywait() since the timeout | |
1473 | * will save us. | |
1474 | */ | |
1475 | SET(tp->t_state, TS_SO_OCOMPLETE); | |
377d4740 | 1476 | ttysleep(tp, TSA_OCOMPLETE(tp), 0, |
984263bc MD |
1477 | "ttyfls", hz / 10); |
1478 | /* | |
1479 | * Don't try sending the stop character again. | |
1480 | */ | |
1481 | CLR(tp->t_state, TS_TBLOCK); | |
1482 | goto again; | |
1483 | } | |
1484 | #endif | |
1485 | } | |
1486 | } | |
1487 | if (rw & FWRITE) { | |
1488 | FLUSHQ(&tp->t_outq); | |
1489 | ttwwakeup(tp); | |
1490 | } | |
2efb75f3 | 1491 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1492 | } |
1493 | ||
1494 | /* | |
1495 | * Copy in the default termios characters. | |
1496 | */ | |
1497 | void | |
c972a82f | 1498 | termioschars(struct termios *t) |
984263bc | 1499 | { |
984263bc MD |
1500 | bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); |
1501 | } | |
1502 | ||
1503 | /* | |
1504 | * Old interface. | |
1505 | */ | |
1506 | void | |
c972a82f | 1507 | ttychars(struct tty *tp) |
984263bc | 1508 | { |
2efb75f3 | 1509 | lwkt_gettoken(&tp->t_token); |
984263bc | 1510 | termioschars(&tp->t_termios); |
2efb75f3 | 1511 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1512 | } |
1513 | ||
1514 | /* | |
1515 | * Handle input high water. Send stop character for the IXOFF case. Turn | |
1516 | * on our input flow control bit and propagate the changes to the driver. | |
1517 | * XXX the stop character should be put in a special high priority queue. | |
1518 | */ | |
1519 | void | |
c972a82f | 1520 | ttyblock(struct tty *tp) |
984263bc | 1521 | { |
2efb75f3 | 1522 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1523 | SET(tp->t_state, TS_TBLOCK); |
1524 | if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && | |
0ced1954 | 1525 | clist_putc(tp->t_cc[VSTOP], &tp->t_outq) != 0) |
984263bc MD |
1526 | CLR(tp->t_state, TS_TBLOCK); /* try again later */ |
1527 | ttstart(tp); | |
2efb75f3 | 1528 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1529 | } |
1530 | ||
1531 | /* | |
1532 | * Handle input low water. Send start character for the IXOFF case. Turn | |
1533 | * off our input flow control bit and propagate the changes to the driver. | |
1534 | * XXX the start character should be put in a special high priority queue. | |
1535 | */ | |
1536 | static void | |
c972a82f | 1537 | ttyunblock(struct tty *tp) |
984263bc | 1538 | { |
2efb75f3 | 1539 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1540 | CLR(tp->t_state, TS_TBLOCK); |
1541 | if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE && | |
0ced1954 | 1542 | clist_putc(tp->t_cc[VSTART], &tp->t_outq) != 0) |
984263bc MD |
1543 | SET(tp->t_state, TS_TBLOCK); /* try again later */ |
1544 | ttstart(tp); | |
2efb75f3 | 1545 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1546 | } |
1547 | ||
984263bc | 1548 | int |
c972a82f | 1549 | ttstart(struct tty *tp) |
984263bc | 1550 | { |
2efb75f3 | 1551 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1552 | if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */ |
1553 | (*tp->t_oproc)(tp); | |
2efb75f3 | 1554 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1555 | return (0); |
1556 | } | |
1557 | ||
1558 | /* | |
1559 | * "close" a line discipline | |
1560 | */ | |
1561 | int | |
c972a82f | 1562 | ttylclose(struct tty *tp, int flag) |
984263bc | 1563 | { |
2efb75f3 | 1564 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1565 | if (flag & FNONBLOCK || ttywflush(tp)) |
1566 | ttyflush(tp, FREAD | FWRITE); | |
2efb75f3 | 1567 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1568 | return (0); |
1569 | } | |
1570 | ||
94a6eea8 MD |
1571 | void |
1572 | ttyhold(struct tty *tp) | |
1573 | { | |
2efb75f3 | 1574 | lwkt_gettoken(&tp->t_token); |
94a6eea8 | 1575 | ++tp->t_refs; |
2efb75f3 | 1576 | lwkt_reltoken(&tp->t_token); |
94a6eea8 MD |
1577 | } |
1578 | ||
1579 | void | |
1580 | ttyunhold(struct tty *tp) | |
1581 | { | |
2efb75f3 | 1582 | lwkt_gettoken(&tp->t_token); |
f5f1bbd4 MD |
1583 | if (tp->t_unhold) |
1584 | tp->t_unhold(tp); | |
1585 | else | |
1586 | --tp->t_refs; | |
2efb75f3 | 1587 | lwkt_reltoken(&tp->t_token); |
94a6eea8 MD |
1588 | } |
1589 | ||
984263bc MD |
1590 | /* |
1591 | * Handle modem control transition on a tty. | |
1592 | * Flag indicates new state of carrier. | |
1593 | * Returns 0 if the line should be turned off, otherwise 1. | |
1594 | */ | |
1595 | int | |
c972a82f | 1596 | ttymodem(struct tty *tp, int flag) |
984263bc | 1597 | { |
2efb75f3 | 1598 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1599 | if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) { |
1600 | /* | |
1601 | * MDMBUF: do flow control according to carrier flag | |
1602 | * XXX TS_CAR_OFLOW doesn't do anything yet. TS_TTSTOP | |
1603 | * works if IXON and IXANY are clear. | |
1604 | */ | |
1605 | if (flag) { | |
1606 | CLR(tp->t_state, TS_CAR_OFLOW); | |
1607 | CLR(tp->t_state, TS_TTSTOP); | |
1608 | ttstart(tp); | |
1609 | } else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) { | |
1610 | SET(tp->t_state, TS_CAR_OFLOW); | |
1611 | SET(tp->t_state, TS_TTSTOP); | |
1612 | (*tp->t_stop)(tp, 0); | |
1613 | } | |
1614 | } else if (flag == 0) { | |
1615 | /* | |
1616 | * Lost carrier. | |
1617 | */ | |
1618 | CLR(tp->t_state, TS_CARR_ON); | |
1619 | if (ISSET(tp->t_state, TS_ISOPEN) && | |
1620 | !ISSET(tp->t_cflag, CLOCAL)) { | |
1621 | SET(tp->t_state, TS_ZOMBIE); | |
1622 | CLR(tp->t_state, TS_CONNECTED); | |
1623 | if (tp->t_session && tp->t_session->s_leader) | |
84204577 | 1624 | ksignal(tp->t_session->s_leader, SIGHUP); |
984263bc | 1625 | ttyflush(tp, FREAD | FWRITE); |
2efb75f3 | 1626 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1627 | return (0); |
1628 | } | |
1629 | } else { | |
1630 | /* | |
1631 | * Carrier now on. | |
1632 | */ | |
1633 | SET(tp->t_state, TS_CARR_ON); | |
1634 | if (!ISSET(tp->t_state, TS_ZOMBIE)) | |
1635 | SET(tp->t_state, TS_CONNECTED); | |
1636 | wakeup(TSA_CARR_ON(tp)); | |
1637 | ttwakeup(tp); | |
1638 | ttwwakeup(tp); | |
1639 | } | |
2efb75f3 | 1640 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1641 | return (1); |
1642 | } | |
1643 | ||
1644 | /* | |
1645 | * Reinput pending characters after state switch | |
984263bc MD |
1646 | */ |
1647 | static void | |
c972a82f | 1648 | ttypend(struct tty *tp) |
984263bc MD |
1649 | { |
1650 | struct clist tq; | |
1fd87d54 | 1651 | int c; |
984263bc | 1652 | |
2efb75f3 | 1653 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
1654 | CLR(tp->t_lflag, PENDIN); |
1655 | SET(tp->t_state, TS_TYPEN); | |
1656 | /* | |
1657 | * XXX this assumes too much about clist internals. It may even | |
1658 | * fail if the cblock slush pool is empty. We can't allocate more | |
1659 | * cblocks here because we are called from an interrupt handler | |
1660 | * and clist_alloc_cblocks() can wait. | |
1661 | */ | |
1662 | tq = tp->t_rawq; | |
1663 | bzero(&tp->t_rawq, sizeof tp->t_rawq); | |
4725869b | 1664 | clist_alloc_cblocks(&tp->t_rawq, tq.c_ccmax); |
0ced1954 | 1665 | while ((c = clist_getc(&tq)) >= 0) |
984263bc MD |
1666 | ttyinput(c, tp); |
1667 | CLR(tp->t_state, TS_TYPEN); | |
4725869b | 1668 | clist_free_cblocks(&tq); |
2efb75f3 | 1669 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1670 | } |
1671 | ||
1672 | /* | |
1673 | * Process a read call on a tty device. | |
1674 | */ | |
1675 | int | |
c972a82f | 1676 | ttread(struct tty *tp, struct uio *uio, int flag) |
984263bc | 1677 | { |
1fd87d54 RG |
1678 | struct clist *qp; |
1679 | int c; | |
1680 | tcflag_t lflag; | |
1681 | cc_t *cc = tp->t_cc; | |
142dd704 | 1682 | struct proc *pp; |
08f2f1bb | 1683 | struct lwp *lp; |
e43a034f | 1684 | int first, error = 0; |
984263bc MD |
1685 | int has_stime = 0, last_cc = 0; |
1686 | long slp = 0; /* XXX this should be renamed `timo'. */ | |
1687 | struct timeval stime; | |
1688 | ||
08f2f1bb | 1689 | lp = curthread->td_lwp; |
d557216f MD |
1690 | stime.tv_sec = 0; /* fix compiler warnings */ |
1691 | stime.tv_usec = 0; | |
08f2f1bb | 1692 | |
2efb75f3 | 1693 | lwkt_gettoken(&tp->t_token); |
984263bc | 1694 | loop: |
984263bc MD |
1695 | lflag = tp->t_lflag; |
1696 | /* | |
1697 | * take pending input first | |
1698 | */ | |
1699 | if (ISSET(lflag, PENDIN)) { | |
1700 | ttypend(tp); | |
e43a034f | 1701 | splz(); /* reduce latency */ |
984263bc MD |
1702 | lflag = tp->t_lflag; /* XXX ttypend() clobbers it */ |
1703 | } | |
1704 | ||
1705 | /* | |
1706 | * Hang process if it's in the background. | |
1707 | */ | |
a8d3ab53 MD |
1708 | if ((pp = curproc) != NULL) |
1709 | lwkt_gettoken(&pp->p_token); | |
1710 | if (pp && isbackground(pp, tp)) { | |
142dd704 | 1711 | if (SIGISMEMBER(pp->p_sigignore, SIGTTIN) || |
08f2f1bb | 1712 | SIGISMEMBER(lp->lwp_sigmask, SIGTTIN) || |
4643740a | 1713 | (pp->p_flags & P_PPWAIT) || pp->p_pgrp->pg_jobc == 0) { |
a8d3ab53 | 1714 | lwkt_reltoken(&pp->p_token); |
2efb75f3 | 1715 | lwkt_reltoken(&tp->t_token); |
984263bc | 1716 | return (EIO); |
22ff886e | 1717 | } |
142dd704 | 1718 | pgsignal(pp->p_pgrp, SIGTTIN, 1); |
377d4740 | 1719 | error = ttysleep(tp, &lbolt, PCATCH, "ttybg2", 0); |
22ff886e | 1720 | if (error) { |
a8d3ab53 | 1721 | lwkt_reltoken(&pp->p_token); |
2efb75f3 | 1722 | lwkt_reltoken(&tp->t_token); |
984263bc | 1723 | return (error); |
22ff886e | 1724 | } |
a8d3ab53 | 1725 | lwkt_reltoken(&pp->p_token); |
984263bc MD |
1726 | goto loop; |
1727 | } | |
a8d3ab53 MD |
1728 | if (pp) |
1729 | lwkt_reltoken(&pp->p_token); | |
984263bc MD |
1730 | |
1731 | if (ISSET(tp->t_state, TS_ZOMBIE)) { | |
2efb75f3 | 1732 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1733 | return (0); /* EOF */ |
1734 | } | |
1735 | ||
1736 | /* | |
1737 | * If canonical, use the canonical queue, | |
1738 | * else use the raw queue. | |
1739 | * | |
1740 | * (should get rid of clists...) | |
1741 | */ | |
1742 | qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq; | |
1743 | ||
1744 | if (flag & IO_NDELAY) { | |
1745 | if (qp->c_cc > 0) | |
1746 | goto read; | |
1747 | if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) { | |
2efb75f3 | 1748 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1749 | return (0); |
1750 | } | |
2efb75f3 | 1751 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1752 | return (EWOULDBLOCK); |
1753 | } | |
1754 | if (!ISSET(lflag, ICANON)) { | |
1755 | int m = cc[VMIN]; | |
1756 | long t = cc[VTIME]; | |
1757 | struct timeval timecopy; | |
1758 | ||
1759 | /* | |
1760 | * Check each of the four combinations. | |
1761 | * (m > 0 && t == 0) is the normal read case. | |
1762 | * It should be fairly efficient, so we check that and its | |
1763 | * companion case (m == 0 && t == 0) first. | |
1764 | * For the other two cases, we compute the target sleep time | |
1765 | * into slp. | |
1766 | */ | |
1767 | if (t == 0) { | |
1768 | if (qp->c_cc < m) | |
1769 | goto sleep; | |
1770 | if (qp->c_cc > 0) | |
1771 | goto read; | |
1772 | ||
1773 | /* m, t and qp->c_cc are all 0. 0 is enough input. */ | |
2efb75f3 | 1774 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1775 | return (0); |
1776 | } | |
1777 | t *= 100000; /* time in us */ | |
1778 | #define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \ | |
1779 | ((t1).tv_usec - (t2).tv_usec)) | |
1780 | if (m > 0) { | |
1781 | if (qp->c_cc <= 0) | |
1782 | goto sleep; | |
1783 | if (qp->c_cc >= m) | |
1784 | goto read; | |
1785 | getmicrotime(&timecopy); | |
d557216f | 1786 | if (has_stime == 0) { |
984263bc MD |
1787 | /* first character, start timer */ |
1788 | has_stime = 1; | |
1789 | stime = timecopy; | |
1790 | slp = t; | |
1791 | } else if (qp->c_cc > last_cc) { | |
1792 | /* got a character, restart timer */ | |
1793 | stime = timecopy; | |
1794 | slp = t; | |
1795 | } else { | |
1796 | /* nothing, check expiration */ | |
1797 | slp = t - diff(timecopy, stime); | |
1798 | if (slp <= 0) | |
1799 | goto read; | |
1800 | } | |
1801 | last_cc = qp->c_cc; | |
1802 | } else { /* m == 0 */ | |
1803 | if (qp->c_cc > 0) | |
1804 | goto read; | |
1805 | getmicrotime(&timecopy); | |
d557216f | 1806 | if (has_stime == 0) { |
984263bc MD |
1807 | has_stime = 1; |
1808 | stime = timecopy; | |
1809 | slp = t; | |
1810 | } else { | |
1811 | slp = t - diff(timecopy, stime); | |
1812 | if (slp <= 0) { | |
1813 | /* Timed out, but 0 is enough input. */ | |
2efb75f3 | 1814 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1815 | return (0); |
1816 | } | |
1817 | } | |
1818 | } | |
1819 | #undef diff | |
1820 | /* | |
1821 | * Rounding down may make us wake up just short | |
1822 | * of the target, so we round up. | |
1823 | * The formula is ceiling(slp * hz/1000000). | |
1824 | * 32-bit arithmetic is enough for hz < 169. | |
1825 | * XXX see tvtohz() for how to avoid overflow if hz | |
1826 | * is large (divide by `tick' and/or arrange to | |
1827 | * use tvtohz() if hz is large). | |
1828 | */ | |
1829 | slp = (long) (((u_long)slp * hz) + 999999) / 1000000; | |
1830 | goto sleep; | |
1831 | } | |
1832 | if (qp->c_cc <= 0) { | |
1833 | sleep: | |
1834 | /* | |
1835 | * There is no input, or not enough input and we can block. | |
1836 | */ | |
377d4740 | 1837 | error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), PCATCH, |
984263bc MD |
1838 | ISSET(tp->t_state, TS_CONNECTED) ? |
1839 | "ttyin" : "ttyhup", (int)slp); | |
984263bc MD |
1840 | if (error == EWOULDBLOCK) |
1841 | error = 0; | |
22ff886e | 1842 | else if (error) { |
2efb75f3 | 1843 | lwkt_reltoken(&tp->t_token); |
984263bc | 1844 | return (error); |
22ff886e | 1845 | } |
984263bc MD |
1846 | /* |
1847 | * XXX what happens if another process eats some input | |
1848 | * while we are asleep (not just here)? It would be | |
1849 | * safest to detect changes and reset our state variables | |
1850 | * (has_stime and last_cc). | |
1851 | */ | |
1852 | slp = 0; | |
1853 | goto loop; | |
1854 | } | |
1855 | read: | |
984263bc MD |
1856 | /* |
1857 | * Input present, check for input mapping and processing. | |
1858 | */ | |
1859 | first = 1; | |
1860 | if (ISSET(lflag, ICANON | ISIG)) | |
1861 | goto slowcase; | |
1862 | for (;;) { | |
1863 | char ibuf[IBUFSIZ]; | |
1864 | int icc; | |
1865 | ||
e54488bb | 1866 | icc = (int)szmin(uio->uio_resid, IBUFSIZ); |
4725869b | 1867 | icc = clist_qtob(qp, ibuf, icc); |
984263bc MD |
1868 | if (icc <= 0) { |
1869 | if (first) | |
1870 | goto loop; | |
1871 | break; | |
1872 | } | |
e54488bb | 1873 | error = uiomove(ibuf, (size_t)icc, uio); |
984263bc MD |
1874 | /* |
1875 | * XXX if there was an error then we should ungetc() the | |
1876 | * unmoved chars and reduce icc here. | |
1877 | */ | |
1878 | if (error) | |
1879 | break; | |
8f706258 | 1880 | if (uio->uio_resid == 0) |
984263bc MD |
1881 | break; |
1882 | first = 0; | |
1883 | } | |
1884 | goto out; | |
1885 | slowcase: | |
1886 | for (;;) { | |
0ced1954 | 1887 | c = clist_getc(qp); |
984263bc MD |
1888 | if (c < 0) { |
1889 | if (first) | |
1890 | goto loop; | |
1891 | break; | |
1892 | } | |
1893 | /* | |
1894 | * delayed suspend (^Y) | |
1895 | */ | |
1896 | if (CCEQ(cc[VDSUSP], c) && | |
1897 | ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) { | |
1898 | pgsignal(tp->t_pgrp, SIGTSTP, 1); | |
1899 | if (first) { | |
377d4740 | 1900 | error = ttysleep(tp, &lbolt, PCATCH, |
984263bc MD |
1901 | "ttybg3", 0); |
1902 | if (error) | |
1903 | break; | |
1904 | goto loop; | |
1905 | } | |
1906 | break; | |
1907 | } | |
1908 | /* | |
1909 | * Interpret EOF only in canonical mode. | |
1910 | */ | |
1911 | if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON)) | |
1912 | break; | |
1913 | /* | |
1914 | * Give user character. | |
1915 | */ | |
8f706258 | 1916 | error = ureadc(c, uio); |
984263bc MD |
1917 | if (error) |
1918 | /* XXX should ungetc(c, qp). */ | |
1919 | break; | |
8f706258 | 1920 | if (uio->uio_resid == 0) |
984263bc MD |
1921 | break; |
1922 | /* | |
1923 | * In canonical mode check for a "break character" | |
1924 | * marking the end of a "line of input". | |
1925 | */ | |
1926 | if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag)) | |
1927 | break; | |
1928 | first = 0; | |
1929 | } | |
1930 | ||
1931 | out: | |
1932 | /* | |
1933 | * Look to unblock input now that (presumably) | |
1934 | * the input queue has gone down. | |
1935 | */ | |
984263bc | 1936 | if (ISSET(tp->t_state, TS_TBLOCK) && |
2efb75f3 | 1937 | tp->t_rawq.c_cc + tp->t_canq.c_cc <= tp->t_ilowat) { |
984263bc | 1938 | ttyunblock(tp); |
2efb75f3 | 1939 | } |
984263bc | 1940 | |
2efb75f3 | 1941 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1942 | return (error); |
1943 | } | |
1944 | ||
1945 | /* | |
1946 | * Check the output queue on tp for space for a kernel message (from uprintf | |
1947 | * or tprintf). Allow some space over the normal hiwater mark so we don't | |
1948 | * lose messages due to normal flow control, but don't let the tty run amok. | |
1949 | * Sleeps here are not interruptible, but we return prematurely if new signals | |
1950 | * arrive. | |
1951 | */ | |
1952 | int | |
c972a82f | 1953 | ttycheckoutq(struct tty *tp, int wait) |
984263bc | 1954 | { |
aa6c3de6 | 1955 | struct lwp *lp = curthread->td_lwp; |
e43a034f | 1956 | int hiwat; |
aa6c3de6 | 1957 | sigset_t oldset, newset; |
984263bc | 1958 | |
2efb75f3 | 1959 | lwkt_gettoken(&tp->t_token); |
984263bc | 1960 | hiwat = tp->t_ohiwat; |
aa6c3de6 SS |
1961 | SIGEMPTYSET(oldset); |
1962 | SIGEMPTYSET(newset); | |
984263bc | 1963 | if (wait) |
aa6c3de6 | 1964 | oldset = lwp_sigpend(lp); |
e43a034f | 1965 | if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100) { |
984263bc MD |
1966 | while (tp->t_outq.c_cc > hiwat) { |
1967 | ttstart(tp); | |
1968 | if (tp->t_outq.c_cc <= hiwat) | |
1969 | break; | |
aa6c3de6 SS |
1970 | if (wait) |
1971 | newset = lwp_sigpend(lp); | |
1972 | if (!wait || SIGSETNEQ(oldset, newset)) { | |
2efb75f3 | 1973 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1974 | return (0); |
1975 | } | |
1976 | SET(tp->t_state, TS_SO_OLOWAT); | |
377d4740 | 1977 | tsleep(TSA_OLOWAT(tp), 0, "ttoutq", hz); |
984263bc | 1978 | } |
e43a034f | 1979 | } |
2efb75f3 | 1980 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
1981 | return (1); |
1982 | } | |
1983 | ||
1984 | /* | |
1985 | * Process a write call on a tty device. | |
1986 | */ | |
1987 | int | |
c972a82f | 1988 | ttwrite(struct tty *tp, struct uio *uio, int flag) |
984263bc | 1989 | { |
1fd87d54 RG |
1990 | char *cp = NULL; |
1991 | int cc, ce; | |
142dd704 | 1992 | struct proc *pp; |
08f2f1bb | 1993 | struct lwp *lp; |
e54488bb MD |
1994 | int i, hiwat, error; |
1995 | size_t cnt; | |
1996 | ||
984263bc MD |
1997 | char obuf[OBUFSIZ]; |
1998 | ||
2efb75f3 | 1999 | lwkt_gettoken(&tp->t_token); |
08f2f1bb | 2000 | lp = curthread->td_lwp; |
984263bc MD |
2001 | hiwat = tp->t_ohiwat; |
2002 | cnt = uio->uio_resid; | |
2003 | error = 0; | |
2004 | cc = 0; | |
2005 | loop: | |
984263bc | 2006 | if (ISSET(tp->t_state, TS_ZOMBIE)) { |
984263bc MD |
2007 | if (uio->uio_resid == cnt) |
2008 | error = EIO; | |
2009 | goto out; | |
2010 | } | |
2011 | if (!ISSET(tp->t_state, TS_CONNECTED)) { | |
2012 | if (flag & IO_NDELAY) { | |
984263bc MD |
2013 | error = EWOULDBLOCK; |
2014 | goto out; | |
2015 | } | |
377d4740 | 2016 | error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "ttydcd", 0); |
984263bc MD |
2017 | if (error) |
2018 | goto out; | |
2019 | goto loop; | |
2020 | } | |
142dd704 | 2021 | |
984263bc MD |
2022 | /* |
2023 | * Hang the process if it's in the background. | |
2024 | */ | |
a8d3ab53 MD |
2025 | if ((pp = curproc) != NULL) |
2026 | lwkt_gettoken(&pp->p_token); | |
2027 | if (pp && isbackground(pp, tp) && | |
4643740a | 2028 | ISSET(tp->t_lflag, TOSTOP) && !(pp->p_flags & P_PPWAIT) && |
142dd704 | 2029 | !SIGISMEMBER(pp->p_sigignore, SIGTTOU) && |
08f2f1bb | 2030 | !SIGISMEMBER(lp->lwp_sigmask, SIGTTOU)) { |
142dd704 | 2031 | if (pp->p_pgrp->pg_jobc == 0) { |
984263bc | 2032 | error = EIO; |
a8d3ab53 | 2033 | lwkt_reltoken(&pp->p_token); |
984263bc MD |
2034 | goto out; |
2035 | } | |
142dd704 | 2036 | pgsignal(pp->p_pgrp, SIGTTOU, 1); |
a8d3ab53 | 2037 | lwkt_reltoken(&pp->p_token); |
377d4740 | 2038 | error = ttysleep(tp, &lbolt, PCATCH, "ttybg4", 0); |
984263bc MD |
2039 | if (error) |
2040 | goto out; | |
2041 | goto loop; | |
2042 | } | |
a8d3ab53 MD |
2043 | if (pp) |
2044 | lwkt_reltoken(&pp->p_token); | |
984263bc MD |
2045 | /* |
2046 | * Process the user's data in at most OBUFSIZ chunks. Perform any | |
2047 | * output translation. Keep track of high water mark, sleep on | |
2048 | * overflow awaiting device aid in acquiring new space. | |
2049 | */ | |
2050 | while (uio->uio_resid > 0 || cc > 0) { | |
2051 | if (ISSET(tp->t_lflag, FLUSHO)) { | |
2052 | uio->uio_resid = 0; | |
2efb75f3 | 2053 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2054 | return (0); |
2055 | } | |
2056 | if (tp->t_outq.c_cc > hiwat) | |
2057 | goto ovhiwat; | |
2058 | /* | |
2059 | * Grab a hunk of data from the user, unless we have some | |
2060 | * leftover from last time. | |
2061 | */ | |
2062 | if (cc == 0) { | |
e54488bb | 2063 | cc = szmin(uio->uio_resid, OBUFSIZ); |
984263bc | 2064 | cp = obuf; |
e54488bb | 2065 | error = uiomove(cp, (size_t)cc, uio); |
984263bc MD |
2066 | if (error) { |
2067 | cc = 0; | |
2068 | break; | |
2069 | } | |
2070 | } | |
2071 | /* | |
2072 | * If nothing fancy need be done, grab those characters we | |
2073 | * can handle without any of ttyoutput's processing and | |
2074 | * just transfer them to the output q. For those chars | |
2075 | * which require special processing (as indicated by the | |
2076 | * bits in char_type), call ttyoutput. After processing | |
2077 | * a hunk of data, look for FLUSHO so ^O's will take effect | |
2078 | * immediately. | |
2079 | */ | |
2080 | while (cc > 0) { | |
2081 | if (!ISSET(tp->t_oflag, OPOST)) | |
2082 | ce = cc; | |
2083 | else { | |
2084 | ce = cc - scanc((u_int)cc, (u_char *)cp, | |
2085 | char_type, CCLASSMASK); | |
2086 | /* | |
2087 | * If ce is zero, then we're processing | |
2088 | * a special character through ttyoutput. | |
2089 | */ | |
2090 | if (ce == 0) { | |
2091 | tp->t_rocount = 0; | |
2092 | if (ttyoutput(*cp, tp) >= 0) { | |
2093 | /* No Clists, wait a bit. */ | |
2094 | ttstart(tp); | |
2095 | if (flag & IO_NDELAY) { | |
2096 | error = EWOULDBLOCK; | |
2097 | goto out; | |
2098 | } | |
2099 | error = ttysleep(tp, &lbolt, | |
377d4740 | 2100 | PCATCH, |
984263bc MD |
2101 | "ttybf1", 0); |
2102 | if (error) | |
2103 | goto out; | |
2104 | goto loop; | |
2105 | } | |
2106 | cp++; | |
2107 | cc--; | |
2108 | if (ISSET(tp->t_lflag, FLUSHO) || | |
2109 | tp->t_outq.c_cc > hiwat) | |
2110 | goto ovhiwat; | |
2111 | continue; | |
2112 | } | |
2113 | } | |
2114 | /* | |
2115 | * A bunch of normal characters have been found. | |
2116 | * Transfer them en masse to the output queue and | |
2117 | * continue processing at the top of the loop. | |
2118 | * If there are any further characters in this | |
2119 | * <= OBUFSIZ chunk, the first should be a character | |
2120 | * requiring special handling by ttyoutput. | |
2121 | */ | |
2122 | tp->t_rocount = 0; | |
4725869b | 2123 | i = clist_btoq(cp, ce, &tp->t_outq); |
984263bc MD |
2124 | ce -= i; |
2125 | tp->t_column += ce; | |
2126 | cp += ce, cc -= ce, tk_nout += ce; | |
2127 | tp->t_outcc += ce; | |
2128 | if (i > 0) { | |
2129 | /* No Clists, wait a bit. */ | |
2130 | ttstart(tp); | |
2131 | if (flag & IO_NDELAY) { | |
2132 | error = EWOULDBLOCK; | |
2133 | goto out; | |
2134 | } | |
377d4740 | 2135 | error = ttysleep(tp, &lbolt, PCATCH, |
984263bc MD |
2136 | "ttybf2", 0); |
2137 | if (error) | |
2138 | goto out; | |
2139 | goto loop; | |
2140 | } | |
2141 | if (ISSET(tp->t_lflag, FLUSHO) || | |
2142 | tp->t_outq.c_cc > hiwat) | |
2143 | break; | |
2144 | } | |
2145 | ttstart(tp); | |
2146 | } | |
2147 | out: | |
2148 | /* | |
2149 | * If cc is nonzero, we leave the uio structure inconsistent, as the | |
2150 | * offset and iov pointers have moved forward, but it doesn't matter | |
2151 | * (the call will either return short or restart with a new uio). | |
2152 | */ | |
2153 | uio->uio_resid += cc; | |
2efb75f3 | 2154 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2155 | return (error); |
2156 | ||
2157 | ovhiwat: | |
2158 | ttstart(tp); | |
984263bc MD |
2159 | /* |
2160 | * This can only occur if FLUSHO is set in t_lflag, | |
2161 | * or if ttstart/oproc is synchronous (or very fast). | |
2162 | */ | |
2163 | if (tp->t_outq.c_cc <= hiwat) { | |
984263bc MD |
2164 | goto loop; |
2165 | } | |
2166 | if (flag & IO_NDELAY) { | |
984263bc | 2167 | uio->uio_resid += cc; |
2efb75f3 | 2168 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2169 | return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); |
2170 | } | |
2171 | SET(tp->t_state, TS_SO_OLOWAT); | |
377d4740 | 2172 | error = ttysleep(tp, TSA_OLOWAT(tp), PCATCH, "ttywri", tp->t_timeout); |
984263bc MD |
2173 | if (error == EWOULDBLOCK) |
2174 | error = EIO; | |
2175 | if (error) | |
2176 | goto out; | |
2177 | goto loop; | |
2178 | } | |
2179 | ||
2180 | /* | |
2181 | * Rubout one character from the rawq of tp | |
2182 | * as cleanly as possible. | |
2efb75f3 | 2183 | * NOTE: Must be called with tp->t_token held |
984263bc MD |
2184 | */ |
2185 | static void | |
c972a82f | 2186 | ttyrub(int c, struct tty *tp) |
984263bc | 2187 | { |
4725869b | 2188 | void *cp; |
1fd87d54 | 2189 | int savecol; |
e43a034f | 2190 | int tabc; |
984263bc | 2191 | |
2efb75f3 | 2192 | ASSERT_LWKT_TOKEN_HELD(&tp->t_token); |
984263bc MD |
2193 | if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) |
2194 | return; | |
2195 | CLR(tp->t_lflag, FLUSHO); | |
2196 | if (ISSET(tp->t_lflag, ECHOE)) { | |
2197 | if (tp->t_rocount == 0) { | |
2198 | /* | |
2199 | * Screwed by ttwrite; retype | |
2200 | */ | |
2201 | ttyretype(tp); | |
2202 | return; | |
2203 | } | |
2204 | if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) | |
2205 | ttyrubo(tp, 2); | |
2206 | else { | |
2207 | CLR(c, ~TTY_CHARMASK); | |
2208 | switch (CCLASS(c)) { | |
2209 | case ORDINARY: | |
2210 | ttyrubo(tp, 1); | |
2211 | break; | |
2212 | case BACKSPACE: | |
2213 | case CONTROL: | |
2214 | case NEWLINE: | |
2215 | case RETURN: | |
2216 | case VTAB: | |
2217 | if (ISSET(tp->t_lflag, ECHOCTL)) | |
2218 | ttyrubo(tp, 2); | |
2219 | break; | |
2220 | case TAB: | |
2221 | if (tp->t_rocount < tp->t_rawq.c_cc) { | |
2222 | ttyretype(tp); | |
2223 | return; | |
2224 | } | |
984263bc MD |
2225 | savecol = tp->t_column; |
2226 | SET(tp->t_state, TS_CNTTB); | |
2227 | SET(tp->t_lflag, FLUSHO); | |
2228 | tp->t_column = tp->t_rocol; | |
4725869b MD |
2229 | |
2230 | cp = clist_nextc(&tp->t_rawq, NULL, &tabc); | |
2231 | while (cp) { | |
984263bc | 2232 | ttyecho(tabc, tp); |
4725869b MD |
2233 | cp = clist_nextc(&tp->t_rawq, |
2234 | cp, &tabc); | |
2235 | } | |
984263bc MD |
2236 | CLR(tp->t_lflag, FLUSHO); |
2237 | CLR(tp->t_state, TS_CNTTB); | |
984263bc MD |
2238 | |
2239 | /* savecol will now be length of the tab. */ | |
2240 | savecol -= tp->t_column; | |
2241 | tp->t_column += savecol; | |
2242 | if (savecol > 8) | |
2243 | savecol = 8; /* overflow screw */ | |
2244 | while (--savecol >= 0) | |
2245 | (void)ttyoutput('\b', tp); | |
2246 | break; | |
2247 | default: /* XXX */ | |
2248 | #define PANICSTR "ttyrub: would panic c = %d, val = %d\n" | |
6ea70f76 | 2249 | (void)kprintf(PANICSTR, c, CCLASS(c)); |
984263bc MD |
2250 | #ifdef notdef |
2251 | panic(PANICSTR, c, CCLASS(c)); | |
2252 | #endif | |
2253 | } | |
2254 | } | |
2255 | } else if (ISSET(tp->t_lflag, ECHOPRT)) { | |
2256 | if (!ISSET(tp->t_state, TS_ERASE)) { | |
2257 | SET(tp->t_state, TS_ERASE); | |
2258 | (void)ttyoutput('\\', tp); | |
2259 | } | |
2260 | ttyecho(c, tp); | |
2261 | } else { | |
2262 | ttyecho(tp->t_cc[VERASE], tp); | |
2263 | /* | |
2264 | * This code may be executed not only when an ERASE key | |
2265 | * is pressed, but also when ^U (KILL) or ^W (WERASE) are. | |
2266 | * So, I didn't think it was worthwhile to pass the extra | |
2267 | * information (which would need an extra parameter, | |
2268 | * changing every call) needed to distinguish the ERASE2 | |
2269 | * case from the ERASE. | |
2270 | */ | |
2271 | } | |
2272 | --tp->t_rocount; | |
2273 | } | |
2274 | ||
2275 | /* | |
2276 | * Back over cnt characters, erasing them. | |
2efb75f3 | 2277 | * NOTE: Must be called with tp->t_token held |
984263bc MD |
2278 | */ |
2279 | static void | |
c972a82f | 2280 | ttyrubo(struct tty *tp, int cnt) |
984263bc | 2281 | { |
2efb75f3 | 2282 | ASSERT_LWKT_TOKEN_HELD(&tp->t_token); |
984263bc MD |
2283 | while (cnt-- > 0) { |
2284 | (void)ttyoutput('\b', tp); | |
2285 | (void)ttyoutput(' ', tp); | |
2286 | (void)ttyoutput('\b', tp); | |
2287 | } | |
2288 | } | |
2289 | ||
2290 | /* | |
2291 | * ttyretype -- | |
2292 | * Reprint the rawq line. Note, it is assumed that c_cc has already | |
2293 | * been checked. | |
2efb75f3 | 2294 | * NOTE: Must be called with tp->t_token held |
984263bc MD |
2295 | */ |
2296 | static void | |
c972a82f | 2297 | ttyretype(struct tty *tp) |
984263bc | 2298 | { |
4725869b | 2299 | void *cp; |
e43a034f | 2300 | int c; |
984263bc | 2301 | |
2efb75f3 | 2302 | ASSERT_LWKT_TOKEN_HELD(&tp->t_token); |
984263bc MD |
2303 | /* Echo the reprint character. */ |
2304 | if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) | |
2305 | ttyecho(tp->t_cc[VREPRINT], tp); | |
2306 | ||
2307 | (void)ttyoutput('\n', tp); | |
2308 | ||
2309 | /* | |
2310 | * XXX | |
2311 | * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE | |
2312 | * BIT OF FIRST CHAR. | |
2313 | */ | |
4725869b MD |
2314 | cp = clist_nextc(&tp->t_canq, NULL, &c); |
2315 | while (cp) { | |
984263bc | 2316 | ttyecho(c, tp); |
4725869b MD |
2317 | cp = clist_nextc(&tp->t_canq, cp, &c); |
2318 | } | |
2319 | cp = clist_nextc(&tp->t_rawq, NULL, &c); | |
2320 | while (cp) { | |
984263bc | 2321 | ttyecho(c, tp); |
4725869b MD |
2322 | cp = clist_nextc(&tp->t_rawq, cp, &c); |
2323 | } | |
984263bc | 2324 | CLR(tp->t_state, TS_ERASE); |
984263bc MD |
2325 | |
2326 | tp->t_rocount = tp->t_rawq.c_cc; | |
2327 | tp->t_rocol = 0; | |
2328 | } | |
2329 | ||
2330 | /* | |
2331 | * Echo a typed character to the terminal. | |
2efb75f3 | 2332 | * NOTE: Must be called with tp->t_token held |
984263bc MD |
2333 | */ |
2334 | static void | |
c972a82f | 2335 | ttyecho(int c, struct tty *tp) |
984263bc | 2336 | { |
2efb75f3 | 2337 | ASSERT_LWKT_TOKEN_HELD(&tp->t_token); |
984263bc MD |
2338 | |
2339 | if (!ISSET(tp->t_state, TS_CNTTB)) | |
2340 | CLR(tp->t_lflag, FLUSHO); | |
2341 | if ((!ISSET(tp->t_lflag, ECHO) && | |
2342 | (c != '\n' || !ISSET(tp->t_lflag, ECHONL))) || | |
2343 | ISSET(tp->t_lflag, EXTPROC)) | |
2344 | return; | |
2345 | if (ISSET(tp->t_lflag, ECHOCTL) && | |
2346 | ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') || | |
2347 | ISSET(c, TTY_CHARMASK) == 0177)) { | |
2348 | (void)ttyoutput('^', tp); | |
2349 | CLR(c, ~TTY_CHARMASK); | |
2350 | if (c == 0177) | |
2351 | c = '?'; | |
2352 | else | |
2353 | c += 'A' - 1; | |
2354 | } | |
2355 | (void)ttyoutput(c, tp); | |
2356 | } | |
2357 | ||
2358 | /* | |
2359 | * Wake up any readers on a tty. | |
2360 | */ | |
2361 | void | |
c972a82f | 2362 | ttwakeup(struct tty *tp) |
984263bc | 2363 | { |
2efb75f3 | 2364 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
2365 | if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) |
2366 | pgsigio(tp->t_sigio, SIGIO, (tp->t_session != NULL)); | |
2367 | wakeup(TSA_HUP_OR_INPUT(tp)); | |
5b22f1a7 | 2368 | KNOTE(&tp->t_rkq.ki_note, 0); |
2efb75f3 | 2369 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2370 | } |
2371 | ||
2372 | /* | |
2373 | * Wake up any writers on a tty. | |
2374 | */ | |
2375 | void | |
c972a82f | 2376 | ttwwakeup(struct tty *tp) |
984263bc | 2377 | { |
2efb75f3 | 2378 | lwkt_gettoken(&tp->t_token); |
984263bc MD |
2379 | if (ISSET(tp->t_state, TS_ASYNC) && tp->t_sigio != NULL) |
2380 | pgsigio(tp->t_sigio, SIGIO, (tp->t_session != NULL)); | |
2381 | if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == | |
2382 | TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { | |
2383 | CLR(tp->t_state, TS_SO_OCOMPLETE); | |
2384 | wakeup(TSA_OCOMPLETE(tp)); | |
2385 | } | |
2386 | if (ISSET(tp->t_state, TS_SO_OLOWAT) && | |
2387 | tp->t_outq.c_cc <= tp->t_olowat) { | |
2388 | CLR(tp->t_state, TS_SO_OLOWAT); | |
2389 | wakeup(TSA_OLOWAT(tp)); | |
2390 | } | |
5b22f1a7 | 2391 | KNOTE(&tp->t_wkq.ki_note, 0); |
2efb75f3 | 2392 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2393 | } |
2394 | ||
2395 | /* | |
2396 | * Look up a code for a specified speed in a conversion table; | |
2397 | * used by drivers to map software speed values to hardware parameters. | |
22ff886e | 2398 | * No requirements |
984263bc MD |
2399 | */ |
2400 | int | |
c972a82f | 2401 | ttspeedtab(int speed, struct speedtab *table) |
984263bc MD |
2402 | { |
2403 | ||
2404 | for ( ; table->sp_speed != -1; table++) | |
2405 | if (table->sp_speed == speed) | |
2406 | return (table->sp_code); | |
2407 | return (-1); | |
2408 | } | |
2409 | ||
2410 | /* | |
2411 | * Set input and output watermarks and buffer sizes. For input, the | |
2412 | * high watermark is about one second's worth of input above empty, the | |
2413 | * low watermark is slightly below high water, and the buffer size is a | |
2414 | * driver-dependent amount above high water. For output, the watermarks | |
2415 | * are near the ends of the buffer, with about 1 second's worth of input | |
2416 | * between them. All this only applies to the standard line discipline. | |
2417 | */ | |
e5117602 MD |
2418 | #define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) |
2419 | ||
984263bc | 2420 | void |
c972a82f | 2421 | ttsetwater(struct tty *tp) |
984263bc | 2422 | { |
e5117602 MD |
2423 | int ttmaxhiwat; /* maximum high water mark */ |
2424 | int cps; /* characters per second */ | |
2425 | int x; | |
984263bc | 2426 | |
2efb75f3 | 2427 | lwkt_gettoken(&tp->t_token); |
e5117602 MD |
2428 | |
2429 | /* | |
2430 | * Input side. | |
2431 | * | |
2432 | * Calculate nominal low and high water marks, leave a little | |
2433 | * room to absorb flow control latencies. | |
2434 | */ | |
4725869b | 2435 | clist_alloc_cblocks(&tp->t_canq, TTYHOG); |
e5117602 | 2436 | |
984263bc MD |
2437 | switch (tp->t_ispeedwat) { |
2438 | case (speed_t)-1: | |
2439 | cps = tp->t_ispeed / 10; | |
2440 | break; | |
2441 | case 0: | |
2442 | /* | |
2443 | * This case is for old drivers that don't know about | |
2444 | * t_ispeedwat. Arrange for them to get the old buffer | |
2445 | * sizes and watermarks. | |
2446 | */ | |
2447 | cps = TTYHOG - 2 * 256; | |
2849f1e5 | 2448 | tp->t_ififosize = 2 * 2048; |
984263bc MD |
2449 | break; |
2450 | default: | |
2451 | cps = tp->t_ispeedwat / 10; | |
2452 | break; | |
2453 | } | |
2454 | tp->t_ihiwat = cps; | |
2455 | tp->t_ilowat = 7 * cps / 8; | |
2456 | x = cps + tp->t_ififosize; | |
4725869b | 2457 | clist_alloc_cblocks(&tp->t_rawq, x); |
984263bc | 2458 | |
e5117602 MD |
2459 | /* |
2460 | * Output side. | |
2461 | * | |
2462 | * Calculate nominal low and high water marks, and make the | |
2463 | * actual buffer just a tad larger to absorb flow control latencies. | |
2464 | */ | |
984263bc MD |
2465 | switch (tp->t_ospeedwat) { |
2466 | case (speed_t)-1: | |
2467 | cps = tp->t_ospeed / 10; | |
2468 | ttmaxhiwat = 2 * TTMAXHIWAT; | |
2469 | break; | |
2470 | case 0: | |
2471 | cps = tp->t_ospeed / 10; | |
2472 | ttmaxhiwat = TTMAXHIWAT; | |
2473 | break; | |
2474 | default: | |
2475 | cps = tp->t_ospeedwat / 10; | |
2476 | ttmaxhiwat = 8 * TTMAXHIWAT; | |
2477 | break; | |
2478 | } | |
e5117602 MD |
2479 | |
2480 | x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); | |
2481 | tp->t_olowat = x; | |
2482 | ||
984263bc | 2483 | x += cps; |
e5117602 MD |
2484 | x = CLAMP(x, ttmaxhiwat, TTMINHIWAT); |
2485 | tp->t_ohiwat = x; | |
2486 | ||
2487 | x = imax(tp->t_ohiwat, TTMAXHIWAT); | |
984263bc | 2488 | x += OBUFSIZ + 100; |
4725869b | 2489 | clist_alloc_cblocks(&tp->t_outq, x); |
e5117602 | 2490 | |
2efb75f3 | 2491 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2492 | } |
2493 | ||
e5117602 MD |
2494 | #undef CLAMP |
2495 | ||
984263bc MD |
2496 | /* |
2497 | * Report on state of foreground process group. | |
2498 | */ | |
2499 | void | |
c972a82f | 2500 | ttyinfo(struct tty *tp) |
984263bc | 2501 | { |
197b441c | 2502 | struct pgrp *pgrp; |
1fd87d54 | 2503 | struct proc *p, *pick; |
08f2f1bb | 2504 | struct lwp *lp; |
fde7ac71 | 2505 | struct rusage ru; |
197b441c MD |
2506 | char buf[64]; |
2507 | const char *str; | |
2508 | struct vmspace *vm; | |
2509 | long vmsz; | |
2510 | int pctcpu; | |
984263bc MD |
2511 | int tmp; |
2512 | ||
2513 | if (ttycheckoutq(tp,0) == 0) | |
2514 | return; | |
2515 | ||
2efb75f3 | 2516 | lwkt_gettoken(&tp->t_token); |
a8d3ab53 | 2517 | |
5daa25b0 MD |
2518 | /* |
2519 | * We always print the load average, then figure out what else to | |
2520 | * print based on the state of the current process group. | |
2521 | */ | |
984263bc MD |
2522 | tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; |
2523 | ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100); | |
2524 | ||
5daa25b0 | 2525 | if (tp->t_session == NULL) { |
984263bc | 2526 | ttyprintf(tp, "not a controlling terminal\n"); |
197b441c MD |
2527 | goto done2; |
2528 | } | |
2529 | if ((pgrp = tp->t_pgrp) == NULL) { | |
984263bc | 2530 | ttyprintf(tp, "no foreground process group\n"); |
197b441c MD |
2531 | goto done2; |
2532 | } | |
5daa25b0 | 2533 | |
197b441c MD |
2534 | /* |
2535 | * Pick an interesting process. Note that certain elements, | |
2efb75f3 | 2536 | * in particular the wmesg. |
197b441c MD |
2537 | * |
2538 | * NOTE: lwp_wmesg is lwp_thread->td_wmesg. | |
2539 | */ | |
2540 | pgref(pgrp); | |
2541 | lwkt_gettoken(&pgrp->pg_token); | |
2542 | ||
2543 | pick = NULL; | |
2544 | for (p = LIST_FIRST(&pgrp->pg_members); | |
2545 | p != NULL; | |
2546 | p = LIST_NEXT(p, p_pglist)) { | |
a8d3ab53 MD |
2547 | PHOLD(p); |
2548 | if (proc_compare(pick, p)) { | |
2549 | if (pick) | |
2550 | PRELE(pick); | |
197b441c | 2551 | pick = p; |
a8d3ab53 MD |
2552 | } else { |
2553 | PRELE(p); | |
2554 | } | |
197b441c MD |
2555 | } |
2556 | if (pick == NULL) { | |
2557 | ttyprintf(tp, "empty foreground process group\n"); | |
2558 | goto done1; | |
2559 | } | |
08f2f1bb | 2560 | |
197b441c MD |
2561 | /* |
2562 | * Pick an interesting LWP (XXX) | |
a8d3ab53 MD |
2563 | * |
2564 | * pick is held. | |
197b441c | 2565 | */ |
197b441c MD |
2566 | lp = FIRST_LWP_IN_PROC(pick); |
2567 | if (lp == NULL) { | |
2568 | PRELE(pick); | |
2569 | ttyprintf(tp, "foreground process without lwp\n"); | |
2570 | goto done1; | |
2571 | } | |
984263bc | 2572 | |
197b441c MD |
2573 | /* |
2574 | * Figure out what wait/process-state message, and command | |
2575 | * buffer to present | |
2576 | */ | |
2577 | /* | |
2578 | * XXX lwp This is a horrible mixture. We need to rework this | |
2579 | * as soon as lwps have their own runnable status. | |
2580 | */ | |
2581 | LWPHOLD(lp); | |
2582 | if (pick->p_flags & P_WEXIT) | |
2583 | str = "exiting"; | |
2584 | else if (lp->lwp_stat == LSRUN) | |
2585 | str = "running"; | |
2586 | else if (pick->p_stat == SIDL) | |
2587 | str = "spawning"; | |
2588 | else if (lp->lwp_wmesg) /* lwp_thread must not be NULL */ | |
213c68be | 2589 | str = lp->lwp_wmesg; |
197b441c MD |
2590 | else |
2591 | str = "iowait"; | |
984263bc | 2592 | |
197b441c MD |
2593 | ksnprintf(buf, sizeof(buf), "cmd: %s %d [%s]", |
2594 | pick->p_comm, pick->p_pid, str); | |
984263bc | 2595 | |
197b441c MD |
2596 | /* |
2597 | * Calculate cpu usage, percent cpu, and cmsz. Note that | |
2efb75f3 | 2598 | * 'pick' becomes invalid the moment we release the token. |
197b441c | 2599 | */ |
ef866ef7 | 2600 | if (lp->lwp_thread) |
197b441c | 2601 | calcru_proc(pick, &ru); |
5daa25b0 | 2602 | |
197b441c | 2603 | pctcpu = (lp->lwp_pctcpu * 10000 + FSCALE / 2) >> FSHIFT; |
5daa25b0 | 2604 | |
197b441c | 2605 | LWPRELE(lp); |
984263bc | 2606 | |
93f86408 MD |
2607 | /* |
2608 | * NOTE: vmspace should be protected from destruction by the | |
2609 | * combination of pg_token and the fact that we are not | |
2610 | * flagged as a zombie. | |
2611 | */ | |
197b441c MD |
2612 | if (pick->p_stat == SIDL || pick->p_stat == SZOMB) { |
2613 | vmsz = 0; | |
2614 | } else if ((vm = pick->p_vmspace) == NULL) { | |
2615 | vmsz = 0; | |
2616 | } else { | |
2617 | vmspace_hold(vm); | |
2618 | vmsz = pgtok(vmspace_resident_count(vm)); | |
2619 | vmspace_drop(vm); | |
984263bc | 2620 | } |
197b441c MD |
2621 | PRELE(pick); |
2622 | ||
2623 | /* | |
2624 | * Dump the output | |
2625 | */ | |
2626 | ttyprintf(tp, " %s ", | |
2627 | buf); | |
2628 | ttyprintf(tp, "%ld.%02ldu ", | |
2629 | ru.ru_utime.tv_sec, ru.ru_utime.tv_usec / 10000); | |
2630 | ttyprintf(tp, "%ld.%02lds ", | |
2631 | ru.ru_stime.tv_sec, ru.ru_stime.tv_usec / 10000); | |
2632 | ttyprintf(tp, "%d%% %ldk\n", | |
2633 | pctcpu / 100, vmsz); | |
2634 | ||
2635 | done1: | |
2636 | lwkt_reltoken(&pgrp->pg_token); | |
2637 | pgrel(pgrp); | |
2638 | done2: | |
984263bc | 2639 | tp->t_rocount = 0; /* so pending input will be retyped if BS */ |
2efb75f3 | 2640 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2641 | } |
2642 | ||
2643 | /* | |
2644 | * Returns 1 if p2 is "better" than p1 | |
2645 | * | |
2646 | * The algorithm for picking the "interesting" process is thus: | |
2647 | * | |
2648 | * 1) Only foreground processes are eligible - implied. | |
2649 | * 2) Runnable processes are favored over anything else. The runner | |
dcc99b62 | 2650 | * with the highest cpu utilization is picked (p_cpticks). Ties are |
984263bc MD |
2651 | * broken by picking the highest pid. |
2652 | * 3) The sleeper with the shortest sleep time is next. With ties, | |
9a379a4a | 2653 | * we pick out just "short-term" sleepers (LWP_SINTR == 0). |
984263bc | 2654 | * 4) Further ties are broken by picking the highest pid. |
22ff886e | 2655 | * |
a8d3ab53 | 2656 | * NOTE: must be called with p1 and p2 held. |
984263bc | 2657 | */ |
164b8401 | 2658 | #define ISRUN(lp) ((lp)->lwp_stat == LSRUN) |
984263bc MD |
2659 | #define TESTAB(a, b) ((a)<<1 | (b)) |
2660 | #define ONLYA 2 | |
2661 | #define ONLYB 1 | |
2662 | #define BOTH 3 | |
2663 | ||
2664 | static int | |
c972a82f | 2665 | proc_compare(struct proc *p1, struct proc *p2) |
984263bc | 2666 | { |
08f2f1bb | 2667 | struct lwp *lp1, *lp2; |
91148fea | 2668 | int res; |
22ff886e | 2669 | |
984263bc MD |
2670 | if (p1 == NULL) |
2671 | return (1); | |
91148fea MD |
2672 | if (lwkt_trytoken(&p1->p_token) == 0) |
2673 | return (1); | |
2674 | if (lwkt_trytoken(&p2->p_token) == 0) { | |
2675 | lwkt_reltoken(&p1->p_token); | |
2676 | return (0); | |
2677 | } | |
08f2f1bb | 2678 | |
93e5e2ea MD |
2679 | /* |
2680 | * weed out zombies | |
2681 | */ | |
2682 | switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) { | |
2683 | case ONLYA: | |
91148fea MD |
2684 | res = 1; |
2685 | goto done; | |
93e5e2ea | 2686 | case ONLYB: |
91148fea MD |
2687 | res = 0; |
2688 | goto done; | |
93e5e2ea | 2689 | case BOTH: |
91148fea MD |
2690 | res = (p2->p_pid > p1->p_pid); /* tie - return highest pid */ |
2691 | goto done; | |
2692 | default: | |
2693 | break; | |
93e5e2ea MD |
2694 | } |
2695 | ||
91148fea | 2696 | /* XXX choose the best lwp? */ |
08f2f1bb SS |
2697 | lp1 = FIRST_LWP_IN_PROC(p1); |
2698 | lp2 = FIRST_LWP_IN_PROC(p2); | |
2699 | ||
91148fea MD |
2700 | /* |
2701 | * Favor one with LWPs verses one that has none (is exiting). | |
2702 | */ | |
2703 | if (lp1 == NULL) { | |
2704 | res = 1; | |
2705 | goto done; | |
2706 | } | |
2707 | if (lp2 == NULL) { | |
2708 | res = 0; | |
2709 | goto done; | |
2710 | } | |
2711 | ||
984263bc MD |
2712 | /* |
2713 | * see if at least one of them is runnable | |
2714 | */ | |
164b8401 | 2715 | switch (TESTAB(ISRUN(lp1), ISRUN(lp2))) { |
984263bc | 2716 | case ONLYA: |
91148fea MD |
2717 | res = 0; |
2718 | goto done; | |
984263bc | 2719 | case ONLYB: |
91148fea MD |
2720 | res = 1; |
2721 | goto done; | |
984263bc MD |
2722 | case BOTH: |
2723 | /* | |
2724 | * tie - favor one with highest recent cpu utilization | |
2725 | */ | |
08f2f1bb | 2726 | if (lp2->lwp_cpticks > lp1->lwp_cpticks) |
91148fea MD |
2727 | res = 1; |
2728 | else if (lp1->lwp_cpticks > lp2->lwp_cpticks) | |
2729 | res = 0; | |
2730 | else | |
2731 | res = (p2->p_pid > p1->p_pid); /* tie - ret highest */ | |
2732 | goto done; | |
2733 | default: | |
2734 | break; | |
984263bc | 2735 | } |
91148fea | 2736 | |
984263bc | 2737 | /* |
91148fea | 2738 | * Pick the one with the smallest sleep time |
984263bc | 2739 | */ |
91148fea MD |
2740 | if (lp2->lwp_slptime > lp1->lwp_slptime) { |
2741 | res = 0; | |
2742 | goto done; | |
2743 | } | |
2744 | if (lp1->lwp_slptime > lp2->lwp_slptime) { | |
2745 | res = 1; | |
2746 | goto done; | |
2747 | } | |
2748 | ||
984263bc | 2749 | /* |
91148fea | 2750 | * Favor one sleeping in a non-interruptible sleep |
984263bc | 2751 | */ |
91148fea MD |
2752 | if ((lp1->lwp_flags & LWP_SINTR) && (lp2->lwp_flags & LWP_SINTR) == 0) |
2753 | res = 1; | |
2754 | else | |
2755 | if ((lp2->lwp_flags & LWP_SINTR) && (lp1->lwp_flags & LWP_SINTR) == 0) | |
2756 | res = 0; | |
2757 | else | |
2758 | res = (p2->p_pid > p1->p_pid); /* tie - return highest pid */ | |
2759 | /* fall through */ | |
2760 | ||
2761 | done: | |
2762 | lwkt_reltoken(&p2->p_token); | |
2763 | lwkt_reltoken(&p1->p_token); | |
2764 | return (res); | |
984263bc MD |
2765 | } |
2766 | ||
2767 | /* | |
2768 | * Output char to tty; console putchar style. | |
2769 | */ | |
2770 | int | |
c972a82f | 2771 | tputchar(int c, struct tty *tp) |
984263bc | 2772 | { |
2efb75f3 | 2773 | lwkt_gettoken(&tp->t_token); |
984263bc | 2774 | if (!ISSET(tp->t_state, TS_CONNECTED)) { |
2efb75f3 | 2775 | lwkt_reltoken(&tp->t_token); |
984263bc MD |
2776 | return (-1); |
2777 | } | |
2778 | if (c == '\n') | |
2779 | (void)ttyoutput('\r', tp); | |
2780 | (void)ttyoutput(c, tp); | |
2781 | ttstart(tp); | |
2efb75f3 MD |
2782 | lwkt_reltoken(&tp->t_token); |
2783 | ||
984263bc MD |
2784 | return (0); |
2785 | } | |
2786 | ||
2787 | /* | |
2788 | * Sleep on chan, returning ERESTART if tty changed while we napped and | |
2789 | * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep. If | |
2790 | * the tty is revoked, restarting a pending call will redo validation done | |
2791 | * at the start of the call. | |
2792 | */ | |
2793 | int | |
c972a82f | 2794 | ttysleep(struct tty *tp, void *chan, int slpflags, char *wmesg, int timo) |
984263bc MD |
2795 | { |
2796 | int error; | |
2797 | int gen; | |
2798 | ||
2799 | gen = tp->t_gen; | |
377d4740 | 2800 | error = tsleep(chan, slpflags, wmesg, timo); |
984263bc MD |
2801 | if (error) |
2802 | return (error); | |
2803 | return (tp->t_gen == gen ? 0 : ERESTART); | |
2804 | } | |
2805 | ||
a32446b7 MD |
2806 | /* |
2807 | * Revoke a tty. | |
2808 | * | |
2809 | * We bump the gen to force any ttysleep()'s to return with ERESTART | |
2810 | * and flush the tty. The related fp's should already have been | |
2811 | * replaced so the tty will close when the last references on the | |
2812 | * original fp's go away. | |
2813 | */ | |
2814 | int | |
2815 | ttyrevoke(struct dev_revoke_args *ap) | |
2816 | { | |
2817 | struct tty *tp; | |
2818 | ||
2819 | tp = ap->a_head.a_dev->si_tty; | |
2efb75f3 | 2820 | lwkt_gettoken(&tp->t_token); |
a32446b7 MD |
2821 | tp->t_gen++; |
2822 | ttyflush(tp, FREAD | FWRITE); | |
2823 | wakeup(TSA_CARR_ON(tp)); | |
2824 | ttwakeup(tp); | |
2825 | ttwwakeup(tp); | |
2efb75f3 MD |
2826 | lwkt_reltoken(&tp->t_token); |
2827 | ||
a32446b7 MD |
2828 | return (0); |
2829 | } | |
2830 | ||
984263bc MD |
2831 | /* |
2832 | * Allocate a tty struct. Clists in the struct will be allocated by | |
2efb75f3 MD |
2833 | * ttyopen(). The tty itself is protected by tp->t_token. tty_token |
2834 | * is primarily used to interlock the NULL test and for registration. | |
2835 | * | |
2836 | * by convention, once allocated, tty structures are never freed. This | |
2837 | * saves us from numerous release race scenarios that can occur due to the | |
2838 | * token being embedded in the tty structure. | |
984263bc MD |
2839 | */ |
2840 | struct tty * | |
2efb75f3 | 2841 | ttymalloc(struct tty **tpp) |
984263bc | 2842 | { |
2efb75f3 | 2843 | struct tty *tp; |
984263bc | 2844 | |
2efb75f3 MD |
2845 | if ((tp = *tpp) == NULL) { |
2846 | tp = kmalloc(sizeof *tp, M_TTYS, M_WAITOK|M_ZERO); | |
2847 | lwkt_gettoken(&tty_token); | |
2848 | if (*tpp == NULL) { /* recheck after blocking kmalloc */ | |
2849 | *tpp = tp; | |
2850 | ttyinit(tp); | |
2851 | ttyregister(tp); | |
2852 | } else { | |
2853 | kfree(tp, M_TTYS); | |
2854 | } | |
2855 | lwkt_reltoken(&tty_token); | |
cd29885a | 2856 | } |
984263bc MD |
2857 | return (tp); |
2858 | } | |
2859 | ||
2efb75f3 MD |
2860 | /* |
2861 | * Caller must hold tp->t_token | |
2862 | */ | |
984263bc | 2863 | void |
ddac2002 | 2864 | ttyunregister(struct tty *tp) |
984263bc | 2865 | { |
22ff886e | 2866 | lwkt_gettoken(&tty_token); |
2efb75f3 MD |
2867 | if (ISSET(tp->t_state, TS_REGISTERED)) { |
2868 | CLR(tp->t_state, TS_REGISTERED); | |
2869 | TAILQ_REMOVE(&tty_list, tp, t_list); | |
2870 | } | |
22ff886e | 2871 | lwkt_reltoken(&tty_token); |
984263bc | 2872 | } |
984263bc | 2873 | |
2efb75f3 MD |
2874 | void |
2875 | ttyinit(struct tty *tp) | |
2876 | { | |
2877 | lwkt_token_init(&tp->t_token, "tp"); | |
2878 | } | |
2879 | ||
984263bc | 2880 | void |
c972a82f | 2881 | ttyregister(struct tty *tp) |
984263bc | 2882 | { |
22ff886e | 2883 | lwkt_gettoken(&tty_token); |
2efb75f3 MD |
2884 | if (!ISSET(tp->t_state, TS_REGISTERED)) { |
2885 | SET(tp->t_state, TS_REGISTERED); | |
2886 | TAILQ_INSERT_HEAD(&tty_list, tp, t_list); | |
2887 | } | |
22ff886e | 2888 | lwkt_reltoken(&tty_token); |
984263bc MD |
2889 | } |
2890 | ||
2891 | static int | |
2892 | sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) | |
2893 | { | |
2894 | int error; | |
ddac2002 MD |
2895 | struct tty *tp; |
2896 | struct tty t; | |
2897 | struct tty marker; | |
2898 | ||
2899 | bzero(&marker, sizeof(marker)); | |
2900 | marker.t_state = TS_MARKER; | |
2901 | error = 0; | |
2902 | ||
22ff886e AH |
2903 | lwkt_gettoken(&tty_token); |
2904 | ||
ddac2002 MD |
2905 | TAILQ_INSERT_HEAD(&tty_list, &marker, t_list); |
2906 | while ((tp = TAILQ_NEXT(&marker, t_list)) != NULL) { | |
2907 | TAILQ_REMOVE(&tty_list, &marker, t_list); | |
2908 | TAILQ_INSERT_AFTER(&tty_list, tp, &marker, t_list); | |
2909 | if (tp->t_state & TS_MARKER) | |
2910 | continue; | |
984263bc MD |
2911 | t = *tp; |
2912 | if (t.t_dev) | |
2ac7d105 | 2913 | t.t_dev = (cdev_t)(uintptr_t)devid_from_dev(t.t_dev); |
984263bc MD |
2914 | error = SYSCTL_OUT(req, (caddr_t)&t, sizeof(t)); |
2915 | if (error) | |
ddac2002 | 2916 | break; |
984263bc | 2917 | } |
ddac2002 | 2918 | TAILQ_REMOVE(&tty_list, &marker, t_list); |
22ff886e | 2919 | lwkt_reltoken(&tty_token); |
ddac2002 | 2920 | return (error); |
984263bc MD |
2921 | } |
2922 | ||
2923 | SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, | |
2924 | 0, 0, sysctl_kern_ttys, "S,tty", "All struct ttys"); | |
2925 | ||
2926 | void | |
c972a82f | 2927 | nottystop(struct tty *tp, int rw) |
984263bc | 2928 | { |
984263bc MD |
2929 | return; |
2930 | } | |
2931 | ||
2932 | int | |
fef8985e | 2933 | ttyread(struct dev_read_args *ap) |
984263bc MD |
2934 | { |
2935 | struct tty *tp; | |
22ff886e | 2936 | int ret; |
984263bc | 2937 | |
fef8985e | 2938 | tp = ap->a_head.a_dev->si_tty; |
984263bc MD |
2939 | if (tp == NULL) |
2940 | return (ENODEV); | |
2efb75f3 | 2941 | lwkt_gettoken(&tp->t_token); |
22ff886e | 2942 | ret = ((*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag)); |
2efb75f3 | 2943 | lwkt_reltoken(&tp->t_token); |
22ff886e AH |
2944 | |
2945 | return ret; | |
984263bc MD |
2946 | } |
2947 | ||
2948 | int | |
fef8985e | 2949 | ttywrite(struct dev_write_args *ap) |
984263bc MD |
2950 | { |
2951 | struct tty *tp; | |
22ff886e | 2952 | int ret; |
984263bc | 2953 | |
fef8985e | 2954 | tp = ap->a_head.a_dev->si_tty; |
984263bc MD |
2955 | if (tp == NULL) |
2956 | return (ENODEV); | |
2efb75f3 | 2957 | lwkt_gettoken(&tp->t_token); |
22ff886e | 2958 | ret = ((*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag)); |
2efb75f3 | 2959 | lwkt_reltoken(&tp->t_token); |
22ff886e AH |
2960 | |
2961 | return ret; | |
984263bc | 2962 | } |