print more junk
[freebsd-src/fkvm-freebsd.git] / sys / kern / tty_pts.c
blob03e78b8a0e3def7b23ae7808dab87267e0ba043e
1 /*-
2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
5 * Portions of this software were developed under sponsorship from Snow
6 * B.V., the Netherlands.
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.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include "opt_tty.h"
35 /* Add compatibility bits for FreeBSD. */
36 #define PTS_COMPAT
37 #ifdef DEV_PTY
38 /* Add /dev/ptyXX compat bits. */
39 #define PTS_EXTERNAL
40 #endif /* DEV_PTY */
41 /* Add bits to make Linux binaries work. */
42 #define PTS_LINUX
44 #include <sys/param.h>
45 #include <sys/lock.h>
46 #include <sys/condvar.h>
47 #include <sys/conf.h>
48 #include <sys/fcntl.h>
49 #include <sys/file.h>
50 #include <sys/filedesc.h>
51 #include <sys/filio.h>
52 #include <sys/kernel.h>
53 #include <sys/malloc.h>
54 #include <sys/poll.h>
55 #include <sys/proc.h>
56 #include <sys/resourcevar.h>
57 #include <sys/serial.h>
58 #include <sys/stat.h>
59 #include <sys/syscall.h>
60 #include <sys/syscallsubr.h>
61 #include <sys/sysent.h>
62 #include <sys/sysproto.h>
63 #include <sys/systm.h>
64 #include <sys/tty.h>
65 #include <sys/ttycom.h>
67 #include <machine/stdarg.h>
69 static struct unrhdr *pts_pool;
70 #define MAXPTSDEVS 999
72 static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device");
75 * Per-PTS structure.
77 * List of locks
78 * (t) locked by tty_lock()
79 * (c) const until freeing
81 struct pts_softc {
82 int pts_unit; /* (c) Device unit number. */
83 unsigned int pts_flags; /* (t) Device flags. */
84 #define PTS_PKT 0x1 /* Packet mode. */
85 char pts_pkt; /* (t) Unread packet mode data. */
87 struct cv pts_inwait; /* (t) Blocking write() on master. */
88 struct selinfo pts_inpoll; /* (t) Select queue for write(). */
89 struct cv pts_outwait; /* (t) Blocking read() on master. */
90 struct selinfo pts_outpoll; /* (t) Select queue for read(). */
92 #ifdef PTS_EXTERNAL
93 struct cdev *pts_cdev; /* (c) Master device node. */
94 #endif /* PTS_EXTERNAL */
96 struct uidinfo *pts_uidinfo; /* (c) Resource limit. */
100 * Controller-side file operations.
103 static int
104 ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
105 int flags, struct thread *td)
107 struct tty *tp = fp->f_data;
108 struct pts_softc *psc = tty_softc(tp);
109 int error = 0;
110 char pkt;
112 if (uio->uio_resid == 0)
113 return (0);
115 tty_lock(tp);
117 for (;;) {
119 * Implement packet mode. When packet mode is turned on,
120 * the first byte contains a bitmask of events that
121 * occured (start, stop, flush, window size, etc).
123 if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
124 pkt = psc->pts_pkt;
125 psc->pts_pkt = 0;
126 tty_unlock(tp);
128 error = ureadc(pkt, uio);
129 return (error);
133 * Transmit regular data.
135 * XXX: We shouldn't use ttydisc_getc_poll()! Even
136 * though in this implementation, there is likely going
137 * to be data, we should just call ttydisc_getc_uio()
138 * and use its return value to sleep.
140 if (ttydisc_getc_poll(tp)) {
141 if (psc->pts_flags & PTS_PKT) {
143 * XXX: Small race. Fortunately PTY
144 * consumers aren't multithreaded.
147 tty_unlock(tp);
148 error = ureadc(TIOCPKT_DATA, uio);
149 if (error)
150 return (error);
151 tty_lock(tp);
154 error = ttydisc_getc_uio(tp, uio);
155 break;
158 /* Maybe the device isn't used anyway. */
159 if (tty_opened(tp) == 0)
160 break;
162 /* Wait for more data. */
163 if (fp->f_flag & O_NONBLOCK) {
164 error = EWOULDBLOCK;
165 break;
167 error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
168 if (error != 0)
169 break;
172 tty_unlock(tp);
174 return (error);
177 static int
178 ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
179 int flags, struct thread *td)
181 struct tty *tp = fp->f_data;
182 struct pts_softc *psc = tty_softc(tp);
183 char ib[256], *ibstart;
184 size_t iblen, rintlen;
185 int error = 0;
187 if (uio->uio_resid == 0)
188 return (0);
190 for (;;) {
191 ibstart = ib;
192 iblen = MIN(uio->uio_resid, sizeof ib);
193 error = uiomove(ib, iblen, uio);
195 tty_lock(tp);
196 if (error != 0)
197 goto done;
200 * When possible, avoid the slow path. rint_bypass()
201 * copies all input to the input queue at once.
203 MPASS(iblen > 0);
204 do {
205 if (ttydisc_can_bypass(tp)) {
206 /* Store data at once. */
207 rintlen = ttydisc_rint_bypass(tp,
208 ibstart, iblen);
209 ibstart += rintlen;
210 iblen -= rintlen;
212 if (iblen == 0) {
213 /* All data written. */
214 break;
216 } else {
217 error = ttydisc_rint(tp, *ibstart, 0);
218 if (error == 0) {
219 /* Character stored successfully. */
220 ibstart++;
221 iblen--;
222 continue;
226 /* Maybe the device isn't used anyway. */
227 if (tty_opened(tp) == 0) {
228 error = EIO;
229 goto done;
232 /* Wait for more data. */
233 if (fp->f_flag & O_NONBLOCK) {
234 error = EWOULDBLOCK;
235 goto done;
238 /* Wake up users on the slave side. */
239 ttydisc_rint_done(tp);
240 error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
241 if (error != 0)
242 goto done;
243 } while (iblen > 0);
245 if (uio->uio_resid == 0)
246 break;
247 tty_unlock(tp);
250 done: ttydisc_rint_done(tp);
251 tty_unlock(tp);
252 return (error);
255 static int
256 ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
257 struct ucred *active_cred, struct thread *td)
259 struct tty *tp = fp->f_data;
260 struct pts_softc *psc = tty_softc(tp);
261 int error = 0, sig;
263 switch (cmd) {
264 case FIONBIO:
265 /* This device supports non-blocking operation. */
266 return (0);
267 case FIODGNAME: {
268 struct fiodgname_arg *fgn;
269 const char *p;
270 int i;
272 /* Reverse device name lookups, for ptsname() and ttyname(). */
273 fgn = data;
274 #ifdef PTS_EXTERNAL
275 if (psc->pts_cdev != NULL)
276 p = devtoname(psc->pts_cdev);
277 else
278 #endif /* PTS_EXTERNAL */
279 p = tty_devname(tp);
280 i = strlen(p) + 1;
281 if (i > fgn->len)
282 return (EINVAL);
283 return copyout(p, fgn->buf, i);
287 * We need to implement TIOCGPGRP and TIOCGSID here again. When
288 * called on the pseudo-terminal master, it should not check if
289 * the terminal is the foreground terminal of the calling
290 * process.
292 * TIOCGETA is also implemented here. Various Linux PTY routines
293 * often call isatty(), which is implemented by tcgetattr().
295 #ifdef PTS_LINUX
296 case TIOCGETA:
297 /* Obtain terminal flags through tcgetattr(). */
298 tty_lock(tp);
299 bcopy(&tp->t_termios, data, sizeof(struct termios));
300 tty_unlock(tp);
301 return (0);
302 #endif /* PTS_LINUX */
303 case TIOCSETAF:
304 case TIOCSETAW:
306 * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
307 * TCSADRAIN into something different. If an application would
308 * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
309 * deadlock waiting for all data to be read.
311 cmd = TIOCSETA;
312 break;
313 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
314 case TIOCGPTN:
316 * Get the device unit number.
318 if (psc->pts_unit < 0)
319 return (ENOTTY);
320 *(unsigned int *)data = psc->pts_unit;
321 return (0);
322 #endif /* PTS_COMPAT || PTS_LINUX */
323 case TIOCGPGRP:
324 /* Get the foreground process group ID. */
325 tty_lock(tp);
326 if (tp->t_pgrp != NULL)
327 *(int *)data = tp->t_pgrp->pg_id;
328 else
329 *(int *)data = NO_PID;
330 tty_unlock(tp);
331 return (0);
332 case TIOCGSID:
333 /* Get the session leader process ID. */
334 tty_lock(tp);
335 if (tp->t_session == NULL)
336 error = ENOTTY;
337 else
338 *(int *)data = tp->t_session->s_sid;
339 tty_unlock(tp);
340 return (error);
341 case TIOCPTMASTER:
342 /* Yes, we are a pseudo-terminal master. */
343 return (0);
344 case TIOCSIG:
345 /* Signal the foreground process group. */
346 sig = *(int *)data;
347 if (sig < 1 || sig >= NSIG)
348 return (EINVAL);
350 tty_lock(tp);
351 tty_signal_pgrp(tp, sig);
352 tty_unlock(tp);
353 return (0);
354 case TIOCPKT:
355 /* Enable/disable packet mode. */
356 tty_lock(tp);
357 if (*(int *)data)
358 psc->pts_flags |= PTS_PKT;
359 else
360 psc->pts_flags &= ~PTS_PKT;
361 tty_unlock(tp);
362 return (0);
365 /* Just redirect this ioctl to the slave device. */
366 tty_lock(tp);
367 error = tty_ioctl(tp, cmd, data, td);
368 tty_unlock(tp);
370 return (error);
373 static int
374 ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
375 struct thread *td)
377 struct tty *tp = fp->f_data;
378 struct pts_softc *psc = tty_softc(tp);
379 int revents = 0;
381 tty_lock(tp);
383 if (tty_opened(tp) == 0) {
384 /* Slave device is not opened. */
385 tty_unlock(tp);
386 return (events &
387 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
390 if (events & (POLLIN|POLLRDNORM)) {
391 /* See if we can getc something. */
392 if (ttydisc_getc_poll(tp) ||
393 (psc->pts_flags & PTS_PKT && psc->pts_pkt))
394 revents |= events & (POLLIN|POLLRDNORM);
396 if (events & (POLLOUT|POLLWRNORM)) {
397 /* See if we can rint something. */
398 if (ttydisc_rint_poll(tp))
399 revents |= events & (POLLOUT|POLLWRNORM);
403 * No need to check for POLLHUP here. This device cannot be used
404 * as a callout device, which means we always have a carrier,
405 * because the master is.
408 if (revents == 0) {
410 * This code might look misleading, but the naming of
411 * poll events on this side is the opposite of the slave
412 * device.
414 if (events & (POLLIN|POLLRDNORM))
415 selrecord(td, &psc->pts_outpoll);
416 if (events & (POLLOUT|POLLWRNORM))
417 selrecord(td, &psc->pts_inpoll);
420 tty_unlock(tp);
422 return (revents);
425 static int
426 ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
427 struct thread *td)
429 struct tty *tp = fp->f_data;
430 #ifdef PTS_EXTERNAL
431 struct pts_softc *psc = tty_softc(tp);
432 #endif /* PTS_EXTERNAL */
435 * According to POSIX, we must implement an fstat(). This also
436 * makes this implementation compatible with Linux binaries,
437 * because Linux calls fstat() on the pseudo-terminal master to
438 * obtain st_rdev.
440 * XXX: POSIX also mentions we must fill in st_dev, st_atime,
441 * st_ctime and st_mtime, but how?
444 bzero(sb, sizeof *sb);
445 #ifdef PTS_EXTERNAL
446 if (psc->pts_cdev != NULL)
447 sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
448 else
449 #endif /* PTS_EXTERNAL */
450 sb->st_ino = sb->st_rdev = tty_udev(tp);
451 sb->st_mode = S_IFCHR;
452 sb->st_uid = tp->t_dev->si_cred->cr_ruid;
453 sb->st_gid = GID_TTY;
455 return (0);
458 static int
459 ptsdev_close(struct file *fp, struct thread *td)
461 struct tty *tp = fp->f_data;
463 /* Deallocate TTY device. */
464 tty_lock(tp);
465 tty_rel_gone(tp);
467 return (0);
470 static struct fileops ptsdev_ops = {
471 .fo_read = ptsdev_read,
472 .fo_write = ptsdev_write,
473 .fo_ioctl = ptsdev_ioctl,
474 .fo_poll = ptsdev_poll,
475 .fo_stat = ptsdev_stat,
476 .fo_close = ptsdev_close,
477 .fo_flags = DFLAG_PASSABLE,
481 * Driver-side hooks.
484 static void
485 ptsdrv_outwakeup(struct tty *tp)
487 struct pts_softc *psc = tty_softc(tp);
489 cv_broadcast(&psc->pts_outwait);
490 selwakeup(&psc->pts_outpoll);
493 static void
494 ptsdrv_inwakeup(struct tty *tp)
496 struct pts_softc *psc = tty_softc(tp);
498 cv_broadcast(&psc->pts_inwait);
499 selwakeup(&psc->pts_inpoll);
502 static void
503 ptsdrv_close(struct tty *tp)
506 /* Wake up any blocked readers/writers. */
507 ptsdrv_outwakeup(tp);
508 ptsdrv_inwakeup(tp);
511 static void
512 ptsdrv_pktnotify(struct tty *tp, char event)
514 struct pts_softc *psc = tty_softc(tp);
517 * Clear conflicting flags.
520 switch (event) {
521 case TIOCPKT_STOP:
522 psc->pts_pkt &= ~TIOCPKT_START;
523 break;
524 case TIOCPKT_START:
525 psc->pts_pkt &= ~TIOCPKT_STOP;
526 break;
527 case TIOCPKT_NOSTOP:
528 psc->pts_pkt &= ~TIOCPKT_DOSTOP;
529 break;
530 case TIOCPKT_DOSTOP:
531 psc->pts_pkt &= ~TIOCPKT_NOSTOP;
532 break;
535 psc->pts_pkt |= event;
536 ptsdrv_outwakeup(tp);
539 static void
540 ptsdrv_free(void *softc)
542 struct pts_softc *psc = softc;
544 /* Make device number available again. */
545 if (psc->pts_unit >= 0)
546 free_unr(pts_pool, psc->pts_unit);
548 chgptscnt(psc->pts_uidinfo, -1, 0);
549 uifree(psc->pts_uidinfo);
551 #ifdef PTS_EXTERNAL
552 /* Destroy master device as well. */
553 if (psc->pts_cdev != NULL)
554 destroy_dev_sched(psc->pts_cdev);
555 #endif /* PTS_EXTERNAL */
557 free(psc, M_PTS);
560 static struct ttydevsw pts_class = {
561 .tsw_flags = TF_NOPREFIX,
562 .tsw_outwakeup = ptsdrv_outwakeup,
563 .tsw_inwakeup = ptsdrv_inwakeup,
564 .tsw_close = ptsdrv_close,
565 .tsw_pktnotify = ptsdrv_pktnotify,
566 .tsw_free = ptsdrv_free,
569 static int
570 pts_alloc(int fflags, struct thread *td, struct file *fp)
572 int unit, ok;
573 struct tty *tp;
574 struct pts_softc *psc;
575 struct proc *p = td->td_proc;
576 struct uidinfo *uid = td->td_ucred->cr_ruidinfo;
578 /* Resource limiting. */
579 PROC_LOCK(p);
580 ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS));
581 PROC_UNLOCK(p);
582 if (!ok)
583 return (EAGAIN);
585 /* Try to allocate a new pts unit number. */
586 unit = alloc_unr(pts_pool);
587 if (unit < 0) {
588 chgptscnt(uid, -1, 0);
589 return (EAGAIN);
592 /* Allocate TTY and softc. */
593 psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
594 cv_init(&psc->pts_inwait, "pts inwait");
595 cv_init(&psc->pts_outwait, "pts outwait");
597 psc->pts_unit = unit;
598 psc->pts_uidinfo = uid;
599 uihold(uid);
601 tp = tty_alloc(&pts_class, psc, NULL);
603 /* Expose the slave device as well. */
604 tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
606 finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
608 return (0);
611 #ifdef PTS_EXTERNAL
613 pts_alloc_external(int fflags, struct thread *td, struct file *fp,
614 struct cdev *dev, const char *name)
616 int ok;
617 struct tty *tp;
618 struct pts_softc *psc;
619 struct proc *p = td->td_proc;
620 struct uidinfo *uid = td->td_ucred->cr_ruidinfo;
622 /* Resource limiting. */
623 PROC_LOCK(p);
624 ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS));
625 PROC_UNLOCK(p);
626 if (!ok)
627 return (EAGAIN);
629 /* Allocate TTY and softc. */
630 psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
631 cv_init(&psc->pts_inwait, "pts inwait");
632 cv_init(&psc->pts_outwait, "pts outwait");
634 psc->pts_unit = -1;
635 psc->pts_cdev = dev;
636 psc->pts_uidinfo = uid;
637 uihold(uid);
639 tp = tty_alloc(&pts_class, psc, NULL);
641 /* Expose the slave device as well. */
642 tty_makedev(tp, td->td_ucred, "%s", name);
644 finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
646 return (0);
648 #endif /* PTS_EXTERNAL */
651 posix_openpt(struct thread *td, struct posix_openpt_args *uap)
653 int error, fd;
654 struct file *fp;
657 * POSIX states it's unspecified when other flags are passed. We
658 * don't allow this.
660 if (uap->flags & ~(O_RDWR|O_NOCTTY))
661 return (EINVAL);
663 error = falloc(td, &fp, &fd);
664 if (error)
665 return (error);
667 /* Allocate the actual pseudo-TTY. */
668 error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp);
669 if (error != 0) {
670 fdclose(td->td_proc->p_fd, fp, fd, td);
671 return (error);
674 /* Pass it back to userspace. */
675 td->td_retval[0] = fd;
676 fdrop(fp, td);
678 return (0);
681 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
682 static int
683 ptmx_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp)
685 int error;
687 error = pts_alloc(fflags & (FREAD|FWRITE), td, fp);
688 if (error != 0)
689 return (error);
691 return (0);
694 static struct cdevsw ptmx_cdevsw = {
695 .d_version = D_VERSION,
696 .d_fdopen = ptmx_fdopen,
697 .d_name = "ptmx",
699 #endif /* PTS_COMPAT || PTS_LINUX */
701 static void
702 pts_init(void *unused)
705 pts_pool = new_unrhdr(0, MAXPTSDEVS, NULL);
706 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
707 make_dev(&ptmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "ptmx");
708 #endif /* PTS_COMPAT || PTS_LINUX */
711 SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL);