Line data Source code
1 : /* $OpenBSD: tty_pty.c,v 1.90 2018/08/30 06:16:30 anton Exp $ */
2 : /* $NetBSD: tty_pty.c,v 1.33.4.1 1996/06/02 09:08:11 mrg Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1982, 1986, 1989, 1993
6 : * The Regents of the University of California. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : * 3. Neither the name of the University nor the names of its contributors
17 : * may be used to endorse or promote products derived from this software
18 : * without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 : * SUCH DAMAGE.
31 : *
32 : * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95
33 : */
34 :
35 : /*
36 : * Pseudo-teletype Driver
37 : * (Actually two drivers, requiring two entries in 'cdevsw')
38 : */
39 :
40 : #include <sys/param.h>
41 : #include <sys/systm.h>
42 : #include <sys/namei.h>
43 : #include <sys/mount.h>
44 : #include <sys/ioctl.h>
45 : #include <sys/proc.h>
46 : #include <sys/tty.h>
47 : #include <sys/fcntl.h>
48 : #include <sys/file.h>
49 : #include <sys/filedesc.h>
50 : #include <sys/uio.h>
51 : #include <sys/kernel.h>
52 : #include <sys/malloc.h>
53 : #include <sys/vnode.h>
54 : #include <sys/signalvar.h>
55 : #include <sys/conf.h>
56 : #include <sys/stat.h>
57 : #include <sys/sysctl.h>
58 : #include <sys/poll.h>
59 : #include <sys/pledge.h>
60 : #include <sys/rwlock.h>
61 :
62 : #define BUFSIZ 100 /* Chunk size iomoved to/from user */
63 :
64 : /*
65 : * pts == /dev/tty[p-zP-T][0-9a-zA-Z]
66 : * ptc == /dev/pty[p-zP-T][0-9a-zA-Z]
67 : */
68 :
69 : /* XXX this needs to come from somewhere sane, and work with MAKEDEV */
70 : #define TTY_LETTERS "pqrstuvwxyzPQRST"
71 : #define TTY_SUFFIX "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
72 :
73 : static int pts_major;
74 :
75 : struct pt_softc {
76 : struct tty *pt_tty;
77 : int pt_flags;
78 : struct selinfo pt_selr, pt_selw;
79 : u_char pt_send;
80 : u_char pt_ucntl;
81 : char pty_pn[11];
82 : char pty_sn[11];
83 : };
84 :
85 : #define NPTY_MIN 8 /* number of initial ptys */
86 : #define NPTY_MAX 992 /* maximum number of ptys supported */
87 :
88 : static struct pt_softc **pt_softc = NULL; /* pty array */
89 : static int npty = 0; /* size of pty array */
90 : static int maxptys = NPTY_MAX; /* maximum number of ptys */
91 : /* for pty array */
92 : struct rwlock pt_softc_lock = RWLOCK_INITIALIZER("ptarrlk");
93 :
94 : #define PF_PKT 0x08 /* packet mode */
95 : #define PF_STOPPED 0x10 /* user told stopped */
96 : #define PF_REMOTE 0x20 /* remote and flow controlled input */
97 : #define PF_NOSTOP 0x40
98 : #define PF_UCNTL 0x80 /* user control mode */
99 :
100 : void ptyattach(int);
101 : void ptcwakeup(struct tty *, int);
102 : struct tty *ptytty(dev_t);
103 : void ptsstart(struct tty *);
104 : int sysctl_pty(int *, u_int, void *, size_t *, void *, size_t);
105 :
106 : void filt_ptcrdetach(struct knote *);
107 : int filt_ptcread(struct knote *, long);
108 : void filt_ptcwdetach(struct knote *);
109 : int filt_ptcwrite(struct knote *, long);
110 :
111 : static struct pt_softc **ptyarralloc(int);
112 : static int check_pty(int);
113 :
114 : static gid_t tty_gid = TTY_GID;
115 :
116 : void ptydevname(int, struct pt_softc *);
117 : dev_t pty_getfree(void);
118 :
119 : void ptmattach(int);
120 : int ptmopen(dev_t, int, int, struct proc *);
121 : int ptmclose(dev_t, int, int, struct proc *);
122 : int ptmioctl(dev_t, u_long, caddr_t, int, struct proc *p);
123 : static int ptm_vn_open(struct nameidata *);
124 :
125 : void
126 0 : ptydevname(int minor, struct pt_softc *pti)
127 : {
128 0 : char buf[11] = "/dev/XtyXX";
129 : int i, j;
130 :
131 0 : i = minor / (sizeof(TTY_SUFFIX) - 1);
132 0 : j = minor % (sizeof(TTY_SUFFIX) - 1);
133 0 : if (i >= sizeof(TTY_LETTERS) - 1) {
134 0 : pti->pty_pn[0] = '\0';
135 0 : pti->pty_sn[0] = '\0';
136 0 : return;
137 : }
138 : buf[5] = 'p';
139 0 : buf[8] = TTY_LETTERS[i];
140 0 : buf[9] = TTY_SUFFIX[j];
141 0 : memcpy(pti->pty_pn, buf, sizeof(buf));
142 : buf[5] = 't';
143 0 : memcpy(pti->pty_sn, buf, sizeof(buf));
144 0 : }
145 :
146 : /*
147 : * Allocate and zero array of nelem elements.
148 : */
149 : struct pt_softc **
150 0 : ptyarralloc(int nelem)
151 : {
152 : struct pt_softc **pt;
153 :
154 0 : pt = mallocarray(nelem, sizeof(struct pt_softc *), M_DEVBUF,
155 : M_WAITOK|M_ZERO);
156 0 : return pt;
157 : }
158 :
159 : /*
160 : * Check if the minor is correct and ensure necessary structures
161 : * are properly allocated.
162 : */
163 : int
164 0 : check_pty(int dev)
165 : {
166 : struct pt_softc *pti;
167 0 : int minor = minor(dev);
168 :
169 0 : rw_enter_write(&pt_softc_lock);
170 0 : if (minor >= npty) {
171 : struct pt_softc **newpt;
172 : int newnpty;
173 :
174 : /* check if the requested pty can be granted */
175 0 : if (minor >= maxptys)
176 0 : goto limit_reached;
177 :
178 : /* grow pty array by powers of two, up to maxptys */
179 0 : for (newnpty = npty; newnpty <= minor; newnpty *= 2)
180 : ;
181 :
182 0 : if (newnpty > maxptys)
183 0 : newnpty = maxptys;
184 0 : newpt = ptyarralloc(newnpty);
185 :
186 0 : memcpy(newpt, pt_softc, npty * sizeof(struct pt_softc *));
187 0 : free(pt_softc, M_DEVBUF, npty * sizeof(struct pt_softc *));
188 0 : pt_softc = newpt;
189 0 : npty = newnpty;
190 0 : }
191 :
192 : /*
193 : * If the entry is not yet allocated, allocate one.
194 : */
195 0 : if (!pt_softc[minor]) {
196 0 : pti = malloc(sizeof(struct pt_softc), M_DEVBUF,
197 : M_WAITOK|M_ZERO);
198 0 : pti->pt_tty = ttymalloc(1000000);
199 0 : pti->pt_tty->t_dev = dev;
200 0 : ptydevname(minor, pti);
201 0 : pt_softc[minor] = pti;
202 0 : }
203 0 : rw_exit_write(&pt_softc_lock);
204 0 : return (0);
205 : limit_reached:
206 0 : rw_exit_write(&pt_softc_lock);
207 0 : tablefull("pty");
208 0 : return (ENXIO);
209 0 : }
210 :
211 : /*
212 : * Establish n (or default if n is 1) ptys in the system.
213 : */
214 : void
215 0 : ptyattach(int n)
216 : {
217 : /* maybe should allow 0 => none? */
218 0 : if (n <= 1)
219 : n = NPTY_MIN;
220 0 : pt_softc = ptyarralloc(n);
221 0 : npty = n;
222 :
223 : /*
224 : * If we have pty, we need ptm too.
225 : */
226 0 : ptmattach(1);
227 0 : }
228 :
229 : int
230 0 : ptsopen(dev_t dev, int flag, int devtype, struct proc *p)
231 : {
232 : struct pt_softc *pti;
233 : struct tty *tp;
234 : int error;
235 :
236 0 : if ((error = check_pty(dev)))
237 0 : return (error);
238 :
239 0 : pti = pt_softc[minor(dev)];
240 0 : tp = pti->pt_tty;
241 0 : if ((tp->t_state & TS_ISOPEN) == 0) {
242 0 : tp->t_state |= TS_WOPEN;
243 0 : ttychars(tp); /* Set up default chars */
244 0 : tp->t_iflag = TTYDEF_IFLAG;
245 0 : tp->t_oflag = TTYDEF_OFLAG;
246 0 : tp->t_lflag = TTYDEF_LFLAG;
247 0 : tp->t_cflag = TTYDEF_CFLAG;
248 0 : tp->t_ispeed = tp->t_ospeed = B115200;
249 0 : ttsetwater(tp); /* would be done in xxparam() */
250 0 : } else if (tp->t_state & TS_XCLUDE && suser(p) != 0)
251 0 : return (EBUSY);
252 0 : if (tp->t_oproc) /* Ctrlr still around. */
253 0 : tp->t_state |= TS_CARR_ON;
254 0 : while ((tp->t_state & TS_CARR_ON) == 0) {
255 0 : tp->t_state |= TS_WOPEN;
256 0 : if (flag & FNONBLOCK)
257 : break;
258 0 : error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
259 : ttopen, 0);
260 0 : if (error)
261 0 : return (error);
262 : }
263 0 : error = (*linesw[tp->t_line].l_open)(dev, tp, p);
264 0 : ptcwakeup(tp, FREAD|FWRITE);
265 0 : return (error);
266 0 : }
267 :
268 : int
269 0 : ptsclose(dev_t dev, int flag, int mode, struct proc *p)
270 : {
271 0 : struct pt_softc *pti = pt_softc[minor(dev)];
272 0 : struct tty *tp = pti->pt_tty;
273 : int error;
274 :
275 0 : error = (*linesw[tp->t_line].l_close)(tp, flag, p);
276 0 : error |= ttyclose(tp);
277 0 : ptcwakeup(tp, FREAD|FWRITE);
278 0 : return (error);
279 : }
280 :
281 : int
282 0 : ptsread(dev_t dev, struct uio *uio, int flag)
283 : {
284 0 : struct proc *p = curproc;
285 0 : struct process *pr = p->p_p;
286 0 : struct pt_softc *pti = pt_softc[minor(dev)];
287 0 : struct tty *tp = pti->pt_tty;
288 0 : int error = 0;
289 :
290 : again:
291 0 : if (pti->pt_flags & PF_REMOTE) {
292 0 : while (isbackground(pr, tp)) {
293 0 : if ((pr->ps_sigacts->ps_sigignore & sigmask(SIGTTIN)) ||
294 0 : (p->p_sigmask & sigmask(SIGTTIN)) ||
295 0 : pr->ps_pgrp->pg_jobc == 0 ||
296 0 : pr->ps_flags & PS_PPWAIT)
297 0 : return (EIO);
298 0 : pgsignal(pr->ps_pgrp, SIGTTIN, 1);
299 0 : error = ttysleep(tp, &lbolt,
300 : TTIPRI | PCATCH, ttybg, 0);
301 0 : if (error)
302 0 : return (error);
303 : }
304 0 : if (tp->t_canq.c_cc == 0) {
305 0 : if (flag & IO_NDELAY)
306 0 : return (EWOULDBLOCK);
307 0 : error = ttysleep(tp, &tp->t_canq,
308 : TTIPRI | PCATCH, ttyin, 0);
309 0 : if (error)
310 0 : return (error);
311 0 : goto again;
312 : }
313 0 : while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
314 0 : if (ureadc(getc(&tp->t_canq), uio) < 0) {
315 : error = EFAULT;
316 0 : break;
317 : }
318 0 : if (tp->t_canq.c_cc == 1)
319 0 : (void) getc(&tp->t_canq);
320 0 : if (tp->t_canq.c_cc)
321 0 : return (error);
322 : } else
323 0 : if (tp->t_oproc)
324 0 : error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
325 0 : ptcwakeup(tp, FWRITE);
326 0 : return (error);
327 0 : }
328 :
329 : /*
330 : * Write to pseudo-tty.
331 : * Wakeups of controlling tty will happen
332 : * indirectly, when tty driver calls ptsstart.
333 : */
334 : int
335 0 : ptswrite(dev_t dev, struct uio *uio, int flag)
336 : {
337 0 : struct pt_softc *pti = pt_softc[minor(dev)];
338 0 : struct tty *tp = pti->pt_tty;
339 :
340 0 : if (tp->t_oproc == 0)
341 0 : return (EIO);
342 0 : return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
343 0 : }
344 :
345 : /*
346 : * Start output on pseudo-tty.
347 : * Wake up process polling or sleeping for input from controlling tty.
348 : */
349 : void
350 0 : ptsstart(struct tty *tp)
351 : {
352 0 : struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
353 :
354 0 : if (tp->t_state & TS_TTSTOP)
355 0 : return;
356 0 : if (pti->pt_flags & PF_STOPPED) {
357 0 : pti->pt_flags &= ~PF_STOPPED;
358 0 : pti->pt_send = TIOCPKT_START;
359 0 : }
360 0 : ptcwakeup(tp, FREAD);
361 0 : }
362 :
363 : int
364 0 : ptsstop(struct tty *tp, int flush)
365 : {
366 0 : struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
367 : int flag;
368 :
369 : /* note: FLUSHREAD and FLUSHWRITE already ok */
370 0 : if (flush == 0) {
371 : flush = TIOCPKT_STOP;
372 0 : pti->pt_flags |= PF_STOPPED;
373 0 : } else
374 0 : pti->pt_flags &= ~PF_STOPPED;
375 0 : pti->pt_send |= flush;
376 : /* change of perspective */
377 : flag = 0;
378 0 : if (flush & FREAD)
379 0 : flag |= FWRITE;
380 0 : if (flush & FWRITE)
381 0 : flag |= FREAD;
382 0 : ptcwakeup(tp, flag);
383 0 : return 0;
384 : }
385 :
386 : void
387 0 : ptcwakeup(struct tty *tp, int flag)
388 : {
389 0 : struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
390 :
391 0 : if (flag & FREAD) {
392 0 : selwakeup(&pti->pt_selr);
393 0 : wakeup(&tp->t_outq.c_cf);
394 0 : }
395 0 : if (flag & FWRITE) {
396 0 : selwakeup(&pti->pt_selw);
397 0 : wakeup(&tp->t_rawq.c_cf);
398 0 : }
399 0 : }
400 :
401 : int ptcopen(dev_t, int, int, struct proc *);
402 :
403 : int
404 0 : ptcopen(dev_t dev, int flag, int devtype, struct proc *p)
405 : {
406 : struct pt_softc *pti;
407 : struct tty *tp;
408 : int error;
409 :
410 0 : if ((error = check_pty(dev)))
411 0 : return (error);
412 :
413 0 : pti = pt_softc[minor(dev)];
414 0 : tp = pti->pt_tty;
415 0 : if (tp->t_oproc)
416 0 : return (EIO);
417 0 : tp->t_oproc = ptsstart;
418 0 : (void)(*linesw[tp->t_line].l_modem)(tp, 1);
419 0 : tp->t_lflag &= ~EXTPROC;
420 0 : pti->pt_flags = 0;
421 0 : pti->pt_send = 0;
422 0 : pti->pt_ucntl = 0;
423 0 : return (0);
424 0 : }
425 :
426 : int
427 0 : ptcclose(dev_t dev, int flag, int devtype, struct proc *p)
428 : {
429 0 : struct pt_softc *pti = pt_softc[minor(dev)];
430 0 : struct tty *tp = pti->pt_tty;
431 :
432 0 : (void)(*linesw[tp->t_line].l_modem)(tp, 0);
433 0 : tp->t_state &= ~TS_CARR_ON;
434 0 : tp->t_oproc = 0; /* mark closed */
435 0 : return (0);
436 : }
437 :
438 : int
439 0 : ptcread(dev_t dev, struct uio *uio, int flag)
440 : {
441 0 : struct pt_softc *pti = pt_softc[minor(dev)];
442 0 : struct tty *tp = pti->pt_tty;
443 0 : char buf[BUFSIZ];
444 : int error = 0, cc, bufcc = 0;
445 :
446 : /*
447 : * We want to block until the slave
448 : * is open, and there's something to read;
449 : * but if we lost the slave or we're NBIO,
450 : * then return the appropriate error instead.
451 : */
452 0 : for (;;) {
453 0 : if (tp->t_state & TS_ISOPEN) {
454 0 : if (pti->pt_flags & PF_PKT && pti->pt_send) {
455 0 : error = ureadc((int)pti->pt_send, uio);
456 0 : if (error)
457 0 : return (error);
458 0 : if (pti->pt_send & TIOCPKT_IOCTL) {
459 0 : cc = MIN(uio->uio_resid,
460 : sizeof(tp->t_termios));
461 0 : error = uiomove(&tp->t_termios, cc, uio);
462 0 : if (error)
463 0 : return (error);
464 : }
465 0 : pti->pt_send = 0;
466 0 : return (0);
467 : }
468 0 : if (pti->pt_flags & PF_UCNTL && pti->pt_ucntl) {
469 0 : error = ureadc((int)pti->pt_ucntl, uio);
470 0 : if (error)
471 0 : return (error);
472 0 : pti->pt_ucntl = 0;
473 0 : return (0);
474 : }
475 0 : if (tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0)
476 : break;
477 : }
478 0 : if ((tp->t_state & TS_CARR_ON) == 0)
479 0 : return (0); /* EOF */
480 0 : if (flag & IO_NDELAY)
481 0 : return (EWOULDBLOCK);
482 0 : error = tsleep(&tp->t_outq.c_cf, TTIPRI | PCATCH,
483 : ttyin, 0);
484 0 : if (error)
485 0 : return (error);
486 : }
487 0 : if (pti->pt_flags & (PF_PKT|PF_UCNTL))
488 0 : error = ureadc(0, uio);
489 0 : while (uio->uio_resid > 0 && error == 0) {
490 0 : cc = MIN(uio->uio_resid, BUFSIZ);
491 0 : cc = q_to_b(&tp->t_outq, buf, cc);
492 0 : if (cc > bufcc)
493 0 : bufcc = cc;
494 0 : if (cc <= 0)
495 : break;
496 0 : error = uiomove(buf, cc, uio);
497 : }
498 0 : ttwakeupwr(tp);
499 0 : if (bufcc)
500 0 : explicit_bzero(buf, bufcc);
501 0 : return (error);
502 0 : }
503 :
504 :
505 : int
506 0 : ptcwrite(dev_t dev, struct uio *uio, int flag)
507 : {
508 0 : struct pt_softc *pti = pt_softc[minor(dev)];
509 0 : struct tty *tp = pti->pt_tty;
510 : u_char *cp = NULL;
511 : int cc = 0, bufcc = 0;
512 0 : u_char buf[BUFSIZ];
513 : size_t cnt = 0;
514 0 : int error = 0;
515 :
516 : again:
517 0 : if ((tp->t_state & TS_ISOPEN) == 0)
518 : goto block;
519 0 : if (pti->pt_flags & PF_REMOTE) {
520 0 : if (tp->t_canq.c_cc)
521 : goto block;
522 0 : while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG(tp) - 1) {
523 0 : if (cc == 0) {
524 0 : cc = MIN(uio->uio_resid, BUFSIZ);
525 0 : cc = min(cc, TTYHOG(tp) - 1 - tp->t_canq.c_cc);
526 0 : if (cc > bufcc)
527 0 : bufcc = cc;
528 0 : cp = buf;
529 0 : error = uiomove(cp, cc, uio);
530 0 : if (error)
531 : goto done;
532 : /* check again for safety */
533 0 : if ((tp->t_state & TS_ISOPEN) == 0) {
534 : error = EIO;
535 0 : goto done;
536 : }
537 : }
538 0 : if (cc)
539 0 : (void) b_to_q((char *)cp, cc, &tp->t_canq);
540 : cc = 0;
541 : }
542 0 : (void) putc(0, &tp->t_canq);
543 0 : ttwakeup(tp);
544 0 : wakeup(&tp->t_canq);
545 0 : goto done;
546 : }
547 0 : do {
548 0 : if (cc == 0) {
549 0 : cc = MIN(uio->uio_resid, BUFSIZ);
550 0 : if (cc > bufcc)
551 0 : bufcc = cc;
552 0 : cp = buf;
553 0 : error = uiomove(cp, cc, uio);
554 0 : if (error)
555 : goto done;
556 : /* check again for safety */
557 0 : if ((tp->t_state & TS_ISOPEN) == 0) {
558 : error = EIO;
559 0 : goto done;
560 : }
561 : }
562 : bufcc = cc;
563 0 : while (cc > 0) {
564 0 : if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG(tp) - 2 &&
565 0 : (tp->t_canq.c_cc > 0 || !ISSET(tp->t_lflag, ICANON))) {
566 0 : wakeup(&tp->t_rawq);
567 0 : goto block;
568 : }
569 0 : (*linesw[tp->t_line].l_rint)(*cp++, tp);
570 0 : cnt++;
571 0 : cc--;
572 : }
573 : cc = 0;
574 0 : } while (uio->uio_resid > 0);
575 : goto done;
576 : block:
577 : /*
578 : * Come here to wait for slave to open, for space
579 : * in outq, or space in rawq.
580 : */
581 0 : if ((tp->t_state & TS_CARR_ON) == 0) {
582 : error = EIO;
583 0 : goto done;
584 : }
585 0 : if (flag & IO_NDELAY) {
586 : /* adjust for data copied in but not written */
587 0 : uio->uio_resid += cc;
588 0 : if (cnt == 0)
589 0 : error = EWOULDBLOCK;
590 : goto done;
591 : }
592 0 : error = tsleep(&tp->t_rawq.c_cf, TTOPRI | PCATCH,
593 : ttyout, 0);
594 0 : if (error == 0)
595 0 : goto again;
596 :
597 : /* adjust for data copied in but not written */
598 0 : uio->uio_resid += cc;
599 : done:
600 0 : if (bufcc)
601 0 : explicit_bzero(buf, bufcc);
602 0 : return (error);
603 0 : }
604 :
605 : int
606 0 : ptcpoll(dev_t dev, int events, struct proc *p)
607 : {
608 0 : struct pt_softc *pti = pt_softc[minor(dev)];
609 0 : struct tty *tp = pti->pt_tty;
610 : int revents = 0, s;
611 :
612 0 : if (!ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_CARR_ON))
613 : goto notopen;
614 :
615 0 : if (events & (POLLIN | POLLRDNORM)) {
616 : /*
617 : * Need to protect access to t_outq
618 : */
619 0 : s = spltty();
620 0 : if ((tp->t_outq.c_cc && !ISSET(tp->t_state, TS_TTSTOP)) ||
621 0 : ((pti->pt_flags & PF_PKT) && pti->pt_send) ||
622 0 : ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
623 0 : revents |= events & (POLLIN | POLLRDNORM);
624 0 : splx(s);
625 0 : }
626 : /* NOTE: POLLHUP and POLLOUT/POLLWRNORM are mutually exclusive */
627 0 : if (!ISSET(tp->t_state, TS_CARR_ON)) {
628 0 : revents |= POLLHUP;
629 0 : } else if (events & (POLLOUT | POLLWRNORM)) {
630 0 : if ((pti->pt_flags & PF_REMOTE) ?
631 0 : (tp->t_canq.c_cc == 0) :
632 0 : ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG(tp) - 2) ||
633 0 : (tp->t_canq.c_cc == 0 && ISSET(tp->t_lflag, ICANON))))
634 0 : revents |= events & (POLLOUT | POLLWRNORM);
635 : }
636 0 : if (events & (POLLPRI | POLLRDBAND)) {
637 : /* If in packet or user control mode, check for data. */
638 0 : if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
639 0 : ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
640 0 : revents |= events & (POLLPRI | POLLRDBAND);
641 : }
642 :
643 0 : if (revents == 0) {
644 : notopen:
645 0 : if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND))
646 0 : selrecord(p, &pti->pt_selr);
647 0 : if (events & (POLLOUT | POLLWRNORM))
648 0 : selrecord(p, &pti->pt_selw);
649 : }
650 :
651 0 : return (revents);
652 : }
653 :
654 : void
655 0 : filt_ptcrdetach(struct knote *kn)
656 : {
657 0 : struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
658 : int s;
659 :
660 0 : s = spltty();
661 0 : SLIST_REMOVE(&pti->pt_selr.si_note, kn, knote, kn_selnext);
662 0 : splx(s);
663 0 : }
664 :
665 : int
666 0 : filt_ptcread(struct knote *kn, long hint)
667 : {
668 0 : struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
669 : struct tty *tp;
670 :
671 0 : tp = pti->pt_tty;
672 0 : kn->kn_data = 0;
673 :
674 0 : if (ISSET(tp->t_state, TS_ISOPEN)) {
675 0 : if (!ISSET(tp->t_state, TS_TTSTOP))
676 0 : kn->kn_data = tp->t_outq.c_cc;
677 0 : if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
678 0 : ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
679 0 : kn->kn_data++;
680 : }
681 0 : return (kn->kn_data > 0);
682 : }
683 :
684 : void
685 0 : filt_ptcwdetach(struct knote *kn)
686 : {
687 0 : struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
688 : int s;
689 :
690 0 : s = spltty();
691 0 : SLIST_REMOVE(&pti->pt_selw.si_note, kn, knote, kn_selnext);
692 0 : splx(s);
693 0 : }
694 :
695 : int
696 0 : filt_ptcwrite(struct knote *kn, long hint)
697 : {
698 0 : struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
699 : struct tty *tp;
700 :
701 0 : tp = pti->pt_tty;
702 0 : kn->kn_data = 0;
703 :
704 0 : if (ISSET(tp->t_state, TS_ISOPEN)) {
705 0 : if (ISSET(pti->pt_flags, PF_REMOTE)) {
706 0 : if (tp->t_canq.c_cc == 0)
707 0 : kn->kn_data = tp->t_canq.c_cn;
708 0 : } else if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG(tp)-2)
709 0 : kn->kn_data = tp->t_canq.c_cn -
710 : (tp->t_rawq.c_cc + tp->t_canq.c_cc);
711 : }
712 :
713 0 : return (kn->kn_data > 0);
714 : }
715 :
716 : struct filterops ptcread_filtops =
717 : { 1, NULL, filt_ptcrdetach, filt_ptcread };
718 : struct filterops ptcwrite_filtops =
719 : { 1, NULL, filt_ptcwdetach, filt_ptcwrite };
720 :
721 : int
722 0 : ptckqfilter(dev_t dev, struct knote *kn)
723 : {
724 0 : struct pt_softc *pti = pt_softc[minor(dev)];
725 : struct klist *klist;
726 : int s;
727 :
728 0 : switch (kn->kn_filter) {
729 : case EVFILT_READ:
730 0 : klist = &pti->pt_selr.si_note;
731 0 : kn->kn_fop = &ptcread_filtops;
732 0 : break;
733 : case EVFILT_WRITE:
734 0 : klist = &pti->pt_selw.si_note;
735 0 : kn->kn_fop = &ptcwrite_filtops;
736 0 : break;
737 : default:
738 0 : return (EINVAL);
739 : }
740 :
741 0 : kn->kn_hook = (caddr_t)pti;
742 :
743 0 : s = spltty();
744 0 : SLIST_INSERT_HEAD(klist, kn, kn_selnext);
745 0 : splx(s);
746 :
747 0 : return (0);
748 0 : }
749 :
750 : struct tty *
751 0 : ptytty(dev_t dev)
752 : {
753 0 : struct pt_softc *pti = pt_softc[minor(dev)];
754 0 : struct tty *tp = pti->pt_tty;
755 :
756 0 : return (tp);
757 : }
758 :
759 : int
760 0 : ptyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
761 : {
762 0 : struct pt_softc *pti = pt_softc[minor(dev)];
763 0 : struct tty *tp = pti->pt_tty;
764 0 : u_char *cc = tp->t_cc;
765 : int stop, error;
766 :
767 : /*
768 : * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
769 : * ttywflush(tp) will hang if there are characters in the outq.
770 : */
771 0 : if (cmd == TIOCEXT) {
772 : /*
773 : * When the EXTPROC bit is being toggled, we need
774 : * to send an TIOCPKT_IOCTL if the packet driver
775 : * is turned on.
776 : */
777 0 : if (*(int *)data) {
778 0 : if (pti->pt_flags & PF_PKT) {
779 0 : pti->pt_send |= TIOCPKT_IOCTL;
780 0 : ptcwakeup(tp, FREAD);
781 0 : }
782 0 : tp->t_lflag |= EXTPROC;
783 0 : } else {
784 0 : if ((tp->t_lflag & EXTPROC) &&
785 0 : (pti->pt_flags & PF_PKT)) {
786 0 : pti->pt_send |= TIOCPKT_IOCTL;
787 0 : ptcwakeup(tp, FREAD);
788 0 : }
789 0 : tp->t_lflag &= ~EXTPROC;
790 : }
791 0 : return(0);
792 0 : } else if (cdevsw[major(dev)].d_open == ptcopen)
793 0 : switch (cmd) {
794 :
795 : case TIOCGPGRP:
796 : /*
797 : * We avoid calling ttioctl on the controller since,
798 : * in that case, tp must be the controlling terminal.
799 : */
800 0 : *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
801 0 : return (0);
802 :
803 : case TIOCPKT:
804 0 : if (*(int *)data) {
805 0 : if (pti->pt_flags & PF_UCNTL)
806 0 : return (EINVAL);
807 0 : pti->pt_flags |= PF_PKT;
808 0 : } else
809 0 : pti->pt_flags &= ~PF_PKT;
810 0 : return (0);
811 :
812 : case TIOCUCNTL:
813 0 : if (*(int *)data) {
814 0 : if (pti->pt_flags & PF_PKT)
815 0 : return (EINVAL);
816 0 : pti->pt_flags |= PF_UCNTL;
817 0 : } else
818 0 : pti->pt_flags &= ~PF_UCNTL;
819 0 : return (0);
820 :
821 : case TIOCREMOTE:
822 0 : if (*(int *)data)
823 0 : pti->pt_flags |= PF_REMOTE;
824 : else
825 0 : pti->pt_flags &= ~PF_REMOTE;
826 0 : ttyflush(tp, FREAD|FWRITE);
827 0 : return (0);
828 :
829 : case TIOCSETD:
830 : case TIOCSETA:
831 : case TIOCSETAW:
832 : case TIOCSETAF:
833 0 : ndflush(&tp->t_outq, tp->t_outq.c_cc);
834 0 : break;
835 :
836 : case TIOCSIG:
837 0 : if (*(unsigned int *)data >= NSIG ||
838 0 : *(unsigned int *)data == 0)
839 0 : return(EINVAL);
840 0 : if ((tp->t_lflag & NOFLSH) == 0)
841 0 : ttyflush(tp, FREAD|FWRITE);
842 0 : pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
843 0 : if ((*(unsigned int *)data == SIGINFO) &&
844 0 : ((tp->t_lflag & NOKERNINFO) == 0))
845 0 : ttyinfo(tp);
846 0 : return (0);
847 :
848 : case FIONREAD:
849 : /*
850 : * FIONREAD on the master side must return the amount
851 : * in the output queue rather than the input.
852 : */
853 0 : *(int *)data = tp->t_outq.c_cc;
854 0 : return (0);
855 : }
856 0 : error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
857 0 : if (error < 0)
858 0 : error = ttioctl(tp, cmd, data, flag, p);
859 0 : if (error < 0) {
860 : /*
861 : * Translate TIOCSBRK/TIOCCBRK to user mode ioctls to
862 : * let the master interpret BREAK conditions.
863 : */
864 0 : switch (cmd) {
865 : case TIOCSBRK:
866 : cmd = UIOCCMD(TIOCUCNTL_SBRK);
867 0 : break;
868 : case TIOCCBRK:
869 : cmd = UIOCCMD(TIOCUCNTL_CBRK);
870 0 : break;
871 : default:
872 : break;
873 : }
874 0 : if (pti->pt_flags & PF_UCNTL &&
875 0 : (cmd & ~0xff) == UIOCCMD(0)) {
876 0 : if (cmd & 0xff) {
877 0 : pti->pt_ucntl = (u_char)cmd;
878 0 : ptcwakeup(tp, FREAD);
879 0 : }
880 0 : return (0);
881 : }
882 : error = ENOTTY;
883 0 : }
884 : /*
885 : * If external processing and packet mode send ioctl packet.
886 : */
887 0 : if ((tp->t_lflag & EXTPROC) && (pti->pt_flags & PF_PKT)) {
888 0 : switch (cmd) {
889 : case TIOCSETA:
890 : case TIOCSETAW:
891 : case TIOCSETAF:
892 0 : pti->pt_send |= TIOCPKT_IOCTL;
893 0 : ptcwakeup(tp, FREAD);
894 : default:
895 : break;
896 : }
897 : }
898 0 : stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) &&
899 0 : CCEQ(cc[VSTART], CTRL('q'));
900 0 : if (pti->pt_flags & PF_NOSTOP) {
901 0 : if (stop) {
902 0 : pti->pt_send &= ~TIOCPKT_NOSTOP;
903 0 : pti->pt_send |= TIOCPKT_DOSTOP;
904 0 : pti->pt_flags &= ~PF_NOSTOP;
905 0 : ptcwakeup(tp, FREAD);
906 0 : }
907 : } else {
908 0 : if (!stop) {
909 0 : pti->pt_send &= ~TIOCPKT_DOSTOP;
910 0 : pti->pt_send |= TIOCPKT_NOSTOP;
911 0 : pti->pt_flags |= PF_NOSTOP;
912 0 : ptcwakeup(tp, FREAD);
913 0 : }
914 : }
915 0 : return (error);
916 0 : }
917 :
918 : /*
919 : * Return pty-related information.
920 : */
921 : int
922 0 : sysctl_pty(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
923 : size_t newlen)
924 : {
925 0 : if (namelen != 1)
926 0 : return (ENOTDIR);
927 :
928 : switch (name[0]) {
929 : default:
930 0 : return (EOPNOTSUPP);
931 : }
932 : /* NOTREACHED */
933 0 : }
934 :
935 : /*
936 : * Check if a pty is free to use.
937 : */
938 : static int
939 0 : pty_isfree_locked(int minor)
940 : {
941 0 : struct pt_softc *pt = pt_softc[minor];
942 :
943 0 : return (pt == NULL || pt->pt_tty == NULL ||
944 0 : pt->pt_tty->t_oproc == NULL);
945 : }
946 :
947 : static int
948 0 : pty_isfree(int minor)
949 : {
950 : int isfree;
951 :
952 0 : rw_enter_read(&pt_softc_lock);
953 0 : isfree = pty_isfree_locked(minor);
954 0 : rw_exit_read(&pt_softc_lock);
955 0 : return(isfree);
956 : }
957 :
958 : dev_t
959 0 : pty_getfree(void)
960 : {
961 : int i;
962 :
963 0 : rw_enter_read(&pt_softc_lock);
964 0 : for (i = 0; i < npty; i++) {
965 0 : if (pty_isfree_locked(i))
966 : break;
967 : }
968 0 : rw_exit_read(&pt_softc_lock);
969 0 : return (makedev(pts_major, i));
970 : }
971 :
972 : /*
973 : * Hacked up version of vn_open. We _only_ handle ptys and only open
974 : * them with FREAD|FWRITE and never deal with creat or stuff like that.
975 : *
976 : * We need it because we have to fake up root credentials to open the pty.
977 : */
978 : static int
979 0 : ptm_vn_open(struct nameidata *ndp)
980 : {
981 0 : struct proc *p = ndp->ni_cnd.cn_proc;
982 : struct ucred *cred;
983 0 : struct vattr vattr;
984 : struct vnode *vp;
985 : int error;
986 :
987 0 : if ((error = namei(ndp)) != 0)
988 0 : return (error);
989 0 : vp = ndp->ni_vp;
990 0 : if (vp->v_type != VCHR) {
991 : error = EINVAL;
992 0 : goto bad;
993 : }
994 :
995 : /*
996 : * Get us a fresh cred with root privileges.
997 : */
998 0 : cred = crget();
999 0 : error = VOP_OPEN(vp, FREAD|FWRITE, cred, p);
1000 0 : if (!error) {
1001 : /* update atime/mtime */
1002 0 : VATTR_NULL(&vattr);
1003 0 : getnanotime(&vattr.va_atime);
1004 0 : vattr.va_mtime = vattr.va_atime;
1005 0 : vattr.va_vaflags |= VA_UTIMES_NULL;
1006 0 : (void)VOP_SETATTR(vp, &vattr, p->p_ucred, p);
1007 0 : }
1008 0 : crfree(cred);
1009 :
1010 0 : if (error)
1011 : goto bad;
1012 :
1013 0 : vp->v_writecount++;
1014 :
1015 0 : return (0);
1016 : bad:
1017 0 : vput(vp);
1018 0 : return (error);
1019 0 : }
1020 :
1021 : void
1022 0 : ptmattach(int n)
1023 : {
1024 : /* find the major and minor of the pty devices */
1025 : int i;
1026 :
1027 0 : for (i = 0; i < nchrdev; i++)
1028 0 : if (cdevsw[i].d_open == ptsopen)
1029 : break;
1030 :
1031 0 : if (i == nchrdev)
1032 0 : panic("ptmattach: Can't find pty slave in cdevsw");
1033 :
1034 0 : pts_major = i;
1035 0 : }
1036 :
1037 : int
1038 0 : ptmopen(dev_t dev, int flag, int mode, struct proc *p)
1039 : {
1040 0 : return(0);
1041 : }
1042 :
1043 :
1044 : int
1045 0 : ptmclose(dev_t dev, int flag, int mode, struct proc *p)
1046 : {
1047 0 : return (0);
1048 : }
1049 :
1050 : int
1051 0 : ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
1052 : {
1053 : dev_t newdev, error;
1054 : struct pt_softc * pti;
1055 0 : struct nameidata cnd, snd;
1056 0 : struct filedesc *fdp = p->p_fd;
1057 0 : struct file *cfp = NULL, *sfp = NULL;
1058 0 : int cindx, sindx;
1059 : uid_t uid;
1060 : gid_t gid;
1061 0 : struct vattr vattr;
1062 : struct ucred *cred;
1063 0 : struct ptmget *ptm = (struct ptmget *)data;
1064 :
1065 0 : switch (cmd) {
1066 : case PTMGET:
1067 0 : fdplock(fdp);
1068 : /* Grab two filedescriptors. */
1069 0 : if ((error = falloc(p, &cfp, &cindx)) != 0) {
1070 0 : fdpunlock(fdp);
1071 0 : break;
1072 : }
1073 0 : if ((error = falloc(p, &sfp, &sindx)) != 0) {
1074 0 : fdremove(fdp, cindx);
1075 0 : closef(cfp, p);
1076 0 : fdpunlock(fdp);
1077 0 : break;
1078 : }
1079 :
1080 : retry:
1081 : /* Find and open a free master pty. */
1082 0 : newdev = pty_getfree();
1083 0 : if ((error = check_pty(newdev)))
1084 : goto bad;
1085 0 : pti = pt_softc[minor(newdev)];
1086 0 : NDINIT(&cnd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
1087 : pti->pty_pn, p);
1088 0 : cnd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
1089 0 : if ((error = ptm_vn_open(&cnd)) != 0) {
1090 : /*
1091 : * Check if the master open failed because we lost
1092 : * the race to grab it.
1093 : */
1094 0 : if (error == EIO && !pty_isfree(minor(newdev)))
1095 0 : goto retry;
1096 : goto bad;
1097 : }
1098 0 : cfp->f_flag = FREAD|FWRITE;
1099 0 : cfp->f_type = DTYPE_VNODE;
1100 0 : cfp->f_ops = &vnops;
1101 0 : cfp->f_data = (caddr_t) cnd.ni_vp;
1102 0 : VOP_UNLOCK(cnd.ni_vp);
1103 :
1104 : /*
1105 : * Open the slave.
1106 : * namei -> setattr -> unlock -> revoke -> vrele ->
1107 : * namei -> open -> unlock
1108 : * Three stage rocket:
1109 : * 1. Change the owner and permissions on the slave.
1110 : * 2. Revoke all the users of the slave.
1111 : * 3. open the slave.
1112 : */
1113 0 : NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
1114 : pti->pty_sn, p);
1115 0 : snd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
1116 0 : snd.ni_unveil = UNVEIL_READ | UNVEIL_WRITE;
1117 0 : if ((error = namei(&snd)) != 0)
1118 : goto bad;
1119 0 : if ((snd.ni_vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
1120 0 : gid = tty_gid;
1121 : /* get real uid */
1122 0 : uid = p->p_ucred->cr_ruid;
1123 :
1124 0 : VATTR_NULL(&vattr);
1125 0 : vattr.va_uid = uid;
1126 0 : vattr.va_gid = gid;
1127 0 : vattr.va_mode = (S_IRUSR|S_IWUSR|S_IWGRP) & ALLPERMS;
1128 : /* Get a fake cred to pretend we're root. */
1129 0 : cred = crget();
1130 0 : error = VOP_SETATTR(snd.ni_vp, &vattr, cred, p);
1131 0 : crfree(cred);
1132 0 : if (error) {
1133 0 : vput(snd.ni_vp);
1134 0 : goto bad;
1135 : }
1136 : }
1137 0 : VOP_UNLOCK(snd.ni_vp);
1138 0 : if (snd.ni_vp->v_usecount > 1 ||
1139 0 : (snd.ni_vp->v_flag & (VALIASED)))
1140 0 : VOP_REVOKE(snd.ni_vp, REVOKEALL);
1141 :
1142 : /*
1143 : * The vnode is useless after the revoke, we need to
1144 : * namei again.
1145 : */
1146 0 : vrele(snd.ni_vp);
1147 :
1148 0 : NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
1149 : pti->pty_sn, p);
1150 0 : snd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
1151 0 : snd.ni_unveil= UNVEIL_READ | UNVEIL_WRITE;
1152 : /* now open it */
1153 0 : if ((error = ptm_vn_open(&snd)) != 0)
1154 : goto bad;
1155 0 : sfp->f_flag = FREAD|FWRITE;
1156 0 : sfp->f_type = DTYPE_VNODE;
1157 0 : sfp->f_ops = &vnops;
1158 0 : sfp->f_data = (caddr_t) snd.ni_vp;
1159 0 : VOP_UNLOCK(snd.ni_vp);
1160 :
1161 : /* now, put the indexen and names into struct ptmget */
1162 0 : ptm->cfd = cindx;
1163 0 : ptm->sfd = sindx;
1164 0 : memcpy(ptm->cn, pti->pty_pn, sizeof(pti->pty_pn));
1165 0 : memcpy(ptm->sn, pti->pty_sn, sizeof(pti->pty_sn));
1166 :
1167 : /* insert files now that we've passed all errors */
1168 0 : fdinsert(fdp, cindx, 0, cfp);
1169 0 : fdinsert(fdp, sindx, 0, sfp);
1170 0 : fdpunlock(fdp);
1171 0 : FRELE(cfp, p);
1172 0 : FRELE(sfp, p);
1173 : break;
1174 : default:
1175 : error = EINVAL;
1176 0 : break;
1177 : }
1178 0 : return (error);
1179 : bad:
1180 0 : fdremove(fdp, cindx);
1181 0 : closef(cfp, p);
1182 0 : fdremove(fdp, sindx);
1183 0 : closef(sfp, p);
1184 0 : fdpunlock(fdp);
1185 0 : return (error);
1186 0 : }
|