1 | /* $NetBSD: tty_pty.c,v 1.142 2015/08/20 09:45:45 christos Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1982, 1986, 1989, 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the University nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. |
30 | * |
31 | * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 |
32 | */ |
33 | |
34 | /* |
35 | * Pseudo-teletype Driver |
36 | * (Actually two drivers, requiring two entries in 'cdevsw') |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: tty_pty.c,v 1.142 2015/08/20 09:45:45 christos Exp $" ); |
41 | |
42 | #include "opt_ptm.h" |
43 | |
44 | #define TTY_ALLOW_PRIVATE |
45 | |
46 | #include <sys/param.h> |
47 | #include <sys/systm.h> |
48 | #include <sys/ioctl.h> |
49 | #include <sys/ioctl_compat.h> |
50 | #include <sys/proc.h> |
51 | #include <sys/tty.h> |
52 | #include <sys/stat.h> |
53 | #include <sys/file.h> |
54 | #include <sys/kernel.h> |
55 | #include <sys/vnode.h> |
56 | #include <sys/namei.h> |
57 | #include <sys/signalvar.h> |
58 | #include <sys/uio.h> |
59 | #include <sys/filedesc.h> |
60 | #include <sys/conf.h> |
61 | #include <sys/poll.h> |
62 | #include <sys/pty.h> |
63 | #include <sys/kauth.h> |
64 | |
65 | #include "ioconf.h" |
66 | |
67 | #define DEFAULT_NPTYS 16 /* default number of initial ptys */ |
68 | #define DEFAULT_MAXPTYS 992 /* default maximum number of ptys */ |
69 | |
70 | #define BUFSIZ 100 /* Chunk size iomoved to/from user */ |
71 | |
72 | struct pt_softc { |
73 | struct tty *pt_tty; |
74 | int pt_flags; |
75 | struct selinfo pt_selr, pt_selw; |
76 | u_char pt_send; |
77 | u_char pt_ucntl; |
78 | }; |
79 | |
80 | static struct pt_softc **pt_softc = NULL; /* pty array */ |
81 | static int maxptys = DEFAULT_MAXPTYS; /* maximum number of ptys (sysctable) */ |
82 | kmutex_t pt_softc_mutex; |
83 | int npty = 0; /* for pstat -t */ |
84 | |
85 | #define PF_PKT 0x08 /* packet mode */ |
86 | #define PF_STOPPED 0x10 /* user told stopped */ |
87 | #define PF_REMOTE 0x20 /* remote and flow controlled input */ |
88 | #define PF_NOSTOP 0x40 |
89 | #define PF_UCNTL 0x80 /* user control mode */ |
90 | |
91 | void ptcwakeup(struct tty *, int); |
92 | void ptsstart(struct tty *); |
93 | int pty_maxptys(int, int); |
94 | |
95 | static struct pt_softc **ptyarralloc(int); |
96 | |
97 | dev_type_open(ptcopen); |
98 | dev_type_close(ptcclose); |
99 | dev_type_read(ptcread); |
100 | dev_type_write(ptcwrite); |
101 | dev_type_poll(ptcpoll); |
102 | dev_type_kqfilter(ptckqfilter); |
103 | |
104 | dev_type_open(ptsopen); |
105 | dev_type_close(ptsclose); |
106 | dev_type_read(ptsread); |
107 | dev_type_write(ptswrite); |
108 | dev_type_stop(ptsstop); |
109 | dev_type_poll(ptspoll); |
110 | |
111 | dev_type_ioctl(ptyioctl); |
112 | dev_type_tty(ptytty); |
113 | |
114 | const struct cdevsw ptc_cdevsw = { |
115 | .d_open = ptcopen, |
116 | .d_close = ptcclose, |
117 | .d_read = ptcread, |
118 | .d_write = ptcwrite, |
119 | .d_ioctl = ptyioctl, |
120 | .d_stop = nullstop, |
121 | .d_tty = ptytty, |
122 | .d_poll = ptcpoll, |
123 | .d_mmap = nommap, |
124 | .d_kqfilter = ptckqfilter, |
125 | .d_discard = nodiscard, |
126 | .d_flag = D_TTY |
127 | }; |
128 | |
129 | const struct cdevsw pts_cdevsw = { |
130 | .d_open = ptsopen, |
131 | .d_close = ptsclose, |
132 | .d_read = ptsread, |
133 | .d_write = ptswrite, |
134 | .d_ioctl = ptyioctl, |
135 | .d_stop = ptsstop, |
136 | .d_tty = ptytty, |
137 | .d_poll = ptspoll, |
138 | .d_mmap = nommap, |
139 | .d_kqfilter = ttykqfilter, |
140 | .d_discard = nodiscard, |
141 | .d_flag = D_TTY |
142 | }; |
143 | |
144 | #if defined(pmax) |
145 | /* |
146 | * Used by arch/pmax/conf/majors.pmax, which needs a second copy as it |
147 | * needs to map this stuff to two pairs of majors. |
148 | */ |
149 | |
150 | const struct cdevsw ptc_ultrix_cdevsw = { |
151 | .d_open = ptcopen, |
152 | .d_close = ptcclose, |
153 | .d_read = ptcread, |
154 | .d_write = ptcwrite, |
155 | .d_ioctl = ptyioctl, |
156 | .d_stop = nullstop, |
157 | .d_tty = ptytty, |
158 | .d_poll = ptcpoll, |
159 | .d_mmap = nommap, |
160 | .d_kqfilter = ptckqfilter, |
161 | .d_discard = nodiscard, |
162 | .d_flag = D_TTY |
163 | }; |
164 | |
165 | const struct cdevsw pts_ultrix_cdevsw = { |
166 | .d_open = ptsopen, |
167 | .d_close = ptsclose, |
168 | .d_read = ptsread, |
169 | .d_write = ptswrite, |
170 | .d_ioctl = ptyioctl, |
171 | .d_stop = ptsstop, |
172 | .d_tty = ptytty, |
173 | .d_poll = ptspoll, |
174 | .d_mmap = nommap, |
175 | .d_kqfilter = ttykqfilter, |
176 | .d_discard = nodiscard, |
177 | .d_flag = D_TTY |
178 | }; |
179 | #endif /* defined(pmax) */ |
180 | |
181 | /* |
182 | * Check if a pty is free to use. |
183 | */ |
184 | int |
185 | pty_isfree(int minor, int lock) |
186 | { |
187 | struct pt_softc *pt = pt_softc[minor]; |
188 | if (lock) |
189 | mutex_enter(&pt_softc_mutex); |
190 | minor = pt == NULL || pt->pt_tty == NULL || |
191 | pt->pt_tty->t_oproc == NULL; |
192 | if (lock) |
193 | mutex_exit(&pt_softc_mutex); |
194 | return minor; |
195 | } |
196 | |
197 | /* |
198 | * Allocate and zero array of nelem elements. |
199 | */ |
200 | static struct pt_softc ** |
201 | ptyarralloc(int nelem) |
202 | { |
203 | struct pt_softc **pt; |
204 | nelem += 10; |
205 | pt = kmem_zalloc(nelem * sizeof(*pt), KM_SLEEP); |
206 | return pt; |
207 | } |
208 | |
209 | static void |
210 | ptyarrfree(struct pt_softc **pt, int nelem) |
211 | { |
212 | |
213 | nelem += 10; |
214 | kmem_free(pt, nelem * sizeof(*pt)); |
215 | } |
216 | |
217 | /* |
218 | * Check if the minor is correct and ensure necessary structures |
219 | * are properly allocated. |
220 | */ |
221 | int |
222 | pty_check(int ptn) |
223 | { |
224 | struct pt_softc *pti; |
225 | |
226 | if (ptn >= npty) { |
227 | struct pt_softc **newpt, **oldpt; |
228 | int newnpty; |
229 | int oldnpty; |
230 | |
231 | /* check if the requested pty can be granted */ |
232 | if (ptn >= maxptys) { |
233 | limit_reached: |
234 | tablefull("pty" , "increase kern.maxptys" ); |
235 | return ENXIO; |
236 | } |
237 | |
238 | /* Allocate a larger pty array */ |
239 | for (newnpty = npty; newnpty <= ptn;) |
240 | newnpty *= 2; |
241 | if (newnpty > maxptys) |
242 | newnpty = maxptys; |
243 | newpt = ptyarralloc(newnpty); |
244 | |
245 | /* |
246 | * Now grab the pty array mutex - we need to ensure |
247 | * that the pty array is consistent while copying its |
248 | * content to newly allocated, larger space; we also |
249 | * need to be safe against pty_maxptys(). |
250 | */ |
251 | mutex_enter(&pt_softc_mutex); |
252 | |
253 | if (newnpty >= maxptys) { |
254 | /* limit cut away beneath us... */ |
255 | if (ptn >= maxptys) { |
256 | mutex_exit(&pt_softc_mutex); |
257 | ptyarrfree(newpt, newnpty); |
258 | goto limit_reached; |
259 | } |
260 | newnpty = maxptys; |
261 | } |
262 | |
263 | /* |
264 | * If the pty array was not enlarged while we were waiting |
265 | * for mutex, copy current contents of pt_softc[] to newly |
266 | * allocated array and start using the new bigger array. |
267 | */ |
268 | if (newnpty > npty) { |
269 | memcpy(newpt, pt_softc, npty*sizeof(struct pt_softc *)); |
270 | oldpt = pt_softc; |
271 | oldnpty = npty; |
272 | pt_softc = newpt; |
273 | npty = newnpty; |
274 | } else { |
275 | /* was enlarged when waited for lock, free new space */ |
276 | oldpt = newpt; |
277 | oldnpty = newnpty; |
278 | } |
279 | |
280 | mutex_exit(&pt_softc_mutex); |
281 | ptyarrfree(oldpt, oldnpty); |
282 | } |
283 | |
284 | /* |
285 | * If the entry is not yet allocated, allocate one. The mutex is |
286 | * needed so that the state of pt_softc[] array is consistant |
287 | * in case it has been lengthened above. |
288 | */ |
289 | if (!pt_softc[ptn]) { |
290 | pti = kmem_zalloc(sizeof(*pti), KM_SLEEP); |
291 | |
292 | selinit(&pti->pt_selr); |
293 | selinit(&pti->pt_selw); |
294 | pti->pt_tty = tty_alloc(); |
295 | |
296 | mutex_enter(&pt_softc_mutex); |
297 | |
298 | /* |
299 | * Check the entry again - it might have been |
300 | * added while we were waiting for mutex. |
301 | */ |
302 | if (pt_softc[ptn]) { |
303 | mutex_exit(&pt_softc_mutex); |
304 | tty_free(pti->pt_tty); |
305 | seldestroy(&pti->pt_selr); |
306 | seldestroy(&pti->pt_selw); |
307 | kmem_free(pti, sizeof(*pti)); |
308 | return 0; |
309 | } |
310 | tty_attach(pti->pt_tty); |
311 | pt_softc[ptn] = pti; |
312 | |
313 | mutex_exit(&pt_softc_mutex); |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | /* |
320 | * Set maxpty in thread-safe way. Returns 0 in case of error, otherwise |
321 | * new value of maxptys. |
322 | */ |
323 | int |
324 | pty_maxptys(int newmax, int set) |
325 | { |
326 | if (!set) |
327 | return maxptys; |
328 | |
329 | /* |
330 | * We have to grab the pt_softc lock, so that we would pick correct |
331 | * value of npty (might be modified in pty_check()). |
332 | */ |
333 | mutex_enter(&pt_softc_mutex); |
334 | |
335 | /* |
336 | * The value cannot be set to value lower than the highest pty |
337 | * number ever allocated. |
338 | */ |
339 | if (newmax >= npty) |
340 | maxptys = newmax; |
341 | else |
342 | newmax = 0; |
343 | |
344 | mutex_exit(&pt_softc_mutex); |
345 | |
346 | return newmax; |
347 | } |
348 | |
349 | /* |
350 | * Establish n (or default if n is 1) ptys in the system. |
351 | */ |
352 | void |
353 | ptyattach(int n) |
354 | { |
355 | |
356 | mutex_init(&pt_softc_mutex, MUTEX_DEFAULT, IPL_NONE); |
357 | |
358 | /* maybe should allow 0 => none? */ |
359 | if (n <= 1) |
360 | n = DEFAULT_NPTYS; |
361 | pt_softc = ptyarralloc(n); |
362 | npty = n; |
363 | #ifndef NO_DEV_PTM |
364 | ptmattach(1); |
365 | #endif |
366 | } |
367 | |
368 | /*ARGSUSED*/ |
369 | int |
370 | ptsopen(dev_t dev, int flag, int devtype, struct lwp *l) |
371 | { |
372 | struct pt_softc *pti; |
373 | struct tty *tp; |
374 | int error; |
375 | int ptn = minor(dev); |
376 | |
377 | if ((error = pty_check(ptn)) != 0) |
378 | return error; |
379 | |
380 | mutex_spin_enter(&tty_lock); |
381 | pti = pt_softc[ptn]; |
382 | tp = pti->pt_tty; |
383 | if (!ISSET(tp->t_state, TS_ISOPEN)) { |
384 | tp->t_dev = dev; |
385 | ttychars(tp); /* Set up default chars */ |
386 | tp->t_iflag = TTYDEF_IFLAG; |
387 | tp->t_oflag = TTYDEF_OFLAG; |
388 | tp->t_lflag = TTYDEF_LFLAG; |
389 | tp->t_cflag = TTYDEF_CFLAG; |
390 | tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; |
391 | ttsetwater(tp); /* would be done in xxparam() */ |
392 | } else if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, |
393 | tp) != 0) { |
394 | mutex_spin_exit(&tty_lock); |
395 | return EBUSY; |
396 | } |
397 | if (tp->t_oproc) /* Ctrlr still around. */ |
398 | SET(tp->t_state, TS_CARR_ON); |
399 | if (!ISSET(flag, O_NONBLOCK)) { |
400 | while (!ISSET(tp->t_state, TS_CARR_ON)) { |
401 | tp->t_wopen++; |
402 | error = ttysleep(tp, &tp->t_rawcv, true, 0); |
403 | tp->t_wopen--; |
404 | if (error != 0) { |
405 | mutex_spin_exit(&tty_lock); |
406 | return error; |
407 | } |
408 | } |
409 | } |
410 | mutex_spin_exit(&tty_lock); |
411 | error = (*tp->t_linesw->l_open)(dev, tp); |
412 | ptcwakeup(tp, FREAD|FWRITE); |
413 | return error; |
414 | } |
415 | |
416 | int |
417 | ptsclose(dev_t dev, int flag, int mode, struct lwp *l) |
418 | { |
419 | struct pt_softc *pti = pt_softc[minor(dev)]; |
420 | struct tty *tp = pti->pt_tty; |
421 | int error; |
422 | |
423 | error = (*tp->t_linesw->l_close)(tp, flag); |
424 | error |= ttyclose(tp); |
425 | ptcwakeup(tp, FREAD|FWRITE); |
426 | return error; |
427 | } |
428 | |
429 | int |
430 | ptsread(dev_t dev, struct uio *uio, int flag) |
431 | { |
432 | struct proc *p = curproc; |
433 | struct pt_softc *pti = pt_softc[minor(dev)]; |
434 | struct tty *tp = pti->pt_tty; |
435 | int error = 0; |
436 | int cc, c; |
437 | |
438 | again: |
439 | if (pti->pt_flags & PF_REMOTE) { |
440 | mutex_spin_enter(&tty_lock); |
441 | while (isbackground(p, tp)) { /* XXXSMP */ |
442 | if (sigismasked(curlwp, SIGTTIN) || |
443 | p->p_pgrp->pg_jobc == 0 || |
444 | p->p_lflag & PL_PPWAIT) { |
445 | mutex_spin_exit(&tty_lock); |
446 | return EIO; |
447 | } |
448 | ttysig(tp, TTYSIG_PG1, SIGTTIN); |
449 | error = ttypause(tp, hz); |
450 | if (error != 0) { |
451 | mutex_spin_exit(&tty_lock); |
452 | return error; |
453 | } |
454 | } |
455 | if (tp->t_canq.c_cc == 0) { |
456 | if (flag & IO_NDELAY) { |
457 | mutex_spin_exit(&tty_lock); |
458 | return EWOULDBLOCK; |
459 | } |
460 | error = ttysleep(tp, &tp->t_cancv, true, 0); |
461 | mutex_spin_exit(&tty_lock); |
462 | if (error != 0) |
463 | return error; |
464 | goto again; |
465 | } |
466 | while(error == 0 && tp->t_canq.c_cc > 1 && uio->uio_resid > 0) { |
467 | c = getc(&tp->t_canq); |
468 | mutex_spin_exit(&tty_lock); |
469 | error = ureadc(c, uio); |
470 | mutex_spin_enter(&tty_lock); |
471 | /* Re-check terminal state here? */ |
472 | } |
473 | if (tp->t_canq.c_cc == 1) |
474 | (void) getc(&tp->t_canq); |
475 | cc = tp->t_canq.c_cc; |
476 | mutex_spin_exit(&tty_lock); |
477 | if (cc) |
478 | return error; |
479 | } else if (tp->t_oproc) |
480 | error = (*tp->t_linesw->l_read)(tp, uio, flag); |
481 | ptcwakeup(tp, FWRITE); |
482 | return error; |
483 | } |
484 | |
485 | /* |
486 | * Write to pseudo-tty. |
487 | * Wakeups of controlling tty will happen |
488 | * indirectly, when tty driver calls ptsstart. |
489 | */ |
490 | int |
491 | ptswrite(dev_t dev, struct uio *uio, int flag) |
492 | { |
493 | struct pt_softc *pti = pt_softc[minor(dev)]; |
494 | struct tty *tp = pti->pt_tty; |
495 | |
496 | if (tp->t_oproc == NULL) |
497 | return EIO; |
498 | return (*tp->t_linesw->l_write)(tp, uio, flag); |
499 | } |
500 | |
501 | /* |
502 | * Poll pseudo-tty. |
503 | */ |
504 | int |
505 | ptspoll(dev_t dev, int events, struct lwp *l) |
506 | { |
507 | struct pt_softc *pti = pt_softc[minor(dev)]; |
508 | struct tty *tp = pti->pt_tty; |
509 | |
510 | if (tp->t_oproc == NULL) |
511 | return POLLHUP; |
512 | |
513 | return (*tp->t_linesw->l_poll)(tp, events, l); |
514 | } |
515 | |
516 | /* |
517 | * Start output on pseudo-tty. |
518 | * Wake up process polling or sleeping for input from controlling tty. |
519 | */ |
520 | void |
521 | ptsstart(struct tty *tp) |
522 | { |
523 | struct pt_softc *pti; |
524 | |
525 | KASSERT(tp->t_dev != NODEV); |
526 | pti = pt_softc[minor(tp->t_dev)]; |
527 | |
528 | KASSERT(mutex_owned(&tty_lock)); |
529 | |
530 | if (ISSET(tp->t_state, TS_TTSTOP)) |
531 | return; |
532 | if (pti->pt_flags & PF_STOPPED) { |
533 | pti->pt_flags &= ~PF_STOPPED; |
534 | pti->pt_send = TIOCPKT_START; |
535 | } |
536 | |
537 | selnotify(&pti->pt_selr, 0, NOTE_SUBMIT); |
538 | cv_broadcast(&tp->t_outcvf); |
539 | } |
540 | |
541 | /* |
542 | * Stop output. |
543 | */ |
544 | void |
545 | ptsstop(struct tty *tp, int flush) |
546 | { |
547 | struct pt_softc *pti; |
548 | |
549 | KASSERT(tp->t_dev != NODEV); |
550 | pti = pt_softc[minor(tp->t_dev)]; |
551 | |
552 | KASSERT(mutex_owned(&tty_lock)); |
553 | |
554 | /* note: FLUSHREAD and FLUSHWRITE already ok */ |
555 | CTASSERT(TIOCPKT_FLUSHREAD == FREAD); |
556 | CTASSERT(TIOCPKT_FLUSHWRITE == FWRITE); |
557 | if (flush == 0) { |
558 | flush = TIOCPKT_STOP; |
559 | pti->pt_flags |= PF_STOPPED; |
560 | } else |
561 | pti->pt_flags &= ~PF_STOPPED; |
562 | pti->pt_send |= flush; |
563 | |
564 | /* change of perspective */ |
565 | if (flush & FREAD) { |
566 | selnotify(&pti->pt_selw, 0, NOTE_SUBMIT); |
567 | cv_broadcast(&tp->t_rawcvf); |
568 | } |
569 | if (flush & FWRITE) { |
570 | selnotify(&pti->pt_selr, 0, NOTE_SUBMIT); |
571 | cv_broadcast(&tp->t_outcvf); |
572 | } |
573 | } |
574 | |
575 | void |
576 | ptcwakeup(struct tty *tp, int flag) |
577 | { |
578 | struct pt_softc *pti; |
579 | |
580 | if (tp->t_dev == NODEV) |
581 | return; /* client side not open yet */ |
582 | |
583 | pti = pt_softc[minor(tp->t_dev)]; |
584 | KASSERT(pti != NULL); |
585 | |
586 | mutex_spin_enter(&tty_lock); |
587 | if (flag & FREAD) { |
588 | selnotify(&pti->pt_selr, 0, NOTE_SUBMIT); |
589 | cv_broadcast(&tp->t_outcvf); |
590 | } |
591 | if (flag & FWRITE) { |
592 | selnotify(&pti->pt_selw, 0, NOTE_SUBMIT); |
593 | cv_broadcast(&tp->t_rawcvf); |
594 | } |
595 | mutex_spin_exit(&tty_lock); |
596 | } |
597 | |
598 | /*ARGSUSED*/ |
599 | int |
600 | ptcopen(dev_t dev, int flag, int devtype, struct lwp *l) |
601 | { |
602 | struct pt_softc *pti; |
603 | struct tty *tp; |
604 | int error; |
605 | int ptn = minor(dev); |
606 | |
607 | if ((error = pty_check(ptn)) != 0) |
608 | return error; |
609 | |
610 | pti = pt_softc[ptn]; |
611 | tp = pti->pt_tty; |
612 | |
613 | mutex_spin_enter(&tty_lock); |
614 | if (tp->t_oproc) { |
615 | mutex_spin_exit(&tty_lock); |
616 | return EIO; |
617 | } |
618 | tp->t_dev = dev; |
619 | tp->t_oproc = ptsstart; |
620 | mutex_spin_exit(&tty_lock); |
621 | (void)(*tp->t_linesw->l_modem)(tp, 1); |
622 | CLR(tp->t_lflag, EXTPROC); |
623 | pti->pt_flags = 0; |
624 | pti->pt_send = 0; |
625 | pti->pt_ucntl = 0; |
626 | return 0; |
627 | } |
628 | |
629 | /*ARGSUSED*/ |
630 | int |
631 | ptcclose(dev_t dev, int flag, int devtype, struct lwp *l) |
632 | { |
633 | struct pt_softc *pti = pt_softc[minor(dev)]; |
634 | struct tty *tp = pti->pt_tty; |
635 | |
636 | (void)(*tp->t_linesw->l_modem)(tp, 0); |
637 | mutex_spin_enter(&tty_lock); |
638 | CLR(tp->t_state, TS_CARR_ON); |
639 | tp->t_oproc = NULL; /* mark closed */ |
640 | mutex_spin_exit(&tty_lock); |
641 | return 0; |
642 | } |
643 | |
644 | int |
645 | ptcread(dev_t dev, struct uio *uio, int flag) |
646 | { |
647 | struct pt_softc *pti = pt_softc[minor(dev)]; |
648 | struct tty *tp = pti->pt_tty; |
649 | u_char bf[BUFSIZ]; |
650 | int error = 0, cc; |
651 | int c; |
652 | |
653 | if (uio->uio_resid <= 0) |
654 | return EINVAL; |
655 | |
656 | /* |
657 | * We want to block until the slave |
658 | * is open, and there's something to read; |
659 | * but if we lost the slave or we're NBIO, |
660 | * then return the appropriate error instead. |
661 | */ |
662 | mutex_spin_enter(&tty_lock); |
663 | for (;;) { |
664 | if (ISSET(tp->t_state, TS_ISOPEN)) { |
665 | if (pti->pt_flags & PF_PKT && (c = pti->pt_send)) { |
666 | pti->pt_send = 0; |
667 | mutex_spin_exit(&tty_lock); |
668 | error = ureadc(c, uio); |
669 | if (error != 0) |
670 | return error; |
671 | /* |
672 | * Since we don't have the tty locked, there's |
673 | * a risk of messing up `t_termios'. This is |
674 | * relevant only if the tty got closed and then |
675 | * opened again while we were out uiomoving. |
676 | */ |
677 | if (c & TIOCPKT_IOCTL) { |
678 | cc = min(uio->uio_resid, |
679 | sizeof(tp->t_termios)); |
680 | uiomove((void *) &tp->t_termios, |
681 | cc, uio); |
682 | } |
683 | return 0; |
684 | } |
685 | if (pti->pt_flags & PF_UCNTL && (c = pti->pt_ucntl)) { |
686 | pti->pt_ucntl = 0; |
687 | mutex_spin_exit(&tty_lock); |
688 | error = ureadc(c, uio); |
689 | if (error != 0) |
690 | return error; |
691 | return 0; |
692 | } |
693 | if (tp->t_outq.c_cc && !ISSET(tp->t_state, TS_TTSTOP)) |
694 | break; |
695 | } |
696 | if (!ISSET(tp->t_state, TS_CARR_ON)) { |
697 | error = 0; /* EOF */ |
698 | goto out; |
699 | } |
700 | if (flag & IO_NDELAY) { |
701 | error = EWOULDBLOCK; |
702 | goto out; |
703 | } |
704 | error = cv_wait_sig(&tp->t_outcvf, &tty_lock); |
705 | if (error != 0) |
706 | goto out; |
707 | } |
708 | |
709 | if (pti->pt_flags & (PF_PKT|PF_UCNTL)) { |
710 | mutex_spin_exit(&tty_lock); |
711 | error = ureadc(0, uio); |
712 | mutex_spin_enter(&tty_lock); |
713 | if (error == 0 && !ISSET(tp->t_state, TS_ISOPEN)) |
714 | error = EIO; |
715 | } |
716 | while (uio->uio_resid > 0 && error == 0) { |
717 | cc = q_to_b(&tp->t_outq, bf, min(uio->uio_resid, BUFSIZ)); |
718 | if (cc <= 0) |
719 | break; |
720 | mutex_spin_exit(&tty_lock); |
721 | error = uiomove(bf, cc, uio); |
722 | mutex_spin_enter(&tty_lock); |
723 | if (error == 0 && !ISSET(tp->t_state, TS_ISOPEN)) |
724 | error = EIO; |
725 | } |
726 | ttypull(tp); |
727 | out: |
728 | mutex_spin_exit(&tty_lock); |
729 | return error; |
730 | } |
731 | |
732 | |
733 | int |
734 | ptcwrite(dev_t dev, struct uio *uio, int flag) |
735 | { |
736 | struct pt_softc *pti = pt_softc[minor(dev)]; |
737 | struct tty *tp = pti->pt_tty; |
738 | u_char *cp = NULL; |
739 | int cc = 0; |
740 | u_char locbuf[BUFSIZ]; |
741 | int cnt = 0; |
742 | int error = 0; |
743 | |
744 | again: |
745 | mutex_spin_enter(&tty_lock); |
746 | if (!ISSET(tp->t_state, TS_ISOPEN)) |
747 | goto block; |
748 | if (pti->pt_flags & PF_REMOTE) { |
749 | if (tp->t_canq.c_cc) |
750 | goto block; |
751 | while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG) { |
752 | if (cc == 0) { |
753 | cc = min(uio->uio_resid, BUFSIZ); |
754 | cc = min(cc, TTYHOG - tp->t_canq.c_cc); |
755 | cp = locbuf; |
756 | mutex_spin_exit(&tty_lock); |
757 | error = uiomove(cp, cc, uio); |
758 | if (error != 0) |
759 | return error; |
760 | mutex_spin_enter(&tty_lock); |
761 | /* check again for safety */ |
762 | if (!ISSET(tp->t_state, TS_ISOPEN)) { |
763 | /* |
764 | * adjust for data copied in but not |
765 | * written |
766 | */ |
767 | uio->uio_resid += cc; |
768 | error = EIO; |
769 | goto out; |
770 | } |
771 | } |
772 | if (cc) { |
773 | cc = b_to_q(cp, cc, &tp->t_outq); |
774 | if (cc > 0) |
775 | goto block; |
776 | } |
777 | } |
778 | (void) putc(0, &tp->t_canq); |
779 | ttwakeup(tp); |
780 | cv_broadcast(&tp->t_cancv); |
781 | error = 0; |
782 | goto out; |
783 | } |
784 | while (uio->uio_resid > 0) { |
785 | if (cc == 0) { |
786 | cc = min(uio->uio_resid, BUFSIZ); |
787 | cp = locbuf; |
788 | mutex_spin_exit(&tty_lock); |
789 | error = uiomove(cp, cc, uio); |
790 | if (error != 0) |
791 | return error; |
792 | mutex_spin_enter(&tty_lock); |
793 | /* check again for safety */ |
794 | if (!ISSET(tp->t_state, TS_ISOPEN)) { |
795 | /* adjust for data copied in but not written */ |
796 | uio->uio_resid += cc; |
797 | error = EIO; |
798 | goto out; |
799 | } |
800 | } |
801 | while (cc > 0) { |
802 | int used = tp->t_rawq.c_cc + tp->t_canq.c_cc; |
803 | int canon = ISSET(tp->t_lflag, ICANON) ? 1 : 0; |
804 | /* |
805 | * We need space for 2 characters if canonical |
806 | * because we might need to print ^C |
807 | */ |
808 | if (used >= (TTYHOG - canon) && |
809 | (tp->t_canq.c_cc > 0 || !canon)) { |
810 | cv_broadcast(&tp->t_rawcv); |
811 | goto block; |
812 | } |
813 | /* |
814 | * XXX - should change l_rint to be called with lock |
815 | * see also tty.c:ttyinput_wlock() |
816 | */ |
817 | mutex_spin_exit(&tty_lock); |
818 | (*tp->t_linesw->l_rint)(*cp++, tp); |
819 | mutex_spin_enter(&tty_lock); |
820 | cnt++; |
821 | cc--; |
822 | } |
823 | } |
824 | error = 0; |
825 | goto out; |
826 | |
827 | block: |
828 | /* |
829 | * Come here to wait for slave to open, for space |
830 | * in outq, or space in rawq. |
831 | */ |
832 | if (!ISSET(tp->t_state, TS_CARR_ON)) { |
833 | /* adjust for data copied in but not written */ |
834 | uio->uio_resid += cc; |
835 | error = EIO; |
836 | goto out; |
837 | } |
838 | if (flag & IO_NDELAY) { |
839 | /* adjust for data copied in but not written */ |
840 | uio->uio_resid += cc; |
841 | error = cnt == 0 ? EWOULDBLOCK : 0; |
842 | goto out; |
843 | } |
844 | error = cv_wait_sig(&tp->t_rawcvf, &tty_lock); |
845 | mutex_spin_exit(&tty_lock); |
846 | if (error != 0) { |
847 | /* adjust for data copied in but not written */ |
848 | uio->uio_resid += cc; |
849 | return error; |
850 | } |
851 | goto again; |
852 | |
853 | out: |
854 | mutex_spin_exit(&tty_lock); |
855 | return error; |
856 | } |
857 | |
858 | int |
859 | ptcpoll(dev_t dev, int events, struct lwp *l) |
860 | { |
861 | struct pt_softc *pti = pt_softc[minor(dev)]; |
862 | struct tty *tp = pti->pt_tty; |
863 | int revents = 0; |
864 | |
865 | mutex_spin_enter(&tty_lock); |
866 | |
867 | if (events & (POLLIN | POLLRDNORM)) |
868 | if (ISSET(tp->t_state, TS_ISOPEN) && |
869 | ((tp->t_outq.c_cc > 0 && !ISSET(tp->t_state, TS_TTSTOP)) || |
870 | ((pti->pt_flags & PF_PKT) && pti->pt_send) || |
871 | ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))) |
872 | revents |= events & (POLLIN | POLLRDNORM); |
873 | |
874 | if (events & (POLLOUT | POLLWRNORM)) |
875 | if (ISSET(tp->t_state, TS_ISOPEN) && |
876 | ((pti->pt_flags & PF_REMOTE) ? |
877 | (tp->t_canq.c_cc == 0) : |
878 | ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2) || |
879 | (tp->t_canq.c_cc == 0 && ISSET(tp->t_lflag, ICANON))))) |
880 | revents |= events & (POLLOUT | POLLWRNORM); |
881 | |
882 | if (events & POLLHUP) |
883 | if (!ISSET(tp->t_state, TS_CARR_ON)) |
884 | revents |= POLLHUP; |
885 | |
886 | if (revents == 0) { |
887 | if (events & (POLLIN | POLLHUP | POLLRDNORM)) |
888 | selrecord(l, &pti->pt_selr); |
889 | |
890 | if (events & (POLLOUT | POLLWRNORM)) |
891 | selrecord(l, &pti->pt_selw); |
892 | } |
893 | |
894 | mutex_spin_exit(&tty_lock); |
895 | |
896 | return revents; |
897 | } |
898 | |
899 | static void |
900 | filt_ptcrdetach(struct knote *kn) |
901 | { |
902 | struct pt_softc *pti; |
903 | |
904 | pti = kn->kn_hook; |
905 | |
906 | mutex_spin_enter(&tty_lock); |
907 | SLIST_REMOVE(&pti->pt_selr.sel_klist, kn, knote, kn_selnext); |
908 | mutex_spin_exit(&tty_lock); |
909 | } |
910 | |
911 | static int |
912 | filt_ptcread(struct knote *kn, long hint) |
913 | { |
914 | struct pt_softc *pti; |
915 | struct tty *tp; |
916 | int canread; |
917 | |
918 | pti = kn->kn_hook; |
919 | tp = pti->pt_tty; |
920 | |
921 | if ((hint & NOTE_SUBMIT) == 0) { |
922 | mutex_spin_enter(&tty_lock); |
923 | } |
924 | |
925 | canread = (ISSET(tp->t_state, TS_ISOPEN) && |
926 | ((tp->t_outq.c_cc > 0 && !ISSET(tp->t_state, TS_TTSTOP)) || |
927 | ((pti->pt_flags & PF_PKT) && pti->pt_send) || |
928 | ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))); |
929 | |
930 | if (canread) { |
931 | /* |
932 | * c_cc is number of characters after output post-processing; |
933 | * the amount of data actually read(2) depends on |
934 | * setting of input flags for the terminal. |
935 | */ |
936 | kn->kn_data = tp->t_outq.c_cc; |
937 | if (((pti->pt_flags & PF_PKT) && pti->pt_send) || |
938 | ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl)) |
939 | kn->kn_data++; |
940 | } |
941 | |
942 | if ((hint & NOTE_SUBMIT) == 0) { |
943 | mutex_spin_exit(&tty_lock); |
944 | } |
945 | |
946 | return canread; |
947 | } |
948 | |
949 | static void |
950 | filt_ptcwdetach(struct knote *kn) |
951 | { |
952 | struct pt_softc *pti; |
953 | |
954 | pti = kn->kn_hook; |
955 | |
956 | mutex_spin_enter(&tty_lock); |
957 | SLIST_REMOVE(&pti->pt_selw.sel_klist, kn, knote, kn_selnext); |
958 | mutex_spin_exit(&tty_lock); |
959 | } |
960 | |
961 | static int |
962 | filt_ptcwrite(struct knote *kn, long hint) |
963 | { |
964 | struct pt_softc *pti; |
965 | struct tty *tp; |
966 | int canwrite; |
967 | int nwrite; |
968 | |
969 | pti = kn->kn_hook; |
970 | tp = pti->pt_tty; |
971 | |
972 | if ((hint & NOTE_SUBMIT) == 0) { |
973 | mutex_spin_enter(&tty_lock); |
974 | } |
975 | |
976 | canwrite = (ISSET(tp->t_state, TS_ISOPEN) && |
977 | ((pti->pt_flags & PF_REMOTE) ? |
978 | (tp->t_canq.c_cc == 0) : |
979 | ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2) || |
980 | (tp->t_canq.c_cc == 0 && ISSET(tp->t_lflag, ICANON))))); |
981 | |
982 | if (canwrite) { |
983 | if (pti->pt_flags & PF_REMOTE) |
984 | nwrite = tp->t_canq.c_cn; |
985 | else { |
986 | /* this is guaranteed to be > 0 due to above check */ |
987 | nwrite = tp->t_canq.c_cn |
988 | - (tp->t_rawq.c_cc + tp->t_canq.c_cc); |
989 | } |
990 | kn->kn_data = nwrite; |
991 | } |
992 | |
993 | if ((hint & NOTE_SUBMIT) == 0) { |
994 | mutex_spin_exit(&tty_lock); |
995 | } |
996 | |
997 | return canwrite; |
998 | } |
999 | |
1000 | static const struct filterops ptcread_filtops = |
1001 | { 1, NULL, filt_ptcrdetach, filt_ptcread }; |
1002 | static const struct filterops ptcwrite_filtops = |
1003 | { 1, NULL, filt_ptcwdetach, filt_ptcwrite }; |
1004 | |
1005 | int |
1006 | ptckqfilter(dev_t dev, struct knote *kn) |
1007 | { |
1008 | struct pt_softc *pti = pt_softc[minor(dev)]; |
1009 | struct klist *klist; |
1010 | |
1011 | switch (kn->kn_filter) { |
1012 | case EVFILT_READ: |
1013 | klist = &pti->pt_selr.sel_klist; |
1014 | kn->kn_fop = &ptcread_filtops; |
1015 | break; |
1016 | case EVFILT_WRITE: |
1017 | klist = &pti->pt_selw.sel_klist; |
1018 | kn->kn_fop = &ptcwrite_filtops; |
1019 | break; |
1020 | default: |
1021 | return EINVAL; |
1022 | } |
1023 | |
1024 | kn->kn_hook = pti; |
1025 | |
1026 | mutex_spin_enter(&tty_lock); |
1027 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
1028 | mutex_spin_exit(&tty_lock); |
1029 | |
1030 | return 0; |
1031 | } |
1032 | |
1033 | struct tty * |
1034 | ptytty(dev_t dev) |
1035 | { |
1036 | struct pt_softc *pti = pt_softc[minor(dev)]; |
1037 | struct tty *tp = pti->pt_tty; |
1038 | |
1039 | return tp; |
1040 | } |
1041 | |
1042 | /*ARGSUSED*/ |
1043 | int |
1044 | ptyioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
1045 | { |
1046 | struct pt_softc *pti = pt_softc[minor(dev)]; |
1047 | struct tty *tp = pti->pt_tty; |
1048 | const struct cdevsw *cdev; |
1049 | u_char *cc = tp->t_cc; |
1050 | int stop, error, sig; |
1051 | #ifndef NO_DEV_PTM |
1052 | struct mount *mp; |
1053 | #endif |
1054 | |
1055 | /* |
1056 | * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. |
1057 | * ttywflush(tp) will hang if there are characters in the outq. |
1058 | */ |
1059 | if (cmd == TIOCEXT) { |
1060 | /* |
1061 | * When the EXTPROC bit is being toggled, we need |
1062 | * to send an TIOCPKT_IOCTL if the packet driver |
1063 | * is turned on. |
1064 | */ |
1065 | if (*(int *)data) { |
1066 | if (pti->pt_flags & PF_PKT) { |
1067 | pti->pt_send |= TIOCPKT_IOCTL; |
1068 | ptcwakeup(tp, FREAD); |
1069 | } |
1070 | SET(tp->t_lflag, EXTPROC); |
1071 | } else { |
1072 | if (ISSET(tp->t_lflag, EXTPROC) && |
1073 | (pti->pt_flags & PF_PKT)) { |
1074 | pti->pt_send |= TIOCPKT_IOCTL; |
1075 | ptcwakeup(tp, FREAD); |
1076 | } |
1077 | CLR(tp->t_lflag, EXTPROC); |
1078 | } |
1079 | return(0); |
1080 | } |
1081 | |
1082 | #ifndef NO_DEV_PTM |
1083 | /* Allow getting the name from either the master or the slave */ |
1084 | if (cmd == TIOCPTSNAME) { |
1085 | if ((error = pty_getmp(l, &mp)) != 0) |
1086 | return error; |
1087 | return pty_fill_ptmget(l, dev, -1, -1, data, mp); |
1088 | } |
1089 | #endif |
1090 | |
1091 | cdev = cdevsw_lookup(dev); |
1092 | if (cdev != NULL && cdev->d_open == ptcopen) |
1093 | switch (cmd) { |
1094 | #ifndef NO_DEV_PTM |
1095 | case TIOCGRANTPT: |
1096 | if ((error = pty_getmp(l, &mp)) != 0) |
1097 | return error; |
1098 | return pty_grant_slave(l, dev, mp); |
1099 | #endif |
1100 | |
1101 | case TIOCGPGRP: |
1102 | /* |
1103 | * We avoid calling ttioctl on the controller since, |
1104 | * in that case, tp must be the controlling terminal. |
1105 | */ |
1106 | *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; |
1107 | return 0; |
1108 | |
1109 | case TIOCPKT: |
1110 | if (*(int *)data) { |
1111 | if (pti->pt_flags & PF_UCNTL) |
1112 | return EINVAL; |
1113 | pti->pt_flags |= PF_PKT; |
1114 | } else |
1115 | pti->pt_flags &= ~PF_PKT; |
1116 | return 0; |
1117 | |
1118 | case TIOCUCNTL: |
1119 | if (*(int *)data) { |
1120 | if (pti->pt_flags & PF_PKT) |
1121 | return EINVAL; |
1122 | pti->pt_flags |= PF_UCNTL; |
1123 | } else |
1124 | pti->pt_flags &= ~PF_UCNTL; |
1125 | return 0; |
1126 | |
1127 | case TIOCREMOTE: |
1128 | if (*(int *)data) |
1129 | pti->pt_flags |= PF_REMOTE; |
1130 | else |
1131 | pti->pt_flags &= ~PF_REMOTE; |
1132 | mutex_spin_enter(&tty_lock); |
1133 | ttyflush(tp, FREAD|FWRITE); |
1134 | mutex_spin_exit(&tty_lock); |
1135 | return 0; |
1136 | |
1137 | case TIOCSETP: |
1138 | case TIOCSETN: |
1139 | case TIOCSETD: |
1140 | case TIOCSETA: |
1141 | case TIOCSETAW: |
1142 | case TIOCSETAF: |
1143 | mutex_spin_enter(&tty_lock); |
1144 | ndflush(&tp->t_outq, tp->t_outq.c_cc); |
1145 | mutex_spin_exit(&tty_lock); |
1146 | break; |
1147 | |
1148 | case TIOCSIG: |
1149 | sig = (int)(long)*(void **)data; |
1150 | if (sig <= 0 || sig >= NSIG) |
1151 | return EINVAL; |
1152 | mutex_spin_enter(&tty_lock); |
1153 | if (!ISSET(tp->t_lflag, NOFLSH)) |
1154 | ttyflush(tp, FREAD|FWRITE); |
1155 | tp->t_state |= TS_SIGINFO; |
1156 | ttysig(tp, TTYSIG_PG1, sig); |
1157 | mutex_spin_exit(&tty_lock); |
1158 | return 0; |
1159 | |
1160 | case FIONREAD: |
1161 | mutex_spin_enter(&tty_lock); |
1162 | *(int *)data = tp->t_outq.c_cc; |
1163 | mutex_spin_exit(&tty_lock); |
1164 | return 0; |
1165 | } |
1166 | |
1167 | error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); |
1168 | if (error == EPASSTHROUGH) |
1169 | error = ttioctl(tp, cmd, data, flag, l); |
1170 | if (error == EPASSTHROUGH) { |
1171 | if (pti->pt_flags & PF_UCNTL && |
1172 | (cmd & ~0xff) == UIOCCMD(0)) { |
1173 | if (cmd & 0xff) { |
1174 | pti->pt_ucntl = (u_char)cmd; |
1175 | ptcwakeup(tp, FREAD); |
1176 | } |
1177 | return 0; |
1178 | } |
1179 | } |
1180 | /* |
1181 | * If external processing and packet mode send ioctl packet. |
1182 | */ |
1183 | if (ISSET(tp->t_lflag, EXTPROC) && (pti->pt_flags & PF_PKT)) { |
1184 | switch(cmd) { |
1185 | case TIOCSETA: |
1186 | case TIOCSETAW: |
1187 | case TIOCSETAF: |
1188 | case TIOCSETP: |
1189 | case TIOCSETN: |
1190 | case TIOCSETC: |
1191 | case TIOCSLTC: |
1192 | case TIOCLBIS: |
1193 | case TIOCLBIC: |
1194 | case TIOCLSET: |
1195 | pti->pt_send |= TIOCPKT_IOCTL; |
1196 | ptcwakeup(tp, FREAD); |
1197 | default: |
1198 | break; |
1199 | } |
1200 | } |
1201 | stop = ISSET(tp->t_iflag, IXON) && CCEQ(cc[VSTOP], CTRL('s')) |
1202 | && CCEQ(cc[VSTART], CTRL('q')); |
1203 | if (pti->pt_flags & PF_NOSTOP) { |
1204 | if (stop) { |
1205 | pti->pt_send &= ~TIOCPKT_NOSTOP; |
1206 | pti->pt_send |= TIOCPKT_DOSTOP; |
1207 | pti->pt_flags &= ~PF_NOSTOP; |
1208 | ptcwakeup(tp, FREAD); |
1209 | } |
1210 | } else { |
1211 | if (!stop) { |
1212 | pti->pt_send &= ~TIOCPKT_DOSTOP; |
1213 | pti->pt_send |= TIOCPKT_NOSTOP; |
1214 | pti->pt_flags |= PF_NOSTOP; |
1215 | ptcwakeup(tp, FREAD); |
1216 | } |
1217 | } |
1218 | return error; |
1219 | } |
1220 | |