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