FreeBSD kernel kern code
tty_pts.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Portions of this software were developed under sponsorship from Snow
6  * B.V., the Netherlands.
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  *
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.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$BSDSUniX$");
32 
33 /* Add compatibility bits for FreeBSD. */
34 #define PTS_COMPAT
35 /* Add pty(4) compat bits. */
36 #define PTS_EXTERNAL
37 /* Add bits to make Linux binaries work. */
38 #define PTS_LINUX
39 
40 #include <sys/param.h>
41 #include <sys/lock.h>
42 #include <sys/condvar.h>
43 #include <sys/conf.h>
44 #include <sys/fcntl.h>
45 #include <sys/file.h>
46 #include <sys/filedesc.h>
47 #include <sys/filio.h>
48 #include <sys/kernel.h>
49 #include <sys/limits.h>
50 #include <sys/malloc.h>
51 #include <sys/poll.h>
52 #include <sys/proc.h>
53 #include <sys/racct.h>
54 #include <sys/resourcevar.h>
55 #include <sys/serial.h>
56 #include <sys/stat.h>
57 #include <sys/syscall.h>
58 #include <sys/syscallsubr.h>
59 #include <sys/sysctl.h>
60 #include <sys/sysent.h>
61 #include <sys/sysproto.h>
62 #include <sys/systm.h>
63 #include <sys/tty.h>
64 #include <sys/ttycom.h>
65 
66 #include <machine/stdarg.h>
67 
68 /*
69  * Our utmp(5) format is limited to 8-byte TTY line names. This means
70  * we can at most allocate 1000 pseudo-terminals ("pts/999"). Allow
71  * users to increase this number, assuming they have manually increased
72  * UT_LINESIZE.
73  */
74 static struct unrhdr *pts_pool;
75 
76 static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device");
77 
78 /*
79  * Per-PTS structure.
80  *
81  * List of locks
82  * (t) locked by tty_lock()
83  * (c) const until freeing
84  */
85 struct pts_softc {
86  int pts_unit; /* (c) Device unit number. */
87  unsigned int pts_flags; /* (t) Device flags. */
88 #define PTS_PKT 0x1 /* Packet mode. */
89 #define PTS_FINISHED 0x2 /* Return errors on read()/write(). */
90  char pts_pkt; /* (t) Unread packet mode data. */
91 
92  struct cv pts_inwait; /* (t) Blocking write() on master. */
93  struct selinfo pts_inpoll; /* (t) Select queue for write(). */
94  struct cv pts_outwait; /* (t) Blocking read() on master. */
95  struct selinfo pts_outpoll; /* (t) Select queue for read(). */
96 
97 #ifdef PTS_EXTERNAL
98  struct cdev *pts_cdev; /* (c) Master device node. */
99 #endif /* PTS_EXTERNAL */
100 
101  struct ucred *pts_cred; /* (c) Resource limit. */
102 };
103 
104 /*
105  * Controller-side file operations.
106  */
107 
108 static int
109 ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
110  int flags, struct thread *td)
111 {
112  struct tty *tp = fp->f_data;
113  struct pts_softc *psc = tty_softc(tp);
114  int error = 0;
115  char pkt;
116 
117  if (uio->uio_resid == 0)
118  return (0);
119 
120  tty_lock(tp);
121 
122  for (;;) {
123  /*
124  * Implement packet mode. When packet mode is turned on,
125  * the first byte contains a bitmask of events that
126  * occured (start, stop, flush, window size, etc).
127  */
128  if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
129  pkt = psc->pts_pkt;
130  psc->pts_pkt = 0;
131  tty_unlock(tp);
132 
133  error = ureadc(pkt, uio);
134  return (error);
135  }
136 
137  /*
138  * Transmit regular data.
139  *
140  * XXX: We shouldn't use ttydisc_getc_poll()! Even
141  * though in this implementation, there is likely going
142  * to be data, we should just call ttydisc_getc_uio()
143  * and use its return value to sleep.
144  */
145  if (ttydisc_getc_poll(tp)) {
146  if (psc->pts_flags & PTS_PKT) {
147  /*
148  * XXX: Small race. Fortunately PTY
149  * consumers aren't multithreaded.
150  */
151 
152  tty_unlock(tp);
153  error = ureadc(TIOCPKT_DATA, uio);
154  if (error)
155  return (error);
156  tty_lock(tp);
157  }
158 
159  error = ttydisc_getc_uio(tp, uio);
160  break;
161  }
162 
163  /* Maybe the device isn't used anyway. */
164  if (psc->pts_flags & PTS_FINISHED)
165  break;
166 
167  /* Wait for more data. */
168  if (fp->f_flag & O_NONBLOCK) {
169  error = EWOULDBLOCK;
170  break;
171  }
172  error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
173  if (error != 0)
174  break;
175  }
176 
177  tty_unlock(tp);
178 
179  return (error);
180 }
181 
182 static int
183 ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
184  int flags, struct thread *td)
185 {
186  struct tty *tp = fp->f_data;
187  struct pts_softc *psc = tty_softc(tp);
188  char ib[256], *ibstart;
189  size_t iblen, rintlen;
190  int error = 0;
191 
192  if (uio->uio_resid == 0)
193  return (0);
194 
195  for (;;) {
196  ibstart = ib;
197  iblen = MIN(uio->uio_resid, sizeof ib);
198  error = uiomove(ib, iblen, uio);
199 
200  tty_lock(tp);
201  if (error != 0) {
202  iblen = 0;
203  goto done;
204  }
205 
206  /*
207  * When possible, avoid the slow path. rint_bypass()
208  * copies all input to the input queue at once.
209  */
210  MPASS(iblen > 0);
211  do {
212  rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
213  ibstart += rintlen;
214  iblen -= rintlen;
215  if (iblen == 0) {
216  /* All data written. */
217  break;
218  }
219 
220  /* Maybe the device isn't used anyway. */
221  if (psc->pts_flags & PTS_FINISHED) {
222  error = EIO;
223  goto done;
224  }
225 
226  /* Wait for more data. */
227  if (fp->f_flag & O_NONBLOCK) {
228  error = EWOULDBLOCK;
229  goto done;
230  }
231 
232  /* Wake up users on the slave side. */
233  ttydisc_rint_done(tp);
234  error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
235  if (error != 0)
236  goto done;
237  } while (iblen > 0);
238 
239  if (uio->uio_resid == 0)
240  break;
241  tty_unlock(tp);
242  }
243 
244 done: ttydisc_rint_done(tp);
245  tty_unlock(tp);
246 
247  /*
248  * Don't account for the part of the buffer that we couldn't
249  * pass to the TTY.
250  */
251  uio->uio_resid += iblen;
252  return (error);
253 }
254 
255 static int
256 ptsdev_truncate(struct file *fp, off_t length, struct ucred *active_cred,
257  struct thread *td)
258 {
259 
260  return (EINVAL);
261 }
262 
263 static int
264 ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
265  struct ucred *active_cred, struct thread *td)
266 {
267  struct tty *tp = fp->f_data;
268  struct pts_softc *psc = tty_softc(tp);
269  int error = 0, sig;
270 
271  switch (cmd) {
272  case FIONBIO:
273  /* This device supports non-blocking operation. */
274  return (0);
275  case FIONREAD:
276  tty_lock(tp);
277  if (psc->pts_flags & PTS_FINISHED) {
278  /* Force read() to be called. */
279  *(int *)data = 1;
280  } else {
281  *(int *)data = ttydisc_getc_poll(tp);
282  }
283  tty_unlock(tp);
284  return (0);
285  case FIODGNAME: {
286  struct fiodgname_arg *fgn;
287  const char *p;
288  int i;
289 
290  /* Reverse device name lookups, for ptsname() and ttyname(). */
291  fgn = data;
292  p = tty_devname(tp);
293  i = strlen(p) + 1;
294  if (i > fgn->len)
295  return (EINVAL);
296  return copyout(p, fgn->buf, i);
297  }
298 
299  /*
300  * We need to implement TIOCGPGRP and TIOCGSID here again. When
301  * called on the pseudo-terminal master, it should not check if
302  * the terminal is the foreground terminal of the calling
303  * process.
304  *
305  * TIOCGETA is also implemented here. Various Linux PTY routines
306  * often call isatty(), which is implemented by tcgetattr().
307  */
308 #ifdef PTS_LINUX
309  case TIOCGETA:
310  /* Obtain terminal flags through tcgetattr(). */
311  tty_lock(tp);
312  *(struct termios*)data = tp->t_termios;
313  tty_unlock(tp);
314  return (0);
315 #endif /* PTS_LINUX */
316  case TIOCSETAF:
317  case TIOCSETAW:
318  /*
319  * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
320  * TCSADRAIN into something different. If an application would
321  * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
322  * deadlock waiting for all data to be read.
323  */
324  cmd = TIOCSETA;
325  break;
326 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
327  case TIOCGPTN:
328  /*
329  * Get the device unit number.
330  */
331  if (psc->pts_unit < 0)
332  return (ENOTTY);
333  *(unsigned int *)data = psc->pts_unit;
334  return (0);
335 #endif /* PTS_COMPAT || PTS_LINUX */
336  case TIOCGPGRP:
337  /* Get the foreground process group ID. */
338  tty_lock(tp);
339  if (tp->t_pgrp != NULL)
340  *(int *)data = tp->t_pgrp->pg_id;
341  else
342  *(int *)data = NO_PID;
343  tty_unlock(tp);
344  return (0);
345  case TIOCGSID:
346  /* Get the session leader process ID. */
347  tty_lock(tp);
348  if (tp->t_session == NULL)
349  error = ENOTTY;
350  else
351  *(int *)data = tp->t_session->s_sid;
352  tty_unlock(tp);
353  return (error);
354  case TIOCPTMASTER:
355  /* Yes, we are a pseudo-terminal master. */
356  return (0);
357  case TIOCSIG:
358  /* Signal the foreground process group. */
359  sig = *(int *)data;
360  if (sig < 1 || sig >= NSIG)
361  return (EINVAL);
362 
363  tty_lock(tp);
364  tty_signal_pgrp(tp, sig);
365  tty_unlock(tp);
366  return (0);
367  case TIOCPKT:
368  /* Enable/disable packet mode. */
369  tty_lock(tp);
370  if (*(int *)data)
371  psc->pts_flags |= PTS_PKT;
372  else
373  psc->pts_flags &= ~PTS_PKT;
374  tty_unlock(tp);
375  return (0);
376  }
377 
378  /* Just redirect this ioctl to the slave device. */
379  tty_lock(tp);
380  error = tty_ioctl(tp, cmd, data, fp->f_flag, td);
381  tty_unlock(tp);
382  if (error == ENOIOCTL)
383  error = ENOTTY;
384 
385  return (error);
386 }
387 
388 static int
389 ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
390  struct thread *td)
391 {
392  struct tty *tp = fp->f_data;
393  struct pts_softc *psc = tty_softc(tp);
394  int revents = 0;
395 
396  tty_lock(tp);
397 
398  if (psc->pts_flags & PTS_FINISHED) {
399  /* Slave device is not opened. */
400  tty_unlock(tp);
401  return ((events & (POLLIN|POLLRDNORM)) | POLLHUP);
402  }
403 
404  if (events & (POLLIN|POLLRDNORM)) {
405  /* See if we can getc something. */
406  if (ttydisc_getc_poll(tp) ||
407  (psc->pts_flags & PTS_PKT && psc->pts_pkt))
408  revents |= events & (POLLIN|POLLRDNORM);
409  }
410  if (events & (POLLOUT|POLLWRNORM)) {
411  /* See if we can rint something. */
412  if (ttydisc_rint_poll(tp))
413  revents |= events & (POLLOUT|POLLWRNORM);
414  }
415 
416  /*
417  * No need to check for POLLHUP here. This device cannot be used
418  * as a callout device, which means we always have a carrier,
419  * because the master is.
420  */
421 
422  if (revents == 0) {
423  /*
424  * This code might look misleading, but the naming of
425  * poll events on this side is the opposite of the slave
426  * device.
427  */
428  if (events & (POLLIN|POLLRDNORM))
429  selrecord(td, &psc->pts_outpoll);
430  if (events & (POLLOUT|POLLWRNORM))
431  selrecord(td, &psc->pts_inpoll);
432  }
433 
434  tty_unlock(tp);
435 
436  return (revents);
437 }
438 
439 /*
440  * kqueue support.
441  */
442 
443 static void
445 {
446  struct file *fp = kn->kn_fp;
447  struct tty *tp = fp->f_data;
448  struct pts_softc *psc = tty_softc(tp);
449 
450  knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
451 }
452 
453 static int
454 pts_kqops_read_event(struct knote *kn, long hint)
455 {
456  struct file *fp = kn->kn_fp;
457  struct tty *tp = fp->f_data;
458  struct pts_softc *psc = tty_softc(tp);
459 
460  if (psc->pts_flags & PTS_FINISHED) {
461  kn->kn_flags |= EV_EOF;
462  return (1);
463  } else {
464  kn->kn_data = ttydisc_getc_poll(tp);
465  return (kn->kn_data > 0);
466  }
467 }
468 
469 static void
471 {
472  struct file *fp = kn->kn_fp;
473  struct tty *tp = fp->f_data;
474  struct pts_softc *psc = tty_softc(tp);
475 
476  knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
477 }
478 
479 static int
480 pts_kqops_write_event(struct knote *kn, long hint)
481 {
482  struct file *fp = kn->kn_fp;
483  struct tty *tp = fp->f_data;
484  struct pts_softc *psc = tty_softc(tp);
485 
486  if (psc->pts_flags & PTS_FINISHED) {
487  kn->kn_flags |= EV_EOF;
488  return (1);
489  } else {
490  kn->kn_data = ttydisc_rint_poll(tp);
491  return (kn->kn_data > 0);
492  }
493 }
494 
495 static struct filterops pts_kqops_read = {
496  .f_isfd = 1,
497  .f_detach = pts_kqops_read_detach,
498  .f_event = pts_kqops_read_event,
499 };
500 static struct filterops pts_kqops_write = {
501  .f_isfd = 1,
502  .f_detach = pts_kqops_write_detach,
503  .f_event = pts_kqops_write_event,
504 };
505 
506 static int
507 ptsdev_kqfilter(struct file *fp, struct knote *kn)
508 {
509  struct tty *tp = fp->f_data;
510  struct pts_softc *psc = tty_softc(tp);
511  int error = 0;
512 
513  tty_lock(tp);
514 
515  switch (kn->kn_filter) {
516  case EVFILT_READ:
517  kn->kn_fop = &pts_kqops_read;
518  knlist_add(&psc->pts_outpoll.si_note, kn, 1);
519  break;
520  case EVFILT_WRITE:
521  kn->kn_fop = &pts_kqops_write;
522  knlist_add(&psc->pts_inpoll.si_note, kn, 1);
523  break;
524  default:
525  error = EINVAL;
526  break;
527  }
528 
529  tty_unlock(tp);
530  return (error);
531 }
532 
533 static int
534 ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
535  struct thread *td)
536 {
537  struct tty *tp = fp->f_data;
538 #ifdef PTS_EXTERNAL
539  struct pts_softc *psc = tty_softc(tp);
540 #endif /* PTS_EXTERNAL */
541  struct cdev *dev = tp->t_dev;
542 
543  /*
544  * According to POSIX, we must implement an fstat(). This also
545  * makes this implementation compatible with Linux binaries,
546  * because Linux calls fstat() on the pseudo-terminal master to
547  * obtain st_rdev.
548  *
549  * XXX: POSIX also mentions we must fill in st_dev, but how?
550  */
551 
552  bzero(sb, sizeof *sb);
553 #ifdef PTS_EXTERNAL
554  if (psc->pts_cdev != NULL)
555  sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
556  else
557 #endif /* PTS_EXTERNAL */
558  sb->st_ino = sb->st_rdev = tty_udev(tp);
559 
560  sb->st_atim = dev->si_atime;
561  sb->st_ctim = dev->si_ctime;
562  sb->st_mtim = dev->si_mtime;
563  sb->st_uid = dev->si_uid;
564  sb->st_gid = dev->si_gid;
565  sb->st_mode = dev->si_mode | S_IFCHR;
566 
567  return (0);
568 }
569 
570 static int
571 ptsdev_close(struct file *fp, struct thread *td)
572 {
573  struct tty *tp = fp->f_data;
574 
575  /* Deallocate TTY device. */
576  tty_lock(tp);
577  tty_rel_gone(tp);
578 
579  /*
580  * Open of /dev/ptmx or /dev/ptyXX changes the type of file
581  * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
582  * use count, we need to decrement it, and possibly do other
583  * required cleanup.
584  */
585  if (fp->f_vnode != NULL)
586  return (vnops.fo_close(fp, td));
587 
588  return (0);
589 }
590 
591 static struct fileops ptsdev_ops = {
592  .fo_read = ptsdev_read,
593  .fo_write = ptsdev_write,
594  .fo_truncate = ptsdev_truncate,
595  .fo_ioctl = ptsdev_ioctl,
596  .fo_poll = ptsdev_poll,
597  .fo_kqfilter = ptsdev_kqfilter,
598  .fo_stat = ptsdev_stat,
599  .fo_close = ptsdev_close,
600  .fo_chmod = invfo_chmod,
601  .fo_chown = invfo_chown,
602  .fo_flags = DFLAG_PASSABLE,
603 };
604 
605 /*
606  * Driver-side hooks.
607  */
608 
609 static void
610 ptsdrv_outwakeup(struct tty *tp)
611 {
612  struct pts_softc *psc = tty_softc(tp);
613 
614  cv_broadcast(&psc->pts_outwait);
615  selwakeup(&psc->pts_outpoll);
616  KNOTE_LOCKED(&psc->pts_outpoll.si_note, 0);
617 }
618 
619 static void
620 ptsdrv_inwakeup(struct tty *tp)
621 {
622  struct pts_softc *psc = tty_softc(tp);
623 
624  cv_broadcast(&psc->pts_inwait);
625  selwakeup(&psc->pts_inpoll);
626  KNOTE_LOCKED(&psc->pts_inpoll.si_note, 0);
627 }
628 
629 static int
630 ptsdrv_open(struct tty *tp)
631 {
632  struct pts_softc *psc = tty_softc(tp);
633 
634  psc->pts_flags &= ~PTS_FINISHED;
635 
636  return (0);
637 }
638 
639 static void
640 ptsdrv_close(struct tty *tp)
641 {
642  struct pts_softc *psc = tty_softc(tp);
643 
644  /* Wake up any blocked readers/writers. */
645  psc->pts_flags |= PTS_FINISHED;
646  ptsdrv_outwakeup(tp);
647  ptsdrv_inwakeup(tp);
648 }
649 
650 static void
651 ptsdrv_pktnotify(struct tty *tp, char event)
652 {
653  struct pts_softc *psc = tty_softc(tp);
654 
655  /*
656  * Clear conflicting flags.
657  */
658 
659  switch (event) {
660  case TIOCPKT_STOP:
661  psc->pts_pkt &= ~TIOCPKT_START;
662  break;
663  case TIOCPKT_START:
664  psc->pts_pkt &= ~TIOCPKT_STOP;
665  break;
666  case TIOCPKT_NOSTOP:
667  psc->pts_pkt &= ~TIOCPKT_DOSTOP;
668  break;
669  case TIOCPKT_DOSTOP:
670  psc->pts_pkt &= ~TIOCPKT_NOSTOP;
671  break;
672  }
673 
674  psc->pts_pkt |= event;
675  ptsdrv_outwakeup(tp);
676 }
677 
678 static void
679 ptsdrv_free(void *softc)
680 {
681  struct pts_softc *psc = softc;
682 
683  /* Make device number available again. */
684  if (psc->pts_unit >= 0)
685  free_unr(pts_pool, psc->pts_unit);
686 
687  chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
688  racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
689  crfree(psc->pts_cred);
690 
691  seldrain(&psc->pts_inpoll);
692  seldrain(&psc->pts_outpoll);
693  knlist_destroy(&psc->pts_inpoll.si_note);
694  knlist_destroy(&psc->pts_outpoll.si_note);
695 
696 #ifdef PTS_EXTERNAL
697  /* Destroy master device as well. */
698  if (psc->pts_cdev != NULL)
700 #endif /* PTS_EXTERNAL */
701 
702  free(psc, M_PTS);
703 }
704 
705 static struct ttydevsw pts_class = {
706  .tsw_flags = TF_NOPREFIX,
707  .tsw_outwakeup = ptsdrv_outwakeup,
708  .tsw_inwakeup = ptsdrv_inwakeup,
709  .tsw_open = ptsdrv_open,
710  .tsw_close = ptsdrv_close,
711  .tsw_pktnotify = ptsdrv_pktnotify,
712  .tsw_free = ptsdrv_free,
713 };
714 
715 #ifndef PTS_EXTERNAL
716 static
717 #endif /* !PTS_EXTERNAL */
718 int
719 pts_alloc(int fflags, struct thread *td, struct file *fp)
720 {
721  int unit, ok, error;
722  struct tty *tp;
723  struct pts_softc *psc;
724  struct proc *p = td->td_proc;
725  struct ucred *cred = td->td_ucred;
726 
727  /* Resource limiting. */
728  PROC_LOCK(p);
729  error = racct_add(p, RACCT_NPTS, 1);
730  if (error != 0) {
731  PROC_UNLOCK(p);
732  return (EAGAIN);
733  }
734  ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(p, RLIMIT_NPTS));
735  if (!ok) {
736  racct_sub(p, RACCT_NPTS, 1);
737  PROC_UNLOCK(p);
738  return (EAGAIN);
739  }
740  PROC_UNLOCK(p);
741 
742  /* Try to allocate a new pts unit number. */
743  unit = alloc_unr(pts_pool);
744  if (unit < 0) {
745  racct_sub(p, RACCT_NPTS, 1);
746  chgptscnt(cred->cr_ruidinfo, -1, 0);
747  return (EAGAIN);
748  }
749 
750  /* Allocate TTY and softc. */
751  psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
752  cv_init(&psc->pts_inwait, "ptsin");
753  cv_init(&psc->pts_outwait, "ptsout");
754 
755  psc->pts_unit = unit;
756  psc->pts_cred = crhold(cred);
757 
758  tp = tty_alloc(&pts_class, psc);
759  knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
760  knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
761 
762  /* Expose the slave device as well. */
763  tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
764 
765  finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
766 
767  return (0);
768 }
769 
770 #ifdef PTS_EXTERNAL
771 int
772 pts_alloc_external(int fflags, struct thread *td, struct file *fp,
773  struct cdev *dev, const char *name)
774 {
775  int ok, error;
776  struct tty *tp;
777  struct pts_softc *psc;
778  struct proc *p = td->td_proc;
779  struct ucred *cred = td->td_ucred;
780 
781  /* Resource limiting. */
782  PROC_LOCK(p);
783  error = racct_add(p, RACCT_NPTS, 1);
784  if (error != 0) {
785  PROC_UNLOCK(p);
786  return (EAGAIN);
787  }
788  ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(p, RLIMIT_NPTS));
789  if (!ok) {
790  racct_sub(p, RACCT_NPTS, 1);
791  PROC_UNLOCK(p);
792  return (EAGAIN);
793  }
794  PROC_UNLOCK(p);
795 
796  /* Allocate TTY and softc. */
797  psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
798  cv_init(&psc->pts_inwait, "ptsin");
799  cv_init(&psc->pts_outwait, "ptsout");
800 
801  psc->pts_unit = -1;
802  psc->pts_cdev = dev;
803  psc->pts_cred = crhold(cred);
804 
805  tp = tty_alloc(&pts_class, psc);
806  knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
807  knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
808 
809  /* Expose the slave device as well. */
810  tty_makedev(tp, td->td_ucred, "%s", name);
811 
812  finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
813 
814  return (0);
815 }
816 #endif /* PTS_EXTERNAL */
817 
818 int
819 sys_posix_openpt(struct thread *td, struct posix_openpt_args *uap)
820 {
821  int error, fd;
822  struct file *fp;
823 
824  /*
825  * POSIX states it's unspecified when other flags are passed. We
826  * don't allow this.
827  */
828  if (uap->flags & ~(O_RDWR|O_NOCTTY|O_CLOEXEC))
829  return (EINVAL);
830 
831  error = falloc(td, &fp, &fd, uap->flags);
832  if (error)
833  return (error);
834 
835  /* Allocate the actual pseudo-TTY. */
836  error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp);
837  if (error != 0) {
838  fdclose(td->td_proc->p_fd, fp, fd, td);
839  fdrop(fp, td);
840  return (error);
841  }
842 
843  /* Pass it back to userspace. */
844  td->td_retval[0] = fd;
845  fdrop(fp, td);
846 
847  return (0);
848 }
849 
850 static void
851 pts_init(void *unused)
852 {
853 
854  pts_pool = new_unrhdr(0, INT_MAX, NULL);
855 }
856 
857 SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL);
static void ptsdrv_outwakeup(struct tty *tp)
Definition: tty_pts.c:610
int fd
Definition: kern_exec.c:199
int ureadc(int c, struct uio *uio)
Definition: subr_uio.c:411
rlim_t lim_cur(struct proc *p, int which)
int racct_add(struct proc *p, int resource, uint64_t amount)
Definition: kern_racct.c:1208
int invfo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, struct thread *td)
static int pts_kqops_read_event(struct knote *kn, long hint)
Definition: tty_pts.c:454
void selwakeup(struct selinfo *sip)
Definition: sys_generic.c:1656
void racct_sub_cred(struct ucred *cred, int resource, uint64_t amount)
Definition: kern_racct.c:1244
static int ptsdrv_open(struct tty *tp)
Definition: tty_pts.c:630
SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL)
void selrecord(struct thread *selector, struct selinfo *sip)
Definition: sys_generic.c:1606
void * malloc(unsigned long size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:454
struct selinfo pts_inpoll
Definition: tty_pts.c:93
static int pts_kqops_write_event(struct knote *kn, long hint)
Definition: tty_pts.c:480
static void pts_kqops_read_detach(struct knote *kn)
Definition: tty_pts.c:444
void tty_rel_gone(struct tty *tp)
Definition: tty.c:1097
void knote(struct knlist *list, long hint, int lockflags)
Definition: kern_event.c:1806
size_t ttydisc_rint_simple(struct tty *tp, const void *buf, size_t len)
Definition: tty_ttydisc.c:1070
int alloc_unr(struct unrhdr *uh)
Definition: subr_unit.c:620
static struct filterops pts_kqops_write
Definition: tty_pts.c:500
const char * name
Definition: kern_fail.c:97
int falloc(struct thread *td, struct file **resultfp, int *resultfd, int flags)
void knlist_destroy(struct knlist *knl)
Definition: kern_event.c:2002
void ttydisc_rint_done(struct tty *tp)
Definition: tty_ttydisc.c:1108
int invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td)
static struct ttydevsw pts_class
Definition: tty_pts.c:705
static int ptsdev_truncate(struct file *fp, off_t length, struct ucred *active_cred, struct thread *td)
Definition: tty_pts.c:256
void racct_sub(struct proc *p, int resource, uint64_t amount)
Definition: kern_racct.c:1239
static struct filterops pts_kqops_read
Definition: tty_pts.c:495
struct tty * tty_alloc(struct ttydevsw *tsw, void *sc)
Definition: tty.c:955
static int ptsdev_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td)
Definition: tty_pts.c:264
static struct fileops ptsdev_ops
Definition: tty_pts.c:591
dev_t tty_udev(struct tty *tp)
Definition: tty.c:1758
static int ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td)
Definition: tty_pts.c:109
static MALLOC_DEFINE(M_PTS,"pts","pseudo tty device")
#define PTS_FINISHED
Definition: tty_pts.c:89
static void pts_kqops_write_detach(struct knote *kn)
Definition: tty_pts.c:470
void knlist_remove(struct knlist *knl, struct knote *kn, int islocked)
Definition: kern_event.c:1909
static void ptsdrv_pktnotify(struct tty *tp, char event)
Definition: tty_pts.c:651
void seldrain(struct selinfo *sip)
Definition: sys_generic.c:1587
struct cdev * pts_cdev
Definition: tty_pts.c:98
size_t ttydisc_getc_poll(struct tty *tp)
Definition: tty_ttydisc.c:1235
static void ptsdrv_close(struct tty *tp)
Definition: tty_pts.c:640
struct selinfo pts_outpoll
Definition: tty_pts.c:95
void crfree(struct ucred *cr)
Definition: kern_prot.c:1835
static void pts_init(void *unused)
Definition: tty_pts.c:851
static int ptsdev_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td)
Definition: tty_pts.c:389
#define PTS_PKT
Definition: tty_pts.c:88
void knlist_add(struct knlist *knl, struct knote *kn, int islocked)
Definition: kern_event.c:1866
__FBSDID("$BSDSUniX$")
struct unrhdr * new_unrhdr(int low, int high, struct mtx *mutex)
Definition: subr_unit.c:325
int tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td)
Definition: tty.c:1741
static void ptsdrv_free(void *softc)
Definition: tty_pts.c:679
void fdclose(struct filedesc *fdp, struct file *fp, int idx, struct thread *td)
struct cv pts_outwait
Definition: tty_pts.c:94
void cv_init(struct cv *cvp, const char *desc)
Definition: kern_condvar.c:63
struct ucred * crhold(struct ucred *cr)
Definition: kern_prot.c:1824
int sys_posix_openpt(struct thread *td, struct posix_openpt_args *uap)
Definition: tty_pts.c:819
int uiomove(void *cp, int n, struct uio *uio)
Definition: subr_uio.c:202
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:554
size_t ttydisc_rint_poll(struct tty *tp)
Definition: tty_ttydisc.c:1123
static int ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td)
Definition: tty_pts.c:534
void finit(struct file *fp, u_int flag, short type, void *data, struct fileops *ops)
int ttydisc_getc_uio(struct tty *tp, struct uio *uio)
Definition: tty_ttydisc.c:1190
unsigned int pts_flags
Definition: tty_pts.c:87
void tty_makedev(struct tty *tp, struct ucred *cred, const char *fmt,...)
Definition: tty.c:1176
char pts_pkt
Definition: tty_pts.c:90
static int ptsdev_kqfilter(struct file *fp, struct knote *kn)
Definition: tty_pts.c:507
int destroy_dev_sched(struct cdev *dev)
Definition: kern_conf.c:1418
int pts_unit
Definition: tty_pts.c:86
struct ucred * pts_cred
Definition: tty_pts.c:101
int chgptscnt(struct uidinfo *uip, int diff, rlim_t max)
struct fileops vnops
Definition: vfs_vnops.c:91
int pts_alloc_external(int fflags, struct thread *td, struct file *fp, struct cdev *dev, const char *name)
Definition: tty_pts.c:772
static int ptsdev_close(struct file *fp, struct thread *td)
Definition: tty_pts.c:571
int pts_alloc(int fflags, struct thread *td, struct file *fp)
Definition: tty_pts.c:719
struct cv pts_inwait
Definition: tty_pts.c:92
void free_unr(struct unrhdr *uh, u_int item)
Definition: subr_unit.c:872
void tty_signal_pgrp(struct tty *tp, int sig)
Definition: tty.c:1283
static int ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td)
Definition: tty_pts.c:183
void knlist_init_mtx(struct knlist *knl, struct mtx *lock)
Definition: kern_event.c:1995
static struct unrhdr * pts_pool
Definition: tty_pts.c:74
static void ptsdrv_inwakeup(struct tty *tp)
Definition: tty_pts.c:620