xref: /vim-8.2.3635/src/pty.c (revision 94688b8a)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 /*
10  * The stuff in this file mostly comes from the "screen" program.
11  * Included with permission from Juergen Weigert.
12  * Copied from "pty.c".  "putenv.c" was used for putenv() in misc2.c.
13  *
14  * It has been modified to work better with Vim.
15  * The parts that are not used in Vim have been deleted.
16  * See the "screen" sources for the complete stuff.
17  *
18  * This specific version is distibuted under the Vim license (attribution by
19  * Juergen Weigert), the GPL applies to the original version, see the
20  * copyright notice below.
21  */
22 
23 /* Copyright (c) 1993
24  *	Juergen Weigert ([email protected])
25  *	Michael Schroeder ([email protected])
26  * Copyright (c) 1987 Oliver Laumann
27  *
28  * This program is free software; you can redistribute it and/or modify
29  * it under the terms of the GNU General Public License as published by
30  * the Free Software Foundation; either version 2, or (at your option)
31  * any later version.
32  *
33  * This program is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License
39  * along with this program (see the file COPYING); if not, write to the
40  * Free Software Foundation, Inc.,
41  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
42  */
43 
44 #include "vim.h"
45 
46 #if defined(FEAT_GUI) || defined(FEAT_JOB_CHANNEL)
47 
48 #include <signal.h>
49 
50 #ifdef __CYGWIN32__
51 # include <sys/termios.h>
52 #endif
53 
54 #ifdef HAVE_SYS_IOCTL_H
55 # include <sys/ioctl.h>
56 #endif
57 
58 #if HAVE_STROPTS_H
59 # include <sys/types.h>
60 # ifdef sinix
61 #  define buf_T __system_buf_t__
62 # endif
63 # include <stropts.h>
64 # ifdef sinix
65 #  undef buf_T
66 # endif
67 # ifdef SUN_SYSTEM
68 #  include <sys/conf.h>
69 #  if defined(HAVE_SYS_PTMS_H) && defined(HAVE_SVR4_PTYS)
70 #   include <sys/ptms.h>
71 #  endif
72 # endif
73 #endif
74 
75 #ifdef HAVE_UNISTD_H
76 # include <unistd.h>
77 #endif
78 
79 #if HAVE_TERMIO_H
80 # include <termio.h>
81 #else
82 # ifdef HAVE_TERMIOS_H
83 #  include <termios.h>
84 # endif
85 #endif
86 
87 #if HAVE_SYS_STREAM_H
88 # include <sys/stream.h>
89 #endif
90 
91 #if HAVE_SYS_PTEM_H
92 # include <sys/ptem.h>
93 #endif
94 
95 #if !defined(SUN_SYSTEM) && !defined(VMS)
96 # include <sys/ioctl.h>
97 #endif
98 
99 #if defined(SUN_SYSTEM) && defined(LOCKPTY) && !defined(TIOCEXCL)
100 # include <sys/ttold.h>
101 #endif
102 
103 #ifdef ISC
104 # include <sys/tty.h>
105 # include <sys/sioctl.h>
106 # include <sys/pty.h>
107 #endif
108 
109 #ifdef sgi
110 # include <sys/sysmacros.h>
111 #endif
112 
113 #if defined(_INCLUDE_HPUX_SOURCE) && !defined(hpux)
114 # define hpux
115 #endif
116 
117 /*
118  * if no PTYRANGE[01] is in the config file, we pick a default
119  */
120 #ifndef PTYRANGE0
121 # define PTYRANGE0 "qprs"
122 #endif
123 #ifndef PTYRANGE1
124 # define PTYRANGE1 "0123456789abcdef"
125 #endif
126 
127 /* SVR4 pseudo ttys don't seem to work with SCO-5 */
128 #ifdef M_UNIX
129 # undef HAVE_SVR4_PTYS
130 #endif
131 
132 /*
133  *  Open all ptys with O_NOCTTY, just to be on the safe side.
134  */
135 #ifndef O_NOCTTY
136 # define O_NOCTTY 0
137 #endif
138 
139     static void
140 initmaster(int f UNUSED)
141 {
142 #ifndef VMS
143 # ifdef POSIX
144     tcflush(f, TCIOFLUSH);
145 # else
146 #  ifdef TIOCFLUSH
147     (void)ioctl(f, TIOCFLUSH, (char *) 0);
148 #  endif
149 # endif
150 # ifdef LOCKPTY
151     (void)ioctl(f, TIOCEXCL, (char *) 0);
152 # endif
153 #endif
154 }
155 
156 /*
157  * This causes a hang on some systems, but is required for a properly working
158  * pty on others.  Needs to be tuned...
159  */
160     int
161 setup_slavepty(int fd)
162 {
163     if (fd < 0)
164 	return 0;
165 #if defined(I_PUSH) && defined(HAVE_SVR4_PTYS) && !defined(sgi) \
166 	&& !defined(linux) && !defined(__osf__) && !defined(M_UNIX)
167 # if defined(HAVE_SYS_PTEM_H) || defined(hpux)
168     if (ioctl(fd, I_PUSH, "ptem") != 0)
169 	return -1;
170 # endif
171     if (ioctl(fd, I_PUSH, "ldterm") != 0)
172 	return -1;
173 # ifdef SUN_SYSTEM
174     if (ioctl(fd, I_PUSH, "ttcompat") != 0)
175 	return -1;
176 # endif
177 #endif
178     return 0;
179 }
180 
181 
182 #if defined(OSX) && !defined(PTY_DONE)
183 #define PTY_DONE
184     int
185 mch_openpty(char **ttyn)
186 {
187     int		f;
188     static char TtyName[32];
189 
190     if ((f = open_controlling_pty(TtyName)) < 0)
191 	return -1;
192     initmaster(f);
193     *ttyn = TtyName;
194     return f;
195 }
196 #endif
197 
198 #if (defined(sequent) || defined(_SEQUENT_)) && defined(HAVE_GETPSEUDOTTY) \
199 	&& !defined(PTY_DONE)
200 #define PTY_DONE
201     int
202 mch_openpty(char **ttyn)
203 {
204     char	*m, *s;
205     int		f;
206     /* used for opening a new pty-pair: */
207     static char PtyName[32];
208     static char TtyName[32];
209 
210     if ((f = getpseudotty(&s, &m)) < 0)
211 	return -1;
212 #ifdef _SEQUENT_
213     fvhangup(s);
214 #endif
215     vim_strncpy((char_u *)PtyName, (char_u *)m, sizeof(PtyName) - 1);
216     vim_strncpy((char_u *)TtyName, (char_u *)s, sizeof(TtyName) - 1);
217     initmaster(f);
218     *ttyn = TtyName;
219     return f;
220 }
221 #endif
222 
223 #if defined(__sgi) && !defined(PTY_DONE)
224 #define PTY_DONE
225     int
226 mch_openpty(char **ttyn)
227 {
228     int f;
229     char *name;
230     RETSIGTYPE (*sigcld) SIGPROTOARG;
231 
232     /*
233      * SIGCHLD set to SIG_DFL for _getpty() because it may fork() and
234      * exec() /usr/adm/mkpts
235      */
236     sigcld = signal(SIGCHLD, SIG_DFL);
237     name = _getpty(&f, O_RDWR | O_NONBLOCK | O_EXTRA, 0600, 0);
238     signal(SIGCHLD, sigcld);
239 
240     if (name == 0)
241 	return -1;
242     initmaster(f);
243     *ttyn = name;
244     return f;
245 }
246 #endif
247 
248 #if defined(MIPS) && defined(HAVE_DEV_PTC) && !defined(PTY_DONE)
249 #define PTY_DONE
250     int
251 mch_openpty(char **ttyn)
252 {
253     int		f;
254     stat_T	buf;
255     /* used for opening a new pty-pair: */
256     static char TtyName[32];
257 
258     if ((f = open("/dev/ptc", O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXTRA, 0)) < 0)
259 	return -1;
260     if (mch_fstat(f, &buf) < 0)
261     {
262 	close(f);
263 	return -1;
264     }
265     sprintf(TtyName, "/dev/ttyq%d", minor(buf.st_rdev));
266     initmaster(f);
267     *ttyn = TtyName;
268     return f;
269 }
270 #endif
271 
272 #if defined(HAVE_SVR4_PTYS) && !defined(PTY_DONE) && !defined(hpux) \
273 	    && !(defined(MACOS_X) && !defined(MAC_OS_X_VERSION_10_6))
274 
275 /* NOTE: Even though HPUX can have /dev/ptmx, the code below doesn't work!
276  * Same for Mac OS X Leopard (10.5). */
277 #define PTY_DONE
278     int
279 mch_openpty(char **ttyn)
280 {
281     int		f;
282     char	*m;
283     char	*(ptsname(int));
284     int		unlockpt(int);
285     int		grantpt(int);
286     RETSIGTYPE (*sigcld) SIGPROTOARG;
287     /* used for opening a new pty-pair: */
288     static char TtyName[32];
289 
290     if ((f = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_EXTRA, 0)) == -1)
291 	return -1;
292 
293     /*
294      * SIGCHLD set to SIG_DFL for grantpt() because it fork()s and
295      * exec()s pt_chmod
296      */
297     sigcld = signal(SIGCHLD, SIG_DFL);
298     if ((m = ptsname(f)) == NULL || grantpt(f) || unlockpt(f))
299     {
300 	signal(SIGCHLD, sigcld);
301 	close(f);
302 	return -1;
303     }
304     signal(SIGCHLD, sigcld);
305     vim_strncpy((char_u *)TtyName, (char_u *)m, sizeof(TtyName) - 1);
306     initmaster(f);
307     *ttyn = TtyName;
308     return f;
309 }
310 #endif
311 
312 #if defined(_AIX) && defined(HAVE_DEV_PTC) && !defined(PTY_DONE)
313 #define PTY_DONE
314 
315 #ifdef _IBMR2
316 int aixhack = -1;
317 #endif
318 
319     int
320 mch_openpty(char **ttyn)
321 {
322     int		f;
323     /* used for opening a new pty-pair: */
324     static char TtyName[32];
325 
326     /* a dumb looking loop replaced by mycrofts code: */
327     if ((f = open("/dev/ptc", O_RDWR | O_NOCTTY | O_EXTRA)) < 0)
328 	return -1;
329     vim_strncpy((char_u *)TtyName, (char_u *)ttyname(f), sizeof(TtyName) - 1);
330     if (geteuid() != ROOT_UID && mch_access(TtyName, R_OK | W_OK))
331     {
332 	close(f);
333 	return -1;
334     }
335     initmaster(f);
336 # ifdef _IBMR2
337     if (aixhack >= 0)
338 	close(aixhack);
339     if ((aixhack = open(TtyName, O_RDWR | O_NOCTTY | O_EXTRA, 0)) < 0)
340     {
341 	close(f);
342 	return -1;
343     }
344 # endif
345     *ttyn = TtyName;
346     return f;
347 }
348 #endif
349 
350 #ifndef PTY_DONE
351 
352 # ifdef hpux
353 static char PtyProto[] = "/dev/ptym/ptyXY";
354 static char TtyProto[] = "/dev/pty/ttyXY";
355 # else
356 #  ifdef __BEOS__
357 static char PtyProto[] = "/dev/pt/XY";
358 static char TtyProto[] = "/dev/tt/XY";
359 #  else
360 static char PtyProto[] = "/dev/ptyXY";
361 static char TtyProto[] = "/dev/ttyXY";
362 #  endif
363 # endif
364 
365     int
366 mch_openpty(char **ttyn)
367 {
368     char	*p, *q, *l, *d;
369     int		f;
370     /* used for opening a new pty-pair: */
371     static char PtyName[32];
372     static char TtyName[32];
373 
374     strcpy(PtyName, PtyProto);
375     strcpy(TtyName, TtyProto);
376     for (p = PtyName; *p != 'X'; p++)
377 	;
378     for (q = TtyName; *q != 'X'; q++)
379 	;
380     for (l = PTYRANGE0; (*p = *l) != '\0'; l++)
381     {
382 	for (d = PTYRANGE1; (p[1] = *d) != '\0'; d++)
383 	{
384 	    if ((f = open(PtyName, O_RDWR | O_NOCTTY | O_EXTRA, 0)) == -1)
385 		continue;
386 	    q[0] = *l;
387 	    q[1] = *d;
388 	    if (geteuid() != ROOT_UID && mch_access(TtyName, R_OK | W_OK))
389 	    {
390 		close(f);
391 		continue;
392 	    }
393 #if defined(SUN_SYSTEM) && defined(TIOCGPGRP) && !defined(SUNOS3)
394 	    /* Hack to ensure that the slave side of the pty is
395 	     * unused. May not work in anything other than SunOS4.1
396 	     */
397 	    {
398 		int pgrp;
399 
400 		/* tcgetpgrp does not work (uses TIOCGETPGRP)! */
401 		if (ioctl(f, TIOCGPGRP, (char *)&pgrp) != -1 || errno != EIO)
402 		{
403 		    close(f);
404 		    continue;
405 		}
406 	    }
407 #endif
408 	    initmaster(f);
409 	    *ttyn = TtyName;
410 	    return f;
411 	}
412     }
413     return -1;
414 }
415 #endif
416 
417 /*
418  * Call isatty(fd), except for SunOS where it's done differently.
419  */
420     int
421 mch_isatty(int fd)
422 {
423 # if defined(I_STR) && defined(HAVE_SYS_PTMS_H) && defined(HAVE_SVR4_PTYS) \
424 	&& defined(SUN_SYSTEM)
425     // On SunOS, isatty() for /dev/ptmx returns false or sometimes can hang up
426     // in the inner ioctl(), and therefore first determine whether "fd" is a
427     // master device.
428     struct strioctl istr;
429 
430     istr.ic_cmd = ISPTM;
431     istr.ic_timout = 0;
432     istr.ic_dp = NULL;
433     istr.ic_len = 0;
434 
435     if (ioctl(fd, I_STR, &istr) == 0)
436 	// Trick: return 2 in order to advice the caller that "fd" is a master
437 	// device. cf. src/os_unix.c:get_tty_fd()
438 	return 2;
439 # endif
440     return isatty(fd);
441 }
442 
443 #endif /* FEAT_GUI || FEAT_JOB_CHANNEL */
444