1 /* Copyright (C) 1998-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <pty.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <termios.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <shlib-compat.h>
28 
29 /* Return the result of ptsname_r in the buffer pointed to by PTS,
30    which should be of length BUF_LEN.  If it is too long to fit in
31    this buffer, a sufficiently long buffer is allocated using malloc,
32    and returned in PTS.  0 is returned upon success, -1 otherwise.  */
33 static int
pts_name(int fd,char ** pts,size_t buf_len)34 pts_name (int fd, char **pts, size_t buf_len)
35 {
36   int rv;
37   char *buf = *pts;
38 
39   for (;;)
40     {
41       char *new_buf;
42 
43       if (buf_len)
44 	{
45 	  rv = __ptsname_r (fd, buf, buf_len);
46 
47 	  if (rv != 0 || memchr (buf, '\0', buf_len))
48 	    /* We either got an error, or we succeeded and the
49 	       returned name fit in the buffer.  */
50 	    break;
51 
52 	  /* Try again with a longer buffer.  */
53 	  buf_len += buf_len;	/* Double it */
54 	}
55       else
56 	/* No initial buffer; start out by mallocing one.  */
57 	buf_len = 128;		/* First time guess.  */
58 
59       if (buf != *pts)
60 	/* We've already malloced another buffer at least once.  */
61 	new_buf = realloc (buf, buf_len);
62       else
63 	new_buf = malloc (buf_len);
64       if (! new_buf)
65 	{
66 	  rv = -1;
67 	  __set_errno (ENOMEM);
68 	  break;
69 	}
70       buf = new_buf;
71     }
72 
73   if (rv == 0)
74     *pts = buf;		/* Return buffer to the user.  */
75   else if (buf != *pts)
76     free (buf);		/* Free what we malloced when returning an error.  */
77 
78   return rv;
79 }
80 
81 /* Create pseudo tty multiplexer/terminal pair and set terminal attributes
82    according to TERMP and WINP.  Return handles for both ends in
83    *PPTMX and *PTERMINAL, and return the name of the terminal end in NAME.  */
84 int
__openpty(int * pptmx,int * pterminal,char * name,const struct termios * termp,const struct winsize * winp)85 __openpty (int *pptmx, int *pterminal, char *name,
86 	   const struct termios *termp, const struct winsize *winp)
87 {
88 #ifdef PATH_MAX
89   char _buf[PATH_MAX];
90 #else
91   char _buf[512];
92 #endif
93   char *buf = _buf;
94   int ptmx, ret = -1, terminal = -1;
95 
96   *buf = '\0';
97 
98   ptmx = __getpt ();
99   if (ptmx == -1)
100     return -1;
101 
102   if (grantpt (ptmx))
103     goto on_error;
104 
105   if (unlockpt (ptmx))
106     goto on_error;
107 
108 #ifdef TIOCGPTPEER
109   /* Try to allocate terminal fd solely based on PTMX fd first. */
110   terminal = __ioctl (ptmx, TIOCGPTPEER, O_RDWR | O_NOCTTY);
111 #endif
112   if (terminal == -1)
113     {
114       /* Fallback to path-based terminal fd allocation in case kernel doesn't
115        * support TIOCGPTPEER.
116        */
117       if (pts_name (ptmx, &buf, sizeof (_buf)))
118         goto on_error;
119 
120       terminal = __open64 (buf, O_RDWR | O_NOCTTY);
121       if (terminal == -1)
122         goto on_error;
123     }
124 
125   /* XXX Should we ignore errors here?  */
126   if (termp)
127     tcsetattr (terminal, TCSAFLUSH, termp);
128 #ifdef TIOCSWINSZ
129   if (winp)
130     __ioctl (terminal, TIOCSWINSZ, winp);
131 #endif
132 
133   *pptmx = ptmx;
134   *pterminal = terminal;
135   if (name != NULL)
136     {
137       if (*buf == '\0')
138         if (pts_name (ptmx, &buf, sizeof (_buf)))
139           goto on_error;
140 
141       strcpy (name, buf);
142     }
143 
144   ret = 0;
145 
146  on_error:
147   if (ret == -1) {
148     __close (ptmx);
149 
150     if (terminal != -1)
151       __close (terminal);
152   }
153 
154   if (buf != _buf)
155     free (buf);
156 
157   return ret;
158 }
159 versioned_symbol (libc, __openpty, openpty, GLIBC_2_34);
160 libc_hidden_ver (__openpty, openpty)
161 
162 #if OTHER_SHLIB_COMPAT (libutil, GLIBC_2_0, GLIBC_2_34)
163 compat_symbol (libutil, __openpty, openpty, GLIBC_2_0);
164 #endif
165