xref: /vim-8.2.3635/src/pty.c (revision 12ee7ff0)
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 #if defined(HAVE_SVR4_PTYS) || defined(HAVE_POSIX_OPENPT)
140 // These should be in stdlib.h, but it depends on _XOPEN_SOURCE.
141 char *ptsname(int);
142 int unlockpt(int);
143 int grantpt(int);
144 int posix_openpt(int flags);
145 #endif
146 
147     static void
148 initmaster(int f UNUSED)
149 {
150 #ifndef VMS
151 # ifdef POSIX
152     tcflush(f, TCIOFLUSH);
153 # else
154 #  ifdef TIOCFLUSH
155     (void)ioctl(f, TIOCFLUSH, (char *) 0);
156 #  endif
157 # endif
158 # ifdef LOCKPTY
159     (void)ioctl(f, TIOCEXCL, (char *) 0);
160 # endif
161 #endif
162 }
163 
164 /*
165  * This causes a hang on some systems, but is required for a properly working
166  * pty on others.  Needs to be tuned...
167  */
168     int
169 setup_slavepty(int fd)
170 {
171     if (fd < 0)
172 	return 0;
173 #if defined(I_PUSH) && defined(HAVE_SVR4_PTYS) && !defined(sgi) \
174 	&& !defined(linux) && !defined(__osf__) && !defined(M_UNIX)
175 # if defined(HAVE_SYS_PTEM_H) || defined(hpux)
176     if (ioctl(fd, I_PUSH, "ptem") != 0)
177 	return -1;
178 # endif
179     if (ioctl(fd, I_PUSH, "ldterm") != 0)
180 	return -1;
181 # ifdef SUN_SYSTEM
182     if (ioctl(fd, I_PUSH, "ttcompat") != 0)
183 	return -1;
184 # endif
185 #endif
186     return 0;
187 }
188 
189 #if defined(HAVE_POSIX_OPENPT) && !defined(PTY_DONE)
190 #define PTY_DONE
191     int
192 mch_openpty(char **ttyn)
193 {
194     int		f;
195     char	*m;
196     RETSIGTYPE (*sigcld) SIGPROTOARG;
197     static char TtyName[32];  // used for opening a new pty-pair
198 
199     if ((f = posix_openpt(O_RDWR | O_NOCTTY | O_EXTRA)) == -1)
200 	return -1;
201 
202     // SIGCHLD set to SIG_DFL for grantpt() because it fork()s and
203     // exec()s pt_chmod
204     sigcld = signal(SIGCHLD, SIG_DFL);
205     if ((m = ptsname(f)) == NULL || grantpt(f) || unlockpt(f))
206     {
207 	signal(SIGCHLD, sigcld);
208 	close(f);
209 	return -1;
210     }
211     signal(SIGCHLD, sigcld);
212     vim_strncpy((char_u *)TtyName, (char_u *)m, sizeof(TtyName) - 1);
213     initmaster(f);
214     *ttyn = TtyName;
215     return f;
216 }
217 #endif
218 
219 #if defined(OSX) && !defined(PTY_DONE)
220 #define PTY_DONE
221     int
222 mch_openpty(char **ttyn)
223 {
224     int		f;
225     static char TtyName[32];
226 
227     if ((f = open_controlling_pty(TtyName)) < 0)
228 	return -1;
229     initmaster(f);
230     *ttyn = TtyName;
231     return f;
232 }
233 #endif
234 
235 #if (defined(sequent) || defined(_SEQUENT_)) && defined(HAVE_GETPSEUDOTTY) \
236 	&& !defined(PTY_DONE)
237 #define PTY_DONE
238     int
239 mch_openpty(char **ttyn)
240 {
241     char	*m, *s;
242     int		f;
243     /* used for opening a new pty-pair: */
244     static char PtyName[32];
245     static char TtyName[32];
246 
247     if ((f = getpseudotty(&s, &m)) < 0)
248 	return -1;
249 #ifdef _SEQUENT_
250     fvhangup(s);
251 #endif
252     vim_strncpy((char_u *)PtyName, (char_u *)m, sizeof(PtyName) - 1);
253     vim_strncpy((char_u *)TtyName, (char_u *)s, sizeof(TtyName) - 1);
254     initmaster(f);
255     *ttyn = TtyName;
256     return f;
257 }
258 #endif
259 
260 #if defined(__sgi) && !defined(PTY_DONE)
261 #define PTY_DONE
262     int
263 mch_openpty(char **ttyn)
264 {
265     int f;
266     char *name;
267     RETSIGTYPE (*sigcld) SIGPROTOARG;
268 
269     /*
270      * SIGCHLD set to SIG_DFL for _getpty() because it may fork() and
271      * exec() /usr/adm/mkpts
272      */
273     sigcld = signal(SIGCHLD, SIG_DFL);
274     name = _getpty(&f, O_RDWR | O_NONBLOCK | O_EXTRA, 0600, 0);
275     signal(SIGCHLD, sigcld);
276 
277     if (name == 0)
278 	return -1;
279     initmaster(f);
280     *ttyn = name;
281     return f;
282 }
283 #endif
284 
285 #if defined(MIPS) && defined(HAVE_DEV_PTC) && !defined(PTY_DONE)
286 #define PTY_DONE
287     int
288 mch_openpty(char **ttyn)
289 {
290     int		f;
291     stat_T	buf;
292     /* used for opening a new pty-pair: */
293     static char TtyName[32];
294 
295     if ((f = open("/dev/ptc", O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXTRA, 0)) < 0)
296 	return -1;
297     if (mch_fstat(f, &buf) < 0)
298     {
299 	close(f);
300 	return -1;
301     }
302     sprintf(TtyName, "/dev/ttyq%d", minor(buf.st_rdev));
303     initmaster(f);
304     *ttyn = TtyName;
305     return f;
306 }
307 #endif
308 
309 #if defined(HAVE_SVR4_PTYS) && !defined(PTY_DONE) && !defined(hpux) \
310 	    && !(defined(MACOS_X) && !defined(MAC_OS_X_VERSION_10_6))
311 
312 /* NOTE: Even though HPUX can have /dev/ptmx, the code below doesn't work!
313  * Same for Mac OS X Leopard (10.5). */
314 #define PTY_DONE
315     int
316 mch_openpty(char **ttyn)
317 {
318     int		f;
319     char	*m;
320     RETSIGTYPE (*sigcld) SIGPROTOARG;
321     /* used for opening a new pty-pair: */
322     static char TtyName[32];
323 
324     if ((f = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_EXTRA, 0)) == -1)
325 	return -1;
326 
327     /*
328      * SIGCHLD set to SIG_DFL for grantpt() because it fork()s and
329      * exec()s pt_chmod
330      */
331     sigcld = signal(SIGCHLD, SIG_DFL);
332     if ((m = ptsname(f)) == NULL || grantpt(f) || unlockpt(f))
333     {
334 	signal(SIGCHLD, sigcld);
335 	close(f);
336 	return -1;
337     }
338     signal(SIGCHLD, sigcld);
339     vim_strncpy((char_u *)TtyName, (char_u *)m, sizeof(TtyName) - 1);
340     initmaster(f);
341     *ttyn = TtyName;
342     return f;
343 }
344 #endif
345 
346 #if defined(_AIX) && defined(HAVE_DEV_PTC) && !defined(PTY_DONE)
347 #define PTY_DONE
348 
349 #ifdef _IBMR2
350 int aixhack = -1;
351 #endif
352 
353     int
354 mch_openpty(char **ttyn)
355 {
356     int		f;
357     /* used for opening a new pty-pair: */
358     static char TtyName[32];
359 
360     /* a dumb looking loop replaced by mycrofts code: */
361     if ((f = open("/dev/ptc", O_RDWR | O_NOCTTY | O_EXTRA)) < 0)
362 	return -1;
363     vim_strncpy((char_u *)TtyName, (char_u *)ttyname(f), sizeof(TtyName) - 1);
364     if (geteuid() != ROOT_UID && mch_access(TtyName, R_OK | W_OK))
365     {
366 	close(f);
367 	return -1;
368     }
369     initmaster(f);
370 # ifdef _IBMR2
371     if (aixhack >= 0)
372 	close(aixhack);
373     if ((aixhack = open(TtyName, O_RDWR | O_NOCTTY | O_EXTRA, 0)) < 0)
374     {
375 	close(f);
376 	return -1;
377     }
378 # endif
379     *ttyn = TtyName;
380     return f;
381 }
382 #endif
383 
384 #ifndef PTY_DONE
385 
386 # ifdef hpux
387 static char PtyProto[] = "/dev/ptym/ptyXY";
388 static char TtyProto[] = "/dev/pty/ttyXY";
389 # else
390 #  ifdef __BEOS__
391 static char PtyProto[] = "/dev/pt/XY";
392 static char TtyProto[] = "/dev/tt/XY";
393 #  else
394 static char PtyProto[] = "/dev/ptyXY";
395 static char TtyProto[] = "/dev/ttyXY";
396 #  endif
397 # endif
398 
399     int
400 mch_openpty(char **ttyn)
401 {
402     char	*p, *q, *l, *d;
403     int		f;
404     /* used for opening a new pty-pair: */
405     static char PtyName[32];
406     static char TtyName[32];
407 
408     strcpy(PtyName, PtyProto);
409     strcpy(TtyName, TtyProto);
410     for (p = PtyName; *p != 'X'; p++)
411 	;
412     for (q = TtyName; *q != 'X'; q++)
413 	;
414     for (l = PTYRANGE0; (*p = *l) != '\0'; l++)
415     {
416 	for (d = PTYRANGE1; (p[1] = *d) != '\0'; d++)
417 	{
418 	    if ((f = open(PtyName, O_RDWR | O_NOCTTY | O_EXTRA, 0)) == -1)
419 		continue;
420 	    q[0] = *l;
421 	    q[1] = *d;
422 	    if (geteuid() != ROOT_UID && mch_access(TtyName, R_OK | W_OK))
423 	    {
424 		close(f);
425 		continue;
426 	    }
427 #if defined(SUN_SYSTEM) && defined(TIOCGPGRP) && !defined(SUNOS3)
428 	    /* Hack to ensure that the slave side of the pty is
429 	     * unused. May not work in anything other than SunOS4.1
430 	     */
431 	    {
432 		int pgrp;
433 
434 		/* tcgetpgrp does not work (uses TIOCGETPGRP)! */
435 		if (ioctl(f, TIOCGPGRP, (char *)&pgrp) != -1 || errno != EIO)
436 		{
437 		    close(f);
438 		    continue;
439 		}
440 	    }
441 #endif
442 	    initmaster(f);
443 	    *ttyn = TtyName;
444 	    return f;
445 	}
446     }
447     return -1;
448 }
449 #endif
450 
451 /*
452  * Call isatty(fd), except for SunOS where it's done differently.
453  */
454     int
455 mch_isatty(int fd)
456 {
457 # if defined(I_STR) && defined(HAVE_SYS_PTMS_H) && defined(HAVE_SVR4_PTYS) \
458 	&& defined(SUN_SYSTEM)
459     // On SunOS, isatty() for /dev/ptmx returns false or sometimes can hang up
460     // in the inner ioctl(), and therefore first determine whether "fd" is a
461     // master device.
462     struct strioctl istr;
463 
464     istr.ic_cmd = ISPTM;
465     istr.ic_timout = 0;
466     istr.ic_dp = NULL;
467     istr.ic_len = 0;
468 
469     if (ioctl(fd, I_STR, &istr) == 0)
470 	// Trick: return 2 in order to advice the caller that "fd" is a master
471 	// device. cf. src/os_unix.c:get_tty_fd()
472 	return 2;
473 # endif
474     return isatty(fd);
475 }
476 
477 #endif /* FEAT_GUI || FEAT_JOB_CHANNEL */
478